@cdx-ui/components 0.0.1-beta.171 → 0.0.1-beta.172

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 (31) hide show
  1. package/coverage.svg +1 -1
  2. package/lib/commonjs/components/Accordion/index.js +295 -0
  3. package/lib/commonjs/components/Accordion/index.js.map +1 -0
  4. package/lib/commonjs/components/Accordion/styles.js +49 -0
  5. package/lib/commonjs/components/Accordion/styles.js.map +1 -0
  6. package/lib/commonjs/components/index.js +12 -0
  7. package/lib/commonjs/components/index.js.map +1 -1
  8. package/lib/commonjs/figma/Accordion.figma.js +47 -0
  9. package/lib/commonjs/figma/Accordion.figma.js.map +1 -0
  10. package/lib/module/components/Accordion/index.js +291 -0
  11. package/lib/module/components/Accordion/index.js.map +1 -0
  12. package/lib/module/components/Accordion/styles.js +45 -0
  13. package/lib/module/components/Accordion/styles.js.map +1 -0
  14. package/lib/module/components/index.js +1 -0
  15. package/lib/module/components/index.js.map +1 -1
  16. package/lib/module/figma/Accordion.figma.js +41 -0
  17. package/lib/module/figma/Accordion.figma.js.map +1 -0
  18. package/lib/typescript/components/Accordion/index.d.ts +85 -0
  19. package/lib/typescript/components/Accordion/index.d.ts.map +1 -0
  20. package/lib/typescript/components/Accordion/styles.d.ts +18 -0
  21. package/lib/typescript/components/Accordion/styles.d.ts.map +1 -0
  22. package/lib/typescript/components/index.d.ts +1 -0
  23. package/lib/typescript/components/index.d.ts.map +1 -1
  24. package/lib/typescript/figma/Accordion.figma.d.ts +8 -0
  25. package/lib/typescript/figma/Accordion.figma.d.ts.map +1 -0
  26. package/lib/typescript/figma/Alert.Button.figma.d.ts +1 -1
  27. package/lib/typescript/figma/Alert.Close.figma.d.ts +1 -1
  28. package/package.json +5 -5
  29. package/src/components/Accordion/index.tsx +388 -0
  30. package/src/components/Accordion/styles.ts +82 -0
  31. package/src/components/index.ts +1 -0
