@cdx-ui/components 0.0.1-beta.171 → 0.0.1-beta.173
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/coverage.svg +1 -1
- package/lib/commonjs/components/Accordion/index.js +295 -0
- package/lib/commonjs/components/Accordion/index.js.map +1 -0
- package/lib/commonjs/components/Accordion/styles.js +49 -0
- package/lib/commonjs/components/Accordion/styles.js.map +1 -0
- package/lib/commonjs/components/Menu/index.js +7 -3
- package/lib/commonjs/components/Menu/index.js.map +1 -1
- package/lib/commonjs/components/Select/index.js +13 -8
- package/lib/commonjs/components/Select/index.js.map +1 -1
- package/lib/commonjs/components/index.js +12 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/figma/Accordion.figma.js +47 -0
- package/lib/commonjs/figma/Accordion.figma.js.map +1 -0
- package/lib/commonjs/providers/forge-design/provider.js +11 -6
- package/lib/commonjs/providers/forge-design/provider.js.map +1 -1
- package/lib/module/components/Accordion/index.js +291 -0
- package/lib/module/components/Accordion/index.js.map +1 -0
- package/lib/module/components/Accordion/styles.js +45 -0
- package/lib/module/components/Accordion/styles.js.map +1 -0
- package/lib/module/components/Menu/index.js +7 -3
- package/lib/module/components/Menu/index.js.map +1 -1
- package/lib/module/components/Select/index.js +13 -8
- package/lib/module/components/Select/index.js.map +1 -1
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/figma/Accordion.figma.js +41 -0
- package/lib/module/figma/Accordion.figma.js.map +1 -0
- package/lib/module/providers/forge-design/provider.js +11 -6
- package/lib/module/providers/forge-design/provider.js.map +1 -1
- package/lib/typescript/components/Accordion/index.d.ts +85 -0
- package/lib/typescript/components/Accordion/index.d.ts.map +1 -0
- package/lib/typescript/components/Accordion/styles.d.ts +18 -0
- package/lib/typescript/components/Accordion/styles.d.ts.map +1 -0
- package/lib/typescript/components/Menu/index.d.ts.map +1 -1
- package/lib/typescript/components/Select/index.d.ts +11 -0
- package/lib/typescript/components/Select/index.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/figma/Accordion.figma.d.ts +8 -0
- package/lib/typescript/figma/Accordion.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Alert.Button.figma.d.ts +1 -1
- package/lib/typescript/figma/Alert.Close.figma.d.ts +1 -1
- package/lib/typescript/providers/forge-design/provider.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/components/Accordion/index.tsx +388 -0
- package/src/components/Accordion/styles.ts +82 -0
- package/src/components/Menu/index.tsx +5 -1
- package/src/components/Select/index.tsx +39 -8
- package/src/components/index.ts +1 -0
- package/src/providers/forge-design/provider.tsx +15 -6
|
@@ -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>;
|
|
@@ -184,6 +184,10 @@ const MenuContent = forwardRef<View, MenuContentProps>(
|
|
|
184
184
|
};
|
|
185
185
|
|
|
186
186
|
const computedClassName = cn(menuRootVariants({ scrollable: shouldScroll }), className);
|
|
187
|
+
// Embedded surfaces inherit the parent collection's slot identity.
|
|
188
|
+
const contentDataAttributes = asSurface
|
|
189
|
+
? mergeDataAttributes(dataAttributes({ slot: 'menu-content' }), props)
|
|
190
|
+
: mergeDataAttributes(props, dataAttributes({ slot: 'menu-content' }));
|
|
187
191
|
|
|
188
192
|
const content = shouldScroll ? (
|
|
189
193
|
<ScrollView
|
|
@@ -207,7 +211,7 @@ const MenuContent = forwardRef<View, MenuContentProps>(
|
|
|
207
211
|
className={computedClassName}
|
|
208
212
|
style={[style, constrainedStyle]}
|
|
209
213
|
{...props}
|
|
210
|
-
{...
|
|
214
|
+
{...contentDataAttributes}
|
|
211
215
|
>
|
|
212
216
|
{content}
|
|
213
217
|
</MenuRootPrimitive>
|
|
@@ -89,18 +89,38 @@ export interface SelectTriggerProps extends ISelectTriggerProps {
|
|
|
89
89
|
* trigger instead of relying on `data-slot`.
|
|
90
90
|
*/
|
|
91
91
|
asChild?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Replaces the default open/closed chevron. Pass `null` to suppress the
|
|
94
|
+
* indicator. When `asChild` is set, only an explicitly provided indicator is
|
|
95
|
+
* appended to the substituted child.
|
|
96
|
+
*/
|
|
97
|
+
indicator?: ReactNode;
|
|
92
98
|
className?: string;
|
|
93
99
|
children?: ReactNode;
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
const SelectTrigger = forwardRef<View, SelectTriggerProps>(
|
|
97
|
-
({ asChild, className, children, style, ...props }, ref) => {
|
|
103
|
+
({ asChild, indicator, className, children, style, ...props }, ref) => {
|
|
98
104
|
const { open } = useSelectContext();
|
|
99
105
|
const { variant, size } = useSelectStyleContext();
|
|
100
106
|
|
|
101
107
|
const computedClassName = asChild
|
|
102
108
|
? className
|
|
103
109
|
: cn(selectTriggerVariants({ variant, size }), className);
|
|
110
|
+
const triggerIndicator =
|
|
111
|
+
indicator === undefined ? (
|
|
112
|
+
asChild ? undefined : (
|
|
113
|
+
<SelectIcon as={open ? KeyboardArrowUp : KeyboardArrowDown} />
|
|
114
|
+
)
|
|
115
|
+
) : (
|
|
116
|
+
indicator
|
|
117
|
+
);
|
|
118
|
+
const triggerChildren =
|
|
119
|
+
asChild &&
|
|
120
|
+
triggerIndicator !== undefined &&
|
|
121
|
+
isValidElement<{ children?: ReactNode }>(children)
|
|
122
|
+
? cloneElement(children, undefined, children.props.children, triggerIndicator)
|
|
123
|
+
: children;
|
|
104
124
|
|
|
105
125
|
return (
|
|
106
126
|
<SelectTriggerPrimitive
|
|
@@ -112,11 +132,11 @@ const SelectTrigger = forwardRef<View, SelectTriggerProps>(
|
|
|
112
132
|
{...mergeDataAttributes(props, dataAttributes({ slot: 'select-trigger' }))}
|
|
113
133
|
>
|
|
114
134
|
{asChild ? (
|
|
115
|
-
|
|
135
|
+
triggerChildren
|
|
116
136
|
) : (
|
|
117
137
|
<>
|
|
118
138
|
{children}
|
|
119
|
-
|
|
139
|
+
{triggerIndicator}
|
|
120
140
|
</>
|
|
121
141
|
)}
|
|
122
142
|
</SelectTriggerPrimitive>
|
|
@@ -243,6 +263,7 @@ const SelectContent = forwardRef<View, SelectContentProps>(
|
|
|
243
263
|
return (
|
|
244
264
|
<SelectContentPrimitive
|
|
245
265
|
ref={ref}
|
|
266
|
+
asChild
|
|
246
267
|
style={style}
|
|
247
268
|
{...props}
|
|
248
269
|
{...mergeDataAttributes(props, dataAttributes({ slot: 'select-content' }))}
|
|
@@ -263,29 +284,39 @@ SelectContent.displayName = 'Select.Content';
|
|
|
263
284
|
|
|
264
285
|
export interface SelectItemProps extends ISelectItemProps {
|
|
265
286
|
asChild?: boolean;
|
|
287
|
+
/**
|
|
288
|
+
* Replaces the default selected-item checkmark. The replacement is rendered
|
|
289
|
+
* only while the item is selected. Pass `null` to suppress the indicator.
|
|
290
|
+
*/
|
|
291
|
+
indicator?: ReactNode;
|
|
266
292
|
className?: string;
|
|
267
293
|
children?: ReactNode;
|
|
268
294
|
}
|
|
269
295
|
|
|
270
296
|
const SelectItem = forwardRef<View, SelectItemProps>(
|
|
271
|
-
({ asChild, className, children, style, value: itemValue, ...props }, ref) => {
|
|
297
|
+
({ asChild, indicator, className, children, style, value: itemValue, ...props }, ref) => {
|
|
272
298
|
const { size } = useSelectStyleContext();
|
|
273
299
|
const { value: selectedValue } = useSelectContext();
|
|
274
300
|
const menuSize = toMenuItemSize(size);
|
|
275
301
|
const checked = selectedValue === itemValue;
|
|
276
302
|
|
|
277
303
|
const computedClassName = cn(menuItemVariants({ size: menuSize }), className);
|
|
278
|
-
const
|
|
304
|
+
const selectedIndicator =
|
|
305
|
+
indicator === undefined ? (
|
|
306
|
+
<Menu.ItemIndicator isChecked={checked} />
|
|
307
|
+
) : checked ? (
|
|
308
|
+
indicator
|
|
309
|
+
) : null;
|
|
279
310
|
const itemChildren = asChild ? (
|
|
280
|
-
|
|
281
|
-
cloneElement(children, undefined, children.props.children,
|
|
311
|
+
isValidElement<{ children?: ReactNode }>(children) ? (
|
|
312
|
+
cloneElement(children, undefined, children.props.children, selectedIndicator)
|
|
282
313
|
) : (
|
|
283
314
|
children
|
|
284
315
|
)
|
|
285
316
|
) : (
|
|
286
317
|
<>
|
|
287
318
|
{children}
|
|
288
|
-
{
|
|
319
|
+
{selectedIndicator}
|
|
289
320
|
</>
|
|
290
321
|
);
|
|
291
322
|
|
package/src/components/index.ts
CHANGED
|
@@ -13,6 +13,15 @@ import { TextComponentProvider } from '../text-component/provider';
|
|
|
13
13
|
import { ToastProvider } from '../toast/provider';
|
|
14
14
|
import type { ForgeDesignProviderProps } from './types';
|
|
15
15
|
|
|
16
|
+
function areInsetsEqual(current: EdgeInsets, next: EdgeInsets): boolean {
|
|
17
|
+
return (
|
|
18
|
+
current.top === next.top &&
|
|
19
|
+
current.right === next.right &&
|
|
20
|
+
current.bottom === next.bottom &&
|
|
21
|
+
current.left === next.left
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
/**
|
|
17
26
|
* ForgeDesignProvider Component
|
|
18
27
|
*
|
|
@@ -41,17 +50,17 @@ const ForgeDesignProvider: React.FC<ForgeDesignProviderProps> = ({ children, con
|
|
|
41
50
|
left: 0,
|
|
42
51
|
});
|
|
43
52
|
|
|
53
|
+
const handleSafeAreaChange = React.useCallback(({ insets }: { insets: EdgeInsets }) => {
|
|
54
|
+
Uniwind.updateInsets(insets);
|
|
55
|
+
setOverlayInsets((current) => (areInsetsEqual(current, insets) ? current : insets));
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
44
58
|
// Determine if toast should be enabled and get props
|
|
45
59
|
const isToastEnabled = toast !== false && toast !== 'disabled';
|
|
46
60
|
const toastProps = typeof toast === 'object' ? toast : {};
|
|
47
61
|
|
|
48
62
|
return (
|
|
49
|
-
<SafeAreaListener
|
|
50
|
-
onChange={({ insets }) => {
|
|
51
|
-
Uniwind.updateInsets(insets);
|
|
52
|
-
setOverlayInsets(insets);
|
|
53
|
-
}}
|
|
54
|
-
>
|
|
63
|
+
<SafeAreaListener onChange={handleSafeAreaChange}>
|
|
55
64
|
<PortalProvider>
|
|
56
65
|
<OverlayStackProvider>
|
|
57
66
|
<OverlayInsetsProvider value={overlayInsets}>
|