@seed-design/lynx-react 0.2.0-alpha.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/lib/_virtual/_rolldown/runtime.js +13 -0
  2. package/lib/components/ActionButton/ActionButton.d.ts +26 -32
  3. package/lib/components/ActionButton/ActionButton.d.ts.map +1 -1
  4. package/lib/components/ActionButton/ActionButton.jsx +139 -120
  5. package/lib/components/ActionButton/index.d.ts +1 -1
  6. package/lib/components/AppBar/AppBar.d.ts +35 -0
  7. package/lib/components/AppBar/AppBar.d.ts.map +1 -0
  8. package/lib/components/AppBar/AppBar.jsx +132 -0
  9. package/lib/components/AppBar/AppBar.namespace.d.ts +2 -0
  10. package/lib/components/AppBar/AppBar.namespace.d.ts.map +1 -0
  11. package/lib/components/AppBar/AppBar.namespace.js +15 -0
  12. package/lib/components/AppBar/context.d.ts +13 -0
  13. package/lib/components/AppBar/context.d.ts.map +1 -0
  14. package/lib/components/AppBar/context.js +12 -0
  15. package/lib/components/AppBar/index.d.ts +3 -0
  16. package/lib/components/AppBar/index.d.ts.map +1 -0
  17. package/lib/components/AppBar/useAppBar.d.ts +15 -0
  18. package/lib/components/AppBar/useAppBar.d.ts.map +1 -0
  19. package/lib/components/AppBar/useAppBar.js +72 -0
  20. package/lib/components/Badge/Badge.d.ts +14 -0
  21. package/lib/components/Badge/Badge.d.ts.map +1 -0
  22. package/lib/components/Badge/Badge.jsx +15 -0
  23. package/lib/components/Badge/index.d.ts +2 -0
  24. package/lib/components/Badge/index.d.ts.map +1 -0
  25. package/lib/components/BottomSheet/BottomSheet.d.ts +22 -27
  26. package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -1
  27. package/lib/components/BottomSheet/BottomSheet.jsx +127 -142
  28. package/lib/components/BottomSheet/BottomSheet.namespace.d.ts +1 -1
  29. package/lib/components/BottomSheet/BottomSheet.namespace.js +18 -1
  30. package/lib/components/BottomSheet/index.d.ts +2 -2
  31. package/lib/components/Box/Box.d.ts +20 -0
  32. package/lib/components/Box/Box.d.ts.map +1 -0
  33. package/lib/components/Box/Box.jsx +14 -0
  34. package/lib/components/Box/index.d.ts +2 -0
  35. package/lib/components/Box/index.d.ts.map +1 -0
  36. package/lib/components/Checkbox/Checkbox.d.ts +14 -30
  37. package/lib/components/Checkbox/Checkbox.d.ts.map +1 -1
  38. package/lib/components/Checkbox/Checkbox.jsx +107 -98
  39. package/lib/components/Checkbox/Checkbox.namespace.d.ts +1 -1
  40. package/lib/components/Checkbox/Checkbox.namespace.js +12 -1
  41. package/lib/components/Checkbox/index.d.ts +2 -2
  42. package/lib/components/Icon/Icon.d.ts +37 -0
  43. package/lib/components/Icon/Icon.d.ts.map +1 -0
  44. package/lib/components/Icon/Icon.jsx +143 -0
  45. package/lib/components/Icon/index.d.ts +2 -0
  46. package/lib/components/Icon/index.d.ts.map +1 -0
  47. package/lib/components/ProgressCircle/ProgressCircle.d.ts +4 -8
  48. package/lib/components/ProgressCircle/ProgressCircle.d.ts.map +1 -1
  49. package/lib/components/ProgressCircle/ProgressCircle.jsx +255 -260
  50. package/lib/components/ProgressCircle/ProgressCircle.namespace.d.ts +1 -1
  51. package/lib/components/ProgressCircle/ProgressCircle.namespace.js +9 -1
  52. package/lib/components/ProgressCircle/index.d.ts +2 -2
  53. package/lib/components/RadioGroup/RadioGroup.d.ts +12 -28
  54. package/lib/components/RadioGroup/RadioGroup.d.ts.map +1 -1
  55. package/lib/components/RadioGroup/RadioGroup.jsx +134 -118
  56. package/lib/components/RadioGroup/RadioGroup.namespace.d.ts +1 -1
  57. package/lib/components/RadioGroup/RadioGroup.namespace.js +12 -1
  58. package/lib/components/RadioGroup/index.d.ts +2 -2
  59. package/lib/components/Stack/Stack.d.ts +33 -0
  60. package/lib/components/Stack/Stack.d.ts.map +1 -0
  61. package/lib/components/Stack/Stack.jsx +39 -0
  62. package/lib/components/Stack/index.d.ts +2 -0
  63. package/lib/components/Stack/index.d.ts.map +1 -0
  64. package/lib/components/Switch/Switch.d.ts +8 -14
  65. package/lib/components/Switch/Switch.d.ts.map +1 -1
  66. package/lib/components/Switch/Switch.jsx +82 -56
  67. package/lib/components/Switch/Switch.namespace.d.ts +1 -1
  68. package/lib/components/Switch/Switch.namespace.js +11 -1
  69. package/lib/components/Switch/index.d.ts +2 -2
  70. package/lib/components/TagGroup/TagGroup.d.ts +8 -14
  71. package/lib/components/TagGroup/TagGroup.d.ts.map +1 -1
  72. package/lib/components/TagGroup/TagGroup.jsx +42 -39
  73. package/lib/components/TagGroup/TagGroup.namespace.d.ts +1 -1
  74. package/lib/components/TagGroup/TagGroup.namespace.js +10 -1
  75. package/lib/components/TagGroup/index.d.ts +2 -2
  76. package/lib/components/Text/Text.d.ts +7 -0
  77. package/lib/components/Text/Text.d.ts.map +1 -0
  78. package/lib/components/Text/Text.jsx +28 -0
  79. package/lib/components/Text/index.d.ts +2 -0
  80. package/lib/components/Text/index.d.ts.map +1 -0
  81. package/lib/components/index.d.ts +13 -7
  82. package/lib/components/index.d.ts.map +1 -1
  83. package/lib/hooks/index.d.ts +4 -3
  84. package/lib/hooks/index.d.ts.map +1 -1
  85. package/lib/hooks/{use-controllable-state.d.ts → useControllableState.d.ts} +1 -1
  86. package/lib/hooks/useControllableState.d.ts.map +1 -0
  87. package/lib/hooks/useControllableState.js +27 -0
  88. package/lib/hooks/useIconColor.d.ts +19 -0
  89. package/lib/hooks/useIconColor.d.ts.map +1 -0
  90. package/lib/hooks/useIconColor.js +38 -0
  91. package/lib/hooks/{use-press-tap.d.ts → usePressTap.d.ts} +12 -9
  92. package/lib/hooks/usePressTap.d.ts.map +1 -0
  93. package/lib/hooks/usePressTap.js +35 -0
  94. package/lib/hooks/useSafeArea.d.ts +19 -0
  95. package/lib/hooks/useSafeArea.d.ts.map +1 -0
  96. package/lib/hooks/useSafeArea.js +33 -0
  97. package/lib/index.d.ts +4 -3
  98. package/lib/index.d.ts.map +1 -1
  99. package/lib/index.js +29 -3
  100. package/lib/types.d.ts +51 -0
  101. package/lib/types.d.ts.map +1 -0
  102. package/lib/utils/children.d.ts +3 -0
  103. package/lib/utils/children.d.ts.map +1 -0
  104. package/lib/utils/children.js +8 -0
  105. package/lib/utils/create-slot-recipe-context.d.ts +13 -12
  106. package/lib/utils/create-slot-recipe-context.d.ts.map +1 -1
  107. package/lib/utils/create-slot-recipe-context.jsx +88 -70
  108. package/lib/utils/get-seed-class-name.d.ts +6 -7
  109. package/lib/utils/get-seed-class-name.d.ts.map +1 -1
  110. package/lib/utils/get-seed-class-name.js +26 -37
  111. package/lib/utils/index.d.ts +4 -4
  112. package/lib/utils/resolve-recipe-token.js +45 -43
  113. package/lib/utils/split-multiple-variants-props.js +32 -32
  114. package/lib/utils/styled.d.ts +83 -0
  115. package/lib/utils/styled.d.ts.map +1 -0
  116. package/lib/utils/styled.js +144 -0
  117. package/package.json +9 -9
  118. package/src/components/ActionButton/ActionButton.test.tsx +167 -0
  119. package/src/components/ActionButton/ActionButton.tsx +210 -168
  120. package/src/components/AppBar/AppBar.namespace.ts +18 -0
  121. package/src/components/AppBar/AppBar.test.tsx +270 -0
  122. package/src/components/AppBar/AppBar.tsx +288 -0
  123. package/src/components/AppBar/context.ts +27 -0
  124. package/src/components/AppBar/index.ts +21 -0
  125. package/src/components/AppBar/useAppBar.ts +100 -0
  126. package/src/components/Badge/Badge.test.tsx +54 -0
  127. package/src/components/Badge/Badge.tsx +32 -0
  128. package/src/components/Badge/index.ts +1 -0
  129. package/src/components/BottomSheet/BottomSheet.test.tsx +219 -0
  130. package/src/components/BottomSheet/BottomSheet.tsx +120 -83
  131. package/src/components/Box/Box.test.tsx +85 -0
  132. package/src/components/Box/Box.tsx +39 -0
  133. package/src/components/Box/index.ts +1 -0
  134. package/src/components/Checkbox/Checkbox.tsx +108 -117
  135. package/src/components/Icon/Icon.tsx +265 -0
  136. package/src/components/Icon/index.ts +8 -0
  137. package/src/components/ProgressCircle/ProgressCircle.tsx +39 -25
  138. package/src/components/RadioGroup/RadioGroup.tsx +100 -118
  139. package/src/components/Stack/Stack.tsx +97 -0
  140. package/src/components/Stack/index.ts +1 -0
  141. package/src/components/Switch/Switch.tsx +61 -52
  142. package/src/components/TagGroup/TagGroup.tsx +27 -25
  143. package/src/components/Text/Text.tsx +64 -0
  144. package/src/components/Text/index.ts +1 -0
  145. package/src/components/index.ts +6 -0
  146. package/src/hooks/index.ts +4 -3
  147. package/src/hooks/{__tests__/use-controllable-state.test.ts → useControllableState.test.ts} +1 -1
  148. package/src/hooks/useIconColor.test.ts +13 -0
  149. package/src/hooks/useIconColor.ts +67 -0
  150. package/src/hooks/{__tests__/use-press-tap.test.ts → usePressTap.test.ts} +1 -1
  151. package/src/hooks/{use-press-tap.ts → usePressTap.ts} +14 -10
  152. package/src/hooks/useSafeArea.test.ts +83 -0
  153. package/src/hooks/useSafeArea.ts +56 -0
  154. package/src/index.ts +1 -0
  155. package/src/types.ts +76 -0
  156. package/src/utils/children.ts +13 -0
  157. package/src/utils/create-slot-recipe-context.test.tsx +96 -0
  158. package/src/utils/create-slot-recipe-context.tsx +55 -15
  159. package/src/utils/get-seed-class-name.ts +7 -12
  160. package/src/utils/{__tests__/split-multiple-variants-props.test.ts → split-multiple-variants-props.test.ts} +1 -1
  161. package/src/utils/styled.ts +442 -0
  162. package/lib/components/ActionButton/index.js +0 -1
  163. package/lib/components/BottomSheet/index.js +0 -2
  164. package/lib/components/Checkbox/index.js +0 -2
  165. package/lib/components/ProgressCircle/index.js +0 -2
  166. package/lib/components/RadioGroup/index.js +0 -2
  167. package/lib/components/Switch/index.js +0 -2
  168. package/lib/components/TagGroup/index.js +0 -2
  169. package/lib/components/index.js +0 -7
  170. package/lib/hooks/index.js +0 -3
  171. package/lib/hooks/use-controllable-state.d.ts.map +0 -1
  172. package/lib/hooks/use-controllable-state.js +0 -27
  173. package/lib/hooks/use-icon-color.d.ts +0 -15
  174. package/lib/hooks/use-icon-color.d.ts.map +0 -1
  175. package/lib/hooks/use-icon-color.js +0 -37
  176. package/lib/hooks/use-press-tap.d.ts.map +0 -1
  177. package/lib/hooks/use-press-tap.js +0 -35
  178. package/lib/utils/index.js +0 -4
  179. package/src/components/ActionButton/__tests__/ActionButton.test.tsx +0 -35
  180. package/src/components/BottomSheet/__tests__/BottomSheet.test.tsx +0 -119
  181. package/src/components/__tests__/ProgressCircle.test.ts +0 -33
  182. package/src/components/__tests__/exports.test.ts +0 -38
  183. package/src/components/__tests__/switch.test.ts +0 -34
  184. package/src/hooks/use-icon-color.ts +0 -51
  185. package/src/utils/__tests__/create-slot-recipe-context.test.tsx +0 -228
  186. /package/src/hooks/{use-controllable-state.ts → useControllableState.ts} +0 -0
