@nordcraft/runtime 1.0.57 → 1.0.59

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.
Files changed (39) hide show
  1. package/dist/components/createComponent.js +12 -13
  2. package/dist/components/createComponent.js.map +1 -1
  3. package/dist/components/createElement.js +9 -7
  4. package/dist/components/createElement.js.map +1 -1
  5. package/dist/components/createNode.js.map +1 -1
  6. package/dist/custom-element.main.esm.js +27 -27
  7. package/dist/custom-element.main.esm.js.map +4 -4
  8. package/dist/editor/input.d.ts +1 -0
  9. package/dist/editor/input.js +16 -0
  10. package/dist/editor/input.js.map +1 -0
  11. package/dist/editor/links.d.ts +6 -0
  12. package/dist/editor/links.js +15 -0
  13. package/dist/editor/links.js.map +1 -0
  14. package/dist/editor/overlay.d.ts +12 -0
  15. package/dist/editor/overlay.js +20 -0
  16. package/dist/editor/overlay.js.map +1 -0
  17. package/dist/editor/types.d.ts +254 -0
  18. package/dist/editor/types.js +42 -0
  19. package/dist/editor/types.js.map +1 -0
  20. package/dist/editor-preview.main.js +147 -198
  21. package/dist/editor-preview.main.js.map +1 -1
  22. package/dist/page.main.esm.js +3 -3
  23. package/dist/page.main.esm.js.map +4 -4
  24. package/dist/page.main.js +20 -0
  25. package/dist/page.main.js.map +1 -1
  26. package/dist/styles/CustomPropertyStyleSheet.test.js +12 -3
  27. package/dist/styles/CustomPropertyStyleSheet.test.js.map +1 -1
  28. package/package.json +4 -4
  29. package/src/components/createComponent.ts +27 -20
  30. package/src/components/createElement.ts +29 -21
  31. package/src/components/createNode.ts +0 -1
  32. package/src/editor/input.ts +17 -0
  33. package/src/editor/links.ts +16 -0
  34. package/src/editor/overlay.ts +21 -0
  35. package/src/editor/types.ts +271 -0
  36. package/src/editor-preview.main.ts +180 -411
  37. package/src/page.main.ts +23 -0
  38. package/src/styles/CustomPropertyStyleSheet.test.ts +12 -3
  39. package/src/editor/types.d.ts +0 -36
@@ -3,15 +3,17 @@
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 { HeadTagTypes, } from '@nordcraft/core/dist/component/component.types';
7
8
  import { isPageComponent } from '@nordcraft/core/dist/component/isPageComponent';
8
9
  import { applyFormula, isToddleFormula, } from '@nordcraft/core/dist/formula/formula';
9
10
  import {} from '@nordcraft/core/dist/formula/formulaTypes';
10
- import { valueFormula } from '@nordcraft/core/dist/formula/formulaUtils';
11
11
  import { getClassName } from '@nordcraft/core/dist/styling/className';
12
+ import { appendUnit } from '@nordcraft/core/dist/styling/customProperty';
12
13
  import { getThemeCss, renderTheme } from '@nordcraft/core/dist/styling/theme';
13
14
  import { mapObject, omitKeys } from '@nordcraft/core/dist/utils/collections';
14
15
  import { safeFunctionName } from '@nordcraft/core/dist/utils/handlerUtils';
16
+ import { isDefined } from '@nordcraft/core/dist/utils/util';
15
17
  import * as libActions from '@nordcraft/std-lib/dist/actions';
16
18
  import * as libFormulas from '@nordcraft/std-lib/dist/formulas';
17
19
  import fastDeepEqual from 'fast-deep-equal';
@@ -26,6 +28,10 @@ import { dragMove } from './editor/drag-drop/dragMove';
26
28
  import { dragReorder } from './editor/drag-drop/dragReorder';
27
29
  import { dragStarted } from './editor/drag-drop/dragStarted';
28
30
  import { introspectApiRequest } from './editor/graphql';
31
+ import { isInputTarget } from './editor/input';
32
+ import { updateComponentLinks } from './editor/links';
33
+ import { getRectData } from './editor/overlay';
34
+ import { TextNodeComputedStyles, } from './editor/types';
29
35
  import { handleAction } from './events/handleAction';
30
36
  import { signal } from './signal/signal';
31
37
  import { insertStyles, styleToCss } from './styles/style';
@@ -34,34 +40,6 @@ import { getNodeAndAncestors, isNodeOrAncestorConditional } from './utils/nodes'
34
40
  import { omitSubnodeStyleForComponent } from './utils/omitStyle';
