Browse Source

Dropdown component.

pull/387/head
Sebastian Stehle 7 years ago
parent
commit
667c848765
  1. 18
      src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html
  2. 12
      src/Squidex/app/features/apps/pages/apps-page.component.html
  3. 6
      src/Squidex/app/features/content/pages/content/content-page.component.html
  4. 4
      src/Squidex/app/features/content/shared/assets-editor.component.html
  5. 33
      src/Squidex/app/features/content/shared/content-item.component.html
  6. 51
      src/Squidex/app/features/content/shared/due-time-selector.component.html
  7. 8
      src/Squidex/app/features/content/shared/preview-button.component.html
  8. 4
      src/Squidex/app/features/content/shared/references-editor.component.html
  9. 6
      src/Squidex/app/features/rules/pages/rules/rules-page.component.html
  10. 77
      src/Squidex/app/features/schemas/pages/schema/field.component.html
  11. 75
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.html
  12. 20
      src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.html
  13. 6
      src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts
  14. 7
      src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.html
  15. 96
      src/Squidex/app/features/settings/pages/clients/client.component.html
  16. 22
      src/Squidex/app/framework/angular/forms/autocomplete.component.html
  17. 22
      src/Squidex/app/framework/angular/forms/color-picker.component.html
  18. 12
      src/Squidex/app/framework/angular/forms/dropdown.component.html
  19. 20
      src/Squidex/app/framework/angular/forms/tag-editor.component.html
  20. 12
      src/Squidex/app/framework/angular/forms/tag-editor.component.ts
  21. 35
      src/Squidex/app/framework/angular/modals/dialog-renderer.component.html
  22. 2
      src/Squidex/app/framework/angular/modals/modal-dialog.component.html
  23. 114
      src/Squidex/app/framework/angular/modals/modal-target.directive.ts
  24. 157
      src/Squidex/app/framework/angular/modals/modal-view.directive.ts
  25. 254
      src/Squidex/app/framework/angular/modals/modal.component.ts
  26. 11
      src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.html
  27. 3
      src/Squidex/app/framework/angular/modals/root-view.component.html
  28. 2
      src/Squidex/app/framework/angular/modals/root-view.component.scss
  29. 7
      src/Squidex/app/framework/angular/modals/root-view.component.ts
  30. 5
      src/Squidex/app/framework/declarations.ts
  31. 15
      src/Squidex/app/framework/module.ts
  32. 2
      src/Squidex/app/framework/services/dialog.service.spec.ts
  33. 13
      src/Squidex/app/shared/components/asset.component.html
  34. 11
      src/Squidex/app/shared/components/language-selector.component.html
  35. 4
      src/Squidex/app/shared/components/markdown-editor.component.html
  36. 4
      src/Squidex/app/shared/components/rich-editor.component.html
  37. 60
      src/Squidex/app/shared/components/search-form.component.html
  38. 57
      src/Squidex/app/shell/pages/internal/apps-menu.component.html
  39. 36
      src/Squidex/app/shell/pages/internal/profile-menu.component.html

18
src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html

@ -59,12 +59,14 @@
</ng-container> </ng-container>
</sqx-panel> </sqx-panel>
<sqx-modal-dialog *sqxModalView="eventConsumerErrorDialog;onRoot:true" (close)="eventConsumerErrorDialog.hide()"> <sqx-modal [model]="eventConsumerErrorDialog">
<ng-container title> <sqx-modal-dialog (close)="eventConsumerErrorDialog.hide()">
Error <ng-container title>
</ng-container> Error
</ng-container>
<ng-container content> <ng-container content>
<textarea readonly class="form-control error-message">{{eventConsumerError}}</textarea> <textarea readonly class="form-control error-message">{{eventConsumerError}}</textarea>
</ng-container> </ng-container>
</sqx-modal-dialog> </sqx-modal-dialog>
</sqx-modal>

12
src/Squidex/app/features/apps/pages/apps-page.component.html

@ -102,20 +102,20 @@
<div *ngIf="info" class="info">{{info}}</div> <div *ngIf="info" class="info">{{info}}</div>
<ng-container *sqxModalView="addAppDialog;onRoot:true"> <sqx-modal [model]="addAppDialog">
<sqx-app-form [template]="addAppTemplate" <sqx-app-form [template]="addAppTemplate"
(complete)="addAppDialog.hide()"> (complete)="addAppDialog.hide()">
</sqx-app-form> </sqx-app-form>
</ng-container> </sqx-modal>
<ng-container *sqxModalView="onboardingDialog;onRoot:true;closeAuto:false"> <sqx-modal [model]="onboardingDialog">
<sqx-onboarding-dialog <sqx-onboarding-dialog
(close)="onboardingDialog.hide()"> (close)="onboardingDialog.hide()">
</sqx-onboarding-dialog> </sqx-onboarding-dialog>
</ng-container> </sqx-modal>
<ng-container *sqxModalView="newsDialog;onRoot:true;closeAuto:false"> <sqx-modal [model]="newsDialog">
<sqx-news-dialog [features]="newsFeatures" <sqx-news-dialog [features]="newsFeatures"
(close)="newsDialog.hide()"> (close)="newsDialog.hide()">
</sqx-news-dialog> </sqx-news-dialog>
</ng-container> </sqx-modal>

6
src/Squidex/app/features/content/pages/content/content-page.component.html

@ -44,8 +44,8 @@
</sqx-content-status> </sqx-content-status>
</button> </button>
<ng-container *ngIf="content.isPending || !schema.isSingleton"> <sqx-modal *ngIf="content.isPending || !schema.isSingleton" [model]="dropdown" [target]="optionsButton" closeAlways="true">
<div class="dropdown-menu" *sqxModalView="dropdown;closeAlways:true" [sqxModalTarget]="optionsButton" @fade> <div class="dropdown-menu" @fade>
<a class="dropdown-item" (click)="discardChanges()" *ngIf="content.canDraftDiscard"> <a class="dropdown-item" (click)="discardChanges()" *ngIf="content.canDraftDiscard">
Discard changes Discard changes
</a> </a>
@ -72,7 +72,7 @@
</a> </a>
</ng-container> </ng-container>
</div> </div>
</ng-container> </sqx-modal>
</div> </div>
<button type="button" class="btn btn-secondary ml-1" (click)="saveAsDraft()" *ngIf="content.canDraftPropose"> <button type="button" class="btn btn-secondary ml-1" (click)="saveAsDraft()" *ngIf="content.canDraftPropose">

4
src/Squidex/app/features/content/shared/assets-editor.component.html

@ -59,8 +59,8 @@
</div> </div>
</div> </div>
<ng-container *sqxModalView="assetsDialog;onRoot:true;closeAuto:false"> <sqx-modal [model]="assetsDialog">
<sqx-assets-selector <sqx-assets-selector
(select)="selectAssets($event)"> (select)="selectAssets($event)">
</sqx-assets-selector> </sqx-assets-selector>
</ng-container> </sqx-modal>

33
src/Squidex/app/features/content/shared/content-item.component.html

@ -55,23 +55,26 @@
<button type="button" class="btn btn-text-secondary" (click)="dropdown.toggle()" [class.active]="dropdown.isOpen | async" #optionsButton> <button type="button" class="btn btn-text-secondary" (click)="dropdown.toggle()" [class.active]="dropdown.isOpen | async" #optionsButton>
<i class="icon-dots"></i> <i class="icon-dots"></i>
</button> </button>
<div class="dropdown-menu" *sqxModalView="dropdown;closeAlways:true" [sqxModalTarget]="optionsButton" @fade>
<a class="dropdown-item" *ngFor="let info of content.statusUpdates" (click)="emitChangeStatus(info.status)">
Change to <i class="icon-circle icon-sm" [style.color]="info.color"></i> {{info.status}}
</a>
<a class="dropdown-item" (click)="emitClone(); dropdown.hide()" *ngIf="canClone">
Clone
</a>
<div class="dropdown-divider"></div> <sqx-modal [model]="dropdown" [target]="optionsButton" closeAlways="true">
<div class="dropdown-menu" @fade>
<a class="dropdown-item" *ngFor="let info of content.statusUpdates" (click)="emitChangeStatus(info.status)">
Change to <i class="icon-circle icon-sm" [style.color]="info.color"></i> {{info.status}}
</a>
<a class="dropdown-item" (click)="emitClone(); dropdown.hide()" *ngIf="canClone">
Clone
</a>
<a class="dropdown-item dropdown-item-delete" <div class="dropdown-divider"></div>
(sqxConfirmClick)="emitDelete()"
confirmTitle="Delete content" <a class="dropdown-item dropdown-item-delete"
confirmText="Do you really want to delete the content?"> (sqxConfirmClick)="emitDelete()"
Delete confirmTitle="Delete content"
</a> confirmText="Do you really want to delete the content?">
</div> Delete
</a>
</div>
</sqx-modal>
</div> </div>
</td> </td>
<td class="cell-actions" *ngIf="isReference" [sqxStopClick]="isDirty"> <td class="cell-actions" *ngIf="isReference" [sqxStopClick]="isDirty">