@@ -24,16 +24,27 @@ import {
24
24
  useContext,
25
25
  useMemo,
26
26
  useRef,
27
- type ReactNode,
28
- type Ref,
27
+ type ForwardRefExoticComponent,
28
+ type PropsWithoutRef,
29
+ type ReactElement,
29
30
  type RefObject,
31
+ type RefAttributes,
30
32
  } from "@lynx-js/react";
31
- import type { CSSProperties } from "react";
32
33
  import clsx from "clsx";
33
34
 
35
+ import { useSafeArea } from "../../hooks/useSafeArea";
36
+ import type {
37
+ LynxPressableProps,
38
+ LynxStyledElementProps,
39
+ LynxTextRef,
40
+ LynxViewRef,
41
+ } from "../../types";
34
42
  import { createSlotRecipeContext } from "../../utils/create-slot-recipe-context";
35
43
 
36
44
  type BottomSheetClassNames = ReturnType<typeof bottomSheet>;
45
+ type LynxForwardRefComponent<T, P> = ForwardRefExoticComponent<
46
+ PropsWithoutRef<P> & RefAttributes<T>
47
+ >;
37
48
 
38
49
  const { ClassNamesProvider, useClassNames, withContext } = createSlotRecipeContext(bottomSheet);
39
50
 
