This is an automated email from the ASF dual-hosted git repository. zjffdu pushed a commit to branch web_angular in repository https://gitbox.apache.org/repos/asf/zeppelin.git
commit ff7938fd9960b61f59a76a3bfa207009a9ddedd8 Author: Hsuan Lee <hsua...@gmail.com> AuthorDate: Tue Dec 24 10:06:54 2019 +0800 [ZEPPELIN-4503] Support note scope dynamic forms ### What is this PR for? Support note scope dynamic forms ### What type of PR is it? [Feature] ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-4503 ### How should this be tested? Not applicable ### Screenshots (if appropriate) ![Kapture 2019-12-24 at 10 05 11](https://user-images.githubusercontent.com/22736418/71389013-a3b6a680-2635-11ea-8439-a4b81cc3277e.gif) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Hsuan Lee <hsua...@gmail.com> Closes #3565 from hsuanxyz/feat/note-dynamic-forms and squashes the following commits: 342aecfd8 [Hsuan Lee] chore: add component modules in share b8208937c [Hsuan Lee] feat: support note scope dynamaic forms --- .../src/interfaces/message-notebook.interface.ts | 1 + .../src/interfaces/message-paragraph.interface.ts | 1 + .../note-form-block.component.html} | 24 +++++---- .../note-form-block.component.less} | 21 ++++---- .../note-form-block/note-form-block.component.ts | 43 +++++++++++++++ .../workspace/notebook/notebook.component.html | 8 +++ .../pages/workspace/notebook/notebook.component.ts | 42 ++++++++++++++- .../pages/workspace/notebook/notebook.module.ts | 4 +- .../elastic-input/elastic-input.component.html | 2 +- .../share/elastic-input/elastic-input.component.ts | 3 +- .../dynamic-forms/dynamic-forms.component.html | 62 +++++++++++++--------- .../dynamic-forms/dynamic-forms.component.less | 24 ++++++++- .../share/dynamic-forms/dynamic-forms.component.ts | 6 +++ .../src/app/pages/workspace/share/share.module.ts | 8 +-- 14 files changed, 195 insertions(+), 54 deletions(-) diff --git a/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-notebook.interface.ts b/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-notebook.interface.ts index f7b22de..649a312 100644 --- a/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-notebook.interface.ts +++ b/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-notebook.interface.ts @@ -197,6 +197,7 @@ export interface NotesInfoItem extends ID { export interface NoteConfig { cron?: string; releaseresource: boolean; + noteFormTitle?: string; cronExecutingRoles?: string; cronExecutingUser?: string; isZeppelinNotebookCronEnable: boolean; diff --git a/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-paragraph.interface.ts b/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-paragraph.interface.ts index bcc0199..2e04b22 100644 --- a/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-paragraph.interface.ts +++ b/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-paragraph.interface.ts @@ -23,6 +23,7 @@ export interface DynamicFormsItem { defaultValue: string | string[]; hidden: boolean; name: string; + displayName?: string; type: DynamicFormsType; argument?: string; options?: Array<{ value: string; displayName?: string }>; diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.html b/zeppelin-web-angular/src/app/pages/workspace/notebook/note-form-block/note-form-block.component.html similarity index 52% copy from zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.html copy to zeppelin-web-angular/src/app/pages/workspace/notebook/note-form-block/note-form-block.component.html index c605d28..2ca5851 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.html +++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/note-form-block/note-form-block.component.html @@ -9,15 +9,17 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - -<div class="elastic" #elasticElement [class.min]="min"> - <input #inputElement - [ngModel]="editValue" - (ngModelChange)="updateValue($event)" - *ngIf="showEditor" - (blur)="setEditorState(false)" - (keydown.esc)="cancelEdit()" - (keydown.enter)="setEditorState(false)" - (input)="updateInputWidth()"> - <p #pElement *ngIf="!showEditor" (click)="setEditorState(true)">{{value || 'Untitled'}}</p> +<div class="forms-wrap"> + <zeppelin-elastic-input [value]="noteTitle" + defaultTitle="Untitled Form" + [min]="true" + (valueUpdate)="setTitle($event)"></zeppelin-elastic-input> + <zeppelin-notebook-paragraph-dynamic-forms + [runOnChange]="true" + [removable]="true" + [paramDefs]="paramDefs" + [formDefs]="formDefs" + (formRemove)="onFormRemove($event)" + (formChange)="onFormChange()"> + </zeppelin-notebook-paragraph-dynamic-forms> </div> diff --git a/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.less b/zeppelin-web-angular/src/app/pages/workspace/notebook/note-form-block/note-form-block.component.less similarity index 71% copy from zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.less copy to zeppelin-web-angular/src/app/pages/workspace/notebook/note-form-block/note-form-block.component.less index 060b2f8..896b6f8 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.less +++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/note-form-block/note-form-block.component.less @@ -9,16 +9,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@import "theme-mixin"; :host { display: block; - .form-item { - margin-bottom: 24px; - nz-select { - width: 100%; - } - ::ng-deep .ant-checkbox-wrapper { - margin-left: 0; - } - } + padding: 0 4px; } + +.themeMixin({ + .forms-wrap { + background: @component-background; + border: 1px solid @border-color-split; + box-shadow: @card-shadow; + padding: 12px; + position: relative; + } +}); diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/note-form-block/note-form-block.component.ts b/zeppelin-web-angular/src/app/pages/workspace/notebook/note-form-block/note-form-block.component.ts new file mode 100644 index 0000000..9eb0b72 --- /dev/null +++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/note-form-block/note-form-block.component.ts @@ -0,0 +1,43 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { DynamicForms, DynamicFormParams } from '@zeppelin/sdk'; + +@Component({ + selector: 'zeppelin-note-form-block', + templateUrl: './note-form-block.component.html', + styleUrls: ['./note-form-block.component.less'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class NoteFormBlockComponent implements OnInit { + @Input() noteTitle: string; + @Input() formDefs: DynamicForms; + @Input() paramDefs: DynamicFormParams; + @Output() readonly noteTitleChange = new EventEmitter<string>(); + @Output() readonly noteFormChange = new EventEmitter<DynamicFormParams>(); + @Output() readonly noteFormNameRemove = new EventEmitter<string>(); + constructor() {} + + ngOnInit() {} + + onFormRemove({ name }) { + this.noteFormNameRemove.emit(name); + } + + onFormChange() { + this.noteFormChange.emit(this.paramDefs); + } + + setTitle(title: string) { + this.noteTitleChange.emit(title); + } +} diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html index c447a59..d9574db 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html +++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html @@ -34,6 +34,14 @@ <zeppelin-notebook-revisions-comparator *ngSwitchCase="'revisions'"></zeppelin-notebook-revisions-comparator> </div> <div class="paragraph-area"> + <zeppelin-note-form-block *ngIf="isShowNoteForms" + [formDefs]="note.noteForms" + [paramDefs]="note.noteParams" + [noteTitle]="note.config?.noteFormTitle" + (noteFormChange)="onNoteFormChange($event)" + (noteFormNameRemove)="onFormNameRemove($event)" + (noteTitleChange)="onNoteTitleChange($event)"> + </zeppelin-note-form-block> <div class="paragraph-inner" nz-row> <zeppelin-notebook-paragraph nz-col *ngFor="let p of note.paragraphs;let first = first; let last = last; index as i" diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.ts b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.ts index 404fc28..0ca7546 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.ts +++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.ts @@ -26,7 +26,14 @@ import { distinctUntilKeyChanged, takeUntil } from 'rxjs/operators'; import { MessageListener, MessageListenersManager } from '@zeppelin/core'; import { Permissions } from '@zeppelin/interfaces'; -import { InterpreterBindingItem, MessageReceiveDataTypeMap, Note, OP, RevisionListItem } from '@zeppelin/sdk'; +import { + DynamicFormParams, + InterpreterBindingItem, + MessageReceiveDataTypeMap, + Note, + OP, + RevisionListItem +} from '@zeppelin/sdk'; import { MessageService, NgZService, @@ -58,6 +65,7 @@ export class NotebookComponent extends MessageListenersManager implements OnInit revisionView = false; collaborativeModeUsers = []; isNoteDirty = false; + isShowNoteForms = false; saveTimer = null; interpreterBindings: InterpreterBindingItem[] = []; activatedExtension: 'interpreter' | 'permissions' | 'revisions' | 'hide' = 'hide'; @@ -81,6 +89,14 @@ export class NotebookComponent extends MessageListenersManager implements OnInit this.note.config.personalizedMode = this.note.config.personalizedMode === undefined ? 'false' : this.note.config.personalizedMode; } + if (this.note.noteForms && this.note.noteParams) { + this.saveNoteForms({ + formsData: { + forms: this.note.noteForms, + params: this.note.noteParams + } + }); + } this.cdr.markForCheck(); } } @@ -125,6 +141,7 @@ export class NotebookComponent extends MessageListenersManager implements OnInit saveNoteForms(data: MessageReceiveDataTypeMap[OP.SAVE_NOTE_FORMS]) { this.note.noteForms = data.formsData.forms; this.note.noteParams = data.formsData.params; + this.setNoteFormsStatus(); } @MessageListener(OP.NOTE_REVISION) @@ -290,6 +307,29 @@ export class NotebookComponent extends MessageListenersManager implements OnInit this.listOfNotebookParagraphComponent.forEach(p => p.setEditorHide(editorHide)); } + onNoteFormChange(noteParams: DynamicFormParams) { + this.messageService.saveNoteForms({ + noteParams, + id: this.note.id + }); + } + + onFormNameRemove(formName: string) { + this.messageService.removeNoteForms(this.note, formName); + } + + onNoteTitleChange(noteFormTitle: string) { + this.messageService.updateNote(this.note.id, this.note.name, { + ...this.note.config, + noteFormTitle + }); + } + + setNoteFormsStatus() { + this.isShowNoteForms = this.note && this.note.noteForms && Object.keys(this.note.noteForms).length !== 0; + this.cdr.markForCheck(); + } + constructor( private activatedRoute: ActivatedRoute, public messageService: MessageService, diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.module.ts b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.module.ts index 4b71d3d..49a6230 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.module.ts +++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.module.ts @@ -48,6 +48,7 @@ import { NotebookRevisionsComparatorComponent } from './revisions-comparator/rev import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; import { WorkspaceShareModule } from '../../workspace/share/share.module'; import { NotebookActionBarComponent } from './action-bar/action-bar.component'; +import { NoteFormBlockComponent } from './note-form-block/note-form-block.component'; import { NotebookRoutingModule } from './notebook-routing.module'; import { NotebookComponent } from './notebook.component'; import { NotebookShareModule } from './share/share.module'; @@ -64,7 +65,8 @@ import { NotebookShareModule } from './share/share.module'; NotebookParagraphCodeEditorComponent, NotebookParagraphProgressComponent, NotebookParagraphFooterComponent, - NotebookParagraphControlComponent + NotebookParagraphControlComponent, + NoteFormBlockComponent ], imports: [ CommonModule, diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.html b/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.html index c605d28..aa7b5e7 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.html +++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.html @@ -19,5 +19,5 @@ (keydown.esc)="cancelEdit()" (keydown.enter)="setEditorState(false)" (input)="updateInputWidth()"> - <p #pElement *ngIf="!showEditor" (click)="setEditorState(true)">{{value || 'Untitled'}}</p> + <p #pElement *ngIf="!showEditor" (click)="setEditorState(true)">{{value || defaultTitle}}</p> </div> diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.ts b/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.ts index ef4dce4..2057a87 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.ts +++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component.ts @@ -33,6 +33,7 @@ export class ElasticInputComponent implements OnChanges { @Input() value: string; @Input() readonly = false; @Input() min = false; + @Input() defaultTitle = 'Untitled'; @Output() readonly valueUpdate = new EventEmitter<string>(); @ViewChild('inputElement', { read: ElementRef, static: false }) inputElement: ElementRef; @ViewChild('pElement', { read: ElementRef, static: false }) pElement: ElementRef; @@ -47,7 +48,7 @@ export class ElasticInputComponent implements OnChanges { updateValue(value: string) { const trimmedNewName = value.trim(); - if (trimmedNewName.length > 0 && this.value !== trimmedNewName) { + if (typeof value === 'string') { this.editValue = trimmedNewName; } } diff --git a/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.html b/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.html index 3a4cfdd..330b3e8 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.html +++ b/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.html @@ -20,32 +20,44 @@ nzLg="8" nzMd="12" nzSm="24"> - <ng-container [ngSwitch]="form.type"> - <input *ngSwitchCase="formType.TextBox" - nz-input - [(ngModel)]="paramDefs[form.name]" - [disabled]="disable" - (ngModelChange)="onFormChange()"> - <input *ngSwitchCase="formType.Password" - nz-input - type="password" - [(ngModel)]="paramDefs[form.name]" - [disabled]="disable" - (ngModelChange)="onFormChange()"> - <nz-select *ngSwitchCase="formType.Select" - [nzDisabled]="disable" + <div nz-row nzType="flex"> + <label class="item-label" *ngIf="form.displayName" nz-col nzSpan="6">{{form.displayName}}: </label> + <div class="control-wrap" nz-col [nzSpan]="form.displayName ? 16 : 24"> + <ng-container [ngSwitch]="form.type"> + <input *ngSwitchCase="formType.TextBox" + nz-input [(ngModel)]="paramDefs[form.name]" + [disabled]="disable" (ngModelChange)="onFormChange()"> - <nz-option *ngFor="let opt of form.options" - [nzLabel]="opt.displayName || opt.value" - [nzValue]="opt.value"> - </nz-option> - </nz-select> - <nz-checkbox-group *ngSwitchCase="formType.CheckBox" - [nzDisabled]="disable" - [(ngModel)]="checkboxGroups[form.name]" - (ngModelChange)="checkboxChange($event, form.name)"> - </nz-checkbox-group> - </ng-container> + <input *ngSwitchCase="formType.Password" + nz-input + type="password" + [(ngModel)]="paramDefs[form.name]" + [disabled]="disable" + (ngModelChange)="onFormChange()"> + <nz-select *ngSwitchCase="formType.Select" + [nzDisabled]="disable" + [(ngModel)]="paramDefs[form.name]" + (ngModelChange)="onFormChange()"> + <nz-option *ngFor="let opt of form.options" + [nzLabel]="opt.displayName || opt.value" + [nzValue]="opt.value"> + </nz-option> + </nz-select> + <nz-checkbox-group *ngSwitchCase="formType.CheckBox" + [nzDisabled]="disable" + [(ngModel)]="checkboxGroups[form.name]" + (ngModelChange)="checkboxChange($event, form.name)"> + </nz-checkbox-group> + </ng-container> + <button *ngIf="removable" + nz-button + nzType="link" + class="remove-button" + (click)="remove(form)"> + <i nz-icon nzType="close" nzTheme="outline"></i> + </button> + </div> + </div> </div> </div> diff --git a/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.less b/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.less index 060b2f8..a9a4b88 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.less +++ b/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.less @@ -10,15 +10,37 @@ * limitations under the License. */ +@import "theme-mixin"; + :host { display: block; +} + +.themeMixin({ + .control-wrap { + display: flex; + } .form-item { margin-bottom: 24px; + .item-label { + font-weight: 700; + } nz-select { width: 100%; } ::ng-deep .ant-checkbox-wrapper { margin-left: 0; + line-height: 32px; + } + &:hover { + .remove-button { + color: @text-color-danger; + } + } + .remove-button { + margin: 0 4px; + transition: color ease-in-out .3s; + color: @text-color-secondary; } } -} +}); diff --git a/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.ts b/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.ts index 338a434..b6eb9f9 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.ts +++ b/zeppelin-web-angular/src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component.ts @@ -42,7 +42,9 @@ export class NotebookParagraphDynamicFormsComponent implements OnInit, OnChanges @Input() paramDefs: DynamicFormParams; @Input() runOnChange = false; @Input() disable = false; + @Input() removable = false; @Output() readonly formChange = new EventEmitter<void>(); + @Output() readonly formRemove = new EventEmitter<DynamicFormsItem>(); formChange$ = new Subject<void>(); forms: DynamicFormsItem[] = []; @@ -97,6 +99,10 @@ export class NotebookParagraphDynamicFormsComponent implements OnInit, OnChanges } } + remove(item: DynamicFormsItem) { + this.formRemove.emit(item); + } + constructor() {} ngOnInit() { diff --git a/zeppelin-web-angular/src/app/pages/workspace/share/share.module.ts b/zeppelin-web-angular/src/app/pages/workspace/share/share.module.ts index 37bebf3..dfc2c4c 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/share/share.module.ts +++ b/zeppelin-web-angular/src/app/pages/workspace/share/share.module.ts @@ -18,7 +18,9 @@ import { FormsModule } from '@angular/forms'; import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; import { NzDropDownModule } from 'ng-zorro-antd/dropdown'; +import { NzGridModule } from 'ng-zorro-antd/grid'; import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzInputModule } from 'ng-zorro-antd/input'; import { NzRadioModule } from 'ng-zorro-antd/radio'; import { NzResizableModule } from 'ng-zorro-antd/resizable'; import { NzSelectModule } from 'ng-zorro-antd/select'; @@ -28,8 +30,6 @@ import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; import { ShareModule } from '@zeppelin/share'; import { VisualizationModule } from '@zeppelin/visualizations/visualization.module'; -import { NzGridModule } from 'ng-zorro-antd/grid'; -import { NzInputModule } from 'ng-zorro-antd/input'; import { NotebookParagraphDynamicFormsComponent } from './dynamic-forms/dynamic-forms.component'; import { NotebookParagraphResultComponent } from './result/result.component'; @@ -51,8 +51,8 @@ import { NotebookParagraphResultComponent } from './result/result.component'; NzCheckboxModule, NzSelectModule, NzSwitchModule, - NzInputModule, - NzGridModule + NzGridModule, + NzInputModule ] }) export class WorkspaceShareModule {}