@nordcraft/runtime 1.0.0

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 (168) hide show
  1. package/README.md +5 -0
  2. package/dist/api/createAPI.d.ts +20 -0
  3. package/dist/api/createAPI.js +319 -0
  4. package/dist/api/createAPI.js.map +1 -0
  5. package/dist/api/createAPIv2.d.ts +7 -0
  6. package/dist/api/createAPIv2.js +686 -0
  7. package/dist/api/createAPIv2.js.map +1 -0
  8. package/dist/components/createComponent.d.ts +13 -0
  9. package/dist/components/createComponent.js +216 -0
  10. package/dist/components/createComponent.js.map +1 -0
  11. package/dist/components/createElement.d.ts +3 -0
  12. package/dist/components/createElement.js +208 -0
  13. package/dist/components/createElement.js.map +1 -0
  14. package/dist/components/createNode.d.ts +22 -0
  15. package/dist/components/createNode.js +272 -0
  16. package/dist/components/createNode.js.map +1 -0
  17. package/dist/components/createSlot.d.ts +3 -0
  18. package/dist/components/createSlot.js +49 -0
  19. package/dist/components/createSlot.js.map +1 -0
  20. package/dist/components/createText.d.ts +23 -0
  21. package/dist/components/createText.js +68 -0
  22. package/dist/components/createText.js.map +1 -0
  23. package/dist/components/createText.test.d.ts +1 -0
  24. package/dist/components/createText.test.js +113 -0
  25. package/dist/components/createText.test.js.map +1 -0
  26. package/dist/components/renderComponent.d.ts +34 -0
  27. package/dist/components/renderComponent.js +66 -0
  28. package/dist/components/renderComponent.js.map +1 -0
  29. package/dist/context/isContextProvider.d.ts +2 -0
  30. package/dist/context/isContextProvider.js +5 -0
  31. package/dist/context/isContextProvider.js.map +1 -0
  32. package/dist/context/subscribeToContext.d.ts +4 -0
  33. package/dist/context/subscribeToContext.js +93 -0
  34. package/dist/context/subscribeToContext.js.map +1 -0
  35. package/dist/custom-components/components.d.ts +1 -0
  36. package/dist/custom-components/components.js +2 -0
  37. package/dist/custom-components/components.js.map +1 -0
  38. package/dist/custom-components/toddle-portal.d.ts +6 -0
  39. package/dist/custom-components/toddle-portal.js +20 -0
  40. package/dist/custom-components/toddle-portal.js.map +1 -0
  41. package/dist/custom-element/ToddleComponent.d.ts +37 -0
  42. package/dist/custom-element/ToddleComponent.js +244 -0
  43. package/dist/custom-element/ToddleComponent.js.map +1 -0
  44. package/dist/custom-element/defineComponents.d.ts +26 -0
  45. package/dist/custom-element/defineComponents.js +42 -0
  46. package/dist/custom-element/defineComponents.js.map +1 -0
  47. package/dist/custom-element.main.d.ts +3 -0
  48. package/dist/custom-element.main.esm.js +266 -0
  49. package/dist/custom-element.main.esm.js.map +7 -0
  50. package/dist/custom-element.main.js +14 -0
  51. package/dist/custom-element.main.js.map +1 -0
  52. package/dist/debug/logState.d.ts +4 -0
  53. package/dist/debug/logState.js +19 -0
  54. package/dist/debug/logState.js.map +1 -0
  55. package/dist/editor/drag-drop/dragEnded.d.ts +2 -0
  56. package/dist/editor/drag-drop/dragEnded.js +56 -0
  57. package/dist/editor/drag-drop/dragEnded.js.map +1 -0
  58. package/dist/editor/drag-drop/dragMove.d.ts +3 -0
  59. package/dist/editor/drag-drop/dragMove.js +74 -0
  60. package/dist/editor/drag-drop/dragMove.js.map +1 -0
  61. package/dist/editor/drag-drop/dragReorder.d.ts +3 -0
  62. package/dist/editor/drag-drop/dragReorder.js +92 -0
  63. package/dist/editor/drag-drop/dragReorder.js.map +1 -0
  64. package/dist/editor/drag-drop/dragStarted.d.ts +9 -0
  65. package/dist/editor/drag-drop/dragStarted.js +100 -0
  66. package/dist/editor/drag-drop/dragStarted.js.map +1 -0
  67. package/dist/editor/drag-drop/dropHighlight.d.ts +16 -0
  68. package/dist/editor/drag-drop/dropHighlight.js +50 -0
  69. package/dist/editor/drag-drop/dropHighlight.js.map +1 -0
  70. package/dist/editor/drag-drop/getInsertAreas.d.ts +20 -0
  71. package/dist/editor/drag-drop/getInsertAreas.js +220 -0
  72. package/dist/editor/drag-drop/getInsertAreas.js.map +1 -0
  73. package/dist/editor-preview.main.d.ts +19 -0
  74. package/dist/editor-preview.main.js +1303 -0
  75. package/dist/editor-preview.main.js.map +1 -0
  76. package/dist/events/handleAction.d.ts +3 -0
  77. package/dist/events/handleAction.js +307 -0
  78. package/dist/events/handleAction.js.map +1 -0
  79. package/dist/page.main.d.ts +7 -0
  80. package/dist/page.main.esm.js +8 -0
  81. package/dist/page.main.esm.js.map +7 -0
  82. package/dist/page.main.js +395 -0
  83. package/dist/page.main.js.map +1 -0
  84. package/dist/signal/signal.d.ts +19 -0
  85. package/dist/signal/signal.js +65 -0
  86. package/dist/signal/signal.js.map +1 -0
  87. package/dist/styles/style.d.ts +4 -0
  88. package/dist/styles/style.js +196 -0
  89. package/dist/styles/style.js.map +1 -0
  90. package/dist/utils/BatchQueue.d.ts +10 -0
  91. package/dist/utils/BatchQueue.js +25 -0
  92. package/dist/utils/BatchQueue.js.map +1 -0
  93. package/dist/utils/createFormulaCache.d.ts +3 -0
  94. package/dist/utils/createFormulaCache.js +81 -0
  95. package/dist/utils/createFormulaCache.js.map +1 -0
  96. package/dist/utils/findNearestLine.d.ts +13 -0
  97. package/dist/utils/findNearestLine.js +74 -0
  98. package/dist/utils/findNearestLine.js.map +1 -0
  99. package/dist/utils/findNearestLine.test.d.ts +1 -0
  100. package/dist/utils/findNearestLine.test.js +59 -0
  101. package/dist/utils/findNearestLine.test.js.map +1 -0
  102. package/dist/utils/getDragData.d.ts +1 -0
  103. package/dist/utils/getDragData.js +10 -0
  104. package/dist/utils/getDragData.js.map +1 -0
  105. package/dist/utils/getElementTagName.d.ts +3 -0
  106. package/dist/utils/getElementTagName.js +7 -0
  107. package/dist/utils/getElementTagName.js.map +1 -0
  108. package/dist/utils/nodes.d.ts +21 -0
  109. package/dist/utils/nodes.js +89 -0
  110. package/dist/utils/nodes.js.map +1 -0
  111. package/dist/utils/omitStyle.d.ts +2 -0
  112. package/dist/utils/omitStyle.js +13 -0
  113. package/dist/utils/omitStyle.js.map +1 -0
  114. package/dist/utils/rectHasPoint.d.ts +2 -0
  115. package/dist/utils/rectHasPoint.js +4 -0
  116. package/dist/utils/rectHasPoint.js.map +1 -0
  117. package/dist/utils/setAttribute.d.ts +4 -0
  118. package/dist/utils/setAttribute.js +57 -0
  119. package/dist/utils/setAttribute.js.map +1 -0
  120. package/dist/utils/tryStartViewTransition.d.ts +5 -0
  121. package/dist/utils/tryStartViewTransition.js +14 -0
  122. package/dist/utils/tryStartViewTransition.js.map +1 -0
  123. package/dist/utils/url.d.ts +2 -0
  124. package/dist/utils/url.js +36 -0
  125. package/dist/utils/url.js.map +1 -0
  126. package/package.json +25 -0
  127. package/src/api/createAPI.ts +375 -0
  128. package/src/api/createAPIv2.ts +931 -0
  129. package/src/components/createComponent.ts +280 -0
  130. package/src/components/createElement.ts +240 -0
  131. package/src/components/createNode.ts +381 -0
  132. package/src/components/createSlot.ts +61 -0
  133. package/src/components/createText.test.ts +117 -0
  134. package/src/components/createText.ts +104 -0
  135. package/src/components/renderComponent.ts +145 -0
  136. package/src/context/isContextProvider.ts +12 -0
  137. package/src/context/subscribeToContext.ts +135 -0
  138. package/src/custom-components/components.ts +1 -0
  139. package/src/custom-components/toddle-portal.ts +19 -0
  140. package/src/custom-element/ToddleComponent.ts +315 -0
  141. package/src/custom-element/defineComponents.ts +65 -0
  142. package/src/custom-element.main.ts +24 -0
  143. package/src/debug/logState.ts +30 -0
  144. package/src/editor/drag-drop/dragEnded.ts +75 -0
  145. package/src/editor/drag-drop/dragMove.ts +95 -0
  146. package/src/editor/drag-drop/dragReorder.ts +137 -0
  147. package/src/editor/drag-drop/dragStarted.ts +145 -0
  148. package/src/editor/drag-drop/dropHighlight.ts +82 -0
  149. package/src/editor/drag-drop/getInsertAreas.ts +235 -0
  150. package/src/editor/types.d.ts +36 -0
  151. package/src/editor-preview.main.ts +1782 -0
  152. package/src/events/handleAction.ts +387 -0
  153. package/src/page.main.ts +489 -0
  154. package/src/signal/signal.ts +74 -0
  155. package/src/styles/style.ts +254 -0
  156. package/src/types.d.ts +93 -0
  157. package/src/utils/BatchQueue.ts +24 -0
  158. package/src/utils/createFormulaCache.ts +96 -0
  159. package/src/utils/findNearestLine.test.ts +65 -0
  160. package/src/utils/findNearestLine.ts +92 -0
  161. package/src/utils/getDragData.ts +11 -0
  162. package/src/utils/getElementTagName.ts +14 -0
  163. package/src/utils/nodes.ts +125 -0
  164. package/src/utils/omitStyle.ts +19 -0
  165. package/src/utils/rectHasPoint.ts +5 -0
  166. package/src/utils/setAttribute.ts +56 -0
  167. package/src/utils/tryStartViewTransition.ts +32 -0
  168. package/src/utils/url.ts +45 -0
