@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,387 @@
1
+ /* eslint-disable no-console */
2
+ import type {
3
+ ActionModel,
4
+ ComponentData,
5
+ SetURLParameterAction,
6
+ } from '@nordcraft/core/dist/component/component.types'
7
+ import { applyFormula } from '@nordcraft/core/dist/formula/formula'
8
+ import { mapValues, omitKeys } from '@nordcraft/core/dist/utils/collections'
9
+ import { isDefined, toBoolean } from '@nordcraft/core/dist/utils/util'
10
+ import type { ComponentContext, Location } from '../types'
11
+ import { getLocationUrl } from '../utils/url'
12
+
13
+ // eslint-disable-next-line max-params
14
+ export function handleAction(
15
+ action: ActionModel,
16
+ data: ComponentData,
17
+ ctx: ComponentContext,
18
+ event?: Event,
19
+ ) {
20
+ try {
21
+ if (!action) {
22
+ throw new Error('Action does not exist')
23
+ }
24
+ switch (action.type) {
25
+ case 'Switch': {
26
+ // find the first case that resolves to true.
27
+ // Only one case in a switch will be executed.
28
+ const actionList =
29
+ action.cases.find(({ condition }) =>
30
+ toBoolean(
31
+ applyFormula(condition, {
32
+ data,
33
+ component: ctx.component,
34
+ formulaCache: ctx.formulaCache,
35
+ root: ctx.root,
36
+ package: ctx.package,
37
+ toddle: ctx.toddle,
38
+ env: ctx.env,
39
+ }),
40
+ ),
41
+ ) ?? action.default
42
+ if (!actionList) {
43
+ return
44
+ }
45
+ // handle all actions for the case
46
+ for (const action of actionList.actions) {
47
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
48
+ handleAction(action, { ...data, ...ctx.dataSignal.get() }, ctx, event)
49
+ }
50
+ break
51
+ }
52
+ case 'SetVariable': {
53
+ const value = applyFormula(action.data, {
54
+ data,
55
+ component: ctx.component,
56
+ formulaCache: ctx.formulaCache,
57
+ root: ctx.root,
58
+ package: ctx.package,
59
+ toddle: ctx.toddle,
60
+ env: ctx.env,
61
+ })
62
+ ctx.dataSignal.update((data) => {
63
+ return {
64
+ ...data,
65
+ Variables: {
66
+ ...data.Variables,
67
+ [action.variable]: value,
68
+ },
69
+ }
70
+ })
71
+ break
72
+ }
73
+ case 'TriggerEvent': {
74
+ const payload = applyFormula(action.data, {
75
+ data,
76
+ component: ctx.component,
77
+ formulaCache: ctx.formulaCache,
78
+ root: ctx.root,
79
+ package: ctx.package,
80
+ toddle: ctx.toddle,
81
+ env: ctx.env,
82
+ })
83
+ ctx.triggerEvent(action.event, payload)
84
+ break
85
+ }
86
+ case 'SetURLParameter': {
87
+ ctx.toddle.locationSignal.update((current) => {
88
+ const value = applyFormula(action.data, {
89
+ data,
90
+ component: ctx.component,
91
+ formulaCache: ctx.formulaCache,
92
+ root: ctx.root,
93
+ package: ctx.package,
94
+ toddle: ctx.toddle,
95
+ env: ctx.env,
96
+ })
97
+ // historyMode was previously not declared explicitly, and we default
98
+ // to push for state changes and replace for query changes
99
+ let historyMode: SetURLParameterAction['historyMode'] | undefined
100
+ let newLocation: Location | undefined
101
+ // We should only match on p.type === 'param', but
102
+ // that would technically be a breaking change
103
+ if (current.route?.path.some((p) => p.name === action.parameter)) {
104
+ historyMode = 'push'
105
+ newLocation = {
106
+ ...current,
107
+ params: {
108
+ ...omitKeys(current.params, [action.parameter]),
109
+ [action.parameter]: value,
110
+ },
111
+ }
112
+ }
113
+ // We should check if the query parameter exists in the route
114
+ // but that would technically be a breaking change
115
+ // else if (Object.values(current.route?.query ?? {}).some((q) => q.name === action.parameter))
116
+ else {
117
+ historyMode = 'replace'
118
+ newLocation = {
119
+ ...current,
120
+ query: {
121
+ ...omitKeys(current.query, [action.parameter]),
122
+ ...(isDefined(value) ? { [action.parameter]: value } : null),
123
+ },
124
+ }
125
+ }
126
+ if (!historyMode) {
127
+ // No path/query parameter matched
128
+ return current
129
+ }
130
+
131
+ const currentUrl = getLocationUrl(current)
132
+ const historyUrl = getLocationUrl(newLocation)
133
+ if (historyUrl !== currentUrl) {
134
+ // Default to the historyMode from the action, and fallback
135
+ // to the default (push for path change, replace for query change)
136
+ historyMode = action.historyMode ?? historyMode
137
+ // Update the window's history state
138
+ if (historyMode === 'push') {
139
+ window.history.pushState({}, '', historyUrl)
140
+ } else {
141
+ window.history.replaceState({}, '', historyUrl)
142
+ }
143
+ }
144
+ return newLocation
145
+ })
146
+ break
147
+ }
148
+ case 'Fetch': {
149
+ const api = ctx.apis[action.api]
150
+ if (!api) {
151
+ console.error('The api ', action.api, 'does not exist')
152
+ return
153
+ }
154
+
155
+ const isv2 = ctx.component.apis?.[action.api]?.version === 2
156
+
157
+ // Evaluate potential inputs here to make sure the api have the right values
158
+ // This is needed if the inputs are formulas referencing workflow parameters
159
+ const actionInputs = isv2
160
+ ? mapValues(action.inputs ?? {}, (input) =>
161
+ applyFormula(input.formula, {
162
+ data,
163
+ component: ctx.component,
164
+ formulaCache: ctx.formulaCache,
165
+ root: ctx.root,
166
+ package: ctx.package,
167
+ toddle: ctx.toddle,
168
+ env: ctx.env,
169
+ }),
170
+ )
171
+ : undefined
172
+
173
+ const actionModels = isv2
174
+ ? {
175
+ onCompleted: action.onSuccess?.actions,
176
+ onFailed: action.onError?.actions,
177
+ onMessage: action.onMessage?.actions,
178
+ }
179
+ : undefined
180
+
181
+ const triggerActions = (actions: ActionModel[]) => {
182
+ // Actions from the fetch action is handled by the api itself
183
+ if (isv2) {
184
+ return
185
+ }
186
+ for (const subAction of actions) {
187
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
188
+ handleAction(
189
+ subAction,
190
+ { ...data, ...ctx.dataSignal.get() },
191
+ ctx,
192
+ event,
193
+ )
194
+ }
195
+ }
196
+
197
+ api.fetch({ actionInputs, actionModels }).then(
198
+ () => {
199
+ triggerActions(action.onSuccess.actions)
200
+ },
201
+ () => {
202
+ triggerActions(action.onError.actions)
203
+ },
204
+ )
205
+ break
206
+ }
207
+ case 'TriggerWorkflow': {
208
+ const parameters = mapValues(action.parameters ?? {}, (parameter) =>
209
+ applyFormula(parameter.formula, {
210
+ data,
211
+ component: ctx.component,
212
+ formulaCache: ctx.formulaCache,
213
+ root: ctx.root,
214
+ package: ctx.package,
215
+ toddle: ctx.toddle,
216
+ env: ctx.env,
217
+ }),
218
+ )
219
+
220
+ if (action.contextProvider) {
221
+ const provider =
222
+ ctx.providers[
223
+ [ctx.package, action.contextProvider].filter(isDefined).join('/')
224
+ ] ?? ctx.providers[action.contextProvider]
225
+ const workflow = provider?.component.workflows?.[action.workflow]
226
+ if (!workflow) {
227
+ if (provider) {
228
+ console.warn(
229
+ `Cannot find workflow "${action.workflow}" on component "${action.contextProvider}". It has likely been removed or modified.`,
230
+ )
231
+ }
232
+ return
233
+ }
234
+
235
+ workflow.actions.forEach((action) =>
236
+ handleAction(
237
+ action,
238
+ {
239
+ ...data,
240
+ ...provider.ctx.dataSignal.get(),
241
+ Parameters: parameters,
242
+ },
243
+ provider.ctx,
244
+ event,
245
+ ),
246
+ )
247
+ return
248
+ }
249
+
250
+ const workflow = ctx.component.workflows?.[action.workflow]
251
+ if (!workflow) {
252
+ console.warn(
253
+ `Workflow ${action.workflow} does not exist on component ${ctx.component.name}`,
254
+ )
255
+ return
256
+ }
257
+
258
+ workflow.actions.forEach((action) =>
259
+ handleAction(
260
+ action,
261
+ {
262
+ ...data,
263
+ ...ctx.dataSignal.get(),
264
+ Parameters: parameters,
265
+ },
266
+ ctx,
267
+ event,
268
+ ),
269
+ )
270
+ break
271
+ }
272
+ default: {
273
+ try {
274
+ // create a handler for actions triggering events
275
+ const triggerActionEvent = (trigger: string, eventData: any) => {
276
+ const subEvent = action.events?.[trigger]
277
+ if (subEvent) {
278
+ subEvent.actions.forEach((action) =>
279
+ handleAction(
280
+ action,
281
+ eventData
282
+ ? { ...data, ...ctx.dataSignal.get(), Event: eventData }
283
+ : { ...data, ...ctx.dataSignal.get() },
284
+ ctx,
285
+ eventData ?? event,
286
+ ),
287
+ )
288
+ }
289
+ }
290
+ const newAction =
291
+ action.version === 2
292
+ ? (ctx.toddle.getCustomAction ?? ctx.toddle.getCustomAction)(
293
+ action.name,
294
+ action.package ?? ctx.package,
295
+ )
296
+ : undefined
297
+ if (newAction) {
298
+ // First evaluate any arguments (input) to the action
299
+ const args = (action.arguments ?? []).reduce<
300
+ Record<string, unknown>
301
+ >(
302
+ (args, arg) => ({
303
+ ...args,
304
+ [arg.name]: applyFormula(arg.formula, {
305
+ data,
306
+ component: ctx.component,
307
+ formulaCache: ctx.formulaCache,
308
+ root: ctx.root,
309
+ package: ctx.package,
310
+ toddle: ctx.toddle,
311
+ env: ctx.env,
312
+ }),
313
+ }),
314
+ {},
315
+ )
316
+ const result = newAction.handler?.(
317
+ args,
318
+ {
319
+ root: ctx.root,
320
+ triggerActionEvent,
321
+ },
322
+ event,
323
+ )
324
+ // If the result is a function, then it should behave as a cleanup function, that runs, usually when the component is unmounted.
325
+ // Useful for removeEventListeners, clearTimeout, etc.
326
+ if (
327
+ result &&
328
+ (typeof result === 'function' || result instanceof Promise)
329
+ ) {
330
+ ctx.dataSignal.subscribe((data) => data, {
331
+ destroy: () => {
332
+ if (result instanceof Promise) {
333
+ result
334
+ .then((cleanup) => {
335
+ if (typeof cleanup === 'function') {
336
+ cleanup()
337
+ }
338
+ })
339
+ .catch((err) => console.error(err))
340
+ } else {
341
+ result()
342
+ }
343
+ },
344
+ })
345
+ }
346
+
347
+ return result
348
+ } else {
349
+ const legacyHandler = ctx.toddle.getAction(action.name)
350
+ if (!legacyHandler) {
351
+ console.error('Missing custom action', action.name)
352
+ return
353
+ }
354
+ // First evaluate any arguments (input) to the action
355
+ const args = action.arguments?.map((arg) =>
356
+ applyFormula(arg.formula, {
357
+ data,
358
+ component: ctx.component,
359
+ formulaCache: ctx.formulaCache,
360
+ root: ctx.root,
361
+ package: ctx.package,
362
+ toddle: ctx.toddle,
363
+ env: ctx.env,
364
+ }),
365
+ ) ?? [
366
+ applyFormula(action.data, {
367
+ data,
368
+ component: ctx.component,
369
+ formulaCache: ctx.formulaCache,
370
+ root: ctx.root,
371
+ package: ctx.package,
372
+ toddle: ctx.toddle,
373
+ env: ctx.env,
374
+ }),
375
+ ] // action.data is a fallback to handle an older version of the action spec.
376
+ return legacyHandler(args, { ...ctx, triggerActionEvent }, event)
377
+ }
378
+ } catch (err) {
379
+ console.error('Error in Custom Action', err)
380
+ }
381
+ }
382
+ }
383
+ } catch (e) {
384
+ console.error(e)
385
+ return null
386
+ }
387
+ }