@nordcraft/core 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 (119) hide show
  1. package/README.md +5 -0
  2. package/dist/api/LegacyToddleApi.d.ts +34 -0
  3. package/dist/api/LegacyToddleApi.js +178 -0
  4. package/dist/api/LegacyToddleApi.js.map +1 -0
  5. package/dist/api/ToddleApiV2.d.ts +77 -0
  6. package/dist/api/ToddleApiV2.js +271 -0
  7. package/dist/api/ToddleApiV2.js.map +1 -0
  8. package/dist/api/api.d.ts +49 -0
  9. package/dist/api/api.js +276 -0
  10. package/dist/api/api.js.map +1 -0
  11. package/dist/api/apiTypes.d.ts +125 -0
  12. package/dist/api/apiTypes.js +11 -0
  13. package/dist/api/apiTypes.js.map +1 -0
  14. package/dist/api/headers.d.ts +10 -0
  15. package/dist/api/headers.js +36 -0
  16. package/dist/api/headers.js.map +1 -0
  17. package/dist/api/template.d.ts +5 -0
  18. package/dist/api/template.js +7 -0
  19. package/dist/api/template.js.map +1 -0
  20. package/dist/component/ToddleComponent.d.ts +45 -0
  21. package/dist/component/ToddleComponent.js +373 -0
  22. package/dist/component/ToddleComponent.js.map +1 -0
  23. package/dist/component/actionUtils.d.ts +2 -0
  24. package/dist/component/actionUtils.js +56 -0
  25. package/dist/component/actionUtils.js.map +1 -0
  26. package/dist/component/component.types.d.ts +313 -0
  27. package/dist/component/component.types.js +9 -0
  28. package/dist/component/component.types.js.map +1 -0
  29. package/dist/component/isPageComponent.d.ts +2 -0
  30. package/dist/component/isPageComponent.js +3 -0
  31. package/dist/component/isPageComponent.js.map +1 -0
  32. package/dist/formula/ToddleFormula.d.ts +15 -0
  33. package/dist/formula/ToddleFormula.js +20 -0
  34. package/dist/formula/ToddleFormula.js.map +1 -0
  35. package/dist/formula/formula.d.ts +103 -0
  36. package/dist/formula/formula.js +186 -0
  37. package/dist/formula/formula.js.map +1 -0
  38. package/dist/formula/formulaTypes.d.ts +30 -0
  39. package/dist/formula/formulaTypes.js +2 -0
  40. package/dist/formula/formulaTypes.js.map +1 -0
  41. package/dist/formula/formulaUtils.d.ts +18 -0
  42. package/dist/formula/formulaUtils.js +240 -0
  43. package/dist/formula/formulaUtils.js.map +1 -0
  44. package/dist/styling/className.d.ts +2 -0
  45. package/dist/styling/className.js +52 -0
  46. package/dist/styling/className.js.map +1 -0
  47. package/dist/styling/style.css.d.ts +5 -0
  48. package/dist/styling/style.css.js +242 -0
  49. package/dist/styling/style.css.js.map +1 -0
  50. package/dist/styling/theme.const.d.ts +3 -0
  51. package/dist/styling/theme.const.js +381 -0
  52. package/dist/styling/theme.const.js.map +1 -0
  53. package/dist/styling/theme.d.ts +72 -0
  54. package/dist/styling/theme.js +200 -0
  55. package/dist/styling/theme.js.map +1 -0
  56. package/dist/styling/variantSelector.d.ts +123 -0
  57. package/dist/styling/variantSelector.js +25 -0
  58. package/dist/styling/variantSelector.js.map +1 -0
  59. package/dist/types.d.ts +97 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils/collections.d.ts +21 -0
  63. package/dist/utils/collections.js +75 -0
  64. package/dist/utils/collections.js.map +1 -0
  65. package/dist/utils/customElements.d.ts +6 -0
  66. package/dist/utils/customElements.js +15 -0
  67. package/dist/utils/customElements.js.map +1 -0
  68. package/dist/utils/hash.d.ts +1 -0
  69. package/dist/utils/hash.js +17 -0
  70. package/dist/utils/hash.js.map +1 -0
  71. package/dist/utils/json.d.ts +5 -0
  72. package/dist/utils/json.js +16 -0
  73. package/dist/utils/json.js.map +1 -0
  74. package/dist/utils/sha1.d.ts +2 -0
  75. package/dist/utils/sha1.js +13 -0
  76. package/dist/utils/sha1.js.map +1 -0
  77. package/dist/utils/url.d.ts +5 -0
  78. package/dist/utils/url.js +27 -0
  79. package/dist/utils/url.js.map +1 -0
  80. package/dist/utils/util.d.ts +3 -0
  81. package/dist/utils/util.js +4 -0
  82. package/dist/utils/util.js.map +1 -0
  83. package/package.json +18 -0
  84. package/src/api/LegacyToddleApi.ts +205 -0
  85. package/src/api/ToddleApiV2.ts +331 -0
  86. package/src/api/api.test.ts +319 -0
  87. package/src/api/api.ts +414 -0
  88. package/src/api/apiTypes.ts +145 -0
  89. package/src/api/headers.ts +41 -0
  90. package/src/api/template.ts +10 -0
  91. package/src/component/ToddleComponent.actionReferences.test.ts +75 -0
  92. package/src/component/ToddleComponent.formulasInComponent.test.ts +234 -0
  93. package/src/component/ToddleComponent.ts +470 -0
  94. package/src/component/actionUtils.ts +61 -0
  95. package/src/component/component.types.ts +362 -0
  96. package/src/component/isPageComponent.ts +6 -0
  97. package/src/formula/ToddleFormula.ts +30 -0
  98. package/src/formula/formula.ts +355 -0
  99. package/src/formula/formulaTypes.ts +45 -0
  100. package/src/formula/formulaUtils.ts +287 -0
  101. package/src/styling/className.test.ts +19 -0
  102. package/src/styling/className.ts +73 -0
  103. package/src/styling/style.css.ts +309 -0
  104. package/src/styling/theme.const.ts +390 -0
  105. package/src/styling/theme.ts +339 -0
  106. package/src/styling/variantSelector.ts +168 -0
  107. package/src/types.ts +158 -0
  108. package/src/utils/collections.test.ts +57 -0
  109. package/src/utils/collections.ts +122 -0
  110. package/src/utils/customElements.test.ts +40 -0
  111. package/src/utils/customElements.ts +16 -0
  112. package/src/utils/hash.test.ts +32 -0
  113. package/src/utils/hash.ts +18 -0
  114. package/src/utils/json.ts +18 -0
  115. package/src/utils/sha1.test.ts +50 -0
  116. package/src/utils/sha1.ts +17 -0
  117. package/src/utils/url.test.ts +17 -0
  118. package/src/utils/url.ts +33 -0
  119. package/src/utils/util.ts +8 -0