@@ -132,8 +143,8 @@ export interface BottomSheetRootProps
132
143
  * - `BottomSheetCloseButton`: Tier B (Lynx SVG 지원 후 추가 예정)
133
144
  * - `BottomSheetTrigger`의 `asChild`: 미지원 (기본 `<view>`만)
134
145
  */
135
- export const BottomSheetRoot = forwardRef<SheetRootRef, BottomSheetRootProps>(
136
- (props, forwardedRef) => {
146
+ export const BottomSheetRoot: LynxForwardRefComponent<SheetRootRef, BottomSheetRootProps> =
147
+ forwardRef<SheetRootRef, BottomSheetRootProps>((props, forwardedRef) => {
137
148
  const [variantProps, restProps] = bottomSheet.splitVariantProps(props);
138
149
  const { open, defaultOpen, onOpenChange, snapPoints, children, ...nativeProps } = restProps;
139
150
  const { headerAlign, skipAnimation } = variantProps;
@@ -186,50 +197,47 @@ export const BottomSheetRoot = forwardRef<SheetRootRef, BottomSheetRootProps>(
186
197
  </ClassNamesProvider>
187
198
  </BottomSheetContext.Provider>
188
199
  );
189
- },
190
- );
200
+ });
191
201
  BottomSheetRoot.displayName = "BottomSheetRoot";
