Browse Source

Modals fixed.

pull/387/head
Sebastian Stehle 7 years ago
parent
commit
7dac86caf6
  1. 4
      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. 52
      src/Squidex/app/features/content/pages/content/content-page.component.html
  4. 4
      src/Squidex/app/features/content/shared/assets-editor.component.html
  5. 6
      src/Squidex/app/features/content/shared/content-item.component.html
  6. 4
      src/Squidex/app/features/content/shared/due-time-selector.component.html
  7. 6
      src/Squidex/app/features/content/shared/preview-button.component.html
  8. 4
      src/Squidex/app/features/content/shared/references-editor.component.html
  9. 4
      src/Squidex/app/features/rules/pages/rules/rules-page.component.html
  10. 3
      src/Squidex/app/features/schemas/declarations.ts
  11. 2
      src/Squidex/app/features/schemas/module.ts
  12. 10
      src/Squidex/app/features/schemas/pages/schema/field.component.html
  13. 9
      src/Squidex/app/features/schemas/pages/schema/schema-export-form.component.html
  14. 2
      src/Squidex/app/features/schemas/pages/schema/schema-export-form.component.scss
  15. 33
      src/Squidex/app/features/schemas/pages/schema/schema-export-form.component.ts
  16. 42
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.html
  17. 62
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts
  18. 6
      src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.html
  19. 4
      src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.html
  20. 4
      src/Squidex/app/features/settings/pages/clients/client.component.html
  21. 6
      src/Squidex/app/framework/angular/forms/autocomplete.component.html
  22. 6
      src/Squidex/app/framework/angular/forms/color-picker.component.html
  23. 6
      src/Squidex/app/framework/angular/forms/dropdown.component.html
  24. 6
      src/Squidex/app/framework/angular/forms/tag-editor.component.html
  25. 12
      src/Squidex/app/framework/angular/modals/dialog-renderer.component.html
  26. 114
      src/Squidex/app/framework/angular/modals/modal-placement.directive.ts
  27. 254
      src/Squidex/app/framework/angular/modals/modal.component.ts
  28. 171
      src/Squidex/app/framework/angular/modals/modal.directive.ts
  29. 11
      src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.html
  30. 3
      src/Squidex/app/framework/declarations.ts
  31. 9
      src/Squidex/app/framework/module.ts
  32. 4
      src/Squidex/app/shared/components/asset.component.html
  33. 6
      src/Squidex/app/shared/components/language-selector.component.html
  34. 4
      src/Squidex/app/shared/components/markdown-editor.component.html
  35. 4
      src/Squidex/app/shared/components/rich-editor.component.html
  36. 10
      src/Squidex/app/shared/components/search-form.component.html
  37. 4
      src/Squidex/app/shared/components/search-form.component.ts
  38. 46
      src/Squidex/app/shared/services/schemas.service.ts
  39. 10
      src/Squidex/app/shell/pages/internal/apps-menu.component.html
  40. 4
      src/Squidex/app/shell/pages/internal/profile-menu.component.html

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

@ -59,7 +59,7 @@
</ng-container>
</sqx-panel>
<sqx-modal [model]="eventConsumerErrorDialog">
<ng-container *sqxModal="eventConsumerErrorDialog">
<sqx-modal-dialog (close)="eventConsumerErrorDialog.hide()">
<ng-container title>
Error
@ -69,4 +69,4 @@
<textarea readonly class="form-control error-message">{{eventConsumerError}}</textarea>
</ng-container>
</sqx-modal-dialog>
</sqx-modal>
</ng-container>

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

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

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