51
src/Squidex/app/features/content/shared/due-time-selector.component.html

@ -1,28 +1,31 @@
<sqx-modal-dialog *sqxModalView="dueTimeDialog;onRoot:true" (close)="cancelStatusChange()"> <sqx-modal [model]="dueTimeDialog">
<ng-container title> <sqx-modal-dialog (close)="cancelStatusChange()">
{{dueTimeAction}} content item(s) <ng-container title>
</ng-container> {{dueTimeAction}} content item(s)
</ng-container>
<ng-container content> <ng-container content>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" [(ngModel)]="dueTimeMode" value="Immediately" id="immediately" name="dueTimeMode"> <input class="form-check-input" type="radio" [(ngModel)]="dueTimeMode" value="Immediately" id="immediately" name="dueTimeMode">
<label class="form-check-label" for="immediately"> <label class="form-check-label" for="immediately">
{{dueTimeAction}} content item(s) immediately. {{dueTimeAction}} content item(s) immediately.
</label> </label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" [(ngModel)]="dueTimeMode" value="Scheduled" id="scheduled" name="dueTimeMode"> <input class="form-check-input" type="radio" [(ngModel)]="dueTimeMode" value="Scheduled" id="scheduled" name="dueTimeMode">
<label class="form-check-label" for="scheduled"> <label class="form-check-label" for="scheduled">
{{dueTimeAction}} content item(s) at a later point date and time. {{dueTimeAction}} content item(s) at a later point date and time.
</label> </label>
</div> </div>
<sqx-date-time-editor [disabled]="dueTimeMode === 'Immediately'" mode="DateTime" hideClear="true" [(ngModel)]="dueTime"></sqx-date-time-editor> <sqx-date-time-editor [disabled]="dueTimeMode === 'Immediately'" mode="DateTime" hideClear="true" [(ngModel)]="dueTime"></sqx-date-time-editor>
</ng-container> </ng-container>
<ng-container footer> <ng-container footer>
<button type="button" class="float-left btn btn-secondary" (click)="cancelStatusChange()">Cancel</button> <button type="button" class="float-left btn btn-secondary" (click)="cancelStatusChange()">Cancel</button>
<button type="button" class="float-right btn btn-primary" [disabled]="dueTimeMode === 'Scheduled' && !dueTime" (click)="confirmStatusChange()" sqxFocusOnInit>Confirm</button> <button type="button" class="float-right btn btn-primary" [disabled]="dueTimeMode === 'Scheduled' && !dueTime" (click)="confirmStatusChange()" sqxFocusOnInit>Confirm</button>
</ng-container> </ng-container>
</sqx-modal-dialog> </sqx-modal-dialog>
</sqx-modal>

8
src/Squidex/app/features/content/shared/preview-button.component.html

@ -9,9 +9,11 @@
<div class="btn-group" *ngIf="snapshot.alternativeNames.length > 0"> <div class="btn-group" *ngIf="snapshot.alternativeNames.length > 0">
<button type="button" class="btn btn-secondary dropdown-toggle" (click)="dropdown.toggle()"></button> <button type="button" class="btn btn-secondary dropdown-toggle" (click)="dropdown.toggle()"></button>
<div class="dropdown-menu" *sqxModalView="dropdown;closeAlways:true" [sqxModalTarget]="buttonGroup" @fade> <sqx-modal [model]="dropdown" [target]="buttonGroup" closeAlways="true">
<a *ngFor="let name of snapshot.alternativeNames" class="dropdown-item" (click)="follow(name)">{{name}}</a> <div class="dropdown-menu" @fade>
</div> <a *ngFor="let name of snapshot.alternativeNames" class="dropdown-item" (click)="follow(name)">{{name}}</a>
</div>
</sqx-modal>
</div> </div>
</div> </div>
</ng-container> </ng-container>

4
src/Squidex/app/features/content/shared/references-editor.component.html

@ -27,7 +27,7 @@
</div> </div>
</div> </div>
<ng-container *sqxModalView="selectorDialog;onRoot:true;closeAuto:false"> <sqx-modal [model]="selectorDialog">
<sqx-contents-selector <sqx-contents-selector
[allowDuplicates]="allowDuplicates" [allowDuplicates]="allowDuplicates"
[alreadySelected]="snapshot.contentItems" [alreadySelected]="snapshot.contentItems"
@ -36,4 +36,4 @@
[schema]="snapshot.schema" [schema]="snapshot.schema"
(select)="select($event)"> (select)="select($event)">
</sqx-contents-selector> </sqx-contents-selector>
</ng-container> </sqx-modal>

6
src/Squidex/app/features/rules/pages/rules/rules-page.component.html

@ -67,8 +67,8 @@
</tbody> </tbody>
</table> </table>
<ng-container *sqxModalView="addRuleDialog;onRoot:true;closeAuto:false"> <sqx-modal [model]="addRuleDialog">
<sqx-rule-wizard <sqx-rule-wizard
[schemas]="schemasState.schemas | async" [schemas]="schemasState.schemas | async"
[rule]="wizardRule" [rule]="wizardRule"
[ruleActions]="ruleActions" [ruleActions]="ruleActions"
@ -76,7 +76,7 @@
[mode]="wizardMode" [mode]="wizardMode"
(complete)="addRuleDialog.hide()"> (complete)="addRuleDialog.hide()">
</sqx-rule-wizard> </sqx-rule-wizard>
</ng-container> </sqx-modal>
</ng-container> </ng-container>
</ng-container> </ng-container>

77
src/Squidex/app/features/schemas/pages/schema/field.component.html

@ -28,45 +28,48 @@
<button type="button" class="btn btn-text-secondary ml-1" (click)="dropdown.toggle()" [disabled]="!field.properties.isContentField && field.isLocked" [class.active]="dropdown.isOpen | async" #optionsButton> <button type="button" class="btn btn-text-secondary ml-1" (click)="dropdown.toggle()" [disabled]="!field.properties.isContentField && field.isLocked" [class.active]="dropdown.isOpen | async" #optionsButton>
<i class="icon-dots"></i> <i class="icon-dots"></i>
</button> </button>
<div class="dropdown-menu" *sqxModalView="dropdown;closeAlways:true" [sqxModalTarget]="optionsButton" @fade>
<ng-container *ngIf="field.properties.isContentField">
<a class="dropdown-item" (click)="enableField()" *ngIf="field.canEnable">
Enable in UI
</a>
<a class="dropdown-item" (click)="disableField()" *ngIf="field.canDisable">
Disable in UI
</a>
<a class="dropdown-item" (click)="hideField()" *ngIf="field.canHide">
Hide in API
</a>
<a class="dropdown-item" (click)="showField()" *ngIf="field.canShow">
Show in API
</a>
</ng-container>
<ng-container *ngIf="field.canLock"> <sqx-modal [model]="dropdown" [target]="optionsButton" closeAlways="true">
<div class="dropdown-divider"></div> <div class="dropdown-menu" @fade>
<ng-container *ngIf="field.properties.isContentField">
<a class="dropdown-item" (click)="enableField()" *ngIf="field.canEnable">
Enable in UI
</a>
<a class="dropdown-item" (click)="disableField()" *ngIf="field.canDisable">
Disable in UI
</a>
<a class="dropdown-item" (click)="hideField()" *ngIf="field.canHide">
Hide in API
</a>
<a class="dropdown-item" (click)="showField()" *ngIf="field.canShow">
Show in API
</a>
</ng-container>
<a class="dropdown-item" <ng-container *ngIf="field.canLock">
(sqxConfirmClick)="lockField()" <div class="dropdown-divider"></div>
confirmTitle="Lock field"
confirmText="Do you really want to lock the field? Locked fields cannot be deleted or changed anymore.">
Lock and prevent changes
</a>
</ng-container>
<ng-container> <a class="dropdown-item"
<div class="dropdown-divider"></div> (sqxConfirmClick)="lockField()"
confirmTitle="Lock field"
confirmText="Do you really want to lock the field? Locked fields cannot be deleted or changed anymore.">
Lock and prevent changes
</a>
</ng-container>
<a class="dropdown-item dropdown-item-delete" <ng-container>
[class.disabled]="!field.canDelete" <div class="dropdown-divider"></div>
(sqxConfirmClick)="deleteField()"
confirmTitle="Delete field" <a class="dropdown-item dropdown-item-delete"
confirmText="Do you really want to delete the field?"> [class.disabled]="!field.canDelete"
Delete (sqxConfirmClick)="deleteField()"
</a> confirmTitle="Delete field"
</ng-container> confirmText="Do you really want to delete the field?">
</div> Delete
</a>
</ng-container>
</div>
</sqx-modal>
</div> </div>
</div> </div>
</div> </div>
@ -110,11 +113,11 @@
</button> </button>
</div> </div>
<ng-container *sqxModalView="addFieldDialog;onRoot:true;closeAuto:false;closeAlways:true"> <sqx-modal [model]="addFieldDialog">
<sqx-field-wizard [schema]="schema" [parent]="field" <sqx-field-wizard [schema]="schema" [parent]="field"
(complete)="addFieldDialog.hide()"> (complete)="addFieldDialog.hide()">
</sqx-field-wizard> </sqx-field-wizard>
</ng-container> </sqx-modal>
</ng-container> </ng-container>
</div> </div>
</div> </div>

