@seed-design/lynx-react 0.7.1 → 0.9.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 (265) hide show
  1. package/lib/components/Accordion/Accordion.jsx +2 -2
  2. package/lib/components/ActionButton/ActionButton.jsx +1 -1
  3. package/lib/components/AlertDialog/AlertDialog.d.ts +64 -0
  4. package/lib/components/AlertDialog/AlertDialog.d.ts.map +1 -0
  5. package/lib/components/AlertDialog/AlertDialog.jsx +138 -0
  6. package/lib/components/AlertDialog/AlertDialog.namespace.d.ts +2 -0
  7. package/lib/components/AlertDialog/AlertDialog.namespace.d.ts.map +1 -0
  8. package/lib/components/AlertDialog/AlertDialog.namespace.js +17 -0
  9. package/lib/components/AlertDialog/index.d.ts +3 -0
  10. package/lib/components/AlertDialog/index.d.ts.map +1 -0
  11. package/lib/components/AppBar/AppBar.d.ts +14 -1
  12. package/lib/components/AppBar/AppBar.d.ts.map +1 -1
  13. package/lib/components/AppBar/AppBar.jsx +42 -16
  14. package/lib/components/AppBar/AppBar.namespace.d.ts +1 -1
  15. package/lib/components/AppBar/AppBar.namespace.d.ts.map +1 -1
  16. package/lib/components/AppBar/context.d.ts +2 -1
  17. package/lib/components/AppBar/context.d.ts.map +1 -1
  18. package/lib/components/AppBar/index.d.ts +1 -1
  19. package/lib/components/AppBar/index.d.ts.map +1 -1
  20. package/lib/components/AppBar/useAppBar.d.ts +2 -1
  21. package/lib/components/AppBar/useAppBar.d.ts.map +1 -1
  22. package/lib/components/AppBar/useAppBar.js +17 -15
  23. package/lib/components/AttachmentDisplay/AttachmentDisplay.d.ts +182 -0
  24. package/lib/components/AttachmentDisplay/AttachmentDisplay.d.ts.map +1 -0
  25. package/lib/components/AttachmentDisplay/AttachmentDisplay.jsx +444 -0
  26. package/lib/components/AttachmentDisplay/index.d.ts +2 -0
  27. package/lib/components/AttachmentDisplay/index.d.ts.map +1 -0
  28. package/lib/components/AttachmentInput/AttachmentInput.d.ts +179 -0
  29. package/lib/components/AttachmentInput/AttachmentInput.d.ts.map +1 -0
  30. package/lib/components/AttachmentInput/AttachmentInput.jsx +520 -0
  31. package/lib/components/AttachmentInput/index.d.ts +2 -0
  32. package/lib/components/AttachmentInput/index.d.ts.map +1 -0
  33. package/lib/components/BottomSheet/BottomSheet.d.ts +1 -1
  34. package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -1
  35. package/lib/components/BottomSheet/BottomSheet.jsx +1 -7
  36. package/lib/components/Box/Box.d.ts +3 -3
  37. package/lib/components/Box/Box.d.ts.map +1 -1
  38. package/lib/components/Callout/Callout.jsx +2 -2
  39. package/lib/components/Checkbox/Checkbox.jsx +2 -2
  40. package/lib/components/Chip/Chip.jsx +2 -2
  41. package/lib/components/ChipTabs/ChipTabs.d.ts +41 -0
  42. package/lib/components/ChipTabs/ChipTabs.d.ts.map +1 -0
  43. package/lib/components/ChipTabs/ChipTabs.jsx +39 -0
  44. package/lib/components/ChipTabs/ChipTabs.namespace.d.ts +2 -0
  45. package/lib/components/ChipTabs/ChipTabs.namespace.d.ts.map +1 -0
  46. package/lib/components/ChipTabs/ChipTabs.namespace.js +13 -0
  47. package/lib/components/ChipTabs/index.d.ts +3 -0
  48. package/lib/components/ChipTabs/index.d.ts.map +1 -0
  49. package/lib/components/ContextualFloatingButton/ContextualFloatingButton.d.ts +18 -0
  50. package/lib/components/ContextualFloatingButton/ContextualFloatingButton.d.ts.map +1 -0
  51. package/lib/components/ContextualFloatingButton/ContextualFloatingButton.jsx +113 -0
  52. package/lib/components/ContextualFloatingButton/index.d.ts +2 -0
  53. package/lib/components/ContextualFloatingButton/index.d.ts.map +1 -0
  54. package/lib/components/Dialog/Dialog.d.ts +87 -0
  55. package/lib/components/Dialog/Dialog.d.ts.map +1 -0
  56. package/lib/components/Dialog/Dialog.jsx +147 -0
  57. package/lib/components/Dialog/Dialog.namespace.d.ts +2 -0
  58. package/lib/components/Dialog/Dialog.namespace.d.ts.map +1 -0
  59. package/lib/components/Dialog/Dialog.namespace.js +18 -0
  60. package/lib/components/Dialog/index.d.ts +3 -0
  61. package/lib/components/Dialog/index.d.ts.map +1 -0
  62. package/lib/components/FloatingActionButton/FloatingActionButton.d.ts +23 -0
  63. package/lib/components/FloatingActionButton/FloatingActionButton.d.ts.map +1 -0
  64. package/lib/components/FloatingActionButton/FloatingActionButton.jsx +76 -0
  65. package/lib/components/FloatingActionButton/FloatingActionButton.namespace.d.ts +2 -0
  66. package/lib/components/FloatingActionButton/FloatingActionButton.namespace.d.ts.map +1 -0
  67. package/lib/components/FloatingActionButton/FloatingActionButton.namespace.js +10 -0
  68. package/lib/components/FloatingActionButton/index.d.ts +3 -0
  69. package/lib/components/FloatingActionButton/index.d.ts.map +1 -0
  70. package/lib/components/HelpBubble/HelpBubble.d.ts +67 -0
  71. package/lib/components/HelpBubble/HelpBubble.d.ts.map +1 -0
  72. package/lib/components/HelpBubble/HelpBubble.jsx +600 -0
  73. package/lib/components/HelpBubble/HelpBubble.namespace.d.ts +2 -0
  74. package/lib/components/HelpBubble/HelpBubble.namespace.d.ts.map +1 -0
  75. package/lib/components/HelpBubble/HelpBubble.namespace.js +18 -0
  76. package/lib/components/HelpBubble/index.d.ts +3 -0
  77. package/lib/components/HelpBubble/index.d.ts.map +1 -0
  78. package/lib/components/Icon/Icon.d.ts +1 -0
  79. package/lib/components/Icon/Icon.d.ts.map +1 -1
  80. package/lib/components/Icon/Icon.jsx +2 -1
  81. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.d.ts +28 -0
  82. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.d.ts.map +1 -0
  83. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.jsx +37 -0
  84. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.d.ts +2 -0
  85. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.d.ts.map +1 -0
  86. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.js +9 -0
  87. package/lib/components/IdentityPlaceholder/identity-placeholder-business.js +4 -0
  88. package/lib/components/IdentityPlaceholder/identity-placeholder-person.js +4 -0
  89. package/lib/components/IdentityPlaceholder/index.d.ts +3 -0
  90. package/lib/components/IdentityPlaceholder/index.d.ts.map +1 -0
  91. package/lib/components/InputButton/InputButton.d.ts +2 -0
  92. package/lib/components/InputButton/InputButton.d.ts.map +1 -1
  93. package/lib/components/InputButton/InputButton.jsx +40 -8
  94. package/lib/components/List/List.d.ts +1 -1
  95. package/lib/components/List/List.d.ts.map +1 -1
  96. package/lib/components/List/List.jsx +26 -12
  97. package/lib/components/Menu/Menu.d.ts.map +1 -1
  98. package/lib/components/Menu/Menu.jsx +15 -5
  99. package/lib/components/PageBanner/PageBanner.d.ts +3 -0
  100. package/lib/components/PageBanner/PageBanner.d.ts.map +1 -1
  101. package/lib/components/PageBanner/PageBanner.jsx +49 -11
  102. package/lib/components/QuantityPicker/QuantityPicker.d.ts +53 -0
  103. package/lib/components/QuantityPicker/QuantityPicker.d.ts.map +1 -0
  104. package/lib/components/QuantityPicker/QuantityPicker.jsx +282 -0
  105. package/lib/components/QuantityPicker/QuantityPicker.namespace.d.ts +2 -0
  106. package/lib/components/QuantityPicker/QuantityPicker.namespace.d.ts.map +1 -0
  107. package/lib/components/QuantityPicker/QuantityPicker.namespace.js +11 -0
  108. package/lib/components/QuantityPicker/index.d.ts +3 -0
  109. package/lib/components/QuantityPicker/index.d.ts.map +1 -0
  110. package/lib/components/RadioGroup/RadioGroup.jsx +2 -2
  111. package/lib/components/ReactionButton/ReactionButton.d.ts +1 -1
  112. package/lib/components/ReactionButton/ReactionButton.d.ts.map +1 -1
  113. package/lib/components/ReactionButton/ReactionButton.jsx +22 -13
  114. package/lib/components/ScrollFog/ScrollFog.d.ts +41 -0
  115. package/lib/components/ScrollFog/ScrollFog.d.ts.map +1 -0
  116. package/lib/components/ScrollFog/ScrollFog.jsx +65 -0
  117. package/lib/components/ScrollFog/index.d.ts +2 -0
  118. package/lib/components/ScrollFog/index.d.ts.map +1 -0
  119. package/lib/components/SegmentedControl/SegmentedControl.jsx +2 -2
  120. package/lib/components/Select/Select.d.ts +112 -0
  121. package/lib/components/Select/Select.d.ts.map +1 -0
  122. package/lib/components/Select/Select.jsx +885 -0
  123. package/lib/components/Select/Select.namespace.d.ts +2 -0
  124. package/lib/components/Select/Select.namespace.d.ts.map +1 -0
  125. package/lib/components/Select/Select.namespace.js +23 -0
  126. package/lib/components/Select/index.d.ts +3 -0
  127. package/lib/components/Select/index.d.ts.map +1 -0
  128. package/lib/components/Skeleton/Skeleton.d.ts +15 -0
  129. package/lib/components/Skeleton/Skeleton.d.ts.map +1 -0
  130. package/lib/components/Skeleton/Skeleton.jsx +18 -0
  131. package/lib/components/Skeleton/index.d.ts +2 -0
  132. package/lib/components/Skeleton/index.d.ts.map +1 -0
  133. package/lib/components/Slider/Slider.d.ts +101 -0
  134. package/lib/components/Slider/Slider.d.ts.map +1 -0
  135. package/lib/components/Slider/Slider.jsx +573 -0
  136. package/lib/components/Slider/Slider.namespace.d.ts +2 -0
  137. package/lib/components/Slider/Slider.namespace.d.ts.map +1 -0
  138. package/lib/components/Slider/Slider.namespace.js +19 -0
  139. package/lib/components/Slider/Slider.utils.d.ts +19 -0
  140. package/lib/components/Slider/Slider.utils.d.ts.map +1 -0
  141. package/lib/components/Slider/Slider.utils.js +106 -0
  142. package/lib/components/Slider/index.d.ts +3 -0
  143. package/lib/components/Slider/index.d.ts.map +1 -0
  144. package/lib/components/Stack/Stack.d.ts +7 -9
  145. package/lib/components/Stack/Stack.d.ts.map +1 -1
  146. package/lib/components/SwipeableMenuSheet/SwipeableMenuSheet.d.ts.map +1 -1
  147. package/lib/components/SwipeableMenuSheet/SwipeableMenuSheet.jsx +22 -8
  148. package/lib/components/Switch/Switch.d.ts.map +1 -1
  149. package/lib/components/Switch/Switch.jsx +5 -3
  150. package/lib/components/Tabs/Tabs.d.ts +34 -2
  151. package/lib/components/Tabs/Tabs.d.ts.map +1 -1
  152. package/lib/components/Tabs/Tabs.jsx +186 -56
  153. package/lib/components/Tabs/Tabs.utils.d.ts +14 -1
  154. package/lib/components/Tabs/Tabs.utils.d.ts.map +1 -1
  155. package/lib/components/Tabs/Tabs.utils.js +24 -3
  156. package/lib/components/Tabs/index.d.ts +1 -1
  157. package/lib/components/Tabs/index.d.ts.map +1 -1
  158. package/lib/components/index.d.ts +14 -0
  159. package/lib/components/index.d.ts.map +1 -1
  160. package/lib/hooks/useControllableState.d.ts +1 -14
  161. package/lib/hooks/useControllableState.d.ts.map +1 -1
  162. package/lib/hooks/usePressTap.d.ts +1 -27
  163. package/lib/hooks/usePressTap.d.ts.map +1 -1
  164. package/lib/hooks/useSafeArea.d.ts +8 -4
  165. package/lib/hooks/useSafeArea.d.ts.map +1 -1
  166. package/lib/hooks/useSafeArea.js +22 -20
  167. package/lib/index.js +29 -6
  168. package/lib/{hooks/useControllableState.js → packages/lynx-react-headless/use-controllable-state/lib/index.js} +2 -3
  169. package/lib/{hooks/usePressTap.js → packages/lynx-react-headless/use-press-tap/lib/index.js} +13 -10
  170. package/lib/types.d.ts +1 -0
  171. package/lib/types.d.ts.map +1 -1
  172. package/lib/utils/styled.d.ts +46 -6
  173. package/lib/utils/styled.d.ts.map +1 -1
  174. package/lib/utils/styled.js +19 -4
  175. package/package.json +9 -5
  176. package/src/components/AlertDialog/AlertDialog.namespace.ts +22 -0
  177. package/src/components/AlertDialog/AlertDialog.test.tsx +226 -0
  178. package/src/components/AlertDialog/AlertDialog.tsx +374 -0
  179. package/src/components/AlertDialog/index.ts +24 -0
  180. package/src/components/AppBar/AppBar.namespace.ts +1 -0
  181. package/src/components/AppBar/AppBar.test.tsx +252 -3
  182. package/src/components/AppBar/AppBar.tsx +108 -25
  183. package/src/components/AppBar/context.ts +3 -1
  184. package/src/components/AppBar/index.ts +1 -0
  185. package/src/components/AppBar/useAppBar.ts +21 -15
  186. package/src/components/AttachmentDisplay/AttachmentDisplay.tsx +886 -0
  187. package/src/components/AttachmentDisplay/index.ts +48 -0
  188. package/src/components/AttachmentInput/AttachmentInput.tsx +1001 -0
  189. package/src/components/AttachmentInput/index.ts +49 -0
  190. package/src/components/BottomSheet/BottomSheet.test.tsx +22 -7
  191. package/src/components/BottomSheet/BottomSheet.tsx +1 -19
  192. package/src/components/Box/Box.test.tsx +104 -0
  193. package/src/components/Box/Box.tsx +9 -6
  194. package/src/components/ChipTabs/ChipTabs.namespace.ts +14 -0
  195. package/src/components/ChipTabs/ChipTabs.tsx +119 -0
  196. package/src/components/ChipTabs/index.ts +16 -0
  197. package/src/components/ContextualFloatingButton/ContextualFloatingButton.test.tsx +63 -0
  198. package/src/components/ContextualFloatingButton/ContextualFloatingButton.tsx +232 -0
  199. package/src/components/ContextualFloatingButton/index.ts +4 -0
  200. package/src/components/Dialog/Dialog.namespace.ts +24 -0
  201. package/src/components/Dialog/Dialog.test.tsx +172 -0
  202. package/src/components/Dialog/Dialog.tsx +373 -0
  203. package/src/components/Dialog/index.ts +26 -0
  204. package/src/components/FloatingActionButton/FloatingActionButton.namespace.ts +8 -0
  205. package/src/components/FloatingActionButton/FloatingActionButton.test.tsx +59 -0
  206. package/src/components/FloatingActionButton/FloatingActionButton.tsx +207 -0
  207. package/src/components/FloatingActionButton/index.ts +10 -0
  208. package/src/components/HelpBubble/HelpBubble.namespace.ts +24 -0
  209. package/src/components/HelpBubble/HelpBubble.tsx +998 -0
  210. package/src/components/HelpBubble/index.ts +26 -0
  211. package/src/components/Icon/Icon.test.tsx +0 -3
  212. package/src/components/Icon/Icon.tsx +11 -1
  213. package/src/components/IdentityPlaceholder/IdentityPlaceholder.namespace.ts +6 -0
  214. package/src/components/IdentityPlaceholder/IdentityPlaceholder.tsx +103 -0
  215. package/src/components/IdentityPlaceholder/assets.d.ts +4 -0
  216. package/src/components/IdentityPlaceholder/identity-placeholder-business.webp +0 -0
  217. package/src/components/IdentityPlaceholder/identity-placeholder-person.webp +0 -0
  218. package/src/components/IdentityPlaceholder/index.ts +8 -0
  219. package/src/components/InputButton/InputButton.test.tsx +128 -1
  220. package/src/components/InputButton/InputButton.tsx +60 -9
  221. package/src/components/List/List.test.tsx +54 -2
  222. package/src/components/List/List.tsx +43 -10
  223. package/src/components/Menu/Menu.tsx +14 -4
  224. package/src/components/Menu/__tests__/Menu.test.tsx +34 -0
  225. package/src/components/PageBanner/PageBanner.test.tsx +110 -3
  226. package/src/components/PageBanner/PageBanner.tsx +83 -19
  227. package/src/components/QuantityPicker/QuantityPicker.namespace.ts +10 -0
  228. package/src/components/QuantityPicker/QuantityPicker.test.tsx +235 -0
  229. package/src/components/QuantityPicker/QuantityPicker.tsx +605 -0
  230. package/src/components/QuantityPicker/index.ts +14 -0
  231. package/src/components/ReactionButton/ReactionButton.feedback.test.tsx +50 -0
  232. package/src/components/ReactionButton/ReactionButton.test.tsx +5 -3
  233. package/src/components/ReactionButton/ReactionButton.tsx +20 -12
  234. package/src/components/ScrollFog/ScrollFog.test.tsx +222 -0
  235. package/src/components/ScrollFog/ScrollFog.tsx +172 -0
  236. package/src/components/ScrollFog/index.ts +1 -0
  237. package/src/components/Select/Select.namespace.ts +34 -0
  238. package/src/components/Select/Select.test.tsx +297 -0
  239. package/src/components/Select/Select.tsx +1483 -0
  240. package/src/components/Select/index.ts +39 -0
  241. package/src/components/Skeleton/Skeleton.test.tsx +64 -0
  242. package/src/components/Skeleton/Skeleton.tsx +45 -0
  243. package/src/components/Skeleton/index.ts +1 -0
  244. package/src/components/Slider/Slider.namespace.ts +26 -0
  245. package/src/components/Slider/Slider.test.tsx +260 -0
  246. package/src/components/Slider/Slider.tsx +897 -0
  247. package/src/components/Slider/Slider.utils.ts +167 -0
  248. package/src/components/Slider/index.ts +30 -0
  249. package/src/components/Stack/Stack.test.tsx +7 -0
  250. package/src/components/Stack/Stack.tsx +11 -10
  251. package/src/components/SwipeableMenuSheet/SwipeableMenuSheet.test.tsx +21 -1
  252. package/src/components/SwipeableMenuSheet/SwipeableMenuSheet.tsx +30 -7
  253. package/src/components/Switch/Switch.tsx +14 -13
  254. package/src/components/Tabs/Tabs.test.tsx +218 -1
  255. package/src/components/Tabs/Tabs.tsx +346 -75
  256. package/src/components/Tabs/Tabs.utils.ts +51 -1
  257. package/src/components/Tabs/index.ts +17 -1
  258. package/src/components/index.ts +14 -0
  259. package/src/hooks/useControllableState.ts +1 -43
  260. package/src/hooks/usePressTap.test.ts +23 -11
  261. package/src/hooks/usePressTap.ts +1 -60
  262. package/src/hooks/useSafeArea.test.ts +13 -1
  263. package/src/hooks/useSafeArea.ts +25 -23
  264. package/src/types.ts +1 -0
  265. package/src/utils/styled.ts +127 -9
