diff --git a/backend/src/Squidex/wwwroot/scripts/editor-sdk.js b/backend/src/Squidex/wwwroot/scripts/editor-sdk.js index 142442f47..baa25ec4e 100644 --- a/backend/src/Squidex/wwwroot/scripts/editor-sdk.js +++ b/backend/src/Squidex/wwwroot/scripts/editor-sdk.js @@ -124,6 +124,8 @@ function SquidexFormField() { var initCalled = false; var disabledHandler; var disabled = false; + var fullscreen = false; + var fullscreenHandler = false; var valueHandler; var value; var formValueHandler; @@ -149,6 +151,12 @@ function SquidexFormField() { } } + function raiseFullscreen() { + if (fullscreenHandler) { + fullscreenHandler(fullscreen); + } + } + function raiseInit() { if (initHandler && !initCalled && context) { initHandler(context); @@ -174,6 +182,10 @@ function SquidexFormField() { formValue = event.data.formValue; raiseFormValueChanged(); + } else if (type === 'fullscreenChanged') { + fullscreen = event.data.fullscreen; + + raiseFullscreen(); } else if (type === 'init') { context = event.data.context; @@ -219,6 +231,8 @@ function SquidexFormField() { /* * Notifies the parent to navigate to the path. + * + * @params url: string: The url to navigate to. */ navigate: function (url) { if (window.parent) { @@ -226,8 +240,21 @@ function SquidexFormField() { } }, + /* + * Notifies the parent to go to fullscreen mode. + * + * @params mode: boolean: The fullscreen mode. + */ + toggleFullscreen: function () { + if (window.parent) { + window.parent.postMessage({ type: 'fullscreen', mode: !fullscreen }, '*'); + } + }, + /** * Notifies the control container that the value has been changed. + * + * @params newValue: any: The new field value. */ valueChanged: function (newValue) { value = newValue; @@ -272,6 +299,15 @@ function SquidexFormField() { raiseFormValueChanged(); }, + + /** + * Register the fullscreen changed handler. + */ + onFullscreen: function (callback) { + fullscreenHandler = callback; + + raiseFullscreen(); + }, /** * Clean the editor SDK. diff --git a/backend/src/Squidex/wwwroot/scripts/editor-simple.html b/backend/src/Squidex/wwwroot/scripts/editor-simple.html index ef07e44a3..a63869b46 100644 --- a/backend/src/Squidex/wwwroot/scripts/editor-simple.html +++ b/backend/src/Squidex/wwwroot/scripts/editor-simple.html @@ -12,10 +12,37 @@ .ck-editor__editable { min-height: 250px; } + + .fullscreen { + height: 100vh; + } + + .fullscreen .ck-editor { + position: fixed !important; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + } + + .fullscreen .ck-editor .ck-editor__editable.ck-rounded-corners.ck-editor__editable_inline, + .fullscreen .ck-editor .ck.ck-editor__main { + height: 100%; + } + + #fullscreenButton { + position: absolute; + z-index: 10000; + right: 10px; + top: 10px; + } + Toggle fullscreen + diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistryTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistryTests.cs index c446ab897..a21b7632c 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistryTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistryTests.cs @@ -159,7 +159,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules Display = "Enum", Description = null, Editor = RuleActionPropertyEditor.Dropdown, - IsRequired = true, + IsRequired = false, Options = new[] { "Yes", "No" } }); diff --git a/frontend/app/framework/angular/forms/editors/iframe-editor.component.html b/frontend/app/framework/angular/forms/editors/iframe-editor.component.html index 19adae7f7..706adf00c 100644 --- a/frontend/app/framework/angular/forms/editors/iframe-editor.component.html +++ b/frontend/app/framework/angular/forms/editors/iframe-editor.component.html @@ -1 +1,6 @@ - +
+
+ +
+
+ diff --git a/frontend/app/framework/angular/forms/editors/iframe-editor.component.scss b/frontend/app/framework/angular/forms/editors/iframe-editor.component.scss index ae2678456..105411a21 100644 --- a/frontend/app/framework/angular/forms/editors/iframe-editor.component.scss +++ b/frontend/app/framework/angular/forms/editors/iframe-editor.component.scss @@ -2,4 +2,16 @@ iframe { background: 0; border: 0; overflow: hidden; +} + +.fullscreen { + @include fixed(0, 0, 0, 0); + background: $panel-light-background; + border: 0; + border-radius: 0; + z-index: 1040; + + iframe { + height: 100% !important; + } } \ No newline at end of file diff --git a/frontend/app/framework/angular/forms/editors/iframe-editor.component.ts b/frontend/app/framework/angular/forms/editors/iframe-editor.component.ts index f0c9e7ea2..b3fa7063a 100644 --- a/frontend/app/framework/angular/forms/editors/iframe-editor.component.ts +++ b/frontend/app/framework/angular/forms/editors/iframe-editor.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnChanges, Renderer2, SimpleChanges, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnChanges, OnDestroy, Renderer2, SimpleChanges, ViewChild } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { Router } from '@angular/router'; import { StatefulControlComponent, Types } from '@app/framework/internal'; @@ -14,6 +14,11 @@ export const SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => IFrameEditorComponent), multi: true }; +interface State { + // True, when the editor is shown as fullscreen. + isFullscreen: boolean; +} + @Component({ selector: 'sqx-iframe-editor', styleUrls: ['./iframe-editor.component.scss'], @@ -23,7 +28,7 @@ export const SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class IFrameEditorComponent extends StatefulControlComponent implements OnChanges, AfterViewInit { +export class IFrameEditorComponent extends StatefulControlComponent implements OnChanges, OnDestroy, AfterViewInit { private value: any; private isDisabled = false; private isInitialized = false; @@ -31,6 +36,12 @@ export class IFrameEditorComponent extends StatefulControlComponent im @ViewChild('iframe', { static: false }) public iframe: ElementRef; + @ViewChild('container', { static: false }) + public container: ElementRef; + + @ViewChild('inner', { static: false }) + public inner: ElementRef; + @Input() public context: any = {}; @@ -40,11 +51,19 @@ export class IFrameEditorComponent extends StatefulControlComponent im @Input() public url: string; + public fullscreen: boolean; + constructor(changeDetector: ChangeDetectorRef, private readonly renderer: Renderer2, private readonly router: Router ) { - super(changeDetector, {}); + super(changeDetector, { + isFullscreen: false + }); + } + + public ngOnDestroy() { + this.toggleFullscreen(false); } public ngOnChanges(changes: SimpleChanges) { @@ -75,17 +94,24 @@ export class IFrameEditorComponent extends StatefulControlComponent im this.isInitialized = true; this.sendInit(); + this.sendFullscreen(); this.sendFormValue(); this.sendDisabled(); this.sendValue(); } else if (type === 'resize') { const { height } = event.data; - this.iframe.nativeElement.height = height + 'px'; + this.renderer.setStyle(this.iframe.nativeElement, 'height', height + 'px'); } else if (type === 'navigate') { const { url } = event.data; this.router.navigateByUrl(url); + } else if (type === 'fullscreen') { + const { mode } = event.data; + + if (mode !== this.snapshot.isFullscreen) { + this.toggleFullscreen(mode); + } } else if (type === 'valueChanged') { const { value } = event.data; @@ -97,6 +123,8 @@ export class IFrameEditorComponent extends StatefulControlComponent im } else if (type === 'touched') { this.callTouched(); } + + this.detectChanges(); } })); } @@ -121,6 +149,10 @@ export class IFrameEditorComponent extends StatefulControlComponent im this.sendMessage('valueChanged', { value: this.value }); } + private sendFullscreen() { + this.sendMessage('fullscreenChanged', { fullscreen: this.snapshot.isFullscreen }); + } + private sendDisabled() { this.sendMessage('disabled', { isDisabled: this.isDisabled }); } @@ -129,6 +161,20 @@ export class IFrameEditorComponent extends StatefulControlComponent im this.sendMessage('formValueChanged', { formValue: this.formValue }); } + private toggleFullscreen(isFullscreen: boolean) { + this.next(s => ({ ...s, isFullscreen })); + + let target = this.container.nativeElement; + + if (isFullscreen) { + target = document.body; + } + + this.renderer.appendChild(target, this.inner.nativeElement); + + this.sendFullscreen(); + } + private sendMessage(type: string, payload: any) { if (!this.iframe) { return; diff --git a/frontend/app/shared/components/forms/markdown-editor.component.ts b/frontend/app/shared/components/forms/markdown-editor.component.ts index 286b4bd23..3faaf8edb 100644 --- a/frontend/app/shared/components/forms/markdown-editor.component.ts +++ b/frontend/app/shared/components/forms/markdown-editor.component.ts @@ -18,7 +18,7 @@ export const SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR: any = { interface State { // True, when the editor is shown as fullscreen. - isFullscreen: false; + isFullscreen: boolean; } @Component({ @@ -187,15 +187,7 @@ export class MarkdownEditorComponent extends StatefulControlComponent { const isFullscreen = this.simplemde.isFullscreenActive(); - let target = this.container.nativeElement; - - if (isFullscreen) { - target = document.body; - } - - this.renderer.appendChild(target, this.inner.nativeElement); - - this.next(s => ({ ...s, isFullscreen })); + this.toggleFullscreen(isFullscreen); }); this.simplemde.codemirror.on('blur', () => { @@ -277,4 +269,16 @@ export class MarkdownEditorComponent extends StatefulControlComponent ({ ...s, isFullscreen })); + } } \ No newline at end of file diff --git a/frontend/app/shell/pages/internal/profile-menu.component.ts b/frontend/app/shell/pages/internal/profile-menu.component.ts index a1eb8972b..ad392c401 100644 --- a/frontend/app/shell/pages/internal/profile-menu.component.ts +++ b/frontend/app/shell/pages/internal/profile-menu.component.ts @@ -22,6 +22,17 @@ interface State { profileUrl: string; } +const ALL_LANGUAGES: ReadonlyArray<{ code: string, name: string }> = [{ + code: 'en', + name: 'English' +}, { + code: 'nl', + name: 'Nederlands' +}, { + code: 'it', + name: 'Italiano' +}]; + @Component({ selector: 'sqx-profile-menu', styleUrls: ['./profile-menu.component.scss'], @@ -37,16 +48,7 @@ export class ProfileMenuComponent extends StatefulComponent implements On public showSubmenu = false; public language = this.uiOptions.get('more.culture'); - public languages: ReadonlyArray<{ code: string, name: string }> = [{ - code: 'en', - name: 'English' - }, { - code: 'nl', - name: 'Hollandske' - }, { - code: 'it', - name: 'Italiano' - }]; + public languages = ALL_LANGUAGES; constructor(changeDetector: ChangeDetectorRef, apiUrl: ApiUrlConfig, public readonly uiState: UIState,