@nordcraft/core 1.0.77 → 1.0.78

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.
@@ -22,9 +22,9 @@ const LEGACY_BREAKPOINTS = {
22
22
  export function kebabCase(string: string) {
23
23
  return string
24
24
  .split('')
25
- .map((char) => {
25
+ .map((char, index) => {
26
26
  return 'ABCDEFGHIJKLMNOPQRSTYVWXYZ'.includes(char)
27
- ? '-' + char.toLocaleLowerCase()
27
+ ? (index === 0 ? '' : '-') + char.toLocaleLowerCase()
28
28
  : char
29
29
  })
30
30
  .join('')
@@ -68,6 +68,150 @@ const SIZE_PROPERTIES = new Set([
68
68
  'outline-width',
69
69
  ])
70
70
 
71
+ export const styleToCss = (style: NodeStyleModel) => {
72
+ return Object.entries(style)
73
+ .map(([property, value]) => {
74
+ if (!isDefined(value)) {
75
+ // ignore undefined/null values
76
+ return
77
+ }
78
+ const propertyName = kebabCase(property)
79
+ const propertyValue =
80
+ String(Number(value)) === String(value) &&
81
+ SIZE_PROPERTIES.has(propertyName)
82
+ ? `${Number(value) * 4}px`
83
+ : value
84
+ return `${propertyName}:${propertyValue};`
85
+ })
86
+ .filter(Boolean)
87
+ .join('\n ')
88
+ }
89
+
90
+ export const getNodeStyles = (
91
+ node: ElementNodeModel | ComponentNodeModel,
92
+ classHash: string,
93
+ animationHashes: Set<string> = new Set(),
94
+ ) => {
95
+ try {
96
+ const style = omitKeys(node.style ?? {}, [
97
+ 'variants',
98
+ 'breakpoints',
99
+ 'shadows',
100
+ ])
101
+ const styleVariants =
102
+ node.variants ??
103
+ // Support for old style variants stored inside style object
104
+ // Once we have better versioning options, this should be removed
105
+ (node.style?.variants as any as StyleVariant[])
106
+ const renderVariant = (
107
+ selector: string,
108
+ style: NodeStyleModel,
109
+ options?: Nullable<{ startingStyle?: Nullable<boolean> }>,
110
+ ) => {
111
+ const scrollbarStyles = Object.entries(style).filter(
112
+ ([key]) => key === 'scrollbar-width',
113
+ )
114
+ // If selectorCss is empty, we don't need to render the selector
115
+ let styles = styleToCss(style)
116
+ if (options?.startingStyle) {
117
+ styles = `
118
+ @starting-style {
119
+ ${styles}
120
+ }`
121
+ }
122
+ const scrollbarCSS =
123
+ scrollbarStyles.length > 0
124
+ ? `
125
+
126
+ ${selector}::-webkit-scrollbar {
127
+ ${scrollbarStyles
128
+ .map(([_, value]) => {
129
+ switch (value) {
130
+ case 'none':
131
+ return 'width: 0;'
132
+ case 'thinn':
133
+ case 'thin':
134
+ return 'width: 4px;'
135
+ default:
136
+ return ''
137
+ }
138
+ })
139
+ .join('\n')}
140
+ }`
141
+ : ''
142
+ const stylesCSS =
143
+ styles.length > 0
144
+ ? `
145
+
146
+ ${selector} {
147
+ ${styles}
148
+ }`
149
+ : ''
150
+ return stylesCSS + scrollbarCSS
151
+ }
152
+
153
+ const variantsCSS = (styleVariants ?? [])
154
+ .map((variant) => {
155
+ const renderedVariant = renderVariant(
156
+ `.${classHash}${variantSelector(variant)}`,
157
+ variant.style,
158
+ {
159
+ startingStyle: variant.startingStyle,
160
+ },
161
+ )
162
+
163
+ if (variant.mediaQuery) {
164
+ return `
165
+
166
+ @media (${Object.entries(variant.mediaQuery)
167
+ .filter(([_, value]) => value !== null && value !== undefined)
168
+ .map(([key, value]) => `${key}: ${value}`)
169
+ .join(') and (')}) {${renderedVariant}
170
+ }`
171
+ }
172
+
173
+ if (variant.breakpoint) {
174
+ return `
175
+
176
+ @media (min-width: ${LEGACY_BREAKPOINTS[variant.breakpoint]}px) {${renderedVariant}
177
+ }`
178
+ }
179
+
180
+ return renderedVariant
181
+ })
182
+ .join('')
183
+ const animationsCSS = node.animations
184
+ ? Object.entries(node.animations)
185
+ .map(([animationName, keyframes]) => {
186
+ // Animation names are stored by their hash, so no need to render them more than once.
187
+ if (animationHashes.has(animationName)) {
188
+ return ''
189
+ }
190
+ animationHashes.add(animationName)
191
+ return `
192
+
193
+ @keyframes ${animationName} {${Object.values(keyframes)
194
+ .sort((a, b) => a.position - b.position)
195
+ .map(({ key, position, value }) => {
196
+ return `
197
+ ${position * 100}% {
198
+ ${key}: ${value};
199
+ }`
200
+ })
201
+ .join('\n')}
202
+ }`
203
+ })
204
+ .join('\n')
205
+ : ''
206
+
207
+ return renderVariant('.' + classHash, style) + variantsCSS + animationsCSS
208
+ } catch (e) {
209
+ // eslint-disable-next-line no-console
210
+ console.error(e)
211
+ return ''
212
+ }
213
+ }
214
+
71
215
  export const createStylesheet = (
72
216
  root: Component,
73
217
  components: Component[],
@@ -109,155 +253,6 @@ export const createStylesheet = (
109
253
  ),
110
254
  options,
111
255
  )
112
- const styleToCss = (style: NodeStyleModel) => {
113
- return Object.entries(style)
114
- .map(([property, value]) => {
115
- if (!isDefined(value)) {
116
- // ignore undefined/null values
117
- return
118
- }
119
- const propertyName = kebabCase(property)
120
- const propertyValue =
121
- String(Number(value)) === String(value) &&
122
- SIZE_PROPERTIES.has(propertyName)
123
- ? `${Number(value) * 4}px`
124
- : value
125
- return `${propertyName}:${propertyValue};`
126
- })
127
- .filter(Boolean)
128
- .join('\n ')
129
- }
130
- const getNodeStyles = (
131
- node: ElementNodeModel | ComponentNodeModel,
132
- classHash: string,
133
- ) => {
134
- try {
135
- const style = omitKeys(node.style ?? {}, [
136
- 'variants',
137
- 'breakpoints',
138
- 'shadows',
139
- ])
140
- const styleVariants =
141
- node.variants ??
142
- // Support for old style variants stored inside style object
143
- // Once we have better versioning options, this should be removed
144
- (node.style?.variants as any as StyleVariant[])
145
- const renderVariant = (
146
- selector: string,
147
- style: NodeStyleModel,
148
- options?: Nullable<{ startingStyle?: Nullable<boolean> }>,
149
- ) => {
150
- const scrollbarStyles = Object.entries(style).filter(
151
- ([key]) => key === 'scrollbar-width',
152
- )
153
- // If selectorCss is empty, we don't need to render the selector
154
- let styles = styleToCss(style)
155
- if (options?.startingStyle) {
156
- styles = `@starting-style {
157
- ${styles}
158
- }`
159
- }
160
-
161
- return `
162
- ${
163
- styles.length > 0
164
- ? `${selector} {
165
- ${styles}
166
- }`
167
- : ''
168
- }
169
- ${
170
- scrollbarStyles.length > 0
171
- ? `
172
- ${selector}::-webkit-scrollbar {
173
- ${scrollbarStyles
174
- .map(([_, value]) => {
175
- switch (value) {
176
- case 'none':
177
- return 'width: 0;'
178
- case 'thinn':
179
- case 'thin':
180
- return 'width: 4px;'
181
- default:
182
- return ''
183
- }
184
- })
185
- .join('\n')}
186
- }
187
- `
188
- : ''
189
- }
190
- `
191
- }
192
-
193
- return `
194
- ${renderVariant('.' + classHash, style)}
195
- ${(styleVariants ?? [])
196
- .map((variant) => {
197
- const renderedVariant = renderVariant(
198
- `.${classHash}${variantSelector(variant)}`,
199
- variant.style,
200
- {
201
- startingStyle: variant.startingStyle,
202
- },
203
- )
204
-
205
- if (variant.mediaQuery) {
206
- return `
207
- @media (${Object.entries(variant.mediaQuery)
208
- .map(([key, value]) => `${key}: ${value}`)
209
- .filter(Boolean)
210
- .join(') and (')}) {
211
- ${renderedVariant}
212
- }
213
- `
214
- }
215
-
216
- if (variant.breakpoint) {
217
- return `
218
- @media (min-width: ${LEGACY_BREAKPOINTS[variant.breakpoint]}px) {
219
- ${renderedVariant}
220
- }
221
- `
222
- }
223
-
224
- return renderedVariant
225
- })
226
- .join('\n')}
227
- ${
228
- node.animations
229
- ? Object.entries(node.animations)
230
- .map(([animationName, keyframes]) => {
231
- // Animation names are stored by their hash, so no need to render them more than once.
232
- if (animationHashes.has(animationName)) {
233
- return ''
234
- }
235
- animationHashes.add(animationName)
236
- return `
237
- @keyframes ${animationName} {
238
- ${Object.values(keyframes)
239
- .sort((a, b) => a.position - b.position)
240
- .map(({ key, position, value }) => {
241
- return `
242
- ${position * 100}% {
243
- ${key}: ${value};
244
- }
245
- `
246
- })
247
- .join('\n')}
248
- }
249
- `
250
- })
251
- .join('\n')
252
- : ''
253
- }
254
- `
255
- } catch (e) {
256
- // eslint-disable-next-line no-console
257
- console.error(e)
258
- return ''
259
- }
260
- }
261
256
 
262
257
  // Make sure that CSS for dependencies are rendered first so that instance styles can override
263
258
  const visitedComponents = new Set<string>()
@@ -288,6 +283,7 @@ ${selector}::-webkit-scrollbar {
288
283
  stylesheet += getNodeStyles(
289
284
  node as any,
290
285
  toValidClassName(`${component.name}:${id}`, true),
286
+ animationHashes,
291
287
  )
292
288
 
293
289
  return
@@ -301,7 +297,7 @@ ${selector}::-webkit-scrollbar {
301
297
  return ''
302
298
  }
303
299
  hashes.add(classHash)
304
- stylesheet += getNodeStyles(node as any, classHash)
300
+ stylesheet += getNodeStyles(node as any, classHash, animationHashes)
305
301
  })
306
302
  }
307
303
  insertComponentStyles(root)
@@ -2,6 +2,9 @@ import type { OldTheme } from './theme'
2
2
 
3
3
  export const CUSTOM_PROPERTIES_STYLESHEET_ID = 'nc-custom-properties'
4
4
 
5
+ export const THEME_DATA_ATTRIBUTE = 'data-nc-theme'
6
+ export const THEME_COOKIE_NAME = 'nc-theme'
7
+
5
8
  export const RESET_STYLES = `
6
9
  @layer reset {
7
10
  html {
@@ -38,11 +38,11 @@ describe('renderThemeValues()', () => {
38
38
  }
39
39
  expect(
40
40
  renderThemeValues(
41
- '[data-theme="my-theme"]',
41
+ '[data-nc-theme="my-theme"]',
42
42
  getThemeEntries(theme, 'my-theme'),
43
43
  ),
44
44
  ).toMatchInlineSnapshot(`
45
- "[data-theme="my-theme"] {
45
+ "[data-nc-theme="my-theme"] {
46
46
  --my-color: rebeccapurple;
47
47
  --my-third-prop: var(--my-color);
48
48
  }"
@@ -126,11 +126,11 @@ describe('getThemeCss()', () => {
126
126
  }
127
127
  }
128
128
 
129
- [data-theme~="my-theme"] {
129
+ [data-nc-theme~="my-theme"] {
130
130
  --my-color: rebeccapurple;
131
131
  --my-third-prop: var(--my-color);
132
132
  }
133
- [data-theme~="super-dark"] {
133
+ [data-nc-theme~="super-dark"] {
134
134
  --my-color: black;
135
135
  }
136
136
 
@@ -2,7 +2,7 @@ import type { CustomPropertyName } from '../component/component.types'
2
2
  import type { Nullable } from '../types'
3
3
  import { isDefined } from '../utils/util'
4
4
  import { renderSyntaxDefinition, type CssSyntaxNode } from './customProperty'
5
- import { RESET_STYLES } from './theme.const'
5
+ import { RESET_STYLES, THEME_DATA_ATTRIBUTE } from './theme.const'
6
6
 
7
7
  export interface ThemeOptions {
8
8
  includeResetStyle: boolean
@@ -147,7 +147,7 @@ export const getThemeCss = (
147
147
  ${Object.entries(themeV2.themes ?? {})
148
148
  .map(([key, _t]) =>
149
149
  renderThemeValues(
150
- `[data-theme~="${key}"]`,
150
+ `[${THEME_DATA_ATTRIBUTE}~="${key}"]`,
151
151
  getThemeEntries(themeV2, key),
152
152
  ),
153
153
  )
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  CustomProperty,
3
3
  CustomPropertyName,
4
+ MediaQuery,
4
5
  NodeStyleModel,
5
6
  } from '../component/component.types'
6
7
  import type { Nullable } from '../types'
@@ -24,13 +25,6 @@ export type Filter =
24
25
  percent: number
25
26
  }
26
27
 
27
- type MediaQuery = {
28
- 'min-width'?: Nullable<string>
29
- 'max-width'?: Nullable<string>
30
- 'min-height'?: Nullable<string>
31
- 'max-height'?: Nullable<string>
32
- }
33
-
34
28
  export interface StyleVariant {
35
29
  'even-child'?: Nullable<boolean>
36
30
  'first-child'?: Nullable<boolean>