@financedistrict/apps-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.
Files changed (136) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +119 -0
  3. package/dist/Radio-BYWz1n7p.d.cts +32 -0
  4. package/dist/Radio-BYWz1n7p.d.ts +32 -0
  5. package/dist/SelectTrigger-By0iPBI3.d.cts +78 -0
  6. package/dist/SelectTrigger-By0iPBI3.d.ts +78 -0
  7. package/dist/accordion.cjs +322 -0
  8. package/dist/accordion.d.cts +156 -0
  9. package/dist/accordion.d.ts +156 -0
  10. package/dist/accordion.js +2 -0
  11. package/dist/apps-ui.umd.js +1 -0
  12. package/dist/button.cjs +366 -0
  13. package/dist/button.d.cts +52 -0
  14. package/dist/button.d.ts +52 -0
  15. package/dist/button.js +3 -0
  16. package/dist/checkbox.cjs +74 -0
  17. package/dist/checkbox.d.cts +63 -0
  18. package/dist/checkbox.d.ts +63 -0
  19. package/dist/checkbox.js +2 -0
  20. package/dist/chunk-3RU7JMFE.js +27 -0
  21. package/dist/chunk-5TWRK4NW.js +783 -0
  22. package/dist/chunk-7EOSDFS3.js +9 -0
  23. package/dist/chunk-AIQ4UOGK.js +142 -0
  24. package/dist/chunk-B3MMGTPV.js +200 -0
  25. package/dist/chunk-DAHQYLUI.js +146 -0
  26. package/dist/chunk-DGBXADSS.js +1 -0
  27. package/dist/chunk-E6KPNTWW.js +295 -0
  28. package/dist/chunk-EOAB6G2D.js +149 -0
  29. package/dist/chunk-FAOVKDVM.js +32 -0
  30. package/dist/chunk-FV76Z4Z2.js +78 -0
  31. package/dist/chunk-GLTLBUZ7.js +285 -0
  32. package/dist/chunk-GTZFV43T.js +154 -0
  33. package/dist/chunk-HRGZNL4A.js +88 -0
  34. package/dist/chunk-IEVN3J2F.js +98 -0
  35. package/dist/chunk-IRS3T6LF.js +352 -0
  36. package/dist/chunk-IYYHCQXG.js +146 -0
  37. package/dist/chunk-JIXW3455.js +89 -0
  38. package/dist/chunk-N4VAP2GB.js +77 -0
  39. package/dist/chunk-SJ2ZRYVV.js +167 -0
  40. package/dist/chunk-SNU4YXOR.js +66 -0
  41. package/dist/chunk-TT6JQATV.js +99 -0
  42. package/dist/chunk-UPQ3ZPCO.js +66 -0
  43. package/dist/chunk-UVUSVFOH.js +264 -0
  44. package/dist/chunk-WJFPGGJB.js +255 -0
  45. package/dist/chunk-WXEBQA7A.js +371 -0
  46. package/dist/chunk-Y2O4YYZQ.js +184 -0
  47. package/dist/cut-corner.cjs +111 -0
  48. package/dist/cut-corner.d.cts +49 -0
  49. package/dist/cut-corner.d.ts +49 -0
  50. package/dist/cut-corner.js +1 -0
  51. package/dist/dialog.cjs +441 -0
  52. package/dist/dialog.d.cts +240 -0
  53. package/dist/dialog.d.ts +240 -0
  54. package/dist/dialog.js +3 -0
  55. package/dist/feature-card.cjs +182 -0
  56. package/dist/feature-card.d.cts +54 -0
  57. package/dist/feature-card.d.ts +54 -0
  58. package/dist/feature-card.js +2 -0
  59. package/dist/filter.cjs +873 -0
  60. package/dist/filter.d.cts +129 -0
  61. package/dist/filter.d.ts +129 -0
  62. package/dist/filter.js +6 -0
  63. package/dist/index.cjs +4583 -0
  64. package/dist/index.d.cts +29 -0
  65. package/dist/index.d.ts +29 -0
  66. package/dist/index.js +27 -0
  67. package/dist/inline-select.cjs +856 -0
  68. package/dist/inline-select.d.cts +92 -0
  69. package/dist/inline-select.d.ts +92 -0
  70. package/dist/inline-select.js +6 -0
  71. package/dist/input.cjs +263 -0
  72. package/dist/input.d.cts +157 -0
  73. package/dist/input.d.ts +157 -0
  74. package/dist/input.js +4 -0
  75. package/dist/panel.cjs +185 -0
  76. package/dist/panel.d.cts +103 -0
  77. package/dist/panel.d.ts +103 -0
  78. package/dist/panel.js +2 -0
  79. package/dist/property-grid.cjs +110 -0
  80. package/dist/property-grid.d.cts +56 -0
  81. package/dist/property-grid.d.ts +56 -0
  82. package/dist/property-grid.js +2 -0
  83. package/dist/radio.cjs +74 -0
  84. package/dist/radio.d.cts +39 -0
  85. package/dist/radio.d.ts +39 -0
  86. package/dist/radio.js +2 -0
  87. package/dist/scrollbar.cjs +34 -0
  88. package/dist/scrollbar.d.cts +35 -0
  89. package/dist/scrollbar.d.ts +35 -0
  90. package/dist/scrollbar.js +1 -0
  91. package/dist/search-bar.cjs +408 -0
  92. package/dist/search-bar.d.cts +42 -0
  93. package/dist/search-bar.d.ts +42 -0
  94. package/dist/search-bar.js +4 -0
  95. package/dist/select.cjs +998 -0
  96. package/dist/select.d.cts +357 -0
  97. package/dist/select.d.ts +357 -0
  98. package/dist/select.js +5 -0
  99. package/dist/selectable-feature-card.cjs +159 -0
  100. package/dist/selectable-feature-card.d.cts +74 -0
  101. package/dist/selectable-feature-card.d.ts +74 -0
  102. package/dist/selectable-feature-card.js +3 -0
  103. package/dist/sidebar-item.cjs +175 -0
  104. package/dist/sidebar-item.d.cts +75 -0
  105. package/dist/sidebar-item.d.ts +75 -0
  106. package/dist/sidebar-item.js +2 -0
  107. package/dist/switch.cjs +86 -0
  108. package/dist/switch.d.cts +72 -0
  109. package/dist/switch.d.ts +72 -0
  110. package/dist/switch.js +2 -0
  111. package/dist/table-cell.cjs +196 -0
  112. package/dist/table-cell.d.cts +76 -0
  113. package/dist/table-cell.d.ts +76 -0
  114. package/dist/table-cell.js +2 -0
  115. package/dist/table-title.cjs +34 -0
  116. package/dist/table-title.d.cts +16 -0
  117. package/dist/table-title.d.ts +16 -0
  118. package/dist/table-title.js +2 -0
  119. package/dist/tabs.cjs +299 -0
  120. package/dist/tabs.d.cts +116 -0
  121. package/dist/tabs.d.ts +116 -0
  122. package/dist/tabs.js +2 -0
  123. package/dist/toast.cjs +87 -0
  124. package/dist/toast.d.cts +48 -0
  125. package/dist/toast.d.ts +48 -0
  126. package/dist/toast.js +2 -0
  127. package/dist/token-input.cjs +415 -0
  128. package/dist/token-input.d.cts +90 -0
  129. package/dist/token-input.d.ts +90 -0
  130. package/dist/token-input.js +3 -0
  131. package/dist/tooltip.cjs +384 -0
  132. package/dist/tooltip.d.cts +142 -0
  133. package/dist/tooltip.d.ts +142 -0
  134. package/dist/tooltip.js +2 -0
  135. package/package.json +200 -0
  136. package/src/fonts.css +33 -0