75
src/Squidex/app/features/schemas/pages/schema/schema-page.component.html

@ -23,34 +23,37 @@
<button type="button" class="btn btn-text-secondary ml-1" (click)="editOptionsDropdown.toggle()" [class.active]="editOptionsDropdown.isOpen | async" #buttonOptions> <button type="button" class="btn btn-text-secondary ml-1" (click)="editOptionsDropdown.toggle()" [class.active]="editOptionsDropdown.isOpen | async" #buttonOptions>
<i class="icon-dots"></i> <i class="icon-dots"></i>
</button> </button>
<div class="dropdown-menu" *sqxModalView="editOptionsDropdown;closeAlways:true" [sqxModalTarget]="buttonOptions" @fade>
<a class="dropdown-item" (click)="configureScriptsDialog.show()">
Scripts
</a>
<a class="dropdown-item" (click)="configurePreviewUrlsDialog.show()">
Preview Urls
</a>
<ng-container *ngIf="schemasState.canCreate">
<div class="dropdown-divider"></div>
<a class="dropdown-item" (click)="cloneSchema()">
Clone
</a>
</ng-container>
<ng-container>
<div class="dropdown-divider"></div>
<a class="dropdown-item dropdown-item-delete" <sqx-modal [model]="editOptionsDropdown" [target]="buttonOptions" closeAlways="true">
[class.disabled]="!schema.canDelete" <div class="dropdown-menu" @fade>
(sqxConfirmClick)="deleteSchema()" <a class="dropdown-item" (click)="configureScriptsDialog.show()">
confirmTitle="Delete schema" Scripts
confirmText="Do you really want to delete the schema?">
Delete
</a> </a>
</ng-container> <a class="dropdown-item" (click)="configurePreviewUrlsDialog.show()">
</div> Preview Urls
</a>
<ng-container *ngIf="schemasState.canCreate">
<div class="dropdown-divider"></div>
<a class="dropdown-item" (click)="cloneSchema()">
Clone
</a>
</ng-container>
<ng-container>
<div class="dropdown-divider"></div>
<a class="dropdown-item dropdown-item-delete"
[class.disabled]="!schema.canDelete"
(sqxConfirmClick)="deleteSchema()"
confirmTitle="Delete schema"
confirmText="Do you really want to delete the schema?">
Delete
</a>
</ng-container>
</div>
</sqx-modal>
</div> </div>
<sqx-onboarding-tooltip helpId="history" [for]="buttonOptions" position="bottom-right" after="60000"> <sqx-onboarding-tooltip helpId="history" [for]="buttonOptions" position="bottom-right" after="60000">
@ -98,31 +101,31 @@
<router-outlet></router-outlet> <router-outlet></router-outlet>
<ng-container *sqxModalView="editSchemaDialog;onRoot:true"> <sqx-modal [model]="editSchemaDialog">
<sqx-schema-edit-form [schema]="schema" <sqx-schema-edit-form [schema]="schema"
(complete)="editSchemaDialog.hide()"> (complete)="editSchemaDialog.hide()">
</sqx-schema-edit-form> </sqx-schema-edit-form>
</ng-container> </sqx-modal>
<ng-container *sqxModalView="addFieldDialog;onRoot:true;closeAuto:false"> <sqx-modal [model]="addFieldDialog">
<sqx-field-wizard [schema]="schema" <sqx-field-wizard [schema]="schema"
(complete)="addFieldDialog.hide()"> (complete)="addFieldDialog.hide()">
</sqx-field-wizard> </sqx-field-wizard>
</ng-container> </sqx-modal>
<ng-container *sqxModalView="configureScriptsDialog;onRoot:true"> <sqx-modal [model]="configureScriptsDialog">
<sqx-schema-scripts-form [schema]="schema" <sqx-schema-scripts-form [schema]="schema"
(complete)="configureScriptsDialog.hide()"> (complete)="configureScriptsDialog.hide()">
</sqx-schema-scripts-form> </sqx-schema-scripts-form>
</ng-container> </sqx-modal>
<ng-container *sqxModalView="configurePreviewUrlsDialog;onRoot:true"> <sqx-modal [model]="configurePreviewUrlsDialog">
<sqx-schema-preview-urls-form [schema]="schema" <sqx-schema-preview-urls-form [schema]="schema"
(complete)="configurePreviewUrlsDialog.hide()"> (complete)="configurePreviewUrlsDialog.hide()">
</sqx-schema-preview-urls-form> </sqx-schema-preview-urls-form>
</ng-container> </sqx-modal>
<ng-container *sqxModalView="exportSchemaDialog;onRoot:true"> <sqx-modal [model]="exportSchemaDialog">
<sqx-modal-dialog (close)="exportSchemaDialog.hide()" large="true"> <sqx-modal-dialog (close)="exportSchemaDialog.hide()" large="true">
<ng-container title> <ng-container title>
Export Schema Export Schema
@ -134,4 +137,4 @@
</div> </div>
</ng-container> </ng-container>
</sqx-modal-dialog> </sqx-modal-dialog>
</ng-container> </sqx-modal>

20
src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.html

@ -38,20 +38,24 @@
<label class="col-3 col-form-label" for="{{field.fieldId}}_fieldPattern">Pattern</label> <label class="col-3 col-form-label" for="{{field.fieldId}}_fieldPattern">Pattern</label>
<div class="col-6"> <div class="col-6">
<input type="text" class="form-control" id="{{field.fieldId}}_fieldPattern" formControlName="pattern" placeholder="Regex Pattern" #patternInput <input type="text" class="form-control" id="{{field.fieldId}}_fieldPattern" formControlName="pattern" placeholder="Regex Pattern" #inputPattern
autocomplete="off" autocomplete="off"
autocorrect="off" autocorrect="off"
autocapitalize="off" autocapitalize="off"
(focus)="patternsModal.show()" (blur)="patternsModal.hide()" /> (focus)="patternsModal.show()" (blur)="patternsModal.hide()" />
<div *ngIf="patterns.length > 0 && (patternsModal.isOpen | async) && (showPatternSuggestions | async)" [sqxModalTarget]="patternInput" class="control-dropdown"> <ng-container *ngIf="patterns.length > 0 && (showPatternSuggestions | async)">
<h4>Suggestions</h4> <sqx-modal [model]="patternsModal" [target]="inputPattern" position="bottom-left">
<div class="control-dropdown" @fade>
<h4>Suggestions</h4>
<div *ngFor="let pattern of patterns" class="control-dropdown-item control-dropdown-item-selectable" (mousedown)="setPattern(pattern)"> <div *ngFor="let pattern of patterns" class="control-dropdown-item control-dropdown-item-selectable" (mousedown)="setPattern(pattern)">
<div class="truncate">{{pattern.name}}</div> <div class="truncate">{{pattern.name}}</div>
<div class="truncate text-muted">{{pattern.pattern}}</div> <div class="truncate text-muted">{{pattern.pattern}}</div>
</div> </div>
</div> </div>
</sqx-modal>
</ng-container>
</div> </div>
<small class="col-3" style="align-self: center"> <small class="col-3" style="align-self: center">
{{patternName}} {{patternName}}