192
202
 
193
203
  ////////////////////////////////////////////////////////////////////////////////////
194
204
  // Trigger
195
205
  ////////////////////////////////////////////////////////////////////////////////////
196
206
 
197
- export interface BottomSheetTriggerProps {
198
- children?: ReactNode;
199
- className?: string;
200
- style?: CSSProperties;
201
- bindtap?: () => void;
202
- }
207
+ export interface BottomSheetTriggerProps
208
+ extends LynxStyledElementProps,
209
+ Pick<LynxPressableProps, "bindtap"> {}
210
+
211
+ export const BottomSheetTrigger: LynxForwardRefComponent<unknown, BottomSheetTriggerProps> =
212
+ forwardRef<unknown, BottomSheetTriggerProps>((props, ref) => {
213
+ const { children, className, style, bindtap: userBindtap } = props;
214
+ const { rootRef, options } = useBottomSheetContext();
215
+
216
+ // `bindtap`을 직접 prop으로 쓰면 React DOM `<view>` 타입(SVG)이 적용되어 TS가 거부한다.
217
+ // Lynx JSX 런타임은 이 prop을 올바르게 처리하므로 spread로 우회한다 (ActionButton과 동일 패턴).
218
+ const handlers: Pick<LynxPressableProps, "bindtap"> = {
219
+ bindtap: (event, instance) => {
220
+ if (options.skipAnimation) {
221
+ rootRef.current?.open({ animate: false });
222
+ } else {
223
+ rootRef.current?.open();
224
+ }
203
225
 
204
- export const BottomSheetTrigger = forwardRef<unknown, BottomSheetTriggerProps>((props, ref) => {
205
- const { children, className, style, bindtap: userBindtap } = props;
206
- const { rootRef, options } = useBottomSheetContext();
207
-
208
- // `bindtap`을 직접 prop으로 쓰면 React DOM `<view>` 타입(SVG)이 적용되어 TS가 거부한다.
209
- // Lynx JSX 런타임은 이 prop을 올바르게 처리하므로 spread로 우회한다 (ActionButton과 동일 패턴).
210
- const handlers = {
211
- bindtap: () => {
212
- if (options.skipAnimation) {
213
- rootRef.current?.open({ animate: false });
214
- } else {
215
- rootRef.current?.open();
216
- }
217
-
218
- userBindtap?.();
219
- },
220
- };
226
+ userBindtap?.(event, instance);
227
+ },
228
+ };
221
229
 
222
- return (
223
- <view
224
- {...(ref ? { ref: ref as Ref<SVGViewElement> } : {})}
225
- {...handlers}
226
- className={className}
227
- style={style}
228
- >
229
- {children}
230
- </view>
231
- );
232
- });
230
+ return (
231
+ <view
232
+ {...(ref ? ({ ref: ref as LynxViewRef } as Record<string, unknown>) : {})}
233
+ {...handlers}
234
+ className={className}
235
+ style={style as never}
236
+ >
237
+ {children}
238
+ </view>
239
+ );
240
+ });
233
241
  BottomSheetTrigger.displayName = "BottomSheetTrigger";