@@ -0,0 +1,285 @@
1
+ import { cn } from './chunk-7EOSDFS3.js';
2
+ import * as React from 'react';
3
+ import { MinusIcon, PlusIcon } from '@phosphor-icons/react';
4
+ import { cva } from 'class-variance-authority';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ var accordionRootVariants = cva(["flex flex-col"]);
8
+ var accordionItemVariants = cva(["border-b border-card-border"], {
9
+ variants: {
10
+ disabled: {
11
+ true: "opacity-60",
12
+ false: ""
13
+ }
14
+ },
15
+ defaultVariants: {
16
+ disabled: false
17
+ }
18
+ });
19
+ var accordionTriggerVariants = cva(
20
+ [
21
+ "group flex w-full cursor-pointer items-start gap-4 pt-3 text-left",
22
+ "outline-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",
23
+ "disabled:cursor-not-allowed"
24
+ ],
25
+ {
26
+ variants: {
27
+ open: {
28
+ true: "pb-2",
29
+ false: "pb-3"
30
+ }
31
+ },
32
+ defaultVariants: {
33
+ open: false
34
+ }
35
+ }
36
+ );
37
+ var accordionTriggerTitleVariants = cva([
38
+ "body-02-medium min-w-0 flex-1 break-words text-card-foreground"
39
+ ]);
40
+ var accordionTriggerIconVariants = cva([
41
+ "flex shrink-0 items-center text-card-foreground-muted transition-colors",
42
+ "group-hover:text-card-foreground",
43
+ "[&_svg]:size-4 [&_svg]:shrink-0"
44
+ ]);
45
+ var accordionContentVariants = cva([
46
+ "body-03 pb-3 text-card-foreground-muted"
47
+ ]);
48
+ var AccordionContext = React.createContext(
49
+ null
50
+ );
51
+ function useAccordionContext(part) {
52
+ const context = React.useContext(AccordionContext);
53
+ if (context === null) {
54
+ throw new Error(`${part} must be used within <Accordion.Root>`);
55
+ }
56
+ return context;
57
+ }
58
+ var AccordionItemContext = React.createContext(null);
59
+ function useAccordionItemContext(part) {
60
+ const context = React.useContext(AccordionItemContext);
61
+ if (context === null) {
62
+ throw new Error(`${part} must be used within <Accordion.Item>`);
63
+ }
64
+ return context;
65
+ }
66
+ function setRef(ref, node) {
67
+ if (typeof ref === "function") ref(node);
68
+ else if (ref) ref.current = node;
69
+ }
70
+ function AccordionRoot({
71
+ type = "single",
72
+ value: valueProp,
73
+ defaultValue,
74
+ onValueChange,
75
+ collapsible = true,
76
+ className,
77
+ children,
78
+ ref,
79
+ ...props
80
+ }) {
81
+ const isMultiple = type === "multiple";
82
+ const isControlled = valueProp !== void 0;
83
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(
84
+ () => defaultValue !== void 0 ? defaultValue : isMultiple ? [] : null
85
+ );
86
+ const openModel = isControlled ? valueProp : uncontrolledValue;
87
+ const commit = (next) => {
88
+ if (!isControlled) setUncontrolledValue(next);
89
+ onValueChange?.(next);
90
+ };
91
+ const isOpen = (itemValue) => isMultiple ? Array.isArray(openModel) && openModel.includes(itemValue) : openModel === itemValue;
92
+ const toggle = (itemValue) => {
93
+ if (isMultiple) {
94
+ const current2 = Array.isArray(openModel) ? openModel : [];
95
+ commit(
96
+ current2.includes(itemValue) ? current2.filter((v) => v !== itemValue) : [...current2, itemValue]
97
+ );
98
+ return;
99
+ }
100
+ const current = Array.isArray(openModel) ? null : openModel;
101
+ if (current === itemValue) {
102
+ commit(collapsible ? null : itemValue);
103
+ } else {
104
+ commit(itemValue);
105
+ }
106
+ };
107
+ const triggersRef = React.useRef(/* @__PURE__ */ new Set());
108
+ const registerTrigger = React.useCallback((node) => {
109
+ triggersRef.current.add(node);
110
+ }, []);
111
+ const unregisterTrigger = React.useCallback((node) => {
112
+ triggersRef.current.delete(node);
113
+ }, []);
114
+ const onTriggerKeyDown = (event) => {
115
+ if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
116
+ const triggers = Array.from(triggersRef.current).filter((el) => !el.disabled).sort(
117
+ (a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
118
+ );
119
+ if (triggers.length === 0) return;
120
+ const currentIndex = triggers.indexOf(event.currentTarget);
121
+ if (currentIndex === -1) return;
122
+ event.preventDefault();
123
+ let nextIndex = currentIndex;
124
+ switch (event.key) {
125
+ case "ArrowDown":
126
+ nextIndex = (currentIndex + 1) % triggers.length;
127
+ break;
128
+ case "ArrowUp":
129
+ nextIndex = (currentIndex - 1 + triggers.length) % triggers.length;
130
+ break;
131
+ case "Home":
132
+ nextIndex = 0;
133
+ break;
134
+ case "End":
135
+ nextIndex = triggers.length - 1;
136
+ break;
137
+ }
138
+ triggers[nextIndex]?.focus();
139
+ };
140
+ const contextValue = {
141
+ type,
142
+ isOpen,
143
+ toggle,
144
+ registerTrigger,
145
+ unregisterTrigger,
146
+ onTriggerKeyDown
147
+ };
148
+ return /* @__PURE__ */ jsx(AccordionContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
149
+ "div",
150
+ {
151
+ ref,
152
+ "data-slot": "accordion",
153
+ className: cn(accordionRootVariants(), className),
154
+ ...props,
155
+ children
156
+ }
157
+ ) });
158
+ }
159
+ function AccordionItem({
160
+ value,
161
+ disabled = false,
162
+ className,
163
+ children,
164
+ ref,
165
+ ...props
166
+ }) {
167
+ const { isOpen } = useAccordionContext("Accordion.Item");
168
+ const open = isOpen(value);
169
+ const reactId = React.useId();
170
+ const triggerId = `${reactId}-trigger`;
171
+ const contentId = `${reactId}-content`;
172
+ const itemContext = {
173
+ value,
174
+ open,
175
+ disabled,
176
+ triggerId,
177
+ contentId
178
+ };
179
+ return /* @__PURE__ */ jsx(AccordionItemContext.Provider, { value: itemContext, children: /* @__PURE__ */ jsx(
180
+ "div",
181
+ {
182
+ ref,
183
+ "data-slot": "accordion-item",
184
+ "data-state": open ? "open" : "closed",
185
+ "data-disabled": disabled || void 0,
186
+ className: cn(accordionItemVariants({ disabled }), className),
187
+ ...props,
188
+ children
189
+ }
190
+ ) });
191
+ }
192
+ function AccordionTrigger({
193
+ className,
194
+ children,
195
+ onClick,
196
+ onKeyDown,
197
+ ref,
198
+ ...props
199
+ }) {
200
+ const { toggle, registerTrigger, unregisterTrigger, onTriggerKeyDown } = useAccordionContext("Accordion.Trigger");
201
+ const { value, open, disabled, triggerId, contentId } = useAccordionItemContext("Accordion.Trigger");
202
+ const buttonRef = React.useRef(null);
203
+ const setButtonRef = (node) => {
204
+ buttonRef.current = node;
205
+ setRef(ref, node);
206
+ };
207
+ React.useEffect(() => {
208
+ const node = buttonRef.current;
209
+ if (!node) return;
210
+ registerTrigger(node);
211
+ return () => unregisterTrigger(node);
212
+ }, [registerTrigger, unregisterTrigger]);
213
+ return /* @__PURE__ */ jsxs(
214
+ "button",
215
+ {
216
+ ...props,
217
+ ref: setButtonRef,
218
+ type: "button",
219
+ id: triggerId,
220
+ "data-slot": "accordion-trigger",
221
+ "aria-expanded": open,
222
+ "aria-controls": open ? contentId : void 0,
223
+ disabled,
224
+ className: cn(accordionTriggerVariants({ open }), className),
225
+ onClick: (event) => {
226
+ onClick?.(event);
227
+ if (!event.defaultPrevented) toggle(value);
228
+ },
229
+ onKeyDown: (event) => {
230
+ onKeyDown?.(event);
231
+ if (!event.defaultPrevented) onTriggerKeyDown(event);
232
+ },
233
+ children: [
234
+ /* @__PURE__ */ jsx(
235
+ "span",
236
+ {
237
+ "data-slot": "accordion-trigger-title",
238
+ className: accordionTriggerTitleVariants(),
239
+ children
240
+ }
241
+ ),
242
+ /* @__PURE__ */ jsx(
243
+ "span",
244
+ {
245
+ "data-slot": "accordion-trigger-icon",
246
+ "aria-hidden": "true",
247
+ className: accordionTriggerIconVariants(),
248
+ children: open ? /* @__PURE__ */ jsx(MinusIcon, {}) : /* @__PURE__ */ jsx(PlusIcon, {})
249
+ }
250
+ )
251
+ ]
252
+ }
253
+ );
254
+ }
255
+ function AccordionContent({
256
+ className,
257
+ children,
258
+ ref,
259
+ ...props
260
+ }) {
261
+ const { open, triggerId, contentId } = useAccordionItemContext("Accordion.Content");
262
+ if (!open) return null;
263
+ return /* @__PURE__ */ jsx(
264
+ "div",
265
+ {
266
+ ref,
267
+ role: "region",
268
+ id: contentId,
269
+ "aria-labelledby": triggerId,
270
+ "data-slot": "accordion-content",
271
+ "data-state": "open",
272
+ className: cn(accordionContentVariants(), className),
273
+ ...props,
274
+ children
275
+ }
276
+ );
277
+ }
278
+ var Accordion = {
279
+ Root: AccordionRoot,
280
+ Item: AccordionItem,
281
+ Trigger: AccordionTrigger,
282
+ Content: AccordionContent
283
+ };
284
+
285
+ export { Accordion, AccordionContent, AccordionItem, AccordionRoot, AccordionTrigger, accordionContentVariants, accordionItemVariants, accordionRootVariants, accordionTriggerIconVariants, accordionTriggerTitleVariants, accordionTriggerVariants };
@@ -0,0 +1,154 @@
1
+ import { Input } from './chunk-AIQ4UOGK.js';
2
+ import { cn } from './chunk-7EOSDFS3.js';
3
+ import * as React from 'react';
4
+ import { XIcon, MagnifyingGlassIcon } from '@phosphor-icons/react';
5
+ import { jsx } from 'react/jsx-runtime';
6
+
7
+ function useDebouncedCallback(fn, delayMs) {
8
+ const fnRef = React.useRef(fn);
9
+ const delayRef = React.useRef(delayMs);
10
+ fnRef.current = fn;
11
+ delayRef.current = delayMs;
12
+ const timerRef = React.useRef(null);
13
+ const debounced = React.useMemo(() => {
14
+ const clear = () => {
15
+ if (timerRef.current !== null) {
16
+ clearTimeout(timerRef.current);
17
+ timerRef.current = null;
18
+ }
19
+ };
20
+ const call = (...args) => {
21
+ clear();
22
+ timerRef.current = setTimeout(() => {
23
+ timerRef.current = null;
24
+ fnRef.current(...args);
25
+ }, delayRef.current);
26
+ };
27
+ call.cancel = clear;
28
+ call.flush = (...args) => {
29
+ clear();
30
+ fnRef.current(...args);
31
+ };
32
+ return call;
33
+ }, []);
34
+ React.useEffect(() => {
35
+ return () => {
36
+ if (timerRef.current !== null) {
37
+ clearTimeout(timerRef.current);
38
+ timerRef.current = null;
39
+ }
40
+ };
41
+ }, []);
42
+ return debounced;
43
+ }
44
+ var DEFAULT_DEBOUNCE_MS = 300;
45
+ function SearchBar({
46
+ onSearch,
47
+ debounceMs = DEFAULT_DEBOUNCE_MS,
48
+ hint,
49
+ wrapperClassName,
50
+ fieldClassName,
51
+ className,
52
+ disabled,
53
+ value,
54
+ defaultValue,
55
+ onChange,
56
+ onKeyDown,
57
+ ref,
58
+ ...props
59
+ }) {
60
+ const isControlled = value !== void 0;
61
+ const innerRef = React.useRef(null);
62
+ const setInputRef = React.useCallback(
63
+ (node) => {
64
+ innerRef.current = node;
65
+ if (typeof ref === "function") ref(node);
66
+ else if (ref)
67
+ ref.current = node;
68
+ },
69
+ [ref]
70
+ );
71
+ const [hasValueState, setHasValueState] = React.useState(
72
+ () => String(value ?? defaultValue ?? "").length > 0
73
+ );
74
+ const hasValue = isControlled ? String(value ?? "").length > 0 : hasValueState;
75
+ const debouncedSearch = useDebouncedCallback((next) => {
76
+ onSearch?.(next);
77
+ }, debounceMs);
78
+ const handleChange = (event) => {
79
+ onChange?.(event);
80
+ if (!isControlled) setHasValueState(event.currentTarget.value.length > 0);
81
+ debouncedSearch(event.currentTarget.value);
82
+ };
83
+ const handleKeyDown = (event) => {
84
+ onKeyDown?.(event);
85
+ if (event.key === "Enter") {
86
+ debouncedSearch.flush(event.currentTarget.value);
87
+ }
88
+ };
89
+ const handleClear = () => {
90
+ const input = innerRef.current;
91
+ if (!input) return;
92
+ const setter = Object.getOwnPropertyDescriptor(
93
+ window.HTMLInputElement.prototype,
94
+ "value"
95
+ )?.set;
96
+ setter?.call(input, "");
97
+ input.dispatchEvent(new Event("input", { bubbles: true }));
98
+ if (!isControlled) setHasValueState(false);
99
+ debouncedSearch.cancel();
100
+ onSearch?.("");
101
+ input.focus();
102
+ };
103
+ const showClear = hasValue && !disabled;
104
+ return /* @__PURE__ */ jsx(
105
+ Input,
106
+ {
107
+ type: "search",
108
+ variation: "field",
109
+ size: "lg",
110
+ ref: setInputRef,
111
+ disabled,
112
+ hint,
113
+ value,
114
+ defaultValue,
115
+ onChange: handleChange,
116
+ onKeyDown: handleKeyDown,
117
+ className: cn(
118
+ // Suppress the browser's native `type=search` clear affordance
119
+ // (WebKit/Chrome render `::-webkit-search-cancel-button`) — SearchBar
120
+ // provides its own clear button, so the native one would show a second
121
+ // "X". `hidden` (display:none) is used rather than `appearance-none`:
122
+ // that pseudo-element is governed by `-webkit-appearance`, which the
123
+ // unprefixed `appearance:none` Tailwind emits does not affect.
124
+ // Caller `className` is merged after and can still override.
125
+ "[&::-webkit-search-cancel-button]:hidden",
126
+ className
127
+ ),
128
+ wrapperClassName,
129
+ fieldClassName,
130
+ leftSlot: /* @__PURE__ */ jsx(MagnifyingGlassIcon, { weight: "regular" }),
131
+ rightSlot: showClear ? /* @__PURE__ */ jsx(
132
+ "button",
133
+ {
134
+ type: "button",
135
+ "aria-label": "Clear search",
136
+ onClick: handleClear,
137
+ "data-slot": "search-clear",
138
+ className: cn(
139
+ // Reset native button chrome; inherit the field's icon colour.
140
+ "inline-flex cursor-pointer items-center justify-center rounded-xs bg-transparent p-0 text-current",
141
+ // Standard ADR-0010 system focus ring (this button is a momentary
142
+ // control — the ADR-0011 no-ring exception applies only to the
143
+ // text input itself, NOT to this button).
144
+ "outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-focus"
145
+ ),
146
+ children: /* @__PURE__ */ jsx(XIcon, { weight: "regular", "aria-hidden": "true" })
147
+ }
148
+ ) : void 0,
149
+ ...props
150
+ }
151
+ );
152
+ }
153
+
154
+ export { SearchBar };
@@ -0,0 +1,88 @@
1
+ import { Radio } from './chunk-UPQ3ZPCO.js';
2
+ import { cn } from './chunk-7EOSDFS3.js';
3
+ import { cva } from 'class-variance-authority';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var selectableFeatureCardVariants = cva([
7
+ "group overflow-hidden rounded-md border border-border bg-card-background",
8
+ "has-[:checked]:border-brand-primary-border",
9
+ "has-[:disabled]:opacity-60"
10
+ ]);
11
+ var selectableFeatureCardRowVariants = cva(
12
+ [
13
+ "flex h-16 items-center gap-3 bg-card-background px-4 py-3",
14
+ "cursor-pointer has-[:disabled]:cursor-not-allowed"
15
+ ],
16
+ {
17
+ variants: {
18
+ editable: {
19
+ true: "group-has-[:checked]:border-b group-has-[:checked]:border-border",
20
+ false: ""
21
+ }
22
+ },
23
+ defaultVariants: { editable: false }
24
+ }
25
+ );
26
+ var selectableFeatureCardTextVariants = cva(
27
+ "flex min-w-0 flex-1 flex-col"
28
+ );
29
+ var selectableFeatureCardTrailingVariants = cva(
30
+ "flex shrink-0 items-center text-card-foreground-muted [&_svg]:size-6 [&_svg]:shrink-0"
31
+ );
32
+ var selectableFeatureCardBottomVariants = cva(
33
+ "hidden bg-card-accent p-4 group-has-[:checked]:block"
34
+ );
35
+ function SelectableFeatureCard({
36
+ title,
37
+ subtitle,
38
+ trailing,
39
+ variation = "basic",
40
+ children,
41
+ className,
42
+ ref,
43
+ ...radioProps
44
+ }) {
45
+ const editable = variation === "editable";
46
+ return /* @__PURE__ */ jsxs(
47
+ "div",
48
+ {
49
+ "data-slot": "selectable-feature-card",
50
+ className: cn(selectableFeatureCardVariants(), className),
51
+ children: [
52
+ /* @__PURE__ */ jsxs("label", { className: selectableFeatureCardRowVariants({ editable }), children: [
53
+ /* @__PURE__ */ jsx(Radio, { ref, ...radioProps }),
54
+ /* @__PURE__ */ jsxs(
55
+ "span",
56
+ {
57
+ "data-slot": "selectable-feature-card-text",
58
+ className: selectableFeatureCardTextVariants(),
59
+ children: [
60
+ /* @__PURE__ */ jsx("span", { className: "body-02-medium truncate text-card-foreground", children: title }),
61
+ subtitle != null && /* @__PURE__ */ jsx("span", { className: "body-03 truncate text-card-foreground-muted", children: subtitle })
62
+ ]
63
+ }
64
+ ),
65
+ trailing != null && /* @__PURE__ */ jsx(
66
+ "span",
67
+ {
68
+ "data-slot": "selectable-feature-card-trailing",
69
+ "aria-hidden": "true",
70
+ className: selectableFeatureCardTrailingVariants(),
71
+ children: trailing
72
+ }
73
+ )
74
+ ] }),
75
+ editable && /* @__PURE__ */ jsx(
76
+ "div",
77
+ {
78
+ "data-slot": "selectable-feature-card-bottom",
79
+ className: selectableFeatureCardBottomVariants(),
80
+ children
81
+ }
82
+ )
83
+ ]
84
+ }
85
+ );
86
+ }
87
+
88
+ export { SelectableFeatureCard, selectableFeatureCardBottomVariants, selectableFeatureCardRowVariants, selectableFeatureCardTextVariants, selectableFeatureCardTrailingVariants, selectableFeatureCardVariants };
@@ -0,0 +1,98 @@
1
+ import { cva } from 'class-variance-authority';
2
+
3
+ // src/input/inputVariants.ts
4
+ var inputVariants = cva(
5
+ [
6
+ // Field row layout — icons + input on one line, vertically centred.
7
+ "flex w-full items-center",
8
+ // Colour transitions for the focus border change.
9
+ "transition-colors",
10
+ // I-beam text cursor over the WHOLE field row (not just the inner input),
11
+ // so clicking anywhere in the padding/icon area reads as "type here". This
12
+ // is the only hover affordance — hover intentionally does NOT recolour the
13
+ // border (user-authorized deviation from the Figma). `cursor-text` sits in
14
+ // the base; `has-[:disabled]:cursor-not-allowed` below is declared AFTER it
15
+ // so it wins on a disabled field (same-specificity utilities, later class
16
+ // takes precedence — confirmed by tailwind-merge ordering).
17
+ "cursor-text",
18
+ // Disabled: not-allowed cursor + block pointer interaction on the whole
19
+ // field (the native `disabled` on the input already stops typing; icons are
20
+ // dimmed separately by the `disabled`-derived `iconStateClass` in <Input>).
21
+ // Declared after `cursor-text` so it overrides the I-beam when disabled.
22
+ "has-[:disabled]:cursor-not-allowed"
23
+ ],
24
+ {
25
+ variants: {
26
+ variation: {
27
+ field: [
28
+ "bg-input-background border border-input-border rounded-sm",
29
+ // Focus (mouse or keyboard) strengthens the whole border to the
30
+ // accent. Hover does NOT change colour (cursor-only affordance).
31
+ "focus-within:border-input-border-accent"
32
+ ],
33
+ line: [
34
+ "border-b border-input-border",
35
+ // Focus (mouse or keyboard) strengthens the bottom line to the
36
+ // accent. Hover does NOT change colour (cursor-only affordance).
37
+ "focus-within:border-input-border-accent"
38
+ ]
39
+ },
40
+ size: {
41
+ // sm/lg heights reuse the Button scale; gap follows Figma per size.
42
+ sm: "h-8 gap-1",
43
+ lg: "h-10 gap-2"
44
+ },
45
+ error: {
46
+ // Error out-ranks focus: declare the destructive border for the resting
47
+ // state AND its `focus-within:` form so an errored field never recolours
48
+ // to the accent when focused (a bare `border-…` alone would lose to the
49
+ // variation's `focus-within:` accent utility above). No `hover:` form is
50
+ // needed — hover no longer changes colour on any variation. The `error`
51
+ // variant's classes are emitted after the variation's, so at equal
52
+ // specificity the destructive `focus-within:` wins by order.
53
+ true: "border-input-destructive-foreground-accent focus-within:border-input-destructive-foreground-accent",
54
+ false: ""
55
+ },
56
+ // Bottom-line visibility toggle. LINE ONLY — the hide effect is applied
57
+ // via a compound variant below (scoped to `variation:line`), so `field`
58
+ // is unaffected. Boolean → `true`/`false`; `true` (default) keeps today's
59
+ // behaviour. No classes here: `true` is the resting `border-input-border`
60
+ // from the variation, and `false`'s hide is the compound variant.
61
+ showBottomLine: {
62
+ true: "",
63
+ false: ""
64
+ }
65
+ },
66
+ compoundVariants: [
67
+ // field padding — Figma field-lg = px-3 (12px) / py-2 (8px).
68
+ { variation: "field", size: "lg", className: "px-3 py-2" },
69
+ // field-sm: DERIVED. px-2 (8px = spacing/2) steps down from field-lg's
70
+ // px-3 (12px) and equals py-2 for a uniform spacing/2 inset all around.
71
+ { variation: "field", size: "sm", className: "px-2 py-2" },
72
+ // line — no horizontal padding (Figma px-0); vertical padding per size.
73
+ { variation: "line", size: "lg", className: "py-2" },
74
+ { variation: "line", size: "sm", className: "py-2.5" },
75
+ // Hide the resting bottom line (LINE ONLY): recolour to transparent while
76
+ // keeping `border-b` for a stable 1px width (no layout shift). Gated on
77
+ // `error:false` so an errored field keeps its destructive rest border
78
+ // (transparent would otherwise last-win over it). Focus still wins:
79
+ // `focus-within:border-input-border-accent` out-ranks `border-transparent`
80
+ // (later in class order), so the line reappears on focus. `field` is
81
+ // untouched — this compound only matches `variation:line`.
82
+ {
83
+ variation: "line",
84
+ showBottomLine: false,
85
+ error: false,
86
+ className: "border-transparent"
87
+ }
88
+ ],
89
+ defaultVariants: {
90
+ variation: "field",
91
+ size: "lg",
92
+ error: false,
93
+ showBottomLine: true
94
+ }
95
+ }
96
+ );
97
+
98
+ export { inputVariants };