6
src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts

@ -10,6 +10,7 @@ import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { import {
fadeAnimation,
FieldDto, FieldDto,
hasNoValue$, hasNoValue$,
ImmutableArray, ImmutableArray,
@ -24,7 +25,10 @@ import {
@Component({ @Component({
selector: 'sqx-string-validation', selector: 'sqx-string-validation',
styleUrls: ['string-validation.component.scss'], styleUrls: ['string-validation.component.scss'],
templateUrl: 'string-validation.component.html' templateUrl: 'string-validation.component.html',
animations: [
fadeAnimation
]
}) })
export class StringValidationComponent extends ResourceOwner implements OnInit { export class StringValidationComponent extends ResourceOwner implements OnInit {
@Input() @Input()

7
src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.html

@ -38,11 +38,10 @@
</ng-container> </ng-container>
</sqx-panel> </sqx-panel>
<ng-container *sqxModalView="addSchemaDialog;onRoot:true"> <sqx-modal [model]="addSchemaDialog">
<sqx-schema-form [import]="import" <sqx-schema-form [import]="import"
(cancel)="addSchemaDialog.hide()" (cancel)="addSchemaDialog.hide()" (complete)="redirectSchema($event)">
(complete)="redirectSchema($event)">
</sqx-schema-form> </sqx-schema-form>
</ng-container> </sqx-modal>
<router-outlet></router-outlet> <router-outlet></router-outlet>

96
src/Squidex/app/features/settings/pages/clients/client.component.html

@ -70,70 +70,72 @@
</div> </div>
</div> </div>
<sqx-modal-dialog *sqxModalView="connectDialog;onRoot:true" large="true" (close)="connectDialog.hide()"> <sqx-modal [model]="connectDialog">
<ng-container title> <sqx-modal-dialog large="true" (close)="connectDialog.hide()">
Connect <ng-container title>
</ng-container> Connect
</ng-container>
<ng-container content> <ng-container content>
<div class="help"> <div class="help">
<h2>How to connect to this client</h2> <h2>How to connect to this client</h2>
<h3>Using HTTP</h3> <h3>Using HTTP</h3>
<p> <p>
1. Make the following request to get an access token. It will be valid for 30 days. 1. Make the following request to get an access token. It will be valid for 30 days.
<sqx-code>{{connectHttpText}}</sqx-code> <sqx-code>{{connectHttpText}}</sqx-code>
</p> </p>
<p> <p>
2. Add the bearer token as authorization header to all requests: 2. Add the bearer token as authorization header to all requests:
<sqx-code>Authorization: Bearer [YOUR_TOKEN]</sqx-code> <sqx-code>Authorization: Bearer [YOUR_TOKEN]</sqx-code>
</p> </p>
<p> <p>
Use the following token for testing Use the following token for testing
<sqx-code>{{connectToken?.accessToken}}</sqx-code> <sqx-code>{{connectToken?.accessToken}}</sqx-code>
</p> </p>
<h3>Using the command line interface (CLI)</h3> <h3>Using the command line interface (CLI)</h3>
<p> <p>
Download the CLI here: <a href="https://github.com/Squidex/squidex-samples/releases" sqxExternalLink>CLI Releases (Linux, OS X, Windows)</a> Download the CLI here: <a href="https://github.com/Squidex/squidex-samples/releases" sqxExternalLink>CLI Releases (Linux, OS X, Windows)</a>
</p> </p>
<p> <p>
Connect with .NET SDK: Connect with .NET SDK:
<sqx-code>{{connectCLINetText}}</sqx-code> <sqx-code>{{connectCLINetText}}</sqx-code>
</p> </p>
<p> <p>
Connect with Windows: Connect with Windows:
<sqx-code>{{connectCLIWinText}}</sqx-code> <sqx-code>{{connectCLIWinText}}</sqx-code>
</p> </p>
<p> <p>
Connect with Linux / OS X Connect with Linux / OS X
<sqx-code>{{connectCLINixText}}</sqx-code> <sqx-code>{{connectCLINixText}}</sqx-code>
</p> </p>
<h3>Using the C# Client Library</h3> <h3>Using the C# Client Library</h3>
<p> <p>
Get the nuget library from <a href="https://www.nuget.org/packages/Squidex.ClientLibrary/" sqxExternalLink>nuget.org</a> Get the nuget library from <a href="https://www.nuget.org/packages/Squidex.ClientLibrary/" sqxExternalLink>nuget.org</a>
</p> </p>
<p> <p>
Create a client manager Create a client manager
<sqx-code>{{connectLibraryText}}</sqx-code> <sqx-code>{{connectLibraryText}}</sqx-code>
</p> </p>
</div> </div>
</ng-container> </ng-container>
</sqx-modal-dialog> </sqx-modal-dialog>
</sqx-modal>

22
src/Squidex/app/framework/angular/forms/autocomplete.component.html

@ -5,17 +5,19 @@
autocorrect="off" autocorrect="off"
autocapitalize="off"> autocapitalize="off">
<div *ngIf="snapshot.suggestedItems.length > 0" [sqxModalTarget]="input" class="control-dropdown" #container position="bottom-left"> <sqx-modal [model]="snapshot.suggestedItems.length > 0" [target]="input" position="bottom-left">
<div *ngFor="let item of snapshot.suggestedItems; let i = index" class="control-dropdown-item control-dropdown-item-selectable" <div class="control-dropdown" #container>
[class.active]="i === snapshot.suggestedIndex" <div *ngFor="let item of snapshot.suggestedItems; let i = index" class="control-dropdown-item control-dropdown-item-selectable"
[container]="container" [class.active]="i === snapshot.suggestedIndex"
(mousedown)="selectItem(item)" [container]="container"
(mouseover)="selectIndex(i)" (mousedown)="selectItem(item)"
[sqxScrollActive]="i === snapshot.suggestedIndex"> (mouseover)="selectIndex(i)"
[sqxScrollActive]="i === snapshot.suggestedIndex">
<ng-container *ngIf="!itemTemplate">{{item}}</ng-container> <ng-container *ngIf="!itemTemplate">{{item}}</ng-container>
<ng-template *ngIf="itemTemplate" [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template> <ng-template *ngIf="itemTemplate" [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template>
</div>
</div> </div>
</div> </sqx-modal>
</span> </span>

22
src/Squidex/app/framework/angular/forms/color-picker.component.html

@ -14,14 +14,16 @@
</ng-template> </ng-template>
</span> </span>
<div *sqxModalView="modal" [sqxModalTarget]="input" position="bottom-left" <sqx-modal [model]="modal" [target]="input" closeAlways="true" position="bottom-left">
[style.height]="'228px'" <div
[style.width]="'130px'"> [style.height]="'228px'"
<div [style.background]="snapshot.value" [style.width]="'130px'">
[cpToggle]="true" <div [style.background]="snapshot.value"
[cpDialogDisplay]="'inline'" [cpToggle]="true"
[cpCancelButton]="false" [cpDialogDisplay]="'inline'"
[colorPicker]="snapshot.value" [cpCancelButton]="false"
(colorPickerChange)="updateValue($event)"> [colorPicker]="snapshot.value"
(colorPickerChange)="updateValue($event)">
</div>
</div> </div>
</div> </sqx-modal>

12
src/Squidex/app/framework/angular/forms/dropdown.component.html

@ -15,12 +15,14 @@
</div> </div>
<div class="items-container"> <div class="items-container">
<div class="control-dropdown" #container *sqxModalView="dropdown" [sqxModalTarget]="input" position="bottom-left"> <sqx-modal [model]="dropdown" [target]="input" position="bottom-left">
<div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === snapshot.selectedIndex" (mousedown)="selectIndexAndClose(i)" [sqxScrollActive]="i === snapshot.selectedIndex" [container]="container"> <div class="control-dropdown" #container>
<ng-container *ngIf="!templateItem">{{item}}</ng-container> <div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === snapshot.selectedIndex" (mousedown)="selectIndexAndClose(i)" [sqxScrollActive]="i === snapshot.selectedIndex" [container]="container">
<ng-container *ngIf="!templateItem">{{item}}</ng-container>
<ng-template *ngIf="templateItem" [sqxTemplateWrapper]="templateItem" [item]="item" [index]="i"></ng-template> <ng-template *ngIf="templateItem" [sqxTemplateWrapper]="templateItem" [item]="item" [index]="i"></ng-template>
</div>
</div> </div>
</div> </sqx-modal>
</div> </div>
</span> </span>

20
src/Squidex/app/framework/angular/forms/tag-editor.component.html

@ -22,13 +22,15 @@
spellcheck="false"> spellcheck="false">
</div> </div>
<div *ngIf="snapshot.suggestedItems.length > 0" [sqxModalTarget]="form" class="control-dropdown" #container position="bottom-left"> <sqx-modal [model]="snapshot.suggestedItems.length > 0" [target]="form">
<div *ngFor="let item of snapshot.suggestedItems; let i = index" class="control-dropdown-item control-dropdown-item-selectable" <div class="control-dropdown" #container @fade>
[class.active]="i === snapshot.suggestedIndex" <div *ngFor="let item of snapshot.suggestedItems; let i = index" class="control-dropdown-item control-dropdown-item-selectable"
[container]="container" [class.active]="i === snapshot.suggestedIndex"
(mousedown)="selectValue(item)" [container]="container"
(mouseover)="selectIndex(i)" (mousedown)="selectValue(item)"
[sqxScrollActive]="i === snapshot.suggestedIndex"> (mouseover)="selectIndex(i)"
<ng-container>{{item}}</ng-container> [sqxScrollActive]="i === snapshot.suggestedIndex">
<ng-container>{{item}}</ng-container>
</div>
</div> </div>
</div> </sqx-modal>

12
src/Squidex/app/framework/angular/forms/tag-editor.component.ts

@ -11,7 +11,12 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, E
import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { distinctUntilChanged, map, tap } from 'rxjs/operators'; import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import { Keys, StatefulControlComponent, Types } from '@app/framework/internal'; import {
fadeAnimation,
Keys,
StatefulControlComponent,
Types
} from '@app/framework/internal';
export const CONVERSION_FAILED = {}; export const CONVERSION_FAILED = {};
@ -135,7 +140,10 @@ interface State {
styleUrls: ['./tag-editor.component.scss'], styleUrls: ['./tag-editor.component.scss'],
templateUrl: './tag-editor.component.html', templateUrl: './tag-editor.component.html',
providers: [SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR], providers: [SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush,
animations: [
fadeAnimation
]
}) })
export class TagEditorComponent extends StatefulControlComponent<State, any[]> implements AfterViewInit, OnInit { export class TagEditorComponent extends StatefulControlComponent<State, any[]> implements AfterViewInit, OnInit {
@ViewChild('form', { static: false }) @ViewChild('form', { static: false })

35
src/Squidex/app/framework/angular/modals/dialog-renderer.component.html

@ -1,19 +1,22 @@
<ng-content></ng-content> <ng-content></ng-content>
<sqx-modal-dialog *sqxModalView="dialogView;onRoot:true" showClose="false" (close)="cancel()">
<ng-container title>
{{snapshot.dialogRequest?.title}}
</ng-container>
<ng-container content> <sqx-modal [model]="dialogView">
{{snapshot.dialogRequest?.text}} <sqx-modal-dialog showClose="false" (close)="cancel()">
</ng-container> <ng-container title>
{{snapshot.dialogRequest?.title}}
</ng-container>
<ng-container footer> <ng-container content>
<button type="button" class="float-left btn btn-secondary" (click)="cancel()">No</button> {{snapshot.dialogRequest?.text}}
<button type="button" class="float-right btn btn-danger" (click)="confirm()" sqxFocusOnInit>Yes</button> </ng-container>
</ng-container>
</sqx-modal-dialog> <ng-container footer>
<button type="button" class="float-left btn btn-secondary" (click)="cancel()">No</button>
<button type="button" class="float-right btn btn-danger" (click)="confirm()" sqxFocusOnInit>Yes</button>
</ng-container>
</sqx-modal-dialog>
</sqx-modal>
<div class="notification-container notification-container-bottom-right"> <div class="notification-container notification-container-bottom-right">
<div class="alert alert-dismissible alert-{{notification.messageType}}" *ngFor="let notification of snapshot.notifications" (click)="close(notification)" @fade> <div class="alert alert-dismissible alert-{{notification.messageType}}" *ngFor="let notification of snapshot.notifications" (click)="close(notification)" @fade>
@ -24,7 +27,9 @@
</div> </div>
<ng-container *ngIf="snapshot.tooltip; let tooltip"> <ng-container *ngIf="snapshot.tooltip; let tooltip">
<div class="tooltip2 tooltip2-{{tooltip.position}}" [sqxModalTarget]="tooltip.target" [position]="tooltip.position" [offset]="8"> <sqx-modal [model]="true" [target]="tooltip.target" [position]="tooltip.position" [offset]="6">
{{tooltip.text}} <div class="tooltip2 tooltip2-{{tooltip.position}}">
</div> {{tooltip.text}}
</div>
</sqx-modal>
</ng-container> </ng-container>

2
src/Squidex/app/framework/angular/modals/modal-dialog.component.html

@ -1,6 +1,4 @@
<div class="modal" @fade> <div class="modal" @fade>
<div class="modal-backdrop show"></div>
<div class="modal-dialog" [class.modal-lg]="large" [class.modal-fh]="fullHeight"> <div class="modal-dialog" [class.modal-lg]="large" [class.modal-fh]="fullHeight">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header" *ngIf="showHeader"> <div class="modal-header" *ngIf="showHeader">

114
src/Squidex/app/framework/angular/modals/modal-target.directive.ts

@ -1,114 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AfterViewInit, Directive, ElementRef, Input, OnDestroy, Renderer2 } from '@angular/core';
import { timer } from 'rxjs';
import { positionModal, ResourceOwner } from '@app/framework/internal';
@Directive({
selector: '[sqxModalTarget]'
})
export class ModalTargetDirective extends ResourceOwner implements AfterViewInit, OnDestroy {
private targetElement: Element;
@Input('sqxModalTarget')
public set target(element: Element) {
if (element !== this.targetElement) {
this.unsubscribeAll();
this.targetElement = element;
if (element) {
this.subscribe(element);
this.updatePosition();
}
}
}
@Input()
public offset = 2;
@Input()
public position = 'bottom-right';
@Input()
public autoPosition = true;
constructor(
private readonly renderer: Renderer2,
private readonly element: ElementRef<Element>
) {
super();
}
private subscribe(element: any) {
this.own(
this.renderer.listen(element, 'resize', () => {
this.updatePosition();
}));
this.own(
this.renderer.listen(this.element.nativeElement, 'resize', () => {
this.updatePosition();
}));
this.own(timer(100, 100).subscribe(() => this.updatePosition()));
}
public ngAfterViewInit() {
const modalRef = this.element.nativeElement;
this.renderer.setStyle(modalRef, 'position', 'fixed');
this.renderer.setStyle(modalRef, 'z-index', '1000000');
this.updatePosition();
}
private updatePosition() {
if (!this.targetElement) {
return;
}
const modalRef = this.element.nativeElement;
const modalRect = this.element.nativeElement.getBoundingClientRect();
if (modalRect.width === 0 || modalRect.height === 0) {
return;
}
const targetRect = this.targetElement.getBoundingClientRect();
let y = 0;
let x = 0;
if (this.position === 'full') {
x = -this.offset + targetRect.left;
y = -this.offset + targetRect.top;
const w = 2 * this.offset + targetRect.width;
const h = 2 * this.offset + targetRect.height;
this.renderer.setStyle(modalRef, 'width', `${w}px`);
this.renderer.setStyle(modalRef, 'height', `${h}px`);
} else {
const viewH = document.documentElement!.clientHeight;
const viewW = document.documentElement!.clientWidth;
const position = positionModal(targetRect, modalRect, this.position, this.offset, this.autoPosition, viewW, viewH);
x = position.x;
y = position.y;
}
this.renderer.setStyle(modalRef, 'top', `${y}px`);
this.renderer.setStyle(modalRef, 'left', `${x}px`);
this.renderer.setStyle(modalRef, 'right', 'auto');
this.renderer.setStyle(modalRef, 'bottom', 'auto');
this.renderer.setStyle(modalRef, 'margin', '0');
}
}

157
src/Squidex/app/framework/angular/modals/modal-view.directive.ts

@ -1,157 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectorRef, Directive, EmbeddedViewRef, Input, OnChanges, OnDestroy, Renderer2, SimpleChanges, TemplateRef, ViewContainerRef } from '@angular/core';
import { Subscription } from 'rxjs';
import {
DialogModel,
ModalModel,
Types
} from '@app/framework/internal';
import { RootViewComponent } from './root-view.component';
@Directive({
selector: '[sqxModalView]'
})
export class ModalViewDirective implements OnChanges, OnDestroy {
private modalSubscription: Subscription | null = null;
private renderedView: EmbeddedViewRef<any> | null = null;
@Input('sqxModalView')
public modalView: DialogModel | ModalModel | any;
@Input('sqxModalViewOnRoot')
public placeOnRoot = false;
@Input('sqxModalViewCloseAuto')
public closeAuto = true;
@Input('sqxModalViewCloseAlways')
public closeAlways = false;
constructor(
private readonly changeDetector: ChangeDetectorRef,
private readonly renderer: Renderer2,
private readonly rootView: RootViewComponent,
private readonly templateRef: TemplateRef<any>,
private readonly viewContainer: ViewContainerRef
) {
}
public ngOnDestroy() {
this.unsubscribeToModal();
this.unsubscribeToClick();
if (Types.is(this.modalView, DialogModel) || Types.is(this.modalView, ModalModel)) {
this.modalView.hide();
}
}
public ngOnChanges(changes: SimpleChanges) {
if (!changes['modalView']) {
return;
}
this.unsubscribeToModal();
if (Types.is(this.modalView, DialogModel) || Types.is(this.modalView, ModalModel)) {
this.modalSubscription =
this.modalView.isOpen.subscribe(isOpen => {
this.update(isOpen);
});
} else {
this.update(!!this.modalView);
}
}
private update(isOpen: boolean) {
if (isOpen === (!!this.renderedView)) {
return;
}
this.unsubscribeToClick();
if (isOpen && !this.renderedView) {
const container = this.getContainer();
this.renderedView = container.createEmbeddedView(this.templateRef);
if (this.renderedView.rootNodes[0].style) {
this.renderer.setStyle(this.renderedView.rootNodes[0], 'display', 'block');
}
this.startListening();
this.changeDetector.detectChanges();
} else if (!isOpen && this.renderedView) {
const container = this.getContainer();
const containerIndex = container.indexOf(this.renderedView);
container.remove(containerIndex);
this.renderedView = null;
this.changeDetector.detectChanges();
}
}
private getContainer() {
return this.placeOnRoot ? this.rootView.viewContainer : this.viewContainer;
}
private startListening() {
if (this.closeAuto) {
document.addEventListener('click', this.documentClickListener, true);
}
}
private documentClickListener = (event: MouseEvent) => {
if (!event.target || this.renderedView === null) {
return;
}
if (this.renderedView.rootNodes.length === 0) {
return;
}
if (this.closeAlways) {
const modal = this.modalView;
setTimeout(() => {
modal.hide();
}, 100);
} else {
try {
const rootNode = this.renderedView.rootNodes[0];
const rootBounds = rootNode.getBoundingClientRect();
if (rootBounds.width > 0 && rootBounds.height > 0) {
const clickedInside = rootNode.contains(event.target);
if (!clickedInside && this.modalView) {
this.modalView.hide();
}
}
} catch (ex) {
return;
}
}
}
private unsubscribeToModal() {
if (this.modalSubscription) {
this.modalSubscription.unsubscribe();
this.modalSubscription = null;
}
}
private unsubscribeToClick() {
document.removeEventListener('click', this.documentClickListener);
}
}

254
src/Squidex/app/framework/angular/modals/modal.component.ts

@ -0,0 +1,254 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AfterViewInit, ChangeDetectorRef, Component, EmbeddedViewRef, Input, OnDestroy, Renderer2, TemplateRef, ViewChild } from '@angular/core';
import { timer } from 'rxjs';
import {
DialogModel,
ModalModel,
positionModal,
ResourceOwner,
Types
} from '@app/framework/internal';
import { RootViewComponent } from './root-view.component';
declare type Model = DialogModel | ModalModel | any;
@Component({
selector: 'sqx-modal',
template: `
<ng-template #templatePortalContent>
<ng-content></ng-content>
</ng-template>
`
})
export class ModalComponent implements AfterViewInit, OnDestroy {
private readonly eventsView = new ResourceOwner();
private readonly eventsModel = new ResourceOwner();
private currentTarget: Element | null = null;
private currentModel: DialogModel | ModalModel | null = null;
private renderedView: EmbeddedViewRef<any> | null = null;
private renderRoot: HTMLElement | null = null;
private isOpen: boolean;
@Input()
public target: Element;
@Input()
public set model(value: Model) {
if (this.currentModel !== value) {
this.currentModel = value;
this.eventsModel.unsubscribeAll();
this.subscribeToModel(value);
}
}
@Input()
public offset = 2;
@Input()
public position = 'bottom-right';
@Input()
public autoPosition = true;
@Input()
public backdrop = true;
@Input()
public closeAuto = true;
@Input()
public closeAlways = false;
@ViewChild('templatePortalContent', { static: false })
public templateRef: TemplateRef<any>;
constructor(
private readonly changeDetector: ChangeDetectorRef,
private readonly renderer: Renderer2,
private readonly rootView: RootViewComponent
) {
}
public ngAfterViewInit() {
this.update(this.isOpen);
}
public ngOnDestroy() {
hideModal(this.currentModel);
this.eventsView.unsubscribeAll();
this.eventsModel.unsubscribeAll();
}
public onClick() {
if (this.closeAlways) {
this.model.hide();
}
}
private update(isOpen: boolean) {
if (!this.templateRef || this.isOpen === isOpen) {
return;
}
this.eventsView.unsubscribeAll();
if (isOpen) {
if (!this.renderedView) {
this.currentTarget = this.target;
this.renderedView = this.rootView.viewContainer.createEmbeddedView(this.templateRef);
this.renderRoot = this.renderedView.rootNodes[0];
this.setupStyles();
this.subscribeToView();
this.changeDetector.detectChanges();
}
} else {
if (this.renderedView) {
this.renderedView.destroy();
this.renderedView = null;
this.renderRoot = null;
this.changeDetector.detectChanges();
}
}
this.isOpen = isOpen;
}
private setupStyles() {
this.renderer.setStyle(this.renderRoot, 'display', 'block');
this.renderer.setStyle(this.renderRoot, 'right', 'auto');
this.renderer.setStyle(this.renderRoot, 'bottom', 'auto');
this.renderer.setStyle(this.renderRoot, 'margin', '0');
this.renderer.setStyle(this.renderRoot, 'position', 'fixed');
this.renderer.setStyle(this.renderRoot, 'z-index', '1000000');
}
private subscribeToModel(value: Model) {
if (isModalModel(value)) {
this.currentModel = value;
this.eventsModel.own(value.isOpen.subscribe(update => {
this.update(update);
}));
} else {
this.update(value === true);
}
}
private subscribeToView() {
if (this.renderRoot) {
this.eventsView.own(this.renderer.listen(this.renderRoot, 'resize', () => {
this.updatePosition();
}));
if (this.currentTarget) {
this.eventsView.own(this.renderer.listen(this.currentTarget, 'resize', () => {
this.updatePosition();
}));
this.eventsView.own(timer(100, 100).subscribe(() => {
this.updatePosition();
}));
}
}
if (this.closeAuto) {
document.addEventListener('click', this.documentClickListener, true);
this.eventsView.own(() => {
document.removeEventListener('click', this.documentClickListener);
});
}
}
private documentClickListener = (event: MouseEvent) => {
if (!event.target || this.renderRoot === null) {
return;
}
const model = this.currentModel;
if (this.closeAlways) {
setTimeout(() => {
hideModal(model);
}, 100);
} else {
try {
const rootBounds = this.renderRoot.getBoundingClientRect();
if (rootBounds.width > 0 && rootBounds.height > 0) {
const clickedInside = this.renderRoot.contains(<Node>event.target);
if (!clickedInside && this.model) {
this.model.hide();
}
}
} catch (ex) {
return;
}
}
}
private updatePosition() {
if (!this.renderRoot || !this.currentTarget) {
return;
}
const modalRect = this.renderRoot.getBoundingClientRect();
if ((modalRect.width === 0 || modalRect.height === 0) && this.position !== 'full') {
return;
}
const targetRect = this.currentTarget.getBoundingClientRect();
let y = 0;
let x = 0;
if (this.position === 'full') {
x = -this.offset + targetRect.left;
y = -this.offset + targetRect.top;
const w = 2 * this.offset + targetRect.width;
const h = 2 * this.offset + targetRect.height;
this.renderer.setStyle(this.renderRoot, 'width', `${w}px`);
this.renderer.setStyle(this.renderRoot, 'height', `${h}px`);
} else {
const viewH = document.documentElement!.clientHeight;
const viewW = document.documentElement!.clientWidth;
const position = positionModal(targetRect, modalRect, this.position, this.offset, this.autoPosition, viewW, viewH);
x = position.x;
y = position.y;
}
this.renderer.setStyle(this.renderRoot, 'top', `${y}px`);
this.renderer.setStyle(this.renderRoot, 'left', `${x}px`);
}
}
function hideModal(model: Model) {
if (model && isModalModel(model)) {
model.hide();
}
}
function isModalModel(model: Model): model is DialogModel | ModalModel {
return Types.is(model, DialogModel) || Types.is(model, ModalModel);
}

11
src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.html

@ -1,6 +1,9 @@
<div *sqxModalView="tooltipModal;onRoot:true" @fade> <sqx-modal [model]="tooltipModal" [target]="for" [offset]="4" position="full">
<div class="onboarding-rect" [sqxModalTarget]="for" [offset]="4" [autoPosition]="false" position="full"></div> <div class="onboarding-rect"></div>
<div class="onboarding-help" [sqxModalTarget]="for" [offset]="4" [autoPosition]="false" [position]="position"> </sqx-modal>
<sqx-modal [model]="tooltipModal" [target]="for" [offset]="4">
<div class="onboarding-help" @fade>
<div class="onboarding-text"> <div class="onboarding-text">
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>
@ -15,4 +18,4 @@
</button> </button>
</div> </div>
</div> </div>
</div> </sqx-modal>

3
src/Squidex/app/framework/angular/modals/root-view.component.html

@ -1,3 +0,0 @@
<div #element></div>
<ng-content></ng-content>

2
src/Squidex/app/framework/angular/modals/root-view.component.scss

@ -1,2 +0,0 @@
@import '_mixins';
@import '_vars';

7
src/Squidex/app/framework/angular/modals/root-view.component.ts

@ -9,8 +9,11 @@ import { ChangeDetectionStrategy, Component, ViewChild, ViewContainerRef } from
@Component({ @Component({
selector: 'sqx-root-view', selector: 'sqx-root-view',
styleUrls: ['./root-view.component.scss'], template: `
templateUrl: './root-view.component.html', <div #element></div>
<ng-content></ng-content>
`,
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class RootViewComponent { export class RootViewComponent {

5
src/Squidex/app/framework/declarations.ts

@ -36,12 +36,11 @@ export * from './angular/http/loading.interceptor';
export * from './angular/http/http-extensions'; export * from './angular/http/http-extensions';
export * from './angular/modals/dialog-renderer.component'; export * from './angular/modals/dialog-renderer.component';
export * from './angular/modals/modal.component';
export * from './angular/modals/modal-dialog.component'; export * from './angular/modals/modal-dialog.component';
export * from './angular/modals/modal-target.directive';
export * from './angular/modals/modal-view.directive';
export * from './angular/modals/onboarding-tooltip.component'; export * from './angular/modals/onboarding-tooltip.component';
export * from './angular/modals/tooltip.directive';
export * from './angular/modals/root-view.component'; export * from './angular/modals/root-view.component';
export * from './angular/modals/tooltip.directive';
export * from './angular/pipes/colors.pipes'; export * from './angular/pipes/colors.pipes';
export * from './angular/pipes/date-time.pipes'; export * from './angular/pipes/date-time.pipes';

15
src/Squidex/app/framework/module.ts

@ -58,9 +58,8 @@ import {
LoadingService, LoadingService,
LocalStoreService, LocalStoreService,
MessageBus, MessageBus,
ModalComponent,
ModalDialogComponent, ModalDialogComponent,
ModalTargetDirective,
ModalViewDirective,
MoneyPipe, MoneyPipe,
MonthPipe, MonthPipe,
OnboardingService, OnboardingService,
@ -93,10 +92,10 @@ import {
@NgModule({ @NgModule({
imports: [ imports: [
FormsModule, ColorPickerModule,
CommonModule, CommonModule,
ReactiveFormsModule, FormsModule,
ColorPickerModule ReactiveFormsModule
], ],
declarations: [ declarations: [
AutocompleteComponent, AutocompleteComponent,
@ -136,9 +135,8 @@ import {
KeysPipe, KeysPipe,
KNumberPipe, KNumberPipe,
LightenPipe, LightenPipe,
ModalComponent,
ModalDialogComponent, ModalDialogComponent,
ModalTargetDirective,
ModalViewDirective,
MoneyPipe, MoneyPipe,
MonthPipe, MonthPipe,
OnboardingTooltipComponent, OnboardingTooltipComponent,
@ -204,9 +202,8 @@ import {
KeysPipe, KeysPipe,
KNumberPipe, KNumberPipe,
LightenPipe, LightenPipe,
ModalComponent,
ModalDialogComponent, ModalDialogComponent,
ModalTargetDirective,
ModalViewDirective,
MoneyPipe, MoneyPipe,
MonthPipe, MonthPipe,
OnboardingTooltipComponent, OnboardingTooltipComponent,

2
src/Squidex/app/framework/services/dialog.service.spec.ts

@ -70,7 +70,7 @@ describe('DialogService', () => {
it('should publish tooltip', () => { it('should publish tooltip', () => {
const dialogService = new DialogService(); const dialogService = new DialogService();
const tooltip = new Tooltip('target', 'text', 'topLeft'); const tooltip = new Tooltip('target', 'text', 'left');
let publishedTooltip: Tooltip; let publishedTooltip: Tooltip;

13
src/Squidex/app/shared/components/asset.component.html

@ -137,9 +137,12 @@
</ng-template> </ng-template>
<ng-container *ngIf="asset"> <ng-container *ngIf="asset">
<sqx-asset-dialog *sqxModalView="editDialog;onRoot:true" <sqx-modal [model]="editDialog">
[asset]="asset" [allTags]="allTags" <sqx-asset-dialog
(cancel)="cancelEdit()" [allTags]="allTags"
(complete)="updateAsset($event, true)"> [asset]="asset"
</sqx-asset-dialog> (cancel)="cancelEdit()"
(complete)="updateAsset($event, true)">
</sqx-asset-dialog>
</sqx-modal>
</ng-container> </ng-container>

11
src/Squidex/app/shared/components/language-selector.component.html

@ -8,9 +8,12 @@
<button type="button" class="btn btn-secondary dropdown-toggle" title="{{selectedLanguage.englishName}}" (click)="dropdown.toggle()" #button tabindex="-1"> <button type="button" class="btn btn-secondary dropdown-toggle" title="{{selectedLanguage.englishName}}" (click)="dropdown.toggle()" #button tabindex="-1">
{{selectedLanguage.iso2Code}} {{selectedLanguage.iso2Code}}
</button> </button>
<div class="dropdown-menu" *sqxModalView="dropdown;closeAlways:true" [sqxModalTarget]="button" @fade>
<div class="dropdown-item" *ngFor="let language of languages; trackBy: trackByLanguage" [class.active]="language == selectedLanguage" (click)="selectLanguage(language)"> <sqx-modal [model]="dropdown" [target]="button" closeAlways="true">
<strong class="iso-code iso-code-dropdown">{{language.iso2Code}}</strong> ({{language.englishName}}) <div class="dropdown-menu" @fade>
<div class="dropdown-item" *ngFor="let language of languages; trackBy: trackByLanguage" [class.active]="language == selectedLanguage" (click)="selectLanguage(language)">
<strong class="iso-code iso-code-dropdown">{{language.iso2Code}}</strong> ({{language.englishName}})
</div>
</div> </div>
</div> </sqx-modal>
</div> </div>

4
src/Squidex/app/shared/components/markdown-editor.component.html

@ -4,8 +4,8 @@
</div> </div>
</div> </div>
<ng-container *sqxModalView="assetsDialog;onRoot:true;closeAuto:false"> <sqx-modal [model]="assetsDialog">
<sqx-assets-selector <sqx-assets-selector
(select)="insertAssets($event)"> (select)="insertAssets($event)">
</sqx-assets-selector> </sqx-assets-selector>
</ng-container> </sqx-modal>

4
src/Squidex/app/shared/components/rich-editor.component.html

@ -2,8 +2,8 @@
<div class="editor" #editor>Loading editor...</div> <div class="editor" #editor>Loading editor...</div>
</div> </div>
<ng-container *sqxModalView="assetsDialog;onRoot:true;closeAuto:false"> <sqx-modal [model]="assetsDialog">
<sqx-assets-selector <sqx-assets-selector
(select)="insertAssets($event)"> (select)="insertAssets($event)">
</sqx-assets-selector> </sqx-assets-selector>
</ng-container> </sqx-modal>

60
src/Squidex/app/shared/components/search-form.component.html

@ -37,45 +37,47 @@
Search for content using full text search over all fields and languages! Search for content using full text search over all fields and languages!
</sqx-onboarding-tooltip> </sqx-onboarding-tooltip>
<div class="dropdown-menu" *sqxModalView="searchModal;onRoot:true" [sqxModalTarget]="inputFind"> <sqx-modal [model]="searchModal" [target]="inputFind">
<div class="form-horizontal"> <div class="dropdown-menu">
<div class="form-group row"> <div class="form-horizontal">
<label class="col-2 col-form-label" for="search">Text</label> <div class="form-group row">
<label class="col-2 col-form-label" for="search">Text</label>
<div class="col-10">
<input type="text" class="form-control" id="search" placeholder="Fulltext search" <div class="col-10">
[ngModel]="filter.fullText | async" <input type="text" class="form-control" id="search" placeholder="Fulltext search"
(ngModelChange)="filter.setFullText($event)" /> [ngModel]="filter.fullText | async"
(ngModelChange)="filter.setFullText($event)" />
</div>
</div> </div>
</div>
<div class="form-group row"> <div class="form-group row">
<label class="col-2 col-form-label" for="filter">Filter</label> <label class="col-2 col-form-label" for="filter">Filter</label>
<div class="col-10"> <div class="col-10">
<input type="text" class="form-control" id="filter" placeholder="{{fieldExample}} eq [VALUE]" <input type="text" class="form-control" id="filter" placeholder="{{fieldExample}} eq [VALUE]"
[ngModel]="filter.filter | async" [ngModel]="filter.filter | async"
(ngModelChange)="filter.setFilter($event)" /> (ngModelChange)="filter.setFilter($event)" />
</div>
</div> </div>
</div>
<div class="form-group row"> <div class="form-group row">
<label class="col-2 col-form-label" for="orderBy">Order</label> <label class="col-2 col-form-label" for="orderBy">Order</label>
<div class="col-10"> <div class="col-10">
<input type="text" class="form-control" id="orderBy" placeholder="{{fieldExample}} desc" <input type="text" class="form-control" id="orderBy" placeholder="{{fieldExample}} desc"
[ngModel]="filter.order | async" [ngModel]="filter.order | async"
(ngModelChange)="filter.setOrder($event)" /> (ngModelChange)="filter.setOrder($event)" />
</div>
</div> </div>
</div>
<div class="link"> <div class="link">
Read more about filtering in the <a href="https://docs.squidex.io/04-guides/02-api.html" sqxExternalLink>Documentation</a>. Read more about filtering in the <a href="https://docs.squidex.io/04-guides/02-api.html" sqxExternalLink>Documentation</a>.
</div>
</div> </div>
</div> </div>
</div> </sqx-modal>
<ng-container *sqxModalView="saveQueryDialog;onRoot:true"> <sqx-modal [model]="saveQueryDialog">
<form [formGroup]="saveQueryForm.form" (ngSubmit)="saveQueryComplete()"> <form [formGroup]="saveQueryForm.form" (ngSubmit)="saveQueryComplete()">
<sqx-modal-dialog (close)="saveQueryDialog.hide()"> <sqx-modal-dialog (close)="saveQueryDialog.hide()">
<ng-container title> <ng-container title>
@ -96,4 +98,4 @@
</ng-container> </ng-container>
</sqx-modal-dialog> </sqx-modal-dialog>
</form> </form>
</ng-container> </sqx-modal>

57
src/Squidex/app/shell/pages/internal/apps-menu.component.html

@ -10,31 +10,33 @@
</ng-template> </ng-template>
</span> </span>
<ng-container *ngIf="appsState.apps | async; let apps"> <ng-container>
<div class="dropdown-menu" *sqxModalView="appsMenu;closeAlways:true" @fade> <sqx-modal [model]="appsMenu" closeAlways="true" *ngIf="appsState.apps | async; let apps">
<a class="dropdown-item all-apps" routerLink="/app"> <div class="dropdown-menu" @fade>
<span class="all-apps-text">All Apps</span> <a class="dropdown-item all-apps" routerLink="/app">
<span class="all-apps-pill badge badge-pill badge-primary">{{apps.length}}</span> <span class="all-apps-text">All Apps</span>
</a> <span class="all-apps-pill badge badge-pill badge-primary">{{apps.length}}</span>
</a>
<ng-container *ngIf="apps.length > 0">
<div class="dropdown-divider"></div> <ng-container *ngIf="apps.length > 0">
<div class="dropdown-divider"></div>
<div class="apps-list">
<a class="dropdown-item" *ngFor="let app of apps; trackBy: trackByApp" [routerLink]="['/app', app.name]" routerLinkActive="active">{{app.name}}</a> <div class="apps-list">
</div> <a class="dropdown-item" *ngFor="let app of apps; trackBy: trackByApp" [routerLink]="['/app', app.name]" routerLinkActive="active">{{app.name}}</a>
</ng-container> </div>
</ng-container>
<ng-container *ngIf="(uiState.settings | async).canCreateApps">
<div class="dropdown-divider"></div> <ng-container *ngIf="(uiState.settings | async).canCreateApps">
<div class="dropdown-divider"></div>
<div class="dropdown-button">
<button type="button" class="btn btn-block btn-success" (click)="addAppDialog.show()"> <div class="dropdown-button">
<i class="icon-plus"></i> Create New App <button type="button" class="btn btn-block btn-success" (click)="addAppDialog.show()">
</button> <i class="icon-plus"></i> Create New App
</div> </button>
</ng-container> </div>
</div> </ng-container>
</div>
</sqx-modal>
</ng-container> </ng-container>
</li> </li>
@ -52,8 +54,9 @@
</ng-container> </ng-container>
</ul> </ul>
<ng-container *sqxModalView="addAppDialog;onRoot:true">
<sqx-modal [model]="addAppDialog">
<sqx-app-form <sqx-app-form
(complete)="addAppDialog.hide()"> (complete)="addAppDialog.hide()">
</sqx-app-form> </sqx-app-form>
</ng-container> </sqx-modal>

36
src/Squidex/app/shell/pages/internal/profile-menu.component.html

@ -8,28 +8,30 @@
</span> </span>
</span> </span>
<div class="dropdown-menu" *sqxModalView="modalMenu;closeAlways:true" @fade> <sqx-modal [model]="modalMenu" closeAlways="true">
<a class="dropdown-item dropdown-info" [sqxPopupLink]="snapshot.profileUrl"> <div class="dropdown-menu" @fade>
<div>Signed in with</div> <a class="dropdown-item dropdown-info" [sqxPopupLink]="snapshot.profileUrl">
<div>Signed in with</div>
<strong>{{snapshot.profileEmail}}</strong> <strong>{{snapshot.profileEmail}}</strong>
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item" routerLink="/app/administration" *ngIf="uiState.canUserAdminResource | async"> <a class="dropdown-item" routerLink="/app/administration" *ngIf="uiState.canUserAdminResource | async">
Administration Administration
</a> </a>
<a class="dropdown-item" [sqxPopupLink]="snapshot.profileUrl"> <a class="dropdown-item" [sqxPopupLink]="snapshot.profileUrl">
Profile Profile
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item" (click)="logout()" sqxExternalLink> <a class="dropdown-item" (click)="logout()" sqxExternalLink>
Logout Logout
</a> </a>
</div> </div>
</sqx-modal>
</li> </li>
</ul> </ul>
Loading…
Cancel
Save