@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.
- package/README.md +5 -0
- package/dist/api/LegacyToddleApi.d.ts +34 -0
- package/dist/api/LegacyToddleApi.js +178 -0
- package/dist/api/LegacyToddleApi.js.map +1 -0
- package/dist/api/ToddleApiV2.d.ts +77 -0
- package/dist/api/ToddleApiV2.js +271 -0
- package/dist/api/ToddleApiV2.js.map +1 -0
- package/dist/api/api.d.ts +49 -0
- package/dist/api/api.js +276 -0
- package/dist/api/api.js.map +1 -0
- package/dist/api/apiTypes.d.ts +125 -0
- package/dist/api/apiTypes.js +11 -0
- package/dist/api/apiTypes.js.map +1 -0
- package/dist/api/headers.d.ts +10 -0
- package/dist/api/headers.js +36 -0
- package/dist/api/headers.js.map +1 -0
- package/dist/api/template.d.ts +5 -0
- package/dist/api/template.js +7 -0
- package/dist/api/template.js.map +1 -0
- package/dist/component/ToddleComponent.d.ts +45 -0
- package/dist/component/ToddleComponent.js +373 -0
- package/dist/component/ToddleComponent.js.map +1 -0
- package/dist/component/actionUtils.d.ts +2 -0
- package/dist/component/actionUtils.js +56 -0
- package/dist/component/actionUtils.js.map +1 -0
- package/dist/component/component.types.d.ts +313 -0
- package/dist/component/component.types.js +9 -0
- package/dist/component/component.types.js.map +1 -0
- package/dist/component/isPageComponent.d.ts +2 -0
- package/dist/component/isPageComponent.js +3 -0
- package/dist/component/isPageComponent.js.map +1 -0
- package/dist/formula/ToddleFormula.d.ts +15 -0
- package/dist/formula/ToddleFormula.js +20 -0
- package/dist/formula/ToddleFormula.js.map +1 -0
- package/dist/formula/formula.d.ts +103 -0
- package/dist/formula/formula.js +186 -0
- package/dist/formula/formula.js.map +1 -0
- package/dist/formula/formulaTypes.d.ts +30 -0
- package/dist/formula/formulaTypes.js +2 -0
- package/dist/formula/formulaTypes.js.map +1 -0
- package/dist/formula/formulaUtils.d.ts +18 -0
- package/dist/formula/formulaUtils.js +240 -0
- package/dist/formula/formulaUtils.js.map +1 -0
- package/dist/styling/className.d.ts +2 -0
- package/dist/styling/className.js +52 -0
- package/dist/styling/className.js.map +1 -0
- package/dist/styling/style.css.d.ts +5 -0
- package/dist/styling/style.css.js +242 -0
- package/dist/styling/style.css.js.map +1 -0
- package/dist/styling/theme.const.d.ts +3 -0
- package/dist/styling/theme.const.js +381 -0
- package/dist/styling/theme.const.js.map +1 -0
- package/dist/styling/theme.d.ts +72 -0
- package/dist/styling/theme.js +200 -0
- package/dist/styling/theme.js.map +1 -0
- package/dist/styling/variantSelector.d.ts +123 -0
- package/dist/styling/variantSelector.js +25 -0
- package/dist/styling/variantSelector.js.map +1 -0
- package/dist/types.d.ts +97 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/collections.d.ts +21 -0
- package/dist/utils/collections.js +75 -0
- package/dist/utils/collections.js.map +1 -0
- package/dist/utils/customElements.d.ts +6 -0
- package/dist/utils/customElements.js +15 -0
- package/dist/utils/customElements.js.map +1 -0
- package/dist/utils/hash.d.ts +1 -0
- package/dist/utils/hash.js +17 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/json.d.ts +5 -0
- package/dist/utils/json.js +16 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/sha1.d.ts +2 -0
- package/dist/utils/sha1.js +13 -0
- package/dist/utils/sha1.js.map +1 -0
- package/dist/utils/url.d.ts +5 -0
- package/dist/utils/url.js +27 -0
- package/dist/utils/url.js.map +1 -0
- package/dist/utils/util.d.ts +3 -0
- package/dist/utils/util.js +4 -0
- package/dist/utils/util.js.map +1 -0
- package/package.json +18 -0
- package/src/api/LegacyToddleApi.ts +205 -0
- package/src/api/ToddleApiV2.ts +331 -0
- package/src/api/api.test.ts +319 -0
- package/src/api/api.ts +414 -0
- package/src/api/apiTypes.ts +145 -0
- package/src/api/headers.ts +41 -0
- package/src/api/template.ts +10 -0
- package/src/component/ToddleComponent.actionReferences.test.ts +75 -0
- package/src/component/ToddleComponent.formulasInComponent.test.ts +234 -0
- package/src/component/ToddleComponent.ts +470 -0
- package/src/component/actionUtils.ts +61 -0
- package/src/component/component.types.ts +362 -0
- package/src/component/isPageComponent.ts +6 -0
- package/src/formula/ToddleFormula.ts +30 -0
- package/src/formula/formula.ts +355 -0
- package/src/formula/formulaTypes.ts +45 -0
- package/src/formula/formulaUtils.ts +287 -0
- package/src/styling/className.test.ts +19 -0
- package/src/styling/className.ts +73 -0
- package/src/styling/style.css.ts +309 -0
- package/src/styling/theme.const.ts +390 -0
- package/src/styling/theme.ts +339 -0
- package/src/styling/variantSelector.ts +168 -0
- package/src/types.ts +158 -0
- package/src/utils/collections.test.ts +57 -0
- package/src/utils/collections.ts +122 -0
- package/src/utils/customElements.test.ts +40 -0
- package/src/utils/customElements.ts +16 -0
- package/src/utils/hash.test.ts +32 -0
- package/src/utils/hash.ts +18 -0
- package/src/utils/json.ts +18 -0
- package/src/utils/sha1.test.ts +50 -0
- package/src/utils/sha1.ts +17 -0
- package/src/utils/url.test.ts +17 -0
- package/src/utils/url.ts +33 -0
- package/src/utils/util.ts +8 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// cSpell: ignore phash
|
|
2
|
+
const SEED = 5381
|
|
3
|
+
|
|
4
|
+
// When we have separate strings it's useful to run a progressive
|
|
5
|
+
// version of djb2 where we pretend that we're still looping over
|
|
6
|
+
// the same string
|
|
7
|
+
const phash = (h: number, x: string) => {
|
|
8
|
+
let i = x.length
|
|
9
|
+
|
|
10
|
+
while (i) {
|
|
11
|
+
h = (h * 33) ^ x.charCodeAt(--i)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return h
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// This is a djb2 hashing function
|
|
18
|
+
const hash = (x: string) => {
|
|
19
|
+
return phash(SEED, x)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const AD_REPLACER_R = /(a)(d)/gi
|
|
23
|
+
|
|
24
|
+
/* This is the "capacity" of our alphabet i.e. 2x26 for all letters plus their capitalized
|
|
25
|
+
* counterparts */
|
|
26
|
+
const charsLength = 52
|
|
27
|
+
|
|
28
|
+
/* start at 75 for 'a' until 'z' (25) and then start at 65 for capitalized letters */
|
|
29
|
+
const getAlphabeticChar = (code: number) =>
|
|
30
|
+
String.fromCharCode(code + (code > 25 ? 39 : 97))
|
|
31
|
+
|
|
32
|
+
/* input a number, usually a hash and convert it to base-52 */
|
|
33
|
+
function generateAlphabeticName(code: number) {
|
|
34
|
+
let name = ''
|
|
35
|
+
let x
|
|
36
|
+
|
|
37
|
+
/* get a char and divide by alphabet-length */
|
|
38
|
+
for (x = Math.abs(code); x > charsLength; x = (x / charsLength) | 0) {
|
|
39
|
+
name = getAlphabeticChar(x % charsLength) + name
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (getAlphabeticChar(x % charsLength) + name).replace(
|
|
43
|
+
AD_REPLACER_R,
|
|
44
|
+
'$1-$2',
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const getClassName = (object: any) => {
|
|
49
|
+
return generateAlphabeticName(hash(JSON.stringify(object)))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const toValidClassName = (
|
|
53
|
+
input: string,
|
|
54
|
+
escapeSpecialCharacters = false,
|
|
55
|
+
) => {
|
|
56
|
+
// Replace invalid characters with hyphens
|
|
57
|
+
let className = input
|
|
58
|
+
// Remove leading and trailing whitespace
|
|
59
|
+
.trim()
|
|
60
|
+
// Replace whitespace with hyphens
|
|
61
|
+
.replace(/\s+/g, '-')
|
|
62
|
+
|
|
63
|
+
if (escapeSpecialCharacters) {
|
|
64
|
+
className = className.replace(/[^a-zA-Z0-9-_]/g, (match) => `\\${match}`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Ensure the class name doesn't start with a number or special character
|
|
68
|
+
if (/^[^a-zA-Z]/.test(className)) {
|
|
69
|
+
className = `_${className}`
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return className
|
|
73
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// cSpell:ignore thinn, ABCDEFGHIJKLMNOPQRSTYVWXYZ
|
|
2
|
+
import type { Component } from '../component/component.types'
|
|
3
|
+
import { omitKeys } from '../utils/collections'
|
|
4
|
+
import { isDefined } from '../utils/util'
|
|
5
|
+
import { getClassName, toValidClassName } from './className'
|
|
6
|
+
import type { OldTheme, Theme, ThemeOptions } from './theme'
|
|
7
|
+
import { getThemeCss } from './theme'
|
|
8
|
+
import type {
|
|
9
|
+
ComponentNodeModel,
|
|
10
|
+
ElementNodeModel,
|
|
11
|
+
StyleDeclarationBlock,
|
|
12
|
+
} from './variantSelector'
|
|
13
|
+
import { variantSelector } from './variantSelector'
|
|
14
|
+
|
|
15
|
+
const LEGACY_BREAKPOINTS = {
|
|
16
|
+
large: 1440,
|
|
17
|
+
small: 576,
|
|
18
|
+
medium: 960,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function kebabCase(string: string) {
|
|
22
|
+
return string
|
|
23
|
+
.split('')
|
|
24
|
+
.map((char) => {
|
|
25
|
+
return 'ABCDEFGHIJKLMNOPQRSTYVWXYZ'.includes(char)
|
|
26
|
+
? '-' + char.toLocaleLowerCase()
|
|
27
|
+
: char
|
|
28
|
+
})
|
|
29
|
+
.join('')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const SIZE_PROPERTIES = new Set([
|
|
33
|
+
'width',
|
|
34
|
+
'min-width',
|
|
35
|
+
'max-width',
|
|
36
|
+
'height',
|
|
37
|
+
'min-height',
|
|
38
|
+
'max-height',
|
|
39
|
+
'margin',
|
|
40
|
+
'margin-top',
|
|
41
|
+
'margin-left',
|
|
42
|
+
'margin-bottom',
|
|
43
|
+
'margin-right',
|
|
44
|
+
'padding',
|
|
45
|
+
'padding-top',
|
|
46
|
+
'padding-left',
|
|
47
|
+
'padding-bottom',
|
|
48
|
+
'padding-right',
|
|
49
|
+
'gap',
|
|
50
|
+
'gap-x',
|
|
51
|
+
'gap-y',
|
|
52
|
+
'border-radius',
|
|
53
|
+
'border-bottom-left-radius',
|
|
54
|
+
'border-bottom-right-radius',
|
|
55
|
+
'border-top-left-radius',
|
|
56
|
+
'border-top-right-radius',
|
|
57
|
+
'border-width',
|
|
58
|
+
'border-top-width',
|
|
59
|
+
'border-left-width',
|
|
60
|
+
'border-bottom-width',
|
|
61
|
+
'border-right-width',
|
|
62
|
+
'font-size',
|
|
63
|
+
'top',
|
|
64
|
+
'right',
|
|
65
|
+
'bottom',
|
|
66
|
+
'left',
|
|
67
|
+
'outline-width',
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
export const createStylesheet = (
|
|
71
|
+
root: Component,
|
|
72
|
+
components: Component[],
|
|
73
|
+
theme: Theme | OldTheme,
|
|
74
|
+
options: ThemeOptions,
|
|
75
|
+
// eslint-disable-next-line max-params
|
|
76
|
+
) => {
|
|
77
|
+
const hashes = new Set<string>()
|
|
78
|
+
|
|
79
|
+
// Get fonts used on the page
|
|
80
|
+
const fonts = getAllFonts(components)
|
|
81
|
+
|
|
82
|
+
//Exclude fonts that are not used on this page.
|
|
83
|
+
let stylesheet = getThemeCss(
|
|
84
|
+
'breakpoints' in theme
|
|
85
|
+
? {
|
|
86
|
+
...theme,
|
|
87
|
+
fontFamily: Object.fromEntries(
|
|
88
|
+
Object.entries(theme.fontFamily).filter(
|
|
89
|
+
([key, value]) => value.default ?? fonts.has('--font-' + key),
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
}
|
|
93
|
+
: {
|
|
94
|
+
...theme,
|
|
95
|
+
fonts: theme.fonts,
|
|
96
|
+
},
|
|
97
|
+
options,
|
|
98
|
+
)
|
|
99
|
+
const styleToCss = (style: StyleDeclarationBlock) => {
|
|
100
|
+
return Object.entries(style)
|
|
101
|
+
.map(([property, value]) => {
|
|
102
|
+
if (!isDefined(value)) {
|
|
103
|
+
// ignore undefined/null values
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
const propertyName = kebabCase(property)
|
|
107
|
+
const propertyValue =
|
|
108
|
+
String(Number(value)) === String(value) &&
|
|
109
|
+
SIZE_PROPERTIES.has(propertyName)
|
|
110
|
+
? `${Number(value) * 4}px`
|
|
111
|
+
: value
|
|
112
|
+
return `${propertyName}:${propertyValue};`
|
|
113
|
+
})
|
|
114
|
+
.filter(Boolean)
|
|
115
|
+
.join('\n ')
|
|
116
|
+
}
|
|
117
|
+
const getNodeStyles = (
|
|
118
|
+
node: ElementNodeModel | ComponentNodeModel,
|
|
119
|
+
classHash: string,
|
|
120
|
+
) => {
|
|
121
|
+
try {
|
|
122
|
+
const style = omitKeys(node.style ?? {}, [
|
|
123
|
+
'variants',
|
|
124
|
+
'breakpoints',
|
|
125
|
+
'shadows',
|
|
126
|
+
])
|
|
127
|
+
const styleVariants = node.variants ?? node.style?.variants
|
|
128
|
+
const renderVariant = (
|
|
129
|
+
selector: string,
|
|
130
|
+
style: StyleDeclarationBlock,
|
|
131
|
+
options?: { startingStyle?: boolean },
|
|
132
|
+
) => {
|
|
133
|
+
const scrollbarStyles = Object.entries(style).filter(
|
|
134
|
+
([key]) => key == 'scrollbar-width',
|
|
135
|
+
)
|
|
136
|
+
// If selectorCss is empty, we don't need to render the selector
|
|
137
|
+
let styles = styleToCss(style)
|
|
138
|
+
if (options?.startingStyle) {
|
|
139
|
+
styles = `@starting-style {
|
|
140
|
+
${styles}
|
|
141
|
+
}`
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return `
|
|
145
|
+
${
|
|
146
|
+
styles.length > 0
|
|
147
|
+
? `${selector} {
|
|
148
|
+
${styles}
|
|
149
|
+
}`
|
|
150
|
+
: ''
|
|
151
|
+
}
|
|
152
|
+
${
|
|
153
|
+
scrollbarStyles.length > 0
|
|
154
|
+
? `
|
|
155
|
+
${selector}::-webkit-scrollbar {
|
|
156
|
+
${scrollbarStyles
|
|
157
|
+
.map(([_, value]) => {
|
|
158
|
+
switch (value) {
|
|
159
|
+
case 'none':
|
|
160
|
+
return 'width: 0;'
|
|
161
|
+
case 'thinn':
|
|
162
|
+
case 'thin':
|
|
163
|
+
return 'width: 4px;'
|
|
164
|
+
default:
|
|
165
|
+
return ''
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
.join('\n')}
|
|
169
|
+
}
|
|
170
|
+
`
|
|
171
|
+
: ''
|
|
172
|
+
}
|
|
173
|
+
`
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return `
|
|
177
|
+
${renderVariant('.' + classHash, style)}
|
|
178
|
+
${(styleVariants ?? [])
|
|
179
|
+
.map((variant) => {
|
|
180
|
+
const renderedVariant = renderVariant(
|
|
181
|
+
`.${classHash}${variantSelector(variant)}`,
|
|
182
|
+
variant.style,
|
|
183
|
+
{
|
|
184
|
+
startingStyle: variant.startingStyle,
|
|
185
|
+
},
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if (variant.mediaQuery) {
|
|
189
|
+
return `
|
|
190
|
+
@media (${Object.entries(variant.mediaQuery)
|
|
191
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
192
|
+
.filter(Boolean)
|
|
193
|
+
.join(') and (')}) {
|
|
194
|
+
${renderedVariant}
|
|
195
|
+
}
|
|
196
|
+
`
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (variant.breakpoint) {
|
|
200
|
+
return `
|
|
201
|
+
@media (min-width: ${LEGACY_BREAKPOINTS[variant.breakpoint]}px) {
|
|
202
|
+
${renderedVariant}
|
|
203
|
+
}
|
|
204
|
+
`
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return renderedVariant
|
|
208
|
+
})
|
|
209
|
+
.join('\n')}
|
|
210
|
+
${
|
|
211
|
+
node.animations
|
|
212
|
+
? Object.entries(node.animations)
|
|
213
|
+
.map(([animationName, keyframes]) => {
|
|
214
|
+
return `
|
|
215
|
+
@keyframes ${animationName} {
|
|
216
|
+
${Object.values(keyframes)
|
|
217
|
+
.sort((a, b) => a.position - b.position)
|
|
218
|
+
.map(({ key, position, value }) => {
|
|
219
|
+
return `
|
|
220
|
+
${position * 100}% {
|
|
221
|
+
${key}: ${value};
|
|
222
|
+
}
|
|
223
|
+
`
|
|
224
|
+
})
|
|
225
|
+
.join('\n')}
|
|
226
|
+
}
|
|
227
|
+
`
|
|
228
|
+
})
|
|
229
|
+
.join('\n')
|
|
230
|
+
: ''
|
|
231
|
+
}
|
|
232
|
+
`
|
|
233
|
+
} catch (e) {
|
|
234
|
+
// eslint-disable-next-line no-console
|
|
235
|
+
console.error(e)
|
|
236
|
+
return ''
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Make sure that CSS for dependencies are rendered first so that instance styles can override
|
|
241
|
+
const visitedComponents = new Set<string>()
|
|
242
|
+
function insertComponentStyles(
|
|
243
|
+
component: Component,
|
|
244
|
+
package_name?: string,
|
|
245
|
+
): string | undefined {
|
|
246
|
+
if (visitedComponents.has(component.name)) {
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
visitedComponents.add(component.name)
|
|
250
|
+
if (!component.nodes) {
|
|
251
|
+
// eslint-disable-next-line no-console
|
|
252
|
+
console.warn('Unable to find nodes for component', component.name)
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
Object.entries(component.nodes).forEach(([id, node]) => {
|
|
256
|
+
if (node.type === 'component') {
|
|
257
|
+
const childComponent = components.find(
|
|
258
|
+
(c) =>
|
|
259
|
+
c.name ===
|
|
260
|
+
[node.package ?? package_name, node.name]
|
|
261
|
+
.filter((c) => c)
|
|
262
|
+
.join('/'),
|
|
263
|
+
)
|
|
264
|
+
if (childComponent) {
|
|
265
|
+
insertComponentStyles(childComponent, node.package ?? package_name)
|
|
266
|
+
stylesheet += getNodeStyles(
|
|
267
|
+
node as any,
|
|
268
|
+
toValidClassName(`${component.name}:${id}`, true),
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (node.type !== 'element') {
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
const classHash = getClassName([node.style, node.variants])
|
|
278
|
+
if (hashes.has(classHash)) {
|
|
279
|
+
return ''
|
|
280
|
+
}
|
|
281
|
+
hashes.add(classHash)
|
|
282
|
+
stylesheet += getNodeStyles(node as any, classHash)
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
insertComponentStyles(root)
|
|
286
|
+
|
|
287
|
+
return stylesheet
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export const getAllFonts = (components: Component[]) => {
|
|
291
|
+
return new Set(
|
|
292
|
+
components
|
|
293
|
+
.flatMap((component) => {
|
|
294
|
+
return Object.values(component.nodes).flatMap((node) => {
|
|
295
|
+
if (node.type === 'element') {
|
|
296
|
+
return [
|
|
297
|
+
node.style.fontFamily,
|
|
298
|
+
node.style['font-family'],
|
|
299
|
+
...(node.variants?.map(
|
|
300
|
+
(v) => v.style.fontFamily ?? v.style['font-family'],
|
|
301
|
+
) ?? []),
|
|
302
|
+
].filter(isDefined)
|
|
303
|
+
}
|
|
304
|
+
return []
|
|
305
|
+
})
|
|
306
|
+
})
|
|
307
|
+
.map((f) => f.replace('var(', '').replace(')', '').replace(/'/g, '')),
|
|
308
|
+
)
|
|
309
|
+
}
|