@newtonedev/components 0.1.0 → 0.1.2

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 (181) hide show
  1. package/dist/AppShell/AppShell.d.ts +4 -0
  2. package/dist/AppShell/AppShell.d.ts.map +1 -0
  3. package/dist/AppShell/AppShell.styles.d.ts +16 -0
  4. package/dist/AppShell/AppShell.styles.d.ts.map +1 -0
  5. package/dist/AppShell/AppShell.types.d.ts +8 -0
  6. package/dist/AppShell/AppShell.types.d.ts.map +1 -0
  7. package/dist/AppShell/index.d.ts +3 -0
  8. package/dist/AppShell/index.d.ts.map +1 -0
  9. package/dist/Button/Button.d.ts +9 -4
  10. package/dist/Button/Button.d.ts.map +1 -1
  11. package/dist/Button/Button.styles.d.ts +33 -26
  12. package/dist/Button/Button.styles.d.ts.map +1 -1
  13. package/dist/Button/Button.types.d.ts +17 -2
  14. package/dist/Button/Button.types.d.ts.map +1 -1
  15. package/dist/ColorScaleSlider/ColorScaleSlider.d.ts +13 -0
  16. package/dist/ColorScaleSlider/ColorScaleSlider.d.ts.map +1 -0
  17. package/dist/ColorScaleSlider/ColorScaleSlider.styles.d.ts +54 -0
  18. package/dist/ColorScaleSlider/ColorScaleSlider.styles.d.ts.map +1 -0
  19. package/dist/ColorScaleSlider/ColorScaleSlider.types.d.ts +25 -0
  20. package/dist/ColorScaleSlider/ColorScaleSlider.types.d.ts.map +1 -0
  21. package/dist/ColorScaleSlider/index.d.ts +3 -0
  22. package/dist/ColorScaleSlider/index.d.ts.map +1 -0
  23. package/dist/Frame/Frame.d.ts +48 -0
  24. package/dist/Frame/Frame.d.ts.map +1 -0
  25. package/dist/Frame/Frame.styles.d.ts +39 -0
  26. package/dist/Frame/Frame.styles.d.ts.map +1 -0
  27. package/dist/Frame/Frame.types.d.ts +115 -0
  28. package/dist/Frame/Frame.types.d.ts.map +1 -0
  29. package/dist/Frame/Frame.utils.d.ts +39 -0
  30. package/dist/Frame/Frame.utils.d.ts.map +1 -0
  31. package/dist/Frame/index.d.ts +4 -0
  32. package/dist/Frame/index.d.ts.map +1 -0
  33. package/dist/HueSlider/HueSlider.d.ts +1 -1
  34. package/dist/HueSlider/HueSlider.d.ts.map +1 -1
  35. package/dist/HueSlider/HueSlider.styles.d.ts +47 -5
  36. package/dist/HueSlider/HueSlider.styles.d.ts.map +1 -1
  37. package/dist/HueSlider/HueSlider.types.d.ts +1 -0
  38. package/dist/HueSlider/HueSlider.types.d.ts.map +1 -1
  39. package/dist/Icon/Icon.d.ts +36 -0
  40. package/dist/Icon/Icon.d.ts.map +1 -0
  41. package/dist/Navbar/Navbar.d.ts +4 -0
  42. package/dist/Navbar/Navbar.d.ts.map +1 -0
  43. package/dist/Navbar/Navbar.styles.d.ts +31 -0
  44. package/dist/Navbar/Navbar.styles.d.ts.map +1 -0
  45. package/dist/Navbar/Navbar.types.d.ts +14 -0
  46. package/dist/Navbar/Navbar.types.d.ts.map +1 -0
  47. package/dist/Navbar/index.d.ts +3 -0
  48. package/dist/Navbar/index.d.ts.map +1 -0
  49. package/dist/Popover/Popover.d.ts +4 -0
  50. package/dist/Popover/Popover.d.ts.map +1 -0
  51. package/dist/Popover/Popover.styles.d.ts +9 -0
  52. package/dist/Popover/Popover.styles.d.ts.map +1 -0
  53. package/dist/Popover/Popover.types.d.ts +37 -0
  54. package/dist/Popover/Popover.types.d.ts.map +1 -0
  55. package/dist/Popover/index.d.ts +4 -0
  56. package/dist/Popover/index.d.ts.map +1 -0
  57. package/dist/Popover/usePopover.d.ts +3 -0
  58. package/dist/Popover/usePopover.d.ts.map +1 -0
  59. package/dist/Select/Select.d.ts +1 -8
  60. package/dist/Select/Select.d.ts.map +1 -1
  61. package/dist/Select/Select.styles.d.ts +32 -5
  62. package/dist/Select/Select.styles.d.ts.map +1 -1
  63. package/dist/Select/Select.types.d.ts +25 -1
  64. package/dist/Select/Select.types.d.ts.map +1 -1
  65. package/dist/Select/SelectOption.d.ts +13 -0
  66. package/dist/Select/SelectOption.d.ts.map +1 -0
  67. package/dist/Select/useSelect.d.ts +15 -0
  68. package/dist/Select/useSelect.d.ts.map +1 -0
  69. package/dist/Sidebar/Sidebar.d.ts +4 -0
  70. package/dist/Sidebar/Sidebar.d.ts.map +1 -0
  71. package/dist/Sidebar/Sidebar.styles.d.ts +31 -0
  72. package/dist/Sidebar/Sidebar.styles.d.ts.map +1 -0
  73. package/dist/Sidebar/Sidebar.types.d.ts +14 -0
  74. package/dist/Sidebar/Sidebar.types.d.ts.map +1 -0
  75. package/dist/Sidebar/index.d.ts +3 -0
  76. package/dist/Sidebar/index.d.ts.map +1 -0
  77. package/dist/Slider/Slider.d.ts +1 -1
  78. package/dist/Slider/Slider.d.ts.map +1 -1
  79. package/dist/Slider/Slider.styles.d.ts +48 -8
  80. package/dist/Slider/Slider.styles.d.ts.map +1 -1
  81. package/dist/Slider/Slider.types.d.ts +1 -0
  82. package/dist/Slider/Slider.types.d.ts.map +1 -1
  83. package/dist/TextInput/TextInput.styles.d.ts +3 -1
  84. package/dist/TextInput/TextInput.styles.d.ts.map +1 -1
  85. package/dist/Toggle/Toggle.styles.d.ts +2 -1
  86. package/dist/Toggle/Toggle.styles.d.ts.map +1 -1
  87. package/dist/fonts/GoogleFontLoader.d.ts +19 -0
  88. package/dist/fonts/GoogleFontLoader.d.ts.map +1 -0
  89. package/dist/fonts/IconFontLoader.d.ts +13 -0
  90. package/dist/fonts/IconFontLoader.d.ts.map +1 -0
  91. package/dist/fonts/buildGoogleFontsUrl.d.ts +17 -0
  92. package/dist/fonts/buildGoogleFontsUrl.d.ts.map +1 -0
  93. package/dist/fonts/googleFonts.d.ts +20 -0
  94. package/dist/fonts/googleFonts.d.ts.map +1 -0
  95. package/dist/index.cjs +2303 -205
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.d.ts +27 -3
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +2279 -200
  100. package/dist/index.js.map +1 -1
  101. package/dist/registry/codegen.d.ts +11 -0
  102. package/dist/registry/codegen.d.ts.map +1 -0
  103. package/dist/registry/index.d.ts +4 -0
  104. package/dist/registry/index.d.ts.map +1 -0
  105. package/dist/registry/registry.d.ts +7 -0
  106. package/dist/registry/registry.d.ts.map +1 -0
  107. package/dist/registry/types.d.ts +32 -0
  108. package/dist/registry/types.d.ts.map +1 -0
  109. package/dist/theme/FrameContext.d.ts +24 -0
  110. package/dist/theme/FrameContext.d.ts.map +1 -0
  111. package/dist/theme/NewtoneProvider.d.ts.map +1 -1
  112. package/dist/theme/defaults.d.ts.map +1 -1
  113. package/dist/theme/types.d.ts +64 -1
  114. package/dist/theme/types.d.ts.map +1 -1
  115. package/dist/tokens/computeTokens.d.ts +55 -3
  116. package/dist/tokens/computeTokens.d.ts.map +1 -1
  117. package/dist/tokens/types.d.ts +52 -0
  118. package/dist/tokens/types.d.ts.map +1 -1
  119. package/dist/tokens/useTokens.d.ts +12 -9
  120. package/dist/tokens/useTokens.d.ts.map +1 -1
  121. package/package.json +1 -1
  122. package/src/AppShell/AppShell.styles.ts +20 -0
  123. package/src/AppShell/AppShell.tsx +17 -0
  124. package/src/AppShell/AppShell.types.ts +8 -0
  125. package/src/AppShell/index.ts +2 -0
  126. package/src/Button/Button.styles.ts +74 -41
  127. package/src/Button/Button.tsx +36 -17
  128. package/src/Button/Button.types.ts +20 -2
  129. package/src/Card/Card.styles.ts +2 -2
  130. package/src/ColorScaleSlider/ColorScaleSlider.styles.ts +60 -0
  131. package/src/ColorScaleSlider/ColorScaleSlider.tsx +156 -0
  132. package/src/ColorScaleSlider/ColorScaleSlider.types.ts +25 -0
  133. package/src/ColorScaleSlider/index.ts +2 -0
  134. package/src/Frame/Frame.styles.ts +213 -0
  135. package/src/Frame/Frame.tsx +242 -0
  136. package/src/Frame/Frame.types.ts +181 -0
  137. package/src/Frame/Frame.utils.ts +189 -0
  138. package/src/Frame/index.ts +21 -0
  139. package/src/HueSlider/HueSlider.styles.ts +58 -39
  140. package/src/HueSlider/HueSlider.tsx +97 -25
  141. package/src/HueSlider/HueSlider.types.ts +1 -0
  142. package/src/Icon/Icon.tsx +76 -0
  143. package/src/Navbar/Navbar.styles.ts +37 -0
  144. package/src/Navbar/Navbar.tsx +32 -0
  145. package/src/Navbar/Navbar.types.ts +14 -0
  146. package/src/Navbar/index.ts +2 -0
  147. package/src/Popover/Popover.styles.ts +39 -0
  148. package/src/Popover/Popover.tsx +103 -0
  149. package/src/Popover/Popover.types.ts +40 -0
  150. package/src/Popover/index.ts +3 -0
  151. package/src/Popover/usePopover.ts +26 -0
  152. package/src/Select/Select.styles.ts +49 -10
  153. package/src/Select/Select.tsx +127 -36
  154. package/src/Select/Select.types.ts +30 -1
  155. package/src/Select/SelectOption.tsx +104 -0
  156. package/src/Select/useSelect.ts +129 -0
  157. package/src/Sidebar/Sidebar.styles.ts +37 -0
  158. package/src/Sidebar/Sidebar.tsx +27 -0
  159. package/src/Sidebar/Sidebar.types.ts +14 -0
  160. package/src/Sidebar/index.ts +2 -0
  161. package/src/Slider/Slider.styles.ts +53 -25
  162. package/src/Slider/Slider.tsx +89 -24
  163. package/src/Slider/Slider.types.ts +1 -0
  164. package/src/TextInput/TextInput.styles.ts +9 -7
  165. package/src/Toggle/Toggle.styles.ts +4 -3
  166. package/src/fonts/GoogleFontLoader.tsx +63 -0
  167. package/src/fonts/IconFontLoader.tsx +49 -0
  168. package/src/fonts/buildGoogleFontsUrl.ts +31 -0
  169. package/src/fonts/googleFonts.ts +87 -0
  170. package/src/index.ts +70 -2
  171. package/src/registry/codegen.ts +132 -0
  172. package/src/registry/index.ts +17 -0
  173. package/src/registry/registry.ts +402 -0
  174. package/src/registry/types.ts +35 -0
  175. package/src/theme/FrameContext.tsx +29 -0
  176. package/src/theme/NewtoneProvider.tsx +9 -1
  177. package/src/theme/defaults.ts +51 -0
  178. package/src/theme/types.ts +66 -1
  179. package/src/tokens/computeTokens.ts +103 -46
  180. package/src/tokens/types.ts +52 -0
  181. package/src/tokens/useTokens.ts +30 -15
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import React7, { createContext, useState, useMemo, useContext } from 'react';
1
+ import React11, { createContext, useState, useMemo, useContext, useRef, useCallback, useEffect } from 'react';
2
2
  import { DEFAULT_NEUTRAL_SATURATION, DEFAULT_NEUTRAL_HUE, DEFAULT_ACCENT_SATURATION, DEFAULT_ACCENT_HUE, DEFAULT_SUCCESS_SATURATION, DEFAULT_SUCCESS_HUE, DEFAULT_WARNING_SATURATION, DEFAULT_WARNING_HUE, DEFAULT_ERROR_SATURATION, DEFAULT_ERROR_HUE, getColor, getColorByContrast, srgbToHex } from 'newtone';
3
- import { Pressable, Text, View, TextInput as TextInput$1, StyleSheet } from 'react-native';
3
+ import { Text, Pressable, View, TextInput as TextInput$1, ScrollView, PanResponder, Animated, StyleSheet } from 'react-native';
4
4
 
5
5
  // src/theme/NewtoneProvider.tsx