234
242
 
235
243
  ////////////////////////////////////////////////////////////////////////////////////
@@ -248,10 +256,8 @@ export interface BottomSheetPositionerProps extends SheetViewProps {}
248
256
  * recipe의 `positioner` slot className을 자동 적용해 `position: fixed` + 전체
249
257
  * 뷰포트 커버 레이아웃을 보장한다.
250
258
  */
251
- export const BottomSheetPositioner = withContext<unknown, BottomSheetPositionerProps>(
252
- SheetView,
253
- "positioner",
254
- );
259
+ export const BottomSheetPositioner: LynxForwardRefComponent<unknown, BottomSheetPositionerProps> =
260
+ withContext<unknown, BottomSheetPositionerProps>(SheetView, "positioner");
255
261
  BottomSheetPositioner.displayName = "BottomSheetPositioner";
256
262
 
257
263
  ////////////////////////////////////////////////////////////////////////////////////
@@ -265,32 +271,40 @@ BottomSheetPositioner.displayName = "BottomSheetPositioner";
265
271
 
266
272
  export interface BottomSheetBackdropProps extends SheetBackdropProps {}
267
273
 
268
- export const BottomSheetBackdrop = withContext<unknown, BottomSheetBackdropProps>(
269
- SheetBackdrop,
270
- "backdrop",
271
- );
274
+ export const BottomSheetBackdrop: LynxForwardRefComponent<unknown, BottomSheetBackdropProps> =
275
+ withContext<unknown, BottomSheetBackdropProps>(SheetBackdrop, "backdrop");
272
276
  BottomSheetBackdrop.displayName = "BottomSheetBackdrop";
273
277
 
274
278
  export interface BottomSheetContentProps extends SheetContentProps {}
275
279
 
