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