@nordcraft/runtime 1.0.34 → 1.0.36

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.
@@ -3,6 +3,7 @@
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 { type ApiRequest } from '@nordcraft/core/dist/api/apiTypes'
6
7
  import type {
7
8
  AnimationKeyframe,
8
9
  Component,
@@ -44,6 +45,7 @@ import { dragEnded } from './editor/drag-drop/dragEnded'
44
45
  import { dragMove } from './editor/drag-drop/dragMove'
45
46
  import { dragReorder } from './editor/drag-drop/dragReorder'
46
47
  import { dragStarted } from './editor/drag-drop/dragStarted'
48
+ import { introspectApiRequest } from './editor/graphql'
47
49
  import type { DragState } from './editor/types'
48
50
  import { handleAction } from './events/handleAction'
49
51
  import type { Signal } from './signal/signal'
@@ -99,6 +101,7 @@ type ToddlePreviewEvent =
99
101
  | { type: 'update_inner_text'; innerText: string }
100
102
  | { type: 'reload' }
101
103
  | { type: 'fetch_api'; apiKey: string }
104
+ | { type: 'introspect_qraphql_api'; apiKey: string }
102
105
  | { type: 'drag-started'; x: number; y: number }
103
106
  | { type: 'drag-ended'; canceled?: true }
104
107
  | { type: 'keydown'; key: string; altKey: boolean; metaKey: boolean }
@@ -338,7 +341,7 @@ export const createRoot = (
338
341
 
339
342
  window.addEventListener(
340
343
  'message',
341
- (message: MessageEvent<ToddlePreviewEvent>) => {
344
+ async (message: MessageEvent<ToddlePreviewEvent>) => {
342
345
  if (!message.isTrusted) {
343
346
  console.error('UNTRUSTED MESSAGE')
344
347
  }
@@ -489,27 +492,21 @@ export const createRoot = (
489
492
  element.value.type === 'value'
490
493
  ) {
491
494
  const computedStyle = window.getComputedStyle(node)
492
- window.parent?.postMessage(
493
- {
494
- type: 'textComputedStyle',
495
- computedStyle: Object.fromEntries(
496
- Object.values(TextNodeComputedStyles).map((style) => [
497
- style,
498
- computedStyle.getPropertyValue(style),
499
- ]),
500
- ),
501
- },
502
- '*',
503
- )
495
+ postMessageToEditor({
496
+ type: 'textComputedStyle',
497
+ computedStyle: Object.fromEntries(
498
+ Object.values(TextNodeComputedStyles).map((style) => [
499
+ style,
500
+ computedStyle.getPropertyValue(style),
501
+ ]),
502
+ ),
503
+ })
504
504
  } else if (node && node.getAttribute('data-node-type') !== 'text') {
505
505
  // Reset computed style on blur
506
- window.parent?.postMessage(
507
- {
508
- type: 'textComputedStyle',
509
- computedStyle: {},
510
- },
511
- '*',
512
- )
506
+ postMessageToEditor({
507
+ type: 'textComputedStyle',
508
+ computedStyle: {},
509
+ })
513
510
  }
514
511
  }
515
512
  return
@@ -600,13 +597,10 @@ export const createRoot = (
600
597
  if (root && id) {
601
598
  const nodeLookup = getNodeAndAncestors(component, root, id)
602
599
  if (nodeLookup?.node.type === 'text') {
603
- window.parent?.postMessage(
604
- {
605
- type: 'selection',
606
- selectedNodeId: id,
607
- },
608
- '*',
609
- )
600
+ postMessageToEditor({
601
+ type: 'selection',
602
+ selectedNodeId: id,
603
+ })
610
604
  } else {
611
605
  const firstTextChild =
612
606
  nodeLookup?.node.type === 'element'
@@ -615,33 +609,24 @@ export const createRoot = (
615
609
  )
616
610
  : undefined
617
611
  if (firstTextChild) {
618
- window.parent?.postMessage(
619
- {
620
- type: 'selection',
621
- selectedNodeId: `${id}.0`,
622
- },
623
- '*',
624
- )
612
+ postMessageToEditor({
613
+ type: 'selection',
614
+ selectedNodeId: `${id}.0`,
615
+ })
625
616
  }
626
617
  }
627
618
  }
628
619
  } else {
629
- window.parent?.postMessage(
630
- {
631
- type: 'selection',
632
- selectedNodeId: id,
633
- },
634
- '*',
635
- )
620
+ postMessageToEditor({
621
+ type: 'selection',
622
+ selectedNodeId: id,
623
+ })
636
624
  }
637
625
  } else if (type === 'mousemove' && id !== highlightedNodeId) {
638
- window.parent?.postMessage(
639
- {
640
- type: 'highlight',
641
- highlightedNodeId: id,
642
- },
643
- '*',
644
- )
626
+ postMessageToEditor({
627
+ type: 'highlight',
628
+ highlightedNodeId: id,
629
+ })
645
630
  } else if (
646
631
  type === 'dblclick' &&
647
632
  id &&
@@ -656,23 +641,17 @@ export const createRoot = (
656
641
  nodeLookup?.node.type === 'component' &&
657
642
  nodeLookup.node.name
658
643
  ) {
659
- window.parent?.postMessage(
660
- {
661
- type: 'navigate',
662
- name: nodeLookup.node.name,
663
- },
664
- '*',
665
- )
644
+ postMessageToEditor({
645
+ type: 'navigate',
646
+ name: nodeLookup.node.name,
647
+ })
666
648
  }
667
649
  // Double click on text node should select the text node for editing
668
650
  else if (nodeLookup?.node.type === 'text') {
669
- window.parent?.postMessage(
670
- {
671
- type: 'selection',
672
- selectedNodeId: id,
673
- },
674
- '*',
675
- )
651
+ postMessageToEditor({
652
+ type: 'selection',
653
+ selectedNodeId: id,
654
+ })
676
655
  }
677
656
  }
678
657
  }
@@ -684,19 +663,16 @@ export const createRoot = (
684
663
  // We request manually instead of automatic to avoid mutation observer spam.
685
664
  // Also, reporting automatically proved unreliable when elements' height was in %
686
665
  case 'report_document_scroll_size':
687
- window.parent?.postMessage(
688
- {
689
- type: 'documentScrollSize',
690
- scrollHeight: domNode.scrollHeight,
691
- scrollWidth: domNode.scrollWidth,
692
- },
693
- '*',
694
- )
666
+ postMessageToEditor({
667
+ type: 'documentScrollSize',
668
+ scrollHeight: domNode.scrollHeight,
669
+ scrollWidth: domNode.scrollWidth,
670
+ })
695
671
  break
696
672
  case 'reload':
697
673
  window.location.reload()
698
674
  break
699
- case 'fetch_api':
675
+ case 'fetch_api': {
700
676
  const { apiKey } = message.data
701
677
  dataSignal.update((data) => ({
702
678
  ...data,
@@ -711,6 +687,36 @@ export const createRoot = (
711
687
  }))
712
688
  void ctx?.apis[apiKey]?.fetch({} as any)
713
689
  break
690
+ }
691
+ case 'introspect_qraphql_api': {
692
+ const { apiKey } = message.data
693
+ const api = component?.apis[apiKey]
694
+ if (api && !isLegacyApi(api) && component) {
695
+ const Attributes = mapObject(
696
+ component.attributes,
697
+ ([name, { testValue }]) => [name, testValue],
698
+ )
699
+ const formulaContext: FormulaContext = {
700
+ component,
701
+ data: { Attributes },
702
+ root: document,
703
+ package: ctx?.package,
704
+ toddle: window.toddle,
705
+ env,
706
+ }
707
+ const introspectionResult = await introspectApiRequest({
708
+ api: api as ApiRequest,
709
+ componentName: component.name,
710
+ formulaContext,
711
+ })
712
+ postMessageToEditor({
713
+ type: 'introspectionResult',
714
+ data: introspectionResult,
715
+ apiKey,
716
+ })
717
+ }
718
+ break
719
+ }
714
720
  case 'drag-started':
715
721
  const draggedElement = getDOMNodeFromNodeId(selectedNodeId)
716
722
  if (!draggedElement || !draggedElement.parentElement) {
@@ -760,17 +766,14 @@ export const createRoot = (
760
766
  dragState?.copy)
761
767
  ) {
762
768
  void dragEnded(dragState, false).then(() => {
763
- window.parent?.postMessage(
764
- {
765
- type: 'nodeMoved',
766
- copy: Boolean(dragState?.copy),
767
- parent: parentDataId,
768
- index: !isNaN(nextSiblingId)
769
- ? nextSiblingId
770
- : component?.nodes[parentNodeId]?.children?.length,
771
- },
772
- '*',
773
- )
769
+ postMessageToEditor({
770
+ type: 'nodeMoved',
771
+ copy: Boolean(dragState?.copy),
772
+ parent: parentDataId,
773
+ index: !isNaN(nextSiblingId)
774
+ ? nextSiblingId
775
+ : component?.nodes[parentNodeId]?.children?.length,
776
+ })
774
777
  dragState = null
775
778
  })
776
779
  } else {
@@ -786,16 +789,12 @@ export const createRoot = (
786
789
  ]
787
790
  if (selectedPermutation && !message.data.canceled) {
788
791
  void dragEnded(dragState, false).then(() => {
789
- window.parent?.postMessage(
790
- {
791
- type: 'nodeMoved',
792
- copy: Boolean(dragState?.copy),
793
- parent:
794
- selectedPermutation?.parent.getAttribute('data-id'),
795
- index: selectedPermutation?.index,
796
- },
797
- '*',
798
- )
792
+ postMessageToEditor({
793
+ type: 'nodeMoved',
794
+ copy: Boolean(dragState?.copy),
795
+ parent: selectedPermutation?.parent.getAttribute('data-id'),
796
+ index: selectedPermutation?.index,
797
+ })
799
798
  dragState = null
800
799
  })
801
800
  } else {
@@ -843,18 +842,15 @@ export const createRoot = (
843
842
 
844
843
  const { styles } = message.data
845
844
  const computedStyle = window.getComputedStyle(selectedNode)
846
- window.parent?.postMessage(
847
- {
848
- type: 'computedStyle',
849
- computedStyle: Object.fromEntries(
850
- (styles ?? []).map((style) => [
851
- style,
852
- computedStyle.getPropertyValue(style),
853
- ]),
854
- ),
855
- },
856
- '*',
857
- )
845
+ postMessageToEditor({
846
+ type: 'computedStyle',
847
+ computedStyle: Object.fromEntries(
848
+ (styles ?? []).map((style) => [
849
+ style,
850
+ computedStyle.getPropertyValue(style),
851
+ ]),
852
+ ),
853
+ })
858
854
  break
859
855
 
860
856
  case 'set_timeline_keyframes':
@@ -1460,15 +1456,12 @@ export const createRoot = (
1460
1456
  }
1461
1457
  console.error(name, message, error)
1462
1458
  }
1463
- window.parent?.postMessage(
1464
- {
1465
- type: 'style',
1466
- time: new Intl.DateTimeFormat('en-GB', {
1467
- timeStyle: 'long',
1468
- }).format(new Date()),
1469
- },
1470
- '*',
1471
- )
1459
+ postMessageToEditor({
1460
+ type: 'style',
1461
+ time: new Intl.DateTimeFormat('en-GB', {
1462
+ timeStyle: 'long',
1463
+ }).format(new Date()),
1464
+ })
1472
1465
  }
1473
1466
  }
1474
1467
 
@@ -1483,17 +1476,14 @@ export const createRoot = (
1483
1476
  component,
1484
1477
  components,
1485
1478
  triggerEvent: (event, data) => {
1486
- window.parent?.postMessage(
1487
- {
1488
- type: 'component event',
1489
- event,
1490
- time: new Intl.DateTimeFormat('en-GB', {
1491
- timeStyle: 'long',
1492
- }).format(new Date()),
1493
- data,
1494
- },
1495
- '*',
1496
- )
1479
+ postMessageToEditor({
1480
+ type: 'component event',
1481
+ event,
1482
+ time: new Intl.DateTimeFormat('en-GB', {
1483
+ timeStyle: 'long',
1484
+ }).format(new Date()),
1485
+ data,
1486
+ })
1497
1487
  },
1498
1488
  dataSignal,
1499
1489
  root: document,
@@ -1552,64 +1542,55 @@ export const createRoot = (
1552
1542
  event.preventDefault()
1553
1543
  }
1554
1544
  }
1555
- window.parent?.postMessage(
1556
- {
1557
- type: 'keydown',
1558
- event: {
1559
- key: event.key,
1560
- metaKey: event.metaKey,
1561
- shiftKey: event.shiftKey,
1562
- altKey: event.altKey,
1563
- },
1545
+ postMessageToEditor({
1546
+ type: 'keydown',
1547
+ event: {
1548
+ key: event.key,
1549
+ metaKey: event.metaKey,
1550
+ shiftKey: event.shiftKey,
1551
+ altKey: event.altKey,
1564
1552
  },
1565
- '*',
1566
- )
1553
+ })
1567
1554
  })
1568
1555
  document.addEventListener('keyup', (event) => {
1569
1556
  if (isInputTarget(event)) {
1570
1557
  return
1571
1558
  }
1572
- window.parent?.postMessage(
1573
- {
1574
- type: 'keyup',
1575
- event: {
1576
- key: event.key,
1577
- metaKey: event.metaKey,
1578
- shiftKey: event.shiftKey,
1579
- altKey: event.altKey,
1580
- },
1559
+ postMessageToEditor({
1560
+ type: 'keyup',
1561
+ event: {
1562
+ key: event.key,
1563
+ metaKey: event.metaKey,
1564
+ shiftKey: event.shiftKey,
1565
+ altKey: event.altKey,
1581
1566
  },
1582
- '*',
1583
- )
1567
+ })
1584
1568
  })
1585
1569
  document.addEventListener('keypress', (event) => {
1586
1570
  if (isInputTarget(event)) {
1587
1571
  return
1588
1572
  }
1589
- window.parent?.postMessage(
1590
- {
1591
- type: 'keypress',
1592
- event: {
1593
- key: event.key,
1594
- metaKey: event.metaKey,
1595
- shiftKey: event.shiftKey,
1596
- altKey: event.altKey,
1597
- },
1573
+ postMessageToEditor({
1574
+ type: 'keypress',
1575
+ event: {
1576
+ key: event.key,
1577
+ metaKey: event.metaKey,
1578
+ shiftKey: event.shiftKey,
1579
+ altKey: event.altKey,
1598
1580
  },
1599
- '*',
1600
- )
1581
+ })
1601
1582
  })
1602
1583
 
1603
1584
  dataSignal.subscribe((data) => {
1604
1585
  if (component && components && packageComponents && data) {
1605
1586
  try {
1606
- window.parent?.postMessage({ type: 'data', data }, '*')
1587
+ postMessageToEditor({ type: 'data', data })
1607
1588
  } catch {
1608
1589
  // If we're unable to send the data, let's try to JSON serialize it
1609
- window.parent?.postMessage(
1610
- { type: 'data', data: JSON.parse(JSON.stringify(data)) },
1611
- '*',
1612
- )
1590
+ postMessageToEditor({
1591
+ type: 'data',
1592
+ data: JSON.parse(JSON.stringify(data)),
1593
+ })
1613
1594
  }
1614
1595
  }
1615
1596
  })
@@ -1655,24 +1636,18 @@ export const createRoot = (
1655
1636
  ) {
1656
1637
  const selectionRect = getRectData(getDOMNodeFromNodeId(selectedNodeId))
1657
1638
  if (!fastDeepEqual(prevSelectionRect, selectionRect)) {
1658
- window.parent?.postMessage(
1659
- {
1660
- type: 'selectionRect',
1661
- rect: selectionRect,
1662
- },
1663
- '*',
1664
- )
1639
+ postMessageToEditor({
1640
+ type: 'selectionRect',
1641
+ rect: selectionRect,
1642
+ })
1665
1643
  }
1666
1644
 
1667
1645
  const highlightRect = getRectData(getDOMNodeFromNodeId(highlightedNodeId))
1668
1646
  if (!fastDeepEqual(prevHighlightedRect, highlightRect)) {
1669
- window.parent?.postMessage(
1670
- {
1671
- type: 'highlightRect',
1672
- rect: highlightRect,
1673
- },
1674
- '*',
1675
- )
1647
+ postMessageToEditor({
1648
+ type: 'highlightRect',
1649
+ rect: highlightRect,
1650
+ })
1676
1651
  }
1677
1652
 
1678
1653
  requestAnimationFrame(() => syncOverlayRects(selectionRect, highlightRect))
@@ -1796,3 +1771,91 @@ const insertTheme = (parent: HTMLElement, theme: Theme | OldTheme) => {
1796
1771
  })
1797
1772
  parent.appendChild(styleElem)
1798
1773
  }
1774
+
1775
+ type PostMessageType =
1776
+ | {
1777
+ type: 'textComputedStyle'
1778
+ computedStyle: Record<string, string>
1779
+ }
1780
+ | {
1781
+ type: 'selection'
1782
+ selectedNodeId: string | null
1783
+ }
1784
+ | {
1785
+ type: 'highlight'
1786
+ highlightedNodeId: string | null
1787
+ }
1788
+ | {
1789
+ type: 'navigate'
1790
+ name: string
1791
+ }
1792
+ | {
1793
+ type: 'documentScrollSize'
1794
+ scrollHeight: number
1795
+ scrollWidth: number
1796
+ }
1797
+ | {
1798
+ type: 'nodeMoved'
1799
+ copy: boolean
1800
+ parent?: string | null
1801
+ index?: number
1802
+ }
1803
+ | {
1804
+ type: 'computedStyle'
1805
+ computedStyle: Record<string, string>
1806
+ }
1807
+ | {
1808
+ type: 'style'
1809
+ time: string
1810
+ }
1811
+ | {
1812
+ type: 'component event'
1813
+ event: any
1814
+ time: string
1815
+ data: any
1816
+ }
1817
+ | {
1818
+ type: 'keydown'
1819
+ event: {
1820
+ key: string
1821
+ metaKey: boolean
1822
+ shiftKey: boolean
1823
+ altKey: boolean
1824
+ }
1825
+ }
1826
+ | {
1827
+ type: 'keyup'
1828
+ event: {
1829
+ key: string
1830
+ metaKey: boolean
1831
+ shiftKey: boolean
1832
+ altKey: boolean
1833
+ }
1834
+ }
1835
+ | {
1836
+ type: 'keypress'
1837
+ event: {
1838
+ key: string
1839
+ metaKey: boolean
1840
+ shiftKey: boolean
1841
+ altKey: boolean
1842
+ }
1843
+ }
1844
+ | { type: 'data'; data: ComponentData }
1845
+ | {
1846
+ type: 'selectionRect'
1847
+ rect: ReturnType<typeof getRectData>
1848
+ }
1849
+ | {
1850
+ type: 'highlightRect'
1851
+ rect: ReturnType<typeof getRectData>
1852
+ }
1853
+ | {
1854
+ type: 'introspectionResult'
1855
+ data: any
1856
+ apiKey: string
1857
+ }
1858
+
1859
+ const postMessageToEditor = (message: PostMessageType) => {
1860
+ window.parent?.postMessage(message, '*')
1861
+ }