276
- export const BottomSheetContent = forwardRef<unknown, BottomSheetContentProps>((props, ref) => {
277
- const { className, snapAnimation, enterAnimation, exitAnimation, ...restProps } = props;
278
- const classNames = useClassNames();
279
- const { options } = useBottomSheetContext();
280
+ export const BottomSheetContent: LynxForwardRefComponent<unknown, BottomSheetContentProps> =
281
+ forwardRef<unknown, BottomSheetContentProps>((props, ref) => {
282
+ const { className, innerStyle, snapAnimation, enterAnimation, exitAnimation, ...restProps } =
283
+ props;
284
+ const classNames = useClassNames();
285
+ const { options } = useBottomSheetContext();
286
+ const { safeAreaInsetBottom } = useSafeArea();
280
287
 
281
- const defaultAnimation = options.skipAnimation ? SKIP_ANIMATION_TRANSITION : undefined;
288
+ const defaultAnimation = options.skipAnimation ? SKIP_ANIMATION_TRANSITION : undefined;
282
289
 
283
- return (
284
- <SheetContent
285
- {...(ref ? { ref } : {})}
286
- {...restProps}
287
- className={clsx(classNames.content, className)}
288
- snapAnimation={snapAnimation ?? defaultAnimation ?? SEED_SNAP_ANIMATION}
289
- enterAnimation={enterAnimation ?? defaultAnimation ?? SEED_ENTER_ANIMATION}
290
- exitAnimation={exitAnimation ?? defaultAnimation ?? SEED_EXIT_ANIMATION}
291
- />
292
- );
293
- });
290
+ return (
291
+ <SheetContent
292
+ {...(ref ? { ref } : {})}
293
+ {...restProps}
294
+ className={clsx(classNames.content, className)}
295
+ innerStyle={{
296
+ display: "flex",
297
+ flexDirection: "column",
298
+ minHeight: "0",
299
+ paddingBottom: safeAreaInsetBottom,
300
+ ...innerStyle,
301
+ }}
302
+ snapAnimation={snapAnimation ?? defaultAnimation ?? SEED_SNAP_ANIMATION}
303
+ enterAnimation={enterAnimation ?? defaultAnimation ?? SEED_ENTER_ANIMATION}
304
+ exitAnimation={exitAnimation ?? defaultAnimation ?? SEED_EXIT_ANIMATION}
305
+ />
306
+ );
307
+ });
294
308
  BottomSheetContent.displayName = "BottomSheetContent";
295
309
 
296
310
  ////////////////////////////////////////////////////////////////////////////////////
@@ -301,15 +315,21 @@ export interface BottomSheetHandleProps extends SheetHandleProps {}
301
315
 
302
316
  /**
303
317
  * @remarks
304
- * `SheetHandle`은 forwardRef가 아니므로 ref 시그니처는 제공하지 않는다. touchArea 슬롯은
305
- * `SheetHandle` JSX가 정적으로 children을 받지 않는 구조라 적용하지 않는다 (Lynx
306
- * BackgroundSnapshot이 정적 slot 외의 동적 children 삽입을 허용하지 않음).
318
+ * `SheetHandle`은 forwardRef가 아니므로 ref 시그니처는 제공하지 않는다.
319
+ * Lynx drag handler는 `SheetHandle`의 outer view에 붙으므로, 44x44 target
320
+ * area는 `SheetHandle`에 적용하고 보이는 handle은 그 중심에 배치한다.
307
321
  */
