@luxfi/ui 1.0.1

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 (124) hide show
  1. package/dist/accordion.cjs +213 -0
  2. package/dist/accordion.js +186 -0
  3. package/dist/alert.cjs +553 -0
  4. package/dist/alert.js +531 -0
  5. package/dist/avatar.cjs +149 -0
  6. package/dist/avatar.js +125 -0
  7. package/dist/badge.cjs +611 -0
  8. package/dist/badge.js +589 -0
  9. package/dist/button.cjs +689 -0
  10. package/dist/button.js +664 -0
  11. package/dist/checkbox.cjs +265 -0
  12. package/dist/checkbox.js +241 -0
  13. package/dist/close-button.cjs +73 -0
  14. package/dist/close-button.js +51 -0
  15. package/dist/collapsible.cjs +711 -0
  16. package/dist/collapsible.js +685 -0
  17. package/dist/color-mode.cjs +36 -0
  18. package/dist/color-mode.js +32 -0
  19. package/dist/dialog.cjs +279 -0
  20. package/dist/dialog.js +246 -0
  21. package/dist/drawer.cjs +207 -0
  22. package/dist/drawer.js +175 -0
  23. package/dist/empty-state.cjs +93 -0
  24. package/dist/empty-state.js +71 -0
  25. package/dist/field.cjs +183 -0
  26. package/dist/field.js +160 -0
  27. package/dist/heading.cjs +46 -0
  28. package/dist/heading.js +40 -0
  29. package/dist/icon-button.cjs +491 -0
  30. package/dist/icon-button.js +470 -0
  31. package/dist/image.cjs +572 -0
  32. package/dist/image.js +551 -0
  33. package/dist/index.cjs +5749 -0
  34. package/dist/index.js +5586 -0
  35. package/dist/input-group.cjs +155 -0
  36. package/dist/input-group.js +133 -0
  37. package/dist/input.cjs +65 -0
  38. package/dist/input.js +59 -0
  39. package/dist/link.cjs +639 -0
  40. package/dist/link.js +612 -0
  41. package/dist/menu.cjs +305 -0
  42. package/dist/menu.js +269 -0
  43. package/dist/pin-input.cjs +182 -0
  44. package/dist/pin-input.js +160 -0
  45. package/dist/popover.cjs +327 -0
  46. package/dist/popover.js +294 -0
  47. package/dist/progress-circle.cjs +152 -0
  48. package/dist/progress-circle.js +128 -0
  49. package/dist/progress.cjs +117 -0
  50. package/dist/progress.js +94 -0
  51. package/dist/provider.cjs +22 -0
  52. package/dist/provider.js +20 -0
  53. package/dist/radio.cjs +177 -0
  54. package/dist/radio.js +153 -0
  55. package/dist/rating.cjs +80 -0
  56. package/dist/rating.js +58 -0
  57. package/dist/select.cjs +791 -0
  58. package/dist/select.js +757 -0
  59. package/dist/separator.cjs +57 -0
  60. package/dist/separator.js +51 -0
  61. package/dist/skeleton.cjs +370 -0
  62. package/dist/skeleton.js +346 -0
  63. package/dist/slider.cjs +138 -0
  64. package/dist/slider.js +115 -0
  65. package/dist/switch.cjs +163 -0
  66. package/dist/switch.js +140 -0
  67. package/dist/table.cjs +1053 -0
  68. package/dist/table.js +1019 -0
  69. package/dist/tabs.cjs +240 -0
  70. package/dist/tabs.js +213 -0
  71. package/dist/tag.cjs +651 -0
  72. package/dist/tag.js +628 -0
  73. package/dist/textarea.cjs +65 -0
  74. package/dist/textarea.js +59 -0
  75. package/dist/toaster.cjs +99 -0
  76. package/dist/toaster.js +96 -0
  77. package/dist/tooltip.cjs +171 -0
  78. package/dist/tooltip.js +148 -0
  79. package/dist/utils.cjs +11 -0
  80. package/dist/utils.js +9 -0
  81. package/package.json +296 -0
  82. package/src/accordion.tsx +285 -0
  83. package/src/alert.tsx +221 -0
  84. package/src/avatar.tsx +174 -0
  85. package/src/badge.tsx +158 -0
  86. package/src/button.tsx +411 -0
  87. package/src/checkbox.tsx +307 -0
  88. package/src/close-button.tsx +51 -0
  89. package/src/collapsible.tsx +126 -0
  90. package/src/color-mode.tsx +37 -0
  91. package/src/dialog.tsx +356 -0
  92. package/src/drawer.tsx +186 -0
  93. package/src/empty-state.tsx +97 -0
  94. package/src/field.tsx +202 -0
  95. package/src/heading.tsx +55 -0
  96. package/src/icon-button.tsx +192 -0
  97. package/src/image.tsx +280 -0
  98. package/src/index.ts +192 -0
  99. package/src/input-group.tsx +159 -0
  100. package/src/input.tsx +60 -0
  101. package/src/link.tsx +333 -0
  102. package/src/menu.tsx +471 -0
  103. package/src/pin-input.tsx +187 -0
  104. package/src/popover.tsx +400 -0
  105. package/src/progress-circle.tsx +180 -0
  106. package/src/progress.tsx +109 -0
  107. package/src/provider.tsx +12 -0
  108. package/src/radio.tsx +175 -0
  109. package/src/rating.tsx +79 -0
  110. package/src/select.tsx +696 -0
  111. package/src/separator.tsx +59 -0
  112. package/src/skeleton.tsx +302 -0
  113. package/src/slider.tsx +152 -0
  114. package/src/switch.tsx +158 -0
  115. package/src/table.tsx +621 -0
  116. package/src/tabs.tsx +354 -0
  117. package/src/tag.tsx +159 -0
  118. package/src/textarea.tsx +60 -0
  119. package/src/toaster.tsx +117 -0
  120. package/src/tokens.css +438 -0
  121. package/src/tooltip.tsx +184 -0
  122. package/src/utils/cn.ts +7 -0
  123. package/src/utils.ts +6 -0
  124. package/tokens.css +438 -0
