@sanity/ui 3.1.12 → 3.1.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ui",
3
- "version": "3.1.12",
3
+ "version": "3.1.13",
4
4
  "keywords": [
5
5
  "sanity",
6
6
  "ui",
@@ -27,6 +27,7 @@ import {
27
27
  ThemeColorSyntax,
28
28
  } from '../system'
29
29
  import {ColorTokenContext, resolveColorTokenValue as _color} from './colorToken'
30
+ import {defineLazyProperty} from './lib/lazy'
30
31
  import {resolveColorTokens} from './resolveColorTokens'
31
32
 
32
33
  export function buildColorTheme(config?: ThemeConfig): ThemeColorSchemes_v2 {
@@ -35,10 +36,13 @@ export function buildColorTheme(config?: ThemeConfig): ThemeColorSchemes_v2 {
35
36
  color: resolveColorTokens(config?.color),
36
37
  }
37
38
 
38
- return {
39
- light: buildColorScheme({scheme: 'light'}, resolvedConfig),
40
- dark: buildColorScheme({scheme: 'dark'}, resolvedConfig),
41
- }
39
+ // Lazy schemes — each scheme's token tree is only built when first accessed
40
+ const schemes = {} as ThemeColorSchemes_v2
41
+
42
+ defineLazyProperty(schemes, 'light', () => buildColorScheme({scheme: 'light'}, resolvedConfig))
43
+ defineLazyProperty(schemes, 'dark', () => buildColorScheme({scheme: 'dark'}, resolvedConfig))
44
+
45
+ return schemes
42
46
  }
43
47
 
44
48
  function buildColorScheme(
@@ -47,10 +51,11 @@ function buildColorScheme(
47
51
  ): ThemeColorScheme_v2 {
48
52
  const {scheme} = options
49
53
 
54
+ // Lazy tones — each tone's token tree is only built when first accessed
50
55
  const colorScheme = {} as ThemeColorScheme_v2
51
56
 
52
57
  for (const tone of THEME_COLOR_CARD_TONES) {
53
- colorScheme[tone] = buildCardColorTheme({scheme, tone}, config)
58
+ defineLazyProperty(colorScheme, tone, () => buildCardColorTheme({scheme, tone}, config))
54
59
  }
55
60
 
56
61
  return colorScheme
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Defines a lazy, self-replacing property on `obj`.
3
+ *
4
+ * On first access the `factory` is called and the getter is replaced with the
5
+ * computed plain value — subsequent reads have zero overhead.
6
+ *
7
+ * The property is enumerable (visible in `Object.keys` / spread) in both states.
8
+ *
9
+ * @internal
10
+ */
11
+ export function defineLazyProperty<T extends object, K extends keyof T>(
12
+ obj: T,
13
+ key: K,
14
+ factory: () => T[K],
15
+ ): void {
16
+ Object.defineProperty(obj, key, {
17
+ get() {
18
+ const value = factory()
19
+ Object.defineProperty(obj, key, {
20
+ value,
21
+ enumerable: true,
22
+ writable: false,
23
+ configurable: false,
24
+ })
25
+ return value
26
+ },
27
+ enumerable: true,
28
+ configurable: true,
29
+ })
30
+ }
@@ -3,6 +3,7 @@ import {COLOR_HUES} from '@sanity/color'
3
3
  import {ThemeColorPalette, ThemeConfig} from '../config'
4
4
  import {defaultColorPalette} from '../defaults/colorPalette'
5
5
  import {
6
+ THEME_COLOR_CARD_TONES,
6
7
  THEME_COLOR_STATE_TONES,
7
8
  ThemeColorAvatar_v2,
8
9
  ThemeColorBadge_v2,
@@ -12,7 +13,6 @@ import {
12
13
  ThemeColorButtonMode_v2,
13
14
  ThemeColorButtonTone_v2,
14
15
  ThemeColorCard_v2,
15
- ThemeColorCardToneKey,
16
16
  ThemeColorInput_v2,
17
17
  ThemeColorInputMode_v2,
18
18
  ThemeColorInputState_v2,
@@ -25,6 +25,7 @@ import {
25
25
  ThemeColorState_v2,
26
26
  ThemeColorSyntax,
27
27
  } from '../system'
28
+ import {defineLazyProperty} from './lib/lazy'
28
29
  import {renderColorValue, RenderColorValueOptions} from './renderColorValue'
29
30
 
30
31
  export function renderThemeColorSchemes(
@@ -33,41 +34,40 @@ export function renderThemeColorSchemes(
33
34
  ): ThemeColorSchemes_v2 {
34
35
  const colorPalette = config?.palette ?? defaultColorPalette
35
36
 
36
- return {
37
- light: renderThemeColorScheme(colorPalette, value.light),
38
- dark: renderThemeColorScheme(colorPalette, value.dark),
39
- }
37
+ // Lazy schemes — each scheme is only rendered when first accessed
38
+ const schemes = {} as ThemeColorSchemes_v2
39
+ defineLazyProperty(schemes, 'light', () => renderThemeColorScheme(colorPalette, value.light))
40
+ defineLazyProperty(schemes, 'dark', () => renderThemeColorScheme(colorPalette, value.dark))
41
+
42
+ return schemes
40
43
  }
41
44
 
42
45
  function renderThemeColorScheme(
43
46
  colorPalette: ThemeColorPalette,
44
47
  value: ThemeColorScheme_v2,
45
48
  ): ThemeColorScheme_v2 {
46
- const toneEntries = Object.entries(value) as [ThemeColorCardToneKey, ThemeColorCard_v2][]
47
-
48
- const [, transparentTone] = toneEntries.find(([k]) => k === 'transparent')!
49
- const [, defaultTone] = toneEntries.find(([k]) => k === 'default')!
49
+ // The `default` tone must be rendered eagerly — other tones depend on its `bg`.
50
+ const renderedDefaultTone = renderThemeColor(value.default, {colorPalette})
50
51
 
51
- // The `transparent` and `default` tones are special cases, so we render them first
52
- // (rendered without a `bg` option).
53
- // But the rest of the tones are rendered on top of the `default` tone's `bg`.
54
- const renderedTransparentTone = renderThemeColor(transparentTone, {colorPalette})
55
- const renderedDefaultTone = renderThemeColor(defaultTone, {colorPalette})
56
-
57
- // Get the `default` tone's `bg` property
58
52
  const bg = renderedDefaultTone.bg
59
53
 
60
54
  if (bg === 'white') {
61
55
  throw new Error('Cannot blend with white background')
62
56
  }
63
57
 
64
- return Object.fromEntries([
65
- ['transparent', renderedTransparentTone],
66
- ['default', renderedDefaultTone],
67
- ...toneEntries
68
- .filter(([k]) => k !== 'default' && k !== 'transparent')
69
- .map(([k, v]) => [k, renderThemeColor(v, {bg, colorPalette})]),
70
- ]) as ThemeColorScheme_v2
58
+ // All other tones are lazy. `transparent` and `default` are rendered without
59
+ // a `bg` option; the rest blend on top of `default`'s `bg`.
60
+ // We iterate THEME_COLOR_CARD_TONES (not Object.entries) to avoid
61
+ // force-evaluating lazy getters from the build phase.
62
+ const scheme = {default: renderedDefaultTone} as ThemeColorScheme_v2
63
+
64
+ for (const tone of THEME_COLOR_CARD_TONES) {
65
+ if (tone === 'default') continue
66
+ const opts = tone === 'transparent' ? {colorPalette} : {bg, colorPalette}
67
+ defineLazyProperty(scheme, tone, () => renderThemeColor(value[tone], opts))
68
+ }
69
+
70
+ return scheme
71
71
  }
72
72
 
73
73
  function renderThemeColor(
@@ -1,3 +1,4 @@
1
+ import {defineLazyProperty} from './build/lib/lazy'
1
2
  import {defaultThemeConfig} from './defaults/config'
2
3
  import {
3
4
  RootTheme,
@@ -29,8 +30,6 @@ export function getScopedTheme(
29
30
  const v0 = is_v2(themeProp) ? v2_v0(themeProp) : themeProp
30
31
  const v2 = is_v2(themeProp) ? themeProp : v0_v2(themeProp)
31
32
 
32
- const colorScheme_v0 = v0.color[scheme] || v0.color.light
33
- const color_v0 = (colorScheme_v0 as Record<string, ThemeColor>)[tone] || colorScheme_v0.default
34
33
  const layer_v0 = v0.layer || defaultThemeConfig.layer
35
34
 
36
35
  const colorScheme_v2 = v2.color[scheme] || v2.color.light
@@ -38,19 +37,27 @@ export function getScopedTheme(
38
37
  const color_v2_9 = themeColor_v0_v2_9(color_v2)
39
38
  const layer_v2 = v2.layer || defaultThemeConfig.layer
40
39
 
41
- const theme: Theme = {
42
- sanity: {
43
- ...v0,
44
- color: color_v0,
45
- layer: layer_v0,
46
- v2: {
47
- ...v2,
48
- _resolved: true,
49
- color: color_v2_9,
50
- layer: layer_v2,
51
- },
40
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
41
+ const {color: _v0Color, ...v0Rest} = v0
42
+
43
+ const sanity = {
44
+ ...v0Rest,
45
+ layer: layer_v0,
46
+ v2: {
47
+ ...v2,
48
+ _resolved: true,
49
+ color: color_v2_9,
50
+ layer: layer_v2,
52
51
  },
53
- }
52
+ } as Theme['sanity']
53
+
54
+ // Defer v0 color computation — only resolved if legacy code reads theme.sanity.color
55
+ defineLazyProperty(sanity, 'color', () => {
56
+ const colorScheme_v0 = v0.color[scheme] || v0.color.light
57
+ return (colorScheme_v0 as Record<string, ThemeColor>)[tone] || colorScheme_v0.default
58
+ })
59
+
60
+ const theme: Theme = {sanity}
54
61
 
55
62
  _setCachedTheme(themeProp, scheme, tone, theme)
56
63
 
@@ -1,3 +1,4 @@
1
+ import {defineLazyProperty} from '../build/lib/lazy'
1
2
  import {
2
3
  RootTheme,
3
4
  RootTheme_v2,
@@ -7,10 +8,23 @@ import {
7
8
  ThemeColorInputState,
8
9
  ThemeColorInputState_v2,
9
10
  ThemeColorInputStates,
11
+ ThemeColorScheme,
10
12
  } from '../system'
11
13
 
12
14
  const cache = new WeakMap<RootTheme_v2, RootTheme>()
13
15
 
16
+ const V0_TONES = ['transparent', 'default', 'primary', 'positive', 'caution', 'critical'] as const
17
+
18
+ function lazyV0Scheme(schemeKey: 'light' | 'dark', color: RootTheme_v2['color']): ThemeColorScheme {
19
+ const scheme = {} as ThemeColorScheme
20
+
21
+ for (const tone of V0_TONES) {
22
+ defineLazyProperty(scheme, tone, () => themeColor_v2_v0(color[schemeKey][tone]))
23
+ }
24
+
25
+ return scheme
26
+ }
27
+
14
28
  /** @internal */
15
29
  export function v2_v0(v2: RootTheme_v2): RootTheme {
16
30
  const cachedTheme = cache.get(v2)
@@ -31,29 +45,19 @@ export function v2_v0(v2: RootTheme_v2): RootTheme {
31
45
  style: styles,
32
46
  } = v2
33
47
 
34
- return {
48
+ // Lazy v0 color — two-level lazy structure so we don't force-evaluate
49
+ // the v2 lazy getters until a v0 tone is actually accessed.
50
+ const v0Color = {} as RootTheme['color']
51
+
52
+ defineLazyProperty(v0Color, 'light', () => lazyV0Scheme('light', color))
53
+ defineLazyProperty(v0Color, 'dark', () => lazyV0Scheme('dark', color))
54
+
55
+ const theme: RootTheme = {
35
56
  _version: 0,
36
57
  avatar,
37
58
  button,
38
59
  container,
39
- color: {
40
- light: {
41
- transparent: themeColor_v2_v0(color.light.transparent),
42
- default: themeColor_v2_v0(color.light.default),
43
- primary: themeColor_v2_v0(color.light.primary),
44
- positive: themeColor_v2_v0(color.light.positive),
45
- caution: themeColor_v2_v0(color.light.caution),
46
- critical: themeColor_v2_v0(color.light.critical),
47
- },
48
- dark: {
49
- transparent: themeColor_v2_v0(color.dark.transparent),
50
- default: themeColor_v2_v0(color.dark.default),
51
- primary: themeColor_v2_v0(color.dark.primary),
52
- positive: themeColor_v2_v0(color.dark.positive),
53
- caution: themeColor_v2_v0(color.dark.caution),
54
- critical: themeColor_v2_v0(color.dark.critical),
55
- },
56
- },
60
+ color: v0Color,
57
61
  focusRing: input.text.focusRing,
58
62
  fonts,
59
63
  input,
@@ -65,6 +69,10 @@ export function v2_v0(v2: RootTheme_v2): RootTheme {
65
69
 
66
70
  v2,
67
71
  }
72
+
73
+ cache.set(v2, theme)
74
+
75
+ return theme
68
76
  }
69
77
 
70
78
  function themeColor_v2_v0(color_v2: ThemeColorCard_v2): ThemeColor {