@protoflexo/proto-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1184 @@
1
+ // src/FlexProvider.tsx
2
+ import { useEffect } from "react";
3
+ import { Fragment, jsx } from "react/jsx-runtime";
4
+ var FLEX_CSS_ID = "flex-design-tokens";
5
+ function FlexProvider({ children }) {
6
+ useEffect(() => {
7
+ if (document.getElementById(FLEX_CSS_ID)) return;
8
+ const root = getComputedStyle(document.documentElement);
9
+ const hasTokens = root.getPropertyValue("--flex-off-black").trim();
10
+ if (!hasTokens) {
11
+ console.warn(
12
+ "[FlexProvider] Design tokens not detected. Make sure tokens.css is loaded via a <link> tag in your index.html."
13
+ );
14
+ }
15
+ }, []);
16
+ return /* @__PURE__ */ jsx(Fragment, { children });
17
+ }
18
+
19
+ // src/tokens.ts
20
+ var palette = {
21
+ brandPurple: {
22
+ "01": "#2C194D",
23
+ "02": "#6A3DB8",
24
+ "03": "#DDC6F9",
25
+ "04": "#EEE2FC",
26
+ "05": "#E3E1E6"
27
+ },
28
+ offWhite: "#F7F7F7",
29
+ offBlack: "#1D1D1D",
30
+ white: "#FFFFFF",
31
+ black: "#000000",
32
+ grayPurple: {
33
+ "01": "#EDEBF1",
34
+ "02": "#D5D1DB",
35
+ "03": "#C0BACA",
36
+ "04": "#ABA3B8",
37
+ "05": "#958CA6",
38
+ "06": "#807594",
39
+ "07": "#6B5E82",
40
+ "08": "#564771"
41
+ },
42
+ gray: {
43
+ "01": "#F3F3F3",
44
+ "02": "#EEEEEE",
45
+ "03": "#E2E2E2",
46
+ "04": "#BBBBBB",
47
+ "05": "#949494",
48
+ "06": "#737373",
49
+ "07": "#6A6A6A",
50
+ "08": "#535353"
51
+ },
52
+ success: {
53
+ "01": "#01795C",
54
+ "02": "#67AF9D",
55
+ "03": "#B3D7CE",
56
+ "04": "#E6F2EF"
57
+ },
58
+ error: {
59
+ "01": "#7A0800",
60
+ "02": "#E27474",
61
+ "03": "#F0BABA",
62
+ "04": "#FAE8E8"
63
+ },
64
+ warning: {
65
+ "01": "#C8880A",
66
+ "05": "#FFF5E2"
67
+ },
68
+ red: {
69
+ "01": "#CE1818"
70
+ },
71
+ blue: {
72
+ "01": "#00359C",
73
+ "02": "#1C53BA",
74
+ "03": "#6CA3EB",
75
+ "04": "#B1D4FF",
76
+ "05": "#E5EEFF"
77
+ }
78
+ };
79
+ var spacing = {
80
+ 12: 2,
81
+ 18: 3,
82
+ 25: 4,
83
+ 37: 6,
84
+ 50: 8,
85
+ 75: 12,
86
+ 87: 14,
87
+ 100: 16,
88
+ 112: 18,
89
+ 125: 20,
90
+ 150: 24,
91
+ 175: 28,
92
+ 200: 32,
93
+ 250: 40,
94
+ 275: 44,
95
+ 300: 48,
96
+ 325: 52,
97
+ 350: 56,
98
+ 375: 60,
99
+ 400: 64,
100
+ 500: 80,
101
+ 600: 96,
102
+ 725: 116,
103
+ 800: 128,
104
+ 900: 144
105
+ };
106
+ var radius = {
107
+ none: 0,
108
+ xs: 2,
109
+ sm: 4,
110
+ md: 8,
111
+ mlg: 10,
112
+ lg: 12,
113
+ xl: 16,
114
+ xxl: 20,
115
+ xxxl: 24,
116
+ super: 100,
117
+ full: 9999
118
+ };
119
+ var fontFamily = {
120
+ sans: "'ABCDiatype', system-ui, -apple-system, sans-serif",
121
+ serif: "'CooperBT-Medium', Georgia, serif",
122
+ mono: "'ABCDiatypeSemi-Mono', ui-monospace, monospace"
123
+ };
124
+ var fontSize = {
125
+ 250: 10,
126
+ 300: 12,
127
+ 350: 14,
128
+ 400: 16,
129
+ 450: 18,
130
+ 500: 20,
131
+ 550: 22,
132
+ 600: 24,
133
+ 700: 28,
134
+ 800: 32,
135
+ 900: 36,
136
+ 1e3: 40,
137
+ 1200: 48
138
+ };
139
+ var lineHeight = {
140
+ 350: 14,
141
+ 400: 16,
142
+ 450: 18,
143
+ 475: 19,
144
+ 500: 20,
145
+ 550: 22,
146
+ 600: 24,
147
+ 700: 28,
148
+ 850: 34,
149
+ 925: 37,
150
+ 1050: 42,
151
+ 1225: 49
152
+ };
153
+ var fontWeight = {
154
+ regular: 400,
155
+ medium: 500,
156
+ bold: 700
157
+ };
158
+
159
+ // src/hooks/useFlexTheme.ts
160
+ function useFlexTheme() {
161
+ return {
162
+ palette,
163
+ spacing,
164
+ radius,
165
+ fontFamily,
166
+ fontSize,
167
+ lineHeight,
168
+ fontWeight
169
+ };
170
+ }
171
+
172
+ // src/components/Typography.tsx
173
+ import React from "react";
174
+ var typeStyles = {
175
+ "hero.lg": { fontFamily: "var(--flex-font-serif)", fontSize: 40, lineHeight: "42px", fontWeight: 400, letterSpacing: -0.8 },
176
+ "hero.lg.italic": { fontFamily: "var(--flex-font-serif)", fontSize: 40, lineHeight: "42px", fontWeight: 400, letterSpacing: -0.8, fontStyle: "italic" },
177
+ "hero.lg.number": { fontFamily: "var(--flex-font-mono)", fontSize: 48, lineHeight: "49px", fontWeight: 500, letterSpacing: -0.96 },
178
+ "hero.md": { fontFamily: "var(--flex-font-serif)", fontSize: 28, lineHeight: "37px", fontWeight: 400, letterSpacing: -0.56 },
179
+ "hero.md.italic": { fontFamily: "var(--flex-font-serif)", fontSize: 32, lineHeight: "37px", fontWeight: 400, letterSpacing: -0.64, fontStyle: "italic" },
180
+ "hero.sm": { fontFamily: "var(--flex-font-sans)", fontSize: 32, lineHeight: "37px", fontWeight: 700, letterSpacing: 0 },
181
+ "hero.sm.expressive": { fontFamily: "var(--flex-font-serif)", fontSize: 32, lineHeight: "37px", fontWeight: 400, letterSpacing: 0 },
182
+ "hero.sm.expressive.italic": { fontFamily: "var(--flex-font-serif)", fontSize: 32, lineHeight: "37px", fontWeight: 400, letterSpacing: 0, fontStyle: "italic" },
183
+ "hero.sm.number": { fontFamily: "var(--flex-font-mono)", fontSize: 24, lineHeight: "34px", fontWeight: 500, letterSpacing: 0 },
184
+ "page.title": { fontFamily: "var(--flex-font-sans)", fontSize: 22, lineHeight: "34px", fontWeight: 700, letterSpacing: -0.44 },
185
+ "section.title": { fontFamily: "var(--flex-font-sans)", fontSize: 18, lineHeight: "28px", fontWeight: 700, letterSpacing: -0.36 },
186
+ "title.number": { fontFamily: "var(--flex-font-mono)", fontSize: 20, lineHeight: "28px", fontWeight: 500, letterSpacing: -0.4 },
187
+ "header.sm": { fontFamily: "var(--flex-font-sans)", fontSize: 16, lineHeight: "24px", fontWeight: 500, letterSpacing: -0.16 },
188
+ "headline": { fontFamily: "var(--flex-font-serif)", fontSize: 48, lineHeight: "49px", fontWeight: 400, letterSpacing: -0.96 },
189
+ "body": { fontFamily: "var(--flex-font-sans)", fontSize: 16, lineHeight: "21.6px", fontWeight: 400, letterSpacing: -0.16 },
190
+ "body.md": { fontFamily: "var(--flex-font-sans)", fontSize: 14, lineHeight: "18px", fontWeight: 400, letterSpacing: -0.14 },
191
+ "body.sm": { fontFamily: "var(--flex-font-sans)", fontSize: 12, lineHeight: "16px", fontWeight: 400, letterSpacing: -0.12 },
192
+ "body.xs": { fontFamily: "var(--flex-font-sans)", fontSize: 10, lineHeight: "14px", fontWeight: 400, letterSpacing: -0.1 },
193
+ "body.bold": { fontFamily: "var(--flex-font-sans)", fontSize: 16, lineHeight: "22px", fontWeight: 700, letterSpacing: -0.16 },
194
+ "body.md.bold": { fontFamily: "var(--flex-font-sans)", fontSize: 14, lineHeight: "18px", fontWeight: 700, letterSpacing: -0.14 },
195
+ "body.input": { fontFamily: "var(--flex-font-sans)", fontSize: 16, lineHeight: "21.6px", fontWeight: 400, letterSpacing: 0 },
196
+ "body.hyperlink": { fontFamily: "var(--flex-font-sans)", fontSize: 16, lineHeight: "21.6px", fontWeight: 500, letterSpacing: -0.16, textDecoration: "underline" },
197
+ "body.md.hyperlink": { fontFamily: "var(--flex-font-sans)", fontSize: 14, lineHeight: "18px", fontWeight: 400, letterSpacing: -0.14, textDecoration: "underline" },
198
+ "label": { fontFamily: "var(--flex-font-sans)", fontSize: 16, lineHeight: "21.6px", fontWeight: 500, letterSpacing: 0 },
199
+ "label.sm": { fontFamily: "var(--flex-font-sans)", fontSize: 12, lineHeight: "16.2px", fontWeight: 400, letterSpacing: -0.12 },
200
+ "label.sm.bold": { fontFamily: "var(--flex-font-sans)", fontSize: 12, lineHeight: "16.2px", fontWeight: 700, letterSpacing: -0.12 },
201
+ "label.sm.hyperlink": { fontFamily: "var(--flex-font-sans)", fontSize: 12, lineHeight: "16.2px", fontWeight: 400, letterSpacing: -0.12, textDecoration: "underline" },
202
+ "button": { fontFamily: "var(--flex-font-sans)", fontSize: 16, lineHeight: "21.6px", fontWeight: 500, letterSpacing: -0.16 },
203
+ "button.sm": { fontFamily: "var(--flex-font-sans)", fontSize: 14, lineHeight: "19px", fontWeight: 500, letterSpacing: -0.14 },
204
+ "number": { fontFamily: "var(--flex-font-mono)", fontSize: 16, lineHeight: "21.6px", fontWeight: 400, letterSpacing: -0.16 },
205
+ "number.bold": { fontFamily: "var(--flex-font-mono)", fontSize: 16, lineHeight: "21.6px", fontWeight: 700, letterSpacing: -0.16 },
206
+ "caption": { fontFamily: "var(--flex-font-sans)", fontSize: 10, lineHeight: "13.5px", fontWeight: 400, letterSpacing: -0.1 },
207
+ "caption.hyperlink": { fontFamily: "var(--flex-font-sans)", fontSize: 10, lineHeight: "13.5px", fontWeight: 400, letterSpacing: -0.1, textDecoration: "underline" },
208
+ "overline": { fontFamily: "var(--flex-font-sans)", fontSize: 10, lineHeight: "13.5px", fontWeight: 400, letterSpacing: 1, textTransform: "uppercase" }
209
+ };
210
+ var defaultElement = {
211
+ "hero.lg": "h1",
212
+ "hero.md": "h1",
213
+ "hero.sm": "h2",
214
+ "headline": "h1",
215
+ "page.title": "h1",
216
+ "section.title": "h2",
217
+ "header.sm": "h3",
218
+ "label": "label",
219
+ "label.sm": "label",
220
+ "label.sm.bold": "label",
221
+ "caption": "span",
222
+ "overline": "span"
223
+ };
224
+ function Typography({
225
+ type = "body",
226
+ color,
227
+ weight,
228
+ align,
229
+ as,
230
+ style,
231
+ children,
232
+ className
233
+ }) {
234
+ const Tag = as ?? defaultElement[type] ?? "span";
235
+ const baseStyle = typeStyles[type];
236
+ const mergedStyle = {
237
+ ...baseStyle,
238
+ color: color ?? "var(--flex-text-default)",
239
+ margin: 0,
240
+ ...weight != null && { fontWeight: weight },
241
+ ...align != null && { textAlign: align },
242
+ ...style
243
+ };
244
+ return React.createElement(Tag, { style: mergedStyle, className }, children);
245
+ }
246
+
247
+ // src/components/Divider.tsx
248
+ import { jsx as jsx2 } from "react/jsx-runtime";
249
+ function Divider({ color, spacing: spacing2 = 0, style }) {
250
+ return /* @__PURE__ */ jsx2(
251
+ "div",
252
+ {
253
+ style: {
254
+ height: 1,
255
+ backgroundColor: color ?? "var(--flex-border-subdued)",
256
+ marginTop: spacing2,
257
+ marginBottom: spacing2,
258
+ ...style
259
+ }
260
+ }
261
+ );
262
+ }
263
+
264
+ // src/components/Button.tsx
265
+ import { Fragment as Fragment2, jsx as jsx3, jsxs } from "react/jsx-runtime";
266
+ var variantStyles = {
267
+ primary: { bg: "var(--flex-action-primary)", text: "var(--flex-off-white)" },
268
+ secondary: { bg: "var(--flex-action-secondary)", text: "var(--flex-text-link)" },
269
+ outline: { bg: "var(--flex-white)", text: "var(--flex-brand-purple-01)", border: "var(--flex-brand-purple-01)" },
270
+ minimal: { bg: "transparent", text: "var(--flex-brand-purple-01)" },
271
+ destructive: { bg: "var(--flex-red-01)", text: "var(--flex-off-white)" }
272
+ };
273
+ function Button({
274
+ variant = "primary",
275
+ size = "default",
276
+ disabled = false,
277
+ loading = false,
278
+ icon,
279
+ fullWidth = false,
280
+ onClick,
281
+ children,
282
+ style,
283
+ type = "button"
284
+ }) {
285
+ const isDisabled = disabled || loading;
286
+ const v = variantStyles[variant] ?? variantStyles.primary;
287
+ const baseStyle = {
288
+ display: "inline-flex",
289
+ alignItems: "center",
290
+ justifyContent: "center",
291
+ gap: 6,
292
+ borderRadius: 9999,
293
+ border: variant === "outline" ? `1px solid ${v.border}` : "none",
294
+ backgroundColor: v.bg,
295
+ color: v.text,
296
+ fontFamily: "var(--flex-font-sans)",
297
+ fontWeight: 500,
298
+ fontSize: size === "sm" ? 14 : 16,
299
+ lineHeight: size === "sm" ? "19px" : "21.6px",
300
+ letterSpacing: size === "sm" ? -0.14 : -0.16,
301
+ height: size === "sm" ? 32 : 48,
302
+ minHeight: size === "sm" ? 32 : 48,
303
+ paddingLeft: size === "sm" ? 16 : 24,
304
+ paddingRight: size === "sm" ? 16 : 24,
305
+ cursor: isDisabled ? "not-allowed" : "pointer",
306
+ opacity: isDisabled ? 0.5 : 1,
307
+ width: fullWidth ? "100%" : void 0,
308
+ transition: "background-color 150ms ease, opacity 150ms ease",
309
+ ...style
310
+ };
311
+ if (isDisabled && variant !== "minimal" && variant !== "outline") {
312
+ baseStyle.backgroundColor = "var(--flex-gray-03)";
313
+ }
314
+ return /* @__PURE__ */ jsx3(
315
+ "button",
316
+ {
317
+ type,
318
+ onClick: isDisabled ? void 0 : onClick,
319
+ disabled: isDisabled,
320
+ style: baseStyle,
321
+ children: loading ? /* @__PURE__ */ jsx3(
322
+ "span",
323
+ {
324
+ style: {
325
+ width: 16,
326
+ height: 16,
327
+ border: "2px solid currentColor",
328
+ borderTopColor: "transparent",
329
+ borderRadius: "50%",
330
+ animation: "flex-spin 0.6s linear infinite"
331
+ }
332
+ }
333
+ ) : /* @__PURE__ */ jsxs(Fragment2, { children: [
334
+ icon,
335
+ children
336
+ ] })
337
+ }
338
+ );
339
+ }
340
+
341
+ // src/components/TextField.tsx
342
+ import { useState } from "react";
343
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
344
+ function TextField({
345
+ label,
346
+ value,
347
+ placeholder,
348
+ helperText,
349
+ error,
350
+ disabled = false,
351
+ type = "text",
352
+ onChange,
353
+ onFocus,
354
+ onBlur,
355
+ style,
356
+ inputStyle
357
+ }) {
358
+ const [hasFocus, setHasFocus] = useState(false);
359
+ const borderColor = error ? "var(--flex-border-critical)" : hasFocus ? "var(--flex-border-pressed)" : "var(--flex-border-default)";
360
+ const borderWidth = hasFocus && !error ? 2 : 1;
361
+ return /* @__PURE__ */ jsxs2("div", { style, children: [
362
+ label && /* @__PURE__ */ jsx4(
363
+ "label",
364
+ {
365
+ style: {
366
+ display: "block",
367
+ fontFamily: "var(--flex-font-sans)",
368
+ fontSize: 14,
369
+ lineHeight: "18px",
370
+ fontWeight: 400,
371
+ letterSpacing: -0.14,
372
+ color: "var(--flex-text-default)",
373
+ marginBottom: 6
374
+ },
375
+ children: label
376
+ }
377
+ ),
378
+ /* @__PURE__ */ jsx4(
379
+ "input",
380
+ {
381
+ type,
382
+ value,
383
+ placeholder,
384
+ disabled,
385
+ onChange: (e) => onChange?.(e.target.value),
386
+ onFocus: () => {
387
+ setHasFocus(true);
388
+ onFocus?.();
389
+ },
390
+ onBlur: () => {
391
+ setHasFocus(false);
392
+ onBlur?.();
393
+ },
394
+ style: {
395
+ width: "100%",
396
+ boxSizing: "border-box",
397
+ fontFamily: "var(--flex-font-sans)",
398
+ fontSize: 16,
399
+ lineHeight: "21.6px",
400
+ fontWeight: 400,
401
+ letterSpacing: 0,
402
+ color: disabled ? "var(--flex-text-disabled)" : "var(--flex-text-default)",
403
+ backgroundColor: disabled ? "var(--flex-surface-disabled)" : "var(--flex-white)",
404
+ border: `${borderWidth}px solid ${borderColor}`,
405
+ borderRadius: 12,
406
+ paddingLeft: 12,
407
+ paddingRight: 12,
408
+ paddingTop: 16,
409
+ paddingBottom: 16,
410
+ outline: "none",
411
+ transition: "border-color 150ms ease",
412
+ ...inputStyle
413
+ }
414
+ }
415
+ ),
416
+ (helperText || error) && /* @__PURE__ */ jsx4(
417
+ "div",
418
+ {
419
+ style: {
420
+ fontFamily: "var(--flex-font-sans)",
421
+ fontSize: 12,
422
+ lineHeight: "16px",
423
+ fontWeight: 400,
424
+ letterSpacing: -0.12,
425
+ color: error ? "var(--flex-text-critical)" : "var(--flex-text-subdued)",
426
+ marginTop: 4
427
+ },
428
+ children: error || helperText
429
+ }
430
+ )
431
+ ] });
432
+ }
433
+
434
+ // src/components/Checkbox.tsx
435
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
436
+ function Checkbox({ checked = false, onChange, label, disabled = false, style }) {
437
+ return /* @__PURE__ */ jsxs3(
438
+ "label",
439
+ {
440
+ style: {
441
+ display: "inline-flex",
442
+ alignItems: "center",
443
+ gap: 8,
444
+ cursor: disabled ? "not-allowed" : "pointer",
445
+ opacity: disabled ? 0.5 : 1,
446
+ ...style
447
+ },
448
+ children: [
449
+ /* @__PURE__ */ jsx5(
450
+ "div",
451
+ {
452
+ onClick: disabled ? void 0 : () => onChange?.(!checked),
453
+ style: {
454
+ width: 20,
455
+ height: 20,
456
+ borderRadius: 4,
457
+ border: checked ? "none" : "2px solid var(--flex-border-default)",
458
+ backgroundColor: checked ? "var(--flex-brand-purple-02)" : "var(--flex-white)",
459
+ display: "flex",
460
+ alignItems: "center",
461
+ justifyContent: "center",
462
+ transition: "background-color 150ms ease, border-color 150ms ease",
463
+ flexShrink: 0,
464
+ cursor: "inherit"
465
+ },
466
+ children: checked && /* @__PURE__ */ jsx5("svg", { width: "12", height: "10", viewBox: "0 0 12 10", fill: "none", children: /* @__PURE__ */ jsx5("path", { d: "M1 5L4.5 8.5L11 1.5", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
467
+ }
468
+ ),
469
+ label && /* @__PURE__ */ jsx5(
470
+ "span",
471
+ {
472
+ style: {
473
+ fontFamily: "var(--flex-font-sans)",
474
+ fontSize: 16,
475
+ lineHeight: "21.6px",
476
+ fontWeight: 400,
477
+ letterSpacing: -0.16,
478
+ color: "var(--flex-text-default)"
479
+ },
480
+ children: label
481
+ }
482
+ )
483
+ ]
484
+ }
485
+ );
486
+ }
487
+
488
+ // src/components/Radio.tsx
489
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
490
+ function Radio({ selected = false, onSelect, label, disabled = false, style }) {
491
+ return /* @__PURE__ */ jsxs4(
492
+ "label",
493
+ {
494
+ style: {
495
+ display: "inline-flex",
496
+ alignItems: "center",
497
+ gap: 8,
498
+ cursor: disabled ? "not-allowed" : "pointer",
499
+ opacity: disabled ? 0.5 : 1,
500
+ ...style
501
+ },
502
+ children: [
503
+ /* @__PURE__ */ jsx6(
504
+ "div",
505
+ {
506
+ onClick: disabled ? void 0 : onSelect,
507
+ style: {
508
+ width: 20,
509
+ height: 20,
510
+ borderRadius: "50%",
511
+ border: `2px solid ${selected ? "var(--flex-brand-purple-02)" : "var(--flex-border-default)"}`,
512
+ backgroundColor: "var(--flex-white)",
513
+ display: "flex",
514
+ alignItems: "center",
515
+ justifyContent: "center",
516
+ transition: "border-color 150ms ease",
517
+ flexShrink: 0,
518
+ cursor: "inherit"
519
+ },
520
+ children: selected && /* @__PURE__ */ jsx6(
521
+ "div",
522
+ {
523
+ style: {
524
+ width: 10,
525
+ height: 10,
526
+ borderRadius: "50%",
527
+ backgroundColor: "var(--flex-brand-purple-02)"
528
+ }
529
+ }
530
+ )
531
+ }
532
+ ),
533
+ label && /* @__PURE__ */ jsx6(
534
+ "span",
535
+ {
536
+ style: {
537
+ fontFamily: "var(--flex-font-sans)",
538
+ fontSize: 16,
539
+ lineHeight: "21.6px",
540
+ fontWeight: 400,
541
+ letterSpacing: -0.16,
542
+ color: "var(--flex-text-default)"
543
+ },
544
+ children: label
545
+ }
546
+ )
547
+ ]
548
+ }
549
+ );
550
+ }
551
+
552
+ // src/components/Toggle.tsx
553
+ import { jsx as jsx7 } from "react/jsx-runtime";
554
+ function Toggle({ checked = false, onChange, disabled = false, style }) {
555
+ return /* @__PURE__ */ jsx7(
556
+ "div",
557
+ {
558
+ onClick: disabled ? void 0 : () => onChange?.(!checked),
559
+ role: "switch",
560
+ "aria-checked": checked,
561
+ style: {
562
+ position: "relative",
563
+ width: 39,
564
+ height: 24,
565
+ borderRadius: 12,
566
+ backgroundColor: checked ? "var(--flex-brand-purple-02)" : "var(--flex-gray-purple-02)",
567
+ cursor: disabled ? "not-allowed" : "pointer",
568
+ opacity: disabled ? 0.5 : 1,
569
+ transition: "background-color 200ms ease",
570
+ flexShrink: 0,
571
+ ...style
572
+ },
573
+ children: /* @__PURE__ */ jsx7(
574
+ "div",
575
+ {
576
+ style: {
577
+ position: "absolute",
578
+ top: 3,
579
+ left: checked ? 18 : 3,
580
+ width: 18,
581
+ height: 18,
582
+ borderRadius: "50%",
583
+ backgroundColor: "var(--flex-white)",
584
+ boxShadow: "0 1px 3px rgba(0,0,0,0.2)",
585
+ transition: "left 200ms ease"
586
+ }
587
+ }
588
+ )
589
+ }
590
+ );
591
+ }
592
+
593
+ // src/components/Card.tsx
594
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
595
+ var variantBg = {
596
+ default: "var(--flex-surface-default)",
597
+ subdued: "var(--flex-surface-subdued)",
598
+ brand: "var(--flex-surface-brand)",
599
+ elevated: "var(--flex-surface-default)"
600
+ };
601
+ function Card({
602
+ variant = "default",
603
+ selected = false,
604
+ onClick,
605
+ header,
606
+ children,
607
+ style
608
+ }) {
609
+ return /* @__PURE__ */ jsxs5(
610
+ "div",
611
+ {
612
+ onClick,
613
+ style: {
614
+ padding: 16,
615
+ borderRadius: 10,
616
+ backgroundColor: variantBg[variant] ?? variantBg.default,
617
+ border: selected ? "2px solid var(--flex-brand-purple-02)" : "1px solid var(--flex-border-subdued)",
618
+ boxShadow: variant === "elevated" ? "0 10px 30px rgba(14, 6, 34, 0.1)" : void 0,
619
+ cursor: onClick ? "pointer" : void 0,
620
+ transition: "border-color 150ms ease, box-shadow 150ms ease",
621
+ ...style
622
+ },
623
+ children: [
624
+ header && /* @__PURE__ */ jsx8(
625
+ "div",
626
+ {
627
+ style: {
628
+ paddingBottom: 16,
629
+ borderBottom: "1px solid var(--flex-gray-purple-02)",
630
+ marginBottom: 16
631
+ },
632
+ children: header
633
+ }
634
+ ),
635
+ children
636
+ ]
637
+ }
638
+ );
639
+ }
640
+
641
+ // src/components/ListItem.tsx
642
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
643
+ function ListItem({
644
+ title,
645
+ subtitle,
646
+ leading,
647
+ trailing,
648
+ onClick,
649
+ divider = true,
650
+ style
651
+ }) {
652
+ return /* @__PURE__ */ jsxs6(
653
+ "div",
654
+ {
655
+ onClick,
656
+ style: {
657
+ display: "flex",
658
+ alignItems: "center",
659
+ gap: 12,
660
+ padding: "12px 0",
661
+ borderBottom: divider ? "1px solid var(--flex-border-subdued)" : void 0,
662
+ cursor: onClick ? "pointer" : void 0,
663
+ ...style
664
+ },
665
+ children: [
666
+ leading && /* @__PURE__ */ jsx9("div", { style: { flexShrink: 0 }, children: leading }),
667
+ /* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
668
+ /* @__PURE__ */ jsx9(
669
+ "div",
670
+ {
671
+ style: {
672
+ fontFamily: "var(--flex-font-sans)",
673
+ fontSize: 16,
674
+ lineHeight: "21.6px",
675
+ fontWeight: 400,
676
+ letterSpacing: -0.16,
677
+ color: "var(--flex-text-default)",
678
+ overflow: "hidden",
679
+ textOverflow: "ellipsis",
680
+ whiteSpace: "nowrap"
681
+ },
682
+ children: title
683
+ }
684
+ ),
685
+ subtitle && /* @__PURE__ */ jsx9(
686
+ "div",
687
+ {
688
+ style: {
689
+ fontFamily: "var(--flex-font-sans)",
690
+ fontSize: 14,
691
+ lineHeight: "18px",
692
+ fontWeight: 400,
693
+ letterSpacing: -0.14,
694
+ color: "var(--flex-text-subdued)",
695
+ marginTop: 2,
696
+ overflow: "hidden",
697
+ textOverflow: "ellipsis",
698
+ whiteSpace: "nowrap"
699
+ },
700
+ children: subtitle
701
+ }
702
+ )
703
+ ] }),
704
+ trailing && /* @__PURE__ */ jsx9("div", { style: { flexShrink: 0 }, children: trailing })
705
+ ]
706
+ }
707
+ );
708
+ }
709
+
710
+ // src/components/Page.tsx
711
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
712
+ function Page({ children, stickyFooter, backgroundColor, style }) {
713
+ return /* @__PURE__ */ jsxs7(
714
+ "div",
715
+ {
716
+ style: {
717
+ display: "flex",
718
+ flexDirection: "column",
719
+ minHeight: "100%",
720
+ backgroundColor: backgroundColor ?? "var(--flex-surface-subdued)",
721
+ ...style
722
+ },
723
+ children: [
724
+ /* @__PURE__ */ jsx10("div", { style: { flex: 1, overflow: "auto" }, children }),
725
+ stickyFooter && /* @__PURE__ */ jsx10(
726
+ "div",
727
+ {
728
+ style: {
729
+ position: "sticky",
730
+ bottom: 0,
731
+ padding: 16,
732
+ backgroundColor: backgroundColor ?? "var(--flex-surface-subdued)",
733
+ borderTop: "1px solid var(--flex-border-subdued)"
734
+ },
735
+ children: stickyFooter
736
+ }
737
+ )
738
+ ]
739
+ }
740
+ );
741
+ }
742
+
743
+ // src/components/Modal.tsx
744
+ import { useEffect as useEffect2 } from "react";
745
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
746
+ function Modal({ open, onClose, title, children, style }) {
747
+ useEffect2(() => {
748
+ if (!open) return;
749
+ const handleKey = (e) => {
750
+ if (e.key === "Escape") onClose();
751
+ };
752
+ window.addEventListener("keydown", handleKey);
753
+ return () => window.removeEventListener("keydown", handleKey);
754
+ }, [open, onClose]);
755
+ if (!open) return null;
756
+ return /* @__PURE__ */ jsxs8(
757
+ "div",
758
+ {
759
+ style: {
760
+ position: "fixed",
761
+ inset: 0,
762
+ zIndex: 1e3,
763
+ display: "flex",
764
+ flexDirection: "column",
765
+ backgroundColor: "var(--flex-surface-default)"
766
+ },
767
+ children: [
768
+ /* @__PURE__ */ jsxs8(
769
+ "div",
770
+ {
771
+ style: {
772
+ display: "flex",
773
+ alignItems: "center",
774
+ justifyContent: "space-between",
775
+ padding: "12px 16px",
776
+ borderBottom: "1px solid var(--flex-border-subdued)"
777
+ },
778
+ children: [
779
+ /* @__PURE__ */ jsx11(
780
+ "div",
781
+ {
782
+ style: {
783
+ fontFamily: "var(--flex-font-sans)",
784
+ fontSize: 18,
785
+ lineHeight: "28px",
786
+ fontWeight: 700,
787
+ letterSpacing: -0.36,
788
+ color: "var(--flex-text-default)"
789
+ },
790
+ children: title
791
+ }
792
+ ),
793
+ /* @__PURE__ */ jsx11(
794
+ "button",
795
+ {
796
+ onClick: onClose,
797
+ style: {
798
+ background: "none",
799
+ border: "none",
800
+ padding: 8,
801
+ cursor: "pointer",
802
+ color: "var(--flex-icon-subdued)",
803
+ fontSize: 20,
804
+ lineHeight: 1
805
+ },
806
+ "aria-label": "Close",
807
+ children: "\u2715"
808
+ }
809
+ )
810
+ ]
811
+ }
812
+ ),
813
+ /* @__PURE__ */ jsx11("div", { style: { flex: 1, overflow: "auto", ...style }, children })
814
+ ]
815
+ }
816
+ );
817
+ }
818
+
819
+ // src/components/Select.tsx
820
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
821
+ function Select({
822
+ label,
823
+ value,
824
+ options,
825
+ onChange,
826
+ placeholder,
827
+ error,
828
+ disabled = false,
829
+ style
830
+ }) {
831
+ const borderColor = error ? "var(--flex-border-critical)" : "var(--flex-border-default)";
832
+ return /* @__PURE__ */ jsxs9("div", { style, children: [
833
+ label && /* @__PURE__ */ jsx12(
834
+ "label",
835
+ {
836
+ style: {
837
+ display: "block",
838
+ fontFamily: "var(--flex-font-sans)",
839
+ fontSize: 14,
840
+ lineHeight: "18px",
841
+ fontWeight: 400,
842
+ letterSpacing: -0.14,
843
+ color: "var(--flex-text-default)",
844
+ marginBottom: 6
845
+ },
846
+ children: label
847
+ }
848
+ ),
849
+ /* @__PURE__ */ jsxs9(
850
+ "select",
851
+ {
852
+ value,
853
+ onChange: (e) => onChange?.(e.target.value),
854
+ disabled,
855
+ style: {
856
+ width: "100%",
857
+ boxSizing: "border-box",
858
+ fontFamily: "var(--flex-font-sans)",
859
+ fontSize: 16,
860
+ lineHeight: "21.6px",
861
+ fontWeight: 400,
862
+ color: disabled ? "var(--flex-text-disabled)" : "var(--flex-text-default)",
863
+ backgroundColor: disabled ? "var(--flex-surface-disabled)" : "var(--flex-white)",
864
+ border: `1px solid ${borderColor}`,
865
+ borderRadius: 12,
866
+ paddingLeft: 12,
867
+ paddingRight: 32,
868
+ paddingTop: 16,
869
+ paddingBottom: 16,
870
+ outline: "none",
871
+ appearance: "none",
872
+ backgroundImage: `url("data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%236A6A6A' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")`,
873
+ backgroundRepeat: "no-repeat",
874
+ backgroundPosition: "right 12px center",
875
+ cursor: disabled ? "not-allowed" : "pointer"
876
+ },
877
+ children: [
878
+ placeholder && /* @__PURE__ */ jsx12("option", { value: "", disabled: true, children: placeholder }),
879
+ options.map((opt) => /* @__PURE__ */ jsx12("option", { value: opt.value, children: opt.label }, opt.value))
880
+ ]
881
+ }
882
+ ),
883
+ error && /* @__PURE__ */ jsx12(
884
+ "div",
885
+ {
886
+ style: {
887
+ fontFamily: "var(--flex-font-sans)",
888
+ fontSize: 12,
889
+ lineHeight: "16px",
890
+ color: "var(--flex-text-critical)",
891
+ marginTop: 4
892
+ },
893
+ children: error
894
+ }
895
+ )
896
+ ] });
897
+ }
898
+
899
+ // src/components/Toast.tsx
900
+ import { useState as useState2, useCallback, useEffect as useEffect3 } from "react";
901
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
902
+ var variantStyles2 = {
903
+ brand: { bg: "var(--flex-surface-brand)", icon: "var(--flex-icon-brand)", border: "var(--flex-brand-purple-03)" },
904
+ success: { bg: "var(--flex-surface-success)", icon: "var(--flex-icon-success)", border: "var(--flex-border-success-subdued)" },
905
+ warning: { bg: "var(--flex-surface-warning)", icon: "var(--flex-icon-warning)", border: "var(--flex-border-warning)" },
906
+ error: { bg: "var(--flex-surface-critical)", icon: "var(--flex-icon-critical)", border: "var(--flex-border-critical-subdued)" }
907
+ };
908
+ function Toast({ title, description, variant = "brand", visible, onDismiss, duration = 4e3 }) {
909
+ const v = variantStyles2[variant] ?? variantStyles2.brand;
910
+ useEffect3(() => {
911
+ if (!visible || !onDismiss || !duration) return;
912
+ const timer = setTimeout(onDismiss, duration);
913
+ return () => clearTimeout(timer);
914
+ }, [visible, onDismiss, duration]);
915
+ if (!visible) return null;
916
+ return /* @__PURE__ */ jsxs10(
917
+ "div",
918
+ {
919
+ style: {
920
+ position: "fixed",
921
+ top: 16,
922
+ left: 16,
923
+ right: 16,
924
+ zIndex: 2e3,
925
+ display: "flex",
926
+ alignItems: "flex-start",
927
+ gap: 12,
928
+ padding: 16,
929
+ borderRadius: 12,
930
+ backgroundColor: v.bg,
931
+ border: `1px solid ${v.border}`,
932
+ animation: "flex-slide-down 300ms ease"
933
+ },
934
+ children: [
935
+ /* @__PURE__ */ jsxs10("div", { style: { flex: 1 }, children: [
936
+ /* @__PURE__ */ jsx13(
937
+ "div",
938
+ {
939
+ style: {
940
+ fontFamily: "var(--flex-font-sans)",
941
+ fontSize: 16,
942
+ lineHeight: "21.6px",
943
+ fontWeight: 500,
944
+ letterSpacing: 0,
945
+ color: "var(--flex-text-default)"
946
+ },
947
+ children: title
948
+ }
949
+ ),
950
+ description && /* @__PURE__ */ jsx13(
951
+ "div",
952
+ {
953
+ style: {
954
+ fontFamily: "var(--flex-font-sans)",
955
+ fontSize: 14,
956
+ lineHeight: "18px",
957
+ fontWeight: 400,
958
+ letterSpacing: -0.14,
959
+ color: "var(--flex-text-subdued)",
960
+ marginTop: 4
961
+ },
962
+ children: description
963
+ }
964
+ )
965
+ ] }),
966
+ onDismiss && /* @__PURE__ */ jsx13(
967
+ "button",
968
+ {
969
+ onClick: onDismiss,
970
+ style: {
971
+ background: "none",
972
+ border: "none",
973
+ padding: 4,
974
+ cursor: "pointer",
975
+ color: "var(--flex-icon-subdued)",
976
+ fontSize: 16,
977
+ lineHeight: 1,
978
+ flexShrink: 0
979
+ },
980
+ "aria-label": "Dismiss",
981
+ children: "\u2715"
982
+ }
983
+ )
984
+ ]
985
+ }
986
+ );
987
+ }
988
+ function useToast() {
989
+ const [toast, setToast] = useState2(null);
990
+ const show = useCallback((props) => {
991
+ setToast(props);
992
+ }, []);
993
+ const dismiss = useCallback(() => {
994
+ setToast(null);
995
+ }, []);
996
+ return {
997
+ toastProps: {
998
+ ...toast ?? { title: "" },
999
+ visible: toast !== null,
1000
+ onDismiss: dismiss
1001
+ },
1002
+ show,
1003
+ dismiss
1004
+ };
1005
+ }
1006
+
1007
+ // src/components/Skeleton.tsx
1008
+ import { jsx as jsx14 } from "react/jsx-runtime";
1009
+ function Skeleton({
1010
+ width = "100%",
1011
+ height = 16,
1012
+ borderRadius = 8,
1013
+ style
1014
+ }) {
1015
+ return /* @__PURE__ */ jsx14(
1016
+ "div",
1017
+ {
1018
+ style: {
1019
+ width,
1020
+ height,
1021
+ borderRadius,
1022
+ backgroundColor: "var(--flex-gray-purple-01)",
1023
+ animation: "flex-pulse 1.5s ease-in-out infinite",
1024
+ ...style
1025
+ }
1026
+ }
1027
+ );
1028
+ }
1029
+
1030
+ // src/components/ProgressBar.tsx
1031
+ import { jsx as jsx15 } from "react/jsx-runtime";
1032
+ function ProgressBar({
1033
+ progress,
1034
+ width = "100%",
1035
+ height = 8,
1036
+ style
1037
+ }) {
1038
+ const clampedProgress = Math.max(0, Math.min(1, progress));
1039
+ const isComplete = clampedProgress >= 1;
1040
+ return /* @__PURE__ */ jsx15(
1041
+ "div",
1042
+ {
1043
+ style: {
1044
+ width,
1045
+ height,
1046
+ borderRadius: height / 2,
1047
+ backgroundColor: "var(--flex-cloud)",
1048
+ overflow: "hidden",
1049
+ ...style
1050
+ },
1051
+ children: /* @__PURE__ */ jsx15(
1052
+ "div",
1053
+ {
1054
+ style: {
1055
+ width: `${clampedProgress * 100}%`,
1056
+ height: "100%",
1057
+ borderRadius: height / 2,
1058
+ backgroundColor: isComplete ? "var(--flex-success-01)" : "var(--flex-brand-purple-02)",
1059
+ transition: "width 300ms ease, background-color 300ms ease"
1060
+ }
1061
+ }
1062
+ )
1063
+ }
1064
+ );
1065
+ }
1066
+
1067
+ // src/components/Accordion.tsx
1068
+ import { useState as useState3 } from "react";
1069
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
1070
+ function Accordion({
1071
+ title,
1072
+ children,
1073
+ defaultOpen = false,
1074
+ onChange,
1075
+ style
1076
+ }) {
1077
+ const [open, setOpen] = useState3(defaultOpen);
1078
+ const toggle = () => {
1079
+ const next = !open;
1080
+ setOpen(next);
1081
+ onChange?.(next);
1082
+ };
1083
+ return /* @__PURE__ */ jsxs11(
1084
+ "div",
1085
+ {
1086
+ style: {
1087
+ borderRadius: 10,
1088
+ backgroundColor: "var(--flex-surface-default)",
1089
+ border: "1px solid var(--flex-border-subdued)",
1090
+ overflow: "hidden",
1091
+ ...style
1092
+ },
1093
+ children: [
1094
+ /* @__PURE__ */ jsxs11(
1095
+ "button",
1096
+ {
1097
+ onClick: toggle,
1098
+ style: {
1099
+ width: "100%",
1100
+ display: "flex",
1101
+ alignItems: "center",
1102
+ justifyContent: "space-between",
1103
+ padding: 16,
1104
+ background: "none",
1105
+ border: "none",
1106
+ cursor: "pointer",
1107
+ fontFamily: "var(--flex-font-sans)",
1108
+ fontSize: 16,
1109
+ lineHeight: "21.6px",
1110
+ fontWeight: 500,
1111
+ letterSpacing: 0,
1112
+ color: "var(--flex-text-default)",
1113
+ textAlign: "left"
1114
+ },
1115
+ children: [
1116
+ title,
1117
+ /* @__PURE__ */ jsx16(
1118
+ "svg",
1119
+ {
1120
+ width: "16",
1121
+ height: "16",
1122
+ viewBox: "0 0 16 16",
1123
+ fill: "none",
1124
+ style: {
1125
+ flexShrink: 0,
1126
+ transform: open ? "rotate(180deg)" : "rotate(0deg)",
1127
+ transition: "transform 200ms ease"
1128
+ },
1129
+ children: /* @__PURE__ */ jsx16(
1130
+ "path",
1131
+ {
1132
+ d: "M4 6L8 10L12 6",
1133
+ stroke: "currentColor",
1134
+ strokeWidth: "1.5",
1135
+ strokeLinecap: "round",
1136
+ strokeLinejoin: "round"
1137
+ }
1138
+ )
1139
+ }
1140
+ )
1141
+ ]
1142
+ }
1143
+ ),
1144
+ open && /* @__PURE__ */ jsx16(
1145
+ "div",
1146
+ {
1147
+ style: {
1148
+ padding: "0 16px 16px"
1149
+ },
1150
+ children
1151
+ }
1152
+ )
1153
+ ]
1154
+ }
1155
+ );
1156
+ }
1157
+ export {
1158
+ Accordion,
1159
+ Button,
1160
+ Card,
1161
+ Checkbox,
1162
+ Divider,
1163
+ FlexProvider,
1164
+ ListItem,
1165
+ Modal,
1166
+ Page,
1167
+ ProgressBar,
1168
+ Radio,
1169
+ Select,
1170
+ Skeleton,
1171
+ TextField,
1172
+ Toast,
1173
+ Toggle,
1174
+ Typography,
1175
+ fontFamily,
1176
+ fontSize,
1177
+ fontWeight,
1178
+ lineHeight,
1179
+ palette,
1180
+ radius,
1181
+ spacing,
1182
+ useFlexTheme,
1183
+ useToast
1184
+ };