Browse Source

IFrame SDK for widget.

pull/1029/head
Sebastian 3 years ago
parent
commit
0386b8f2c1
  1. 5
      backend/src/Squidex/wwwroot/scripts/editor-context.html
  2. 21
      backend/src/Squidex/wwwroot/scripts/editor-sdk.d.ts
  3. 77
      backend/src/Squidex/wwwroot/scripts/editor-sdk.js
  4. 2
      backend/src/Squidex/wwwroot/scripts/sidebar-content.html
  5. 5
      backend/src/Squidex/wwwroot/scripts/sidebar-context.html
  6. 45
      backend/src/Squidex/wwwroot/scripts/widget-context.html
  7. 3
      frontend/src/app/features/dashboard/pages/dashboard-config.component.ts
  8. 2
      frontend/src/app/features/dashboard/pages/dashboard-page.component.html
  9. 2
      frontend/src/app/features/teams/pages/dashboard/dashboard-page.component.html
  10. 58
      frontend/src/app/shared/components/cards/iframe-card.component.ts

5
backend/src/Squidex/wwwroot/scripts/editor-context.html

@ -11,7 +11,8 @@
textarea {
box-sizing: border-box;
resize: none;
overflow: hidden;
overflow-x: hidden;
overflow-y: hidden;
width: 100%;
}
</style>
@ -25,7 +26,7 @@
}
</script>
<textarea oninput="grow(this)" name="content" id="editor"></textarea>
<textarea oninput="grow(this)" name="content" id="editor"></textarea>
<script>
var element = document.getElementById('editor');

21
backend/src/Squidex/wwwroot/scripts/editor-sdk.d.ts

