@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
@@ -0,0 +1,232 @@
1
+ import {
2
+ contextualFloatingButton,
3
+ type ContextualFloatingButtonVariantProps,
4
+ } from "@seed-design/lynx-css/recipes/contextual-floating-button";
5
+ import clsx from "clsx";
6
+ import * as React from "@lynx-js/react";
7
+
8
+ import { usePressTap } from "../../hooks/usePressTap";
9
+ import { useScaleFeedback } from "../../hooks/useScaleFeedback";
10
+ import type {
11
+ LynxAccessibilityProps,
12
+ LynxElementProps,
13
+ LynxPressableProps,
14
+ LynxStyledElementProps,
15
+ LynxTouchProps,
16
+ LynxViewRef,
17
+ } from "../../types";
18
+ import { toArray } from "../../utils/children";
19
+ import { createSlotRecipeContext } from "../../utils/create-slot-recipe-context";
20
+ import { mergeProps } from "../../utils/merge-props";
21
+ import { IconRequired, IconSlotProvider, getIconSlotName } from "../Icon/Icon";
22
+ import { ProgressCircleRange, ProgressCircleRoot } from "../ProgressCircle";
23
+
24
+ const { ClassNamesProvider, useClassNames } = createSlotRecipeContext(contextualFloatingButton);
25
+
26
+ type ContextualFloatingButtonPublicVariantProps = Omit<
27
+ ContextualFloatingButtonVariantProps,
28
+ "pressed"
29
+ >;
30
+
31
+ interface ContextualFloatingButtonRootProps
32
+ extends ContextualFloatingButtonVariantProps,
33
+ Omit<LynxStyledElementProps, "flatten">,
34
+ LynxTouchProps,
35
+ LynxAccessibilityProps {
36
+ flatten?: false;
37
+ }
38
+
39
+ const ContextualFloatingButtonRoot = React.forwardRef<unknown, ContextualFloatingButtonRootProps>(
40
+ (innerProps, ref) => {
41
+ const [variantProps, otherProps] = contextualFloatingButton.splitVariantProps(innerProps);
42
+ const classNames = contextualFloatingButton(variantProps);
43
+ const { children, className, ...nativeProps } = otherProps;
44
+ const iconSlotContextValue = React.useMemo(
45
+ () => ({
46
+ classNames: {
47
+ icon: classNames.icon,
48
+ prefixIcon: classNames.prefixIcon,
49
+ },
50
+ deps: [
51
+ variantProps.variant ?? null,
52
+ variantProps.layout ?? null,
53
+ variantProps.pressed ?? false,
54
+ variantProps.disabled ?? false,
55
+ variantProps.loading ?? false,
56
+ ],
57
+ }),
58
+ [
59
+ classNames.icon,
60
+ classNames.prefixIcon,
61
+ variantProps.variant,
62
+ variantProps.layout,
63
+ variantProps.pressed,
64
+ variantProps.disabled,
65
+ variantProps.loading,
66
+ ],
67
+ );
68
+
69
+ return (
70
+ <ClassNamesProvider value={classNames}>
71
+ <IconSlotProvider value={iconSlotContextValue}>
72
+ <view
73
+ {...mergeProps(ref ? { ref: ref as LynxViewRef } : {}, nativeProps)}
74
+ className={clsx(classNames.root, className)}
75
+ >
76
+ {children}
77
+ </view>
78
+ </IconSlotProvider>
79
+ </ClassNamesProvider>
80
+ );
81
+ },
82
+ );
83
+ ContextualFloatingButtonRoot.displayName = "ContextualFloatingButtonRoot";
84
+
85
+ function ContextualFloatingButtonText({ children }: LynxElementProps) {
86
+ const classNames = useClassNames();
87
+
88
+ return <text className={classNames.text}>{children}</text>;
89
+ }
90
+
91
+ function ContextualFloatingButtonContent({
92
+ children,
93
+ isIconOnly,
94
+ }: {
95
+ children: React.ReactNode;
96
+ isIconOnly: boolean;
97
+ }) {
98
+ const classNames = useClassNames();
99
+ const prefixIconChildren: React.ReactNode[] = [];
100
+ const iconChildren: React.ReactNode[] = [];
101
+ const textChildren: React.ReactNode[] = [];
102
+
103
+ for (const child of toArray(children)) {
104
+ const slotName = getIconSlotName(child);
105
+
106
+ if (slotName === "prefixIcon") {
107
+ prefixIconChildren.push(child);
108
+ continue;
109
+ }
110
+ if (slotName === "icon") {
111
+ iconChildren.push(child);
112
+ continue;
113
+ }
114
+
115
+ textChildren.push(child);
116
+ }
117
+
118
+ return (
119
+ <view className={classNames.content}>
120
+ {isIconOnly ? (
121
+ <>{iconChildren}</>
122
+ ) : (
123
+ <>
124
+ {prefixIconChildren}
125
+ {textChildren.length > 0 ? (
126
+ <ContextualFloatingButtonText>{textChildren}</ContextualFloatingButtonText>
127
+ ) : null}
128
+ </>
129
+ )}
130
+ </view>
131
+ );
132
+ }
133
+
134
+ function ContextualFloatingButtonLoadingIndicator() {
135
+ const classNames = useClassNames();
136
+
137
+ return (
138
+ <view className={classNames.loadingIndicator}>
139
+ <ProgressCircleRoot size="16" tone="inherit">
140
+ <ProgressCircleRange />
141
+ </ProgressCircleRoot>
142
+ </view>
143
+ );
144
+ }
145
+
146
+ /**
147
+ * @platform Lynx
148
+ *
149
+ * Web version differences:
150
+ * - Native `<view>` and `bindtap` / `main-thread:bindtap` replace HTML button events,
151
+ * form props, and `asChild`.
152
+ * - `loading` blocks both native tap callbacks, unlike the React reference which only
153
+ * exposes pending state to its underlying button.
154
+ */
155
+ export interface ContextualFloatingButtonProps
156
+ extends ContextualFloatingButtonPublicVariantProps,
157
+ Omit<LynxStyledElementProps, "flatten">,
158
+ LynxPressableProps,
159
+ LynxAccessibilityProps {}
160
+
161
+ export const ContextualFloatingButton = React.forwardRef<unknown, ContextualFloatingButtonProps>(
162
+ (props, ref) => {
163
+ const [variantProps, otherProps] = contextualFloatingButton.splitVariantProps(props);
164
+ const {
165
+ children,
166
+ bindtap,
167
+ "main-thread:bindtap": mainThreadBindtap,
168
+ "accessibility-element": accessibilityElement = true,
169
+ "accessibility-label": accessibilityLabel,
170
+ "accessibility-role-description": accessibilityRoleDescription = "button",
171
+ "accessibility-traits": accessibilityTraits,
172
+ ...nativeProps
173
+ } = otherProps;
174
+ const layout = variantProps.layout ?? "withText";
175
+ const disabled = variantProps.disabled ?? false;
176
+ const loading = variantProps.loading ?? false;
177
+ const isInteractive = !disabled && !loading;
178
+
179
+ if (
180
+ process.env.NODE_ENV !== "production" &&
181
+ layout === "iconOnly" &&
182
+ accessibilityElement &&
183
+ !accessibilityLabel
184
+ ) {
185
+ console.warn(
186
+ 'ContextualFloatingButton: `layout="iconOnly"` requires `accessibility-label` for accessibility.',
187
+ );
188
+ }
189
+
190
+ const { pressed, bindtouchstart, bindtouchend, bindtouchcancel, ...pressTapHandlers } =
191
+ usePressTap({
192
+ disabled: !isInteractive,
193
+ onTap: bindtap,
194
+ mainThreadOnTap: mainThreadBindtap,
195
+ });
196
+ const { scaleFeedbackTriggerProps, scaleFeedbackTargetProps } = useScaleFeedback({
197
+ disabled: !isInteractive,
198
+ onTouchStart: bindtouchstart,
199
+ onTouchEnd: bindtouchend,
200
+ onTouchCancel: bindtouchcancel,
201
+ });
202
+
203
+ return (
204
+ <IconRequired enabled={layout === "iconOnly"}>
205
+ <ContextualFloatingButtonRoot
206
+ {...mergeProps(
207
+ { ref },
208
+ scaleFeedbackTargetProps,
209
+ scaleFeedbackTriggerProps,
210
+ pressTapHandlers,
211
+ nativeProps,
212
+ )}
213
+ {...variantProps}
214
+ pressed={pressed}
215
+ accessibility-element={accessibilityElement}
216
+ accessibility-label={accessibilityLabel}
217
+ accessibility-role-description={accessibilityRoleDescription}
218
+ accessibility-traits={
219
+ accessibilityTraits ?? (disabled || loading ? "disabled" : undefined)
220
+ }
221
+ flatten={false}
222
+ >
223
+ {loading ? <ContextualFloatingButtonLoadingIndicator /> : null}
224
+ <ContextualFloatingButtonContent isIconOnly={layout === "iconOnly"}>
225
+ {children}
226
+ </ContextualFloatingButtonContent>
227
+ </ContextualFloatingButtonRoot>
228
+ </IconRequired>
229
+ );
230
+ },
231
+ );
232
+ ContextualFloatingButton.displayName = "ContextualFloatingButton";
@@ -0,0 +1,4 @@
1
+ export {
2
+ ContextualFloatingButton,
3
+ type ContextualFloatingButtonProps,
4
+ } from "./ContextualFloatingButton";
@@ -0,0 +1,24 @@
1
+ export {
2
+ DialogAction as Action,
3
+ DialogBackdrop as Backdrop,
4
+ DialogBody as Body,
5
+ DialogContent as Content,
6
+ DialogDescription as Description,
7
+ DialogFooter as Footer,
8
+ DialogHeader as Header,
9
+ DialogPositioner as Positioner,
10
+ DialogRoot as Root,
11
+ DialogTitle as Title,
12
+ DialogTrigger as Trigger,
13
+ type DialogActionProps as ActionProps,
14
+ type DialogBackdropProps as BackdropProps,
15
+ type DialogBodyProps as BodyProps,
16
+ type DialogContentProps as ContentProps,
17
+ type DialogDescriptionProps as DescriptionProps,
18
+ type DialogFooterProps as FooterProps,
19
+ type DialogHeaderProps as HeaderProps,
20
+ type DialogPositionerProps as PositionerProps,
21
+ type DialogRootProps as RootProps,
22
+ type DialogTitleProps as TitleProps,
23
+ type DialogTriggerProps as TriggerProps,
24
+ } from "./Dialog";
@@ -0,0 +1,172 @@
1
+ import type { ReactNode } from "@lynx-js/react";
2
+
3
+ import { render } from "@lynx-js/react/testing-library";
4
+ import { beforeEach, describe, expect, it, vi } from "vitest";
5
+
6
+ const dialogMocks = vi.hoisted(() => ({
7
+ rootProps: [] as Array<Record<string, unknown>>,
8
+ viewProps: [] as Array<Record<string, unknown>>,
9
+ backdropProps: [] as Array<Record<string, unknown>>,
10
+ contentProps: [] as Array<Record<string, unknown>>,
11
+ closeProps: [] as Array<Record<string, unknown>>,
12
+ }));
13
+
14
+ vi.mock("@lynx-js/lynx-ui-dialog", () => {
15
+ const renderChildren = (children: unknown, status: Record<string, boolean>): ReactNode => {
16
+ if (typeof children === "function") {
17
+ return (children as (status: Record<string, boolean>) => ReactNode)(status);
18
+ }
19
+ return children as ReactNode;
20
+ };
21
+
22
+ const DialogRoot = (props: Record<string, unknown>) => {
23
+ dialogMocks.rootProps.push(props);
24
+ return <>{renderChildren(props["children"], { open: props["show"] === true })}</>;
25
+ };
26
+ DialogRoot.displayName = "MockDialogRoot";
27
+
28
+ const DialogTrigger = (props: Record<string, unknown>) => (
29
+ <view>{renderChildren(props["children"], { active: false, busy: false })}</view>
30
+ );
31
+ DialogTrigger.displayName = "MockDialogTrigger";
32
+
33
+ const DialogView = (props: Record<string, unknown>) => {
34
+ dialogMocks.viewProps.push(props);
35
+ return (
36
+ <view className={props["className"] as string}>
37
+ {renderChildren(props["children"], { open: props["show"] === true })}
38
+ </view>
39
+ );
40
+ };
41
+ DialogView.displayName = "MockDialogView";
42
+
43
+ const DialogBackdrop = (props: Record<string, unknown>) => {
44
+ dialogMocks.backdropProps.push(props);
45
+ return <view className={props["className"] as string}>{props["children"] as ReactNode}</view>;
46
+ };
47
+ DialogBackdrop.displayName = "MockDialogBackdrop";
48
+
49
+ const DialogContent = (props: Record<string, unknown>) => {
50
+ dialogMocks.contentProps.push(props);
51
+ return <view className={props["className"] as string}>{props["children"] as ReactNode}</view>;
52
+ };
53
+ DialogContent.displayName = "MockDialogContent";
54
+
55
+ const DialogClose = (props: Record<string, unknown>) => {
56
+ dialogMocks.closeProps.push(props);
57
+ return <view>{renderChildren(props["children"], { active: false, busy: false })}</view>;
58
+ };
59
+ DialogClose.displayName = "MockDialogClose";
60
+
61
+ return {
62
+ DialogBackdrop,
63
+ DialogClose,
64
+ DialogContent,
65
+ DialogRoot,
66
+ DialogTrigger,
67
+ DialogView,
68
+ };
69
+ });
70
+
71
+ import * as Dialog from "./Dialog.namespace";
72
+
73
+ describe("Dialog", () => {
74
+ beforeEach(() => {
75
+ dialogMocks.rootProps = [];
76
+ dialogMocks.viewProps = [];
77
+ dialogMocks.backdropProps = [];
78
+ dialogMocks.contentProps = [];
79
+ dialogMocks.closeProps = [];
80
+ });
81
+
82
+ it("maps the namespace API to the Lynx UI headless primitives", () => {
83
+ const onOpenChange = vi.fn();
84
+ const { container } = render(
85
+ <Dialog.Root open onOpenChange={onOpenChange}>
86
+ <Dialog.Trigger>
87
+ <text>Open</text>
88
+ </Dialog.Trigger>
89
+ <Dialog.Positioner>
90
+ <Dialog.Backdrop />
91
+ <Dialog.Content>
92
+ <Dialog.Header>
93
+ <Dialog.Title>Title</Dialog.Title>
94
+ <Dialog.Description>Description</Dialog.Description>
95
+ </Dialog.Header>
96
+ <Dialog.Body
97
+ className="custom-body"
98
+ maxHeight="96px"
99
+ paddingLeft="16px"
100
+ style={{ maxHeight: "120px" }}
101
+ >
102
+ <text>Scrollable content</text>
103
+ </Dialog.Body>
104
+ <Dialog.Footer>
105
+ <Dialog.Action>
106
+ <text>Close</text>
107
+ </Dialog.Action>
108
+ </Dialog.Footer>
109
+ </Dialog.Content>
110
+ </Dialog.Positioner>
111
+ </Dialog.Root>,
112
+ );
113
+
114
+ expect(dialogMocks.rootProps.at(-1)).toMatchObject({
115
+ show: true,
116
+ onShowChange: onOpenChange,
117
+ });
118
+ expect(container.querySelector(".seed-dialog__positioner")).not.toBeNull();
119
+ expect(container.querySelector(".seed-dialog__backdrop")).not.toBeNull();
120
+ expect(container.querySelector(".seed-dialog__content")).not.toBeNull();
121
+ expect(container.querySelector(".seed-dialog__header")).not.toBeNull();
122
+ expect(container.querySelector(".seed-dialog__title")).not.toBeNull();
123
+ expect(container.querySelector(".seed-dialog__description")).not.toBeNull();
124
+ expect(container.querySelector(".seed-dialog__footer")).not.toBeNull();
125
+
126
+ const body = container.querySelector<HTMLElement>("scroll-view");
127
+
128
+ expect(body?.classList.contains("seed-dialog__body")).toBe(true);
129
+ expect(body?.classList.contains("custom-body")).toBe(true);
130
+ expect(body?.hasAttribute("scroll-y")).toBe(true);
131
+ expect(body?.style.maxHeight).toBe("120px");
132
+ expect(body?.style.paddingLeft).toBe("16px");
133
+ });
134
+
135
+ it("forwards headless mount options and removes only reserved lifecycle handlers", () => {
136
+ const userBindTap = vi.fn();
137
+ const dialogContentProps = {
138
+ style: { paddingTop: "12px" },
139
+ bindtap: userBindTap,
140
+ };
141
+
142
+ render(
143
+ <Dialog.Root defaultOpen forceMount skipAnimation>
144
+ <Dialog.Positioner
145
+ container="window"
146
+ overlayLevel={2}
147
+ dialogViewProps={{ style: { top: "8px" } }}
148
+ >
149
+ <Dialog.Backdrop />
150
+ <Dialog.Content dialogContentProps={dialogContentProps} />
151
+ </Dialog.Positioner>
152
+ </Dialog.Root>,
153
+ );
154
+
155
+ expect(dialogMocks.rootProps.at(-1)).toMatchObject({
156
+ defaultShow: true,
157
+ forceMount: true,
158
+ });
159
+ expect(dialogMocks.viewProps.at(-1)).toMatchObject({
160
+ container: "window",
161
+ overlayLevel: 2,
162
+ transition: false,
163
+ dialogViewProps: { style: { top: "8px" } },
164
+ });
165
+ expect(dialogMocks.contentProps.at(-1)).toMatchObject({
166
+ transition: false,
167
+ dialogContentProps: { style: { paddingTop: "12px" } },
168
+ });
169
+ expect(dialogMocks.contentProps.at(-1)?.["dialogContentProps"]).not.toHaveProperty("bindtap");
170
+ expect(userBindTap).not.toHaveBeenCalled();
171
+ });
172
+ });