308
- export function BottomSheetHandle(props: BottomSheetHandleProps) {
309
- const { className, style, ...rest } = props;
322
+ export function BottomSheetHandle(props: BottomSheetHandleProps): ReactElement {
323
+ const { children, className, style, ...rest } = props;
310
324
  const classNames = bottomSheetHandle();
311
325
 
312
- return <SheetHandle className={clsx(classNames.root, className)} style={style} {...rest} />;
326
+ return (
327
+ <SheetHandle className={classNames.touchArea} {...rest}>
328
+ <view className={clsx(classNames.root, className)} style={style as never}>
329
+ {children}
330
+ </view>
331
+ </SheetHandle>
332
+ );
313
333
  }
314
334
  BottomSheetHandle.displayName = "BottomSheetHandle";
315
335
 
@@ -323,22 +343,20 @@ BottomSheetHandle.displayName = "BottomSheetHandle";
323
343
  // (lynx-ui-sheet 같은 외부 컴포넌트 감싸기엔 withContext를 그대로 사용해도 안전.)
324
344
  ////////////////////////////////////////////////////////////////////////////////////
325
345
 
326
- export interface BottomSheetSlotProps {
327
- children?: ReactNode;
328
- className?: string;
329
- style?: CSSProperties;
330
- }
346
+ export interface BottomSheetSlotProps extends LynxStyledElementProps {}
331
347
 
332
- function createViewSlot(slotName: keyof BottomSheetClassNames) {
348
+ function createViewSlot(
349
+ slotName: keyof BottomSheetClassNames,
350
+ ): LynxForwardRefComponent<unknown, BottomSheetSlotProps> {
333
351
  const Slot = forwardRef<unknown, BottomSheetSlotProps>((props, ref) => {
334
352
  const { children, className, style } = props;
335
353
  const classNames = useClassNames();
336
354
 
337
355
  return (
338
356
  <view
339
- {...(ref ? { ref: ref as Ref<SVGViewElement> } : {})}
357
+ {...(ref ? ({ ref: ref as LynxViewRef } as Record<string, unknown>) : {})}
340
358
  className={clsx(classNames[slotName], className)}
341
- style={style}
359
+ style={style as never}
342
360
  >
343
361
  {children}
344
362
  </view>
@@ -347,16 +365,18 @@ function createViewSlot(slotName: keyof BottomSheetClassNames) {
347
365
  return Slot;
348
366
  }
349
367
 
350
- function createTextSlot(slotName: keyof BottomSheetClassNames) {
368
+ function createTextSlot(
369
+ slotName: keyof BottomSheetClassNames,
370
+ ): LynxForwardRefComponent<unknown, BottomSheetSlotProps> {
351
371
  const Slot = forwardRef<unknown, BottomSheetSlotProps>((props, ref) => {
352
372
  const { children, className, style } = props;
353
373
  const classNames = useClassNames();
354
374
 
355
375
  return (
356
376
  <text
357
- {...(ref ? { ref: ref as Ref<SVGTextElement> } : {})}
377
+ {...(ref ? ({ ref: ref as LynxTextRef } as Record<string, unknown>) : {})}
358
378
  className={clsx(classNames[slotName], className)}
359
- style={style}
379
+ style={style as never}
360
380
  >
361
381
  {children}
362
382
  </text>
@@ -370,7 +390,24 @@ export const BottomSheetHeader = createViewSlot("header");
370
390
  BottomSheetHeader.displayName = "BottomSheetHeader";
371
391
 
372
392
  export interface BottomSheetBodyProps extends BottomSheetSlotProps {}
373
- export const BottomSheetBody = createViewSlot("body");
393
+ export const BottomSheetBody: LynxForwardRefComponent<unknown, BottomSheetBodyProps> = forwardRef<
394
+ unknown,
395
+ BottomSheetBodyProps
396
+ >((props, ref) => {
397
+ const { children, className, style } = props;
398
+ const classNames = useClassNames();
399
+
400
+ return (
401
+ <scroll-view
402
+ {...(ref ? ({ ref: ref as LynxViewRef } as Record<string, unknown>) : {})}
403
+ scroll-y
404
+ className={clsx(classNames.body, className)}
405
+ style={style as never}
406
+ >
407
+ {children}
408
+ </scroll-view>
409
+ );
410
+ });
374
411
  BottomSheetBody.displayName = "BottomSheetBody";
375
412
 
376
413
  export interface BottomSheetFooterProps extends BottomSheetSlotProps {}
@@ -0,0 +1,85 @@
1
+ import "@testing-library/jest-dom";
2
+ import { render } from "@lynx-js/react/testing-library";
3
+ import { afterEach, describe, expect, it, vi } from "vitest";
4
+
5
+ import { Text } from "../Text";
6
+ import { Box } from "./Box";
7
+
8
+ interface TestLynxGlobal {
9
+ lynx?: {
10
+ __globalProps?: {
11
+ safeAreaInsetTop?: number;
12
+ safeAreaInsetBottom?: number;
13
+ };
14
+ };
15
+ lynxTestingEnv?: {
16
+ backgroundThread: {
17
+ globalThis: TestLynxGlobal;
18
+ };
19
+ mainThread: {
20
+ globalThis: TestLynxGlobal;
21
+ };
22
+ };
23
+ }
24
+
25
+ function getRenderedRoot() {
26
+ const root = elementTree.root;
27
+
28
+ if (!root) {
29
+ throw new Error("Expected Lynx render root to exist.");
30
+ }
31
+
32
+ return root;
33
+ }
34
+
35
+ function expectStyle(style: CSSStyleDeclaration, expected: Record<string, string>) {
36
+ for (const [key, value] of Object.entries(expected)) {
37
+ expect(style.getPropertyValue(key)).toBe(value);
38
+ }
39
+ }
40
+
41
+ function setGlobalProps(
42
+ globalProps: NonNullable<NonNullable<TestLynxGlobal["lynx"]>["__globalProps"]>,
43
+ ) {
44
+ const lynxTestingEnv = (globalThis as TestLynxGlobal).lynxTestingEnv;
45
+ const globals = [
46
+ globalThis as TestLynxGlobal,
47
+ lynxTestingEnv?.backgroundThread.globalThis,
48
+ lynxTestingEnv?.mainThread.globalThis,
49
+ ].filter((global): global is TestLynxGlobal => Boolean(global));
50
+
51
+ for (const global of globals) {
52
+ global.lynx = {
53
+ ...global.lynx,
54
+ __globalProps: globalProps,
55
+ };
56
+ }
57
+ }
58
+
59
+ describe("Box", () => {
60
+ afterEach(() => {
61
+ setGlobalProps({});
62
+ vi.unstubAllGlobals();
63
+ });
64
+
65
+ it("resolves top and bottom safe area padding from global props", () => {
66
+ setGlobalProps({
67
+ safeAreaInsetTop: 47,
68
+ safeAreaInsetBottom: 34,
69
+ });
70
+
71
+ render(
72
+ <Box className="box-test" pt="safeArea" pb="safeArea">
73
+ <Text>Box content</Text>
74
+ </Box>,
75
+ );
76
+
77
+ const box = getRenderedRoot().querySelector(".box-test");
78
+
79
+ expect(box).toBeInTheDocument();
80
+ expectStyle((box as HTMLElement).style, {
81
+ "padding-top": "47px",
82
+ "padding-bottom": "34px",
83
+ });
84
+ });
85
+ });
@@ -0,0 +1,39 @@
1
+ import clsx from "clsx";
2
+ import * as React from "@lynx-js/react";
3
+
4
+ import type { LynxPressableProps, LynxStyledElementProps, LynxViewRef } from "../../types";
5
+ import { useStyleProps, type StyleProps } from "../../utils/styled";
6
+
7
+ /**
8
+ * @platform Lynx
9
+ *
10
+ * `Box`는 SEED style prop을 native `<view>`의 object style로 변환하는 편의
11
+ * primitive입니다. 많은 항목을 반복 렌더링하는 화면에서는 primitive 컴포넌트
12
+ * 렌더링과 style prop 해석 비용이 누적될 수 있습니다. 앱 코드에서는 native
13
+ * `<view>`/`<text>`와 Tailwind utility className을 우선 고려하세요. 패키지
14
+ * 컴포넌트 내부에서는 native tag와 recipe className을 사용하고, inline style은
15
+ * 런타임 값이 필요한 경우에만 제한적으로 사용합니다.
16
+ */
17
+ export interface BoxProps extends StyleProps, LynxStyledElementProps, LynxPressableProps {
18
+ bindtouchstart?: () => void;
19
+ bindtouchend?: () => void;
20
+ bindtouchcancel?: () => void;
21
+ }
22
+
23
+ export const Box = React.forwardRef<unknown, BoxProps>((props, ref) => {
24
+ const { style, restProps } = useStyleProps(props);
25
+ const { children, className, ...nativeProps } = restProps;
26
+
27
+ return (
28
+ <view
29
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
30
+ {...nativeProps}
31
+ className={clsx(className)}
32
+ style={style}
33
+ >
34
+ {children}
35
+ </view>
36
+ );
37
+ });
38
+
39
+ Box.displayName = "Box";
@@ -0,0 +1 @@
1
+ export { Box, type BoxProps } from "./Box";