@ -44,35 +44,37 @@
</sqx-content-status>
</button>
<sqx-modal *ngIf="content.isPending || !schema.isSingleton" [model]="dropdown" [target]="optionsButton" closeAlways="true">
<div class="dropdown-menu" @fade>
<a class="dropdown-item" (click)="discardChanges()" *ngIf="content.canDraftDiscard">
Discard changes
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" (click)="publishChanges()" *ngIf="content.canDraftPublish">
Publish changes
</a>
<ng-container *ngIf="!schema.isSingleton">
<a class="dropdown-item" *ngFor="let info of content.statusUpdates" (click)="changeStatus(info.status)">
Change to <i class="icon-circle icon-sm" [style.color]="info.color"></i> {{info.status}}
<ng-container *ngIf="content.isPending || !schema.isSingleton">
<ng-container *sqxModal="dropdown;closeAlways:true">
<div class="dropdown-menu" [sqxAnchoredTo]="optionsButton" @fade>
<a class="dropdown-item" (click)="discardChanges()" *ngIf="content.canDraftDiscard">
Discard changes
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item dropdown-item-delete"
[class.disabled]="!content.canDelete"
(sqxConfirmClick)="delete()"
confirmTitle="Delete content"
confirmText="Do you really want to delete the content?">
Delete
<a class="dropdown-item" (click)="publishChanges()" *ngIf="content.canDraftPublish">
Publish changes
</a>
</ng-container>
</div>
</sqx-modal>
<ng-container *ngIf="!schema.isSingleton">
<a class="dropdown-item" *ngFor="let info of content.statusUpdates" (click)="changeStatus(info.status)">
Change to <i class="icon-circle icon-sm" [style.color]="info.color"></i> {{info.status}}
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item dropdown-item-delete"
[class.disabled]="!content.canDelete"
(sqxConfirmClick)="delete()"
confirmTitle="Delete content"
confirmText="Do you really want to delete the content?">
Delete
</a>
</ng-container>
</div>
</ng-container>
</ng-container>
</div>
<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>
<sqx-modal [model]="assetsDialog">
<ng-container *sqxModal="assetsDialog">
<sqx-assets-selector
(select)="selectAssets($event)">
</sqx-assets-selector>
</sqx-modal>
</ng-container>

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

@ -56,8 +56,8 @@
<i class="icon-dots"></i>
</button>
<sqx-modal [model]="dropdown" [target]="optionsButton" closeAlways="true">
<div class="dropdown-menu" @fade>
<ng-container *sqxModal="dropdown;closeAlways:true">
<div class="dropdown-menu" [sqxAnchoredTo]="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>
@ -74,7 +74,7 @@
Delete
</a>
</div>
</sqx-modal>
</ng-container>
</div>
</td>
<td class="cell-actions" *ngIf="isReference" [sqxStopClick]="isDirty">

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

@ -1,4 +1,4 @@
<sqx-modal [model]="dueTimeDialog">
<ng-container *sqxModal="dueTimeDialog">
<sqx-modal-dialog (close)="cancelStatusChange()">
<ng-container title>
{{dueTimeAction}} content item(s)
@ -28,4 +28,4 @@
</ng-container>
</sqx-modal-dialog>
</sqx-modal>
</ng-container>

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

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

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

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

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

@ -67,7 +67,7 @@
</tbody>
</table>
<sqx-modal [model]="addRuleDialog">
<ng-container *sqxModal="addRuleDialog">
<sqx-rule-wizard
[schemas]="schemasState.schemas | async"
[rule]="wizardRule"
@ -76,7 +76,7 @@
[mode]="wizardMode"
(complete)="addRuleDialog.hide()">
</sqx-rule-wizard>
</sqx-modal>
</ng-container>
</ng-container>
</ng-container>

3
src/Squidex/app/features/schemas/declarations.ts

@ -28,9 +28,10 @@ export * from './pages/schema/forms/field-form-ui.component';
export * from './pages/schema/forms/field-form-validation.component';
export * from './pages/schema/forms/field-form.component';
export * from './pages/schema/field.component';
export * from './pages/schema/field-wizard.component';
export * from './pages/schema/field.component';
export * from './pages/schema/schema-edit-form.component';
export * from './pages/schema/schema-export-form.component';
export * from './pages/schema/schema-page.component';
export * from './pages/schema/schema-preview-urls-form.component';
export * from './pages/schema/schema-scripts-form.component';

2
src/Squidex/app/features/schemas/module.ts