package/package.json ADDED
@@ -0,0 +1,296 @@
1
+ {
2
+ "name": "@luxfi/ui",
3
+ "version": "1.0.1",
4
+ "description": "Radix + Tailwind UI primitives for Lux apps. Drop-in replacements for Chakra compound components.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": {
8
+ "name": "Lux Partners",
9
+ "url": "https://lux.network"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/luxfi/ui.git",
14
+ "directory": "packages/ui"
15
+ },
16
+ "publishConfig": {
17
+ "registry": "https://registry.npmjs.org/",
18
+ "access": "public"
19
+ },
20
+ "sideEffects": [
21
+ "*.css"
22
+ ],
23
+ "main": "./dist/index.cjs",
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "files": [
27
+ "dist",
28
+ "src",
29
+ "tokens.css"
30
+ ],
31
+ "exports": {
32
+ "./package.json": "./package.json",
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "import": "./dist/index.js",
36
+ "require": "./dist/index.cjs"
37
+ },
38
+ "./tokens.css": "./tokens.css",
39
+ "./accordion": {
40
+ "types": "./dist/accordion.d.ts",
41
+ "import": "./dist/accordion.js",
42
+ "require": "./dist/accordion.cjs"
43
+ },
44
+ "./alert": {
45
+ "types": "./dist/alert.d.ts",
46
+ "import": "./dist/alert.js",
47
+ "require": "./dist/alert.cjs"
48
+ },
49
+ "./avatar": {
50
+ "types": "./dist/avatar.d.ts",
51
+ "import": "./dist/avatar.js",
52
+ "require": "./dist/avatar.cjs"
53
+ },
54
+ "./badge": {
55
+ "types": "./dist/badge.d.ts",
56
+ "import": "./dist/badge.js",
57
+ "require": "./dist/badge.cjs"
58
+ },
59
+ "./button": {
60
+ "types": "./dist/button.d.ts",
61
+ "import": "./dist/button.js",
62
+ "require": "./dist/button.cjs"
63
+ },
64
+ "./checkbox": {
65
+ "types": "./dist/checkbox.d.ts",
66
+ "import": "./dist/checkbox.js",
67
+ "require": "./dist/checkbox.cjs"
68
+ },
69
+ "./close-button": {
70
+ "types": "./dist/close-button.d.ts",
71
+ "import": "./dist/close-button.js",
72
+ "require": "./dist/close-button.cjs"
73
+ },
74
+ "./collapsible": {
75
+ "types": "./dist/collapsible.d.ts",
76
+ "import": "./dist/collapsible.js",
77
+ "require": "./dist/collapsible.cjs"
78
+ },
79
+ "./color-mode": {
80
+ "types": "./dist/color-mode.d.ts",
81
+ "import": "./dist/color-mode.js",
82
+ "require": "./dist/color-mode.cjs"
83
+ },
84
+ "./dialog": {
85
+ "types": "./dist/dialog.d.ts",
86
+ "import": "./dist/dialog.js",
87
+ "require": "./dist/dialog.cjs"
88
+ },
89
+ "./drawer": {
90
+ "types": "./dist/drawer.d.ts",
91
+ "import": "./dist/drawer.js",
92
+ "require": "./dist/drawer.cjs"
93
+ },
94
+ "./empty-state": {
95
+ "types": "./dist/empty-state.d.ts",
96
+ "import": "./dist/empty-state.js",
97
+ "require": "./dist/empty-state.cjs"
98
+ },
99
+ "./field": {
100
+ "types": "./dist/field.d.ts",
101
+ "import": "./dist/field.js",
102
+ "require": "./dist/field.cjs"
103
+ },
104
+ "./heading": {
105
+ "types": "./dist/heading.d.ts",
106
+ "import": "./dist/heading.js",
107
+ "require": "./dist/heading.cjs"
108
+ },
109
+ "./icon-button": {
110
+ "types": "./dist/icon-button.d.ts",
111
+ "import": "./dist/icon-button.js",
112
+ "require": "./dist/icon-button.cjs"
113
+ },
114
+ "./image": {
115
+ "types": "./dist/image.d.ts",
116
+ "import": "./dist/image.js",
117
+ "require": "./dist/image.cjs"
118
+ },
119
+ "./input": {
120
+ "types": "./dist/input.d.ts",
121
+ "import": "./dist/input.js",
122
+ "require": "./dist/input.cjs"
123
+ },
124
+ "./input-group": {
125
+ "types": "./dist/input-group.d.ts",
126
+ "import": "./dist/input-group.js",
127
+ "require": "./dist/input-group.cjs"
128
+ },
129
+ "./link": {
130
+ "types": "./dist/link.d.ts",
131
+ "import": "./dist/link.js",
132
+ "require": "./dist/link.cjs"
133
+ },
134
+ "./menu": {
135
+ "types": "./dist/menu.d.ts",
136
+ "import": "./dist/menu.js",
137
+ "require": "./dist/menu.cjs"
138
+ },
139
+ "./pin-input": {
140
+ "types": "./dist/pin-input.d.ts",
141
+ "import": "./dist/pin-input.js",
142
+ "require": "./dist/pin-input.cjs"
143
+ },
144
+ "./popover": {
145
+ "types": "./dist/popover.d.ts",
146
+ "import": "./dist/popover.js",
147
+ "require": "./dist/popover.cjs"
148
+ },
149
+ "./progress": {
150
+ "types": "./dist/progress.d.ts",
151
+ "import": "./dist/progress.js",
152
+ "require": "./dist/progress.cjs"
153
+ },
154
+ "./progress-circle": {
155
+ "types": "./dist/progress-circle.d.ts",
156
+ "import": "./dist/progress-circle.js",
157
+ "require": "./dist/progress-circle.cjs"
158
+ },
159
+ "./radio": {
160
+ "types": "./dist/radio.d.ts",
161
+ "import": "./dist/radio.js",
162
+ "require": "./dist/radio.cjs"
163
+ },
164
+ "./rating": {
165
+ "types": "./dist/rating.d.ts",
166
+ "import": "./dist/rating.js",
167
+ "require": "./dist/rating.cjs"
168
+ },
169
+ "./select": {
170
+ "types": "./dist/select.d.ts",
171
+ "import": "./dist/select.js",
172
+ "require": "./dist/select.cjs"
173
+ },
174
+ "./separator": {
175
+ "types": "./dist/separator.d.ts",
176
+ "import": "./dist/separator.js",
177
+ "require": "./dist/separator.cjs"
178
+ },
179
+ "./skeleton": {
180
+ "types": "./dist/skeleton.d.ts",
181
+ "import": "./dist/skeleton.js",
182
+ "require": "./dist/skeleton.cjs"
183
+ },
184
+ "./slider": {
185
+ "types": "./dist/slider.d.ts",
186
+ "import": "./dist/slider.js",
187
+ "require": "./dist/slider.cjs"
188
+ },
189
+ "./switch": {
190
+ "types": "./dist/switch.d.ts",
191
+ "import": "./dist/switch.js",
192
+ "require": "./dist/switch.cjs"
193
+ },
194
+ "./table": {
195
+ "types": "./dist/table.d.ts",
196
+ "import": "./dist/table.js",
197
+ "require": "./dist/table.cjs"
198
+ },
199
+ "./tabs": {
200
+ "types": "./dist/tabs.d.ts",
201
+ "import": "./dist/tabs.js",
202
+ "require": "./dist/tabs.cjs"
203
+ },
204
+ "./tag": {
205
+ "types": "./dist/tag.d.ts",
206
+ "import": "./dist/tag.js",
207
+ "require": "./dist/tag.cjs"
208
+ },
209
+ "./textarea": {
210
+ "types": "./dist/textarea.d.ts",
211
+ "import": "./dist/textarea.js",
212
+ "require": "./dist/textarea.cjs"
213
+ },
214
+ "./toaster": {
215
+ "types": "./dist/toaster.d.ts",
216
+ "import": "./dist/toaster.js",
217
+ "require": "./dist/toaster.cjs"
218
+ },
219
+ "./tooltip": {
220
+ "types": "./dist/tooltip.d.ts",
221
+ "import": "./dist/tooltip.js",
222
+ "require": "./dist/tooltip.cjs"
223
+ },
224
+ "./provider": {
225
+ "types": "./dist/provider.d.ts",
226
+ "import": "./dist/provider.js",
227
+ "require": "./dist/provider.cjs"
228
+ },
229
+ "./utils": {
230
+ "types": "./dist/utils.d.ts",
231
+ "import": "./dist/utils.js",
232
+ "require": "./dist/utils.cjs"
233
+ },
234
+ "./explore": {
235
+ "import": "./dist/index.js",
236
+ "require": "./dist/index.cjs",
237
+ "types": "./dist/index.d.ts"
238
+ },
239
+ "./explore/*": {
240
+ "import": "./dist/*.js",
241
+ "require": "./dist/*.cjs",
242
+ "types": "./dist/*.d.ts"
243
+ }
244
+ },
245
+ "scripts": {
246
+ "build": "tsup",
247
+ "build:check": "tsc --noEmit",
248
+ "clean": "rm -rf dist",
249
+ "dev": "tsup --watch"
250
+ },
251
+ "dependencies": {
252
+ "@radix-ui/react-accordion": "^1.2.12",
253
+ "@radix-ui/react-avatar": "^1.1.10",
254
+ "@radix-ui/react-checkbox": "^1.3.3",
255
+ "@radix-ui/react-collapsible": "^1.1.12",
256
+ "@radix-ui/react-dialog": "^1.1.15",
257
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
258
+ "@radix-ui/react-label": "^2.1.7",
259
+ "@radix-ui/react-popover": "^1.1.15",
260
+ "@radix-ui/react-progress": "^1.1.7",
261
+ "@radix-ui/react-radio-group": "^1.3.8",
262
+ "@radix-ui/react-select": "^2.2.6",
263
+ "@radix-ui/react-slider": "^1.3.6",
264
+ "@radix-ui/react-slot": "^1.2.3",
265
+ "@radix-ui/react-switch": "^1.2.6",
266
+ "@radix-ui/react-tabs": "^1.1.13",
267
+ "@radix-ui/react-tooltip": "^1.2.8",
268
+ "@uidotdev/usehooks": "^2.4.1",
269
+ "class-variance-authority": "^0.7.1",
270
+ "clsx": "^2.1.1",
271
+ "es-toolkit": "^1.31.0",
272
+ "react-icons": "^5.4.0",
273
+ "sonner": "^2.0.0",
274
+ "tailwind-merge": "^3.0.0"
275
+ },
276
+ "peerDependencies": {
277
+ "next": "^14.0.0 || ^15.0.0 || ^16.0.0",
278
+ "next-themes": "^0.2.1 || ^0.3.0 || ^0.4.0",
279
+ "react": "^18.0.0 || ^19.0.0",
280
+ "react-dom": "^18.0.0 || ^19.0.0"
281
+ },
282
+ "peerDependenciesMeta": {
283
+ "next": {
284
+ "optional": true
285
+ },
286
+ "next-themes": {
287
+ "optional": true
288
+ }
289
+ },
290
+ "devDependencies": {
291
+ "@types/react": "^19.0.0",
292
+ "@types/react-dom": "^19.0.0",
293
+ "tsup": "^8.5.0",
294
+ "typescript": "^5.9.3"
295
+ }
296
+ }
@@ -0,0 +1,285 @@
1
+ import * as AccordionPrimitive from '@radix-ui/react-accordion';
2
+ import * as React from 'react';
3
+
4
+ import { cn } from './utils';
5
+
6
+ // Inline chevron icon (east-mini arrow)
7
+ const IndicatorIcon = ({ className }: { readonly className?: string }) => (
8
+ <svg className={ className } viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
9
+ <path d="M7.5 15L12.5 10L7.5 5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
10
+ </svg>
11
+ );
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // AccordionRoot
15
+ // ---------------------------------------------------------------------------
16
+
17
+ interface AccordionRootProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'defaultValue' | 'dir'> {
18
+
19
+ /** Open multiple items simultaneously. Defaults to true. */
20
+ readonly multiple?: boolean;
21
+
22
+ /** Controlled open items (array of `value` strings). */
23
+ readonly value?: Array<string>;
24
+
25
+ /** Uncontrolled initial open items. */
26
+ readonly defaultValue?: Array<string>;
27
+
28
+ /**
29
+ * Called when open items change.
30
+ * Wraps the value in `{ value }` to stay compatible with the Chakra callback shape
31
+ * that consumers already rely on.
32
+ */
33
+ readonly onValueChange?: (details: { value: Array<string> }) => void;
34
+ readonly variant?: 'outline' | 'faq';
35
+ readonly size?: 'sm' | 'md';
36
+
37
+ /** Accepted for compatibility; not applied visually. */
38
+ readonly noAnimation?: boolean;
39
+
40
+ /** Accepted for compatibility; Radix defers rendering internally. */
41
+ readonly lazyMount?: boolean;
42
+ readonly children?: React.ReactNode;
43
+ // Legacy Chakra style-prop shims
44
+ readonly position?: string;
45
+ readonly w?: string;
46
+ readonly bgColor?: string | Record<string, string>;
47
+ readonly borderRadius?: string;
48
+ }
49
+
50
+ export const AccordionRoot = React.forwardRef<HTMLDivElement, AccordionRootProps>(
51
+ function AccordionRoot(props, ref) {
52
+ const {
53
+ multiple: _multiple,
54
+ value,
55
+ defaultValue,
56
+ onValueChange,
57
+ variant,
58
+ size,
59
+ noAnimation: _noAnimation,
60
+ lazyMount: _lazyMount,
61
+ className,
62
+ children,
63
+ position: _position,
64
+ w: _w,
65
+ bgColor: _bgColor,
66
+ borderRadius: _borderRadius,
67
+ ...rest
68
+ } = props;
69
+
70
+ // Radix requires `type` discriminant. We default to "multiple" to match
71
+ // the original Chakra wrapper behavior.
72
+ const handleValueChange = React.useCallback(
73
+ (next: Array<string>) => {
74
+ onValueChange?.({ value: next });
75
+ },
76
+ [ onValueChange ],
77
+ );
78
+
79
+ return (
80
+ <AccordionPrimitive.Root
81
+ ref={ ref }
82
+ type="multiple"
83
+ value={ value }
84
+ defaultValue={ defaultValue }
85
+ onValueChange={ handleValueChange }
86
+ className={ cn(
87
+ 'w-full',
88
+ variant === 'faq' && 'accordion-faq',
89
+ size === 'sm' && 'accordion-sm',
90
+ className,
91
+ ) }
92
+ data-variant={ variant }
93
+ data-size={ size }
94
+ { ...rest }
95
+ >
96
+ { children }
97
+ </AccordionPrimitive.Root>
98
+ );
99
+ },
100
+ );
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // AccordionItem
104
+ // ---------------------------------------------------------------------------
105
+
106
+ interface AccordionItemProps extends React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> {
107
+ // Legacy Chakra style-prop shims
108
+ as?: string;
109
+ _first?: Record<string, unknown>;
110
+ _last?: Record<string, unknown>;
111
+ display?: string;
112
+ }
113
+
114
+ export const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
115
+ function AccordionItem(props, ref) {
116
+ const { className, as: _as, _first, _last, display: _display, ...rest } = props;
117
+ return (
118
+ <AccordionPrimitive.Item
119
+ ref={ ref }
120
+ className={ cn(
121
+ 'border-b border-[var(--color-border-divider)]',
122
+ className,
123
+ ) }
124
+ { ...rest }
125
+ />
126
+ );
127
+ },
128
+ );
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // AccordionItemTrigger
132
+ // ---------------------------------------------------------------------------
133
+
134
+ interface AccordionItemTriggerProps extends Omit<React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>, 'dir'> {
135
+ readonly indicatorPlacement?: 'start' | 'end';
136
+ readonly noIndicator?: boolean;
137
+ readonly variant?: 'outline' | 'faq';
138
+ // Legacy Chakra style-prop shims
139
+ px?: number | string;
140
+ py?: number | string;
141
+ _hover?: Record<string, string>;
142
+ wordBreak?: string;
143
+ textAlign?: string;
144
+ cursor?: string;
145
+ display?: string;
146
+ alignItems?: string;
147
+ columnGap?: number | string;
148
+ }
149
+
150
+ export const AccordionItemTrigger = React.forwardRef<
151
+ HTMLButtonElement,
152
+ AccordionItemTriggerProps
153
+ >(function AccordionItemTrigger(props, ref) {
154
+ const {
155
+ children, indicatorPlacement: indicatorPlacementProp, variant, noIndicator, className,
156
+ // Strip Chakra style props
157
+ px: _px, py: _py, _hover, wordBreak: _wordBreak, textAlign: _textAlign,
158
+ cursor: _cursor, display: _display, alignItems: _alignItems, columnGap: _columnGap,
159
+ ...rest
160
+ } = props;
161
+
162
+ const indicatorPlacement = variant === 'faq' ? 'start' : (indicatorPlacementProp ?? 'end');
163
+
164
+ const indicator = variant === 'faq' ? (
165
+ <span
166
+ className={ cn(
167
+ 'relative inline-block h-3 w-3 shrink-0',
168
+ // horizontal bar (always visible)
169
+ 'before:absolute before:left-0 before:top-1/2 before:block before:h-[2px] before:w-full before:-translate-y-1/2 before:rounded-[2px] before:bg-current',
170
+ // vertical bar (rotates to 0 when open)
171
+ 'after:absolute after:left-1/2 after:top-0 after:block after:h-full after:w-[2px]',
172
+ 'after:-translate-x-1/2 after:rounded-[2px] after:bg-current',
173
+ 'after:transition-transform after:duration-200 after:ease-in-out',
174
+ // When parent trigger has data-state="open", rotate vertical bar
175
+ 'group-data-[state=open]/trigger:after:rotate-90',
176
+ ) }
177
+ />
178
+ ) : (
179
+ <span
180
+ className={ cn(
181
+ 'inline-flex shrink-0 transition-transform duration-200',
182
+ 'rotate-180',
183
+ 'group-data-[state=open]/trigger:rotate-[270deg]',
184
+ ) }
185
+ >
186
+ <IndicatorIcon className="h-5 w-5"/>
187
+ </span>
188
+ );
189
+
190
+ return (
191
+ <AccordionPrimitive.Header asChild>
192
+ <div>
193
+ <AccordionPrimitive.Trigger
194
+ ref={ ref }
195
+ className={ cn(
196
+ 'group/trigger flex w-full cursor-pointer items-center gap-2 py-3 text-left text-[var(--color-text-primary)]',
197
+ 'hover:text-[var(--color-hover)]',
198
+ 'focus-visible:outline-none',
199
+ className,
200
+ ) }
201
+ { ...rest }
202
+ >
203
+ { indicatorPlacement === 'start' && !noIndicator && indicator }
204
+ { children }
205
+ { indicatorPlacement === 'end' && !noIndicator && indicator }
206
+ </AccordionPrimitive.Trigger>
207
+ </div>
208
+ </AccordionPrimitive.Header>
209
+ );
210
+ });
211
+
212
+ // ---------------------------------------------------------------------------
213
+ // AccordionItemContent
214
+ // ---------------------------------------------------------------------------
215
+
216
+ export interface AccordionItemContentProps extends React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content> {
217
+ // Legacy Chakra style-prop shims
218
+ pb?: number | string;
219
+ pr?: number | string;
220
+ pl?: string;
221
+ w?: string;
222
+ display?: string;
223
+ flexDir?: string;
224
+ rowGap?: number | string;
225
+ }
226
+
227
+ export const AccordionItemContent = React.forwardRef<
228
+ HTMLDivElement,
229
+ AccordionItemContentProps
230
+ >(function AccordionItemContent(props, ref) {
231
+ const { className, children, pb: _pb, pr: _pr, pl: _pl, w: _w, display: _display, flexDir: _flexDir, rowGap: _rowGap, ...rest } = props;
232
+ return (
233
+ <AccordionPrimitive.Content
234
+ ref={ ref }
235
+ className={ cn(
236
+ 'overflow-hidden text-[var(--color-text-primary)]',
237
+ 'data-[state=open]:animate-accordion-down',
238
+ 'data-[state=closed]:animate-accordion-up',
239
+ className,
240
+ ) }
241
+ { ...rest }
242
+ >
243
+ <div className="pb-3">
244
+ { children }
245
+ </div>
246
+ </AccordionPrimitive.Content>
247
+ );
248
+ });
249
+
250
+ // ---------------------------------------------------------------------------
251
+ // useAccordion
252
+ // ---------------------------------------------------------------------------
253
+
254
+ export function useAccordion(items: Array<{ id: string }>) {
255
+ const [ value, setValue ] = React.useState<Array<string>>([]);
256
+
257
+ const onValueChange = React.useCallback(({ value }: { value: Array<string> }) => {
258
+ setValue(value);
259
+ }, []);
260
+
261
+ const scrollToItemFromUrl = React.useCallback(() => {
262
+ const hash = window.location.hash.replace('#', '');
263
+
264
+ if (!hash) {
265
+ return;
266
+ }
267
+
268
+ const itemToScroll = items.find((item) => item.id === hash);
269
+ if (itemToScroll) {
270
+ const el = document.getElementById(itemToScroll.id);
271
+ if (el) {
272
+ el.scrollIntoView({ behavior: 'smooth', block: 'start' });
273
+ }
274
+ setValue([ itemToScroll.id ]);
275
+ }
276
+ }, [ items ]);
277
+
278
+ return React.useMemo(() => {
279
+ return {
280
+ value,
281
+ onValueChange,
282
+ scrollToItemFromUrl,
283
+ };
284
+ }, [ value, onValueChange, scrollToItemFromUrl ]);
285
+ }