35
41
  import { rectHasPoint } from './utils/rectHasPoint';
36
42
  import { getScrollStateRestorer, storeScrollState, } from './utils/storeScrollState';
37
- /**
38
- * Styles required for rendering the same exact text again somewhere else (on a overlay rect in the editor)
39
- */
40
- var TextNodeComputedStyles;
41
- (function (TextNodeComputedStyles) {
42
- // Caret color is important as it is the only visible part of the text node (when text is not highlighted)
43
- TextNodeComputedStyles["CARET_COLOR"] = "caret-color";
44
- TextNodeComputedStyles["FONT_FAMILY"] = "font-family";
45
- TextNodeComputedStyles["FONT_SIZE"] = "font-size";
46
- TextNodeComputedStyles["FONT_WEIGHT"] = "font-weight";
47
- TextNodeComputedStyles["FONT_STYLE"] = "font-style";
48
- TextNodeComputedStyles["FONT_VARIANT"] = "font-variant";
49
- TextNodeComputedStyles["FONT_STRETCH"] = "font-stretch";
50
- TextNodeComputedStyles["LINE_HEIGHT"] = "line-height";
51
- TextNodeComputedStyles["TEXT_ALIGN"] = "text-align";
52
- TextNodeComputedStyles["TEXT_TRANSFORM"] = "text-transform";
53
- TextNodeComputedStyles["LETTER_SPACING"] = "letter-spacing";
54
- TextNodeComputedStyles["WHITE_SPACE"] = "white-space";
55
- TextNodeComputedStyles["WORD_SPACING"] = "word-spacing";
56
- TextNodeComputedStyles["TEXT_INDENT"] = "text-indent";
57
- TextNodeComputedStyles["TEXT_OVERFLOW"] = "text-overflow";
58
- TextNodeComputedStyles["TEXT_RENDERING"] = "text-rendering";
59
- TextNodeComputedStyles["WORD_BREAK"] = "word-break";
60
- TextNodeComputedStyles["WORD_WRAP"] = "word-wrap";
61
- TextNodeComputedStyles["DIRECTION"] = "direction";
62
- TextNodeComputedStyles["UNICODE_BIDI"] = "unicode-bidi";
63
- TextNodeComputedStyles["VERTICAL_ALIGN"] = "vertical-align";
64
- })(TextNodeComputedStyles || (TextNodeComputedStyles = {}));
65
43
  let env;
66
44
  export const initGlobalObject = () => {
67
45
  env = {
@@ -144,37 +122,23 @@ export const initGlobalObject = () => {
144
122
  : undefined));
145
123
  Object.entries(libActions).forEach(([name, module]) => window.toddle.registerAction('@toddle/' + name, module.default));
146
124
  };
125
+ const EMPTY_COMPONENT_DATA = {
126
+ Location: {
127
+ query: {},
128
+ params: {},
129
+ page: '/',
130
+ path: '/',
131
+ hash: '',
132
+ },
133
+ Attributes: {},
134
+ Variables: {},
135
+ };
147
136
  // imported by "/.toddle/preview" (see worker/src/preview.ts)
