@seed-design/lynx-react 0.4.1 → 0.5.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 (96) hide show
  1. package/lib/components/Accordion/Accordion.d.ts +56 -0
  2. package/lib/components/Accordion/Accordion.d.ts.map +1 -0
  3. package/lib/components/Accordion/Accordion.jsx +210 -0
  4. package/lib/components/Accordion/Accordion.namespace.d.ts +2 -0
  5. package/lib/components/Accordion/Accordion.namespace.d.ts.map +1 -0
  6. package/lib/components/Accordion/Accordion.namespace.js +17 -0
  7. package/lib/components/Accordion/index.d.ts +3 -0
  8. package/lib/components/Accordion/index.d.ts.map +1 -0
  9. package/lib/components/ActionButton/ActionButton.jsx +6 -6
  10. package/lib/components/AppBar/AppBar.jsx +1 -1
  11. package/lib/components/Badge/Badge.jsx +1 -1
  12. package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -1
  13. package/lib/components/BottomSheet/BottomSheet.jsx +6 -3
  14. package/lib/components/Box/Box.jsx +1 -1
  15. package/lib/components/Callout/Callout.d.ts +32 -0
  16. package/lib/components/Callout/Callout.d.ts.map +1 -0
  17. package/lib/components/Callout/Callout.jsx +113 -0
  18. package/lib/components/Callout/Callout.namespace.d.ts +2 -0
  19. package/lib/components/Callout/Callout.namespace.d.ts.map +1 -0
  20. package/lib/components/Callout/Callout.namespace.js +13 -0
  21. package/lib/components/Callout/index.d.ts +3 -0
  22. package/lib/components/Callout/index.d.ts.map +1 -0
  23. package/lib/components/Checkbox/Checkbox.d.ts.map +1 -1
  24. package/lib/components/Checkbox/Checkbox.jsx +36 -5
  25. package/lib/components/Chip/Chip.d.ts +66 -0
  26. package/lib/components/Chip/Chip.d.ts.map +1 -0
  27. package/lib/components/Chip/Chip.jsx +159 -0
  28. package/lib/components/Chip/Chip.namespace.d.ts +2 -0
  29. package/lib/components/Chip/Chip.namespace.d.ts.map +1 -0
  30. package/lib/components/Chip/Chip.namespace.js +16 -0
  31. package/lib/components/Chip/index.d.ts +3 -0
  32. package/lib/components/Chip/index.d.ts.map +1 -0
  33. package/lib/components/Divider/Divider.d.ts +31 -0
  34. package/lib/components/Divider/Divider.d.ts.map +1 -0
  35. package/lib/components/Divider/Divider.jsx +26 -0
  36. package/lib/components/Divider/index.d.ts +2 -0
  37. package/lib/components/Divider/index.d.ts.map +1 -0
  38. package/lib/components/Field/Field.jsx +1 -1
  39. package/lib/components/Icon/Icon.jsx +2 -2
  40. package/lib/components/ProgressCircle/ProgressCircle.jsx +2 -2
  41. package/lib/components/RadioGroup/RadioGroup.jsx +2 -2
  42. package/lib/components/Stack/Stack.jsx +1 -1
  43. package/lib/components/Switch/Switch.jsx +2 -2
  44. package/lib/components/Tabs/Tabs.d.ts +59 -0
  45. package/lib/components/Tabs/Tabs.d.ts.map +1 -0
  46. package/lib/components/Tabs/Tabs.jsx +419 -0
  47. package/lib/components/Tabs/Tabs.namespace.d.ts +2 -0
  48. package/lib/components/Tabs/Tabs.namespace.d.ts.map +1 -0
  49. package/lib/components/Tabs/Tabs.namespace.js +14 -0
  50. package/lib/components/Tabs/Tabs.utils.d.ts +18 -0
  51. package/lib/components/Tabs/Tabs.utils.d.ts.map +1 -0
  52. package/lib/components/Tabs/Tabs.utils.js +37 -0
  53. package/lib/components/Tabs/index.d.ts +3 -0
  54. package/lib/components/Tabs/index.d.ts.map +1 -0
  55. package/lib/components/TagGroup/TagGroup.jsx +2 -2
  56. package/lib/components/Text/Text.jsx +2 -2
  57. package/lib/components/TextField/TextField.jsx +2 -2
  58. package/lib/components/index.d.ts +5 -0
  59. package/lib/components/index.d.ts.map +1 -1
  60. package/lib/hooks/useIconColor.d.ts +2 -1
  61. package/lib/hooks/useIconColor.d.ts.map +1 -1
  62. package/lib/hooks/useIconColor.js +29 -5
  63. package/lib/hooks/usePressTap.d.ts.map +1 -1
  64. package/lib/hooks/usePressTap.js +1 -0
  65. package/lib/index.js +14 -5
  66. package/lib/utils/create-slot-recipe-context.jsx +1 -1
  67. package/package.json +2 -2
  68. package/src/components/Accordion/Accordion.namespace.ts +22 -0
  69. package/src/components/Accordion/Accordion.test.tsx +129 -0
  70. package/src/components/Accordion/Accordion.tsx +453 -0
  71. package/src/components/Accordion/index.ts +24 -0
  72. package/src/components/BottomSheet/BottomSheet.test.tsx +24 -0
  73. package/src/components/BottomSheet/BottomSheet.tsx +14 -2
  74. package/src/components/Callout/Callout.namespace.ts +14 -0
  75. package/src/components/Callout/Callout.test.tsx +188 -0
  76. package/src/components/Callout/Callout.tsx +272 -0
  77. package/src/components/Callout/index.ts +16 -0
  78. package/src/components/Checkbox/Checkbox.test.tsx +176 -0
  79. package/src/components/Checkbox/Checkbox.tsx +36 -2
  80. package/src/components/Chip/Chip.namespace.ts +20 -0
  81. package/src/components/Chip/Chip.test.tsx +113 -0
  82. package/src/components/Chip/Chip.tsx +373 -0
  83. package/src/components/Chip/index.ts +22 -0
  84. package/src/components/Divider/Divider.test.tsx +73 -0
  85. package/src/components/Divider/Divider.tsx +76 -0
  86. package/src/components/Divider/index.ts +1 -0
  87. package/src/components/Icon/Icon.test.tsx +129 -0
  88. package/src/components/Tabs/Tabs.namespace.ts +16 -0
  89. package/src/components/Tabs/Tabs.test.tsx +221 -0
  90. package/src/components/Tabs/Tabs.tsx +727 -0
  91. package/src/components/Tabs/Tabs.utils.ts +56 -0
  92. package/src/components/Tabs/index.ts +2 -0
  93. package/src/components/index.ts +5 -0
  94. package/src/hooks/useIconColor.ts +43 -5
  95. package/src/hooks/usePressTap.test.ts +13 -0
  96. package/src/hooks/usePressTap.ts +1 -0