@@ -0,0 +1,388 @@
1
+ import { forwardRef, useMemo, type ReactNode } from 'react';
2
+ import { Pressable, Text, View, type TextProps, type ViewProps } from 'react-native';
3
+ import {
4
+ AccordionItemRoot as AccordionItemRootPrimitive,
5
+ AccordionPanelRoot as AccordionPanelRootPrimitive,
6
+ AccordionRoot as AccordionRootPrimitive,
7
+ AccordionTriggerRoot as AccordionTriggerRootPrimitive,
8
+ Slot,
9
+ dataAttributes,
10
+ mergeDataAttributes,
11
+ useAccordionItemContext,
12
+ type IAccordionItemProps,
13
+ type IAccordionProps,
14
+ type IAccordionTriggerProps,
15
+ } from '@cdx-ui/primitives';
16
+ import { KeyboardArrowDown } from '@cdx-ui/icons';
17
+ import { cn, ParentContext, useParentContext, useStyleContext } from '@cdx-ui/utils';
18
+ import { Divider } from '../Divider';
19
+ import { Icon } from '../Icon';
20
+ import {
21
+ accordionBodyVariants,
22
+ accordionDescriptionVariants,
23
+ accordionHeadingVariants,
24
+ accordionIndicatorVariants,
25
+ accordionItemVariants,
26
+ accordionLabelVariants,
27
+ accordionLeadingSlotVariants,
28
+ accordionMetaVariants,
29
+ accordionRootVariants,
30
+ accordionTitleVariants,
31
+ accordionTriggerVariants,
32
+ type AccordionItemVariantProps,
33
+ } from './styles';
34
+
35
+ const ITEM_SCOPE = 'ACCORDION_ITEM';
36
+
37
+ type AccordionItemStyleContext = Pick<AccordionItemVariantProps, 'disableGutters'> & {
38
+ showSeparator?: boolean;
39
+ };
40
+
41
+ const useAccordionItemStyleContext = (): AccordionItemStyleContext => {
42
+ const ctx = useStyleContext(ITEM_SCOPE) as AccordionItemStyleContext | undefined;
43
+ return {
44
+ disableGutters: ctx?.disableGutters ?? false,
45
+ showSeparator: ctx?.showSeparator ?? true,
46
+ };
47
+ };
48
+
49
+ // =============================================================================
50
+ // ROOT
51
+ // =============================================================================
52
+
53
+ export interface AccordionProps extends IAccordionProps {
54
+ className?: string;
55
+ children?: ReactNode;
56
+ }
57
+
58
+ const AccordionRoot = forwardRef<View, AccordionProps>(
59
+ ({ className, style, children, ...props }, ref) => (
60
+ <AccordionRootPrimitive
61
+ ref={ref}
62
+ className={cn(accordionRootVariants(), className)}
63
+ style={style}
64
+ {...props}
65
+ >
66
+ {children}
67
+ </AccordionRootPrimitive>
68
+ ),
69
+ );
70
+
71
+ AccordionRoot.displayName = 'Accordion';
72
+
73
+ // =============================================================================
74
+ // ITEM
75
+ // =============================================================================
76
+
77
+ export interface AccordionItemProps extends IAccordionItemProps {
78
+ className?: string;
79
+ children?: ReactNode;
80
+ /** Removes horizontal padding on trigger and body. */
81
+ disableGutters?: boolean;
82
+ /** Renders a Divider below the item when true. @default true */
83
+ showSeparator?: boolean;
84
+ }
85
+
86
+ const AccordionItem = forwardRef<View, AccordionItemProps>(
87
+ ({ className, style, children, disableGutters = false, showSeparator = true, ...props }, ref) => {
88
+ const parent = useParentContext();
89
+ const ctx = useMemo(
90
+ () => ({ ...parent, [ITEM_SCOPE]: { disableGutters, showSeparator } }),
91
+ [parent, disableGutters, showSeparator],
92
+ );
93
+
94
+ return (
95
+ <ParentContext.Provider value={ctx}>
96
+ <AccordionItemRootPrimitive
97
+ ref={ref}
98
+ className={cn(accordionItemVariants(), className)}
99
+ style={style}
100
+ {...props}
101
+ >
102
+ {children}
103
+ {showSeparator ? <Divider /> : null}
104
+ </AccordionItemRootPrimitive>
105
+ </ParentContext.Provider>
106
+ );
107
+ },
108
+ );
109
+
110
+ AccordionItem.displayName = 'Accordion.Item';
111
+
112
+ // =============================================================================
113
+ // TRIGGER
114
+ // =============================================================================
115
+
116
+ export interface AccordionTriggerProps extends IAccordionTriggerProps {
117
+ /**
118
+ * Substitute the rendered trigger element with the single child. The child must
119
+ * be another Forge primitive (IconButton, Chip, Button) — anything whose
120
+ * underlying Pressable accepts Pressable-shaped handlers. When set, the
121
+ * auto-injected expand indicator is skipped and `accordionTriggerVariants` is not
122
+ * applied so the substituted component's own appearance wins.
123
+ */
124
+ asChild?: boolean;
125
+ className?: string;
126
+ children?: ReactNode;
127
+ }
128
+
129
+ const AccordionTrigger = forwardRef<React.ComponentRef<typeof Pressable>, AccordionTriggerProps>(
130
+ ({ className, style, children, asChild, accessibilityLabel, ...props }, ref) => {
131
+ const { disableGutters } = useAccordionItemStyleContext();
132
+
133
+ const computedClassName = asChild
134
+ ? className
135
+ : cn(accordionTriggerVariants({ disableGutters }), className);
136
+
137
+ return (
138
+ <View className={cn(accordionHeadingVariants())}>
139
+ <AccordionTriggerRootPrimitive
140
+ ref={ref}
141
+ asChild={asChild}
142
+ accessibilityLabel={accessibilityLabel}
143
+ className={computedClassName}
144
+ style={style}
145
+ {...props}
146
+ >
147
+ {asChild ? (
148
+ children
149
+ ) : (
150
+ <>
151
+ {children}
152
+ <AccordionIndicator />
153
+ </>
154
+ )}
155
+ </AccordionTriggerRootPrimitive>
156
+ </View>
157
+ );
158
+ },
159
+ );
160
+
161
+ AccordionTrigger.displayName = 'Accordion.Trigger';
162
+
163
+ // =============================================================================
164
+ // INDICATOR
165
+ // =============================================================================
166
+
167
+ export interface AccordionIndicatorProps extends ViewProps {
168
+ className?: string;
169
+ }
170
+
171
+ const AccordionIndicator = forwardRef<View, AccordionIndicatorProps>(
172
+ ({ className, style, ...props }, ref) => {
173
+ const { isExpanded } = useAccordionItemContext();
174
+
175
+ return (
176
+ <View
177
+ ref={ref}
178
+ className={cn(accordionIndicatorVariants(), className)}
179
+ style={style}
180
+ {...props}
181
+ {...dataAttributes({
182
+ slot: 'accordion-indicator',
183
+ state: isExpanded ? 'open' : 'closed',
184
+ })}
185
+ >
186
+ <Icon as={KeyboardArrowDown} size={24} />
187
+ </View>
188
+ );
189
+ },
190
+ );
191
+
192
+ AccordionIndicator.displayName = 'Accordion.Indicator';
193
+
194
+ // =============================================================================
195
+ // SLOTS
196
+ // =============================================================================
197
+
198
+ export interface AccordionLeadingSlotProps extends ViewProps {
199
+ asChild?: boolean;
200
+ className?: string;
201
+ 'aria-hidden'?: boolean;
202
+ }
203
+
204
+ const AccordionLeadingSlot = forwardRef<View, AccordionLeadingSlotProps>(
205
+ ({ asChild, className, style, 'aria-hidden': ariaHidden, ...props }, ref) => {
206
+ const Comp = asChild ? Slot : View;
207
+ return (
208
+ <Comp
209
+ ref={ref}
210
+ accessibilityElementsHidden={ariaHidden !== false}
211
+ aria-hidden={ariaHidden ?? true}
212
+ className={cn(accordionLeadingSlotVariants(), className)}
213
+ style={style}
214
+ {...props}
215
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'accordion-leading' }))}
216
+ />
217
+ );
218
+ },
219
+ );
220
+
221
+ AccordionLeadingSlot.displayName = 'Accordion.LeadingSlot';
222
+
223
+ export interface AccordionLabelProps extends ViewProps {
224
+ asChild?: boolean;
225
+ className?: string;
226
+ }
227
+
228
+ const AccordionLabel = forwardRef<View, AccordionLabelProps>(
229
+ ({ asChild, className, style, ...props }, ref) => {
230
+ const Comp = asChild ? Slot : View;
231
+ return (
232
+ <Comp
233
+ ref={ref}
234
+ className={cn(accordionLabelVariants(), className)}
235
+ style={style}
236
+ {...props}
237
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'accordion-label' }))}
238
+ />
239
+ );
240
+ },
241
+ );
242
+
243
+ AccordionLabel.displayName = 'Accordion.Label';
244
+
245
+ export interface AccordionTitleProps extends TextProps {
246
+ asChild?: boolean;
247
+ className?: string;
248
+ }
249
+
250
+ const AccordionTitle = forwardRef<Text, AccordionTitleProps>(
251
+ ({ asChild, className, style, ...props }, ref) => {
252
+ const Comp = asChild ? Slot : Text;
253
+ return (
254
+ <Comp
255
+ ref={ref}
256
+ className={cn(accordionTitleVariants(), className)}
257
+ style={style}
258
+ {...props}
259
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'accordion-title' }))}
260
+ />
261
+ );
262
+ },
263
+ );
264
+
265
+ AccordionTitle.displayName = 'Accordion.Title';
266
+
267
+ export interface AccordionDescriptionProps extends TextProps {
268
+ asChild?: boolean;
269
+ className?: string;
270
+ }
271
+
272
+ const AccordionDescription = forwardRef<Text, AccordionDescriptionProps>(
273
+ ({ asChild, className, style, ...props }, ref) => {
274
+ const Comp = asChild ? Slot : Text;
275
+ return (
276
+ <Comp
277
+ ref={ref}
278
+ className={cn(accordionDescriptionVariants(), className)}
279
+ style={style}
280
+ {...props}
281
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'accordion-description' }))}
282
+ />
283
+ );
284
+ },
285
+ );
286
+
287
+ AccordionDescription.displayName = 'Accordion.Description';
288
+
289
+ export interface AccordionMetaProps extends TextProps {
290
+ asChild?: boolean;
291
+ className?: string;
292
+ }
293
+
294
+ const AccordionMeta = forwardRef<Text, AccordionMetaProps>(
295
+ ({ asChild, className, style, ...props }, ref) => {
296
+ const Comp = asChild ? Slot : Text;
297
+ return (
298
+ <Comp
299
+ ref={ref}
300
+ className={cn(accordionMetaVariants(), className)}
301
+ style={style}
302
+ {...props}
303
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'accordion-meta' }))}
304
+ />
305
+ );
306
+ },
307
+ );
308
+
309
+ AccordionMeta.displayName = 'Accordion.Meta';
310
+
311
+ // =============================================================================
312
+ // PANEL / BODY
313
+ // =============================================================================
314
+
315
+ export interface AccordionPanelProps extends ViewProps {
316
+ className?: string;
317
+ children?: ReactNode;
318
+ }
319
+
320
+ const AccordionPanel = forwardRef<View, AccordionPanelProps>(
321
+ ({ className, style, children, ...props }, ref) => (
322
+ <AccordionPanelRootPrimitive
323
+ ref={ref}
324
+ className={cn('w-full', className)}
325
+ style={style}
326
+ {...props}
327
+ >
328
+ {children}
329
+ </AccordionPanelRootPrimitive>
330
+ ),
331
+ );
332
+
333
+ AccordionPanel.displayName = 'Accordion.Panel';
334
+
335
+ export interface AccordionBodyProps extends ViewProps {
336
+ className?: string;
337
+ children?: ReactNode;
338
+ }
339
+
340
+ const AccordionBody = forwardRef<View, AccordionBodyProps>(
341
+ ({ className, style, children, ...props }, ref) => {
342
+ const { disableGutters } = useAccordionItemStyleContext();
343
+
344
+ return (
345
+ <View
346
+ ref={ref}
347
+ className={cn(accordionBodyVariants({ disableGutters }), className)}
348
+ style={style}
349
+ {...props}
350
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'accordion-body' }))}
351
+ >
352
+ {children}
353
+ </View>
354
+ );
355
+ },
356
+ );
357
+
358
+ AccordionBody.displayName = 'Accordion.Body';
359
+
360
+ // =============================================================================
361
+ // COMPOUND EXPORT
362
+ // =============================================================================
363
+
364
+ type AccordionCompoundComponent = typeof AccordionRoot & {
365
+ Item: typeof AccordionItem;
366
+ Trigger: typeof AccordionTrigger;
367
+ LeadingSlot: typeof AccordionLeadingSlot;
368
+ Label: typeof AccordionLabel;
369
+ Title: typeof AccordionTitle;
370
+ Description: typeof AccordionDescription;
371
+ Meta: typeof AccordionMeta;
372
+ Indicator: typeof AccordionIndicator;
373
+ Panel: typeof AccordionPanel;
374
+ Body: typeof AccordionBody;
375
+ };
376
+
377
+ export const Accordion = Object.assign(AccordionRoot, {
378
+ Item: AccordionItem,
379
+ Trigger: AccordionTrigger,
380
+ LeadingSlot: AccordionLeadingSlot,
381
+ Label: AccordionLabel,
382
+ Title: AccordionTitle,
383
+ Description: AccordionDescription,
384
+ Meta: AccordionMeta,
385
+ Indicator: AccordionIndicator,
386
+ Panel: AccordionPanel,
387
+ Body: AccordionBody,
388
+ }) as AccordionCompoundComponent;
@@ -0,0 +1,82 @@
1
+ import { Platform } from 'react-native';
2
+ import { cva, type VariantProps } from '@cdx-ui/utils';
3
+ import { DISABLED_CURSOR, TRANSITION_COLORS } from '../../styles/constants';
4
+
5
+ export const accordionRootVariants = cva(['flex w-full flex-col']);
6
+
7
+ export const accordionItemVariants = cva([
8
+ 'flex w-full flex-col overflow-clip',
9
+ 'data-[disabled=true]:opacity-[var(--opacity-disabled)]',
10
+ ]);
11
+
12
+ export const accordionHeadingVariants = cva([
13
+ 'relative w-full bg-[var(--color-transparent,rgba(255,255,255,0))]',
14
+ Platform.select({
15
+ web: [
16
+ 'before:pointer-events-none before:absolute before:inset-0 before:z-0 before:bg-content-primary before:opacity-0 before:content-[""]',
17
+ 'before:transition-opacity before:duration-150 before:ease-out',
18
+ // Overlay tracks trigger interaction data-* (Figma Accordion.Heading > [background-color]).
19
+ 'has-[[data-hover=true]]:before:opacity-[var(--opacity-hover)]',
20
+ 'has-[[data-active=true][data-hover=true]]:before:opacity-[var(--opacity-hover-overlay)]',
21
+ 'has-[[data-focus-visible=true]]:before:opacity-[var(--opacity-focus)]',
22
+ ].join(' '),
23
+ default: '',
24
+ }),
25
+ ]);
26
+
27
+ export const accordionTriggerVariants = cva(
28
+ [
29
+ 'relative z-10 flex w-full flex-row items-center gap-3 min-h-[52px] bg-transparent outline-none',
30
+ 'web:outline-none web:focus:outline-none web:focus-visible:outline-none',
31
+ DISABLED_CURSOR,
32
+ Platform.select({
33
+ web: '',
34
+ default: 'data-[active=true]:bg-surface-primary-hover',
35
+ }),
36
+ ],
37
+ {
38
+ variants: {
39
+ disableGutters: {
40
+ true: 'px-0 py-4',
41
+ false: 'p-4',
42
+ },
43
+ },
44
+ defaultVariants: {
45
+ disableGutters: false,
46
+ },
47
+ },
48
+ );
49
+
50
+ export const accordionLeadingSlotVariants = cva([
51
+ 'relative z-10 flex shrink-0 items-center justify-center',
52
+ ]);
53
+
54
+ export const accordionLabelVariants = cva([
55
+ 'relative z-10 flex min-w-0 flex-1 flex-col justify-center gap-0.5',
56
+ ]);
57
+
58
+ export const accordionTitleVariants = cva(['body-md font-medium text-content-primary']);
59
+
60
+ export const accordionDescriptionVariants = cva(['body-sm text-content-secondary']);
61
+
62
+ export const accordionMetaVariants = cva(['body-xs text-content-secondary']);
63
+
64
+ export const accordionIndicatorVariants = cva([
65
+ 'relative z-10 flex shrink-0 items-center justify-center text-content-primary',
66
+ 'data-[state=open]:rotate-180',
67
+ TRANSITION_COLORS,
68
+ ]);
69
+
70
+ export const accordionBodyVariants = cva(['flex w-full flex-col gap-2 pb-4'], {
71
+ variants: {
72
+ disableGutters: {
73
+ true: 'px-0',
74
+ false: 'px-4',
75
+ },
76
+ },
77
+ defaultVariants: {
78
+ disableGutters: false,
79
+ },
80
+ });
81
+
82
+ export type AccordionItemVariantProps = VariantProps<typeof accordionTriggerVariants>;
@@ -31,6 +31,7 @@ export * from './Input';
31
31
  export * from './OtpInput';
32
32
  export * from './Link';
33
33
  export * from './ListItem';
34
+ export * from './Accordion';
34
35
  export * from './Menu';
35
36
  export * from './ProgressBar';
36
37
  export * from './ProgressCircle';