@nordcraft/runtime 1.0.58 → 1.0.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/createElement.js +1 -1
- package/dist/components/createElement.js.map +1 -1
- package/dist/components/createNode.js.map +1 -1
- package/dist/custom-element.main.esm.js +19 -19
- package/dist/custom-element.main.esm.js.map +3 -3
- package/dist/editor/input.d.ts +1 -0
- package/dist/editor/input.js +16 -0
- package/dist/editor/input.js.map +1 -0
- package/dist/editor/links.d.ts +6 -0
- package/dist/editor/links.js +15 -0
- package/dist/editor/links.js.map +1 -0
- package/dist/editor/overlay.d.ts +12 -0
- package/dist/editor/overlay.js +20 -0
- package/dist/editor/overlay.js.map +1 -0
- package/dist/editor/types.d.ts +254 -0
- package/dist/editor/types.js +42 -0
- package/dist/editor/types.js.map +1 -0
- package/dist/editor-preview.main.js +203 -291
- package/dist/editor-preview.main.js.map +1 -1
- package/dist/page.main.esm.js +2 -2
- package/dist/page.main.esm.js.map +3 -3
- package/dist/styles/CustomPropertyStyleSheet.test.js +12 -3
- package/dist/styles/CustomPropertyStyleSheet.test.js.map +1 -1
- package/package.json +4 -4
- package/src/components/createElement.ts +1 -1
- package/src/components/createNode.ts +0 -1
- package/src/editor/input.ts +17 -0
- package/src/editor/links.ts +16 -0
- package/src/editor/overlay.ts +21 -0
- package/src/editor/types.ts +271 -0
- package/src/editor-preview.main.ts +220 -498
- package/src/styles/CustomPropertyStyleSheet.test.ts +12 -3
- package/src/editor/types.d.ts +0 -36
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
/* eslint-disable no-case-declarations */
|
|
4
4
|
/* eslint-disable no-fallthrough */
|
|
5
5
|
import { isLegacyApi } from '@nordcraft/core/dist/api/api'
|
|
6
|
+
import { isLegacyPluginAction } from '@nordcraft/core/dist/component/actionUtils'
|
|
6
7
|
import {
|
|
7
8
|
HeadTagTypes,
|
|
8
|
-
type AnimationKeyframe,
|
|
9
9
|
type Component,
|
|
10
10
|
type ComponentData,
|
|
11
11
|
type MetaEntry,
|
|
@@ -24,8 +24,6 @@ import {
|
|
|
24
24
|
type PluginFormula,
|
|
25
25
|
type ToddleFormula,
|
|
26
26
|
} from '@nordcraft/core/dist/formula/formulaTypes'
|
|
27
|
-
import { valueFormula } from '@nordcraft/core/dist/formula/formulaUtils'
|
|
28
|
-
import { getClassName } from '@nordcraft/core/dist/styling/className'
|
|
29
27
|
import { appendUnit } from '@nordcraft/core/dist/styling/customProperty'
|
|
30
28
|
import type { OldTheme, Theme } from '@nordcraft/core/dist/styling/theme'
|
|
31
29
|
import { getThemeCss, renderTheme } from '@nordcraft/core/dist/styling/theme'
|
|
@@ -57,7 +55,15 @@ import { dragMove } from './editor/drag-drop/dragMove'
|
|
|
57
55
|
import { dragReorder } from './editor/drag-drop/dragReorder'
|
|
58
56
|
import { dragStarted } from './editor/drag-drop/dragStarted'
|
|
59
57
|
import { introspectApiRequest } from './editor/graphql'
|
|
60
|
-
import
|
|
58
|
+
import { isInputTarget } from './editor/input'
|
|
59
|
+
import { updateComponentLinks } from './editor/links'
|
|
60
|
+
import { getRectData } from './editor/overlay'
|
|
61
|
+
import {
|
|
62
|
+
TextNodeComputedStyles,
|
|
63
|
+
type DragState,
|
|
64
|
+
type EditorPostMessageType,
|
|
65
|
+
type NordcraftPreviewEvent,
|
|
66
|
+
} from './editor/types'
|
|
61
67
|
import { handleAction } from './events/handleAction'
|
|
62
68
|
import type { Signal } from './signal/signal'
|
|
63
69
|
import { signal } from './signal/signal'
|
|
@@ -70,150 +76,12 @@ import type {
|
|
|
70
76
|
} from './types'
|
|
71
77
|
import { createFormulaCache } from './utils/createFormulaCache'
|
|
72
78
|
import { getNodeAndAncestors, isNodeOrAncestorConditional } from './utils/nodes'
|
|
73
|
-
import { omitSubnodeStyleForComponent } from './utils/omitStyle'
|
|
74
79
|
import { rectHasPoint } from './utils/rectHasPoint'
|
|
75
80
|
import {
|
|
76
81
|
getScrollStateRestorer,
|
|
77
82
|
storeScrollState,
|
|
78
83
|
} from './utils/storeScrollState'
|
|
79
84
|
|
|
80
|
-
type ToddlePreviewEvent =
|
|
81
|
-
| {
|
|
82
|
-
type: 'style_variant_changed'
|
|
83
|
-
variantIndex: number | null
|
|
84
|
-
}
|
|
85
|
-
| {
|
|
86
|
-
type: 'component'
|
|
87
|
-
component: Component
|
|
88
|
-
}
|
|
89
|
-
| { type: 'components'; components: Component[] }
|
|
90
|
-
| {
|
|
91
|
-
type: 'packages'
|
|
92
|
-
packages: Record<
|
|
93
|
-
string,
|
|
94
|
-
{
|
|
95
|
-
components: Record<string, Component>
|
|
96
|
-
formulas: Record<
|
|
97
|
-
string,
|
|
98
|
-
PluginFormula<FormulaHandlerV2> | PluginFormula<string>
|
|
99
|
-
>
|
|
100
|
-
actions: Record<string, PluginActionV2 | PluginAction>
|
|
101
|
-
manifest: {
|
|
102
|
-
name: string
|
|
103
|
-
// commit represents the commit hash (version) of the package
|
|
104
|
-
commit: string
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
>
|
|
108
|
-
}
|
|
109
|
-
| {
|
|
110
|
-
type: 'global_formulas'
|
|
111
|
-
formulas: Record<
|
|
112
|
-
string,
|
|
113
|
-
PluginFormula<FormulaHandlerV2> | PluginFormula<string>
|
|
114
|
-
>
|
|
115
|
-
}
|
|
116
|
-
| {
|
|
117
|
-
type: 'global_actions'
|
|
118
|
-
actions: Record<string, PluginActionV2 | PluginAction>
|
|
119
|
-
}
|
|
120
|
-
| { type: 'theme'; theme: Record<string, OldTheme | Theme> }
|
|
121
|
-
| { type: 'mode'; mode: 'design' | 'test' }
|
|
122
|
-
| { type: 'attrs'; attrs: Record<string, unknown> }
|
|
123
|
-
| { type: 'selection'; selectedNodeId: string | null }
|
|
124
|
-
| { type: 'highlight'; highlightedNodeId: string | null }
|
|
125
|
-
| {
|
|
126
|
-
type: 'click' | 'mousemove' | 'dblclick'
|
|
127
|
-
metaKey: boolean
|
|
128
|
-
x: number
|
|
129
|
-
y: number
|
|
130
|
-
}
|
|
131
|
-
| { type: 'report_document_scroll_size' }
|
|
132
|
-
| { type: 'update_inner_text'; innerText: string }
|
|
133
|
-
| { type: 'reload' }
|
|
134
|
-
| { type: 'fetch_api'; apiKey: string }
|
|
135
|
-
| { type: 'introspect_qraphql_api'; apiKey: string }
|
|
136
|
-
| { type: 'drag-started'; x: number; y: number }
|
|
137
|
-
| { type: 'drag-ended'; canceled?: true }
|
|
138
|
-
| { type: 'keydown'; key: string; altKey: boolean; metaKey: boolean }
|
|
139
|
-
| { type: 'keyup'; key: string; altKey: boolean; metaKey: boolean }
|
|
140
|
-
| {
|
|
141
|
-
type: 'get_computed_style'
|
|
142
|
-
styles?: string[]
|
|
143
|
-
}
|
|
144
|
-
| {
|
|
145
|
-
type: 'set_timeline_keyframes'
|
|
146
|
-
keyframes: Record<string, AnimationKeyframe> | null
|
|
147
|
-
}
|
|
148
|
-
| {
|
|
149
|
-
type: 'set_timeline_time'
|
|
150
|
-
time: number | null
|
|
151
|
-
timingFunction:
|
|
152
|
-
| 'linear'
|
|
153
|
-
| 'ease'
|
|
154
|
-
| 'ease-in'
|
|
155
|
-
| 'ease-out'
|
|
156
|
-
| 'ease-in-out'
|
|
157
|
-
| 'step-start'
|
|
158
|
-
| 'step-end'
|
|
159
|
-
| string
|
|
160
|
-
| undefined
|
|
161
|
-
fillMode: 'none' | 'forwards' | 'backwards' | 'both' | undefined
|
|
162
|
-
}
|
|
163
|
-
| {
|
|
164
|
-
type: 'preview_style'
|
|
165
|
-
styles: Record<string, string> | null
|
|
166
|
-
theme?: {
|
|
167
|
-
key: string
|
|
168
|
-
value: Theme
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
| {
|
|
172
|
-
type: 'preview_theme'
|
|
173
|
-
theme: string | null
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Styles required for rendering the same exact text again somewhere else (on a overlay rect in the editor)
|
|
178
|
-
*/
|
|
179
|
-
enum TextNodeComputedStyles {
|
|
180
|
-
// Caret color is important as it is the only visible part of the text node (when text is not highlighted)
|
|
181
|
-
CARET_COLOR = 'caret-color',
|
|
182
|
-
DISPLAY = 'display',
|
|
183
|
-
FONT_FAMILY = 'font-family',
|
|
184
|
-
FONT_SIZE = 'font-size',
|
|
185
|
-
FONT_WEIGHT = 'font-weight',
|
|
186
|
-
FONT_STYLE = 'font-style',
|
|
187
|
-
FONT_VARIANT = 'font-variant',
|
|
188
|
-
FONT_STRETCH = 'font-stretch',
|
|
189
|
-
LINE_HEIGHT = 'line-height',
|
|
190
|
-
TEXT_ALIGN = 'text-align',
|
|
191
|
-
TEXT_TRANSFORM = 'text-transform',
|
|
192
|
-
LETTER_SPACING = 'letter-spacing',
|
|
193
|
-
WHITE_SPACE = 'white-space',
|
|
194
|
-
WORD_SPACING = 'word-spacing',
|
|
195
|
-
TEXT_INDENT = 'text-indent',
|
|
196
|
-
TEXT_OVERFLOW = 'text-overflow',
|
|
197
|
-
TEXT_RENDERING = 'text-rendering',
|
|
198
|
-
WORD_BREAK = 'word-break',
|
|
199
|
-
WORD_WRAP = 'word-wrap',
|
|
200
|
-
DIRECTION = 'direction',
|
|
201
|
-
UNICODE_BIDI = 'unicode-bidi',
|
|
202
|
-
VERTICAL_ALIGN = 'vertical-align',
|
|
203
|
-
FONT_KERNING = 'font-kerning',
|
|
204
|
-
FONT_FEATURE_SETTINGS = 'font-feature-settings',
|
|
205
|
-
FONT_VARIATION_SETTINGS = 'font-variation-settings',
|
|
206
|
-
FONT_SMOOTHING = '-webkit-font-smoothing',
|
|
207
|
-
ANTI_ALIASING = '-moz-osx-font-smoothing',
|
|
208
|
-
FONT_OPTICAL_SIZING = 'font-optical-sizing',
|
|
209
|
-
TAB_SIZE = 'tab-size',
|
|
210
|
-
HYPHENS = 'hyphens',
|
|
211
|
-
TEXT_ORIENTATION = 'text-orientation',
|
|
212
|
-
WRITING_MODE = 'writing-mode',
|
|
213
|
-
LINE_BREAK = 'line-break',
|
|
214
|
-
OVERFLOW_WRAP = 'overflow-wrap',
|
|
215
|
-
}
|
|
216
|
-
|
|
217
85
|
let env: ToddleEnv
|
|
218
86
|
|
|
219
87
|
export const initGlobalObject = () => {
|
|
@@ -312,6 +180,18 @@ export const initGlobalObject = () => {
|
|
|
312
180
|
)
|
|
313
181
|
}
|
|
314
182
|
|
|
183
|
+
const EMPTY_COMPONENT_DATA: ComponentData = {
|
|
184
|
+
Location: {
|
|
185
|
+
query: {},
|
|
186
|
+
params: {},
|
|
187
|
+
page: '/',
|
|
188
|
+
path: '/',
|
|
189
|
+
hash: '',
|
|
190
|
+
},
|
|
191
|
+
Attributes: {},
|
|
192
|
+
Variables: {},
|
|
193
|
+
}
|
|
194
|
+
|
|
315
195
|
// imported by "/.toddle/preview" (see worker/src/preview.ts)
|
|
316
196
|
export const createRoot = (
|
|
317
197
|
domNode: HTMLElement | null = document.getElementById('App'),
|
|
@@ -319,35 +199,8 @@ export const createRoot = (
|
|
|
319
199
|
if (!domNode) {
|
|
320
200
|
throw new Error('Cant find root domNode')
|
|
321
201
|
}
|
|
322
|
-
const isInputTarget = (event: Event) => {
|
|
323
|
-
const target = event.target
|
|
324
|
-
if (target instanceof HTMLElement) {
|
|
325
|
-
if (
|
|
326
|
-
target.tagName === 'INPUT' ||
|
|
327
|
-
target.tagName === 'TEXTAREA' ||
|
|
328
|
-
target.tagName === 'SELECT' ||
|
|
329
|
-
target.tagName === 'STYLE-EDITOR'
|
|
330
|
-
) {
|
|
331
|
-
return true
|
|
332
|
-
}
|
|
333
|
-
if (target.contentEditable?.toLocaleLowerCase() === 'true') {
|
|
334
|
-
return true
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return false
|
|
338
|
-
}
|
|
339
202
|
|
|
340
|
-
const dataSignal = signal
|
|
341
|
-
Location: {
|
|
342
|
-
query: {},
|
|
343
|
-
params: {},
|
|
344
|
-
page: '/',
|
|
345
|
-
path: '/',
|
|
346
|
-
hash: '',
|
|
347
|
-
},
|
|
348
|
-
Attributes: {},
|
|
349
|
-
Variables: {},
|
|
350
|
-
})
|
|
203
|
+
const dataSignal = signal(EMPTY_COMPONENT_DATA)
|
|
351
204
|
let ctxDataSignal: Signal<ComponentData> | undefined
|
|
352
205
|
|
|
353
206
|
let ctx: ComponentContext | null = null
|
|
@@ -387,87 +240,26 @@ export const createRoot = (
|
|
|
387
240
|
let previewStyleAnimationFrame = -1
|
|
388
241
|
let timelineTimeAnimationFrame = -1
|
|
389
242
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
return component
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const registerActions = (
|
|
405
|
-
allActions: Record<string, PluginActionV2 | PluginAction>,
|
|
406
|
-
packageName?: string,
|
|
407
|
-
) => {
|
|
408
|
-
const actions: Record<string, PluginActionV2> = {}
|
|
409
|
-
Object.entries(allActions ?? {}).forEach(([name, action]) => {
|
|
410
|
-
if (typeof action.name === 'string' && action.version === undefined) {
|
|
411
|
-
// Legacy actions are self-registering. We need to execute them to register them
|
|
412
|
-
Function(action.handler)()
|
|
413
|
-
return
|
|
414
|
-
}
|
|
415
|
-
// We need to convert the handler string into a real function
|
|
416
|
-
actions[name] = {
|
|
417
|
-
...(action as PluginActionV2),
|
|
418
|
-
handler:
|
|
419
|
-
typeof action.handler === 'string'
|
|
420
|
-
? (new Function(
|
|
421
|
-
'args, ctx',
|
|
422
|
-
`${action.handler}
|
|
423
|
-
return ${safeFunctionName(action.name)}(args, ctx)`,
|
|
424
|
-
) as ActionHandlerV2)
|
|
425
|
-
: action.handler,
|
|
426
|
-
}
|
|
427
|
-
})
|
|
428
|
-
window.toddle.actions[packageName ?? window.__toddle.project] = actions
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const registerFormulas = (
|
|
432
|
-
allFormulas: Record<
|
|
433
|
-
string,
|
|
434
|
-
ToddleFormula | CodeFormula<FormulaHandlerV2> | CodeFormula<string>
|
|
435
|
-
>,
|
|
436
|
-
packageName?: string,
|
|
437
|
-
) => {
|
|
438
|
-
const formulas: Record<string, PluginFormula<FormulaHandlerV2>> = {}
|
|
439
|
-
Object.entries(allFormulas ?? {}).forEach(([name, formula]) => {
|
|
440
|
-
if (
|
|
441
|
-
!isToddleFormula<FormulaHandlerV2 | string>(formula) &&
|
|
442
|
-
typeof formula.name === 'string' &&
|
|
443
|
-
formula.version === undefined
|
|
444
|
-
) {
|
|
445
|
-
// Legacy formulas are self-registering. We need to execute them to register them
|
|
446
|
-
Function(formula.handler as unknown as string)()
|
|
447
|
-
return
|
|
448
|
-
} else if (!isToddleFormula<FormulaHandlerV2 | string>(formula)) {
|
|
449
|
-
// For code formulas we need to convert the handler string into a real function
|
|
450
|
-
formulas[name] = {
|
|
451
|
-
...formula,
|
|
452
|
-
handler:
|
|
453
|
-
typeof formula.handler === 'string'
|
|
454
|
-
? (new Function(
|
|
455
|
-
'args, ctx',
|
|
456
|
-
`${formula.handler}
|
|
457
|
-
return ${safeFunctionName(formula.name)}(args, ctx)`,
|
|
458
|
-
) as FormulaHandlerV2)
|
|
459
|
-
: formula.handler,
|
|
243
|
+
const setupDataSignalSubscribers = () => {
|
|
244
|
+
dataSignal.subscribe((data) => {
|
|
245
|
+
if (component && components && packageComponents && data) {
|
|
246
|
+
try {
|
|
247
|
+
postMessageToEditor({ type: 'data', data })
|
|
248
|
+
} catch {
|
|
249
|
+
// If we're unable to send the data, let's try to JSON serialize it
|
|
250
|
+
postMessageToEditor({
|
|
251
|
+
type: 'data',
|
|
252
|
+
data: JSON.parse(JSON.stringify(data)),
|
|
253
|
+
})
|
|
460
254
|
}
|
|
461
|
-
return
|
|
462
255
|
}
|
|
463
|
-
formulas[name] = formula as PluginFormula<FormulaHandlerV2>
|
|
464
256
|
})
|
|
465
|
-
window.toddle.formulas[packageName ?? window.__toddle.project] = formulas
|
|
466
257
|
}
|
|
258
|
+
setupDataSignalSubscribers()
|
|
467
259
|
|
|
468
260
|
window.addEventListener(
|
|
469
261
|
'message',
|
|
470
|
-
async (message: MessageEvent<
|
|
262
|
+
async (message: MessageEvent<NordcraftPreviewEvent>) => {
|
|
471
263
|
if (!message.isTrusted) {
|
|
472
264
|
console.error('UNTRUSTED MESSAGE')
|
|
473
265
|
}
|
|
@@ -481,11 +273,22 @@ export const createRoot = (
|
|
|
481
273
|
| undefined
|
|
482
274
|
|
|
483
275
|
if (message.data.component.name !== component?.name) {
|
|
276
|
+
// Store scroll state for the previous component
|
|
484
277
|
storeScrollState(component?.name)
|
|
278
|
+
// Remove all subscribers from the previous showSignal
|
|
485
279
|
showSignal.cleanSubscribers()
|
|
280
|
+
// Clear any previously overridden conditional elements
|
|
281
|
+
showSignal.set({ displayedNodes: [], testMode: mode === 'test' })
|
|
282
|
+
// Restore scroll state for the new component
|
|
486
283
|
scrollStateRestorer = getScrollStateRestorer(
|
|
487
284
|
message.data.component.name,
|
|
488
285
|
)
|
|
286
|
+
// Destroy the dataSignal (including subscribers) for the previous component
|
|
287
|
+
dataSignal.destroy()
|
|
288
|
+
// Re-subscribe all dataSignal subscribers
|
|
289
|
+
setupDataSignalSubscribers()
|
|
290
|
+
// Re-initialize the data signal for the new component
|
|
291
|
+
ctxDataSignal?.destroy()
|
|
489
292
|
}
|
|
490
293
|
|
|
491
294
|
component = updateComponentLinks(message.data.component)
|
|
@@ -550,7 +353,7 @@ export const createRoot = (
|
|
|
550
353
|
ctx.components = allComponents
|
|
551
354
|
}
|
|
552
355
|
|
|
553
|
-
updateStyle()
|
|
356
|
+
updateStyle(component)
|
|
554
357
|
update()
|
|
555
358
|
}
|
|
556
359
|
|
|
@@ -582,7 +385,7 @@ export const createRoot = (
|
|
|
582
385
|
ctx.components = allComponents
|
|
583
386
|
}
|
|
584
387
|
|
|
585
|
-
updateStyle()
|
|
388
|
+
updateStyle(component)
|
|
586
389
|
update()
|
|
587
390
|
}
|
|
588
391
|
|
|
@@ -1203,7 +1006,7 @@ export const createRoot = (
|
|
|
1203
1006
|
storeScrollState(component?.name)
|
|
1204
1007
|
})
|
|
1205
1008
|
|
|
1206
|
-
const updateStyle = () => {
|
|
1009
|
+
const updateStyle = (component: Component | null) => {
|
|
1207
1010
|
if (component) {
|
|
1208
1011
|
insertStyles(document.head, component, getAllComponents())
|
|
1209
1012
|
}
|
|
@@ -1574,80 +1377,52 @@ export const createRoot = (
|
|
|
1574
1377
|
if (
|
|
1575
1378
|
fastDeepEqual(newCtx.component.nodes, ctx?.component?.nodes) === false
|
|
1576
1379
|
) {
|
|
1577
|
-
updateStyle()
|
|
1380
|
+
updateStyle(newCtx.component)
|
|
1578
1381
|
|
|
1579
1382
|
// Remove preview styles automatically when the component changes
|
|
1580
1383
|
document.head.querySelector('[data-id="selected-node-styles"]')?.remove()
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
)
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
.forEach((nodeInstance) => {
|
|
1600
|
-
nodeInstance.classList.remove(
|
|
1601
|
-
getClassName([oldNode.style, oldNode.variants]),
|
|
1602
|
-
)
|
|
1603
|
-
nodeInstance.classList.add(
|
|
1604
|
-
getClassName([newNode.style, newNode.variants]),
|
|
1605
|
-
)
|
|
1606
|
-
})
|
|
1607
|
-
}
|
|
1384
|
+
|
|
1385
|
+
Array.from(domNode.children).forEach((child) => {
|
|
1386
|
+
if (child.tagName !== 'SCRIPT') {
|
|
1387
|
+
child.remove()
|
|
1388
|
+
}
|
|
1389
|
+
})
|
|
1390
|
+
|
|
1391
|
+
// Clear old root signal and create a new one to not keep old signals with previous root around
|
|
1392
|
+
ctxDataSignal?.destroy()
|
|
1393
|
+
ctxDataSignal = dataSignal.map((data) => data)
|
|
1394
|
+
try {
|
|
1395
|
+
const rootElem = createNode({
|
|
1396
|
+
id: 'root',
|
|
1397
|
+
path: '0',
|
|
1398
|
+
dataSignal: ctxDataSignal,
|
|
1399
|
+
ctx: newCtx,
|
|
1400
|
+
parentElement: domNode,
|
|
1401
|
+
instance: { [newCtx.component.name]: 'root' },
|
|
1608
1402
|
})
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
child.remove()
|
|
1613
|
-
}
|
|
1403
|
+
newCtx.component.onLoad?.actions.forEach((action) => {
|
|
1404
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1405
|
+
handleAction(action, dataSignal.get(), newCtx)
|
|
1614
1406
|
})
|
|
1407
|
+
rootElem.forEach((elem) => domNode.appendChild(elem))
|
|
1408
|
+
} catch (error: unknown) {
|
|
1409
|
+
const isPage = isPageComponent(newCtx.component)
|
|
1410
|
+
let name = `Unexpected error while rendering ${isPage ? 'page' : 'component'}`
|
|
1411
|
+
let message = error instanceof Error ? error.message : String(error)
|
|
1412
|
+
let panic = false
|
|
1413
|
+
if (error instanceof RangeError) {
|
|
1414
|
+
// RangeError is unrecoverable
|
|
1415
|
+
panic = true
|
|
1416
|
+
name = 'Infinite loop detected'
|
|
1417
|
+
message =
|
|
1418
|
+
'RangeError (Maximum call stack size exceeded): Remove any circular dependencies or recursive calls (Try undoing your last change). This is most likely caused by a component, formula or action using itself.'
|
|
1419
|
+
}
|
|
1615
1420
|
|
|
1616
|
-
//
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
id: 'root',
|
|
1622
|
-
path: '0',
|
|
1623
|
-
dataSignal: ctxDataSignal,
|
|
1624
|
-
ctx: newCtx,
|
|
1625
|
-
parentElement: domNode,
|
|
1626
|
-
instance: { [newCtx.component.name]: 'root' },
|
|
1627
|
-
})
|
|
1628
|
-
newCtx.component.onLoad?.actions.forEach((action) => {
|
|
1629
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1630
|
-
handleAction(action, dataSignal.get(), newCtx)
|
|
1631
|
-
})
|
|
1632
|
-
rootElem.forEach((elem) => domNode.appendChild(elem))
|
|
1633
|
-
} catch (error: unknown) {
|
|
1634
|
-
const isPage = isPageComponent(newCtx.component)
|
|
1635
|
-
let name = `Unexpected error while rendering ${isPage ? 'page' : 'component'}`
|
|
1636
|
-
let message = error instanceof Error ? error.message : String(error)
|
|
1637
|
-
let panic = false
|
|
1638
|
-
if (error instanceof RangeError) {
|
|
1639
|
-
// RangeError is unrecoverable
|
|
1640
|
-
panic = true
|
|
1641
|
-
name = 'Infinite loop detected'
|
|
1642
|
-
message =
|
|
1643
|
-
'RangeError (Maximum call stack size exceeded): Remove any circular dependencies or recursive calls (Try undoing your last change). This is most likely caused by a component, formula or action using itself.'
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
// This can be triggered by setting "type" on a select etc.
|
|
1647
|
-
if (error instanceof TypeError) {
|
|
1648
|
-
panic = true
|
|
1649
|
-
name = 'TypeError'
|
|
1650
|
-
message = `Type errors are often caused by:
|
|
1421
|
+
// This can be triggered by setting "type" on a select etc.
|
|
1422
|
+
if (error instanceof TypeError) {
|
|
1423
|
+
panic = true
|
|
1424
|
+
name = 'TypeError'
|
|
1425
|
+
message = `Type errors are often caused by:
|
|
1651
1426
|
|
|
1652
1427
|
• Trying to set a read-only property (like "type" on a select element).
|
|
1653
1428
|
|
|
@@ -1656,36 +1431,35 @@ export const createRoot = (
|
|
|
1656
1431
|
• Trying to access a property on an undefined or null value.
|
|
1657
1432
|
|
|
1658
1433
|
• Trying to call a method on an undefined or null value.`
|
|
1659
|
-
|
|
1434
|
+
}
|
|
1660
1435
|
|
|
1661
|
-
|
|
1436
|
+
console.error(name, message, error)
|
|
1662
1437
|
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1438
|
+
if (panic) {
|
|
1439
|
+
// Show error overlay in the editor until next update
|
|
1440
|
+
const panicScreen = createPanicScreen({
|
|
1441
|
+
name: name,
|
|
1442
|
+
message,
|
|
1443
|
+
isPage,
|
|
1444
|
+
cause: error,
|
|
1445
|
+
})
|
|
1671
1446
|
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
}
|
|
1447
|
+
// Replace the inner HTML of the editor preview with the panic screen
|
|
1448
|
+
domNode.innerHTML = ''
|
|
1449
|
+
domNode.appendChild(panicScreen)
|
|
1450
|
+
} else {
|
|
1451
|
+
// Otherwise send a toast to the editor with the error (unknown errors may be recoverable), if not please add the error-type to the above
|
|
1452
|
+
sendEditorToast(name, message, {
|
|
1453
|
+
type: 'critical',
|
|
1454
|
+
})
|
|
1681
1455
|
}
|
|
1682
|
-
postMessageToEditor({
|
|
1683
|
-
type: 'style',
|
|
1684
|
-
time: new Intl.DateTimeFormat('en-GB', {
|
|
1685
|
-
timeStyle: 'long',
|
|
1686
|
-
}).format(new Date()),
|
|
1687
|
-
})
|
|
1688
1456
|
}
|
|
1457
|
+
postMessageToEditor({
|
|
1458
|
+
type: 'style',
|
|
1459
|
+
time: new Intl.DateTimeFormat('en-GB', {
|
|
1460
|
+
timeStyle: 'long',
|
|
1461
|
+
}).format(new Date()),
|
|
1462
|
+
})
|
|
1689
1463
|
}
|
|
1690
1464
|
|
|
1691
1465
|
ctx = newCtx
|
|
@@ -1758,68 +1532,7 @@ export const createRoot = (
|
|
|
1758
1532
|
return ctx
|
|
1759
1533
|
}
|
|
1760
1534
|
|
|
1761
|
-
|
|
1762
|
-
if (isInputTarget(event)) {
|
|
1763
|
-
return
|
|
1764
|
-
}
|
|
1765
|
-
switch (event.key) {
|
|
1766
|
-
case 'k':
|
|
1767
|
-
if (event.metaKey) {
|
|
1768
|
-
event.preventDefault()
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
postMessageToEditor({
|
|
1772
|
-
type: 'keydown',
|
|
1773
|
-
event: {
|
|
1774
|
-
key: event.key,
|
|
1775
|
-
metaKey: event.metaKey,
|
|
1776
|
-
shiftKey: event.shiftKey,
|
|
1777
|
-
altKey: event.altKey,
|
|
1778
|
-
},
|
|
1779
|
-
})
|
|
1780
|
-
})
|
|
1781
|
-
document.addEventListener('keyup', (event) => {
|
|
1782
|
-
if (isInputTarget(event)) {
|
|
1783
|
-
return
|
|
1784
|
-
}
|
|
1785
|
-
postMessageToEditor({
|
|
1786
|
-
type: 'keyup',
|
|
1787
|
-
event: {
|
|
1788
|
-
key: event.key,
|
|
1789
|
-
metaKey: event.metaKey,
|
|
1790
|
-
shiftKey: event.shiftKey,
|
|
1791
|
-
altKey: event.altKey,
|
|
1792
|
-
},
|
|
1793
|
-
})
|
|
1794
|
-
})
|
|
1795
|
-
document.addEventListener('keypress', (event) => {
|
|
1796
|
-
if (isInputTarget(event)) {
|
|
1797
|
-
return
|
|
1798
|
-
}
|
|
1799
|
-
postMessageToEditor({
|
|
1800
|
-
type: 'keypress',
|
|
1801
|
-
event: {
|
|
1802
|
-
key: event.key,
|
|
1803
|
-
metaKey: event.metaKey,
|
|
1804
|
-
shiftKey: event.shiftKey,
|
|
1805
|
-
altKey: event.altKey,
|
|
1806
|
-
},
|
|
1807
|
-
})
|
|
1808
|
-
})
|
|
1809
|
-
|
|
1810
|
-
dataSignal.subscribe((data) => {
|
|
1811
|
-
if (component && components && packageComponents && data) {
|
|
1812
|
-
try {
|
|
1813
|
-
postMessageToEditor({ type: 'data', data })
|
|
1814
|
-
} catch {
|
|
1815
|
-
// If we're unable to send the data, let's try to JSON serialize it
|
|
1816
|
-
postMessageToEditor({
|
|
1817
|
-
type: 'data',
|
|
1818
|
-
data: JSON.parse(JSON.stringify(data)),
|
|
1819
|
-
})
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
})
|
|
1535
|
+
initKeyListeners()
|
|
1823
1536
|
|
|
1824
1537
|
const clearSelectedStyleVariant = () => {
|
|
1825
1538
|
if (styleVariantSelection) {
|
|
@@ -1880,28 +1593,6 @@ export const createRoot = (
|
|
|
1880
1593
|
})()
|
|
1881
1594
|
}
|
|
1882
1595
|
|
|
1883
|
-
function getRectData(selectedNode: Element | null | undefined) {
|
|
1884
|
-
if (!selectedNode) {
|
|
1885
|
-
return null
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
const { borderRadius, rotate } = window.getComputedStyle(selectedNode)
|
|
1889
|
-
const rect: DOMRect = selectedNode.getBoundingClientRect()
|
|
1890
|
-
|
|
1891
|
-
return {
|
|
1892
|
-
left: rect.left,
|
|
1893
|
-
right: rect.right,
|
|
1894
|
-
top: rect.top,
|
|
1895
|
-
bottom: rect.bottom,
|
|
1896
|
-
width: rect.width,
|
|
1897
|
-
height: rect.height,
|
|
1898
|
-
x: rect.x,
|
|
1899
|
-
y: rect.y,
|
|
1900
|
-
borderRadius: borderRadius.split(' '),
|
|
1901
|
-
rotate,
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
1596
|
const insertOrReplaceHeadNode = (id: string, node: Node) => {
|
|
1906
1597
|
const existing = document.head.querySelector(`[data-meta-id="${id}"]`)
|
|
1907
1598
|
if (existing) {
|
|
@@ -2004,90 +1695,121 @@ const insertTheme = (
|
|
|
2004
1695
|
parent.appendChild(styleElem)
|
|
2005
1696
|
}
|
|
2006
1697
|
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
}
|
|
2012
|
-
| {
|
|
2013
|
-
type: 'selection'
|
|
2014
|
-
selectedNodeId: string | null
|
|
2015
|
-
}
|
|
2016
|
-
| {
|
|
2017
|
-
type: 'highlight'
|
|
2018
|
-
highlightedNodeId: string | null
|
|
2019
|
-
}
|
|
2020
|
-
| {
|
|
2021
|
-
type: 'navigate'
|
|
2022
|
-
name: string
|
|
2023
|
-
}
|
|
2024
|
-
| {
|
|
2025
|
-
type: 'documentScrollSize'
|
|
2026
|
-
scrollHeight: number
|
|
2027
|
-
scrollWidth: number
|
|
2028
|
-
}
|
|
2029
|
-
| {
|
|
2030
|
-
type: 'nodeMoved'
|
|
2031
|
-
copy: boolean
|
|
2032
|
-
parent?: string | null
|
|
2033
|
-
index?: number
|
|
2034
|
-
}
|
|
2035
|
-
| {
|
|
2036
|
-
type: 'computedStyle'
|
|
2037
|
-
computedStyle: Record<string, string>
|
|
2038
|
-
}
|
|
2039
|
-
| {
|
|
2040
|
-
type: 'style'
|
|
2041
|
-
time: string
|
|
1698
|
+
const initKeyListeners = () => {
|
|
1699
|
+
document.addEventListener('keydown', (event) => {
|
|
1700
|
+
if (isInputTarget(event)) {
|
|
1701
|
+
return
|
|
2042
1702
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
1703
|
+
switch (event.key) {
|
|
1704
|
+
case 'k':
|
|
1705
|
+
if (event.metaKey) {
|
|
1706
|
+
event.preventDefault()
|
|
1707
|
+
}
|
|
2048
1708
|
}
|
|
2049
|
-
|
|
2050
|
-
type: 'keydown'
|
|
1709
|
+
postMessageToEditor({
|
|
1710
|
+
type: 'keydown',
|
|
2051
1711
|
event: {
|
|
2052
|
-
key:
|
|
2053
|
-
metaKey:
|
|
2054
|
-
shiftKey:
|
|
2055
|
-
altKey:
|
|
2056
|
-
}
|
|
1712
|
+
key: event.key,
|
|
1713
|
+
metaKey: event.metaKey,
|
|
1714
|
+
shiftKey: event.shiftKey,
|
|
1715
|
+
altKey: event.altKey,
|
|
1716
|
+
},
|
|
1717
|
+
})
|
|
1718
|
+
})
|
|
1719
|
+
document.addEventListener('keyup', (event) => {
|
|
1720
|
+
if (isInputTarget(event)) {
|
|
1721
|
+
return
|
|
2057
1722
|
}
|
|
2058
|
-
|
|
2059
|
-
type: 'keyup'
|
|
1723
|
+
postMessageToEditor({
|
|
1724
|
+
type: 'keyup',
|
|
2060
1725
|
event: {
|
|
2061
|
-
key:
|
|
2062
|
-
metaKey:
|
|
2063
|
-
shiftKey:
|
|
2064
|
-
altKey:
|
|
2065
|
-
}
|
|
1726
|
+
key: event.key,
|
|
1727
|
+
metaKey: event.metaKey,
|
|
1728
|
+
shiftKey: event.shiftKey,
|
|
1729
|
+
altKey: event.altKey,
|
|
1730
|
+
},
|
|
1731
|
+
})
|
|
1732
|
+
})
|
|
1733
|
+
document.addEventListener('keypress', (event) => {
|
|
1734
|
+
if (isInputTarget(event)) {
|
|
1735
|
+
return
|
|
2066
1736
|
}
|
|
2067
|
-
|
|
2068
|
-
type: 'keypress'
|
|
1737
|
+
postMessageToEditor({
|
|
1738
|
+
type: 'keypress',
|
|
2069
1739
|
event: {
|
|
2070
|
-
key:
|
|
2071
|
-
metaKey:
|
|
2072
|
-
shiftKey:
|
|
2073
|
-
altKey:
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
1740
|
+
key: event.key,
|
|
1741
|
+
metaKey: event.metaKey,
|
|
1742
|
+
shiftKey: event.shiftKey,
|
|
1743
|
+
altKey: event.altKey,
|
|
1744
|
+
},
|
|
1745
|
+
})
|
|
1746
|
+
})
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
const registerActions = (
|
|
1750
|
+
allActions: Record<string, PluginAction>,
|
|
1751
|
+
packageName?: string,
|
|
1752
|
+
) => {
|
|
1753
|
+
const actions: Record<string, PluginActionV2> = {}
|
|
1754
|
+
Object.entries(allActions ?? {}).forEach(([name, action]) => {
|
|
1755
|
+
if (isLegacyPluginAction(action)) {
|
|
1756
|
+
// Legacy actions are self-registering. We need to execute them to register them
|
|
1757
|
+
Function(action.handler)()
|
|
1758
|
+
return
|
|
2080
1759
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
1760
|
+
// We need to convert the handler string into a real function
|
|
1761
|
+
actions[name] = {
|
|
1762
|
+
...(action as PluginActionV2),
|
|
1763
|
+
handler:
|
|
1764
|
+
typeof action.handler === 'string'
|
|
1765
|
+
? (new Function(
|
|
1766
|
+
'args, ctx',
|
|
1767
|
+
`${action.handler}
|
|
1768
|
+
return ${safeFunctionName(action.name)}(args, ctx)`,
|
|
1769
|
+
) as ActionHandlerV2)
|
|
1770
|
+
: action.handler,
|
|
2084
1771
|
}
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
1772
|
+
})
|
|
1773
|
+
window.toddle.actions[packageName ?? window.__toddle.project] = actions
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
const registerFormulas = (
|
|
1777
|
+
allFormulas: Record<
|
|
1778
|
+
string,
|
|
1779
|
+
ToddleFormula | CodeFormula<FormulaHandlerV2> | CodeFormula<string>
|
|
1780
|
+
>,
|
|
1781
|
+
packageName?: string,
|
|
1782
|
+
) => {
|
|
1783
|
+
const formulas: Record<string, PluginFormula<FormulaHandlerV2>> = {}
|
|
1784
|
+
Object.entries(allFormulas ?? {}).forEach(([name, formula]) => {
|
|
1785
|
+
if (
|
|
1786
|
+
!isToddleFormula<FormulaHandlerV2 | string>(formula) &&
|
|
1787
|
+
typeof formula.name === 'string' &&
|
|
1788
|
+
formula.version === undefined
|
|
1789
|
+
) {
|
|
1790
|
+
// Legacy formulas are self-registering. We need to execute them to register them
|
|
1791
|
+
Function(formula.handler as unknown as string)()
|
|
1792
|
+
return
|
|
1793
|
+
} else if (!isToddleFormula<FormulaHandlerV2 | string>(formula)) {
|
|
1794
|
+
// For code formulas we need to convert the handler string into a real function
|
|
1795
|
+
formulas[name] = {
|
|
1796
|
+
...formula,
|
|
1797
|
+
handler:
|
|
1798
|
+
typeof formula.handler === 'string'
|
|
1799
|
+
? (new Function(
|
|
1800
|
+
'args, ctx',
|
|
1801
|
+
`${formula.handler}
|
|
1802
|
+
return ${safeFunctionName(formula.name)}(args, ctx)`,
|
|
1803
|
+
) as FormulaHandlerV2)
|
|
1804
|
+
: formula.handler,
|
|
1805
|
+
}
|
|
1806
|
+
return
|
|
2089
1807
|
}
|
|
1808
|
+
formulas[name] = formula as PluginFormula<FormulaHandlerV2>
|
|
1809
|
+
})
|
|
1810
|
+
window.toddle.formulas[packageName ?? window.__toddle.project] = formulas
|
|
1811
|
+
}
|
|
2090
1812
|
|
|
2091
|
-
const postMessageToEditor = (message:
|
|
1813
|
+
const postMessageToEditor = (message: EditorPostMessageType) => {
|
|
2092
1814
|
window.parent?.postMessage(message, '*')
|
|
2093
1815
|
}
|