@@ -0,0 +1,453 @@
1
+ import * as React from "@lynx-js/react";
2
+ import type { ReactElement } from "@lynx-js/react";
3
+ import clsx from "clsx";
4
+
5
+ import { accordion, type AccordionVariantProps } from "@seed-design/lynx-css/recipes/accordion";
6
+
7
+ import { useControllableState } from "../../hooks/useControllableState";
8
+ import { usePressTap } from "../../hooks/usePressTap";
9
+ import type {
10
+ LynxAccessibilityProps,
11
+ LynxIconElementProps,
12
+ LynxPressableProps,
13
+ LynxStyledElementProps,
14
+ LynxTextRef,
15
+ LynxViewProps,
16
+ LynxViewRef,
17
+ } from "../../types";
18
+ import { toArray } from "../../utils/children";
19
+ import { createSlotRecipeContext } from "../../utils/create-slot-recipe-context";
20
+ import { InternalIcon } from "../Icon/Icon";
21
+
22
+ /**
23
+ * @platform Lynx
24
+ *
25
+ * 웹 대비 미지원 기능:
26
+ * - `asChild`: Lynx에 Slot 기반 polymorphic 렌더링이 없음
27
+ * - `headingLevel`: Lynx 접근성 heading은 level을 받지 않음
28
+ * - DOM ARIA와 키보드 탐색: Lynx native 접근성 속성과 tap 상호작용으로 대체
29
+ * - `size="responsive"`: Lynx preset은 viewport media query를 지원하지 않음
30
+ */
31
+
32
+ type PublicAccordionVariantProps = Omit<AccordionVariantProps, "open" | "pressed" | "disabled">;
33
+
34
+ interface AccordionContextValue {
35
+ values: string[];
36
+ disabled: boolean;
37
+ variantProps: PublicAccordionVariantProps;
38
+ toggle: (value: string) => void;
39
+ }
40
+
41
+ const AccordionContext = React.createContext<AccordionContextValue | null>(null);
42
+
43
+ function useAccordionContext(consumer: string): AccordionContextValue {
44
+ const context = React.useContext(AccordionContext);
45
+ if (!context) {
46
+ throw new Error(`<${consumer}/> must be rendered inside <AccordionRoot/>.`);
47
+ }
48
+ return context;
49
+ }
50
+
51
+ interface AccordionItemContextValue {
52
+ value: string;
53
+ open: boolean;
54
+ disabled: boolean;
55
+ variantProps: PublicAccordionVariantProps;
56
+ toggle: () => void;
57
+ }
58
+
59
+ const AccordionItemContext = React.createContext<AccordionItemContextValue | null>(null);
60
+ const AccordionItemPositionContext = React.createContext({ isLast: false });
61
+
62
+ function useAccordionItemContext(consumer: string): AccordionItemContextValue {
63
+ const context = React.useContext(AccordionItemContext);
64
+ if (!context) {
65
+ throw new Error(`<${consumer}/> must be rendered inside <AccordionItem/>.`);
66
+ }
67
+ return context;
68
+ }
69
+
70
+ const { ClassNamesProvider, useClassNames } = createSlotRecipeContext(accordion);
71
+
72
+ ////////////////////////////////////////////////////////////////////////////////////
73
+
74
+ export interface AccordionRootProps extends PublicAccordionVariantProps, LynxStyledElementProps {
75
+ values?: string[];
76
+ defaultValues?: string[];
77
+ onValuesChange?: (values: string[]) => void;
78
+ disabled?: boolean;
79
+ multiple?: boolean;
80
+ }
81
+
82
+ export const AccordionRoot = React.forwardRef<unknown, AccordionRootProps>((props, ref) => {
83
+ const {
84
+ children,
85
+ className,
86
+ values: valuesProp,
87
+ defaultValues = [],
88
+ onValuesChange,
89
+ disabled = false,
90
+ multiple = false,
91
+ ...restProps
92
+ } = props;
93
+ const [variantProps, nativeProps] = accordion.splitVariantProps(restProps);
94
+ const [rawValues, setValues] = useControllableState({
95
+ value: valuesProp,
96
+ defaultValue: defaultValues,
97
+ onChange: onValuesChange,
98
+ });
99
+ const values = multiple ? rawValues : rawValues.slice(0, 1);
100
+
101
+ const toggle = React.useCallback(
102
+ (itemValue: string) => {
103
+ if (disabled) return;
104
+
105
+ if (!multiple) {
106
+ setValues(values[0] === itemValue ? [] : [itemValue]);
107
+ return;
108
+ }
109
+
110
+ setValues(
111
+ values.includes(itemValue)
112
+ ? values.filter((value) => value !== itemValue)
113
+ : [...values, itemValue],
114
+ );
115
+ },
116
+ [disabled, multiple, setValues, values],
117
+ );
118
+
119
+ const contextValue = React.useMemo<AccordionContextValue>(
120
+ () => ({ values, disabled, variantProps, toggle }),
121
+ [disabled, toggle, values, variantProps],
122
+ );
123
+ const classes = accordion({ ...variantProps, disabled });
124
+ const items = toArray(children);
125
+
126
+ return (
127
+ <AccordionContext.Provider value={contextValue}>
128
+ <view
129
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
130
+ className={clsx(classes.root, className)}
131
+ {...nativeProps}
132
+ >
133
+ {items.map((item, index) => (
134
+ <AccordionItemPositionContext.Provider
135
+ key={React.isValidElement(item) ? (item.key ?? index) : index}
136
+ value={{ isLast: index === items.length - 1 }}
137
+ >
138
+ {item}
139
+ </AccordionItemPositionContext.Provider>
140
+ ))}
141
+ </view>
142
+ </AccordionContext.Provider>
143
+ );
144
+ });
145
+ AccordionRoot.displayName = "AccordionRoot";
146
+
147
+ ////////////////////////////////////////////////////////////////////////////////////
148
+
149
+ export interface AccordionItemProps extends LynxStyledElementProps {
150
+ value: string;
151
+ disabled?: boolean;
152
+ }
153
+
154
+ export const AccordionItem = React.forwardRef<unknown, AccordionItemProps>((props, ref) => {
155
+ const { children, className, value, disabled: itemDisabled = false, ...nativeProps } = props;
156
+ const accordionContext = useAccordionContext("AccordionItem");
157
+ const { isLast } = React.useContext(AccordionItemPositionContext);
158
+ const disabled = accordionContext.disabled || itemDisabled;
159
+ const open = accordionContext.values.includes(value);
160
+ const toggle = React.useCallback(() => accordionContext.toggle(value), [accordionContext, value]);
161
+ const classes = accordion({
162
+ ...accordionContext.variantProps,
163
+ open,
164
+ disabled,
165
+ pressed: false,
166
+ });
167
+ const contextValue = React.useMemo<AccordionItemContextValue>(
168
+ () => ({ value, open, disabled, variantProps: accordionContext.variantProps, toggle }),
169
+ [accordionContext.variantProps, disabled, open, toggle, value],
170
+ );
171
+
172
+ return (
173
+ <AccordionItemContext.Provider value={contextValue}>
174
+ <ClassNamesProvider value={classes}>
175
+ <view
176
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
177
+ className={clsx(classes.item, className)}
178
+ {...nativeProps}
179
+ >
180
+ {children}
181
+ {!isLast ? (
182
+ <view className={classes.divider} accessibility-elements-hidden={true} />
183
+ ) : null}
184
+ </view>
185
+ </ClassNamesProvider>
186
+ </AccordionItemContext.Provider>
187
+ );
188
+ });
189
+ AccordionItem.displayName = "AccordionItem";
190
+
191
+ ////////////////////////////////////////////////////////////////////////////////////
192
+
193
+ export interface AccordionHeaderProps extends LynxStyledElementProps, LynxAccessibilityProps {}
194
+
195
+ export const AccordionHeader = React.forwardRef<unknown, AccordionHeaderProps>((props, ref) => {
196
+ const {
197
+ children,
198
+ className,
199
+ "accessibility-heading": accessibilityHeading = true,
200
+ ...nativeProps
201
+ } = props;
202
+ const classes = useClassNames();
203
+
204
+ return (
205
+ <view
206
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
207
+ className={clsx(classes.header, className)}
208
+ accessibility-heading={accessibilityHeading}
209
+ {...nativeProps}
210
+ >
211
+ {children}
212
+ </view>
213
+ );
214
+ });
215
+ AccordionHeader.displayName = "AccordionHeader";
216
+
217
+ ////////////////////////////////////////////////////////////////////////////////////
218
+
219
+ export interface AccordionTriggerProps
220
+ extends LynxStyledElementProps,
221
+ LynxAccessibilityProps,
222
+ Pick<LynxPressableProps, "bindtap"> {
223
+ expandedAccessibilityValue?: string;
224
+ collapsedAccessibilityValue?: string;
225
+ }
226
+
227
+ export const AccordionTrigger = React.forwardRef<unknown, AccordionTriggerProps>((props, ref) => {
228
+ const {
229
+ children,
230
+ className,
231
+ bindtap,
232
+ expandedAccessibilityValue = "펼쳐짐",
233
+ collapsedAccessibilityValue = "접힘",
234
+ "accessibility-element": accessibilityElement = true,
235
+ "accessibility-role-description": accessibilityRoleDescription = "button",
236
+ "accessibility-traits": accessibilityTraits,
237
+ "accessibility-value": accessibilityValue,
238
+ ...nativeProps
239
+ } = props;
240
+ const context = useAccordionItemContext("AccordionTrigger");
241
+ const handleTap = React.useCallback(
242
+ (event: Parameters<NonNullable<LynxPressableProps["bindtap"]>>[0]) => {
243
+ context.toggle();
244
+ bindtap?.(event);
245
+ },
246
+ [bindtap, context],
247
+ );
248
+ const { pressed, ...pressHandlers } = usePressTap({
249
+ disabled: context.disabled,
250
+ onTap: handleTap,
251
+ });
252
+ const classes = accordion({
253
+ ...context.variantProps,
254
+ open: context.open,
255
+ disabled: context.disabled,
256
+ pressed,
257
+ });
258
+
259
+ return (
260
+ <ClassNamesProvider value={classes}>
261
+ <view
262
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
263
+ className={clsx(classes.trigger, className)}
264
+ accessibility-element={accessibilityElement}
265
+ accessibility-role-description={accessibilityRoleDescription}
266
+ accessibility-traits={accessibilityTraits ?? (context.disabled ? "disabled" : "button")}
267
+ accessibility-value={
268
+ accessibilityValue ??
269
+ (context.open ? expandedAccessibilityValue : collapsedAccessibilityValue)
270
+ }
271
+ {...pressHandlers}
272
+ {...nativeProps}
273
+ >
274
+ <view className={classes.pressedOverlay} accessibility-elements-hidden={true} />
275
+ {children}
276
+ </view>
277
+ </ClassNamesProvider>
278
+ );
279
+ });
280
+ AccordionTrigger.displayName = "AccordionTrigger";
281
+
282
+ ////////////////////////////////////////////////////////////////////////////////////
283
+
284
+ export interface AccordionContentProps extends LynxStyledElementProps, LynxAccessibilityProps {}
285
+
286
+ type ContentLayoutChangeHandler = NonNullable<LynxViewProps["bindlayoutchange"]>;
287
+
288
+ function getContentLayoutHeight(event: Parameters<ContentLayoutChangeHandler>[0]): number | null {
289
+ const eventWithHeight = event as Parameters<ContentLayoutChangeHandler>[0] & { height?: number };
290
+ const height = event.detail?.height ?? event.params?.height ?? eventWithHeight.height;
291
+
292
+ if (typeof height !== "number" || !Number.isFinite(height)) return null;
293
+ return Math.max(0, height);
294
+ }
295
+
296
+ export const AccordionContent = React.forwardRef<unknown, AccordionContentProps>((props, ref) => {
297
+ const {
298
+ children,
299
+ className,
300
+ style,
301
+ "accessibility-elements-hidden": accessibilityElementsHidden = false,
302
+ ...nativeProps
303
+ } = props;
304
+ const context = useAccordionItemContext("AccordionContent");
305
+ const classes = useClassNames();
306
+ const [contentHeight, setContentHeight] = React.useState(0);
307
+ const handleContentLayoutChange = React.useCallback<ContentLayoutChangeHandler>((event) => {
308
+ const height = getContentLayoutHeight(event);
309
+ if (height !== null) setContentHeight((current) => (current === height ? current : height));
310
+ }, []);
311
+
312
+ return (
313
+ <view
314
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
315
+ className={clsx(classes.content, className)}
316
+ style={{ ...style, height: context.open ? `${contentHeight}px` : "0px" }}
317
+ accessibility-elements-hidden={!context.open || accessibilityElementsHidden}
318
+ {...nativeProps}
319
+ >
320
+ <view className={classes.contentInner} bindlayoutchange={handleContentLayoutChange}>
321
+ {children}
322
+ </view>
323
+ </view>
324
+ );
325
+ });
326
+ AccordionContent.displayName = "AccordionContent";
327
+
328
+ ////////////////////////////////////////////////////////////////////////////////////
329
+
330
+ export interface AccordionBodyProps extends LynxStyledElementProps {}
331
+
332
+ export const AccordionBody = React.forwardRef<unknown, AccordionBodyProps>((props, ref) => {
333
+ const { children, className, ...nativeProps } = props;
334
+ const classes = useClassNames();
335
+
336
+ return (
337
+ <view
338
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
339
+ className={clsx(classes.body, className)}
340
+ {...nativeProps}
341
+ >
342
+ {children}
343
+ </view>
344
+ );
345
+ });
346
+ AccordionBody.displayName = "AccordionBody";
347
+
348
+ ////////////////////////////////////////////////////////////////////////////////////
349
+
350
+ export interface AccordionTitleProps extends LynxStyledElementProps {}
351
+
352
+ export const AccordionTitle = React.forwardRef<unknown, AccordionTitleProps>((props, ref) => {
353
+ const { children, className, ...nativeProps } = props;
354
+ const classes = useClassNames();
355
+
356
+ return (
357
+ <text
358
+ {...(ref ? { ref: ref as LynxTextRef } : {})}
359
+ className={clsx(classes.title, className)}
360
+ {...nativeProps}
361
+ >
362
+ {children}
363
+ </text>
364
+ );
365
+ });
366
+ AccordionTitle.displayName = "AccordionTitle";
367
+
368
+ ////////////////////////////////////////////////////////////////////////////////////
369
+
370
+ export interface AccordionDescriptionProps extends LynxStyledElementProps {}
371
+
372
+ export const AccordionDescription = React.forwardRef<unknown, AccordionDescriptionProps>(
373
+ (props, ref) => {
374
+ const { children, className, ...nativeProps } = props;
375
+ const classes = useClassNames();
376
+
377
+ return (
378
+ <text
379
+ {...(ref ? { ref: ref as LynxTextRef } : {})}
380
+ className={clsx(classes.description, className)}
381
+ {...nativeProps}
382
+ >
383
+ {children}
384
+ </text>
385
+ );
386
+ },
387
+ );
388
+ AccordionDescription.displayName = "AccordionDescription";
389
+
390
+ ////////////////////////////////////////////////////////////////////////////////////
391
+
392
+ export interface AccordionPrefixProps extends LynxStyledElementProps {}
393
+
394
+ export const AccordionPrefix = React.forwardRef<unknown, AccordionPrefixProps>((props, ref) => {
395
+ const { children, className, ...nativeProps } = props;
396
+ const classes = useClassNames();
397
+
398
+ return (
399
+ <view
400
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
401
+ className={clsx(classes.prefix, className)}
402
+ {...nativeProps}
403
+ >
404
+ {children}
405
+ </view>
406
+ );
407
+ });
408
+ AccordionPrefix.displayName = "AccordionPrefix";
409
+
410
+ ////////////////////////////////////////////////////////////////////////////////////
411
+
412
+ export interface AccordionSuffixIconProps extends LynxStyledElementProps {
413
+ icon?: ReactElement<LynxIconElementProps>;
414
+ }
415
+
416
+ export const AccordionSuffixIcon = React.forwardRef<unknown, AccordionSuffixIconProps>(
417
+ (props, ref) => {
418
+ const { icon, children, className, style, ...nativeProps } = props;
419
+ const context = useAccordionItemContext("AccordionSuffixIcon");
420
+ const classes = useClassNames();
421
+ const mergedClassName = clsx(classes.suffixIcon, className);
422
+
423
+ if (icon) {
424
+ return (
425
+ <view
426
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
427
+ accessibility-elements-hidden={true}
428
+ {...nativeProps}
429
+ >
430
+ <InternalIcon
431
+ icon={icon}
432
+ className={mergedClassName}
433
+ style={style}
434
+ deps={[context.open, context.disabled]}
435
+ />
436
+ </view>
437
+ );
438
+ }
439
+
440
+ return (
441
+ <view
442
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
443
+ className={mergedClassName}
444
+ style={style}
445
+ accessibility-elements-hidden={true}
446
+ {...nativeProps}
447
+ >
448
+ {children}
449
+ </view>
450
+ );
451
+ },
452
+ );
453
+ AccordionSuffixIcon.displayName = "AccordionSuffixIcon";
@@ -0,0 +1,24 @@
1
+ export {
2
+ AccordionRoot,
3
+ AccordionItem,
4
+ AccordionHeader,
5
+ AccordionTrigger,
6
+ AccordionContent,
7
+ AccordionBody,
8
+ AccordionTitle,
9
+ AccordionDescription,
10
+ AccordionPrefix,
11
+ AccordionSuffixIcon,
12
+ type AccordionRootProps,
13
+ type AccordionItemProps,
14
+ type AccordionHeaderProps,
15
+ type AccordionTriggerProps,
16
+ type AccordionContentProps,
17
+ type AccordionBodyProps,
18
+ type AccordionTitleProps,
19
+ type AccordionDescriptionProps,
20
+ type AccordionPrefixProps,
21
+ type AccordionSuffixIconProps,
22
+ } from "./Accordion";
23
+
24
+ export * as Accordion from "./Accordion.namespace";
@@ -143,6 +143,30 @@ describe("BottomSheet", () => {
143
143
  });