148
137
  export const createRoot = (domNode = document.getElementById('App')) => {
149
138
  if (!domNode) {
150
139
  throw new Error('Cant find root domNode');
151
140
  }
152
- const isInputTarget = (event) => {
153
- const target = event.target;
154
- if (target instanceof HTMLElement) {
155
- if (target.tagName === 'INPUT' ||
156
- target.tagName === 'TEXTAREA' ||
157
- target.tagName === 'SELECT' ||
158
- target.tagName === 'STYLE-EDITOR') {
159
- return true;
160
- }
161
- if (target.contentEditable?.toLocaleLowerCase() === 'true') {
162
- return true;
163
- }
164
- }
165
- return false;
166
- };
167
- const dataSignal = signal({
168
- Location: {
169
- query: {},
170
- params: {},
171
- page: '/',
172
- path: '/',
173
- hash: '',
174
- },
175
- Attributes: {},
176
- Variables: {},
177
- });
141
+ const dataSignal = signal(EMPTY_COMPONENT_DATA);
178
142
  let ctxDataSignal;
179
143
  let ctx = null;
180
144
  let mode = 'design';
@@ -204,63 +168,23 @@ export const createRoot = (domNode = document.getElementById('App')) => {
204
168
  let metaKey = false;
205
169
  let previewStyleAnimationFrame = -1;
206
170
  let timelineTimeAnimationFrame = -1;
207
- /**
208
- * Modifies all link nodes on a component
209
- * NOTE: alters in place
210
- */
211
- const updateComponentLinks = (component) => {
212
- // Find all links and add target="_blank" to them
213
- Object.entries(component.nodes ?? {}).forEach(([_, node]) => {
214
- if (node.type === 'element' && node.tag === 'a') {
215
- node.attrs['target'] = valueFormula('_blank');
216
- }
217
- });
218
- return component;
219
- };
220
- const registerActions = (allActions, packageName) => {
221
- const actions = {};
222
- Object.entries(allActions ?? {}).forEach(([name, action]) => {
223
- if (typeof action.name === 'string' && action.version === undefined) {
224
- // Legacy actions are self-registering. We need to execute them to register them
225
- Function(action.handler)();
226
- return;
227
- }
228
- // We need to convert the handler string into a real function
229
- actions[name] = {
230
- ...action,
231
- handler: typeof action.handler === 'string'
232
- ? new Function('args, ctx', `${action.handler}
233
- return ${safeFunctionName(action.name)}(args, ctx)`)
234
- : action.handler,
235
- };
236
- });
237
- window.toddle.actions[packageName ?? window.__toddle.project] = actions;
238
- };
239
- const registerFormulas = (allFormulas, packageName) => {
240
- const formulas = {};
241
- Object.entries(allFormulas ?? {}).forEach(([name, formula]) => {
242
- if (!isToddleFormula(formula) &&
243
- typeof formula.name === 'string' &&
244
- formula.version === undefined) {
245
- // Legacy formulas are self-registering. We need to execute them to register them
246
- Function(formula.handler)();
247
- return;
248
- }
249
- else if (!isToddleFormula(formula)) {
250
- // For code formulas we need to convert the handler string into a real function
251
- formulas[name] = {
252
- ...formula,
253
- handler: typeof formula.handler === 'string'
254
- ? new Function('args, ctx', `${formula.handler}
255
- return ${safeFunctionName(formula.name)}(args, ctx)`)
256
- : formula.handler,
257
- };
258
- return;
171
+ const setupDataSignalSubscribers = () => {
172
+ dataSignal.subscribe((data) => {
173
+ if (component && components && packageComponents && data) {
174
+ try {
175
+ postMessageToEditor({ type: 'data', data });
176
+ }
177
+ catch {
178
+ // If we're unable to send the data, let's try to JSON serialize it
179
+ postMessageToEditor({
180
+ type: 'data',
181
+ data: JSON.parse(JSON.stringify(data)),
182
+ });
183
+ }
259
184
  }
260
- formulas[name] = formula;
261
185
  });
262
- window.toddle.formulas[packageName ?? window.__toddle.project] = formulas;
263
186
  };
187
+ setupDataSignalSubscribers();
264
188
  window.addEventListener('message', async (message) => {
265
189
  if (!message.isTrusted) {
266
190
  console.error('UNTRUSTED MESSAGE');
@@ -272,9 +196,20 @@ export const createRoot = (domNode = document.getElementById('App')) => {
272
196
  }
273
197
  let scrollStateRestorer;
274
198
  if (message.data.component.name !== component?.name) {
199
+ // Store scroll state for the previous component
275
200
  storeScrollState(component?.name);
201
+ // Remove all subscribers from the previous showSignal
276
202
  showSignal.cleanSubscribers();
203
+ // Clear any previously overridden conditional elements
204
+ showSignal.set({ displayedNodes: [], testMode: mode === 'test' });
205
+ // Restore scroll state for the new component
277
206
  scrollStateRestorer = getScrollStateRestorer(message.data.component.name);
207
+ // Destroy the dataSignal (including subscribers) for the previous component
208
+ dataSignal.destroy();
209
+ // Re-subscribe all dataSignal subscribers
210
+ setupDataSignalSubscribers();
211
+ // Re-initialize the data signal for the new component
212
+ ctxDataSignal?.destroy();
278
213
  }
279
214
  component = updateComponentLinks(message.data.component);
280
215
  if (components && packageComponents && ctx) {
@@ -913,10 +848,10 @@ export const createRoot = (domNode = document.getElementById('App')) => {
913
848
  const selectedStyleVariant = nodeLookup.node.variants?.[styleVariantSelection.styleVariantIndex] ?? { style: {} };
914
849
  // Add a style element specific to the selected element which
915
850
  // is only applied when the preview is in design mode
916
- const styleVariantCustomProperties = Object.entries(selectedStyleVariant.customProperties ?? {})
917
- .map(([customPropertyName, customProperty]) => ({
918
- name: customPropertyName,
919
- value: applyFormula(customProperty.formula, {
851
+ const styleVariantCustomProperties = Object.fromEntries(Object.entries(selectedStyleVariant.customProperties ?? {})
852
+ .map(([customPropertyName, customProperty]) => [
853
+ customPropertyName,
854
+ appendUnit(applyFormula(customProperty.formula, {
920
855
  data: {
921
856
  Attributes: dataSignal.get().Attributes,
922
857
  Variables: dataSignal.get().Variables,
@@ -928,9 +863,9 @@ export const createRoot = (domNode = document.getElementById('App')) => {
928
863
  package: ctx?.package,
929
864
  toddle: window.toddle,
930
865
  env,
931
- }),
932
- }))
933
- .filter(({ value }) => value !== undefined);
866
+ }), customProperty.unit),
867
+ ])
868
+ .filter(([, value]) => isDefined(value)));
934
869
  const styleElem = document.createElement('style');
935
870
  const pseudoElement = selectedStyleVariant.pseudoElement
936
871
  ? `::${selectedStyleVariant.pseudoElement}`
@@ -941,7 +876,7 @@ export const createRoot = (domNode = document.getElementById('App')) => {
941
876
  ${styleToCss({
942
877
  ...(!pseudoElement && nodeLookup.node.style),
943
878
  ...selectedStyleVariant.style,
944
- ...Object.fromEntries(styleVariantCustomProperties.map(({ name, value }) => [name, value])),
879
+ ...styleVariantCustomProperties,
945
880
  })}
946
881
  }
947
882
  `));
@@ -1297,68 +1232,7 @@ export const createRoot = (domNode = document.getElementById('App')) => {
1297
1232
  }
1298
1233
  return ctx;
1299
1234
  };
1300
- document.addEventListener('keydown', (event) => {
1301
- if (isInputTarget(event)) {
1302
- return;
1303
- }
1304
- switch (event.key) {
1305
- case 'k':
1306
- if (event.metaKey) {
1307
- event.preventDefault();
1308
- }
1309
- }
1310
- postMessageToEditor({
1311
- type: 'keydown',
1312
- event: {
1313
- key: event.key,
1314
- metaKey: event.metaKey,
1315
- shiftKey: event.shiftKey,
1316
- altKey: event.altKey,
1317
- },
1318
- });
1319
- });
1320
- document.addEventListener('keyup', (event) => {
1321
- if (isInputTarget(event)) {
1322
- return;
1323
- }
1324
- postMessageToEditor({
1325
- type: 'keyup',
1326
- event: {
1327
- key: event.key,
1328
- metaKey: event.metaKey,
1329
- shiftKey: event.shiftKey,
1330
- altKey: event.altKey,
1331
- },
1332
- });
1333
- });
1334
- document.addEventListener('keypress', (event) => {
1335
- if (isInputTarget(event)) {
1336
- return;
1337
- }
1338
- postMessageToEditor({
1339
- type: 'keypress',
1340
- event: {
1341
- key: event.key,
1342
- metaKey: event.metaKey,
1343
- shiftKey: event.shiftKey,
1344
- altKey: event.altKey,
1345
- },
1346
- });
1347
- });
1348
- dataSignal.subscribe((data) => {
1349
- if (component && components && packageComponents && data) {
1350
- try {
1351
- postMessageToEditor({ type: 'data', data });
1352
- }
1353
- catch {
1354
- // If we're unable to send the data, let's try to JSON serialize it
1355
- postMessageToEditor({
1356
- type: 'data',
1357
- data: JSON.parse(JSON.stringify(data)),
1358
- });
1359
- }
1360
- }
1361
- });
1235
+ initKeyListeners();
1362
1236
  const clearSelectedStyleVariant = () => {
1363
1237
  if (styleVariantSelection) {
1364
1238
  const styleElem = document.head.querySelector(`[data-hash="${styleVariantSelection.nodeId}"]`);
@@ -1405,25 +1279,6 @@ export const createRoot = (domNode = document.getElementById('App')) => {
1405
1279
  requestAnimationFrame(() => syncOverlayRects(selectionRect, highlightRect));
1406
1280
  })();
1407
1281
  };
1408
- function getRectData(selectedNode) {
1409
- if (!selectedNode) {
1410
- return null;
1411
- }
1412
- const { borderRadius, rotate } = window.getComputedStyle(selectedNode);
1413
- const rect = selectedNode.getBoundingClientRect();
1414
- return {
1415
- left: rect.left,
1416
- right: rect.right,
1417
- top: rect.top,
1418
- bottom: rect.bottom,
1419
- width: rect.width,
1420
- height: rect.height,
1421
- x: rect.x,
1422
- y: rect.y,
1423
- borderRadius: borderRadius.split(' '),
1424
- rotate,
1425
- };
1426
- }
1427
1282
  const insertOrReplaceHeadNode = (id, node) => {
1428
1283
  const existing = document.head.querySelector(`[data-meta-id="${id}"]`);
1429
1284
  if (existing) {
@@ -1499,6 +1354,100 @@ const insertTheme = (parent, themes) => {
1499
1354
  });
1500
1355
  parent.appendChild(styleElem);
1501
1356
  };
1357
+ const initKeyListeners = () => {
1358
+ document.addEventListener('keydown', (event) => {
1359
+ if (isInputTarget(event)) {
1360
+ return;
1361
+ }
1362
+ switch (event.key) {
1363
+ case 'k':
1364
+ if (event.metaKey) {
1365
+ event.preventDefault();
1366
+ }
1367
+ }
1368
+ postMessageToEditor({
1369
+ type: 'keydown',
1370
+ event: {
1371
+ key: event.key,
1372
+ metaKey: event.metaKey,
1373
+ shiftKey: event.shiftKey,
1374
+ altKey: event.altKey,
1375
+ },
1376
+ });
1377
+ });
1378
+ document.addEventListener('keyup', (event) => {
1379
+ if (isInputTarget(event)) {
1380
+ return;
1381
+ }
1382
+ postMessageToEditor({
1383
+ type: 'keyup',
1384
+ event: {
1385
+ key: event.key,
1386
+ metaKey: event.metaKey,
1387
+ shiftKey: event.shiftKey,
1388
+ altKey: event.altKey,
1389
+ },
1390
+ });
1391
+ });
1392
+ document.addEventListener('keypress', (event) => {
1393
+ if (isInputTarget(event)) {
1394
+ return;
1395
+ }
1396
+ postMessageToEditor({
1397
+ type: 'keypress',
1398
+ event: {
1399
+ key: event.key,
1400
+ metaKey: event.metaKey,
1401
+ shiftKey: event.shiftKey,
1402
+ altKey: event.altKey,
1403
+ },
1404
+ });
1405
+ });
1406
+ };
1407
+ const registerActions = (allActions, packageName) => {
1408
+ const actions = {};
1409
+ Object.entries(allActions ?? {}).forEach(([name, action]) => {
1410
+ if (isLegacyPluginAction(action)) {
1411
+ // Legacy actions are self-registering. We need to execute them to register them
1412
+ Function(action.handler)();
1413
+ return;
1414
+ }
1415
+ // We need to convert the handler string into a real function
1416
+ actions[name] = {
1417
+ ...action,
1418
+ handler: typeof action.handler === 'string'
1419
+ ? new Function('args, ctx', `${action.handler}
1420
+ return ${safeFunctionName(action.name)}(args, ctx)`)
1421
+ : action.handler,
1422
+ };
1423
+ });
1424
+ window.toddle.actions[packageName ?? window.__toddle.project] = actions;
1425
+ };
1426
+ const registerFormulas = (allFormulas, packageName) => {
1427
+ const formulas = {};
1428
+ Object.entries(allFormulas ?? {}).forEach(([name, formula]) => {
1429
+ if (!isToddleFormula(formula) &&
1430
+ typeof formula.name === 'string' &&
1431
+ formula.version === undefined) {
1432
+ // Legacy formulas are self-registering. We need to execute them to register them
1433
+ Function(formula.handler)();
1434
+ return;
1435
+ }
1436
+ else if (!isToddleFormula(formula)) {
1437
+ // For code formulas we need to convert the handler string into a real function
1438
+ formulas[name] = {
1439
+ ...formula,
1440
+ handler: typeof formula.handler === 'string'
1441
+ ? new Function('args, ctx', `${formula.handler}
1442
+ return ${safeFunctionName(formula.name)}(args, ctx)`)
1443
+ : formula.handler,
1444
+ };
1445
+ return;
1446
+ }
1447
+ formulas[name] = formula;
1448
+ });
1449
+ window.toddle.formulas[packageName ?? window.__toddle.project] = formulas;
1450
+ };
1502
1451
  const postMessageToEditor = (message) => {
1503
1452
  window.parent?.postMessage(message, '*');
1504
1453
  };