@ -37,6 +37,7 @@ import {
ReferencesUIComponent,
ReferencesValidationComponent,
SchemaEditFormComponent,
SchemaExportFormComponent,
SchemaFormComponent,
SchemaPageComponent,
SchemaPreviewUrlsFormComponent,
@ -101,6 +102,7 @@ const routes: Routes = [
ReferencesUIComponent,
ReferencesValidationComponent,
SchemaEditFormComponent,
SchemaExportFormComponent,
SchemaFormComponent,
SchemaPageComponent,
SchemaPreviewUrlsFormComponent,

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

@ -29,8 +29,8 @@
<i class="icon-dots"></i>
</button>
<sqx-modal [model]="dropdown" [target]="optionsButton" closeAlways="true">
<div class="dropdown-menu" @fade>
<ng-container *sqxModal="dropdown;closeAlways:true">
<div class="dropdown-menu" [sqxAnchoredTo]="optionsButton" @fade>
<ng-container *ngIf="field.properties.isContentField">
<a class="dropdown-item" (click)="enableField()" *ngIf="field.canEnable">
Enable in UI
@ -69,7 +69,7 @@
</a>
</ng-container>
</div>
</sqx-modal>
</ng-container>
</div>
</div>
</div>
@ -113,11 +113,11 @@
</button>
</div>
<sqx-modal [model]="addFieldDialog">
<ng-container *sqxModal="addFieldDialog">
<sqx-field-wizard [schema]="schema" [parent]="field"
(complete)="addFieldDialog.hide()">
</sqx-field-wizard>
</sqx-modal>
</ng-container>
</ng-container>
</div>
</div>

9
src/Squidex/app/features/schemas/pages/schema/schema-export-form.component.html

@ -0,0 +1,9 @@
<sqx-modal-dialog (close)="emitComplete()" large="true">
<ng-container title>
Export Schema
</ng-container>
<ng-container content>
<sqx-json-editor disabled [ngModel]="export"></sqx-json-editor>
</ng-container>
</sqx-modal-dialog>

2
src/Squidex/app/features/schemas/pages/schema/schema-export-form.component.scss

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

33
src/Squidex/app/features/schemas/pages/schema/schema-export-form.component.ts

@ -0,0 +1,33 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { SchemaDetailsDto } from '@app/shared';
@Component({
selector: 'sqx-schema-export-form',
styleUrls: ['./schema-export-form.component.scss'],
templateUrl: './schema-export-form.component.html'
})
export class SchemaExportFormComponent implements OnInit {
@Output()
public complete = new EventEmitter();
@Input()
public schema: SchemaDetailsDto;
public export: any;
public ngOnInit() {
this.export = this.schema.export();
}
public emitComplete() {
this.complete.emit();
}
}

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

@ -6,7 +6,7 @@
</ng-container>
<ng-container menu>
<button type="button" class="btn btn-text mr-1" (click)="exportSchemaDialog.show()">
<button type="button" class="btn btn-text mr-1" (click)="exportDialog.show()">
JSON Preview
</button>
@ -24,8 +24,8 @@
<i class="icon-dots"></i>
</button>
<sqx-modal [model]="editOptionsDropdown" [target]="buttonOptions" closeAlways="true">
<div class="dropdown-menu" @fade>
<ng-container *sqxModal="editOptionsDropdown;closeAlways:true">
<div class="dropdown-menu" [sqxAnchoredTo]="buttonOptions" @fade>
<a class="dropdown-item" (click)="configureScriptsDialog.show()">
Scripts
</a>
@ -53,7 +53,7 @@
</a>
</ng-container>
</div>
</sqx-modal>
</ng-container>
</div>
<sqx-onboarding-tooltip helpId="history" [for]="buttonOptions" position="bottom-right" after="60000">
@ -101,40 +101,32 @@
<router-outlet></router-outlet>
<sqx-modal [model]="editSchemaDialog">
<ng-container *sqxModal="editSchemaDialog">
<sqx-schema-edit-form [schema]="schema"
(complete)="editSchemaDialog.hide()">
</sqx-schema-edit-form>
</sqx-modal>
</ng-container>
<sqx-modal [model]="addFieldDialog">
<ng-container *sqxModal="addFieldDialog">
<sqx-field-wizard [schema]="schema"
(complete)="addFieldDialog.hide()">
</sqx-field-wizard>
</sqx-modal>
</ng-container>
<sqx-modal [model]="configureScriptsDialog">
<ng-container *sqxModal="configureScriptsDialog">
<sqx-schema-scripts-form [schema]="schema"
(complete)="configureScriptsDialog.hide()">
</sqx-schema-scripts-form>
</sqx-modal>
</ng-container>
<sqx-modal [model]="configurePreviewUrlsDialog">
<ng-container *sqxModal="configurePreviewUrlsDialog">
<sqx-schema-preview-urls-form [schema]="schema"
(complete)="configurePreviewUrlsDialog.hide()">
</sqx-schema-preview-urls-form>
</sqx-modal>
</ng-container>
<sqx-modal [model]="exportSchemaDialog">
<sqx-modal-dialog (close)="exportSchemaDialog.hide()" large="true">
<ng-container title>
Export Schema
</ng-container>
<ng-container content>
<div class="json-preview">
<sqx-json-editor disabled [ngModel]="schemaExport"></sqx-json-editor>
</div>
</ng-container>
</sqx-modal-dialog>
</sqx-modal>
<ng-container *sqxModal="exportDialog">
<sqx-schema-export-form [schema]="schema"
(complete)="exportDialog.hide()">
</sqx-schema-export-form>
</ng-container>

62
src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts

@ -21,8 +21,7 @@ import {
PatternsState,
ResourceOwner,
SchemaDetailsDto,
SchemasState,
Types
SchemasState
} from '@app/shared';
import {
@ -40,18 +39,14 @@ import {
export class SchemaPageComponent extends ResourceOwner implements OnInit {
public fieldTypes = fieldTypes;
public schemaExport: any;
public schema: SchemaDetailsDto;
public exportSchemaDialog = new DialogModel();
public configureScriptsDialog = new DialogModel();
public addFieldDialog = new DialogModel();
public configurePreviewUrlsDialog = new DialogModel();
public configureScriptsDialog = new DialogModel();
public editOptionsDropdown = new ModalModel();
public editSchemaDialog = new DialogModel();
public addFieldDialog = new DialogModel();
public exportDialog = new DialogModel();
constructor(
public readonly appsState: AppsState,
@ -72,8 +67,6 @@ export class SchemaPageComponent extends ResourceOwner implements OnInit {
.subscribe(schema => {
if (schema) {
this.schema = schema;
this.export();
}
}));
}
@ -102,52 +95,7 @@ export class SchemaPageComponent extends ResourceOwner implements OnInit {
}
public cloneSchema() {
this.messageBus.emit(new SchemaCloning(this.schemaExport));
}
private export() {
const cleanup = (source: any, ...exclude: string[]): any => {
const clone = {};
for (const key in source) {
if (source.hasOwnProperty(key) && exclude.indexOf(key) < 0) {
const value = source[key];
if (value) {
clone[key] = value;
}
}
}
return clone;
};
const result: any = {
fields: this.schema.fields.map(field => {
const copy = cleanup(field, 'fieldId');
copy.properties = cleanup(field.properties);
if (Types.isArray(copy.nested)) {
if (copy.nested.length === 0) {
delete copy['nested'];
} else {
copy.nested = field.nested.map(nestedField => {
const nestedCopy = cleanup(nestedField, 'fieldId', 'parentId');
nestedCopy.properties = cleanup(nestedField.properties);
return nestedCopy;
});
}
}
return copy;
}),
properties: cleanup(this.schema.properties)
};
this.schemaExport = result;
this.messageBus.emit(new SchemaCloning(this.schema.export()));
}
private back() {

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

@ -45,8 +45,8 @@
(focus)="patternsModal.show()" (blur)="patternsModal.hide()" />
<ng-container *ngIf="patterns.length > 0 && (showPatternSuggestions | async)">
<sqx-modal [model]="patternsModal" [target]="inputPattern" position="bottom-left">
<div class="control-dropdown" @fade>
<ng-container *sqxModal="patternsModal" position="bottom-left">
<div class="control-dropdown" [sqxAnchoredTo]="inputPattern" @fade>
<h4>Suggestions</h4>
<div *ngFor="let pattern of patterns" class="control-dropdown-item control-dropdown-item-selectable" (mousedown)="setPattern(pattern)">
@ -54,7 +54,7 @@
<div class="truncate text-muted">{{pattern.pattern}}</div>
</div>
</div>
</sqx-modal>
</ng-container>
</ng-container>
</div>
<small class="col-3" style="align-self: center">

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

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

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

@ -70,7 +70,7 @@
</div>
</div>
<sqx-modal [model]="connectDialog">
<ng-container *sqxModal="connectDialog">
<sqx-modal-dialog large="true" (close)="connectDialog.hide()">
<ng-container title>
Connect
@ -138,4 +138,4 @@
</div>
</ng-container>
</sqx-modal-dialog>
</sqx-modal>
</ng-container>

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

@ -5,8 +5,8 @@
autocorrect="off"
autocapitalize="off">
<sqx-modal [model]="snapshot.suggestedItems.length > 0" [target]="input" position="bottom-left">
<div class="control-dropdown" #container>
<ng-container *sqxModal="snapshot.suggestedItems.length > 0" position="bottom-left">
<div class="control-dropdown" [sqxAnchoredTo]="input" #container>
<div *ngFor="let item of snapshot.suggestedItems; let i = index" class="control-dropdown-item control-dropdown-item-selectable"
[class.active]="i === snapshot.suggestedIndex"
[container]="container"
@ -19,5 +19,5 @@
<ng-template *ngIf="itemTemplate" [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template>
</div>
</div>
</sqx-modal>
</ng-container>
</span>

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

@ -14,8 +14,8 @@
</ng-template>
</span>
<sqx-modal [model]="modal" [target]="input" closeAlways="true" position="bottom-left">
<div
<ng-container *sqxModal="modal">
<div [sqxAnchoredTo]="input" position="bottom-left"
[style.height]="'228px'"
[style.width]="'130px'">
<div [style.background]="snapshot.value"
@ -26,4 +26,4 @@
(colorPickerChange)="updateValue($event)">
</div>
</div>
</sqx-modal>
</ng-container>

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

@ -15,14 +15,14 @@
</div>
<div class="items-container">
<sqx-modal [model]="dropdown" [target]="input" position="bottom-left">
<div class="control-dropdown" #container>
<ng-container *sqxModal="dropdown">
<div class="control-dropdown" [sqxAnchoredTo]="input" position="bottom-left" #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>
</div>
</div>
</sqx-modal>
</ng-container>
</div>
</span>

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

@ -22,8 +22,8 @@
spellcheck="false">
</div>
<sqx-modal [model]="snapshot.suggestedItems.length > 0" [target]="form">
<div class="control-dropdown" #container @fade>
<ng-container *sqxModal="snapshot.suggestedItems.length > 0">
<div class="control-dropdown" [sqxAnchoredTo]="form" #container @fade>
<div *ngFor="let item of snapshot.suggestedItems; let i = index" class="control-dropdown-item control-dropdown-item-selectable"
[class.active]="i === snapshot.suggestedIndex"
[container]="container"
@ -33,4 +33,4 @@
<ng-container>{{item}}</ng-container>
</div>
</div>
</sqx-modal>
</ng-container>

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

@ -1,7 +1,7 @@
<ng-content></ng-content>
<sqx-modal [model]="dialogView">
<ng-container *sqxModal="dialogView">
<sqx-modal-dialog showClose="false" (close)="cancel()">
<ng-container title>
{{snapshot.dialogRequest?.title}}
@ -16,7 +16,7 @@
<button type="button" class="float-right btn btn-danger" (click)="confirm()" sqxFocusOnInit>Yes</button>
</ng-container>
</sqx-modal-dialog>
</sqx-modal>
</ng-container>
<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>
@ -27,9 +27,7 @@
</div>
<ng-container *ngIf="snapshot.tooltip; let tooltip">
<sqx-modal [model]="true" [target]="tooltip.target" [position]="tooltip.position" [offset]="6">
<div class="tooltip2 tooltip2-{{tooltip.position}}">
{{tooltip.text}}
</div>
</sqx-modal>
<div class="tooltip2 tooltip2-{{tooltip.position}}" [sqxAnchoredTo]="tooltip.target" [position]="tooltip.position" [offset]="6">
{{tooltip.text}}
</div>
</ng-container>

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

@ -0,0 +1,114 @@
/*
* 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: '[sqxAnchoredTo]'
})
export class ModalPlacementDirective extends ResourceOwner implements AfterViewInit, OnDestroy {
private targetElement: Element;
@Input('sqxAnchoredTo')
public set target(element: Element) {
if (element !== this.targetElement) {
this.unsubscribeAll();
this.targetElement = element;
if (element) {
this.listenToElement(element);
this.updatePosition();
}
}
}
@Input()
public offset = 2;
@Input()
public position = 'bottom-right';
@Input()
public update = true;
constructor(
private readonly renderer: Renderer2,
private readonly element: ElementRef<Element>
) {
super();
}
private listenToElement(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.renderer.setStyle(modalRef, 'right', 'auto');
this.renderer.setStyle(modalRef, 'bottom', 'auto');
this.renderer.setStyle(modalRef, 'margin', '0');
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.update, viewW, viewH);
x = position.x;
y = position.y;
}
this.renderer.setStyle(modalRef, 'top', `${y}px`);
this.renderer.setStyle(modalRef, 'left', `${x}px`);
}
}

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

@ -1,254 +0,0 @@
/*
* 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);
}

171
src/Squidex/app/framework/angular/modals/modal.directive.ts

@ -0,0 +1,171 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectorRef, Directive, EmbeddedViewRef, Input, OnDestroy, Renderer2, TemplateRef } from '@angular/core';
import {
DialogModel,
ModalModel,
ResourceOwner,
Types
} from '@app/framework/internal';
import { RootViewComponent } from './root-view.component';
declare type Model = DialogModel | ModalModel | any;
@Directive({
selector: '[sqxModal]'
})
export class ModalDirective implements OnDestroy {
private readonly eventsView = new ResourceOwner();
private readonly eventsModel = new ResourceOwner();
private currentModel: DialogModel | ModalModel | null = null;
private renderedView: EmbeddedViewRef<any> | null = null;
private renderRoots: HTMLElement[] | null;
private isOpen: boolean;
@Input('sqxModal')
public set model(value: Model) {
if (this.currentModel !== value) {
this.currentModel = value;
this.eventsModel.unsubscribeAll();
this.subscribeToModel(value);
}
}
@Input('sqxModalCloseAuto')
public closeAuto = true;
@Input('sqxModalCloseAlways')
public closeAlways = false;
constructor(
private readonly changeDetector: ChangeDetectorRef,
private readonly renderer: Renderer2,
private readonly rootView: RootViewComponent,
private readonly templateRef: TemplateRef<any>
) {
}
public ngOnDestroy() {
hideModal(this.currentModel);
this.eventsView.unsubscribeAll();
this.eventsModel.unsubscribeAll();
}
private update(isOpen: boolean) {
if (!this.templateRef || this.isOpen === isOpen) {
return;
}
this.eventsView.unsubscribeAll();
if (isOpen) {
if (!this.renderedView) {
this.renderedView = this.rootView.viewContainer.createEmbeddedView(this.templateRef);
this.renderRoots = this.renderedView.rootNodes.filter(x => !!x.style);
this.setupStyles();
this.subscribeToView();
this.changeDetector.detectChanges();
}
} else {
if (this.renderedView) {
this.renderedView.destroy();
this.renderedView = null;
this.renderRoots = null;
this.changeDetector.detectChanges();
}
}
this.isOpen = isOpen;
}
private setupStyles() {
if (this.renderRoots) {
for (let node of this.renderRoots) {
this.renderer.setStyle(node, 'display', 'block');
}
}
}
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.closeAuto) {
document.addEventListener('click', this.documentClickListener, true);
this.eventsView.own(() => {
document.removeEventListener('click', this.documentClickListener);
});
}
if (this.closeAlways && this.renderRoots) {
for (let node of this.renderRoots) {
this.eventsView.own(this.renderer.listen(node, 'click', this.elementListener));
}
}
}
private elementListener = (event: MouseEvent) => {
if (this.isClickedInside(event)) {
hideModal(this.currentModel);
}
}
private documentClickListener = (event: MouseEvent) => {
if (!this.isClickedInside(event)) {
hideModal(this.currentModel);
}
}
private isClickedInside(event: MouseEvent) {
try {
if (!this.renderRoots) {
return false;
}
for (let node of this.renderRoots) {
const bounds = node.getBoundingClientRect();
if (bounds.width > 0 && bounds.height > 0 && node.contains(<Node>event.target)) {
return true;
}
}
return false;
} catch (ex) {
return false;
}
}
}
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,9 +1,6 @@
<sqx-modal [model]="tooltipModal" [target]="for" [offset]="4" position="full">
<div class="onboarding-rect"></div>
</sqx-modal>
<sqx-modal [model]="tooltipModal" [target]="for" [offset]="4">
<div class="onboarding-help" @fade>
<ng-container *sqxModal="tooltipModal">
<div class="onboarding-rect" [sqxAnchoredTo]="for" [offset]="4" position="full"></div>
<div class="onboarding-help" [sqxAnchoredTo]="for" [offset]="4" @fade>
<div class="onboarding-text">
<ng-content></ng-content>
</div>
@ -18,4 +15,4 @@
</button>
</div>
</div>
</sqx-modal>
</ng-container>

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

@ -36,8 +36,9 @@ export * from './angular/http/loading.interceptor';
export * from './angular/http/http-extensions';
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-placement.directive';
export * from './angular/modals/modal.directive';
export * from './angular/modals/onboarding-tooltip.component';
export * from './angular/modals/root-view.component';
export * from './angular/modals/tooltip.directive';

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

@ -58,8 +58,9 @@ import {
LoadingService,
LocalStoreService,
MessageBus,
ModalComponent,
ModalDialogComponent,
ModalDirective,
ModalPlacementDirective,
MoneyPipe,
MonthPipe,
OnboardingService,
@ -135,8 +136,9 @@ import {
KeysPipe,
KNumberPipe,
LightenPipe,
ModalComponent,
ModalDialogComponent,
ModalDirective,
ModalPlacementDirective,
MoneyPipe,
MonthPipe,
OnboardingTooltipComponent,
@ -202,8 +204,9 @@ import {
KeysPipe,
KNumberPipe,
LightenPipe,
ModalComponent,
ModalDirective,
ModalDialogComponent,
ModalPlacementDirective,
MoneyPipe,
MonthPipe,
OnboardingTooltipComponent,

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

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

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

@ -9,11 +9,11 @@
{{selectedLanguage.iso2Code}}
</button>
<sqx-modal [model]="dropdown" [target]="button" closeAlways="true">
<div class="dropdown-menu" @fade>
<ng-container *sqxModal="dropdown">
<div class="dropdown-menu" [sqxAnchoredTo]="button" @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>
</sqx-modal>
</ng-container>
</div>

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

@ -4,8 +4,8 @@
</div>
</div>
<sqx-modal [model]="assetsDialog">
<ng-container *sqxModal="assetsDialog">
<sqx-assets-selector
(select)="insertAssets($event)">
</sqx-assets-selector>
</sqx-modal>
</ng-container>

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

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

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

@ -37,8 +37,8 @@
Search for content using full text search over all fields and languages!
</sqx-onboarding-tooltip>
<sqx-modal [model]="searchModal" [target]="inputFind">
<div class="dropdown-menu">
<ng-container *sqxModal="searchModal">
<div class="dropdown-menu" [sqxAnchoredTo]="inputFind" @fade>
<div class="form-horizontal">
<div class="form-group row">
<label class="col-2 col-form-label" for="search">Text</label>
@ -75,9 +75,9 @@
</div>
</div>
</div>
</sqx-modal>
</ng-container>
<sqx-modal [model]="saveQueryDialog">
<ng-container *sqxModal="saveQueryDialog">
<form [formGroup]="saveQueryForm.form" (ngSubmit)="saveQueryComplete()">
<sqx-modal-dialog (close)="saveQueryDialog.hide()">
<ng-container title>
@ -98,4 +98,4 @@
</ng-container>
</sqx-modal-dialog>
</form>
</sqx-modal>
</ng-container>

4
src/Squidex/app/shared/components/search-form.component.ts

@ -11,6 +11,7 @@ import { Observable } from 'rxjs';
import {
DialogModel,
fadeAnimation,
FilterState,
ModalModel,
Queries,
@ -21,6 +22,9 @@ import {
selector: 'sqx-search-form',
styleUrls: ['./search-form.component.scss'],
templateUrl: './search-form.component.html',
animations: [
fadeAnimation
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SearchFormComponent implements OnInit {

46
src/Squidex/app/shared/services/schemas.service.ts

@ -20,6 +20,7 @@ import {
Resource,
ResourceLinks,
StringHelper,
Types,
Version,
Versioned
} from '@app/framework';
@ -112,6 +113,51 @@ export class SchemaDetailsDto extends SchemaDto {
this.listFieldsEditable = listFields.filter(x => x.isInlineEditable);
}
}
public export(): any {
const cleanup = (source: any, ...exclude: string[]): any => {
const clone = {};
for (const key in source) {
if (source.hasOwnProperty(key) && exclude.indexOf(key) < 0) {
const value = source[key];
if (value) {
clone[key] = value;
}
}
}
return clone;
};
const result: any = {
fields: this.fields.map(field => {
const copy = cleanup(field, 'fieldId');
copy.properties = cleanup(field.properties);
if (Types.isArray(copy.nested)) {
if (copy.nested.length === 0) {
delete copy['nested'];
} else {
copy.nested = field.nested.map(nestedField => {
const nestedCopy = cleanup(nestedField, 'fieldId', 'parentId');
nestedCopy.properties = cleanup(nestedField.properties);
return nestedCopy;
});
}
}
return copy;
}),
properties: cleanup(this.properties)
};
return result;
}
}
export class FieldDto {

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

@ -10,8 +10,8 @@
</ng-template>
</span>
<ng-container>
<sqx-modal [model]="appsMenu" closeAlways="true" *ngIf="appsState.apps | async; let apps">
<ng-container *ngIf="appsState.apps | async; let apps">
<ng-container *sqxModal="appsMenu;closeAlways:true">
<div class="dropdown-menu" @fade>
<a class="dropdown-item all-apps" routerLink="/app">
<span class="all-apps-text">All Apps</span>
@ -36,7 +36,7 @@
</div>
</ng-container>
</div>
</sqx-modal>
</ng-container>
</ng-container>
</li>
@ -55,8 +55,8 @@
</ul>
<sqx-modal [model]="addAppDialog">
<ng-container *sqxModal="addAppDialog">
<sqx-app-form
(complete)="addAppDialog.hide()">
</sqx-app-form>
</sqx-modal>
</ng-container>

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

@ -8,7 +8,7 @@
</span>
</span>
<sqx-modal [model]="modalMenu" closeAlways="true">
<ng-container *sqxModal="modalMenu;closeAlways:true">
<div class="dropdown-menu" @fade>
<a class="dropdown-item dropdown-info" [sqxPopupLink]="snapshot.profileUrl">
<div>Signed in with</div>
@ -32,6 +32,6 @@
Logout
</a>
</div>
</sqx-modal>
</ng-container>
</li>
</ul>
Loading…
Cancel
Save