144
144
  });
145
145
 
146
+ it("releases the sheet's inline left inset so the positioner can center it", () => {
147
+ render(
148
+ <BottomSheet.Root>
149
+ <BottomSheet.Content />
150
+ </BottomSheet.Root>,
151
+ );
152
+
153
+ expect(sheetMocks.contentProps.at(-1)).toMatchObject({
154
+ style: { left: "auto" },
155
+ });
156
+ });
157
+
158
+ it("lets a user-provided style win over the released left inset", () => {
159
+ render(
160
+ <BottomSheet.Root>
161
+ <BottomSheet.Content style={{ left: "12px" }} />
162
+ </BottomSheet.Root>,
163
+ );
164
+
165
+ expect(sheetMocks.contentProps.at(-1)).toMatchObject({
166
+ style: { left: "12px" },
167
+ });
168
+ });
169
+
146
170
  it("skips motion-engine animations when Root has skipAnimation", () => {
147
171
  render(
148
172
  <BottomSheet.Root skipAnimation>
@@ -279,8 +279,15 @@ export interface BottomSheetContentProps extends SheetContentProps {}
279
279
 
280
280
  export const BottomSheetContent: LynxForwardRefComponent<unknown, BottomSheetContentProps> =
281
281
  forwardRef<unknown, BottomSheetContentProps>((props, ref) => {
282
- const { className, innerStyle, snapAnimation, enterAnimation, exitAnimation, ...restProps } =
283
- props;
282
+ const {
283
+ className,
284
+ style,
285
+ innerStyle,
286
+ snapAnimation,
287
+ enterAnimation,
288
+ exitAnimation,
289
+ ...restProps
290
+ } = props;
284
291
  const classNames = useClassNames();
285
292
  const { options } = useBottomSheetContext();
286
293
  const { safeAreaInsetBottom } = useSafeArea();
@@ -292,6 +299,11 @@ export const BottomSheetContent: LynxForwardRefComponent<unknown, BottomSheetCon
292
299
  {...(ref ? { ref } : {})}
293
300
  {...restProps}
294
301
  className={clsx(classNames.content, className)}
302
+ // `SheetContent` pins its outer view with an inline `left: 0`, which a recipe class
303
+ // cannot outrank. Releasing it lets the positioner's `justify-content: center` place
304
+ // the sheet, so the recipe's `max-width` leaves centered gutters instead of a right
305
+ // one. `transform` is off-limits here — the main-thread motion engine owns it.
306
+ style={{ left: "auto", ...style }}
295
307
  innerStyle={{
296
308
  display: "flex",
297
309
  flexDirection: "column",
@@ -0,0 +1,14 @@
1
+ export {
2
+ CalloutCloseButton as CloseButton,
3
+ CalloutContent as Content,
4
+ CalloutDescription as Description,
5
+ CalloutLink as Link,
6
+ CalloutRoot as Root,
7
+ CalloutTitle as Title,
8
+ type CalloutCloseButtonProps as CloseButtonProps,
9
+ type CalloutContentProps as ContentProps,
10
+ type CalloutDescriptionProps as DescriptionProps,
11
+ type CalloutLinkProps as LinkProps,
12
+ type CalloutRootProps as RootProps,
13
+ type CalloutTitleProps as TitleProps,
14
+ } from "./Callout";