@@ -36,6 +36,7 @@ export function getTabsOrderedItems<T extends { value: string }>(
36
36
  export function getTabsTriggerRects(
37
37
  values: string[],
38
38
  widths: Record<string, number>,
39
+ triggerGap = 0,
39
40
  ): Record<string, TabsLayoutRect> {
40
41
  const rects = Object.create(null) as Record<string, TabsLayoutRect>;
41
42
  let left = 0;
@@ -49,12 +50,61 @@ export function getTabsTriggerRects(
49
50
  continue;
50
51
  }
51
52
  if (hasCompletePrefix) rects[value] = { left, width };
52
- left += width;
53
+ left += width + triggerGap;
53
54
  }
54
55
 
55
56
  return rects;
56
57
  }
57
58
 
59
+ /**
60
+ * Trigger rectangles begin at the first trigger, so they exclude list-content
61
+ * padding. Scroll offsets are measured from the scroll content edge.
62
+ */
63
+ export function getTabsScrollOffset({
64
+ scrollAlign,
65
+ currentOffset,
66
+ viewportWidth,
67
+ contentWidth,
68
+ contentInsetStart,
69
+ contentInsetEnd,
70
+ triggerRect,
71
+ }: {
72
+ scrollAlign: "nearest" | "start" | "center" | "end";
73
+ currentOffset: number;
74
+ viewportWidth: number;
75
+ contentWidth: number;
76
+ contentInsetStart: number;
77
+ contentInsetEnd: number;
78
+ triggerRect: TabsLayoutRect;
79
+ }): number {
80
+ const maxOffset = Math.max(0, contentWidth - viewportWidth);
81
+ const clampOffset = (offset: number) => Math.min(maxOffset, Math.max(0, offset));
82
+ const clampedCurrentOffset = clampOffset(currentOffset);
83
+ if (maxOffset === 0) return clampedCurrentOffset;
84
+
85
+ const triggerStart = contentInsetStart + triggerRect.left;
86
+ const triggerEnd = triggerStart + triggerRect.width;
87
+ if (scrollAlign === "nearest") {
88
+ // Visibility is the scroll-view's actual viewport. Insets provide the
89
+ // additional room only after a clipped trigger needs to move.
90
+ const visibleStart = clampedCurrentOffset;
91
+ const visibleEnd = clampedCurrentOffset + viewportWidth;
92
+
93
+ if (triggerStart >= visibleStart && triggerEnd <= visibleEnd) return clampedCurrentOffset;
94
+ return clampOffset(
95
+ triggerStart < visibleStart
96
+ ? triggerStart - contentInsetStart
97
+ : triggerEnd + contentInsetEnd - viewportWidth,
98
+ );
99
+ }
100
+
101
+ if (scrollAlign === "start") return clampOffset(triggerStart - contentInsetStart);
102
+ if (scrollAlign === "center") {
103
+ return clampOffset(triggerStart + triggerRect.width / 2 - viewportWidth / 2);
104
+ }
105
+ return clampOffset(triggerEnd + contentInsetEnd - viewportWidth);
106
+ }
107
+
58
108
  export function areTabsTransitionsEnabled(
59
109
  values: string[],
60
110
  rects: Record<string, TabsLayoutRect>,
@@ -1,2 +1,18 @@
1
- export * from "./Tabs";
1
+ export {
2
+ TabsCarousel,
3
+ TabsCarouselCamera,
4
+ TabsContent,
5
+ TabsIndicator,
6
+ TabsList,
7
+ TabsRoot,
8
+ TabsTrigger,
9
+ type TabsCarouselCameraProps,
10
+ type TabsCarouselProps,
11
+ type TabsContentProps,
12
+ type TabsIndicatorProps,
13
+ type TabsListProps,
14
+ type TabsRootProps,
15
+ type TabsTriggerProps,
16
+ } from "./Tabs";
17
+
2
18
  export * as Tabs from "./Tabs.namespace";
@@ -7,10 +7,17 @@ export * from "./BottomSheet";
7
7
  export * from "./Callout";
8
8
  export * from "./Checkbox";
9
9
  export * from "./Chip";
10
+ export * from "./ChipTabs";
10
11
  export * from "./Count";
12
+ export * from "./ContextualFloatingButton";
11
13
  export * from "./Divider";
12
14
  export * from "./Field";
15
+ export * from "./FloatingActionButton";
13
16
  export * from "./Icon";
17
+ export * from "./Dialog";
18
+ export * from "./AlertDialog";
19
+ export * from "./HelpBubble";
20
+ export * from "./IdentityPlaceholder";
14
21
  export * from "./InputButton";
15
22
  export * from "./KeyboardAvoidingScrollView";
16
23
  export * from "./MannerTemp";
@@ -20,15 +27,22 @@ export * from "./NotificationBadge";
20
27
  export * from "./PageBanner";
21
28
  export * from "./List";
22
29
  export * from "./ProgressCircle";
30
+ export * from "./QuantityPicker";
23
31
  export * from "./RadioGroup";
24
32
  export * from "./ReactionButton";
25
33
  export * from "./ScaleFeedback";
26
34
  export * from "./SegmentedControl";
35
+ export * from "./Select";
27
36
  export * from "./SelectBox";
37
+ export * from "./Skeleton";
38
+ export * from "./Slider";
28
39
  export * from "./Stack";
29
40
  export * from "./Switch";
41
+ export * from "./ScrollFog";
30
42
  export * from "./SwipeableMenuSheet";
31
43
  export * from "./Tabs";
32
44
  export * from "./TagGroup";
33
45
  export * from "./Text";
34
46
  export * from "./TextField";
47
+ export * from "./AttachmentInput";
48
+ export * from "./AttachmentDisplay";
@@ -1,43 +1 @@
1
- import { useRef, useState } from "@lynx-js/react";
2
- import { useMemoizedFn } from "@lynx-js/lynx-ui-common";
3
-
4
- export interface UseControllableStateProps<T> {
5
- value?: T;
6
- defaultValue: T;
7
- onChange?: (value: T) => void;
8
- }
9
-
10
- /**
11
- * Controlled/uncontrolled state pattern for Lynx components.
12
- *
13
- * - When `value` is provided, the component is controlled.
14
- * - When `value` is `undefined`, the component manages its own state
15
- * starting from `defaultValue`.
16
- * - `onChange` fires in both modes.
17
- */
18
- export function useControllableState<T>(
19
- props: UseControllableStateProps<T>,
20
- ): [T, (value: T) => void] {
21
- const { value, defaultValue, onChange } = props;
22
- const isControlled = value !== undefined;
23
- const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
24
- const currentValue = isControlled ? value : uncontrolledValue;
25
- const prevValueRef = useRef(currentValue);
26
-
27
- const setValue = useMemoizedFn((nextValue: T) => {
28
- if (isControlled) {
29
- if (nextValue !== value) {
30
- onChange?.(nextValue);
31
- }
32
- return;
33
- }
34
-
35
- setUncontrolledValue(nextValue);
36
- if (nextValue !== prevValueRef.current) {
37
- onChange?.(nextValue);
38
- prevValueRef.current = nextValue;
39
- }
40
- });
41
-
42
- return [currentValue, setValue];
43
- }
1
+ export * from "@seed-design/lynx-react-use-controllable-state";
@@ -60,8 +60,8 @@ describe("usePressTap", () => {
60
60
  expect(onTap).toHaveBeenCalledTimes(1);
61
61
  });
62
62
 
63
- it("clears pressed when bindtap fires before touchend", () => {
64
- const { result } = renderHook(() => usePressTap({ onTap: vi.fn() }));
63
+ it("clears pressed on tap without requiring a callback", () => {
64
+ const { result } = renderHook(() => usePressTap());
65
65
 
66
66
  act(() => {
67
67
  result.current.bindtouchstart(fakeEvent);
@@ -72,19 +72,31 @@ describe("usePressTap", () => {
72
72
 
73
73
  expect(result.current.pressed).toBe(false);
74
74
  });
75
+ });
75
76
 
76
- it("can be omitted without error", () => {
77
- const { result } = renderHook(() => usePressTap());
77
+ describe("disabled", () => {
78
+ it("clears an active press without restoring it when re-enabled", () => {
79
+ const { result, rerender } = renderHook(({ disabled }) => usePressTap({ disabled }), {
80
+ initialProps: { disabled: false },
81
+ });
82
+
83
+ act(() => {
84
+ result.current.bindtouchstart(fakeEvent);
85
+ });
86
+ expect(result.current.pressed).toBe(true);
78
87
 
79
- expect(() => {
80
- act(() => {
81
- result.current.bindtap(fakeEvent);
82
- });
83
- }).not.toThrow();
88
+ rerender({ disabled: true });
89
+ expect(result.current.pressed).toBe(false);
90
+
91
+ rerender({ disabled: false });
92
+ expect(result.current.pressed).toBe(false);
93
+
94
+ act(() => {
95
+ result.current.bindtouchstart(fakeEvent);
96
+ });
97
+ expect(result.current.pressed).toBe(true);
84
98
  });
85
- });
86
99
 
87
- describe("disabled", () => {
88
100
  it("does not update pressed on bindtouchstart", () => {
89
101
  const { result } = renderHook(() => usePressTap({ disabled: true }));
90
102
 
@@ -1,60 +1 @@
1
- import { useState } from "@lynx-js/react";
2
- import { useMemoizedFn } from "@lynx-js/lynx-ui-common";
3
- import type { BaseTouchEvent, EventHandler, Target } from "@lynx-js/types";
4
-
5
- import type { LynxTouchProps } from "../types";
6
-
7
- type TouchHandler = EventHandler<BaseTouchEvent<Target>>;
8
- type PressStateHandler = TouchHandler & (() => void);
9
-
10
- export interface UsePressTapOptions {
11
- disabled?: boolean;
12
- onTap?: TouchHandler;
13
- mainThreadOnTap?: LynxTouchProps["main-thread:bindtap"];
14
- }
15
-
16
- export interface UsePressTapReturn {
17
- pressed: boolean;
18
- bindtap: TouchHandler;
19
- bindtouchstart: PressStateHandler;
20
- bindtouchend: PressStateHandler;
21
- bindtouchcancel: PressStateHandler;
22
- "main-thread:bindtap"?: LynxTouchProps["main-thread:bindtap"];
23
- }
24
-
25
- /**
26
- * Hook that provides press/tap interaction state for Lynx elements.
27
- *
28
- * - Tracks `pressed` state via touch events.
29
- * - When `disabled` is true, the element cannot become active and no tap fires.
30
- * - If the element becomes disabled during a press, the active state is cleared.
31
- * - `main-thread:bindtap` is conditionally included (only when enabled and provided).
32
- */
33
- export function usePressTap(options: UsePressTapOptions = {}): UsePressTapReturn {
34
- const { disabled = false, onTap, mainThreadOnTap } = options;
35
- const [pressed, setPressed] = useState(false);
36
-
37
- const press = useMemoizedFn(() => {
38
- if (disabled) return;
39
- setPressed(true);
40
- });
41
-
42
- const reset = useMemoizedFn(() => {
43
- setPressed(false);
44
- });
45
-
46
- const handleTap = useMemoizedFn((...args: Parameters<TouchHandler>) => {
47
- if (disabled) return;
48
- setPressed(false);
49
- onTap?.(...args);
50
- });
51
-
52
- return {
53
- pressed,
54
- bindtap: handleTap,
55
- bindtouchstart: press,
56
- bindtouchend: reset,
57
- bindtouchcancel: reset,
58
- ...(!disabled && mainThreadOnTap ? { "main-thread:bindtap": mainThreadOnTap } : {}),
59
- };
60
- }
1
+ export * from "@seed-design/lynx-react-use-press-tap";
@@ -7,7 +7,9 @@ interface TestLynxGlobal {
7
7
  lynx?: {
8
8
  __globalProps?: {
9
9
  safeAreaInsetTop?: number;
10
+ safeAreaInsetRight?: number;
10
11
  safeAreaInsetBottom?: number;
12
+ safeAreaInsetLeft?: number;
11
13
  };
12
14
  };
13
15
  lynxTestingEnv?: {
@@ -49,35 +51,45 @@ describe("useSafeArea", () => {
49
51
 
50
52
  expect(result.current).toEqual({
51
53
  safeAreaInsetTop: "env(safe-area-inset-top)",
54
+ safeAreaInsetRight: "env(safe-area-inset-right)",
52
55
  safeAreaInsetBottom: "env(safe-area-inset-bottom)",
56
+ safeAreaInsetLeft: "env(safe-area-inset-left)",
53
57
  });
54
58
  });
55
59
 
56
60
  it("uses flat globalProps values before env fallback", () => {
57
61
  setGlobalProps({
58
62
  safeAreaInsetTop: 48,
63
+ safeAreaInsetRight: 62,
59
64
  safeAreaInsetBottom: 35,
65
+ safeAreaInsetLeft: 59,
60
66
  });
61
67
 
62
68
  const { result } = renderHook(() => useSafeArea());
63
69
 
64
70
  expect(result.current).toEqual({
65
71
  safeAreaInsetTop: "48px",
72
+ safeAreaInsetRight: "62px",
66
73
  safeAreaInsetBottom: "35px",
74
+ safeAreaInsetLeft: "59px",
67
75
  });
68
76
  });
69
77
 
70
- it("falls back to Lynx env values when host flat global props are zero", () => {
78
+ it("falls back to Lynx env values only for top and bottom when host flat global props are zero", () => {
71
79
  setGlobalProps({
72
80
  safeAreaInsetTop: 0,
81
+ safeAreaInsetRight: 0,
73
82
  safeAreaInsetBottom: 0,
83
+ safeAreaInsetLeft: 0,
74
84
  });
75
85
 
76
86
  const { result } = renderHook(() => useSafeArea());
77
87
 
78
88
  expect(result.current).toEqual({
79
89
  safeAreaInsetTop: "env(safe-area-inset-top)",
90
+ safeAreaInsetRight: "0px",
80
91
  safeAreaInsetBottom: "env(safe-area-inset-bottom)",
92
+ safeAreaInsetLeft: "0px",
81
93
  });
82
94
  });
83
95
  });
@@ -1,56 +1,58 @@
1
1
  import { useMemo } from "@lynx-js/react";
2
2
 
3
- type SafeAreaEdge = "top" | "bottom";
3
+ type SafeAreaEdge = "top" | "right" | "bottom" | "left";
4
4
 
5
5
  declare module "@lynx-js/types" {
6
6
  interface GlobalProps {
7
7
  safeAreaInsetTop?: number;
8
+ safeAreaInsetRight?: number;
8
9
  safeAreaInsetBottom?: number;
10
+ safeAreaInsetLeft?: number;
9
11
  }
10
12
  }
11
13
 
12
- const safeAreaProp: Record<SafeAreaEdge, "safeAreaInsetTop" | "safeAreaInsetBottom"> = {
13
- top: "safeAreaInsetTop",
14
- bottom: "safeAreaInsetBottom",
15
- };
14
+ function normalizeSafeAreaValue(edge: SafeAreaEdge, value: number | undefined) {
15
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return undefined;
16
16
 
17
- function getSafeAreaEnvInset(edge: SafeAreaEdge): string {
18
- return `env(safe-area-inset-${edge})`;
19
- }
20
-
21
- function normalizeSafeAreaValue(value: number | undefined): string | undefined {
22
- if (typeof value !== "number" || value <= 0 || !Number.isFinite(value)) {
23
- return undefined;
24
- }
17
+ // TODO: confirm why a host-provided 0 falls back to `env()` on top and bottom. The reason isn't
18
+ // recorded anywhere, and a host that subtracts insets its parent already consumed (Android
19
+ // Compose) passes 0 on purpose, which `env()` may then override with the window's inset.
20
+ if (value === 0 && (edge === "top" || edge === "bottom")) return undefined;
25
21
 
26
22
  return `${value}px`;
27
23
  }
28
24
 
29
- function resolveSafeAreaInset(edge: SafeAreaEdge, value: number | undefined): string {
30
- return normalizeSafeAreaValue(value) ?? getSafeAreaEnvInset(edge);
31
- }
25
+ const resolveSafeAreaInset = (edge: SafeAreaEdge, value: number | undefined) =>
26
+ normalizeSafeAreaValue(edge, value) ?? `env(safe-area-inset-${edge})`;
32
27
 
33
28
  export interface UseSafeAreaReturn {
34
29
  safeAreaInsetTop: string;
30
+ safeAreaInsetRight: string;
35
31
  safeAreaInsetBottom: string;
32
+ safeAreaInsetLeft: string;
36
33
  }
37
34
 
38
35
  /**
39
- * Lynx 앱에서 top/bottom safe area inset 값을 반환합니다.
36
+ * Lynx 앱에서 top/right/bottom/left safe area inset 값을 반환합니다.
40
37
  *
41
- * `lynx.__globalProps.safeAreaInsetTop`과 `safeAreaInsetBottom`을 우선 사용하고,
42
- * host가 값을 제공하지 않으면 Lynx CSS `env(safe-area-inset-*)` 값을 fallback으로
43
- * 반환합니다.
38
+ * `lynx.__globalProps.safeAreaInset*` 값을 우선 사용하고, host가 값을 제공하지 않으면
39
+ * Lynx CSS `env(safe-area-inset-*)` 값을 fallback으로 반환합니다. top/bottom은 host가
40
+ * `0`을 제공해도 fallback합니다.
44
41
  */
45
42
  export function useSafeArea(): UseSafeAreaReturn {
46
- const safeAreaInsetTop = lynx.__globalProps?.[safeAreaProp.top];
47
- const safeAreaInsetBottom = lynx.__globalProps?.[safeAreaProp.bottom];
43
+ const globalProps = lynx.__globalProps;
44
+ const safeAreaInsetTop = globalProps?.safeAreaInsetTop;
45
+ const safeAreaInsetRight = globalProps?.safeAreaInsetRight;
46
+ const safeAreaInsetBottom = globalProps?.safeAreaInsetBottom;
47
+ const safeAreaInsetLeft = globalProps?.safeAreaInsetLeft;
48
48
 
49
49
  return useMemo(
50
50
  () => ({
51
51
  safeAreaInsetTop: resolveSafeAreaInset("top", safeAreaInsetTop),
52
+ safeAreaInsetRight: resolveSafeAreaInset("right", safeAreaInsetRight),
52
53
  safeAreaInsetBottom: resolveSafeAreaInset("bottom", safeAreaInsetBottom),
54
+ safeAreaInsetLeft: resolveSafeAreaInset("left", safeAreaInsetLeft),
53
55
  }),
54
- [safeAreaInsetTop, safeAreaInsetBottom],
56
+ [safeAreaInsetTop, safeAreaInsetRight, safeAreaInsetBottom, safeAreaInsetLeft],
55
57
  );
56
58
  }
package/src/types.ts CHANGED
@@ -41,6 +41,7 @@ export interface LynxIconElementProps {
41
41
  className?: IntrinsicElements["image"]["className"];
42
42
  style?: LynxStyle;
43
43
  ref?: LynxMainThreadRef;
44
+ "disable-default-resize"?: boolean;
44
45
  "main-thread:binduiappear"?: IntrinsicElements["image"]["main-thread:binduiappear"];
45
46
  }
46
47
 
@@ -76,6 +76,12 @@ export function handlePaddingWithSafeArea(
76
76
  return handleDimension(padding);
77
77
  }
78
78
 
79
+ function handleBleed(bleed: BleedValue | undefined, safeAreaInset: string) {
80
+ if (bleed == null) return undefined;
81
+
82
+ return `calc(${bleed === "safeArea" ? safeAreaInset : bleed === 0 ? "0px" : bleed} * -1)`;
83
+ }
84
+
79
85
  export function handleRadius(radius: string | 0 | undefined) {
80
86
  if (radius == null) {
81
87
  return undefined;
@@ -206,8 +212,14 @@ export interface StyleProps {
206
212
  | "safeArea"
207
213
  | (string & {});
208
214
  pt?: Dimension | `spacingX.${SpacingX}` | `spacingY.${SpacingY}` | 0 | "safeArea" | (string & {});
209
- paddingRight?: Dimension | `spacingX.${SpacingX}` | `spacingY.${SpacingY}` | 0 | (string & {});
210
- pr?: Dimension | `spacingX.${SpacingX}` | `spacingY.${SpacingY}` | 0 | (string & {});
215
+ paddingRight?:
216
+ | Dimension
217
+ | `spacingX.${SpacingX}`
218
+ | `spacingY.${SpacingY}`
219
+ | 0
220
+ | "safeArea"
221
+ | (string & {});
222
+ pr?: Dimension | `spacingX.${SpacingX}` | `spacingY.${SpacingY}` | 0 | "safeArea" | (string & {});
211
223
  paddingBottom?:
212
224
  | Dimension
213
225
  | `spacingX.${SpacingX}`
@@ -216,8 +228,14 @@ export interface StyleProps {
216
228
  | "safeArea"
217
229
  | (string & {});
218
230
  pb?: Dimension | `spacingX.${SpacingX}` | `spacingY.${SpacingY}` | 0 | "safeArea" | (string & {});
219
- paddingLeft?: Dimension | `spacingX.${SpacingX}` | `spacingY.${SpacingY}` | 0 | (string & {});
220
- pl?: Dimension | `spacingX.${SpacingX}` | `spacingY.${SpacingY}` | 0 | (string & {});
231
+ paddingLeft?:
232
+ | Dimension
233
+ | `spacingX.${SpacingX}`
234
+ | `spacingY.${SpacingY}`
235
+ | 0
236
+ | "safeArea"
237
+ | (string & {});
238
+ pl?: Dimension | `spacingX.${SpacingX}` | `spacingY.${SpacingY}` | 0 | "safeArea" | (string & {});
221
239
  display?: "flex" | "none" | (string & {});
222
240
  position?: "relative" | "absolute" | "fixed" | (string & {});
223
241
  overflowX?: "visible" | "hidden" | (string & {});
@@ -241,10 +259,61 @@ export interface StyleProps {
241
259
  gap?: Dimension | `spacingX.${SpacingX}` | `spacingY.${SpacingY}` | 0 | (string & {});
242
260
  }
243
261
 
244
- interface UseStyleProps extends StyleProps {
245
- style?: CSSProperties;
262
+ type MarginValue =
263
+ | Dimension
264
+ | `spacingX.${SpacingX}`
265
+ | `spacingY.${SpacingY}`
266
+ | 0
267
+ | "auto"
268
+ | (string & {});
269
+
270
+ /**
271
+ * Dimension tokens are left out: every token resolves to `var()`, and Lynx drops an inline
272
+ * `calc()` that contains `var()`, so a negated token would silently apply no margin.
273
+ */
274
+ type BleedValue = 0 | `${number}px` | "safeArea";
275
+
276
+ interface MarginStyleProps {
277
+ margin?: MarginValue;
278
+ m?: MarginValue;
279
+ marginX?: MarginValue;
280
+ mx?: MarginValue;
281
+ marginY?: MarginValue;
282
+ my?: MarginValue;
283
+ marginTop?: MarginValue;
284
+ mt?: MarginValue;
285
+ marginRight?: MarginValue;
286
+ mr?: MarginValue;
287
+ marginBottom?: MarginValue;
288
+ mb?: MarginValue;
289
+ marginLeft?: MarginValue;
290
+ ml?: MarginValue;
246
291
  }
247
292
 
293
+ interface BleedStyleProps {
294
+ bleed?: BleedValue;
295
+ bleedX?: BleedValue;
296
+ bleedY?: BleedValue;
297
+ bleedTop?: BleedValue;
298
+ bleedRight?: BleedValue;
299
+ bleedBottom?: BleedValue;
300
+ bleedLeft?: BleedValue;
301
+ }
302
+
303
+ /**
304
+ * Margin and bleed props both resolve to `margin-*` values, so they are mutually exclusive at
305
+ * the type level.
306
+ */
307
+ export type MarginBleedStyleProps =
308
+ | (BleedStyleProps & { [K in keyof MarginStyleProps]?: never })
309
+ | (MarginStyleProps & { [K in keyof BleedStyleProps]?: never });
310
+
311
+ type UseStyleProps = StyleProps &
312
+ MarginStyleProps &
313
+ BleedStyleProps & {
314
+ style?: CSSProperties;
315
+ };
316
+
248
317
  export function useStyleProps<T extends UseStyleProps>(
249
318
  props: T,
250
319
  ): {
@@ -286,6 +355,27 @@ export function useStyleProps<T extends UseStyleProps>(
286
355
  pr,
287
356
  pb,
288
357
  pl,
358
+ margin,
359
+ m,
360
+ marginX,
361
+ mx,
362
+ marginY,
363
+ my,
364
+ marginTop,
365
+ mt,
366
+ marginRight,
367
+ mr,
368
+ marginBottom,
369
+ mb,
370
+ marginLeft,
371
+ ml,
372
+ bleed,
373
+ bleedX,
374
+ bleedY,
375
+ bleedTop,
376
+ bleedRight,
377
+ bleedBottom,
378
+ bleedLeft,
289
379
  bottom,
290
380
  left,
291
381
  right,
@@ -308,7 +398,8 @@ export function useStyleProps<T extends UseStyleProps>(
308
398
  style,
309
399
  ...restProps
310
400
  } = props;
311
- const { safeAreaInsetTop, safeAreaInsetBottom } = useSafeArea();
401
+ const { safeAreaInsetTop, safeAreaInsetRight, safeAreaInsetBottom, safeAreaInsetLeft } =
402
+ useSafeArea();
312
403
 
313
404
  const backgroundValue = handleColor(background ?? bg);
314
405
  const paddingValue = handleDimension(padding ?? p);
@@ -316,10 +407,33 @@ export function useStyleProps<T extends UseStyleProps>(
316
407
  const paddingYValue = handleDimension(paddingY ?? py) ?? paddingValue;
317
408
  const paddingTopValue =
318
409
  handlePaddingWithSafeArea(paddingTop ?? pt, safeAreaInsetTop) ?? paddingYValue;
319
- const paddingRightValue = handleDimension(paddingRight ?? pr) ?? paddingXValue;
410
+ const paddingRightValue =
411
+ handlePaddingWithSafeArea(paddingRight ?? pr, safeAreaInsetRight) ?? paddingXValue;
320
412
  const paddingBottomValue =
321
413
  handlePaddingWithSafeArea(paddingBottom ?? pb, safeAreaInsetBottom) ?? paddingYValue;
322
- const paddingLeftValue = handleDimension(paddingLeft ?? pl) ?? paddingXValue;
414
+ const paddingLeftValue =
415
+ handlePaddingWithSafeArea(paddingLeft ?? pl, safeAreaInsetLeft) ?? paddingXValue;
416
+
417
+ const marginValue = handleDimension(margin ?? m);
418
+ const marginXValue = handleDimension(marginX ?? mx) ?? marginValue;
419
+ const marginYValue = handleDimension(marginY ?? my) ?? marginValue;
420
+ const marginTopValue =
421
+ handleDimension(marginTop ?? mt) ??
422
+ marginYValue ??
423
+ handleBleed(bleedTop ?? bleedY ?? bleed, safeAreaInsetTop);
424
+ const marginRightValue =
425
+ handleDimension(marginRight ?? mr) ??
426
+ marginXValue ??
427
+ handleBleed(bleedRight ?? bleedX ?? bleed, safeAreaInsetRight);
428
+ const marginBottomValue =
429
+ handleDimension(marginBottom ?? mb) ??
430
+ marginYValue ??
431
+ handleBleed(bleedBottom ?? bleedY ?? bleed, safeAreaInsetBottom);
432
+ const marginLeftValue =
433
+ handleDimension(marginLeft ?? ml) ??
434
+ marginXValue ??
435
+ handleBleed(bleedLeft ?? bleedX ?? bleed, safeAreaInsetLeft);
436
+
323
437
  const hasBorderStyle =
324
438
  borderColor != null ||
325
439
  borderWidth != null ||
@@ -354,6 +468,10 @@ export function useStyleProps<T extends UseStyleProps>(
354
468
  paddingRight: paddingRightValue,
355
469
  paddingBottom: paddingBottomValue,
356
470
  paddingLeft: paddingLeftValue,
471
+ marginTop: marginTopValue,
472
+ marginRight: marginRightValue,
473
+ marginBottom: marginBottomValue,
474
+ marginLeft: marginLeftValue,
357
475
  top: handleDimension(top),
358
476
  left: handleDimension(left),
359
477
  right: handleDimension(right),