@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,897 @@
1
+ import { getRectByRef } from "@lynx-js/lynx-ui-common";
2
+ import type { NodesRef } from "@lynx-js/types";
3
+ import * as React from "@lynx-js/react";
4
+ import clsx from "clsx";
5
+
6
+ import { useControllableState } from "../../hooks/useControllableState";
7
+ import type {
8
+ LynxAccessibilityProps,
9
+ LynxStyledElementProps,
10
+ LynxTextProps,
11
+ LynxViewProps,
12
+ } from "../../types";
13
+ import { mergeProps } from "../../utils/merge-props";
14
+ import { slider } from "@seed-design/lynx-css/recipes/slider";
15
+ import type { SliderVariantProps } from "@seed-design/lynx-css/recipes/slider";
16
+ import { sliderTick, type SliderTickVariantProps } from "@seed-design/lynx-css/recipes/slider-tick";
17
+ import {
18
+ sliderMarker,
19
+ type SliderMarkerVariantProps,
20
+ } from "@seed-design/lynx-css/recipes/slider-marker";
21
+ import {
22
+ eventPageX,
23
+ getStickyLabelOffset,
24
+ getThumbInBoundsOffset,
25
+ geometryFromRect,
26
+ hasMinimumSteps,
27
+ nearestValueIndex,
28
+ normalizeValue,
29
+ normalizeValues,
30
+ percentageForValue,
31
+ valueForPercentage,
32
+ } from "./Slider.utils";
33
+
34
+ export type SliderValues = number[];
35
+ type SliderNativeProps = Omit<LynxViewProps, "children" | "className" | "style"> &
36
+ LynxStyledElementProps;
37
+ type LayoutChangeHandler = NonNullable<LynxViewProps["bindlayoutchange"]>;
38
+
39
+ function getLayoutWidth(event: Parameters<LayoutChangeHandler>[0]): number | null {
40
+ const eventWithWidth = event as Parameters<LayoutChangeHandler>[0] & { width?: number };
41
+ const nextWidth = event.detail?.width ?? event.params?.width ?? eventWithWidth.width;
42
+ if (typeof nextWidth !== "number" || !Number.isFinite(nextWidth)) return null;
43
+ return Math.max(0, nextWidth);
44
+ }
45
+
46
+ /**
47
+ * @platform Lynx
48
+ *
49
+ * 웹 전용 HiddenInput/name, DOM form 이벤트, keyboard/focus/hover 상태는 제공하지 않습니다.
50
+ * 값 변경은 native touch gesture로 처리하며 Thumb는 `accessibility-*` 속성을 직접 노출합니다.
51
+ */
52
+ export interface SliderRootProps extends SliderNativeProps, LynxAccessibilityProps {
53
+ children?: React.ReactNode;
54
+ values?: number[];
55
+ defaultValues?: number[];
56
+ min?: number;
57
+ max?: number;
58
+ step?: number;
59
+ allowedValues?: number[];
60
+ minStepsBetweenThumbs?: number;
61
+ dir?: "ltr" | "rtl";
62
+ disabled?: boolean;
63
+ readOnly?: boolean;
64
+ invalid?: boolean;
65
+ onValuesChange?: (values: number[]) => void;
66
+ onValuesCommit?: (values: number[]) => void;
67
+ getAccessibilityLabel?: (thumbIndex: number) => string;
68
+ getAccessibilityValueText?: (value: number, thumbIndex: number) => string;
69
+ getValueIndicatorLabel?: (params: { value: number; thumbIndex: number }) => React.ReactNode;
70
+ valueIndicatorTrigger?: "auto" | "active";
71
+ }
72
+
73
+ export interface SliderControlProps extends SliderNativeProps {
74
+ children?: React.ReactNode;
75
+ }
76
+ export interface SliderTrackProps extends SliderNativeProps {
77
+ children?: React.ReactNode;
78
+ }
79
+ export interface SliderRangeProps extends SliderNativeProps {
80
+ children?: React.ReactNode;
81
+ }
82
+ export interface SliderThumbProps extends SliderNativeProps, LynxAccessibilityProps {
83
+ index?: number;
84
+ children?: React.ReactNode;
85
+ }
86
+ export interface SliderTickProps extends SliderNativeProps, SliderTickVariantProps {
87
+ value: number;
88
+ children?: React.ReactNode;
89
+ }
90
+ export interface SliderMarkersProps extends SliderNativeProps {
91
+ children?: React.ReactNode;
92
+ }
93
+ export interface SliderMarkerProps
94
+ extends SliderNativeProps,
95
+ Omit<SliderMarkerVariantProps, "dir" | "disabled"> {
96
+ value: number;
97
+ children?: React.ReactNode;
98
+ }
99
+ export interface SliderValueIndicatorRootProps extends SliderNativeProps {
100
+ index?: number;
101
+ children?: React.ReactNode;
102
+ }
103
+ export interface SliderValueIndicatorArrowProps extends SliderNativeProps {
104
+ children?: React.ReactNode;
105
+ }
106
+ export interface SliderValueIndicatorArrowTipProps extends SliderNativeProps {
107
+ children?: React.ReactNode;
108
+ }
109
+ export interface SliderValueIndicatorLabelProps
110
+ extends Omit<LynxTextProps, "children" | "className" | "style">,
111
+ LynxStyledElementProps {
112
+ index?: number;
113
+ children?: React.ReactNode;
114
+ }
115
+
116
+ export interface SliderClassNames {
117
+ root: string;
118
+ control: string;
119
+ track: string;
120
+ range: string;
121
+ thumb: string;
122
+ markers: string;
123
+ valueIndicatorMotion: string;
124
+ valueIndicatorRoot: string;
125
+ valueIndicatorArrow: string;
126
+ valueIndicatorArrowTip: string;
127
+ valueIndicatorLabel: string;
128
+ }
129
+
130
+ interface SliderDimensions {
131
+ trackLeft: number;
132
+ trackWidth: number;
133
+ thumbWidth: number;
134
+ indicatorWidths: Record<number, number>;
135
+ }
136
+
137
+ interface SliderFormatters {
138
+ getAccessibilityLabel?: (index: number) => string;
139
+ getAccessibilityValueText?: (value: number, index: number) => string;
140
+ getValueIndicatorLabel: (params: { value: number; thumbIndex: number }) => React.ReactNode;
141
+ }
142
+
143
+ interface SliderInteraction {
144
+ active: boolean;
145
+ index: number | null;
146
+ changed: boolean;
147
+ pendingX: number | null;
148
+ }
149
+ interface SliderContextValue {
150
+ values: SliderValues;
151
+ min: number;
152
+ max: number;
153
+ step: number;
154
+ valueIndicatorTrigger: "active";
155
+ allowedValues?: number[];
156
+ minStepsBetweenThumbs: number;
157
+ dir: "ltr" | "rtl";
158
+ disabled: boolean;
159
+ readOnly: boolean;
160
+ invalid: boolean;
161
+ dragging: boolean;
162
+ activeThumbIndex: number | null;
163
+ valueIndicatorEverShown: boolean;
164
+ classes: SliderClassNames;
165
+ getClasses: (state?: SliderVariantProps) => SliderClassNames;
166
+ trackRef: React.RefObject<NodesRef | null>;
167
+ dimensions: SliderDimensions;
168
+ registerThumb: (index: number, ref: React.RefObject<NodesRef | null>) => void;
169
+ updateIndicatorWidth: (index: number, width: number) => void;
170
+ registerIndicator: (index: number, ref: React.RefObject<NodesRef | null>) => void;
171
+ startThumb: (index: number) => void;
172
+ formatters: SliderFormatters;
173
+ interaction: SliderInteraction;
174
+ }
175
+
176
+ const SliderContext = React.createContext<SliderContextValue | null>(null);
177
+ function useSliderContext(consumer: string): SliderContextValue {
178
+ const context = React.useContext(SliderContext);
179
+ if (!context) throw new Error(`<${consumer}/> must be rendered inside <Slider.Root/>.`);
180
+ return context;
181
+ }
182
+
183
+ function dynamicStyle(
184
+ style: LynxStyledElementProps["style"],
185
+ values: Record<string, string | number>,
186
+ ) {
187
+ return { ...values, ...(style ?? {}) };
188
+ }
189
+
190
+ function defaultGetValueIndicatorLabel({ value }: { value: number }): React.ReactNode {
191
+ return String(value);
192
+ }
193
+
194
+ export const SliderRoot = React.forwardRef<NodesRef, SliderRootProps>((props, forwardedRef) => {
195
+ const [variantProps, restProps] = slider.splitVariantProps(props);
196
+ const { disabled = false } = variantProps;
197
+ const {
198
+ children,
199
+ className,
200
+ style,
201
+ values: valuesProp,
202
+ defaultValues,
203
+ min = 0,
204
+ max = 100,
205
+ step = 1,
206
+ allowedValues,
207
+ minStepsBetweenThumbs = 0,
208
+ dir = "ltr",
209
+ readOnly = false,
210
+ invalid = false,
211
+ onValuesChange,
212
+ onValuesCommit,
213
+ getAccessibilityLabel,
214
+ getAccessibilityValueText,
215
+ getValueIndicatorLabel = defaultGetValueIndicatorLabel,
216
+ valueIndicatorTrigger = "auto",
217
+ "consume-slide-event": consumeSlideEvent = [
218
+ [-180, -135],
219
+ [-45, 45],
220
+ [135, 180],
221
+ ],
222
+ ...nativeProps
223
+ } = restProps;
224
+ const resolvedValueIndicatorTrigger =
225
+ valueIndicatorTrigger === "auto" ? "active" : valueIndicatorTrigger;
226
+ const getClasses = React.useCallback((state: SliderVariantProps = {}) => slider(state), []);
227
+
228
+ const initialValues = React.useMemo(
229
+ () => normalizeValues(defaultValues ?? valuesProp, min, max, step, allowedValues),
230
+ [defaultValues, valuesProp, min, max, step, allowedValues],
231
+ );
232
+ const [values, setValues] = useControllableState<SliderValues>({
233
+ value:
234
+ valuesProp === undefined
235
+ ? undefined
236
+ : normalizeValues(valuesProp, min, max, step, allowedValues),
237
+ defaultValue: initialValues,
238
+ onChange: onValuesChange,
239
+ });
240
+ const valuesRef = React.useRef(values);
241
+ valuesRef.current = values;
242
+ const interaction = React.useRef<SliderInteraction>({
243
+ active: false,
244
+ index: null,
245
+ changed: false,
246
+ pendingX: null,
247
+ });
248
+ const pendingEndRef = React.useRef(false);
249
+ const moveFrameRef = React.useRef<number | null>(null);
250
+ const [valueIndicatorEverShown, setValueIndicatorEverShown] = React.useState(false);
251
+
252
+ const trackRef = React.useRef<NodesRef>(null);
253
+ const thumbRefs = React.useRef<Record<number, React.RefObject<NodesRef | null>>>({});
254
+ const indicatorRefs = React.useRef<Record<number, React.RefObject<NodesRef | null>>>({});
255
+ const [dragging, setDragging] = React.useState(false);
256
+ const [activeThumbIndex, setActiveThumbIndex] = React.useState<number | null>(null);
257
+ const [trackLeft, setTrackLeft] = React.useState(0);
258
+ const trackLeftRef = React.useRef(0);
259
+ const trackWidthRef = React.useRef(0);
260
+ const [trackWidth, setTrackWidth] = React.useState(0);
261
+ const [thumbWidth, setThumbWidth] = React.useState(0);
262
+ const [indicatorWidths, setIndicatorWidths] = React.useState<Record<number, number>>({});
263
+ const updateIndicatorWidth = React.useCallback((index: number, width: number) => {
264
+ setIndicatorWidths((previous) => {
265
+ if (previous[index] === width) return previous;
266
+ return { ...previous, [index]: width };
267
+ });
268
+ }, []);
269
+ const measureInFlight = React.useRef(false);
270
+ const finalizeRef = React.useRef<(() => void) | undefined>(undefined);
271
+ const updateFromRatioRef = React.useRef<((ratio: number) => void) | undefined>(undefined);
272
+
273
+ const measure = React.useCallback(() => {
274
+ const trackNode = trackRef.current;
275
+ if (measureInFlight.current || !trackNode) return;
276
+ measureInFlight.current = true;
277
+ const thumbNode = thumbRefs.current[0]?.current;
278
+ Promise.all([
279
+ getRectByRef({ current: trackNode }),
280
+ thumbNode ? getRectByRef({ current: thumbNode }) : Promise.resolve(null),
281
+ ])
282
+ .then(([trackRect, thumbRect]) => {
283
+ const track = geometryFromRect(trackRect);
284
+ const thumb = geometryFromRect(thumbRect);
285
+ if (track) {
286
+ trackLeftRef.current = track.left;
287
+ trackWidthRef.current = track.width;
288
+ setTrackLeft(track.left);
289
+ setTrackWidth(track.width);
290
+ }
291
+ if (thumb) setThumbWidth(thumb.width);
292
+ measureInFlight.current = false;
293
+ const pending = interaction.current.pendingX;
294
+ if (pending !== null && track) {
295
+ interaction.current.pendingX = null;
296
+ updateFromRatioRef.current?.(clampRatio((pending - track.left) / track.width));
297
+ }
298
+ if (pendingEndRef.current) finalizeRef.current?.();
299
+ })
300
+ .catch(() => {
301
+ measureInFlight.current = false;
302
+ if (!pendingEndRef.current) return;
303
+ const pending = interaction.current.pendingX;
304
+ if (pending !== null && trackWidthRef.current > 0) {
305
+ interaction.current.pendingX = null;
306
+ updateFromRatioRef.current?.(
307
+ clampRatio((pending - trackLeftRef.current) / trackWidthRef.current),
308
+ );
309
+ }
310
+ finalizeRef.current?.();
311
+ });
312
+ }, []);
313
+
314
+ const registerThumb = React.useCallback(
315
+ (index: number, ref: React.RefObject<NodesRef | null>) => {
316
+ thumbRefs.current[index] = ref;
317
+ if (index === 0) measure();
318
+ },
319
+ [measure],
320
+ );
321
+ const registerIndicator = React.useCallback(
322
+ (index: number, ref: React.RefObject<NodesRef | null>) => {
323
+ indicatorRefs.current[index] = ref;
324
+ const indicatorNode = ref.current;
325
+ if (!indicatorNode) return;
326
+ getRectByRef({ current: indicatorNode })
327
+ .then((rect) => {
328
+ const geometry = geometryFromRect(rect);
329
+ if (geometry) updateIndicatorWidth(index, geometry.width);
330
+ })
331
+ .catch(() => undefined);
332
+ },
333
+ [updateIndicatorWidth],
334
+ );
335
+ const updateFromRatio = React.useCallback(
336
+ (ratio: number) => {
337
+ if (disabled || readOnly || !interaction.current.active) return;
338
+ const current = valuesRef.current;
339
+ const requested = valueForPercentage(ratio * 100, min, max, dir);
340
+ const index = interaction.current.index ?? nearestValueIndex(current, requested);
341
+ const nextValue = normalizeValue(requested, min, max, step, allowedValues);
342
+ const next = [...current];
343
+ next[index] = nextValue;
344
+ next.sort((a, b) => a - b);
345
+ if (!allowedValues?.length && !hasMinimumSteps(next, minStepsBetweenThumbs, step)) return;
346
+ if (next.every((value, valueIndex) => value === current[valueIndex])) return;
347
+ valuesRef.current = next;
348
+ interaction.current.index = next.indexOf(nextValue);
349
+ interaction.current.changed = true;
350
+ setActiveThumbIndex(interaction.current.index);
351
+ setValues(next);
352
+ },
353
+ [allowedValues, dir, disabled, max, min, minStepsBetweenThumbs, readOnly, setValues, step],
354
+ );
355
+ updateFromRatioRef.current = updateFromRatio;
356
+
357
+ const cancelPendingMove = React.useCallback(() => {
358
+ if (moveFrameRef.current === null) return;
359
+ cancelAnimationFrame(moveFrameRef.current);
360
+ moveFrameRef.current = null;
361
+ }, []);
362
+
363
+ const flushPendingMove = React.useCallback(() => {
364
+ "background only";
365
+ moveFrameRef.current = null;
366
+ if (!interaction.current.active || disabled || readOnly) return;
367
+ const pending = interaction.current.pendingX;
368
+ if (pending === null) return;
369
+ const track = trackWidthRef.current;
370
+ if (!track) {
371
+ measure();
372
+ return;
373
+ }
374
+ interaction.current.pendingX = null;
375
+ updateFromRatio(clampRatio((pending - trackLeftRef.current) / track));
376
+ }, [disabled, measure, readOnly, updateFromRatio]);
377
+
378
+ const finalize = React.useCallback(() => {
379
+ if (!interaction.current.active) return;
380
+ interaction.current.active = false;
381
+ interaction.current.pendingX = null;
382
+ pendingEndRef.current = false;
383
+ setDragging(false);
384
+ setActiveThumbIndex(null);
385
+ if (interaction.current.changed && !disabled && !readOnly)
386
+ onValuesCommit?.([...valuesRef.current]);
387
+ interaction.current.index = null;
388
+ interaction.current.changed = false;
389
+ }, [disabled, onValuesCommit, readOnly]);
390
+ finalizeRef.current = finalize;
391
+
392
+ const begin = React.useCallback(
393
+ (event: unknown) => {
394
+ "background only";
395
+ if (disabled || readOnly) {
396
+ interaction.current.index = null;
397
+ return;
398
+ }
399
+ const x = eventPageX(event);
400
+ if (!Number.isFinite(x)) return;
401
+ pendingEndRef.current = false;
402
+ interaction.current.active = true;
403
+ interaction.current.changed = false;
404
+ interaction.current.pendingX = x;
405
+ const current = valuesRef.current;
406
+ if (interaction.current.index === null && trackWidthRef.current > 0) {
407
+ const ratio = clampRatio((x - trackLeftRef.current) / trackWidthRef.current);
408
+ interaction.current.index = nearestValueIndex(
409
+ current,
410
+ valueForPercentage(ratio * 100, min, max, dir),
411
+ );
412
+ }
413
+ setActiveThumbIndex(interaction.current.index);
414
+ setDragging(true);
415
+ setValueIndicatorEverShown(true);
416
+ measure();
417
+ if (trackWidthRef.current > 0) {
418
+ updateFromRatio(clampRatio((x - trackLeftRef.current) / trackWidthRef.current));
419
+ }
420
+ },
421
+ [dir, disabled, max, measure, min, readOnly, updateFromRatio],
422
+ );
423
+
424
+ const move = React.useCallback(
425
+ (event: unknown) => {
426
+ "background only";
427
+ if (!interaction.current.active || disabled || readOnly) return;
428
+ const x = eventPageX(event);
429
+ if (!Number.isFinite(x)) return;
430
+ interaction.current.pendingX = x;
431
+ if (moveFrameRef.current !== null) return;
432
+ moveFrameRef.current = requestAnimationFrame(flushPendingMove);
433
+ },
434
+ [disabled, flushPendingMove, readOnly],
435
+ );
436
+
437
+ const finish = React.useCallback(
438
+ (event: unknown) => {
439
+ "background only";
440
+ if (!interaction.current.active) return;
441
+ cancelPendingMove();
442
+ const x = eventPageX(event);
443
+ if (Number.isFinite(x)) interaction.current.pendingX = x;
444
+ if (interaction.current.pendingX !== null && measureInFlight.current) {
445
+ pendingEndRef.current = true;
446
+ setDragging(false);
447
+ setActiveThumbIndex(null);
448
+ measure();
449
+ return;
450
+ }
451
+ if (interaction.current.pendingX !== null && trackWidthRef.current <= 0) {
452
+ pendingEndRef.current = true;
453
+ setDragging(false);
454
+ setActiveThumbIndex(null);
455
+ measure();
456
+ return;
457
+ }
458
+ if (interaction.current.pendingX !== null && trackWidthRef.current > 0) {
459
+ const pending = interaction.current.pendingX;
460
+ interaction.current.pendingX = null;
461
+ updateFromRatio(clampRatio((pending - trackLeftRef.current) / trackWidthRef.current));
462
+ }
463
+ finalizeRef.current?.();
464
+ },
465
+ [cancelPendingMove, measure, updateFromRatio],
466
+ );
467
+
468
+ const cancel = React.useCallback(() => {
469
+ "background only";
470
+ if (!interaction.current.active) return;
471
+ cancelPendingMove();
472
+ interaction.current.active = false;
473
+ interaction.current.pendingX = null;
474
+ pendingEndRef.current = false;
475
+ setDragging(false);
476
+ setActiveThumbIndex(null);
477
+ interaction.current.index = null;
478
+ interaction.current.changed = false;
479
+ }, [cancelPendingMove]);
480
+
481
+ const rootHandlers = {
482
+ catchtouchstart: begin,
483
+ catchtouchmove: move,
484
+ catchtouchend: finish,
485
+ catchtouchcancel: cancel,
486
+ };
487
+
488
+ React.useEffect(() => cancelPendingMove, [cancelPendingMove]);
489
+
490
+ React.useEffect(() => {
491
+ measure();
492
+ }, [measure, values.length]);
493
+ const classes = React.useMemo(
494
+ () => getClasses({ disabled, dragging, valueIndicatorEverShown }),
495
+ [disabled, dragging, getClasses, valueIndicatorEverShown],
496
+ );
497
+ const dimensions = React.useMemo<SliderDimensions>(
498
+ () => ({ trackLeft, trackWidth, thumbWidth, indicatorWidths }),
499
+ [indicatorWidths, thumbWidth, trackLeft, trackWidth],
500
+ );
501
+ const startThumb = React.useCallback((index: number) => {
502
+ interaction.current.index = index;
503
+ }, []);
504
+ const formatters = React.useMemo<SliderFormatters>(
505
+ () => ({ getAccessibilityLabel, getAccessibilityValueText, getValueIndicatorLabel }),
506
+ [getAccessibilityLabel, getAccessibilityValueText, getValueIndicatorLabel],
507
+ );
508
+
509
+ const contextValue = React.useMemo<SliderContextValue>(
510
+ () => ({
511
+ values,
512
+ min,
513
+ max,
514
+ step,
515
+ allowedValues,
516
+ minStepsBetweenThumbs,
517
+ dir,
518
+ disabled,
519
+ readOnly,
520
+ invalid,
521
+ valueIndicatorTrigger: resolvedValueIndicatorTrigger,
522
+ dragging,
523
+ activeThumbIndex,
524
+ valueIndicatorEverShown,
525
+ classes,
526
+ getClasses,
527
+ trackRef,
528
+ dimensions,
529
+ registerThumb,
530
+ updateIndicatorWidth,
531
+ registerIndicator,
532
+ startThumb,
533
+ formatters,
534
+ interaction: interaction.current,
535
+ }),
536
+ [
537
+ activeThumbIndex,
538
+ allowedValues,
539
+ classes,
540
+ dimensions,
541
+ dir,
542
+ disabled,
543
+ dragging,
544
+ formatters,
545
+ getClasses,
546
+ invalid,
547
+ max,
548
+ min,
549
+ minStepsBetweenThumbs,
550
+ readOnly,
551
+ registerIndicator,
552
+ registerThumb,
553
+ resolvedValueIndicatorTrigger,
554
+ startThumb,
555
+ step,
556
+ updateIndicatorWidth,
557
+ valueIndicatorEverShown,
558
+ values,
559
+ ],
560
+ );
561
+
562
+ return (
563
+ <SliderContext.Provider value={contextValue}>
564
+ <view
565
+ {...mergeProps(
566
+ rootHandlers,
567
+ nativeProps,
568
+ { "consume-slide-event": consumeSlideEvent },
569
+ forwardedRef ? { ref: forwardedRef } : {},
570
+ )}
571
+ className={clsx(classes.root, className)}
572
+ style={style}
573
+ >
574
+ {children}
575
+ </view>
576
+ </SliderContext.Provider>
577
+ );
578
+ });
579
+ SliderRoot.displayName = "SliderRoot";
580
+
581
+ export const SliderControl = React.forwardRef<NodesRef, SliderControlProps>((props, ref) => {
582
+ const { children, className, ...nativeProps } = props;
583
+ const context = useSliderContext("Slider.Control");
584
+ return (
585
+ <view
586
+ {...mergeProps(ref ? { ref } : {}, nativeProps)}
587
+ className={clsx(context.classes.control, className)}
588
+ >
589
+ {children}
590
+ </view>
591
+ );
592
+ });
593
+ SliderControl.displayName = "SliderControl";
594
+
595
+ export const SliderTrack = React.forwardRef<NodesRef, SliderTrackProps>((props, ref) => {
596
+ const { children, className, ...nativeProps } = props;
597
+ const context = useSliderContext("Slider.Track");
598
+ return (
599
+ <view
600
+ {...mergeProps(ref ? { ref } : {}, { ref: context.trackRef }, nativeProps)}
601
+ className={clsx(
602
+ context.getClasses({ disabled: context.disabled, dragging: context.dragging }).track,
603
+ className,
604
+ )}
605
+ >
606
+ {children}
607
+ </view>
608
+ );
609
+ });
610
+ SliderTrack.displayName = "SliderTrack";
611
+
612
+ export const SliderRange = React.forwardRef<NodesRef, SliderRangeProps>((props, ref) => {
613
+ const { children: _children, className, style, ...nativeProps } = props;
614
+ const context = useSliderContext("Slider.Range");
615
+ const percentages = context.values.map((value) =>
616
+ percentageForValue(value, context.min, context.max),
617
+ );
618
+ const end = context.values.length > 1 ? Math.max(...percentages) : (percentages[0] ?? 0);
619
+ const start = context.values.length > 1 ? Math.min(...percentages) : 0;
620
+ const left = context.dir === "ltr" ? start : 100 - end;
621
+ const width = Math.max(0, end - start);
622
+ return (
623
+ <view
624
+ {...mergeProps(ref ? { ref } : {}, nativeProps)}
625
+ className={clsx(
626
+ context.getClasses({ disabled: context.disabled, dragging: context.dragging }).range,
627
+ className,
628
+ )}
629
+ style={dynamicStyle(style, {
630
+ "--slider-range-left": `${left}%`,
631
+ "--slider-range-width": `${width}%`,
632
+ })}
633
+ />
634
+ );
635
+ });
636
+ SliderRange.displayName = "SliderRange";
637
+
638
+ export const SliderThumb = React.forwardRef<NodesRef, SliderThumbProps>((props, forwardedRef) => {
639
+ const { children, index = 0, className, style, ...nativeProps } = props;
640
+ const context = useSliderContext("Slider.Thumb");
641
+ const value = context.values[index];
642
+ const localRef = React.useRef<NodesRef>(null);
643
+ React.useEffect(() => {
644
+ context.registerThumb(index, localRef);
645
+ }, [context.registerThumb, index]);
646
+ if (value === undefined) return null;
647
+ const percent = percentageForValue(value, context.min, context.max);
648
+ const physicalPercent = context.dir === "ltr" ? percent : 100 - percent;
649
+ const offset = getThumbInBoundsOffset(context.dimensions.thumbWidth, physicalPercent, 1);
650
+ const accessibilityValue =
651
+ context.formatters.getAccessibilityValueText?.(value, index) ??
652
+ `minimum ${context.min}, maximum ${context.max}, current ${value}`;
653
+ const {
654
+ "accessibility-element": accessibilityElement = true,
655
+ "accessibility-label": accessibilityLabel,
656
+ "accessibility-role-description": accessibilityRoleDescription = "adjustable",
657
+ "accessibility-value": providedAccessibilityValue,
658
+ "accessibility-traits": accessibilityTraits,
659
+ ...restNativeProps
660
+ } = nativeProps;
661
+ return (
662
+ <view
663
+ {...mergeProps(
664
+ forwardedRef ? { ref: forwardedRef } : {},
665
+ { ref: localRef },
666
+ restNativeProps,
667
+ { bindtouchstart: () => context.startThumb(index) },
668
+ )}
669
+ className={clsx(
670
+ context.getClasses({
671
+ disabled: context.disabled,
672
+ dragging: context.dragging,
673
+ thumbDragging: context.dragging && context.activeThumbIndex === index,
674
+ }).thumb,
675
+ className,
676
+ )}
677
+ style={dynamicStyle(style, {
678
+ "--slider-thumb-left": `${physicalPercent}%`,
679
+ "--slider-thumb-offset": `${offset}px`,
680
+ })}
681
+ accessibility-element={accessibilityElement}
682
+ accessibility-label={accessibilityLabel ?? context.formatters.getAccessibilityLabel?.(index)}
683
+ accessibility-role-description={accessibilityRoleDescription}
684
+ accessibility-value={providedAccessibilityValue ?? accessibilityValue}
685
+ accessibility-traits={context.disabled ? "disabled" : accessibilityTraits}
686
+ >
687
+ {children}
688
+ </view>
689
+ );
690
+ });
691
+ SliderThumb.displayName = "SliderThumb";
692
+
693
+ export const SliderTick = React.forwardRef<NodesRef, SliderTickProps>((props, ref) => {
694
+ const [variantProps, restProps] = sliderTick.splitVariantProps(props);
695
+ const { children: _children, className, style, value, ...nativeProps } = restProps;
696
+ const context = useSliderContext("Slider.Tick");
697
+ const percent = percentageForValue(value, context.min, context.max);
698
+ const physicalPercent = context.dir === "ltr" ? percent : 100 - percent;
699
+ return (
700
+ <view
701
+ {...mergeProps(ref ? { ref } : {}, nativeProps)}
702
+ className={clsx(sliderTick(variantProps), className)}
703
+ style={dynamicStyle(style, {
704
+ "--slider-tick-left": `${physicalPercent}%`,
705
+ "--slider-tick-offset": `${getThumbInBoundsOffset(context.dimensions.thumbWidth, physicalPercent, 1)}px`,
706
+ })}
707
+ accessibility-elements-hidden={true}
708
+ />
709
+ );
710
+ });
711
+ SliderTick.displayName = "SliderTick";
712
+
713
+ export const SliderMarkers = React.forwardRef<NodesRef, SliderMarkersProps>((props, ref) => {
714
+ const { children, className, ...nativeProps } = props;
715
+ const context = useSliderContext("Slider.Markers");
716
+ return (
717
+ <view
718
+ {...mergeProps(ref ? { ref } : {}, nativeProps)}
719
+ className={clsx(context.getClasses({ disabled: context.disabled }).markers, className)}
720
+ >
721
+ {children}
722
+ </view>
723
+ );
724
+ });
725
+ SliderMarkers.displayName = "SliderMarkers";
726
+
727
+ export const SliderMarker = React.forwardRef<NodesRef, SliderMarkerProps>((props, ref) => {
728
+ const [variantProps, restProps] = sliderMarker.splitVariantProps(props);
729
+ const { children, className, style, value, ...nativeProps } = restProps;
730
+ const context = useSliderContext("Slider.Marker");
731
+ const percent = percentageForValue(value, context.min, context.max);
732
+ const physicalPercent = context.dir === "ltr" ? percent : 100 - percent;
733
+ return (
734
+ <view
735
+ {...mergeProps(ref ? { ref } : {}, nativeProps)}
736
+ className={clsx(
737
+ sliderMarker({ ...variantProps, dir: context.dir, disabled: context.disabled }),
738
+ className,
739
+ )}
740
+ style={dynamicStyle(style, {
741
+ "--slider-marker-left": `${physicalPercent}%`,
742
+ "--slider-marker-offset":
743
+ percent === 0 || percent === 100
744
+ ? "0px"
745
+ : `${getThumbInBoundsOffset(context.dimensions.thumbWidth, physicalPercent, 1)}px`,
746
+ })}
747
+ accessibility-elements-hidden={true}
748
+ >
749
+ {children}
750
+ </view>
751
+ );
752
+ });
753
+ SliderMarker.displayName = "SliderMarker";
754
+
755
+ export const SliderValueIndicatorRoot = React.forwardRef<NodesRef, SliderValueIndicatorRootProps>(
756
+ (props, ref) => {
757
+ const {
758
+ children,
759
+ className,
760
+ style,
761
+ index = 0,
762
+ bindlayoutchange: userBindLayoutChange,
763
+ ...nativeProps
764
+ } = props;
765
+ const context = useSliderContext("Slider.ValueIndicatorRoot");
766
+ const value = context.values[index];
767
+ const localRef = React.useRef<NodesRef>(null);
768
+ React.useEffect(() => {
769
+ context.registerIndicator(index, localRef);
770
+ }, [context.registerIndicator, index, value]);
771
+ const handleLayoutChange = React.useCallback<LayoutChangeHandler>(
772
+ (...args) => {
773
+ "background only";
774
+ userBindLayoutChange?.(...args);
775
+ const width = getLayoutWidth(args[0]);
776
+ if (width != null) context.updateIndicatorWidth(index, width);
777
+ },
778
+ [context, index, userBindLayoutChange],
779
+ );
780
+ if (value === undefined) return null;
781
+ const percent = percentageForValue(value, context.min, context.max);
782
+ const physicalPercent = context.dir === "ltr" ? percent : 100 - percent;
783
+ const thumbOffset = getThumbInBoundsOffset(context.dimensions.thumbWidth, physicalPercent, 1);
784
+ const indicatorOffset = getStickyLabelOffset(
785
+ context.dimensions.indicatorWidths[index] ?? 0,
786
+ physicalPercent,
787
+ context.dimensions.thumbWidth,
788
+ context.dimensions.trackWidth,
789
+ 1,
790
+ );
791
+ const shown = context.dragging && context.activeThumbIndex === index;
792
+ const visible = !context.disabled && !context.readOnly && shown;
793
+ const classes = context.getClasses({
794
+ disabled: context.disabled,
795
+ dragging: context.dragging,
796
+ valueIndicatorShown: visible,
797
+ valueIndicatorEverShown: context.valueIndicatorEverShown,
798
+ });
799
+ return (
800
+ <view
801
+ className={classes.valueIndicatorMotion}
802
+ style={dynamicStyle(undefined, {
803
+ "--slider-value-indicator-left": `${physicalPercent}%`,
804
+ "--slider-value-indicator-offset": `${indicatorOffset}px`,
805
+ "--slider-thumb-offset": `${thumbOffset}px`,
806
+ })}
807
+ accessibility-elements-hidden={true}
808
+ >
809
+ <view
810
+ {...mergeProps(
811
+ nativeProps,
812
+ ref ? { ref } : {},
813
+ { ref: localRef },
814
+ { bindlayoutchange: handleLayoutChange },
815
+ )}
816
+ className={clsx(classes.valueIndicatorRoot, className)}
817
+ style={style}
818
+ accessibility-elements-hidden={true}
819
+ >
820
+ {children}
821
+ </view>
822
+ </view>
823
+ );
824
+ },
825
+ );
826
+ SliderValueIndicatorRoot.displayName = "SliderValueIndicatorRoot";
827
+
828
+ export const SliderValueIndicatorArrow = React.forwardRef<NodesRef, SliderValueIndicatorArrowProps>(
829
+ (props, ref) => {
830
+ const { children, className, ...nativeProps } = props;
831
+ const context = useSliderContext("Slider.ValueIndicatorArrow");
832
+ return (
833
+ <view
834
+ {...mergeProps(ref ? { ref } : {}, nativeProps)}
835
+ className={clsx(
836
+ context.getClasses({ disabled: context.disabled, dragging: context.dragging })
837
+ .valueIndicatorArrow,
838
+ className,
839
+ )}
840
+ >
841
+ {children}
842
+ </view>
843
+ );
844
+ },
845
+ );
846
+ SliderValueIndicatorArrow.displayName = "SliderValueIndicatorArrow";
847
+
848
+ export const SliderValueIndicatorArrowTip = React.forwardRef<
849
+ NodesRef,
850
+ SliderValueIndicatorArrowTipProps
851
+ >((props, ref) => {
852
+ const { children, className, ...nativeProps } = props;
853
+ const context = useSliderContext("Slider.ValueIndicatorArrowTip");
854
+ return (
855
+ <view
856
+ {...mergeProps(ref ? { ref } : {}, nativeProps)}
857
+ className={clsx(
858
+ context.getClasses({ disabled: context.disabled, dragging: context.dragging })
859
+ .valueIndicatorArrowTip,
860
+ className,
861
+ )}
862
+ >
863
+ {children}
864
+ </view>
865
+ );
866
+ });
867
+ SliderValueIndicatorArrowTip.displayName = "SliderValueIndicatorArrowTip";
868
+
869
+ export const SliderValueIndicatorLabel = React.forwardRef<NodesRef, SliderValueIndicatorLabelProps>(
870
+ (props, ref) => {
871
+ const { children, className, style, index = 0, ...nativeProps } = props;
872
+ const context = useSliderContext("Slider.ValueIndicatorLabel");
873
+ const value = context.values[index];
874
+ if (value === undefined) return null;
875
+ return (
876
+ <text
877
+ {...mergeProps(ref ? { ref } : {}, nativeProps)}
878
+ className={clsx(
879
+ context.getClasses({
880
+ disabled: context.disabled,
881
+ dragging: context.dragging,
882
+ valueIndicatorEverShown: context.valueIndicatorEverShown,
883
+ }).valueIndicatorLabel,
884
+ className,
885
+ )}
886
+ style={style}
887
+ >
888
+ {children ?? context.formatters.getValueIndicatorLabel({ value, thumbIndex: index })}
889
+ </text>
890
+ );
891
+ },
892
+ );
893
+ SliderValueIndicatorLabel.displayName = "SliderValueIndicatorLabel";
894
+
895
+ function clampRatio(value: number): number {
896
+ return Math.min(1, Math.max(0, value));
897
+ }