@@ -0,0 +1,125 @@
1
+ import type {
2
+ Component,
3
+ NodeModel,
4
+ } from '@nordcraft/core/dist/component/component.types'
5
+
6
+ type NodeWithNodeId = NodeModel & { nodeId: string }
7
+
8
+ interface NodeAndAncestorLookup {
9
+ node: NodeWithNodeId
10
+ ancestors: NodeWithNodeId[]
11
+ }
12
+
13
+ export const getNodeAndAncestors = (
14
+ component: Component,
15
+ root: NodeModel,
16
+ id: unknown,
17
+ ): NodeAndAncestorLookup | undefined => {
18
+ if (typeof id !== 'string' || id.length === 0) {
19
+ return undefined
20
+ }
21
+ const path = id.split('.')
22
+ const pathParsed = path.map((n) => parseInt(n))
23
+ const ancestors: NodeWithNodeId[] = []
24
+ // nodePath skips the root element as it's selected as the initial
25
+ // value in the reduce below
26
+ const nodePath = pathParsed.slice(1)
27
+ const node = nodePath.reduce((node: NodeModel | undefined, childIndex, i) => {
28
+ switch (node?.type) {
29
+ // 'text' elements don't have any children
30
+ case 'element':
31
+ case 'component':
32
+ case 'slot':
33
+ // Ancestors are elements before the target node
34
+ if (i <= nodePath.length - 1) {
35
+ ancestors.push({
36
+ ...node,
37
+ // Use the original path as origin to get correct nodeIds
38
+ nodeId: path.slice(0, i + 1).join('.'),
39
+ })
40
+ }
41
+ return component.nodes[node.children[childIndex]]
42
+ default:
43
+ return undefined
44
+ }
45
+ }, root)
46
+ if (node === undefined) {
47
+ return undefined
48
+ }
49
+ return { node: { ...node, nodeId: id }, ancestors }
50
+ }
51
+
52
+ export const isNodeOrAncestorConditional = (
53
+ nodeLookup?: NodeAndAncestorLookup,
54
+ ): nodeLookup is NodeAndAncestorLookup =>
55
+ nodeLookup?.node?.condition !== undefined ||
56
+ nodeLookup?.ancestors.some((a) => a.condition !== undefined) === true
57
+
58
+ /**
59
+ * @returns The next toddle sibling element or null if this is the last element. A toddle sibling is a sibling with a higher index or a the index, but a higher repeat index.
60
+ */
61
+ export const getNextSiblingElement = (
62
+ path: string,
63
+ parentElement: Element | ShadowRoot,
64
+ ) => {
65
+ const pathParts = path.split('.')
66
+ const lastPathPart = pathParts.slice(-1)[0]
67
+ const index = parseInt(lastPathPart)
68
+ const repeatIndex = parseInt(String(lastPathPart.split('(')[1]))
69
+
70
+ // Find the first child that either has a higher index or a similar index, but higher repeat index
71
+ for (const child of parentElement.children) {
72
+ const childPath = child.getAttribute('data-id')
73
+ const lastChildPathPart = childPath?.split('.').slice(-1)[0]
74
+ const childIndex = parseInt(String(lastChildPathPart))
75
+ if (
76
+ childIndex === index &&
77
+ parseInt(String(lastChildPathPart?.split('(')[1])) > repeatIndex
78
+ ) {
79
+ return child
80
+ }
81
+
82
+ if (childIndex > index) {
83
+ return child
84
+ }
85
+ }
86
+
87
+ return null
88
+ }
89
+
90
+ /**
91
+ * This function efficiently ensures that:
92
+ * 1. New items are added in the correct position.
93
+ * 2. Existing items are not moved if they are already in the correct order.
94
+ */
95
+ export function ensureEfficientOrdering(
96
+ parentElement: Element | ShadowRoot,
97
+ items: ReadonlyArray<Element | Text>,
98
+ nextElement: Element | Text | null = null,
99
+ ) {
100
+ // Identify the starting point for comparisons.
101
+ let insertBeforeElement = nextElement // If insertBeforeElement is null, items will be appended at the end.
102
+
103
+ // To track the current position in the DOM, we'll use a marker that advances through the sibling elements.
104
+ let currentMarker = insertBeforeElement
105
+ ? insertBeforeElement.previousSibling
106
+ : parentElement.lastChild
107
+
108
+ // We'll process the items array in reverse order to minimize the number of DOM operations.
109
+ for (let i = items.length - 1; i >= 0; i--) {
110
+ const item = items[i]
111
+
112
+ // Check if the item is already in the correct position by comparing it with the currentMarker.
113
+ if (item === currentMarker) {
114
+ // The item is in the correct position, move the marker to the previous sibling.
115
+ currentMarker = item.previousSibling
116
+ } else {
117
+ // The item is either not in the DOM or not in the correct position.
118
+ // Insert the item before the insertBeforeElement (or append it if insertBeforeElement is null).
119
+ parentElement.insertBefore(item, insertBeforeElement)
120
+ }
121
+
122
+ // Update insertBeforeElement to the current item for the next iteration, as we need to insert subsequent items before this one.
123
+ insertBeforeElement = item
124
+ }
125
+ }
@@ -0,0 +1,19 @@
1
+ import type { Component } from '@nordcraft/core/dist/component/component.types'
2
+
3
+ export function omitSubnodeStyleForComponent<T extends Component | undefined>(
4
+ component: T,
5
+ ): T {
6
+ const clone = structuredClone(component)
7
+ Object.entries(clone?.nodes ?? {}).forEach(([nodeId, node]) => {
8
+ if (
9
+ (node.type === 'element' || node.type === 'component') &&
10
+ nodeId !== 'root'
11
+ ) {
12
+ delete node.style
13
+ delete node.variants
14
+ delete node.animations
15
+ }
16
+ })
17
+
18
+ return clone
19
+ }
@@ -0,0 +1,5 @@
1
+ import type { Point } from '../editor/types'
2
+
3
+ export function rectHasPoint(rect: DOMRect, { x, y }: Point) {
4
+ return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
5
+ }
@@ -0,0 +1,56 @@
1
+ import { isDefined, toBoolean } from '@nordcraft/core/dist/utils/util'
2
+
3
+ /**
4
+ * Some attributes need special handling.
5
+ */
6
+ export function setAttribute(
7
+ elem: HTMLElement | SVGElement | MathMLElement,
8
+ attr: string,
9
+ value: any,
10
+ ) {
11
+ switch (attr) {
12
+ case 'srcObject':
13
+ case 'src':
14
+ if (elem instanceof HTMLMediaElement) {
15
+ ;(elem as any)[attr] = value
16
+ } else {
17
+ elem.setAttribute(attr, String(value))
18
+ }
19
+ break
20
+ case 'value':
21
+ case 'type': {
22
+ let val = value
23
+ if (elem instanceof HTMLProgressElement) {
24
+ // An HTMLProgressElement will break other elements in our editor if the value is not a (finite) number
25
+ // See docs here https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress#value
26
+ // and original issue here https://discord.com/channels/972416966683926538/1317827591230722048
27
+ if (!isDefined(value) || !Number.isFinite(Number(value))) {
28
+ val = 0
29
+ }
30
+ }
31
+ ;(elem as any)[attr] = toBoolean(val) ? String(val) : undefined
32
+ break
33
+ }
34
+ case 'muted':
35
+ case 'autoplay':
36
+ if (elem instanceof HTMLMediaElement) {
37
+ ;(elem as any)[attr] = toBoolean(value)
38
+ } else {
39
+ elem.setAttribute(attr, String(value))
40
+ }
41
+ break
42
+ default:
43
+ if (toBoolean(value)) {
44
+ elem.setAttribute(attr, String(value))
45
+ if (
46
+ // autofocus often does not work in the editor
47
+ attr === 'autofocus' &&
48
+ document.body.getAttribute('data-mode') !== 'design'
49
+ ) {
50
+ setTimeout(() => elem.focus(), 100)
51
+ }
52
+ } else {
53
+ elem.removeAttribute(attr)
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,32 @@
1
+ interface DocumentWithViewTransition {
2
+ startViewTransition?: (callback: () => void) => {
3
+ finished: Promise<void>
4
+ ready: Promise<void>
5
+ updateCallbackDone: Promise<void>
6
+ }
7
+ }
8
+
9
+ export function tryStartViewTransition(
10
+ updateCallback: () => void,
11
+ options?: {
12
+ skipPrefersReducedMotionCheck?: boolean
13
+ },
14
+ ): {
15
+ finished: Promise<void>
16
+ } {
17
+ const startViewTransition = (document as DocumentWithViewTransition)
18
+ .startViewTransition
19
+
20
+ if (
21
+ !startViewTransition ||
22
+ (options?.skipPrefersReducedMotionCheck !== true &&
23
+ window.matchMedia('(prefers-reduced-motion: reduce)').matches)
24
+ ) {
25
+ updateCallback()
26
+ return {
27
+ finished: Promise.resolve(),
28
+ }
29
+ }
30
+
31
+ return startViewTransition.call(document, updateCallback)
32
+ }
@@ -0,0 +1,45 @@
1
+ import { isDefined } from '@nordcraft/core/dist/utils/util'
2
+ import { compile } from 'path-to-regexp'
3
+ import type { Location } from '../types'
4
+
5
+ export const getLocationUrl = ({
6
+ query,
7
+ page,
8
+ route,
9
+ params,
10
+ hash,
11
+ }: Location) => {
12
+ let path: string
13
+ if (route) {
14
+ const pathSegments: string[] = []
15
+ for (const segment of route.path) {
16
+ if (segment.type === 'static') {
17
+ pathSegments.push(segment.name)
18
+ } else {
19
+ const segmentValue = params[segment.name]
20
+ if (isDefined(segmentValue)) {
21
+ pathSegments.push(segmentValue)
22
+ } else {
23
+ // If a param is missing, we can't build the rest of the path
24
+ break
25
+ }
26
+ }
27
+ }
28
+ path = '/' + pathSegments.join('/')
29
+ } else {
30
+ path = compile(page as string, { encode: encodeURIComponent })(params)
31
+ }
32
+ const hashString = hash === undefined || hash === '' ? '' : '#' + hash
33
+ const queryString = Object.entries(query)
34
+ .filter(([_, q]) => q !== null)
35
+ .map(([key, value]) => {
36
+ return `${encodeURIComponent(
37
+ route?.query[key]?.name ?? key,
38
+ )}=${encodeURIComponent(String(value))}`
39
+ })
40
+ .join('&')
41
+
42
+ return `${path}${hashString}${
43
+ queryString.length > 0 ? '?' + queryString : ''
44
+ }`
45
+ }