Skip to content

Commit 634b0aa

Browse files
committed
Lexical: Started converting drawio to TS
Converted events service to TS as part of this.
1 parent 5002a89 commit 634b0aa

9 files changed

Lines changed: 143 additions & 113 deletions

File tree

resources/js/app.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as events from './services/events';
1+
import {EventManager} from './services/events.ts';
22
import * as httpInstance from './services/http';
33
import Translations from './services/translations';
44
import * as componentMap from './components';
@@ -21,7 +21,7 @@ window.importVersioned = function importVersioned(moduleName) {
2121

2222
// Set events and http services on window
2323
window.$http = httpInstance;
24-
window.$events = events;
24+
window.$events = new EventManager();
2525

2626
// Translation setup
2727
// Creates a global function with name 'trans' to be used in the same way as the Laravel translation system

resources/js/global.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {ComponentStore} from "./services/components";
2+
import {EventManager} from "./services/events";
23

34
declare global {
45
interface Window {
56
$components: ComponentStore,
7+
$events: EventManager,
68
}
79
}
Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
// Docs: https://www.diagrams.net/doc/faq/embed-mode
22
import * as store from './store';
3-
4-
let iFrame = null;
5-
let lastApprovedOrigin;
6-
let onInit;
7-
let onSave;
3+
import {ConfirmDialog} from "../components";
4+
5+
type DrawioExportEventResponse = {
6+
action: 'export',
7+
format: string,
8+
message: string,
9+
data: string,
10+
xml: string,
11+
};
12+
13+
type DrawioSaveEventResponse = {
14+
action: 'save',
15+
xml: string,
16+
};
17+
18+
let iFrame: HTMLIFrameElement|null = null;
19+
let lastApprovedOrigin: string;
20+
let onInit: () => Promise<string>;
21+
let onSave: (data: string) => Promise<any>;
822
const saveBackupKey = 'last-drawing-save';
923

10-
function drawPostMessage(data) {
11-
iFrame.contentWindow.postMessage(JSON.stringify(data), lastApprovedOrigin);
24+
function drawPostMessage(data: Record<any, any>): void {
25+
iFrame?.contentWindow?.postMessage(JSON.stringify(data), lastApprovedOrigin);
1226
}
1327

14-
function drawEventExport(message) {
28+
function drawEventExport(message: DrawioExportEventResponse) {
1529
store.set(saveBackupKey, message.data);
1630
if (onSave) {
1731
onSave(message.data).then(() => {
@@ -20,7 +34,7 @@ function drawEventExport(message) {
2034
}
2135
}
2236

23-
function drawEventSave(message) {
37+
function drawEventSave(message: DrawioSaveEventResponse) {
2438
drawPostMessage({
2539
action: 'export', format: 'xmlpng', xml: message.xml, spin: 'Updating drawing',
2640
});
@@ -35,8 +49,10 @@ function drawEventInit() {
3549

3650
function drawEventConfigure() {
3751
const config = {};
38-
window.$events.emitPublic(iFrame, 'editor-drawio::configure', {config});
39-
drawPostMessage({action: 'configure', config});
52+
if (iFrame) {
53+
window.$events.emitPublic(iFrame, 'editor-drawio::configure', {config});
54+
drawPostMessage({action: 'configure', config});
55+
}
4056
}
4157

4258
function drawEventClose() {
@@ -47,9 +63,8 @@ function drawEventClose() {
4763

4864
/**
4965
* Receive and handle a message event from the draw.io window.
50-
* @param {MessageEvent} event
5166
*/
52-
function drawReceive(event) {
67+
function drawReceive(event: MessageEvent) {
5368
if (!event.data || event.data.length < 1) return;
5469
if (event.origin !== lastApprovedOrigin) return;
5570

@@ -59,9 +74,9 @@ function drawReceive(event) {
5974
} else if (message.event === 'exit') {
6075
drawEventClose();
6176
} else if (message.event === 'save') {
62-
drawEventSave(message);
77+
drawEventSave(message as DrawioSaveEventResponse);
6378
} else if (message.event === 'export') {
64-
drawEventExport(message);
79+
drawEventExport(message as DrawioExportEventResponse);
6580
} else if (message.event === 'configure') {
6681
drawEventConfigure();
6782
}
@@ -79,9 +94,8 @@ async function attemptRestoreIfExists() {
7994
console.error('Missing expected unsaved-drawing dialog');
8095
}
8196

82-
if (backupVal) {
83-
/** @var {ConfirmDialog} */
84-
const dialog = window.$components.firstOnElement(dialogEl, 'confirm-dialog');
97+
if (backupVal && dialogEl) {
98+
const dialog = window.$components.firstOnElement(dialogEl, 'confirm-dialog') as ConfirmDialog;
8599
const restore = await dialog.show();
86100
if (restore) {
87101
onInit = async () => backupVal;
@@ -94,11 +108,9 @@ async function attemptRestoreIfExists() {
94108
* onSaveCallback must return a promise that resolves on successful save and errors on failure.
95109
* onInitCallback must return a promise with the xml to load for the editor.
96110
* Will attempt to provide an option to restore unsaved changes if found to exist.
97-
* @param {String} drawioUrl
98-
* @param {Function<Promise<String>>} onInitCallback
99-
* @param {Function<Promise>} onSaveCallback - Is called with the drawing data on save.
111+
* onSaveCallback Is called with the drawing data on save.
100112
*/
101-
export async function show(drawioUrl, onInitCallback, onSaveCallback) {
113+
export async function show(drawioUrl: string, onInitCallback: () => Promise<string>, onSaveCallback: (data: string) => Promise<void>): Promise<void> {
102114
onInit = onInitCallback;
103115
onSave = onSaveCallback;
104116

@@ -114,7 +126,7 @@ export async function show(drawioUrl, onInitCallback, onSaveCallback) {
114126
lastApprovedOrigin = (new URL(drawioUrl)).origin;
115127
}
116128

117-
export async function upload(imageData, pageUploadedToId) {
129+
export async function upload(imageData: string, pageUploadedToId: string): Promise<{}|string> {
118130
const data = {
119131
image: imageData,
120132
uploaded_to: pageUploadedToId,
@@ -129,10 +141,8 @@ export function close() {
129141

130142
/**
131143
* Load an existing image, by fetching it as Base64 from the system.
132-
* @param drawingId
133-
* @returns {Promise<string>}
134144
*/
135-
export async function load(drawingId) {
145+
export async function load(drawingId: string): Promise<string> {
136146
try {
137147
const resp = await window.$http.get(window.baseUrl(`/images/drawio/base64/${drawingId}`));
138148
return `data:image/png;base64,${resp.data.content}`;

resources/js/services/events.js

Lines changed: 0 additions & 81 deletions
This file was deleted.

resources/js/services/events.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
export class EventManager {
2+
protected listeners: Record<string, ((data: {}) => void)[]> = {};
3+
protected stack: {name: string, data: {}}[] = [];
4+
5+
/**
6+
* Emit a custom event for any handlers to pick-up.
7+
*/
8+
emit(eventName: string, eventData: {}): void {
9+
this.stack.push({name: eventName, data: eventData});
10+
11+
const listenersToRun = this.listeners[eventName] || [];
12+
for (const listener of listenersToRun) {
13+
listener(eventData);
14+
}
15+
}
16+
17+
/**
18+
* Listen to a custom event and run the given callback when that event occurs.
19+
*/
20+
listen(eventName: string, callback: (data: {}) => void): void {
21+
if (typeof this.listeners[eventName] === 'undefined') this.listeners[eventName] = [];
22+
this.listeners[eventName].push(callback);
23+
}
24+
25+
/**
26+
* Emit an event for public use.
27+
* Sends the event via the native DOM event handling system.
28+
*/
29+
emitPublic(targetElement: Element, eventName: string, eventData: {}): void {
30+
const event = new CustomEvent(eventName, {
31+
detail: eventData,
32+
bubbles: true,
33+
});
34+
targetElement.dispatchEvent(event);
35+
}
36+
37+
/**
38+
* Emit a success event with the provided message.
39+
*/
40+
success(message: string): void {
41+
this.emit('success', message);
42+
}
43+
44+
/**
45+
* Emit an error event with the provided message.
46+
*/
47+
error(message: string): void {
48+
this.emit('error', message);
49+
}
50+
51+
/**
52+
* Notify of standard server-provided validation errors.
53+
*/
54+
showValidationErrors(responseErr: {status?: number, data?: object}): void {
55+
if (!responseErr.status) return;
56+
if (responseErr.status === 422 && responseErr.data) {
57+
const message = Object.values(responseErr.data).flat().join('\n');
58+
this.error(message);
59+
}
60+
}
61+
62+
/**
63+
* Notify standard server-provided error messages.
64+
*/
65+
showResponseError(responseErr: {status?: number, data?: {message?: string}}): void {
66+
if (!responseErr.status) return;
67+
if (responseErr.status >= 400 && responseErr.data && responseErr.data.message) {
68+
this.error(responseErr.data.message);
69+
}
70+
}
71+
}

resources/js/wysiwyg/todo.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
# Lexical based editor todo
22

3+
## In progress
4+
5+
- Add Type: Drawings
6+
- Continue converting drawio to typescript
7+
- Next step to convert http service to ts.
8+
39
## Main Todo
410

511
- Alignments: Use existing classes for blocks
612
- Alignments: Handle inline block content (image, video)
713
- Add Type: Video/media/embed
8-
- Add Type: Drawings
914
- Handle toolbars on scroll
1015
- Table features
1116
- Image paste upload
@@ -20,6 +25,7 @@
2025
- Link heading-based ID reference menu
2126
- Image gallery integration for insert
2227
- Image gallery integration for form
28+
- Drawing gallery integration
2329

2430
## Bugs
2531

resources/js/wysiwyg/ui/decorators/code-block.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {EditorDecorator} from "../framework/decorator";
22
import {EditorUiContext} from "../framework/core";
33
import {$openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
44
import {$selectionContainsNode, $selectSingleNode} from "../../helpers";
5-
import {context} from "esbuild";
65
import {BaseSelection} from "lexical";
76

87

resources/js/wysiwyg/ui/decorators/diagram.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
11
import {EditorDecorator} from "../framework/decorator";
22
import {EditorUiContext} from "../framework/core";
3+
import {$selectionContainsNode, $selectSingleNode} from "../../helpers";
4+
import {$openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
5+
import {BaseSelection} from "lexical";
6+
import {$openDrawingEditorForNode, DiagramNode} from "../../nodes/diagram";
37

48

59
export class DiagramDecorator extends EditorDecorator {
610
protected completedSetup: boolean = false;
711

812
setup(context: EditorUiContext, element: HTMLElement) {
9-
//
13+
const diagramNode = this.getNode();
14+
element.addEventListener('click', event => {
15+
context.editor.update(() => {
16+
$selectSingleNode(this.getNode());
17+
})
18+
});
19+
20+
element.addEventListener('dblclick', event => {
21+
context.editor.getEditorState().read(() => {
22+
$openDrawingEditorForNode(context.editor, (this.getNode() as DiagramNode));
23+
});
24+
});
25+
26+
const selectionChange = (selection: BaseSelection|null): void => {
27+
element.classList.toggle('selected', $selectionContainsNode(selection, diagramNode));
28+
};
29+
context.manager.onSelectionChange(selectionChange);
30+
this.onDestroy(() => {
31+
context.manager.offSelectionChange(selectionChange);
32+
});
1033

1134
this.completedSetup = true;
1235
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
1313

1414
/* Language and Environment */
15-
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15+
"target": "es2019", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
1616
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
1717
// "jsx": "preserve", /* Specify what JSX code is generated. */
1818
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */

0 commit comments

Comments
 (0)