@ -1,4 +1,4 @@
declare class EditorPlugin {
declare class SquidexSidebar {
/**
* Get the current context.
*/
@ -29,6 +29,25 @@ declare class EditorPlugin {
clean(): void;
}
declare class SquidexWidget {
/**
* Get the current context.
*/
getContext(): any;
/**
* Register an function that is called when the sidebar is initialized.
*
* @param {function} callback: The callback to invoke.
*/
onInit(callback: () => void): void;
/**
* Clean the editor SDK.
*/
clean(): void;
}
declare class SquidexFormField {
/**
* Get the current value.

77
backend/src/Squidex/wwwroot/scripts/editor-sdk.js

@ -53,7 +53,7 @@ function isArrayOfStrings(value) {
return true;
}
function SquidexPlugin() {
function SquidexSidebar() {
var initHandler;
var initCalled = false;
var contentHandler;
@ -94,7 +94,7 @@ function SquidexPlugin() {
timer = measureAndNotifyParent();
var editor = {
var plugin = {
/**
* Get the current context.
*/
@ -153,7 +153,70 @@ function SquidexPlugin() {
}
};
return editor;
return plugin;
}
function SquidexWidget() {
var initHandler;
var initCalled = false;
var context;
document.body.style.margin = '0';
document.body.style.padding = '0';
function raiseInit() {
if (initHandler && !initCalled && context) {
initHandler(context);
initCalled = true;
}
}
function eventListener(event) {
if (event.source !== window) {
var type = event.data.type;
if (type === 'init') {
context = event.data.context;
raiseInit();
}
}
}
window.addEventListener('message', eventListener, false);
var plugin = {
/**
* Get the current context.
*/
getContext: function () {
return context;
},
/**
* Register an function that is called when the sidebar is initialized.
*
* @param {function} callback: The callback to invoke.
*/
onInit: function (callback) {
if (!isFunction(callback)) {
return;
}
initHandler = callback;
raiseInit();
},
/**
* Clean the editor SDK.
*/
clean: function () {
window.removeEventListener('message', eventListener);
}
};
return plugin;
}
function SquidexFormField() {
@ -310,7 +373,7 @@ function SquidexFormField() {
timer = measureAndNotifyParent();
var editor = {
var plugin = {
/**
* Get the current value.
*/
@ -634,13 +697,13 @@ function SquidexFormField() {
* Clean the editor SDK.
*/
clean: function () {
if (timer) {
window.removeEventListener('message', eventListener);
window.removeEventListener('message', eventListener);
if (timer) {
timer();
}
}
};
return editor;
return plugin;
};

2
backend/src/Squidex/wwwroot/scripts/sidebar-content.html

@ -27,7 +27,7 @@
}
</script>
<textarea oninput="grow(this)" name="content" id="editor"></textarea>
<textarea oninput="grow(this)" name="content" id="editor"></textarea>
<script>
var element = document.getElementById('editor');

5
backend/src/Squidex/wwwroot/scripts/sidebar-context.html

@ -13,7 +13,8 @@
border: 0;
border-radius: 0;
resize: none;
overflow: hidden;
overflow-x: hidden;
overflow-y: hidden;
width: 100%;
}
</style>
@ -27,7 +28,7 @@
}
</script>
<textarea oninput="grow(this)" name="content" id="editor"></textarea>
<textarea oninput="grow(this)" name="content" id="editor"></textarea>
<script>
var element = document.getElementById('editor');

45
backend/src/Squidex/wwwroot/scripts/widget-context.html

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js -->
<script src="editor-sdk.js"></script>
<style>
textarea {
border-radius: 0;
border: 0;
bottom: 0;
box-sizing: border-box;
left: 0;
position: fixed;
resize: none;
right: 0;
top: 0;
}
</style>
</head>
<body>
<textarea name="content" id="editor"></textarea>
<script>
var element = document.getElementById('editor');
// When the field is instantiated it notifies the UI that it has been loaded.
//
// Furthermore it sends the current size to the parent.
var field = new SquidexFormField();
// Init is called once with a context that contains the app name, schema name and authentication information.
field.onInit(function (context) {
element.innerHTML = JSON.stringify(context, null, 2);
grow(element);
});
</script>
</body>
</html>

3
frontend/src/app/features/dashboard/pages/dashboard-config.component.ts

@ -60,7 +60,7 @@ export class DashboardConfigComponent {
}
if (changes.app) {
this.uiState.getAppUser('dashboard.grid', this.configDefaults).pipe(take(1))
this.uiState.getAppShared('dashboard.grid', this.configDefaults).pipe(take(1))
.subscribe(dto => {
this.setConfig(dto);
});
@ -91,7 +91,6 @@ export class DashboardConfigComponent {
public resetConfig() {
this.setConfig(Types.clone(this.configDefaults));
this.saveConfig();
}

2
frontend/src/app/features/dashboard/pages/dashboard-page.component.html

@ -68,7 +68,7 @@
<sqx-content-summary-card [app]="app" [options]="item"></sqx-content-summary-card>
</ng-container>
<ng-container *ngSwitchCase="'iframe'">
<sqx-iframe-card [options]="item"></sqx-iframe-card>
<sqx-iframe-card [app]="app" [options]="item"></sqx-iframe-card>
</ng-container>
</ng-container>
</gridster-item>

2
frontend/src/app/features/teams/pages/dashboard/dashboard-page.component.html

@ -56,7 +56,7 @@
<sqx-support-card></sqx-support-card>
</ng-container>
<ng-container *ngSwitchCase="'iframe'">
<sqx-iframe-card [options]="item"></sqx-iframe-card>
<sqx-iframe-card [team]="team" [options]="item"></sqx-iframe-card>
</ng-container>
</ng-container>
</gridster-item>

58
frontend/src/app/shared/components/cards/iframe-card.component.ts

@ -5,7 +5,9 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild } from '@angular/core';
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, HostListener, Input, ViewChild } from '@angular/core';
import { ApiUrlConfig, Types } from '@app/framework';
import { AppDto, AuthService, TeamDto } from '@app/shared/internal';
@Component({
selector: 'sqx-iframe-card',
@ -14,13 +16,67 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, V
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class IFrameCardComponent implements AfterViewInit {
private readonly context: any;
private isInitialized = false;
@Input()
public options: any;
@ViewChild('iframe', { static: false })
public iframe!: ElementRef<HTMLIFrameElement>;
@Input()
public set team(value: TeamDto | undefined | null) {
if (value) {
this.context.teamId = value.id;
this.context.teamName = value.name;
}
}
@Input()
public set app(value: AppDto | undefined | null) {
if (value) {
this.context.appId = value.id;
this.context.appName = value.name;
}
}
constructor(apiUrl: ApiUrlConfig, authService: AuthService) {
this.context = { apiUrl: apiUrl.buildUrl('api'), user: authService.user };
}
public ngAfterViewInit() {
this.iframe.nativeElement.src = this.options?.src;
}
@HostListener('window:message', ['$event'])
public onWindowMessage(event: MessageEvent) {
if (event.source === this.iframe.nativeElement.contentWindow) {
const { type } = event.data;
if (type === 'started') {
this.isInitialized = true;
this.sendInit();
}
}
}
private sendInit() {
this.sendMessage('init', { context: this.context });
}
private sendMessage(type: string, payload: any) {
if (!this.iframe) {
return;
}
const iframe = this.iframe.nativeElement;
if (this.isInitialized && iframe.contentWindow && Types.isFunction(iframe.contentWindow.postMessage)) {
const message = { type, ...payload };
iframe.contentWindow.postMessage(message, '*');
}
}
}

Loading…
Cancel
Save