@@ -0,0 +1,61 @@
1
+ import { isDefined } from '../utils/util'
2
+ import type { ActionModel } from './component.types'
3
+
4
+ export function* getActionsInAction(
5
+ action: ActionModel | null,
6
+ path: (string | number)[] = [],
7
+ ): Generator<[(string | number)[], ActionModel]> {
8
+ if (!isDefined(action)) {
9
+ return
10
+ }
11
+
12
+ yield [path, action]
13
+ switch (action.type) {
14
+ case 'SetVariable':
15
+ case 'SetURLParameter':
16
+ case 'TriggerEvent':
17
+ case 'TriggerWorkflow':
18
+ break
19
+ case 'Fetch':
20
+ for (const [key, a] of Object.entries(action.onSuccess?.actions ?? {})) {
21
+ yield* getActionsInAction(a, [...path, 'onSuccess', 'actions', key])
22
+ }
23
+ for (const [key, a] of Object.entries(action.onError?.actions ?? {})) {
24
+ yield* getActionsInAction(a, [...path, 'onError', 'actions', key])
25
+ }
26
+ for (const [key, a] of Object.entries(action.onMessage?.actions ?? {})) {
27
+ yield* getActionsInAction(a, [...path, 'onMessage', 'actions', key])
28
+ }
29
+ break
30
+ case 'Custom':
31
+ case undefined:
32
+ for (const [eventKey, event] of Object.entries(action.events ?? {})) {
33
+ for (const [key, a] of Object.entries(event?.actions ?? {})) {
34
+ yield* getActionsInAction(a, [
35
+ ...path,
36
+ 'events',
37
+ eventKey,
38
+ 'actions',
39
+ key,
40
+ ])
41
+ }
42
+ }
43
+ break
44
+ case 'Switch':
45
+ for (const [key, c] of action.cases.entries()) {
46
+ for (const [actionKey, a] of Object.entries(c?.actions ?? {})) {
47
+ yield* getActionsInAction(a, [
48
+ ...path,
49
+ 'cases',
50
+ key,
51
+ 'actions',
52
+ actionKey,
53
+ ])
54
+ }
55
+ }
56
+ for (const [actionKey, a] of Object.entries(action.default.actions)) {
57
+ yield* getActionsInAction(a, [...path, 'default', 'actions', actionKey])
58
+ }
59
+ break
60
+ }
61
+ }
@@ -0,0 +1,362 @@
1
+ import type { ApiStatus, ComponentAPI, LegacyApiStatus } from '../api/apiTypes'
2
+ import type { Formula } from '../formula/formula'
3
+ import type { StyleTokenCategory } from '../styling/theme'
4
+ import type { RequireFields, ToddleMetadata } from '../types'
5
+
6
+ interface ListItem {
7
+ Item: unknown
8
+ Index: number
9
+ Parent?: ListItem
10
+ }
11
+
12
+ export interface ComponentData {
13
+ Location?: {
14
+ page?: string
15
+ path: string
16
+ // params is a combination of path and query parameters
17
+ params: Record<string, string | null>
18
+ query: Record<string, string | null>
19
+ hash: string
20
+ }
21
+ Attributes: Record<string, unknown>
22
+ Variables?: Record<string, unknown>
23
+ Contexts?: Record<string, Record<string, unknown>>
24
+ 'URL parameters'?: Record<string, string | null>
25
+ // { path: { docs: null }, query: { embed: everything } }
26
+ 'Route parameters'?: {
27
+ path: Record<string, string | null>
28
+ query: Record<string, string | null>
29
+ }
30
+ Apis?: Record<
31
+ string,
32
+ LegacyApiStatus | (ApiStatus & { inputs?: Record<string, unknown> })
33
+ >
34
+ Args?: unknown
35
+ Parameters?: Record<string, unknown>
36
+ Event?: unknown
37
+ ListItem?: ListItem
38
+ }
39
+
40
+ export interface AnimationKeyframe {
41
+ position: number
42
+ key: string
43
+ value: string
44
+ easing?: never
45
+ }
46
+
47
+ export interface StyleVariant {
48
+ id?: string
49
+ className?: string
50
+ hover?: boolean
51
+ focus?: boolean
52
+ focusWithin?: boolean
53
+ active?: boolean
54
+ disabled?: boolean
55
+ firstChild?: boolean
56
+ lastChild?: boolean
57
+ evenChild?: boolean
58
+ empty?: boolean
59
+ mediaQuery?: MediaQuery
60
+ breakpoint: 'small' | 'medium' | 'large'
61
+ startingStyle?: boolean
62
+ style: NodeStyleModel
63
+ }
64
+
65
+ export type NodeStyleModel = Record<string, string>
66
+
67
+ export interface TextNodeModel {
68
+ id?: string
69
+ type: 'text'
70
+ condition?: Formula
71
+ repeat?: Formula
72
+ slot?: string
73
+ repeatKey?: Formula
74
+ value: Formula
75
+ children?: undefined
76
+ }
77
+
78
+ export interface ElementNodeModel {
79
+ id?: string
80
+ type: 'element'
81
+ slot?: string
82
+ condition?: Formula
83
+ repeat?: Formula
84
+ repeatKey?: Formula
85
+ tag: string
86
+ attrs: Partial<Record<string, Formula>>
87
+ style: NodeStyleModel
88
+ variants?: StyleVariant[]
89
+ animations?: Record<string, Record<string, AnimationKeyframe>>
90
+ children: string[]
91
+ events: Record<string, EventModel>
92
+ classes: Record<string, { formula?: Formula }>
93
+ 'style-variables'?: Array<{
94
+ category: StyleTokenCategory
95
+ name: string
96
+ formula: Formula
97
+ unit?: string
98
+ }>
99
+ }
100
+
101
+ export interface ComponentNodeModel {
102
+ id?: string
103
+ type: 'component'
104
+ slot?: string
105
+ path?: string
106
+ name: string
107
+ package?: string
108
+ condition?: Formula
109
+ repeat?: Formula
110
+ repeatKey?: Formula
111
+ style?: NodeStyleModel
112
+ variants?: StyleVariant[]
113
+ animations?: Record<string, Record<string, AnimationKeyframe>>
114
+ attrs: Record<string, Formula>
115
+ children: string[]
116
+ events: Record<string, EventModel>
117
+ }
118
+
119
+ export interface SlotNodeModel {
120
+ type: 'slot'
121
+ slot?: string
122
+ name?: string
123
+ condition?: Formula
124
+ repeat?: undefined
125
+ repeatKey?: undefined
126
+ children: string[]
127
+ }
128
+ export type NodeModel =
129
+ | TextNodeModel
130
+ | SlotNodeModel
131
+ | ComponentNodeModel
132
+ | ElementNodeModel
133
+
134
+ export interface MetaEntry {
135
+ tag: HeadTagTypes
136
+ attrs: Record<string, Formula>
137
+ content: Formula
138
+ }
139
+
140
+ export interface StaticPathSegment {
141
+ type: 'static'
142
+ optional?: boolean
143
+ testValue?: undefined
144
+ name: string
145
+ }
146
+
147
+ export interface DynamicPathSegment {
148
+ type: 'param'
149
+ testValue: string
150
+ optional?: boolean
151
+ name: string
152
+ }
153
+
154
+ type MediaQuery = {
155
+ 'min-width'?: string
156
+ 'max-width'?: string
157
+ 'min-height'?: string
158
+ 'max-height'?: string
159
+ }
160
+
161
+ export interface Component {
162
+ name: string // component name
163
+ /**
164
+ * version 2 indicates that the component's name is no longer prefixed, but will be automatically prefixed by the project name
165
+ *
166
+ * @default undefined (version 1)
167
+ * @deprecated - we are no longer using version 2 components, but we are keeping this field for backwards compatibility
168
+ */
169
+ version?: 2
170
+ // @deprecated - use route->path instead
171
+ page?: string // page url /projects/:id - only for pages
172
+ route?: PageRoute | null
173
+ attributes: Record<string, ComponentAttribute>
174
+ variables: Record<string, ComponentVariable>
175
+ formulas?: Record<string, ComponentFormula>
176
+ contexts?: Record<
177
+ // `componentName` or `packageName/componentName` if the context comes from a different package than the component itself
178
+ string,
179
+ ComponentContext
180
+ >
181
+ workflows?: Record<string, ComponentWorkflow>
182
+ apis: Record<string, ComponentAPI>
183
+ nodes: Record<string, NodeModel>
184
+ events?: ComponentEvent[]
185
+ onLoad?: EventModel
186
+ onAttributeChange?: EventModel
187
+ // exported indicates that a component is exported in a package
188
+ exported?: boolean
189
+ }
190
+
191
+ export interface ComponentFormula extends ToddleMetadata {
192
+ name: string
193
+ arguments?: Array<{ name: string; testValue: any }> | null
194
+ memoize?: boolean
195
+ exposeInContext?: boolean
196
+ formula: Formula
197
+ }
198
+
199
+ export interface ComponentWorkflow extends ToddleMetadata {
200
+ name: string
201
+ parameters: Array<{ name: string; testValue: any }>
202
+ actions: ActionModel[]
203
+ exposeInContext?: boolean
204
+ }
205
+
206
+ export interface ComponentContext {
207
+ formulas: string[]
208
+ workflows: string[]
209
+ componentName?: string
210
+ package?: string
211
+ }
212
+
213
+ export type PageComponent = RequireFields<Component, 'route'>
214
+
215
+ export interface RouteDeclaration {
216
+ path: Array<StaticPathSegment | DynamicPathSegment>
217
+ query: Record<string, { name: string; testValue: any }>
218
+ }
219
+
220
+ export interface PageRoute extends RouteDeclaration {
221
+ // Information for the <head> element
222
+ // only relevant for pages - not for regular
223
+ // components
224
+ info?: {
225
+ // value for <html lang= > - defaults to 'en'
226
+ language?: { formula: Formula }
227
+ // title (for <title>) - defaults to page name
228
+ title?: { formula: Formula }
229
+ description?: { formula: Formula }
230
+ // links - only icon (+icon:16 and icon:32) for now:
231
+ // - manifest
232
+ // - mask-icon
233
+ // - apple-touch-icon
234
+ // - icon
235
+ // - icon (32)
236
+ // - icon (16)
237
+ // cSpell:ignore Vhmkm
238
+ icon?: { formula: Formula } // /cdn-cgi/imagedelivery/ZIty0Vhmkm0nD-fBKJrTZQ/my-icon
239
+ charset?: { formula: Formula } // defaults to utf-8
240
+
241
+ // meta:
242
+ // - viewport <meta name="viewport" content="width=device-width, initial-scale=1">
243
+ // - description <meta name="description" content="My Page description">
244
+ // - apple-mobile-web-app-title
245
+ // - application-name <meta name="application-name" content="My App">
246
+ // - msapplication-TileColor
247
+ // - theme-color
248
+ // - og:title - defaults to page name <meta property="og:title" content="My Page">
249
+ // - og:type - defaults to "website" - see https://stackoverflow.com/a/54741252 for more types e.g. "product"
250
+ // - og:description - defaults to page description <meta property="og:description" content="My Page description">
251
+ // - og:image <meta property="og:image" content="https://example.com/image.jpg">
252
+ // - og:url - defaults to page url (with no query params etc.)
253
+ // - og:locale
254
+ // - twitter:card
255
+ // - twitter:site
256
+ // - twitter:creator
257
+ meta?: Record<string, MetaEntry>
258
+ }
259
+ }
260
+
261
+ export enum HeadTagTypes {
262
+ Meta = 'meta',
263
+ Link = 'link',
264
+ Script = 'script',
265
+ NoScript = 'noscript',
266
+ Style = 'style',
267
+ }
268
+
269
+ export type EventModel = {
270
+ trigger: string
271
+ actions: ActionModel[]
272
+ }
273
+
274
+ export type CustomActionModel = {
275
+ // Some legacy custom actions use an undefined type
276
+ type?: 'Custom'
277
+ package?: string
278
+ name: string
279
+ description?: string
280
+ data?: string | number | boolean | Formula
281
+ arguments?: { name: string; formula: Formula }[]
282
+ events?: Record<string, { actions: ActionModel[] }>
283
+ version?: 2 | never
284
+ label?: string
285
+ }
286
+
287
+ export type SwitchActionModel = {
288
+ type: 'Switch'
289
+ data?: string | number | boolean | Formula
290
+ cases: Array<{
291
+ condition: Formula
292
+ actions: ActionModel[]
293
+ }>
294
+ default: { actions: ActionModel[] }
295
+ }
296
+
297
+ export type VariableActionModel = {
298
+ type: 'SetVariable'
299
+ variable: string
300
+ data: Formula
301
+ }
302
+ export type FetchActionModel = {
303
+ type: 'Fetch'
304
+ api: string
305
+ inputs?: Record<string, { formula: Formula | null }>
306
+ onSuccess: { actions: ActionModel[] }
307
+ onError: { actions: ActionModel[] }
308
+ onMessage?: { actions: ActionModel[] }
309
+ }
310
+
311
+ export type SetURLParameterAction = {
312
+ type: 'SetURLParameter'
313
+ parameter: string
314
+ data: Formula
315
+ historyMode?: 'replace' | 'push' | null
316
+ }
317
+
318
+ export type EventActionModel = {
319
+ type: 'TriggerEvent'
320
+ event: string
321
+ data: Formula
322
+ }
323
+
324
+ export type WorkflowActionModel = {
325
+ type: 'TriggerWorkflow'
326
+ workflow: string
327
+ parameters: Record<string, { formula: Formula }>
328
+ contextProvider?: string
329
+ }
330
+
331
+ export type ActionModel =
332
+ | VariableActionModel
333
+ | EventActionModel
334
+ | SwitchActionModel
335
+ | FetchActionModel
336
+ | CustomActionModel
337
+ | SetURLParameterAction
338
+ | WorkflowActionModel
339
+
340
+ export interface ComponentEvent extends ToddleMetadata {
341
+ name: string
342
+ // eslint-disable-next-line inclusive-language/use-inclusive-words
343
+ dummyEvent: any
344
+ }
345
+
346
+ export interface ComponentVariable extends ToddleMetadata {
347
+ initialValue: Formula
348
+ }
349
+
350
+ export interface ComponentAttribute extends ToddleMetadata {
351
+ name: string
352
+ testValue: unknown
353
+ }
354
+
355
+ /**
356
+ * We must specify the namespace for some nodes when created programmatically that are not in the default namespace.
357
+ * We infer the namespace based on the tag name, but it would be interesting to also allow the user to specify it explicitly with the `xmlns` attribute.
358
+ */
359
+ export type SupportedNamespaces =
360
+ | 'http://www.w3.org/1999/xhtml'
361
+ | 'http://www.w3.org/2000/svg'
362
+ | 'http://www.w3.org/1998/Math/MathML'
@@ -0,0 +1,6 @@
1
+ import { isDefined } from '../utils/util'
2
+ import type { Component, PageComponent } from './component.types'
3
+
4
+ export const isPageComponent = (
5
+ component: Component,
6
+ ): component is PageComponent => isDefined(component.route)
@@ -0,0 +1,30 @@
1
+ import type { Formula } from './formula'
2
+ import type { GlobalFormulas } from './formulaTypes'
3
+ import { getFormulasInFormula } from './formulaUtils'
4
+
5
+ export class ToddleFormula<Handler> {
6
+ private formula: Formula
7
+ private globalFormulas: GlobalFormulas<Handler>
8
+
9
+ constructor({
10
+ formula,
11
+ globalFormulas,
12
+ }: {
13
+ formula: Formula
14
+ globalFormulas: GlobalFormulas<Handler>
15
+ }) {
16
+ this.formula = formula
17
+ this.globalFormulas = globalFormulas
18
+ }
19
+
20
+ /**
21
+ * Traverse all formulas in the formula.
22
+ * @returns An iterable that yields the path and formula.
23
+ */
24
+ *formulasInFormula(): Generator<[(string | number)[], Formula]> {
25
+ yield* getFormulasInFormula({
26
+ formula: this.formula,
27
+ globalFormulas: this.globalFormulas,
28
+ })
29
+ }
30
+ }