6
6
  var DEFAULT_THEME_CONFIG = {
@@ -26,9 +26,145 @@ var DEFAULT_THEME_CONFIG = {
26
26
  elevation: {
27
27
  offsets: [-0.02, 0, 0.04]
28
28
  // [sunken, default, elevated]
29
+ },
30
+ spacing: {
31
+ xs: 4,
32
+ sm: 8,
33
+ md: 12,
34
+ lg: 16,
35
+ xl: 24,
36
+ xxl: 32
37
+ },
38
+ radius: {
39
+ none: 0,
40
+ sm: 4,
41
+ md: 6,
42
+ lg: 8,
43
+ xl: 12,
44
+ pill: 999
45
+ },
46
+ typography: {
47
+ fonts: {
48
+ mono: {
49
+ type: "system",
50
+ family: "ui-monospace",
51
+ fallback: "SFMono-Regular, Menlo, Monaco, Consolas, monospace"
52
+ },
53
+ display: {
54
+ type: "system",
55
+ family: "system-ui",
56
+ fallback: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
57
+ },
58
+ default: {
59
+ type: "system",
60
+ family: "system-ui",
61
+ fallback: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
62
+ }
63
+ },
64
+ scale: {
65
+ xs: 10,
66
+ // 16 / 1.25^2
67
+ sm: 13,
68
+ // 16 / 1.25
69
+ base: 16,
70
+ md: 20,
71
+ // 16 * 1.25
72
+ lg: 25,
73
+ // 16 * 1.25^2
74
+ xl: 31,
75
+ // 16 * 1.25^3
76
+ xxl: 39
77
+ // 16 * 1.25^4
78
+ },
79
+ lineHeight: { tight: 1.25, normal: 1.5, relaxed: 1.75 },
80
+ fontWeight: { regular: 400, medium: 500, semibold: 600, bold: 700 }
81
+ },
82
+ icons: {
83
+ variant: "rounded",
84
+ // Material Design 3 aesthetic
85
+ weight: 400,
86
+ // Normal weight
87
+ autoGrade: true
88
+ // Enable mode-aware grade
29
89
  }
30
90
  };
31
91
 
92
+ // src/fonts/buildGoogleFontsUrl.ts
93
+ function buildGoogleFontsUrl(fonts) {
94
+ const googleFonts = [fonts.mono, fonts.display, fonts.default].filter(
95
+ (f) => f.type === "google"
96
+ );
97
+ if (googleFonts.length === 0) return null;
98
+ const unique = [...new Map(googleFonts.map((f) => [f.family, f])).values()];
99
+ const families = unique.map((f) => {
100
+ const encoded = f.family.replace(/ /g, "+");
101
+ return `family=${encoded}:wght@400;500;600;700`;
102
+ });
103
+ return `https://fonts.googleapis.com/css2?${families.join("&")}&display=swap`;
104
+ }
105
+
106
+ // src/fonts/GoogleFontLoader.tsx
107
+ function GoogleFontLoader({ fonts }) {
108
+ const linkRef = useRef(null);
109
+ useEffect(() => {
110
+ if (typeof document === "undefined") return;
111
+ const url = buildGoogleFontsUrl(fonts);
112
+ if (linkRef.current) {
113
+ linkRef.current.remove();
114
+ linkRef.current = null;
115
+ }
116
+ if (!url) return;
117
+ const links = Array.from(document.head.querySelectorAll('link[rel="stylesheet"]'));
118
+ if (links.some((el) => el.href === url)) return;
119
+ const link = document.createElement("link");
120
+ link.rel = "stylesheet";
121
+ link.href = url;
122
+ document.head.appendChild(link);
123
+ linkRef.current = link;
124
+ return () => {
125
+ if (linkRef.current) {
126
+ linkRef.current.remove();
127
+ linkRef.current = null;
128
+ }
129
+ };
130
+ }, [
131
+ fonts.mono.family,
132
+ fonts.mono.type,
133
+ fonts.display.family,
134
+ fonts.display.type,
135
+ fonts.default.family,
136
+ fonts.default.type
137
+ ]);
138
+ return null;
139
+ }
140
+ function IconFontLoader({ icons }) {
141
+ const linkRef = useRef(null);
142
+ useEffect(() => {
143
+ if (typeof document === "undefined") return;
144
+ const variantName = icons.variant.charAt(0).toUpperCase() + icons.variant.slice(1);
145
+ const family = `Material+Symbols+${variantName}`;
146
+ const url = `https://fonts.googleapis.com/css2?family=${family}:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=block`;
147
+ if (linkRef.current) {
148
+ linkRef.current.remove();
149
+ linkRef.current = null;
150
+ }
151
+ const links = Array.from(document.head.querySelectorAll('link[rel="stylesheet"]'));
152
+ if (links.some((el) => el.href === url)) return;
153
+ const link = document.createElement("link");
154
+ link.rel = "stylesheet";
155
+ link.href = url;
156
+ document.head.appendChild(link);
157
+ linkRef.current = link;
158
+ return () => {
159
+ if (linkRef.current) {
160
+ linkRef.current.remove();
161
+ linkRef.current = null;
162
+ }
163
+ };
164
+ }, [icons.variant]);
165
+ return null;
166
+ }
167
+
32
168
  // src/theme/NewtoneProvider.tsx
33
169
  var ThemeContext = createContext(null);
34
170
  function NewtoneProvider({
@@ -49,7 +185,7 @@ function NewtoneProvider({
49
185
  }),
50
186
  [config, mode, theme]
51
187
  );
52
- return /* @__PURE__ */ React7.createElement(ThemeContext.Provider, { value }, children);
188
+ return /* @__PURE__ */ React11.createElement(ThemeContext.Provider, { value }, /* @__PURE__ */ React11.createElement(GoogleFontLoader, { fonts: config.typography.fonts }), /* @__PURE__ */ React11.createElement(IconFontLoader, { icons: config.icons }), children);
53
189
  }
54
190
  function useNewtoneTheme() {
55
191
  const context = useContext(ThemeContext);
@@ -58,7 +194,15 @@ function useNewtoneTheme() {
58
194
  }
59
195
  return context;
60
196
  }
61
- function computeTokens(config, mode, themeMapping, elevation, elevationOffsets) {
197
+ var FrameContext = createContext(null);
198
+ function useFrameContext() {
199
+ return useContext(FrameContext);
200
+ }
201
+ function fontConfigToFamily(font) {
202
+ const family = font.family.includes(" ") ? `"${font.family}"` : font.family;
203
+ return `${family}, ${font.fallback}`;
204
+ }
205
+ function computeTokens(config, mode, themeMapping, elevation, elevationOffsets, spacing, radius, typography, icons) {
62
206
  const { dynamicRange, palettes } = config;
63
207
  const palette = palettes[themeMapping.paletteIndex];
64
208
  if (!palette) {
@@ -101,7 +245,8 @@ function computeTokens(config, mode, themeMapping, elevation, elevationOffsets)
101
245
  4.5,
102
246
  effectiveTextMode,
103
247
  palette.desaturation,
104
- palette.paletteHueGrading
248
+ palette.paletteHueGrading,
249
+ background
105
250
  );
106
251
  const textSecondary = getColorByContrast(
107
252
  palette.hue,
@@ -110,22 +255,33 @@ function computeTokens(config, mode, themeMapping, elevation, elevationOffsets)
110
255
  3,
111
256
  effectiveTextMode,
112
257
  palette.desaturation,
113
- palette.paletteHueGrading
258
+ palette.paletteHueGrading,
259
+ background
114
260
  );
115
261
  const accentPalette = palettes[1];
116
262
  if (!accentPalette) {
117
263
  throw new Error("Accent palette (index 1) not found");
118
264
  }
119
- const interactive = getColorByContrast(
265
+ const resolveKeyNv = (p) => mode === "dark" ? p.keyNormalizedValueDark : p.keyNormalizedValue;
266
+ const accentKeyNv = resolveKeyNv(accentPalette);
267
+ const interactive = accentKeyNv !== void 0 ? getColor(
268
+ accentPalette.hue,
269
+ accentPalette.saturation,
270
+ dynamicRange,
271
+ accentKeyNv,
272
+ accentPalette.desaturation,
273
+ accentPalette.paletteHueGrading
274
+ ) : getColorByContrast(
120
275
  accentPalette.hue,
121
276
  accentPalette.saturation,
122
277
  dynamicRange,
123
278
  4.5,
124
279
  effectiveTextMode,
125
280
  accentPalette.desaturation,
126
- accentPalette.paletteHueGrading
281
+ accentPalette.paletteHueGrading,
282
+ background
127
283
  );
128
- const interactiveNv = effectiveTextMode === "light" ? 0.3 : 0.7;
284
+ const interactiveNv = accentKeyNv ?? (effectiveTextMode === "light" ? 0.3 : 0.7);
129
285
  const interactiveHover = getColor(
130
286
  accentPalette.hue,
131
287
  accentPalette.saturation,
@@ -154,32 +310,59 @@ function computeTokens(config, mode, themeMapping, elevation, elevationOffsets)
154
310
  const successPalette = palettes[2];
155
311
  const warningPalette = palettes[3];
156
312
  const errorPalette = palettes[4];
157
- const success = successPalette ? getColorByContrast(
313
+ const successKeyNv = successPalette ? resolveKeyNv(successPalette) : void 0;
314
+ const success = successPalette ? successKeyNv !== void 0 ? getColor(
315
+ successPalette.hue,
316
+ successPalette.saturation,
317
+ dynamicRange,
318
+ successKeyNv,
319
+ successPalette.desaturation,
320
+ successPalette.paletteHueGrading
321
+ ) : getColorByContrast(
158
322
  successPalette.hue,
159
323
  successPalette.saturation,
160
324
  dynamicRange,
161
325
  4.5,
162
326
  effectiveTextMode,
163
327
  successPalette.desaturation,
164
- successPalette.paletteHueGrading
328
+ successPalette.paletteHueGrading,
329
+ background
165
330
  ) : interactive;
166
- const warning = warningPalette ? getColorByContrast(
331
+ const warningKeyNv = warningPalette ? resolveKeyNv(warningPalette) : void 0;
332
+ const warning = warningPalette ? warningKeyNv !== void 0 ? getColor(
333
+ warningPalette.hue,
334
+ warningPalette.saturation,
335
+ dynamicRange,
336
+ warningKeyNv,
337
+ warningPalette.desaturation,
338
+ warningPalette.paletteHueGrading
339
+ ) : getColorByContrast(
167
340
  warningPalette.hue,
168
341
  warningPalette.saturation,
169
342
  dynamicRange,
170
343
  4.5,
171
344
  effectiveTextMode,
172
345
  warningPalette.desaturation,
173
- warningPalette.paletteHueGrading
346
+ warningPalette.paletteHueGrading,
347
+ background
174
348
  ) : interactive;
175
- const error = errorPalette ? getColorByContrast(
349
+ const errorKeyNv = errorPalette ? resolveKeyNv(errorPalette) : void 0;
350
+ const error = errorPalette ? errorKeyNv !== void 0 ? getColor(
351
+ errorPalette.hue,
352
+ errorPalette.saturation,
353
+ dynamicRange,
354
+ errorKeyNv,
355
+ errorPalette.desaturation,
356
+ errorPalette.paletteHueGrading
357
+ ) : getColorByContrast(
176
358
  errorPalette.hue,
177
359
  errorPalette.saturation,
178
360
  dynamicRange,
179
361
  4.5,
180
362
  effectiveTextMode,
181
363
  errorPalette.desaturation,
182
- errorPalette.paletteHueGrading
364
+ errorPalette.paletteHueGrading,
365
+ background
183
366
  ) : interactive;
184
367
  return {
185
368
  background,
@@ -193,38 +376,86 @@ function computeTokens(config, mode, themeMapping, elevation, elevationOffsets)
193
376
  border,
194
377
  success,
195
378
  warning,
196
- error
379
+ error,
380
+ spacing,
381
+ radius,
382
+ typography: {
383
+ fonts: {
384
+ mono: fontConfigToFamily(typography.fonts.mono),
385
+ display: fontConfigToFamily(typography.fonts.display),
386
+ default: fontConfigToFamily(typography.fonts.default)
387
+ },
388
+ size: typography.scale,
389
+ lineHeight: typography.lineHeight,
390
+ weight: typography.fontWeight
391
+ },
392
+ icons: {
393
+ variant: icons.variant,
394
+ weight: icons.weight,
395
+ grade: icons.autoGrade ? mode === "light" ? -25 : 200 : 0
396
+ }
197
397
  };
198
398
  }
199
399
 
200
400
  // src/tokens/useTokens.ts
201
- function useTokens(elevation = 1) {
202
- const { config, mode, theme } = useNewtoneTheme();
401
+ function useTokens(elevation) {
402
+ const { config, mode, theme: providerTheme } = useNewtoneTheme();
403
+ const frameCtx = useFrameContext();
404
+ const resolvedTheme = frameCtx?.theme ?? providerTheme;
405
+ const resolvedElevation = elevation ?? frameCtx?.elevation ?? 1;
203
406
  return useMemo(() => {
204
- const themeMapping = config.themes[theme];
407
+ const themeMapping = config.themes[resolvedTheme];
205
408
  return computeTokens(
206
409
  config.colorSystem,
207
410
  mode,
208
411
  themeMapping,
209
- elevation,
210
- config.elevation.offsets
412
+ resolvedElevation,
413
+ config.elevation.offsets,
414
+ config.spacing,
415
+ config.radius,
416
+ config.typography,
417
+ config.icons
211
418
  );
212
- }, [config, mode, theme, elevation]);
419
+ }, [config, mode, resolvedTheme, resolvedElevation]);
213
420
  }
214
- var SIZE_CONFIG = {
215
- sm: { paddingVertical: 6, paddingHorizontal: 12, fontSize: 12, borderRadius: 4 },
216
- md: { paddingVertical: 10, paddingHorizontal: 20, fontSize: 14, borderRadius: 6 },
217
- lg: { paddingVertical: 14, paddingHorizontal: 28, fontSize: 16, borderRadius: 8 }
218
- };
219
- function getButtonStyles(tokens, variant, size, disabled) {
220
- const sizeConfig = SIZE_CONFIG[size];
421
+ function getSizeConfig(tokens) {
422
+ return {
423
+ sm: {
424
+ paddingVertical: tokens.spacing.xs,
425
+ paddingHorizontal: tokens.spacing.md,
426
+ fontSize: tokens.typography.size.sm,
427
+ borderRadius: tokens.radius.sm,
428
+ gap: tokens.spacing.xs,
429
+ iconSize: tokens.typography.size.sm
430
+ },
431
+ md: {
432
+ paddingVertical: tokens.spacing.sm,
433
+ paddingHorizontal: tokens.spacing.lg,
434
+ fontSize: tokens.typography.size.base,
435
+ borderRadius: tokens.radius.md,
436
+ gap: tokens.spacing.sm,
437
+ iconSize: tokens.typography.size.base
438
+ },
439
+ lg: {
440
+ paddingVertical: tokens.spacing.md,
441
+ paddingHorizontal: tokens.spacing.xl,
442
+ fontSize: tokens.typography.size.base,
443
+ borderRadius: tokens.radius.lg,
444
+ gap: tokens.spacing.sm,
445
+ iconSize: tokens.typography.size.base
446
+ }
447
+ };
448
+ }
449
+ function getButtonStyles(tokens, variant, size, disabled, isIconOnly) {
450
+ const sizeConfig = getSizeConfig(tokens)[size];
221
451
  const base = {
222
452
  paddingVertical: sizeConfig.paddingVertical,
223
- paddingHorizontal: sizeConfig.paddingHorizontal,
453
+ paddingHorizontal: isIconOnly ? sizeConfig.paddingVertical : sizeConfig.paddingHorizontal,
224
454
  borderRadius: sizeConfig.borderRadius,
225
455
  alignItems: "center",
226
456
  justifyContent: "center",
227
- flexDirection: "row"
457
+ flexDirection: "row",
458
+ gap: sizeConfig.gap
228
459
  };
229
460
  let backgroundColor;
230
461
  let textColor;
@@ -250,37 +481,77 @@ function getButtonStyles(tokens, variant, size, disabled) {
250
481
  textColor = srgbToHex(tokens.interactive.srgb);
251
482
  break;
252
483
  }
253
- return StyleSheet.create({
254
- base: {
255
- ...base,
256
- backgroundColor: disabled ? srgbToHex(tokens.backgroundSunken.srgb) : backgroundColor,
257
- borderWidth,
258
- ...borderColor && { borderColor }
259
- },
260
- pressed: {
261
- backgroundColor: variant === "primary" ? srgbToHex(tokens.interactiveActive.srgb) : variant === "secondary" ? srgbToHex(tokens.backgroundSunken.srgb) : "transparent",
262
- opacity: variant === "ghost" || variant === "outline" ? 0.7 : 1
263
- },
264
- disabled: {
265
- opacity: 0.4
266
- },
267
- text: {
268
- fontSize: sizeConfig.fontSize,
269
- fontWeight: "600",
270
- color: disabled ? srgbToHex(tokens.textSecondary.srgb) : textColor
271
- },
272
- textPressed: {
273
- // Color changes handled by parent opacity
484
+ const resolvedTextColor = disabled ? srgbToHex(tokens.textSecondary.srgb) : textColor;
485
+ return {
486
+ iconColor: resolvedTextColor,
487
+ iconSize: sizeConfig.iconSize,
488
+ styles: StyleSheet.create({
489
+ base: {
490
+ ...base,
491
+ backgroundColor: disabled ? srgbToHex(tokens.backgroundSunken.srgb) : backgroundColor,
492
+ borderWidth,
493
+ ...borderColor && { borderColor }
494
+ },
495
+ pressed: {
496
+ backgroundColor: variant === "primary" ? srgbToHex(tokens.interactiveActive.srgb) : variant === "secondary" ? srgbToHex(tokens.backgroundSunken.srgb) : "transparent",
497
+ opacity: variant === "ghost" || variant === "outline" ? 0.7 : 1
498
+ },
499
+ disabled: {
500
+ opacity: 0.4
501
+ },
502
+ text: {
503
+ fontFamily: tokens.typography.fonts.default,
504
+ fontSize: sizeConfig.fontSize,
505
+ fontWeight: tokens.typography.weight.semibold,
506
+ color: resolvedTextColor
507
+ },
508
+ textPressed: {
509
+ // Color changes handled by parent opacity
510
+ },
511
+ textDisabled: {
512
+ // Color already set in text style via disabled check
513
+ }
514
+ })
515
+ };
516
+ }
517
+ function Icon({
518
+ name,
519
+ size,
520
+ opticalSize,
521
+ fill = 0,
522
+ color,
523
+ elevation = 1,
524
+ style,
525
+ onPress
526
+ }) {
527
+ const tokens = useTokens(elevation);
528
+ const fontSize = size ?? tokens.typography.size.base;
529
+ const opsz = opticalSize ?? fontSize;
530
+ const iconColor = color ?? srgbToHex(tokens.textPrimary.srgb);
531
+ const fontFamily = `Material Symbols ${tokens.icons.variant.charAt(0).toUpperCase() + tokens.icons.variant.slice(1)}`;
532
+ const fontVariationSettings = `'FILL' ${fill}, 'wght' ${tokens.icons.weight}, 'GRAD' ${tokens.icons.grade}, 'opsz' ${opsz}`;
533
+ return /* @__PURE__ */ React11.createElement(
534
+ Text,
535
+ {
536
+ style: {
537
+ fontFamily,
538
+ fontSize,
539
+ color: iconColor,
540
+ userSelect: "none",
541
+ fontVariationSettings,
542
+ ...style
543
+ },
544
+ onPress
274
545
  },
275
- textDisabled: {
276
- // Color already set in text style via disabled check
277
- }
278
- });
546
+ name
547
+ );
279
548
  }
280
549
 
281
550
  // src/Button/Button.tsx
282
551
  function Button({
283
552
  children,
553
+ icon,
554
+ iconPosition = "left",
284
555
  variant = "primary",
285
556
  size = "md",
286
557
  disabled = false,
@@ -289,11 +560,13 @@ function Button({
289
560
  ...pressableProps
290
561
  }) {
291
562
  const tokens = useTokens(1);
292
- const styles = React7.useMemo(
293
- () => getButtonStyles(tokens, variant, size, disabled),
294
- [tokens, variant, size, disabled]
563
+ const isIconOnly = !!icon && !children;
564
+ const { styles, iconColor, iconSize } = React11.useMemo(
565
+ () => getButtonStyles(tokens, variant, size, disabled, isIconOnly),
566
+ [tokens, variant, size, disabled, isIconOnly]
295
567
  );
296
- return /* @__PURE__ */ React7.createElement(
568
+ const renderIcon = () => /* @__PURE__ */ React11.createElement(Icon, { name: icon, size: iconSize, color: iconColor });
569
+ return /* @__PURE__ */ React11.createElement(
297
570
  Pressable,
298
571
  {
299
572
  style: ({ pressed }) => [
@@ -305,7 +578,7 @@ function Button({
305
578
  disabled,
306
579
  ...pressableProps
307
580
  },
308
- ({ pressed }) => /* @__PURE__ */ React7.createElement(
581
+ ({ pressed }) => /* @__PURE__ */ React11.createElement(React11.Fragment, null, icon && iconPosition === "left" && renderIcon(), children != null && /* @__PURE__ */ React11.createElement(
309
582
  Text,
310
583
  {
311
584
  style: [
@@ -316,7 +589,7 @@ function Button({
316
589
  ]
317
590
  },
318
591
  children
319
- )
592
+ ), icon && iconPosition === "right" && renderIcon())
320
593
  );
321
594
  }
322
595
  function getCardStyles(tokens, disabled) {
@@ -325,8 +598,8 @@ function getCardStyles(tokens, disabled) {
325
598
  backgroundColor: srgbToHex(tokens.background.srgb),
326
599
  borderWidth: 1,
327
600
  borderColor: srgbToHex(tokens.border.srgb),
328
- borderRadius: 8,
329
- padding: 16,
601
+ borderRadius: tokens.radius.lg,
602
+ padding: tokens.spacing.lg,
330
603
  opacity: disabled ? 0.5 : 1
331
604
  }
332
605
  });
@@ -340,30 +613,391 @@ function Card({
340
613
  disabled = false
341
614
  }) {
342
615
  const tokens = useTokens(elevation);
343
- const styles = React7.useMemo(
616
+ const styles = React11.useMemo(
344
617
  () => getCardStyles(tokens, disabled),
345
618
  [tokens, disabled]
346
619
  );
347
- return /* @__PURE__ */ React7.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, children);
620
+ return /* @__PURE__ */ React11.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, children);
621
+ }
622
+
623
+ // src/Frame/Frame.utils.ts
624
+ function resolveSpacing(value, tokens) {
625
+ if (typeof value === "number") return value;
626
+ return tokens.spacing[value];
627
+ }
628
+ function resolvePadding(prop, tokens) {
629
+ if (typeof prop === "string" || typeof prop === "number") {
630
+ const px = resolveSpacing(prop, tokens);
631
+ return { top: px, right: px, bottom: px, left: px };
632
+ }
633
+ if ("x" in prop || "y" in prop) {
634
+ const axes = prop;
635
+ const x = axes.x !== void 0 ? resolveSpacing(axes.x, tokens) : 0;
636
+ const y = axes.y !== void 0 ? resolveSpacing(axes.y, tokens) : 0;
637
+ return { top: y, right: x, bottom: y, left: x };
638
+ }
639
+ const sides = prop;
640
+ return {
641
+ top: sides.top !== void 0 ? resolveSpacing(sides.top, tokens) : 0,
642
+ right: sides.right !== void 0 ? resolveSpacing(sides.right, tokens) : 0,
643
+ bottom: sides.bottom !== void 0 ? resolveSpacing(sides.bottom, tokens) : 0,
644
+ left: sides.left !== void 0 ? resolveSpacing(sides.left, tokens) : 0
645
+ };
646
+ }
647
+ function resolveGap(prop, tokens) {
648
+ if (typeof prop === "string" || typeof prop === "number") {
649
+ const px = resolveSpacing(prop, tokens);
650
+ return { rowGap: px, columnGap: px };
651
+ }
652
+ return {
653
+ rowGap: prop.row !== void 0 ? resolveSpacing(prop.row, tokens) : 0,
654
+ columnGap: prop.column !== void 0 ? resolveSpacing(prop.column, tokens) : 0
655
+ };
656
+ }
657
+ function resolveRadius(value, tokens) {
658
+ if (typeof value === "number") return value;
659
+ return tokens.radius[value];
660
+ }
661
+ function resolveRadiusCorners(prop, tokens) {
662
+ if (typeof prop === "string" || typeof prop === "number") {
663
+ const px = resolveRadius(prop, tokens);
664
+ return { topLeft: px, topRight: px, bottomLeft: px, bottomRight: px };
665
+ }
666
+ return {
667
+ topLeft: prop.topLeft !== void 0 ? resolveRadius(prop.topLeft, tokens) : 0,
668
+ topRight: prop.topRight !== void 0 ? resolveRadius(prop.topRight, tokens) : 0,
669
+ bottomLeft: prop.bottomLeft !== void 0 ? resolveRadius(prop.bottomLeft, tokens) : 0,
670
+ bottomRight: prop.bottomRight !== void 0 ? resolveRadius(prop.bottomRight, tokens) : 0
671
+ };
672
+ }
673
+ function hasPositiveRadius(corners) {
674
+ return corners.topLeft > 0 || corners.topRight > 0 || corners.bottomLeft > 0 || corners.bottomRight > 0;
675
+ }
676
+ function resolveSizing(width, height) {
677
+ const style = {};
678
+ if (width !== void 0) {
679
+ if (width === "fill") {
680
+ style.flexGrow = 1;
681
+ style.width = "100%";
682
+ } else if (typeof width === "number") {
683
+ style.width = width;
684
+ }
685
+ }
686
+ if (height !== void 0) {
687
+ if (height === "fill") {
688
+ style.flexGrow = style.flexGrow === 1 ? 1 : 1;
689
+ style.height = "100%";
690
+ } else if (typeof height === "number") {
691
+ style.height = height;
692
+ }
693
+ }
694
+ return style;
695
+ }
696
+ var ALIGN_MAP = {
697
+ start: "flex-start",
698
+ center: "center",
699
+ end: "flex-end",
700
+ stretch: "stretch",
701
+ baseline: "baseline"
702
+ };
703
+ var JUSTIFY_MAP = {
704
+ start: "flex-start",
705
+ center: "center",
706
+ end: "flex-end",
707
+ between: "space-between",
708
+ around: "space-around",
709
+ evenly: "space-evenly"
710
+ };
711
+ function resolveAlignment(align) {
712
+ return ALIGN_MAP[align];
713
+ }
714
+ function resolveJustification(justify) {
715
+ return JUSTIFY_MAP[justify];
716
+ }
717
+ function resolveFlexDirection(direction, reverse) {
718
+ if (direction === "horizontal") {
719
+ return reverse ? "row-reverse" : "row";
720
+ }
721
+ return reverse ? "column-reverse" : "column";
722
+ }
723
+
724
+ // src/Frame/Frame.styles.ts
725
+ function getFrameStyles(input) {
726
+ const {
727
+ tokens,
728
+ frameElevation,
729
+ layout = "flex",
730
+ direction = "vertical",
731
+ wrap = false,
732
+ reverse = false,
733
+ columns,
734
+ rows,
735
+ align,
736
+ justify,
737
+ padding,
738
+ gap,
739
+ width,
740
+ height,
741
+ minWidth,
742
+ maxWidth,
743
+ minHeight,
744
+ maxHeight,
745
+ radius,
746
+ bordered = false,
747
+ disabled = false
748
+ } = input;
749
+ const container = {};
750
+ container.backgroundColor = srgbToHex(tokens.background.srgb);
751
+ container.color = srgbToHex(tokens.textPrimary.srgb);
752
+ if (layout === "flex") {
753
+ container.display = "flex";
754
+ container.flexDirection = resolveFlexDirection(direction, reverse);
755
+ if (wrap) container.flexWrap = "wrap";
756
+ }
757
+ if (layout === "grid") {
758
+ container.display = "flex";
759
+ container.flexDirection = "row";
760
+ container.flexWrap = "wrap";
761
+ }
762
+ if (align) container.alignItems = resolveAlignment(align);
763
+ if (justify) container.justifyContent = resolveJustification(justify);
764
+ if (padding !== void 0) {
765
+ const p = resolvePadding(padding, tokens);
766
+ container.paddingTop = p.top;
767
+ container.paddingRight = p.right;
768
+ container.paddingBottom = p.bottom;
769
+ container.paddingLeft = p.left;
770
+ }
771
+ if (gap !== void 0) {
772
+ const g = resolveGap(gap, tokens);
773
+ container.rowGap = g.rowGap;
774
+ container.columnGap = g.columnGap;
775
+ }
776
+ const sizing = resolveSizing(width, height);
777
+ Object.assign(container, sizing);
778
+ if (minWidth !== void 0) container.minWidth = minWidth;
779
+ if (maxWidth !== void 0) container.maxWidth = maxWidth;
780
+ if (minHeight !== void 0) container.minHeight = minHeight;
781
+ if (maxHeight !== void 0) container.maxHeight = maxHeight;
782
+ if (radius !== void 0) {
783
+ const corners = resolveRadiusCorners(radius, tokens);
784
+ container.borderTopLeftRadius = corners.topLeft;
785
+ container.borderTopRightRadius = corners.topRight;
786
+ container.borderBottomLeftRadius = corners.bottomLeft;
787
+ container.borderBottomRightRadius = corners.bottomRight;
788
+ if (hasPositiveRadius(corners)) {
789
+ container.overflow = "hidden";
790
+ }
791
+ }
792
+ if (bordered) {
793
+ container.borderWidth = 1;
794
+ container.borderColor = srgbToHex(tokens.border.srgb);
795
+ }
796
+ if (frameElevation === 2) {
797
+ container.shadowColor = "#000";
798
+ container.shadowOffset = { width: 0, height: 2 };
799
+ container.shadowOpacity = 0.12;
800
+ container.shadowRadius = 6;
801
+ container.elevation = 4;
802
+ }
803
+ if (disabled) {
804
+ container.opacity = 0.5;
805
+ }
806
+ const pressed = StyleSheet.create({
807
+ s: { backgroundColor: srgbToHex(tokens.backgroundSunken.srgb) }
808
+ }).s;
809
+ let gridWebStyle = null;
810
+ if (layout === "grid") {
811
+ gridWebStyle = {
812
+ display: "grid",
813
+ gridTemplateColumns: columns ? `repeat(${columns}, 1fr)` : void 0,
814
+ gridTemplateRows: rows ? `repeat(${rows}, 1fr)` : void 0
815
+ };
816
+ }
817
+ const insetBoxShadow = frameElevation === -2 ? "inset 0 2px 4px rgba(0,0,0,0.12)" : null;
818
+ return {
819
+ container: StyleSheet.create({ c: container }).c,
820
+ pressed,
821
+ gridWebStyle,
822
+ insetBoxShadow
823
+ };
824
+ }
825
+
826
+ // src/Frame/Frame.tsx
827
+ function wrapTextChildren(children, textStyle) {
828
+ return React11.Children.map(children, (child) => {
829
+ if (typeof child === "string" || typeof child === "number") {
830
+ return /* @__PURE__ */ React11.createElement(Text, { style: textStyle }, child);
831
+ }
832
+ return child;
833
+ });
834
+ }
835
+ function toElevationLevel(frameElevation) {
836
+ if (frameElevation <= -1) return 0;
837
+ if (frameElevation === 0) return 1;
838
+ return 2;
839
+ }
840
+ function Frame({
841
+ children,
842
+ // Theme & elevation
843
+ theme,
844
+ elevation,
845
+ // Layout
846
+ layout,
847
+ direction,
848
+ wrap,
849
+ reverse,
850
+ columns,
851
+ rows,
852
+ // Alignment
853
+ align,
854
+ justify,
855
+ // Spacing
856
+ padding,
857
+ gap,
858
+ // Sizing
859
+ width,
860
+ height,
861
+ minWidth,
862
+ maxWidth,
863
+ minHeight,
864
+ maxHeight,
865
+ // Appearance
866
+ radius,
867
+ bordered,
868
+ // Interactivity
869
+ onPress,
870
+ href,
871
+ disabled = false,
872
+ // Style override
873
+ style
874
+ }) {
875
+ const { config, mode, theme: providerTheme } = useNewtoneTheme();
876
+ const parentFrameCtx = useFrameContext();
877
+ const resolvedTheme = theme ?? parentFrameCtx?.theme ?? providerTheme;
878
+ const resolvedFrameElevation = elevation ?? 0;
879
+ const resolvedElevation = elevation !== void 0 ? toElevationLevel(elevation) : parentFrameCtx?.elevation ?? 1;
880
+ const tokens = useMemo(() => {
881
+ const themeMapping = config.themes[resolvedTheme];
882
+ return computeTokens(
883
+ config.colorSystem,
884
+ mode,
885
+ themeMapping,
886
+ resolvedElevation,
887
+ config.elevation.offsets,
888
+ config.spacing,
889
+ config.radius,
890
+ config.typography,
891
+ config.icons
892
+ );
893
+ }, [config, mode, resolvedTheme, resolvedElevation]);
894
+ const styles = useMemo(
895
+ () => getFrameStyles({
896
+ tokens,
897
+ frameElevation: resolvedFrameElevation,
898
+ layout,
899
+ direction,
900
+ wrap,
901
+ reverse,
902
+ columns,
903
+ rows,
904
+ align,
905
+ justify,
906
+ padding,
907
+ gap,
908
+ width,
909
+ height,
910
+ minWidth,
911
+ maxWidth,
912
+ minHeight,
913
+ maxHeight,
914
+ radius,
915
+ bordered,
916
+ disabled
917
+ }),
918
+ [
919
+ tokens,
920
+ resolvedFrameElevation,
921
+ layout,
922
+ direction,
923
+ wrap,
924
+ reverse,
925
+ columns,
926
+ rows,
927
+ align,
928
+ justify,
929
+ padding,
930
+ gap,
931
+ width,
932
+ height,
933
+ minWidth,
934
+ maxWidth,
935
+ minHeight,
936
+ maxHeight,
937
+ radius,
938
+ bordered,
939
+ disabled
940
+ ]
941
+ );
942
+ const contextValue = useMemo(
943
+ () => ({ theme: resolvedTheme, elevation: resolvedElevation }),
944
+ [resolvedTheme, resolvedElevation]
945
+ );
946
+ const webOverrides = [];
947
+ if (styles.gridWebStyle) {
948
+ webOverrides.push(styles.gridWebStyle);
949
+ }
950
+ if (styles.insetBoxShadow) {
951
+ webOverrides.push({ boxShadow: styles.insetBoxShadow });
952
+ }
953
+ const userStyles = Array.isArray(style) ? style : style ? [style] : [];
954
+ const isInteractive = onPress !== void 0 || href !== void 0;
955
+ const textStyle = useMemo(
956
+ () => ({
957
+ color: srgbToHex(tokens.textPrimary.srgb),
958
+ fontSize: tokens.typography.size.base,
959
+ fontFamily: tokens.typography.fonts.default,
960
+ lineHeight: tokens.typography.size.base * tokens.typography.lineHeight.normal
961
+ }),
962
+ [tokens]
963
+ );
964
+ const wrappedChildren = wrapTextChildren(children, textStyle);
965
+ return /* @__PURE__ */ React11.createElement(FrameContext.Provider, { value: contextValue }, isInteractive ? /* @__PURE__ */ React11.createElement(
966
+ Pressable,
967
+ {
968
+ onPress,
969
+ disabled,
970
+ ...href ? { href, accessibilityRole: "link" } : { accessibilityRole: "button" },
971
+ style: ({ pressed }) => [
972
+ styles.container,
973
+ pressed && !disabled && styles.pressed,
974
+ ...webOverrides,
975
+ ...userStyles
976
+ ]
977
+ },
978
+ wrappedChildren
979
+ ) : /* @__PURE__ */ React11.createElement(View, { style: [styles.container, ...webOverrides, ...userStyles] }, wrappedChildren));
348
980
  }
349
981
  function getTextInputStyles(tokens, disabled) {
350
982
  return StyleSheet.create({
351
983
  container: {
352
- gap: 4
984
+ gap: tokens.spacing.xs
353
985
  },
354
986
  label: {
355
- fontSize: 12,
356
- fontWeight: "600",
987
+ fontFamily: tokens.typography.fonts.default,
988
+ fontSize: tokens.typography.size.sm,
989
+ fontWeight: tokens.typography.weight.semibold,
357
990
  color: srgbToHex(tokens.textSecondary.srgb)
358
991
  },
359
992
  input: {
993
+ fontFamily: tokens.typography.fonts.default,
360
994
  backgroundColor: srgbToHex(tokens.backgroundSunken.srgb),
361
995
  borderWidth: 1,
362
996
  borderColor: srgbToHex(tokens.border.srgb),
363
- borderRadius: 6,
364
- paddingVertical: 8,
365
- paddingHorizontal: 12,
366
- fontSize: 14,
997
+ borderRadius: tokens.radius.md,
998
+ paddingVertical: tokens.spacing.sm,
999
+ paddingHorizontal: tokens.spacing.md,
1000
+ fontSize: tokens.typography.size.base,
367
1001
  color: disabled ? srgbToHex(tokens.textSecondary.srgb) : srgbToHex(tokens.textPrimary.srgb),
368
1002
  opacity: disabled ? 0.5 : 1
369
1003
  }
@@ -376,11 +1010,11 @@ function TextInput({
376
1010
  ...textInputProps
377
1011
  }) {
378
1012
  const tokens = useTokens(1);
379
- const styles = React7.useMemo(
1013
+ const styles = React11.useMemo(
380
1014
  () => getTextInputStyles(tokens, disabled),
381
1015
  [tokens, disabled]
382
1016
  );
383
- return /* @__PURE__ */ React7.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, label && /* @__PURE__ */ React7.createElement(Text, { style: styles.label }, label), /* @__PURE__ */ React7.createElement(
1017
+ return /* @__PURE__ */ React11.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, label && /* @__PURE__ */ React11.createElement(Text, { style: styles.label }, label), /* @__PURE__ */ React11.createElement(
384
1018
  TextInput$1,
385
1019
  {
386
1020
  style: styles.input,
@@ -390,64 +1024,486 @@ function TextInput({
390
1024
  }
391
1025
  ));
392
1026
  }
393
- function getSelectStyles(tokens, disabled) {
1027
+ function getPopoverStyles(tokens, triggerHeight, offset, maxHeight, width, isOpen) {
1028
+ const widthStyle = width === "trigger" ? { left: 0, right: 0 } : typeof width === "number" ? { width, left: 0 } : { left: 0 };
1029
+ return StyleSheet.create({
1030
+ container: {
1031
+ position: "relative",
1032
+ zIndex: isOpen ? 999 : 1
1033
+ },
1034
+ content: {
1035
+ position: "absolute",
1036
+ top: triggerHeight + offset,
1037
+ ...widthStyle,
1038
+ backgroundColor: srgbToHex(tokens.backgroundElevated.srgb),
1039
+ borderWidth: 1,
1040
+ borderColor: srgbToHex(tokens.border.srgb),
1041
+ borderRadius: tokens.radius.md,
1042
+ maxHeight,
1043
+ zIndex: 1e3,
1044
+ overflow: "hidden",
1045
+ ...{ boxShadow: "0 4px 12px rgba(0,0,0,0.15)" }
1046
+ }
1047
+ });
1048
+ }
1049
+
1050
+ // src/Popover/Popover.tsx
1051
+ var openPopovers = /* @__PURE__ */ new Set();
1052
+ function Popover({
1053
+ isOpen,
1054
+ onClose,
1055
+ trigger,
1056
+ children,
1057
+ width = "trigger",
1058
+ maxHeight = 240,
1059
+ offset = 4,
1060
+ closeOnEscape = true,
1061
+ style,
1062
+ contentStyle
1063
+ }) {
1064
+ const tokens = useTokens(1);
1065
+ const containerRef = useRef(null);
1066
+ const [triggerHeight, setTriggerHeight] = useState(0);
1067
+ const onTriggerLayout = useCallback(
1068
+ (e) => {
1069
+ setTriggerHeight(e.nativeEvent.layout.height);
1070
+ },
1071
+ []
1072
+ );
1073
+ useEffect(() => {
1074
+ if (!isOpen) return;
1075
+ openPopovers.forEach((closeFn) => closeFn());
1076
+ openPopovers.clear();
1077
+ openPopovers.add(onClose);
1078
+ return () => {
1079
+ openPopovers.delete(onClose);
1080
+ };
1081
+ }, [isOpen, onClose]);
1082
+ useEffect(() => {
1083
+ if (!isOpen) return;
1084
+ const handleMouseDown = (e) => {
1085
+ const node = containerRef.current;
1086
+ if (node && !node.contains(e.target)) {
1087
+ onClose();
1088
+ }
1089
+ };
1090
+ document.addEventListener("mousedown", handleMouseDown, true);
1091
+ return () => document.removeEventListener("mousedown", handleMouseDown, true);
1092
+ }, [isOpen, onClose]);
1093
+ const handleKeyDown = useCallback(
1094
+ (e) => {
1095
+ if (closeOnEscape && e.key === "Escape") {
1096
+ e.stopPropagation();
1097
+ onClose();
1098
+ }
1099
+ },
1100
+ [closeOnEscape, onClose]
1101
+ );
1102
+ const styles = useMemo(
1103
+ () => getPopoverStyles(tokens, triggerHeight, offset, maxHeight, width, isOpen),
1104
+ [tokens, triggerHeight, offset, maxHeight, width, isOpen]
1105
+ );
1106
+ const containerStyles = useMemo(
1107
+ () => [styles.container, ...Array.isArray(style) ? style : style ? [style] : []],
1108
+ [styles.container, style]
1109
+ );
1110
+ const mergedContentStyles = useMemo(
1111
+ () => [styles.content, ...Array.isArray(contentStyle) ? contentStyle : contentStyle ? [contentStyle] : []],
1112
+ [styles.content, contentStyle]
1113
+ );
1114
+ const webProps = { onKeyDown: handleKeyDown };
1115
+ return /* @__PURE__ */ React11.createElement(
1116
+ View,
1117
+ {
1118
+ ref: containerRef,
1119
+ style: containerStyles,
1120
+ ...webProps
1121
+ },
1122
+ /* @__PURE__ */ React11.createElement(View, { onLayout: onTriggerLayout }, trigger),
1123
+ isOpen && /* @__PURE__ */ React11.createElement(View, { style: mergedContentStyles }, children)
1124
+ );
1125
+ }
1126
+ function usePopover(options) {
1127
+ const [isOpen, setIsOpen] = useState(options?.initialOpen ?? false);
1128
+ const open = useCallback(() => {
1129
+ setIsOpen(true);
1130
+ options?.onOpenChange?.(true);
1131
+ }, [options]);
1132
+ const close = useCallback(() => {
1133
+ setIsOpen(false);
1134
+ options?.onOpenChange?.(false);
1135
+ }, [options]);
1136
+ const toggle = useCallback(() => {
1137
+ setIsOpen((prev) => {
1138
+ const next = !prev;
1139
+ options?.onOpenChange?.(next);
1140
+ return next;
1141
+ });
1142
+ }, [options]);
1143
+ return { isOpen, open, close, toggle };
1144
+ }
1145
+
1146
+ // src/Select/Select.types.ts
1147
+ function isOptionGroup(item) {
1148
+ return "options" in item;
1149
+ }
1150
+ function getSelectStyles(tokens, disabled, size, isOpen) {
1151
+ const isSm = size === "sm";
1152
+ const fontSize = isSm ? tokens.typography.size.sm : tokens.typography.size.base;
1153
+ const iconSize = fontSize + 2;
1154
+ const iconSpace = iconSize + tokens.spacing.sm;
1155
+ const paddingVertical = isSm ? tokens.spacing.xs : tokens.spacing.sm;
1156
+ const paddingHorizontal = isSm ? tokens.spacing.sm : tokens.spacing.md;
394
1157
  return StyleSheet.create({
395
1158
  container: {
396
- gap: 4
1159
+ gap: tokens.spacing.xs,
1160
+ zIndex: isOpen ? 999 : 0
397
1161
  },
398
1162
  label: {
399
- fontSize: 12,
400
- fontWeight: "600",
1163
+ fontFamily: tokens.typography.fonts.default,
1164
+ fontSize: tokens.typography.size.sm,
1165
+ fontWeight: tokens.typography.weight.semibold,
401
1166
  color: srgbToHex(tokens.textSecondary.srgb)
402
1167
  },
403
- select: {
1168
+ trigger: {
1169
+ flexDirection: "row",
1170
+ alignItems: "center",
404
1171
  backgroundColor: srgbToHex(tokens.backgroundSunken.srgb),
405
1172
  borderWidth: 1,
406
1173
  borderColor: srgbToHex(tokens.border.srgb),
407
- borderRadius: 6,
408
- paddingVertical: 8,
409
- paddingHorizontal: 12,
410
- fontSize: 14,
411
- color: disabled ? srgbToHex(tokens.textSecondary.srgb) : srgbToHex(tokens.textPrimary.srgb),
1174
+ borderRadius: tokens.radius.md,
1175
+ paddingVertical,
1176
+ paddingLeft: paddingHorizontal,
1177
+ paddingRight: iconSpace + (isSm ? tokens.spacing.xs : tokens.spacing.sm),
412
1178
  opacity: disabled ? 0.5 : 1
1179
+ },
1180
+ triggerText: {
1181
+ flex: 1,
1182
+ fontFamily: tokens.typography.fonts.default,
1183
+ fontSize,
1184
+ color: disabled ? srgbToHex(tokens.textSecondary.srgb) : srgbToHex(tokens.textPrimary.srgb)
1185
+ },
1186
+ iconWrapper: {
1187
+ position: "absolute",
1188
+ right: isSm ? tokens.spacing.xs : tokens.spacing.sm,
1189
+ top: 0,
1190
+ bottom: 0,
1191
+ justifyContent: "center"
1192
+ },
1193
+ groupLabel: {
1194
+ fontFamily: tokens.typography.fonts.default,
1195
+ fontSize: tokens.typography.size.xs,
1196
+ fontWeight: tokens.typography.weight.semibold,
1197
+ color: srgbToHex(tokens.textSecondary.srgb),
1198
+ textTransform: "uppercase",
1199
+ letterSpacing: 0.5,
1200
+ paddingVertical: tokens.spacing.xs,
1201
+ paddingHorizontal: isSm ? tokens.spacing.sm : tokens.spacing.md,
1202
+ paddingTop: tokens.spacing.sm
413
1203
  }
414
1204
  });
415
1205
  }
416
-
417
- // src/Select/Select.tsx
1206
+ function findNextEnabled(options, startIndex, direction) {
1207
+ const len = options.length;
1208
+ if (len === 0) return -1;
1209
+ for (let i = 1; i <= len; i++) {
1210
+ const idx = (startIndex + i * direction + len) % len;
1211
+ if (!options[idx].disabled) return idx;
1212
+ }
1213
+ return -1;
1214
+ }
1215
+ function useSelect({
1216
+ flatOptions,
1217
+ value,
1218
+ isOpen,
1219
+ onSelect,
1220
+ onClose,
1221
+ onOpen
1222
+ }) {
1223
+ const [focusedIndex, setFocusedIndex] = useState(-1);
1224
+ const typeAheadRef = useRef("");
1225
+ const typeAheadTimerRef = useRef();
1226
+ useEffect(() => {
1227
+ if (isOpen) {
1228
+ const selectedIdx = flatOptions.findIndex((o) => o.value === value);
1229
+ if (selectedIdx >= 0 && !flatOptions[selectedIdx].disabled) {
1230
+ setFocusedIndex(selectedIdx);
1231
+ } else {
1232
+ const firstEnabled = flatOptions.findIndex((o) => !o.disabled);
1233
+ setFocusedIndex(firstEnabled >= 0 ? firstEnabled : -1);
1234
+ }
1235
+ } else {
1236
+ setFocusedIndex(-1);
1237
+ }
1238
+ }, [isOpen, flatOptions, value]);
1239
+ const handleKeyDown = useCallback(
1240
+ (e) => {
1241
+ const key = e.key;
1242
+ if (!isOpen) {
1243
+ if (key === "ArrowDown" || key === "ArrowUp" || key === "Enter" || key === " ") {
1244
+ e.preventDefault();
1245
+ onOpen();
1246
+ }
1247
+ return;
1248
+ }
1249
+ switch (key) {
1250
+ case "ArrowDown": {
1251
+ e.preventDefault();
1252
+ const next = findNextEnabled(flatOptions, focusedIndex, 1);
1253
+ if (next >= 0) setFocusedIndex(next);
1254
+ break;
1255
+ }
1256
+ case "ArrowUp": {
1257
+ e.preventDefault();
1258
+ const prev = findNextEnabled(flatOptions, focusedIndex, -1);
1259
+ if (prev >= 0) setFocusedIndex(prev);
1260
+ break;
1261
+ }
1262
+ case "Enter":
1263
+ case " ": {
1264
+ e.preventDefault();
1265
+ if (focusedIndex >= 0 && !flatOptions[focusedIndex].disabled) {
1266
+ onSelect(flatOptions[focusedIndex].value);
1267
+ }
1268
+ break;
1269
+ }
1270
+ case "Escape": {
1271
+ e.preventDefault();
1272
+ onClose();
1273
+ break;
1274
+ }
1275
+ case "Home": {
1276
+ e.preventDefault();
1277
+ const first = flatOptions.findIndex((o) => !o.disabled);
1278
+ if (first >= 0) setFocusedIndex(first);
1279
+ break;
1280
+ }
1281
+ case "End": {
1282
+ e.preventDefault();
1283
+ const lastEnabled = [...flatOptions].reverse().findIndex((o) => !o.disabled);
1284
+ if (lastEnabled >= 0) setFocusedIndex(flatOptions.length - 1 - lastEnabled);
1285
+ break;
1286
+ }
1287
+ default: {
1288
+ if (key.length === 1 && !e.ctrlKey && !e.metaKey) {
1289
+ clearTimeout(typeAheadTimerRef.current);
1290
+ typeAheadRef.current += key.toLowerCase();
1291
+ const match = flatOptions.findIndex(
1292
+ (o) => !o.disabled && o.label.toLowerCase().startsWith(typeAheadRef.current)
1293
+ );
1294
+ if (match >= 0) setFocusedIndex(match);
1295
+ typeAheadTimerRef.current = setTimeout(() => {
1296
+ typeAheadRef.current = "";
1297
+ }, 500);
1298
+ }
1299
+ break;
1300
+ }
1301
+ }
1302
+ },
1303
+ [isOpen, focusedIndex, flatOptions, onSelect, onClose, onOpen]
1304
+ );
1305
+ return { focusedIndex, handleKeyDown };
1306
+ }
1307
+ function SelectOptionRow({
1308
+ option,
1309
+ isSelected,
1310
+ isFocused,
1311
+ onSelect,
1312
+ renderOption,
1313
+ size
1314
+ }) {
1315
+ const tokens = useTokens(1);
1316
+ const paddingVertical = size === "sm" ? tokens.spacing.xs : tokens.spacing.sm;
1317
+ const paddingHorizontal = size === "sm" ? tokens.spacing.sm : tokens.spacing.md;
1318
+ const fontSize = size === "sm" ? tokens.typography.size.sm : tokens.typography.size.base;
1319
+ if (renderOption) {
1320
+ return /* @__PURE__ */ React11.createElement(
1321
+ Pressable,
1322
+ {
1323
+ onPress: option.disabled ? void 0 : onSelect,
1324
+ disabled: option.disabled,
1325
+ role: "option",
1326
+ "aria-selected": isSelected
1327
+ },
1328
+ renderOption(option, { isSelected, isFocused })
1329
+ );
1330
+ }
1331
+ return /* @__PURE__ */ React11.createElement(
1332
+ Pressable,
1333
+ {
1334
+ onPress: option.disabled ? void 0 : onSelect,
1335
+ disabled: option.disabled,
1336
+ role: "option",
1337
+ "aria-selected": isSelected,
1338
+ style: ({ pressed }) => [
1339
+ {
1340
+ flexDirection: "row",
1341
+ alignItems: "center",
1342
+ justifyContent: "space-between",
1343
+ paddingVertical,
1344
+ paddingHorizontal
1345
+ },
1346
+ isSelected && {
1347
+ backgroundColor: srgbToHex(tokens.backgroundSunken.srgb)
1348
+ },
1349
+ isFocused && !isSelected && {
1350
+ backgroundColor: `${srgbToHex(tokens.border.srgb)}20`
1351
+ },
1352
+ option.disabled && {
1353
+ opacity: 0.5
1354
+ },
1355
+ pressed && {
1356
+ opacity: 0.7
1357
+ }
1358
+ ]
1359
+ },
1360
+ /* @__PURE__ */ React11.createElement(
1361
+ Text,
1362
+ {
1363
+ style: [
1364
+ {
1365
+ flex: 1,
1366
+ fontFamily: tokens.typography.fonts.default,
1367
+ fontSize,
1368
+ color: srgbToHex(tokens.textPrimary.srgb)
1369
+ },
1370
+ isSelected && {
1371
+ fontWeight: tokens.typography.weight.semibold,
1372
+ color: srgbToHex(tokens.interactive.srgb)
1373
+ },
1374
+ option.disabled && {
1375
+ color: srgbToHex(tokens.textSecondary.srgb)
1376
+ }
1377
+ ],
1378
+ numberOfLines: 1
1379
+ },
1380
+ option.label
1381
+ ),
1382
+ isSelected && /* @__PURE__ */ React11.createElement(View, { style: { marginLeft: tokens.spacing.sm } }, /* @__PURE__ */ React11.createElement(
1383
+ Icon,
1384
+ {
1385
+ name: "check",
1386
+ size: fontSize,
1387
+ color: srgbToHex(tokens.interactive.srgb)
1388
+ }
1389
+ ))
1390
+ );
1391
+ }
1392
+ function flattenOptions(items) {
1393
+ const result = [];
1394
+ for (const item of items) {
1395
+ if ("options" in item) {
1396
+ result.push(...item.options);
1397
+ } else {
1398
+ result.push(item);
1399
+ }
1400
+ }
1401
+ return result;
1402
+ }
418
1403
  function Select({
419
1404
  options,
420
1405
  value,
421
1406
  onValueChange,
422
1407
  label,
1408
+ placeholder,
423
1409
  disabled = false,
1410
+ renderOption,
1411
+ renderValue,
1412
+ size = "md",
424
1413
  style
425
1414
  }) {
426
1415
  const tokens = useTokens(1);
427
- const styles = React7.useMemo(
428
- () => getSelectStyles(tokens, disabled),
429
- [tokens, disabled]
430
- );
431
- const handleChange = React7.useCallback(
432
- (e) => {
433
- onValueChange(e.target.value);
1416
+ const { isOpen, open, close, toggle } = usePopover();
1417
+ const flatOptions = useMemo(() => flattenOptions(options), [options]);
1418
+ const { focusedIndex, handleKeyDown } = useSelect({
1419
+ flatOptions,
1420
+ value,
1421
+ isOpen,
1422
+ onSelect: (v) => {
1423
+ onValueChange(v);
1424
+ close();
434
1425
  },
435
- [onValueChange]
1426
+ onClose: close,
1427
+ onOpen: open
1428
+ });
1429
+ const styles = useMemo(
1430
+ () => getSelectStyles(tokens, disabled, size, isOpen),
1431
+ [tokens, disabled, size, isOpen]
436
1432
  );
437
- const selectStyle = StyleSheet.flatten(styles.select);
438
- return /* @__PURE__ */ React7.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, label && /* @__PURE__ */ React7.createElement(Text, { style: styles.label }, label), /* @__PURE__ */ React7.createElement(
439
- "select",
1433
+ const selectedOption = flatOptions.find((o) => o.value === value);
1434
+ const displayLabel = selectedOption?.label ?? placeholder ?? value;
1435
+ const iconColor = disabled ? srgbToHex(tokens.textSecondary.srgb) : srgbToHex(tokens.textPrimary.srgb);
1436
+ const triggerWebProps = { onKeyDown: handleKeyDown };
1437
+ const trigger = /* @__PURE__ */ React11.createElement(
1438
+ Pressable,
440
1439
  {
441
- value,
442
- onChange: handleChange,
1440
+ onPress: disabled ? void 0 : toggle,
443
1441
  disabled,
444
- style: {
445
- ...selectStyle,
446
- cursor: disabled ? "default" : "pointer",
447
- appearance: "auto"
1442
+ role: "combobox",
1443
+ "aria-expanded": isOpen,
1444
+ "aria-haspopup": "listbox",
1445
+ ...triggerWebProps,
1446
+ style: styles.trigger
1447
+ },
1448
+ renderValue ? renderValue(selectedOption) : /* @__PURE__ */ React11.createElement(Text, { style: styles.triggerText, numberOfLines: 1 }, displayLabel),
1449
+ /* @__PURE__ */ React11.createElement(View, { style: styles.iconWrapper, pointerEvents: "none" }, /* @__PURE__ */ React11.createElement(
1450
+ Icon,
1451
+ {
1452
+ name: isOpen ? "expand_less" : "expand_more",
1453
+ size: size === "sm" ? tokens.typography.size.sm + 2 : tokens.typography.size.base + 2,
1454
+ color: iconColor
448
1455
  }
1456
+ ))
1457
+ );
1458
+ return /* @__PURE__ */ React11.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : style ? [style] : []] }, label && /* @__PURE__ */ React11.createElement(Text, { style: styles.label }, label), /* @__PURE__ */ React11.createElement(
1459
+ Popover,
1460
+ {
1461
+ isOpen: isOpen && !disabled,
1462
+ onClose: close,
1463
+ trigger
449
1464
  },
450
- options.map((option) => /* @__PURE__ */ React7.createElement("option", { key: option.value, value: option.value }, option.label))
1465
+ /* @__PURE__ */ React11.createElement(
1466
+ ScrollView,
1467
+ {
1468
+ bounces: false,
1469
+ keyboardShouldPersistTaps: "handled",
1470
+ role: "listbox"
1471
+ },
1472
+ options.map((item) => {
1473
+ if (isOptionGroup(item)) {
1474
+ return /* @__PURE__ */ React11.createElement(View, { key: item.label }, /* @__PURE__ */ React11.createElement(Text, { style: styles.groupLabel }, item.label), item.options.map((opt) => /* @__PURE__ */ React11.createElement(
1475
+ SelectOptionRow,
1476
+ {
1477
+ key: opt.value,
1478
+ option: opt,
1479
+ isSelected: opt.value === value,
1480
+ isFocused: flatOptions[focusedIndex]?.value === opt.value,
1481
+ onSelect: () => {
1482
+ onValueChange(opt.value);
1483
+ close();
1484
+ },
1485
+ renderOption,
1486
+ size
1487
+ }
1488
+ )));
1489
+ }
1490
+ return /* @__PURE__ */ React11.createElement(
1491
+ SelectOptionRow,
1492
+ {
1493
+ key: item.value,
1494
+ option: item,
1495
+ isSelected: item.value === value,
1496
+ isFocused: flatOptions[focusedIndex]?.value === item.value,
1497
+ onSelect: () => {
1498
+ onValueChange(item.value);
1499
+ close();
1500
+ },
1501
+ renderOption,
1502
+ size
1503
+ }
1504
+ );
1505
+ })
1506
+ )
451
1507
  ));
452
1508
  }
453
1509
  var TRACK_WIDTH = 40;
@@ -459,12 +1515,13 @@ function getToggleStyles(tokens, value, disabled) {
459
1515
  container: {
460
1516
  flexDirection: "row",
461
1517
  alignItems: "center",
462
- gap: 8,
1518
+ gap: tokens.spacing.sm,
463
1519
  opacity: disabled ? 0.5 : 1
464
1520
  },
465
1521
  label: {
466
- fontSize: 12,
467
- fontWeight: "600",
1522
+ fontFamily: tokens.typography.fonts.default,
1523
+ fontSize: tokens.typography.size.sm,
1524
+ fontWeight: tokens.typography.weight.semibold,
468
1525
  color: srgbToHex(tokens.textSecondary.srgb)
469
1526
  },
470
1527
  track: {
@@ -494,16 +1551,16 @@ function Toggle({
494
1551
  style
495
1552
  }) {
496
1553
  const tokens = useTokens(1);
497
- const styles = React7.useMemo(
1554
+ const styles = React11.useMemo(
498
1555
  () => getToggleStyles(tokens, value, disabled),
499
1556
  [tokens, value, disabled]
500
1557
  );
501
- const handlePress = React7.useCallback(() => {
1558
+ const handlePress = React11.useCallback(() => {
502
1559
  if (!disabled) {
503
1560
  onValueChange(!value);
504
1561
  }
505
1562
  }, [disabled, value, onValueChange]);
506
- return /* @__PURE__ */ React7.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, label && /* @__PURE__ */ React7.createElement(Text, { style: styles.label }, label), /* @__PURE__ */ React7.createElement(
1563
+ return /* @__PURE__ */ React11.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, label && /* @__PURE__ */ React11.createElement(Text, { style: styles.label }, label), /* @__PURE__ */ React11.createElement(
507
1564
  Pressable,
508
1565
  {
509
1566
  onPress: handlePress,
@@ -511,13 +1568,16 @@ function Toggle({
511
1568
  accessibilityRole: "switch",
512
1569
  accessibilityState: { checked: value, disabled }
513
1570
  },
514
- /* @__PURE__ */ React7.createElement(View, { style: styles.track }, /* @__PURE__ */ React7.createElement(View, { style: styles.thumb }))
1571
+ /* @__PURE__ */ React11.createElement(View, { style: styles.track }, /* @__PURE__ */ React11.createElement(View, { style: styles.thumb }))
515
1572
  ));
516
1573
  }
517
- function getSliderStyles(tokens) {
1574
+ var TRACK_HEIGHT2 = 6;
1575
+ var THUMB_SIZE2 = 16;
1576
+ function getSliderStyles(tokens, disabled) {
518
1577
  return StyleSheet.create({
519
1578
  container: {
520
- gap: 4
1579
+ gap: tokens.spacing.xs,
1580
+ opacity: disabled ? 0.5 : 1
521
1581
  },
522
1582
  labelRow: {
523
1583
  flexDirection: "row",
@@ -525,28 +1585,60 @@ function getSliderStyles(tokens) {
525
1585
  alignItems: "center"
526
1586
  },
527
1587
  label: {
528
- fontSize: 12,
529
- fontWeight: "600",
1588
+ fontFamily: tokens.typography.fonts.default,
1589
+ fontSize: tokens.typography.size.sm,
1590
+ fontWeight: tokens.typography.weight.semibold,
530
1591
  color: srgbToHex(tokens.textSecondary.srgb)
531
1592
  },
532
1593
  value: {
533
- fontSize: 12,
534
- fontWeight: "500",
1594
+ fontFamily: tokens.typography.fonts.default,
1595
+ fontSize: tokens.typography.size.sm,
1596
+ fontWeight: tokens.typography.weight.medium,
535
1597
  color: srgbToHex(tokens.textPrimary.srgb)
1598
+ },
1599
+ valueInput: {
1600
+ width: 48,
1601
+ paddingVertical: 0,
1602
+ paddingHorizontal: 4,
1603
+ borderWidth: 1,
1604
+ borderColor: srgbToHex(tokens.border.srgb),
1605
+ borderRadius: 4,
1606
+ backgroundColor: "transparent",
1607
+ color: srgbToHex(tokens.textPrimary.srgb),
1608
+ fontFamily: tokens.typography.fonts.default,
1609
+ fontSize: tokens.typography.size.sm,
1610
+ fontWeight: tokens.typography.weight.medium,
1611
+ textAlign: "right"
1612
+ },
1613
+ trackContainer: {
1614
+ height: TRACK_HEIGHT2 + THUMB_SIZE2,
1615
+ justifyContent: "center",
1616
+ position: "relative"
1617
+ },
1618
+ trackRail: {
1619
+ position: "absolute",
1620
+ left: 0,
1621
+ right: 0,
1622
+ height: TRACK_HEIGHT2,
1623
+ borderRadius: TRACK_HEIGHT2 / 2,
1624
+ backgroundColor: srgbToHex(tokens.border.srgb)
1625
+ },
1626
+ trackFill: {
1627
+ position: "absolute",
1628
+ left: 0,
1629
+ height: TRACK_HEIGHT2,
1630
+ borderRadius: TRACK_HEIGHT2 / 2,
1631
+ backgroundColor: srgbToHex(tokens.interactive.srgb)
1632
+ },
1633
+ thumb: {
1634
+ position: "absolute",
1635
+ width: THUMB_SIZE2,
1636
+ height: THUMB_SIZE2,
1637
+ borderRadius: THUMB_SIZE2 / 2,
1638
+ backgroundColor: srgbToHex(tokens.interactive.srgb)
536
1639
  }
537
1640
  });
538
1641
  }
539
- function getSliderInputStyle(tokens, disabled) {
540
- return {
541
- width: "100%",
542
- height: 6,
543
- borderRadius: 3,
544
- appearance: "auto",
545
- cursor: disabled ? "default" : "pointer",
546
- opacity: disabled ? 0.5 : 1,
547
- accentColor: srgbToHex(tokens.interactive.srgb)
548
- };
549
- }
550
1642
 
551
1643
  // src/Slider/Slider.tsx
552
1644
  function Slider({
@@ -557,39 +1649,108 @@ function Slider({
557
1649
  step = 1,
558
1650
  label,
559
1651
  showValue = false,
1652
+ editableValue = false,
560
1653
  disabled = false,
561
1654
  style
562
1655
  }) {
563
1656
  const tokens = useTokens(1);
564
- const styles = React7.useMemo(
565
- () => getSliderStyles(tokens),
566
- [tokens]
567
- );
568
- const inputStyle = React7.useMemo(
569
- () => getSliderInputStyle(tokens, disabled),
1657
+ const styles = React11.useMemo(
1658
+ () => getSliderStyles(tokens, disabled),
570
1659
  [tokens, disabled]
571
1660
  );
572
- const handleChange = React7.useCallback(
573
- (e) => {
574
- onValueChange(Number(e.target.value));
1661
+ const trackRef = React11.useRef(null);
1662
+ const trackWidth = React11.useRef(0);
1663
+ const trackPageX = React11.useRef(0);
1664
+ const onValueChangeRef = React11.useRef(onValueChange);
1665
+ const minRef = React11.useRef(min);
1666
+ const maxRef = React11.useRef(max);
1667
+ const stepRef = React11.useRef(step);
1668
+ const disabledRef = React11.useRef(disabled);
1669
+ React11.useEffect(() => {
1670
+ onValueChangeRef.current = onValueChange;
1671
+ }, [onValueChange]);
1672
+ React11.useEffect(() => {
1673
+ minRef.current = min;
1674
+ }, [min]);
1675
+ React11.useEffect(() => {
1676
+ maxRef.current = max;
1677
+ }, [max]);
1678
+ React11.useEffect(() => {
1679
+ stepRef.current = step;
1680
+ }, [step]);
1681
+ React11.useEffect(() => {
1682
+ disabledRef.current = disabled;
1683
+ }, [disabled]);
1684
+ const computeValue = React11.useCallback((pageX) => {
1685
+ const localX = pageX - trackPageX.current;
1686
+ const ratio2 = Math.min(1, Math.max(0, localX / trackWidth.current));
1687
+ const raw = minRef.current + ratio2 * (maxRef.current - minRef.current);
1688
+ const stepped = Math.round(raw / stepRef.current) * stepRef.current;
1689
+ return Math.min(maxRef.current, Math.max(minRef.current, stepped));
1690
+ }, []);
1691
+ const panResponder = React11.useRef(
1692
+ PanResponder.create({
1693
+ onStartShouldSetPanResponder: () => !disabledRef.current,
1694
+ onMoveShouldSetPanResponder: () => !disabledRef.current,
1695
+ onPanResponderGrant: (evt) => {
1696
+ onValueChangeRef.current(computeValue(evt.nativeEvent.pageX));
1697
+ },
1698
+ onPanResponderMove: (_evt, gestureState) => {
1699
+ onValueChangeRef.current(computeValue(gestureState.moveX));
1700
+ }
1701
+ })
1702
+ ).current;
1703
+ const ratio = max > min ? (value - min) / (max - min) : 0;
1704
+ const usableWidth = Math.max(0, trackWidth.current - THUMB_SIZE2);
1705
+ const thumbLeft = ratio * usableWidth;
1706
+ const fillWidth = thumbLeft + THUMB_SIZE2 / 2;
1707
+ const handleValueTextSubmit = React11.useCallback(
1708
+ (text) => {
1709
+ const raw = Number(text);
1710
+ if (!Number.isNaN(raw)) {
1711
+ onValueChange(Math.min(max, Math.max(min, raw)));
1712
+ }
575
1713
  },
576
- [onValueChange]
1714
+ [onValueChange, min, max]
577
1715
  );
578
- return /* @__PURE__ */ React7.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, (label || showValue) && /* @__PURE__ */ React7.createElement(View, { style: styles.labelRow }, label && /* @__PURE__ */ React7.createElement(Text, { style: styles.label }, label), showValue && /* @__PURE__ */ React7.createElement(Text, { style: styles.value }, value)), /* @__PURE__ */ React7.createElement(
579
- "input",
1716
+ const [editText, setEditText] = React11.useState(String(value));
1717
+ React11.useEffect(() => {
1718
+ setEditText(String(value));
1719
+ }, [value]);
1720
+ const showLabel = label || showValue || editableValue;
1721
+ return /* @__PURE__ */ React11.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, showLabel && /* @__PURE__ */ React11.createElement(View, { style: styles.labelRow }, label && /* @__PURE__ */ React11.createElement(Text, { style: styles.label }, label), editableValue ? /* @__PURE__ */ React11.createElement(
1722
+ TextInput$1,
580
1723
  {
581
- type: "range",
582
- min,
583
- max,
584
- step,
585
- value,
586
- onChange: handleChange,
587
- disabled,
588
- style: inputStyle
1724
+ style: styles.valueInput,
1725
+ value: editText,
1726
+ keyboardType: "numeric",
1727
+ onChangeText: setEditText,
1728
+ onSubmitEditing: (e) => handleValueTextSubmit(e.nativeEvent.text),
1729
+ onBlur: () => handleValueTextSubmit(editText),
1730
+ selectTextOnFocus: true,
1731
+ editable: !disabled
589
1732
  }
1733
+ ) : showValue && /* @__PURE__ */ React11.createElement(Text, { style: styles.value }, value)), /* @__PURE__ */ React11.createElement(
1734
+ View,
1735
+ {
1736
+ ref: trackRef,
1737
+ style: styles.trackContainer,
1738
+ onLayout: (e) => {
1739
+ trackWidth.current = e.nativeEvent.layout.width;
1740
+ trackRef.current?.measure((_x, _y, _w, _h, pageX) => {
1741
+ if (pageX != null) trackPageX.current = pageX;
1742
+ });
1743
+ },
1744
+ ...panResponder.panHandlers
1745
+ },
1746
+ /* @__PURE__ */ React11.createElement(View, { style: styles.trackRail }),
1747
+ /* @__PURE__ */ React11.createElement(View, { style: [styles.trackFill, { width: fillWidth }] }),
1748
+ /* @__PURE__ */ React11.createElement(View, { style: [styles.thumb, { left: thumbLeft }] })
590
1749
  ));
591
1750
  }
592
- var HUE_GRADIENT = "linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)";
1751
+ var TRACK_HEIGHT3 = 22;
1752
+ var THUMB_SIZE3 = 18;
1753
+ var SEGMENT_COUNT = 48;
593
1754
  function hueToHex(hue) {
594
1755
  const h = (hue % 360 + 360) % 360;
595
1756
  const x = 1 - Math.abs(h / 60 % 2 - 1);
@@ -622,20 +1783,17 @@ function hueToHex(hue) {
622
1783
  const toHex = (v) => Math.round(v * 255).toString(16).padStart(2, "0");
623
1784
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
624
1785
  }
625
- function buildHueGradient(min, max) {
626
- if (min === 0 && max === 359) return HUE_GRADIENT;
627
- const steps = 7;
628
- const stops = [];
629
- for (let i = 0; i <= steps; i++) {
630
- const hue = min + (max - min) * (i / steps);
631
- stops.push(hueToHex(hue));
632
- }
633
- return `linear-gradient(to right, ${stops.join(", ")})`;
1786
+ function buildHueSegments(min, max) {
1787
+ return Array.from({ length: SEGMENT_COUNT }, (_, i) => {
1788
+ const hue = min + (max - min) * (i / (SEGMENT_COUNT - 1));
1789
+ return hueToHex(hue);
1790
+ });
634
1791
  }
635
- function getHueSliderStyles(tokens) {
1792
+ function getHueSliderStyles(tokens, disabled) {
636
1793
  return StyleSheet.create({
637
1794
  container: {
638
- gap: 4
1795
+ gap: tokens.spacing.xs,
1796
+ opacity: disabled ? 0.5 : 1
639
1797
  },
640
1798
  labelRow: {
641
1799
  flexDirection: "row",
@@ -643,33 +1801,59 @@ function getHueSliderStyles(tokens) {
643
1801
  alignItems: "center"
644
1802
  },
645
1803
  label: {
646
- fontSize: 12,
647
- fontWeight: "600",
1804
+ fontFamily: tokens.typography.fonts.default,
1805
+ fontSize: tokens.typography.size.sm,
1806
+ fontWeight: tokens.typography.weight.semibold,
648
1807
  color: srgbToHex(tokens.textSecondary.srgb)
649
1808
  },
650
1809
  value: {
651
- fontSize: 12,
652
- fontWeight: "500",
1810
+ fontFamily: tokens.typography.fonts.default,
1811
+ fontSize: tokens.typography.size.sm,
1812
+ fontWeight: tokens.typography.weight.medium,
653
1813
  color: srgbToHex(tokens.textPrimary.srgb)
654
1814
  },
655
- sliderTrack: {
656
- height: 22,
657
- borderRadius: 11,
1815
+ valueInput: {
1816
+ width: 48,
1817
+ paddingVertical: 0,
1818
+ paddingHorizontal: 4,
1819
+ borderWidth: 1,
1820
+ borderColor: srgbToHex(tokens.border.srgb),
1821
+ borderRadius: 4,
1822
+ backgroundColor: "transparent",
1823
+ color: srgbToHex(tokens.textPrimary.srgb),
1824
+ fontFamily: tokens.typography.fonts.default,
1825
+ fontSize: tokens.typography.size.sm,
1826
+ fontWeight: tokens.typography.weight.medium,
1827
+ textAlign: "right"
1828
+ },
1829
+ trackContainer: {
1830
+ height: TRACK_HEIGHT3 + THUMB_SIZE3,
1831
+ justifyContent: "center",
1832
+ position: "relative"
1833
+ },
1834
+ gradientTrack: {
1835
+ position: "absolute",
1836
+ left: 0,
1837
+ right: 0,
1838
+ height: TRACK_HEIGHT3,
1839
+ borderRadius: TRACK_HEIGHT3 / 2,
1840
+ flexDirection: "row",
658
1841
  overflow: "hidden"
1842
+ },
1843
+ segment: {
1844
+ flex: 1
1845
+ },
1846
+ thumb: {
1847
+ position: "absolute",
1848
+ width: THUMB_SIZE3,
1849
+ height: THUMB_SIZE3,
1850
+ borderRadius: THUMB_SIZE3 / 2,
1851
+ backgroundColor: "#ffffff",
1852
+ borderWidth: 2,
1853
+ borderColor: srgbToHex(tokens.border.srgb)
659
1854
  }
660
1855
  });
661
1856
  }
662
- function getHueSliderInputStyle(disabled, min = 0, max = 359) {
663
- return {
664
- width: "100%",
665
- height: 22,
666
- borderRadius: 11,
667
- cursor: disabled ? "default" : "pointer",
668
- opacity: disabled ? 0.5 : 1,
669
- background: buildHueGradient(min, max),
670
- appearance: "auto"
671
- };
672
- }
673
1857
 
674
1858
  // src/HueSlider/HueSlider.tsx
675
1859
  function HueSlider({
@@ -679,41 +1863,936 @@ function HueSlider({
679
1863
  max = 359,
680
1864
  label,
681
1865
  showValue = false,
1866
+ editableValue = false,
682
1867
  disabled = false,
683
1868
  style
684
1869
  }) {
685
1870
  const tokens = useTokens(1);
686
- const styles = React7.useMemo(
687
- () => getHueSliderStyles(tokens),
688
- [tokens]
1871
+ const styles = React11.useMemo(
1872
+ () => getHueSliderStyles(tokens, disabled),
1873
+ [tokens, disabled]
689
1874
  );
690
- const inputStyle = React7.useMemo(
691
- () => getHueSliderInputStyle(disabled, min, max),
692
- [disabled, min, max]
1875
+ const segments = React11.useMemo(
1876
+ () => buildHueSegments(min, max),
1877
+ [min, max]
693
1878
  );
1879
+ const trackRef = React11.useRef(null);
1880
+ const trackWidth = React11.useRef(0);
1881
+ const trackPageX = React11.useRef(0);
1882
+ const onValueChangeRef = React11.useRef(onValueChange);
1883
+ const minRef = React11.useRef(min);
1884
+ const maxRef = React11.useRef(max);
1885
+ const disabledRef = React11.useRef(disabled);
1886
+ React11.useEffect(() => {
1887
+ onValueChangeRef.current = onValueChange;
1888
+ }, [onValueChange]);
1889
+ React11.useEffect(() => {
1890
+ minRef.current = min;
1891
+ }, [min]);
1892
+ React11.useEffect(() => {
1893
+ maxRef.current = max;
1894
+ }, [max]);
1895
+ React11.useEffect(() => {
1896
+ disabledRef.current = disabled;
1897
+ }, [disabled]);
1898
+ const computeHue = React11.useCallback((pageX) => {
1899
+ const localX = pageX - trackPageX.current;
1900
+ const ratio2 = Math.min(1, Math.max(0, localX / trackWidth.current));
1901
+ const raw = minRef.current + ratio2 * (maxRef.current - minRef.current);
1902
+ const stepped = Math.round(raw);
1903
+ return (stepped % 360 + 360) % 360;
1904
+ }, []);
1905
+ const panResponder = React11.useRef(
1906
+ PanResponder.create({
1907
+ onStartShouldSetPanResponder: () => !disabledRef.current,
1908
+ onMoveShouldSetPanResponder: () => !disabledRef.current,
1909
+ onPanResponderGrant: (evt) => {
1910
+ onValueChangeRef.current(computeHue(evt.nativeEvent.pageX));
1911
+ },
1912
+ onPanResponderMove: (_evt, gestureState) => {
1913
+ onValueChangeRef.current(computeHue(gestureState.moveX));
1914
+ }
1915
+ })
1916
+ ).current;
694
1917
  const sliderValue = max > 359 && value < min ? value + 360 : value;
695
- const handleChange = React7.useCallback(
696
- (e) => {
697
- const raw = Number(e.target.value);
698
- onValueChange((raw % 360 + 360) % 360);
1918
+ const ratio = max > min ? (sliderValue - min) / (max - min) : 0;
1919
+ const usableWidth = Math.max(0, trackWidth.current - THUMB_SIZE3);
1920
+ const thumbLeft = ratio * usableWidth;
1921
+ const handleValueTextSubmit = React11.useCallback(
1922
+ (text) => {
1923
+ const raw = Number(text);
1924
+ if (!Number.isNaN(raw)) {
1925
+ onValueChange((raw % 360 + 360) % 360);
1926
+ }
699
1927
  },
700
1928
  [onValueChange]
701
1929
  );
702
- return /* @__PURE__ */ React7.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, (label || showValue) && /* @__PURE__ */ React7.createElement(View, { style: styles.labelRow }, label && /* @__PURE__ */ React7.createElement(Text, { style: styles.label }, label), showValue && /* @__PURE__ */ React7.createElement(Text, { style: styles.value }, value, "\xB0")), /* @__PURE__ */ React7.createElement(
703
- "input",
1930
+ const [editText, setEditText] = React11.useState(String(value));
1931
+ React11.useEffect(() => {
1932
+ setEditText(String(value));
1933
+ }, [value]);
1934
+ const showLabel = label || showValue || editableValue;
1935
+ return /* @__PURE__ */ React11.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, showLabel && /* @__PURE__ */ React11.createElement(View, { style: styles.labelRow }, label && /* @__PURE__ */ React11.createElement(Text, { style: styles.label }, label), editableValue ? /* @__PURE__ */ React11.createElement(
1936
+ TextInput$1,
704
1937
  {
705
- type: "range",
706
- min,
707
- max,
708
- step: 1,
709
- value: sliderValue,
710
- onChange: handleChange,
711
- disabled,
712
- style: inputStyle
1938
+ style: styles.valueInput,
1939
+ value: editText,
1940
+ keyboardType: "numeric",
1941
+ onChangeText: setEditText,
1942
+ onSubmitEditing: (e) => handleValueTextSubmit(e.nativeEvent.text),
1943
+ onBlur: () => handleValueTextSubmit(editText),
1944
+ selectTextOnFocus: true,
1945
+ editable: !disabled
713
1946
  }
1947
+ ) : showValue && /* @__PURE__ */ React11.createElement(Text, { style: styles.value }, value, "\xB0")), /* @__PURE__ */ React11.createElement(
1948
+ View,
1949
+ {
1950
+ ref: trackRef,
1951
+ style: styles.trackContainer,
1952
+ onLayout: (e) => {
1953
+ trackWidth.current = e.nativeEvent.layout.width;
1954
+ trackRef.current?.measure((_x, _y, _w, _h, pageX) => {
1955
+ if (pageX != null) trackPageX.current = pageX;
1956
+ });
1957
+ },
1958
+ ...panResponder.panHandlers
1959
+ },
1960
+ /* @__PURE__ */ React11.createElement(View, { style: styles.gradientTrack }, segments.map((color, i) => /* @__PURE__ */ React11.createElement(View, { key: i, style: [styles.segment, { backgroundColor: color }] }))),
1961
+ /* @__PURE__ */ React11.createElement(View, { style: [styles.thumb, { left: thumbLeft }] })
714
1962
  ));
715
1963
  }
1964
+ var TRACK_HEIGHT4 = 22;
1965
+ var THUMB_SIZE4 = 18;
1966
+ function getColorScaleSliderStyles(tokens, disabled) {
1967
+ return StyleSheet.create({
1968
+ container: {
1969
+ gap: tokens.spacing.xs,
1970
+ opacity: disabled ? 0.5 : 1
1971
+ },
1972
+ labelRow: {
1973
+ flexDirection: "row",
1974
+ justifyContent: "space-between",
1975
+ alignItems: "center"
1976
+ },
1977
+ label: {
1978
+ fontFamily: tokens.typography.fonts.default,
1979
+ fontSize: tokens.typography.size.sm,
1980
+ fontWeight: tokens.typography.weight.semibold,
1981
+ color: srgbToHex(tokens.textSecondary.srgb)
1982
+ },
1983
+ trackContainer: {
1984
+ height: TRACK_HEIGHT4 + THUMB_SIZE4,
1985
+ justifyContent: "center",
1986
+ position: "relative"
1987
+ },
1988
+ gradientTrack: {
1989
+ position: "absolute",
1990
+ left: 0,
1991
+ right: 0,
1992
+ height: TRACK_HEIGHT4,
1993
+ borderRadius: TRACK_HEIGHT4 / 2,
1994
+ flexDirection: "row",
1995
+ overflow: "hidden"
1996
+ },
1997
+ segment: {
1998
+ flex: 1
1999
+ },
2000
+ thumb: {
2001
+ position: "absolute",
2002
+ width: THUMB_SIZE4,
2003
+ height: THUMB_SIZE4,
2004
+ borderRadius: THUMB_SIZE4 / 2,
2005
+ backgroundColor: "#ffffff",
2006
+ borderWidth: 2,
2007
+ borderColor: srgbToHex(tokens.border.srgb)
2008
+ },
2009
+ warning: {
2010
+ fontFamily: tokens.typography.fonts.default,
2011
+ fontSize: tokens.typography.size.xs,
2012
+ fontWeight: tokens.typography.weight.medium,
2013
+ color: srgbToHex(tokens.error.srgb)
2014
+ }
2015
+ });
2016
+ }
2017
+
2018
+ // src/ColorScaleSlider/ColorScaleSlider.tsx
2019
+ function ColorScaleSlider({
2020
+ colors,
2021
+ value,
2022
+ onValueChange,
2023
+ label,
2024
+ warning,
2025
+ trimEnds = false,
2026
+ snap = false,
2027
+ disabled = false,
2028
+ animateValue = false,
2029
+ style
2030
+ }) {
2031
+ const tokens = useTokens(1);
2032
+ const styles = React11.useMemo(
2033
+ () => getColorScaleSliderStyles(tokens, disabled),
2034
+ [tokens, disabled]
2035
+ );
2036
+ const trackRef = React11.useRef(null);
2037
+ const trackWidth = React11.useRef(0);
2038
+ const trackPageX = React11.useRef(0);
2039
+ const isDragging = React11.useRef(false);
2040
+ const thumbAnim = React11.useRef(new Animated.Value(0)).current;
2041
+ const onValueChangeRef = React11.useRef(onValueChange);
2042
+ const disabledRef = React11.useRef(disabled);
2043
+ const colorsLengthRef = React11.useRef(colors.length);
2044
+ const trimEndsRef = React11.useRef(trimEnds);
2045
+ const snapRef = React11.useRef(snap);
2046
+ React11.useEffect(() => {
2047
+ onValueChangeRef.current = onValueChange;
2048
+ }, [onValueChange]);
2049
+ React11.useEffect(() => {
2050
+ disabledRef.current = disabled;
2051
+ }, [disabled]);
2052
+ React11.useEffect(() => {
2053
+ colorsLengthRef.current = colors.length;
2054
+ }, [colors.length]);
2055
+ React11.useEffect(() => {
2056
+ trimEndsRef.current = trimEnds;
2057
+ }, [trimEnds]);
2058
+ React11.useEffect(() => {
2059
+ snapRef.current = snap;
2060
+ }, [snap]);
2061
+ const computeNv = React11.useCallback((pageX) => {
2062
+ const localX = pageX - trackPageX.current;
2063
+ const ratio2 = Math.min(1, Math.max(0, localX / trackWidth.current));
2064
+ const totalSteps2 = colorsLengthRef.current - 1;
2065
+ const minNV2 = trimEndsRef.current ? 1 / totalSteps2 : 0;
2066
+ const maxNV2 = trimEndsRef.current ? 1 - 1 / totalSteps2 : 1;
2067
+ const range2 = maxNV2 - minNV2;
2068
+ let nv = maxNV2 - ratio2 * range2;
2069
+ if (snapRef.current && totalSteps2 > 0) {
2070
+ const stepNv = 1 / totalSteps2;
2071
+ nv = Math.round(nv / stepNv) * stepNv;
2072
+ nv = Math.min(maxNV2, Math.max(minNV2, nv));
2073
+ }
2074
+ return nv;
2075
+ }, []);
2076
+ const panResponder = React11.useRef(
2077
+ PanResponder.create({
2078
+ onStartShouldSetPanResponder: () => !disabledRef.current,
2079
+ onMoveShouldSetPanResponder: () => !disabledRef.current,
2080
+ onPanResponderGrant: (evt) => {
2081
+ isDragging.current = true;
2082
+ onValueChangeRef.current(computeNv(evt.nativeEvent.pageX));
2083
+ },
2084
+ onPanResponderMove: (_evt, gestureState) => {
2085
+ onValueChangeRef.current(computeNv(gestureState.moveX));
2086
+ },
2087
+ onPanResponderRelease: () => {
2088
+ isDragging.current = false;
2089
+ },
2090
+ onPanResponderTerminate: () => {
2091
+ isDragging.current = false;
2092
+ }
2093
+ })
2094
+ ).current;
2095
+ const visibleColors = trimEnds ? colors.slice(1, -1) : colors;
2096
+ const totalSteps = colors.length - 1;
2097
+ const minNV = trimEnds ? 1 / totalSteps : 0;
2098
+ const maxNV = trimEnds ? 1 - 1 / totalSteps : 1;
2099
+ const range = maxNV - minNV;
2100
+ const clampedValue = value !== void 0 ? Math.min(maxNV, Math.max(minNV, value)) : (maxNV + minNV) / 2;
2101
+ const ratio = range > 0 ? (maxNV - clampedValue) / range : 0.5;
2102
+ const usableWidth = Math.max(0, trackWidth.current - THUMB_SIZE4);
2103
+ const thumbLeft = ratio * usableWidth;
2104
+ React11.useEffect(() => {
2105
+ if (isDragging.current || !animateValue) {
2106
+ thumbAnim.setValue(thumbLeft);
2107
+ } else {
2108
+ Animated.timing(thumbAnim, {
2109
+ toValue: thumbLeft,
2110
+ duration: 300,
2111
+ useNativeDriver: false
2112
+ }).start();
2113
+ }
2114
+ }, [thumbLeft, animateValue, thumbAnim]);
2115
+ return /* @__PURE__ */ React11.createElement(View, { style: [styles.container, ...Array.isArray(style) ? style : [style]] }, label && /* @__PURE__ */ React11.createElement(View, { style: styles.labelRow }, /* @__PURE__ */ React11.createElement(Text, { style: styles.label }, label)), /* @__PURE__ */ React11.createElement(
2116
+ View,
2117
+ {
2118
+ ref: trackRef,
2119
+ style: styles.trackContainer,
2120
+ onLayout: (e) => {
2121
+ trackWidth.current = e.nativeEvent.layout.width;
2122
+ const newUsableWidth = Math.max(0, e.nativeEvent.layout.width - THUMB_SIZE4);
2123
+ thumbAnim.setValue(ratio * newUsableWidth);
2124
+ trackRef.current?.measure((_x, _y, _w, _h, pageX) => {
2125
+ if (pageX != null) trackPageX.current = pageX;
2126
+ });
2127
+ },
2128
+ ...panResponder.panHandlers
2129
+ },
2130
+ /* @__PURE__ */ React11.createElement(View, { style: styles.gradientTrack }, visibleColors.map((color, i) => /* @__PURE__ */ React11.createElement(View, { key: i, style: [styles.segment, { backgroundColor: srgbToHex(color.srgb) }] }))),
2131
+ /* @__PURE__ */ React11.createElement(Animated.View, { style: [styles.thumb, { left: thumbAnim }] })
2132
+ ), warning && /* @__PURE__ */ React11.createElement(Text, { style: styles.warning }, warning));
2133
+ }
2134
+ function getAppShellStyles(tokens) {
2135
+ return StyleSheet.create({
2136
+ container: {
2137
+ flex: 1,
2138
+ flexDirection: "row",
2139
+ overflow: "hidden",
2140
+ backgroundColor: srgbToHex(tokens.background.srgb)
2141
+ },
2142
+ main: {
2143
+ flex: 1,
2144
+ flexDirection: "column",
2145
+ minWidth: 0,
2146
+ overflow: "hidden"
2147
+ }
2148
+ });
2149
+ }
2150
+
2151
+ // src/AppShell/AppShell.tsx
2152
+ function AppShell({ sidebar, children }) {
2153
+ const tokens = useTokens();
2154
+ const styles = React11.useMemo(() => getAppShellStyles(tokens), [tokens]);
2155
+ return /* @__PURE__ */ React11.createElement(View, { style: styles.container }, sidebar, /* @__PURE__ */ React11.createElement(View, { style: styles.main }, children));
2156
+ }
2157
+ function getSidebarStyles({ tokens, width, bordered }) {
2158
+ const borderColor = srgbToHex(tokens.border.srgb);
2159
+ return StyleSheet.create({
2160
+ container: {
2161
+ width,
2162
+ flexShrink: 0,
2163
+ flexDirection: "column",
2164
+ backgroundColor: srgbToHex(tokens.background.srgb),
2165
+ borderRightWidth: bordered ? 1 : 0,
2166
+ borderRightColor: borderColor
2167
+ },
2168
+ header: {
2169
+ flexShrink: 0,
2170
+ borderBottomWidth: 1,
2171
+ borderBottomColor: borderColor
2172
+ },
2173
+ body: {
2174
+ flex: 1
2175
+ },
2176
+ footer: {
2177
+ flexShrink: 0,
2178
+ borderTopWidth: 1,
2179
+ borderTopColor: borderColor
2180
+ }
2181
+ });
2182
+ }
2183
+
2184
+ // src/Sidebar/Sidebar.tsx
2185
+ function Sidebar({
2186
+ children,
2187
+ header,
2188
+ footer,
2189
+ width = 260,
2190
+ bordered = true
2191
+ }) {
2192
+ const tokens = useTokens();
2193
+ const styles = React11.useMemo(
2194
+ () => getSidebarStyles({ tokens, width, bordered }),
2195
+ [tokens, width, bordered]
2196
+ );
2197
+ return /* @__PURE__ */ React11.createElement(View, { style: styles.container }, header && /* @__PURE__ */ React11.createElement(View, { style: styles.header }, header), /* @__PURE__ */ React11.createElement(ScrollView, { style: styles.body }, children), footer && /* @__PURE__ */ React11.createElement(View, { style: styles.footer }, footer));
2198
+ }
2199
+ function getNavbarStyles({ tokens, height, bordered }) {
2200
+ const borderColor = srgbToHex(tokens.border.srgb);
2201
+ return StyleSheet.create({
2202
+ container: {
2203
+ flexDirection: "row",
2204
+ alignItems: "center",
2205
+ height,
2206
+ flexShrink: 0,
2207
+ paddingHorizontal: 24,
2208
+ backgroundColor: srgbToHex(tokens.background.srgb),
2209
+ borderBottomWidth: bordered ? 1 : 0,
2210
+ borderBottomColor: borderColor
2211
+ },
2212
+ left: {
2213
+ flex: 1,
2214
+ flexDirection: "row",
2215
+ alignItems: "center"
2216
+ },
2217
+ right: {
2218
+ flex: 1,
2219
+ flexDirection: "row",
2220
+ alignItems: "center",
2221
+ justifyContent: "flex-end"
2222
+ }
2223
+ });
2224
+ }
2225
+
2226
+ // src/Navbar/Navbar.tsx
2227
+ function Navbar({
2228
+ children,
2229
+ left,
2230
+ right,
2231
+ height = 56,
2232
+ bordered = true
2233
+ }) {
2234
+ const tokens = useTokens();
2235
+ const styles = React11.useMemo(
2236
+ () => getNavbarStyles({ tokens, height, bordered }),
2237
+ [tokens, height, bordered]
2238
+ );
2239
+ return /* @__PURE__ */ React11.createElement(View, { style: styles.container }, children ? children : /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(View, { style: styles.left }, left), /* @__PURE__ */ React11.createElement(View, { style: styles.right }, right)));
2240
+ }
2241
+
2242
+ // src/registry/registry.ts
2243
+ var CATEGORIES = [
2244
+ { id: "actions", name: "Actions", description: "Interactive elements that trigger actions" },
2245
+ { id: "form-controls", name: "Form Controls", description: "Input elements for user data entry" },
2246
+ { id: "range-inputs", name: "Range Inputs", description: "Slider controls for numeric values" },
2247
+ { id: "layout", name: "Layout", description: "Structural and container components" }
2248
+ ];
2249
+ var COMPONENTS = [
2250
+ {
2251
+ id: "button",
2252
+ name: "Button",
2253
+ importName: "Button",
2254
+ categoryId: "actions",
2255
+ description: "Interactive button with multiple variants, sizes, and optional icon",
2256
+ hasChildren: true,
2257
+ variants: [
2258
+ { id: "primary-md", label: "Primary", props: { variant: "primary", size: "md" } },
2259
+ { id: "secondary-md", label: "Secondary", props: { variant: "secondary", size: "md" } },
2260
+ { id: "ghost-md", label: "Ghost", props: { variant: "ghost", size: "md" } },
2261
+ { id: "outline-md", label: "Outline", props: { variant: "outline", size: "md" } },
2262
+ { id: "primary-sm", label: "Primary Small", props: { variant: "primary", size: "sm" } },
2263
+ { id: "primary-lg", label: "Primary Large", props: { variant: "primary", size: "lg" } },
2264
+ { id: "icon-left", label: "Icon Left", props: { variant: "primary", size: "md", icon: "add" } },
2265
+ { id: "icon-right", label: "Icon Right", props: { variant: "primary", size: "md", icon: "arrow_forward", iconPosition: "right" } },
2266
+ { id: "icon-only", label: "Icon Only", props: { variant: "ghost", size: "md", icon: "settings" } }
2267
+ ],
2268
+ editableProps: [
2269
+ {
2270
+ name: "variant",
2271
+ label: "Variant",
2272
+ control: "select",
2273
+ options: [
2274
+ { label: "Primary", value: "primary" },
2275
+ { label: "Secondary", value: "secondary" },
2276
+ { label: "Ghost", value: "ghost" },
2277
+ { label: "Outline", value: "outline" }
2278
+ ],
2279
+ defaultValue: "primary"
2280
+ },
2281
+ {
2282
+ name: "size",
2283
+ label: "Size",
2284
+ control: "select",
2285
+ options: [
2286
+ { label: "Small", value: "sm" },
2287
+ { label: "Medium", value: "md" },
2288
+ { label: "Large", value: "lg" }
2289
+ ],
2290
+ defaultValue: "md"
2291
+ },
2292
+ {
2293
+ name: "icon",
2294
+ label: "Icon",
2295
+ control: "text",
2296
+ defaultValue: ""
2297
+ },
2298
+ {
2299
+ name: "iconPosition",
2300
+ label: "Icon Position",
2301
+ control: "select",
2302
+ options: [
2303
+ { label: "Left", value: "left" },
2304
+ { label: "Right", value: "right" }
2305
+ ],
2306
+ defaultValue: "left"
2307
+ },
2308
+ {
2309
+ name: "disabled",
2310
+ label: "Disabled",
2311
+ control: "toggle",
2312
+ defaultValue: false
2313
+ }
2314
+ ]
2315
+ },
2316
+ {
2317
+ id: "text-input",
2318
+ name: "TextInput",
2319
+ importName: "TextInput",
2320
+ categoryId: "form-controls",
2321
+ description: "Text input field with label support",
2322
+ hasChildren: false,
2323
+ variants: [
2324
+ { id: "default", label: "Default", props: { label: "Label", value: "Sample text" } },
2325
+ { id: "empty", label: "Empty", props: { label: "Email", value: "" } },
2326
+ { id: "disabled", label: "Disabled", props: { label: "Disabled", value: "Read only", disabled: true } }
2327
+ ],
2328
+ editableProps: [
2329
+ {
2330
+ name: "label",
2331
+ label: "Label",
2332
+ control: "text",
2333
+ defaultValue: "Label"
2334
+ },
2335
+ {
2336
+ name: "disabled",
2337
+ label: "Disabled",
2338
+ control: "toggle",
2339
+ defaultValue: false
2340
+ }
2341
+ ]
2342
+ },
2343
+ {
2344
+ id: "select",
2345
+ name: "Select",
2346
+ importName: "Select",
2347
+ categoryId: "form-controls",
2348
+ description: "Dropdown selector with options",
2349
+ hasChildren: false,
2350
+ variants: [
2351
+ {
2352
+ id: "default",
2353
+ label: "Default",
2354
+ props: {
2355
+ label: "Choose an option",
2356
+ value: "option-1",
2357
+ options: [
2358
+ { label: "Option 1", value: "option-1" },
2359
+ { label: "Option 2", value: "option-2" },
2360
+ { label: "Option 3", value: "option-3" }
2361
+ ]
2362
+ }
2363
+ },
2364
+ {
2365
+ id: "disabled",
2366
+ label: "Disabled",
2367
+ props: {
2368
+ label: "Disabled select",
2369
+ value: "option-1",
2370
+ disabled: true,
2371
+ options: [{ label: "Option 1", value: "option-1" }]
2372
+ }
2373
+ }
2374
+ ],
2375
+ editableProps: [
2376
+ {
2377
+ name: "label",
2378
+ label: "Label",
2379
+ control: "text",
2380
+ defaultValue: "Choose an option"
2381
+ },
2382
+ {
2383
+ name: "disabled",
2384
+ label: "Disabled",
2385
+ control: "toggle",
2386
+ defaultValue: false
2387
+ }
2388
+ ]
2389
+ },
2390
+ {
2391
+ id: "toggle",
2392
+ name: "Toggle",
2393
+ importName: "Toggle",
2394
+ categoryId: "form-controls",
2395
+ description: "Binary switch component",
2396
+ hasChildren: false,
2397
+ variants: [
2398
+ { id: "on", label: "On", props: { label: "Enabled", value: true } },
2399
+ { id: "off", label: "Off", props: { label: "Disabled", value: false } },
2400
+ { id: "disabled", label: "Disabled", props: { label: "Locked", value: true, disabled: true } }
2401
+ ],
2402
+ editableProps: [
2403
+ {
2404
+ name: "label",
2405
+ label: "Label",
2406
+ control: "text",
2407
+ defaultValue: "Toggle"
2408
+ },
2409
+ {
2410
+ name: "disabled",
2411
+ label: "Disabled",
2412
+ control: "toggle",
2413
+ defaultValue: false
2414
+ }
2415
+ ]
2416
+ },
2417
+ {
2418
+ id: "slider",
2419
+ name: "Slider",
2420
+ importName: "Slider",
2421
+ categoryId: "range-inputs",
2422
+ description: "Numeric range slider",
2423
+ hasChildren: false,
2424
+ variants: [
2425
+ { id: "default", label: "Default", props: { label: "Volume", value: 50, min: 0, max: 100 } },
2426
+ { id: "with-step", label: "With Step", props: { label: "Steps", value: 3, min: 0, max: 10, step: 1, showValue: true } }
2427
+ ],
2428
+ editableProps: [
2429
+ {
2430
+ name: "label",
2431
+ label: "Label",
2432
+ control: "text",
2433
+ defaultValue: "Slider"
2434
+ },
2435
+ {
2436
+ name: "min",
2437
+ label: "Min",
2438
+ control: "number",
2439
+ defaultValue: 0
2440
+ },
2441
+ {
2442
+ name: "max",
2443
+ label: "Max",
2444
+ control: "number",
2445
+ defaultValue: 100
2446
+ },
2447
+ {
2448
+ name: "showValue",
2449
+ label: "Show Value",
2450
+ control: "toggle",
2451
+ defaultValue: false
2452
+ },
2453
+ {
2454
+ name: "disabled",
2455
+ label: "Disabled",
2456
+ control: "toggle",
2457
+ defaultValue: false
2458
+ }
2459
+ ]
2460
+ },
2461
+ {
2462
+ id: "hue-slider",
2463
+ name: "HueSlider",
2464
+ importName: "HueSlider",
2465
+ categoryId: "range-inputs",
2466
+ description: "Specialized slider for hue selection (0-360\xB0)",
2467
+ hasChildren: false,
2468
+ variants: [
2469
+ { id: "default", label: "Default", props: { label: "Hue", value: 180 } },
2470
+ { id: "red", label: "Red", props: { label: "Warm Hue", value: 0 } },
2471
+ { id: "blue", label: "Blue", props: { label: "Cool Hue", value: 240 } }
2472
+ ],
2473
+ editableProps: [
2474
+ {
2475
+ name: "label",
2476
+ label: "Label",
2477
+ control: "text",
2478
+ defaultValue: "Hue"
2479
+ },
2480
+ {
2481
+ name: "showValue",
2482
+ label: "Show Value",
2483
+ control: "toggle",
2484
+ defaultValue: false
2485
+ },
2486
+ {
2487
+ name: "disabled",
2488
+ label: "Disabled",
2489
+ control: "toggle",
2490
+ defaultValue: false
2491
+ }
2492
+ ]
2493
+ },
2494
+ {
2495
+ id: "frame",
2496
+ name: "Frame",
2497
+ importName: "Frame",
2498
+ categoryId: "layout",
2499
+ description: "Foundational layout container with theme, elevation, spacing, and interactivity",
2500
+ hasChildren: true,
2501
+ variants: [
2502
+ { id: "default", label: "Default", props: {} },
2503
+ { id: "primary", label: "Primary", props: { theme: "primary" } },
2504
+ { id: "secondary", label: "Secondary", props: { theme: "secondary" } },
2505
+ { id: "strong", label: "Strong", props: { theme: "strong" } },
2506
+ { id: "deeply-sunken", label: "Deeply Sunken", props: { elevation: -2, radius: "md" } },
2507
+ { id: "sunken", label: "Sunken", props: { elevation: -1 } },
2508
+ { id: "elevated", label: "Elevated", props: { elevation: 1 } },
2509
+ { id: "prominent", label: "Prominent", props: { elevation: 2, radius: "lg" } },
2510
+ { id: "padded", label: "Padded", props: { padding: "lg", gap: "md" } },
2511
+ { id: "horizontal", label: "Horizontal", props: { direction: "horizontal", gap: "md", align: "center" } },
2512
+ { id: "grid-3col", label: "3-Column Grid", props: { layout: "grid", columns: 3, gap: "md" } },
2513
+ { id: "card-like", label: "Card-like", props: { radius: "lg", padding: "lg", bordered: true, elevation: 1 } }
2514
+ ],
2515
+ editableProps: [
2516
+ {
2517
+ name: "theme",
2518
+ label: "Theme",
2519
+ control: "select",
2520
+ options: [
2521
+ { label: "Neutral", value: "neutral" },
2522
+ { label: "Primary", value: "primary" },
2523
+ { label: "Secondary", value: "secondary" },
2524
+ { label: "Strong", value: "strong" }
2525
+ ],
2526
+ defaultValue: "neutral"
2527
+ },
2528
+ {
2529
+ name: "elevation",
2530
+ label: "Elevation",
2531
+ control: "select",
2532
+ options: [
2533
+ { label: "Deeply Sunken (-2)", value: -2 },
2534
+ { label: "Sunken (-1)", value: -1 },
2535
+ { label: "Default (0)", value: 0 },
2536
+ { label: "Elevated (1)", value: 1 },
2537
+ { label: "Prominent (2)", value: 2 }
2538
+ ],
2539
+ defaultValue: 0
2540
+ },
2541
+ {
2542
+ name: "direction",
2543
+ label: "Direction",
2544
+ control: "select",
2545
+ options: [
2546
+ { label: "Vertical", value: "vertical" },
2547
+ { label: "Horizontal", value: "horizontal" }
2548
+ ],
2549
+ defaultValue: "vertical"
2550
+ },
2551
+ {
2552
+ name: "padding",
2553
+ label: "Padding",
2554
+ control: "select",
2555
+ options: [
2556
+ { label: "None", value: "" },
2557
+ { label: "Small", value: "sm" },
2558
+ { label: "Medium", value: "md" },
2559
+ { label: "Large", value: "lg" },
2560
+ { label: "Extra Large", value: "xl" }
2561
+ ],
2562
+ defaultValue: ""
2563
+ },
2564
+ {
2565
+ name: "gap",
2566
+ label: "Gap",
2567
+ control: "select",
2568
+ options: [
2569
+ { label: "None", value: "" },
2570
+ { label: "Small", value: "sm" },
2571
+ { label: "Medium", value: "md" },
2572
+ { label: "Large", value: "lg" }
2573
+ ],
2574
+ defaultValue: ""
2575
+ },
2576
+ {
2577
+ name: "radius",
2578
+ label: "Radius",
2579
+ control: "select",
2580
+ options: [
2581
+ { label: "None", value: "none" },
2582
+ { label: "Small", value: "sm" },
2583
+ { label: "Medium", value: "md" },
2584
+ { label: "Large", value: "lg" },
2585
+ { label: "Pill", value: "pill" }
2586
+ ],
2587
+ defaultValue: "none"
2588
+ },
2589
+ {
2590
+ name: "bordered",
2591
+ label: "Bordered",
2592
+ control: "toggle",
2593
+ defaultValue: false
2594
+ }
2595
+ ]
2596
+ },
2597
+ {
2598
+ id: "card",
2599
+ name: "Card",
2600
+ importName: "Card",
2601
+ categoryId: "layout",
2602
+ description: "Surface container with elevation levels",
2603
+ hasChildren: true,
2604
+ variants: [
2605
+ { id: "elevation-0", label: "Elevation 0", props: { elevation: 0 } },
2606
+ { id: "elevation-1", label: "Elevation 1", props: { elevation: 1 } },
2607
+ { id: "elevation-2", label: "Elevation 2", props: { elevation: 2 } }
2608
+ ],
2609
+ editableProps: [
2610
+ {
2611
+ name: "elevation",
2612
+ label: "Elevation",
2613
+ control: "select",
2614
+ options: [
2615
+ { label: "Level 0", value: 0 },
2616
+ { label: "Level 1", value: 1 },
2617
+ { label: "Level 2", value: 2 }
2618
+ ],
2619
+ defaultValue: 0
2620
+ },
2621
+ {
2622
+ name: "disabled",
2623
+ label: "Disabled",
2624
+ control: "toggle",
2625
+ defaultValue: false
2626
+ }
2627
+ ]
2628
+ }
2629
+ ];
2630
+ function getComponent(id) {
2631
+ return COMPONENTS.find((c) => c.id === id);
2632
+ }
2633
+ function getCategory(id) {
2634
+ return CATEGORIES.find((c) => c.id === id);
2635
+ }
2636
+ function getComponentsByCategory(categoryId) {
2637
+ return COMPONENTS.filter((c) => c.categoryId === categoryId);
2638
+ }
2639
+
2640
+ // src/registry/codegen.ts
2641
+ var HANDLER_PROPS = {
2642
+ button: ["onPress={() => {}}"],
2643
+ "text-input": ["value={value}", "onChangeText={setValue}"],
2644
+ select: ["value={value}", "onValueChange={setValue}"],
2645
+ toggle: ["value={value}", "onValueChange={setValue}"],
2646
+ slider: ["value={value}", "onValueChange={setValue}"],
2647
+ "hue-slider": ["value={value}", "onValueChange={setValue}"]
2648
+ };
2649
+ var CHILDREN_CONTENT = {
2650
+ button: "Button",
2651
+ card: "{/* content */}",
2652
+ frame: "{/* content */}"
2653
+ };
2654
+ function generateComponentCode(component, propOverrides) {
2655
+ const lines = [];
2656
+ lines.push(`import { ${component.importName} } from '@newtonedev/components';`);
2657
+ if (component.id === "select") {
2658
+ lines.push("");
2659
+ lines.push("const options = [");
2660
+ lines.push(" { label: 'Option 1', value: 'option-1' },");
2661
+ lines.push(" { label: 'Option 2', value: 'option-2' },");
2662
+ lines.push(" { label: 'Option 3', value: 'option-3' },");
2663
+ lines.push("];");
2664
+ }
2665
+ lines.push("");
2666
+ const propEntries = [];
2667
+ for (const prop of component.editableProps) {
2668
+ const value = propOverrides[prop.name] ?? prop.defaultValue;
2669
+ if (value === prop.defaultValue) continue;
2670
+ propEntries.push(formatProp(prop.name, value));
2671
+ }
2672
+ const handlers = HANDLER_PROPS[component.id];
2673
+ if (handlers) {
2674
+ for (const handler of handlers) {
2675
+ propEntries.push(handler);
2676
+ }
2677
+ }
2678
+ if (component.id === "select") {
2679
+ propEntries.push("options={options}");
2680
+ }
2681
+ const children = CHILDREN_CONTENT[component.id];
2682
+ const hasProps = propEntries.length > 0;
2683
+ const tag = component.importName;
2684
+ if (component.hasChildren && children) {
2685
+ if (hasProps && propEntries.length <= 3) {
2686
+ lines.push(`<${tag} ${propEntries.join(" ")}>`);
2687
+ lines.push(` ${children}`);
2688
+ lines.push(`</${tag}>`);
2689
+ } else if (hasProps) {
2690
+ lines.push(`<${tag}`);
2691
+ for (const entry of propEntries) {
2692
+ lines.push(` ${entry}`);
2693
+ }
2694
+ lines.push(">");
2695
+ lines.push(` ${children}`);
2696
+ lines.push(`</${tag}>`);
2697
+ } else {
2698
+ lines.push(`<${tag}>`);
2699
+ lines.push(` ${children}`);
2700
+ lines.push(`</${tag}>`);
2701
+ }
2702
+ } else {
2703
+ if (hasProps && propEntries.length <= 3) {
2704
+ lines.push(`<${tag} ${propEntries.join(" ")} />`);
2705
+ } else if (hasProps) {
2706
+ lines.push(`<${tag}`);
2707
+ for (const entry of propEntries) {
2708
+ lines.push(` ${entry}`);
2709
+ }
2710
+ lines.push("/>");
2711
+ } else {
2712
+ lines.push(`<${tag} />`);
2713
+ }
2714
+ }
2715
+ return lines.join("\n");
2716
+ }
2717
+ function formatProp(name, value) {
2718
+ if (typeof value === "string") {
2719
+ return `${name}="${value}"`;
2720
+ }
2721
+ if (typeof value === "boolean") {
2722
+ return value ? name : `${name}={false}`;
2723
+ }
2724
+ if (typeof value === "number") {
2725
+ return `${name}={${value}}`;
2726
+ }
2727
+ return `${name}={${JSON.stringify(value)}}`;
2728
+ }
2729
+
2730
+ // src/fonts/googleFonts.ts
2731
+ var GOOGLE_FONTS = [
2732
+ // Sans-serif
2733
+ { family: "Inter", category: "sans-serif", fallback: "sans-serif" },
2734
+ { family: "Roboto", category: "sans-serif", fallback: "sans-serif" },
2735
+ { family: "Open Sans", category: "sans-serif", fallback: "sans-serif" },
2736
+ { family: "Lato", category: "sans-serif", fallback: "sans-serif" },
2737
+ { family: "Montserrat", category: "sans-serif", fallback: "sans-serif" },
2738
+ { family: "Poppins", category: "sans-serif", fallback: "sans-serif" },
2739
+ { family: "Nunito", category: "sans-serif", fallback: "sans-serif" },
2740
+ { family: "Source Sans 3", category: "sans-serif", fallback: "sans-serif" },
2741
+ { family: "Work Sans", category: "sans-serif", fallback: "sans-serif" },
2742
+ { family: "Raleway", category: "sans-serif", fallback: "sans-serif" },
2743
+ { family: "DM Sans", category: "sans-serif", fallback: "sans-serif" },
2744
+ { family: "Plus Jakarta Sans", category: "sans-serif", fallback: "sans-serif" },
2745
+ { family: "Outfit", category: "sans-serif", fallback: "sans-serif" },
2746
+ { family: "Space Grotesk", category: "sans-serif", fallback: "sans-serif" },
2747
+ { family: "Manrope", category: "sans-serif", fallback: "sans-serif" },
2748
+ // Serif
2749
+ { family: "Playfair Display", category: "serif", fallback: "serif" },
2750
+ { family: "Merriweather", category: "serif", fallback: "serif" },
2751
+ { family: "Lora", category: "serif", fallback: "serif" },
2752
+ { family: "Libre Baskerville", category: "serif", fallback: "serif" },
2753
+ { family: "Source Serif 4", category: "serif", fallback: "serif" },
2754
+ { family: "Bitter", category: "serif", fallback: "serif" },
2755
+ { family: "Cormorant Garamond", category: "serif", fallback: "serif" },
2756
+ { family: "EB Garamond", category: "serif", fallback: "serif" },
2757
+ { family: "Crimson Text", category: "serif", fallback: "serif" },
2758
+ { family: "Noto Serif", category: "serif", fallback: "serif" },
2759
+ // Monospace
2760
+ { family: "Fira Code", category: "monospace", fallback: "monospace" },
2761
+ { family: "JetBrains Mono", category: "monospace", fallback: "monospace" },
2762
+ { family: "Source Code Pro", category: "monospace", fallback: "monospace" },
2763
+ { family: "IBM Plex Mono", category: "monospace", fallback: "monospace" },
2764
+ { family: "Roboto Mono", category: "monospace", fallback: "monospace" },
2765
+ { family: "Space Mono", category: "monospace", fallback: "monospace" },
2766
+ { family: "Ubuntu Mono", category: "monospace", fallback: "monospace" },
2767
+ { family: "Inconsolata", category: "monospace", fallback: "monospace" },
2768
+ // Display
2769
+ { family: "Abril Fatface", category: "display", fallback: "serif" },
2770
+ { family: "Bebas Neue", category: "display", fallback: "sans-serif" },
2771
+ { family: "Oswald", category: "display", fallback: "sans-serif" },
2772
+ { family: "Righteous", category: "display", fallback: "sans-serif" },
2773
+ { family: "Lobster", category: "display", fallback: "cursive" },
2774
+ { family: "Pacifico", category: "display", fallback: "cursive" },
2775
+ { family: "Comfortaa", category: "display", fallback: "sans-serif" },
2776
+ { family: "Fredoka", category: "display", fallback: "sans-serif" }
2777
+ ];
2778
+ var SYSTEM_FONTS = [
2779
+ {
2780
+ family: "system-ui",
2781
+ category: "sans-serif",
2782
+ fallback: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
2783
+ },
2784
+ {
2785
+ family: "ui-monospace",
2786
+ category: "monospace",
2787
+ fallback: "SFMono-Regular, Menlo, Monaco, Consolas, monospace"
2788
+ },
2789
+ {
2790
+ family: "ui-serif",
2791
+ category: "serif",
2792
+ fallback: 'Georgia, "Times New Roman", serif'
2793
+ }
2794
+ ];
716
2795
 
717
- export { Button, Card, DEFAULT_THEME_CONFIG, HueSlider, NewtoneProvider, Select, Slider, TextInput, Toggle, computeTokens, useNewtoneTheme, useTokens };
2796
+ export { AppShell, Button, CATEGORIES, COMPONENTS, Card, ColorScaleSlider, DEFAULT_THEME_CONFIG, Frame, GOOGLE_FONTS, HueSlider, Icon, Navbar, NewtoneProvider, Popover, SYSTEM_FONTS, Select, Sidebar, Slider, TextInput, Toggle, buildGoogleFontsUrl, computeTokens, generateComponentCode, getCategory, getComponent, getComponentsByCategory, isOptionGroup, useFrameContext, useNewtoneTheme, usePopover, useTokens };
718
2797
  //# sourceMappingURL=index.js.map
719
2798
  //# sourceMappingURL=index.js.map