@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
@@ -1,4 +1,5 @@
1
1
  import { tabs, type TabsVariantProps } from "@seed-design/lynx-css/recipes/tabs";
2
+ import { runOnMainThread } from "@lynx-js/react";
2
3
  import * as React from "@lynx-js/react";
3
4
  import type {
4
5
  IntrinsicElements,
@@ -20,28 +21,90 @@ import type {
20
21
  LynxViewProps,
21
22
  LynxViewRef,
22
23
  } from "../../types";
24
+ import { mergeProps } from "../../utils/merge-props";
23
25
  import { createSlotRecipeContext } from "../../utils/create-slot-recipe-context";
24
26
  import { Box } from "../Box";
27
+ import { HStack } from "../Stack";
25
28
  import {
26
29
  areTabsTransitionsEnabled,
27
30
  getTabsLayoutWidth,
28
31
  getTabsOrderedItems,
32
+ getTabsScrollOffset,
29
33
  getTabsTriggerRects,
30
34
  type TabsLayoutRect,
31
35
  } from "./Tabs.utils";
32
- import { mergeProps } from "../../utils/merge-props";
33
36
 
34
37
  type NativeViewProps = IntrinsicElements["view"];
35
38
  type NativeViewPagerProps = IntrinsicElements["viewpager"];
39
+ type TouchEndHandler = NonNullable<NativeViewPagerProps["bindtouchend"]>;
40
+ type TouchCancelHandler = NonNullable<NativeViewPagerProps["bindtouchcancel"]>;
36
41
  type LayoutChangeHandler = NonNullable<NativeViewProps["bindlayoutchange"]>;
42
+ type NativeScrollViewProps = IntrinsicElements["scroll-view"];
43
+ type ScrollViewLayoutChangeHandler = NonNullable<NativeScrollViewProps["bindlayoutchange"]>;
44
+ type ScrollViewHandler = NonNullable<NativeScrollViewProps["bindscroll"]>;
45
+ type ContentSizeChangedHandler = NonNullable<NativeScrollViewProps["bindcontentsizechanged"]>;
46
+
47
+ type ComputedStyleElement = MainThread.Element & {
48
+ getComputedStyleProperty?: (name: string) => string;
49
+ };
50
+
51
+ interface TabsContentInsets {
52
+ start: number;
53
+ end: number;
54
+ }
55
+
56
+ interface TabsScrollMetrics {
57
+ currentOffset: number;
58
+ viewportWidth: number | null;
59
+ contentWidth: number | null;
60
+ insets: TabsContentInsets | null;
61
+ }
62
+
63
+ function getTabsContentInsets(
64
+ contentRef: React.RefObject<ComputedStyleElement | null>,
65
+ ): TabsContentInsets | null {
66
+ "main thread";
67
+
68
+ const content = contentRef.current;
69
+ if (!content || typeof content.getComputedStyleProperty !== "function") return null;
70
+ // Read used values so Rootage tokens and consumer style overrides share the
71
+ // same scroll-content origin as the trigger rectangles; do not invent a fallback.
72
+
73
+ const start = Number.parseFloat(content.getComputedStyleProperty("padding-left"));
74
+ const end = Number.parseFloat(content.getComputedStyleProperty("padding-right"));
75
+ if (!Number.isFinite(start) || !Number.isFinite(end) || start < 0 || end < 0) return null;
76
+ return { start, end };
77
+ }
37
78
  type TriggerRect = TabsLayoutRect;
38
79
  type TriggerItem = { value: string; disabled: boolean };
80
+ type TabsSharedClassNames = {
81
+ root: string;
82
+ list: string;
83
+ listContent: string;
84
+ carousel: string;
85
+ carouselCamera: string;
86
+ content: string;
87
+ trigger: string;
88
+ triggerLabel: string;
89
+ };
90
+ type TabsRecipeState = {
91
+ selected?: boolean;
92
+ pressed?: boolean;
93
+ disabled?: boolean;
94
+ inCarousel?: boolean;
95
+ transitionEnabled?: boolean;
96
+ };
97
+ type TabsRecipeAdapter = {
98
+ getClassNames: (state?: TabsRecipeState) => TabsSharedClassNames;
99
+ getIndicatorClassName?: (state?: TabsRecipeState) => string;
100
+ triggerGap: number;
101
+ };
39
102
  type TabsPublicVariantProps = Omit<
40
103
  TabsVariantProps,
41
104
  "selected" | "disabled" | "inCarousel" | "transitionEnabled"
42
105
  >;
43
106
 
44
- const { ClassNamesProvider, useClassNames } = createSlotRecipeContext(tabs);
107
+ const { ClassNamesProvider } = createSlotRecipeContext(tabs);
45
108
 
46
109
  function invokeSelectTab(pager: NodesRef | null, index: number, smooth: boolean) {
47
110
  "background only";
@@ -66,7 +129,10 @@ function invokeScrollToOffset(list: NodesRef | null, offset: number) {
66
129
  interface TabsContextValue {
67
130
  value: string | undefined;
68
131
  visualValue: string | undefined;
69
- variantProps: TabsPublicVariantProps;
132
+ classNames: TabsSharedClassNames;
133
+ getClassNames: (state?: TabsRecipeState) => TabsSharedClassNames;
134
+ getIndicatorClassName?: (state?: TabsRecipeState) => string;
135
+ inlineNotification: boolean;
70
136
  items: TriggerItem[];
71
137
  pagerValues: string[];
72
138
  indicatorIndex: number;
@@ -79,7 +145,6 @@ interface TabsContextValue {
79
145
  updateTriggerDisabled: (value: string, disabled: boolean) => void;
80
146
  syncTriggerOrder: (values: string[]) => void;
81
147
  updateTriggerWidth: (value: string, width: number) => void;
82
- setListRef: (ref: NodesRef | null) => void;
83
148
  setPagerRef: (ref: NodesRef | null) => void;
84
149
  selectValue: (value: string) => void;
85
150
  handlePagerWillChange: (index: number) => void;
@@ -135,9 +200,15 @@ export interface TabsRootProps extends TabsPublicVariantProps, LynxStyledElement
135
200
  onValueChange?: (value: string) => void;
136
201
  }
137
202
 
138
- export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) => {
139
- const [variantProps, otherProps] = tabs.splitVariantProps(props);
203
+ interface TabsRootPrimitiveProps extends Omit<TabsRootProps, keyof TabsPublicVariantProps> {
204
+ recipe: TabsRecipeAdapter;
205
+ inlineNotification?: boolean;
206
+ }
207
+
208
+ export const TabsRootPrimitive = React.forwardRef<unknown, TabsRootPrimitiveProps>((props, ref) => {
140
209
  const {
210
+ recipe,
211
+ inlineNotification = false,
141
212
  children,
142
213
  className,
143
214
  style,
@@ -145,7 +216,7 @@ export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) =>
145
216
  defaultValue,
146
217
  onValueChange,
147
218
  ...nativeProps
148
- } = otherProps;
219
+ } = props;
149
220
  const [value, setValueInternal] = useControllableState<string | undefined>({
150
221
  value: valueProp,
151
222
  defaultValue,
@@ -158,7 +229,6 @@ export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) =>
158
229
  const [contentValues, setContentValues] = React.useState<string[]>([]);
159
230
  const [indicatorValue, setIndicatorValue] = React.useState<string | undefined>();
160
231
  const [triggerWidths, setTriggerWidths] = React.useState<Record<string, number>>({});
161
- const listRef = React.useRef<NodesRef | null>(null);
162
232
  const pagerRef = React.useRef<NodesRef | null>(null);
163
233
  const indicatorRef = React.useMainThreadRef<MainThread.Element>(null);
164
234
 
@@ -174,28 +244,19 @@ export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) =>
174
244
  getTabsTriggerRects(
175
245
  items.map((item) => item.value),
176
246
  triggerWidths,
247
+ recipe.triggerGap,
177
248
  ),
178
- [items, triggerWidths],
249
+ [items, recipe.triggerGap, triggerWidths],
179
250
  );
180
251
  const transitionsEnabled = areTabsTransitionsEnabled(
181
252
  items.map((item) => item.value),
182
253
  triggerRects,
183
254
  );
184
- const classNames = tabs({ ...variantProps, transitionEnabled: transitionsEnabled });
255
+ const getClassNames = recipe.getClassNames;
256
+ const classNames = getClassNames({ transitionEnabled: transitionsEnabled });
185
257
  const visualValue = indicatorValue ?? value;
186
258
  const indicatorIndex = items.findIndex((item) => item.value === visualValue);
187
259
  const selectedPagerIndex = value === undefined ? -1 : pagerValues.indexOf(value);
188
- const selectedRect = value === undefined ? undefined : triggerRects[value];
189
- const selectedOffset = selectedRect?.left ?? null;
190
-
191
- const setListNode = React.useCallback(
192
- (node: NodesRef | null) => {
193
- listRef.current = node;
194
- if (node && selectedOffset !== null) invokeScrollToOffset(node, selectedOffset);
195
- },
196
- [selectedOffset],
197
- );
198
-
199
260
  const setPagerNode = React.useCallback(
200
261
  (node: NodesRef | null) => {
201
262
  pagerRef.current = node;
@@ -283,14 +344,16 @@ export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) =>
283
344
  if (selectedPagerIndex >= 0) {
284
345
  invokeSelectTab(pagerRef.current, selectedPagerIndex, false);
285
346
  }
286
- if (selectedOffset !== null) invokeScrollToOffset(listRef.current, selectedOffset);
287
- }, [selectedPagerIndex, selectedOffset]);
347
+ }, [selectedPagerIndex]);
288
348
 
289
349
  const contextValue = React.useMemo<TabsContextValue>(
290
350
  () => ({
291
351
  value,
292
352
  visualValue,
293
- variantProps,
353
+ classNames,
354
+ getClassNames,
355
+ getIndicatorClassName: recipe.getIndicatorClassName,
356
+ inlineNotification,
294
357
  items,
295
358
  pagerValues,
296
359
  indicatorIndex,
@@ -303,7 +366,6 @@ export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) =>
303
366
  updateTriggerDisabled,
304
367
  syncTriggerOrder,
305
368
  updateTriggerWidth,
306
- setListRef: setListNode,
307
369
  setPagerRef: setPagerNode,
308
370
  selectValue,
309
371
  handlePagerWillChange,
@@ -312,7 +374,10 @@ export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) =>
312
374
  [
313
375
  value,
314
376
  visualValue,
315
- variantProps,
377
+ classNames,
378
+ getClassNames,
379
+ recipe.getIndicatorClassName,
380
+ inlineNotification,
316
381
  items,
317
382
  pagerValues,
318
383
  indicatorIndex,
@@ -325,7 +390,6 @@ export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) =>
325
390
  updateTriggerDisabled,
326
391
  syncTriggerOrder,
327
392
  updateTriggerWidth,
328
- setListNode,
329
393
  setPagerNode,
330
394
  selectValue,
331
395
  handlePagerWillChange,
@@ -335,18 +399,46 @@ export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) =>
335
399
 
336
400
  return (
337
401
  <TabsContext.Provider value={contextValue}>
338
- <ClassNamesProvider value={classNames}>
339
- <view
340
- {...mergeProps(ref ? { ref: ref as LynxViewRef } : {}, nativeProps)}
341
- className={clsx(classNames.root, className)}
342
- style={style}
343
- >
344
- {children}
345
- </view>
346
- </ClassNamesProvider>
402
+ <view
403
+ {...mergeProps(ref ? { ref: ref as LynxViewRef } : {}, nativeProps)}
404
+ className={clsx(classNames.root, className)}
405
+ style={style}
406
+ >
407
+ {children}
408
+ </view>
347
409
  </TabsContext.Provider>
348
410
  );
349
411
  });
412
+ TabsRootPrimitive.displayName = "TabsRootPrimitive";
413
+
414
+ export const TabsRoot = React.forwardRef<unknown, TabsRootProps>((props, ref) => {
415
+ const [variantProps, rootProps] = tabs.splitVariantProps(props);
416
+ const getClassNames = React.useCallback(
417
+ (state: TabsRecipeState = {}) =>
418
+ tabs({
419
+ ...variantProps,
420
+ selected: state.selected,
421
+ disabled: state.disabled,
422
+ inCarousel: state.inCarousel,
423
+ transitionEnabled: state.transitionEnabled,
424
+ }),
425
+ [variantProps],
426
+ );
427
+ const recipe = React.useMemo<TabsRecipeAdapter>(
428
+ () => ({
429
+ getClassNames,
430
+ getIndicatorClassName: (state) => getClassNames(state).indicator,
431
+ triggerGap: 0,
432
+ }),
433
+ [getClassNames],
434
+ );
435
+
436
+ return (
437
+ <ClassNamesProvider value={getClassNames()}>
438
+ <TabsRootPrimitive {...rootProps} ref={ref} recipe={recipe} />
439
+ </ClassNamesProvider>
440
+ );
441
+ });
350
442
  TabsRoot.displayName = "TabsRoot";
351
443
 
352
444
  ////////////////////////////////////////////////////////////////////////////////////
@@ -371,27 +463,169 @@ function getTabsTriggerValues(children: React.ReactNode): string[] {
371
463
  return values;
372
464
  }
373
465
 
374
- export interface TabsListProps extends LynxStyledElementProps {}
466
+ export interface TabsListProps
467
+ extends LynxStyledElementProps,
468
+ Omit<
469
+ NativeScrollViewProps,
470
+ | "children"
471
+ | "className"
472
+ | "style"
473
+ | "scroll-orientation"
474
+ | "scroll-bar-enable"
475
+ | "bindlayoutchange"
476
+ | "bindscroll"
477
+ | "bindcontentsizechanged"
478
+ > {
479
+ /** 선택한 tab을 목록 안에서 정렬할 방식입니다. @defaultValue "start" */
480
+ scrollAlign?: "nearest" | "start" | "center" | "end";
481
+ }
375
482
 
376
483
  export const TabsList = React.forwardRef<unknown, TabsListProps>((props, ref) => {
377
- const { children, className, style, ...nativeProps } = props;
378
- const classNames = useClassNames();
379
- const { items, setListRef, syncTriggerOrder } = useTabsContext("TabsList");
484
+ const { children, className, style, scrollAlign = "start", ...nativeProps } = props;
485
+ const { classNames, items, syncTriggerOrder, triggerRects, value } = useTabsContext("TabsList");
486
+ const scrollRef = React.useRef<NodesRef | null>(null);
487
+ const contentRef = React.useMainThreadRef<ComputedStyleElement | null>(null);
488
+ const metricsRef = React.useRef<TabsScrollMetrics>({
489
+ currentOffset: 0,
490
+ viewportWidth: null,
491
+ contentWidth: null,
492
+ insets: null,
493
+ });
494
+ const contentInsetRequestRef = React.useRef(0);
495
+ const selectedValueRef = React.useRef(value);
496
+ const [geometryRevision, setGeometryRevision] = React.useState(0);
380
497
  const triggerOrder = React.useMemo(() => getTabsTriggerValues(children), [children]);
381
498
 
499
+ selectedValueRef.current = value;
500
+
382
501
  React.useEffect(() => {
383
502
  "background only";
384
503
  syncTriggerOrder(triggerOrder);
385
504
  }, [items, syncTriggerOrder, triggerOrder]);
386
505
 
506
+ const requestContentInsets = React.useCallback(() => {
507
+ "background only";
508
+ const request = ++contentInsetRequestRef.current;
509
+ metricsRef.current.insets = null;
510
+
511
+ void runOnMainThread<TabsContentInsets | null, typeof getTabsContentInsets>(
512
+ getTabsContentInsets,
513
+ )(contentRef).then(
514
+ (insets) => {
515
+ "background only";
516
+ if (request !== contentInsetRequestRef.current || !insets) return;
517
+
518
+ metricsRef.current.insets = insets;
519
+ setGeometryRevision((revision) => revision + 1);
520
+ },
521
+ () => {
522
+ // Missing native measurement intentionally leaves alignment pending.
523
+ },
524
+ );
525
+ }, [contentRef]);
526
+
527
+ const handleListLayoutChange = React.useCallback<ScrollViewLayoutChangeHandler>((event) => {
528
+ "background only";
529
+ const width = getTabsLayoutWidth(event);
530
+ if (width !== null && width > 0 && metricsRef.current.viewportWidth !== width) {
531
+ metricsRef.current.viewportWidth = width;
532
+ setGeometryRevision((revision) => revision + 1);
533
+ }
534
+ }, []);
535
+
536
+ const handleContentLayoutChange = React.useCallback<LayoutChangeHandler>(
537
+ (event) => {
538
+ "background only";
539
+ const width = getTabsLayoutWidth(event);
540
+ if (width !== null && width > 0 && metricsRef.current.contentWidth !== width) {
541
+ // Layout width is the ListContent border box until scrollWidth reports
542
+ // the scroll-view content width; the latter replaces this value below.
543
+ metricsRef.current.contentWidth = width;
544
+ setGeometryRevision((revision) => revision + 1);
545
+ }
546
+ requestContentInsets();
547
+ },
548
+ [requestContentInsets],
549
+ );
550
+
551
+ const updateScrollMetrics = React.useCallback(
552
+ (scrollLeft: number, scrollWidth: number) => {
553
+ "background only";
554
+ if (Number.isFinite(scrollLeft)) metricsRef.current.currentOffset = Math.max(0, scrollLeft);
555
+ if (
556
+ Number.isFinite(scrollWidth) &&
557
+ scrollWidth > 0 &&
558
+ metricsRef.current.contentWidth !== scrollWidth
559
+ ) {
560
+ metricsRef.current.contentWidth = scrollWidth;
561
+ requestContentInsets();
562
+ setGeometryRevision((revision) => revision + 1);
563
+ }
564
+ },
565
+ [requestContentInsets],
566
+ );
567
+
568
+ const handleScroll = React.useCallback<ScrollViewHandler>(
569
+ (event) => {
570
+ "background only";
571
+ updateScrollMetrics(event.detail.scrollLeft, event.detail.scrollWidth);
572
+ },
573
+ [updateScrollMetrics],
574
+ );
575
+
576
+ const handleContentSizeChanged = React.useCallback<ContentSizeChangedHandler>(
577
+ (event) => {
578
+ "background only";
579
+ updateScrollMetrics(event.detail.scrollLeft, event.detail.scrollWidth);
580
+ },
581
+ [updateScrollMetrics],
582
+ );
583
+
584
+ React.useEffect(() => {
585
+ "background only";
586
+ const selectedRect = value === undefined ? undefined : triggerRects[value];
587
+ const { currentOffset, viewportWidth, contentWidth, insets } = metricsRef.current;
588
+ if (
589
+ !scrollRef.current ||
590
+ value === undefined ||
591
+ !selectedRect ||
592
+ viewportWidth === null ||
593
+ contentWidth === null ||
594
+ insets === null
595
+ ) {
596
+ return;
597
+ }
598
+
599
+ const targetOffset = getTabsScrollOffset({
600
+ scrollAlign,
601
+ currentOffset,
602
+ viewportWidth,
603
+ contentWidth,
604
+ contentInsetStart: insets.start,
605
+ contentInsetEnd: insets.end,
606
+ triggerRect: selectedRect,
607
+ });
608
+ if (selectedValueRef.current !== value || targetOffset === currentOffset) return;
609
+
610
+ invokeScrollToOffset(scrollRef.current, targetOffset);
611
+ }, [geometryRevision, items, scrollAlign, triggerRects, value]);
612
+
387
613
  const mergedRef = React.useMemo(
388
- () => mergeProps({ ref: setListRef }, { ref: ref as LynxViewRef }).ref,
389
- [ref, setListRef],
614
+ () => mergeProps({ ref: scrollRef }, { ref: ref as LynxViewRef }).ref,
615
+ [ref],
390
616
  );
391
617
 
392
618
  return (
393
619
  <scroll-view
394
- {...mergeProps({ ref: mergedRef }, nativeProps)}
620
+ {...mergeProps(
621
+ {
622
+ ref: mergedRef,
623
+ bindlayoutchange: handleListLayoutChange,
624
+ bindscroll: handleScroll,
625
+ bindcontentsizechanged: handleContentSizeChanged,
626
+ },
627
+ nativeProps,
628
+ )}
395
629
  scroll-orientation="horizontal"
396
630
  scroll-bar-enable={false}
397
631
  accessibility-element={false}
@@ -399,7 +633,13 @@ export const TabsList = React.forwardRef<unknown, TabsListProps>((props, ref) =>
399
633
  className={clsx(classNames.list, className)}
400
634
  style={style}
401
635
  >
402
- <view className={classNames.listContent}>{children}</view>
636
+ <view
637
+ className={classNames.listContent}
638
+ main-thread:ref={contentRef}
639
+ bindlayoutchange={handleContentLayoutChange}
640
+ >
641
+ {children}
642
+ </view>
403
643
  </scroll-view>
404
644
  );
405
645
  });
@@ -452,13 +692,10 @@ export const TabsTrigger = React.forwardRef<unknown, TabsTriggerProps>((props, r
452
692
  },
453
693
  [bindtap, context.selectValue, triggerValue],
454
694
  );
455
- const {
456
- pressed: _pressed,
457
- bindtouchstart,
458
- bindtouchend,
459
- bindtouchcancel,
460
- ...pressHandlers
461
- } = usePressTap({ disabled, onTap: handleTap });
695
+ const { pressed, bindtouchstart, bindtouchend, bindtouchcancel, ...pressHandlers } = usePressTap({
696
+ disabled,
697
+ onTap: handleTap,
698
+ });
462
699
  const { scaleFeedbackTriggerProps, scaleFeedbackTargetProps } = useScaleFeedback({
463
700
  disabled,
464
701
  onTouchStart: bindtouchstart,
@@ -475,10 +712,10 @@ export const TabsTrigger = React.forwardRef<unknown, TabsTriggerProps>((props, r
475
712
  [context.updateTriggerWidth, triggerValue],
476
713
  );
477
714
 
478
- const triggerClasses = tabs({
479
- ...context.variantProps,
715
+ const triggerClasses = context.getClassNames({
480
716
  selected: visuallySelected,
481
717
  disabled,
718
+ pressed,
482
719
  transitionEnabled: context.transitionsEnabled,
483
720
  });
484
721
  const label =
@@ -504,10 +741,17 @@ export const TabsTrigger = React.forwardRef<unknown, TabsTriggerProps>((props, r
504
741
  style={style}
505
742
  >
506
743
  {notification ? (
507
- <Box position="relative">
508
- <text className={triggerClasses.triggerLabel}>{children}</text>
509
- {notification}
510
- </Box>
744
+ context.inlineNotification ? (
745
+ <HStack position="relative" gap="x1_5">
746
+ <text className={triggerClasses.triggerLabel}>{children}</text>
747
+ <view accessibility-elements-hidden={true}>{notification}</view>
748
+ </HStack>
749
+ ) : (
750
+ <Box position="relative">
751
+ <text className={triggerClasses.triggerLabel}>{children}</text>
752
+ {notification}
753
+ </Box>
754
+ )
511
755
  ) : (
512
756
  <text className={triggerClasses.triggerLabel}>{children}</text>
513
757
  )}
@@ -522,8 +766,18 @@ export interface TabsIndicatorProps extends LynxStyledElementProps {}
522
766
 
523
767
  export const TabsIndicator = React.forwardRef<unknown, TabsIndicatorProps>((props, ref) => {
524
768
  const { className, style, ...nativeProps } = props;
525
- const classNames = useClassNames();
526
- const { indicatorRef, items, indicatorIndex, triggerRects } = useTabsContext("TabsIndicator");
769
+ const {
770
+ indicatorRef,
771
+ items,
772
+ indicatorIndex,
773
+ triggerRects,
774
+ getIndicatorClassName,
775
+ transitionsEnabled,
776
+ } = useTabsContext("TabsIndicator");
777
+ if (!getIndicatorClassName) {
778
+ throw new Error("<TabsIndicator/> is only supported inside <TabsRoot/>.");
779
+ }
780
+ const indicatorClassName = getIndicatorClassName({ transitionEnabled: transitionsEnabled });
527
781
  const position = indicatorIndex;
528
782
  const lowerIndex = Math.max(0, Math.floor(position));
529
783
  const upperIndex = Math.min(items.length - 1, Math.ceil(position));
@@ -545,7 +799,7 @@ export const TabsIndicator = React.forwardRef<unknown, TabsIndicatorProps>((prop
545
799
  nativeProps,
546
800
  )}
547
801
  accessibility-elements-hidden={true}
548
- className={clsx(classNames.indicator, className)}
802
+ className={clsx(indicatorClassName, className)}
549
803
  style={
550
804
  {
551
805
  "--tabs-indicator-x": `${x}px`,
@@ -570,7 +824,7 @@ export const TabsContent = React.forwardRef<unknown, TabsContentProps>((props, r
570
824
  const inCarousel = useTabsCarouselCameraContext();
571
825
  const selected = tabsContext.value === contentValue;
572
826
  const disabled = tabsContext.items.find((item) => item.value === contentValue)?.disabled ?? false;
573
- const contentClasses = tabs({ ...tabsContext.variantProps, selected, inCarousel });
827
+ const contentClasses = tabsContext.getClassNames({ selected, inCarousel });
574
828
 
575
829
  React.useEffect(() => {
576
830
  "background only";
@@ -611,7 +865,9 @@ export interface TabsCarouselProps extends LynxStyledElementProps {
611
865
  /** iOS 뒤로가기 제스처를 우선하는 화면 왼쪽 가장자리 너비입니다. */
612
866
  iosBackGestureEdgeWidth?: number;
613
867
  onSettle?: () => void;
868
+ /** 네이티브 pager가 drag를 감지했을 때 호출합니다. */
614
869
  onSwipeStart?: () => void;
870
+ /** 시작된 스와이프가 끝나거나 취소되면 호출합니다. */
615
871
  onSwipeEnd?: () => void;
616
872
  }
617
873
 
@@ -627,7 +883,7 @@ export const TabsCarousel = React.forwardRef<unknown, TabsCarouselProps>((props,
627
883
  onSwipeEnd,
628
884
  ...nativeProps
629
885
  } = props;
630
- const classNames = useClassNames();
886
+ const { classNames } = useTabsContext("TabsCarousel");
631
887
  const contextValue = React.useMemo<TabsCarouselContextValue>(
632
888
  () => ({ swipeable, iosBackGestureEdgeWidth, onSettle, onSwipeStart, onSwipeEnd }),
633
889
  [swipeable, iosBackGestureEdgeWidth, onSettle, onSwipeStart, onSwipeEnd],
@@ -666,8 +922,8 @@ export const TabsCarouselCamera = React.forwardRef<unknown, TabsCarouselCameraPr
666
922
  bindoffsetchange,
667
923
  ...nativeProps
668
924
  } = props;
669
- const classNames = useClassNames();
670
925
  const tabsContext = useTabsContext("TabsCarouselCamera");
926
+ const { classNames } = tabsContext;
671
927
  const carouselContext = useTabsCarouselContext("TabsCarouselCamera");
672
928
  const swipingRef = React.useRef(false);
673
929
  const { indicatorRef, pagerValues, triggerRects } = tabsContext;
@@ -678,19 +934,41 @@ export const TabsCarouselCamera = React.forwardRef<unknown, TabsCarouselCameraPr
678
934
  [ref, tabsContext.setPagerRef],
679
935
  );
680
936
 
937
+ const finishSwipe = React.useCallback(() => {
938
+ "background only";
939
+ if (!swipingRef.current) return;
940
+ swipingRef.current = false;
941
+ carouselContext.onSwipeEnd?.();
942
+ }, [carouselContext.onSwipeEnd]);
943
+
944
+ const handleTouchEnd = React.useCallback<TouchEndHandler>(() => {
945
+ "background only";
946
+ finishSwipe();
947
+ }, [finishSwipe]);
948
+
949
+ const handleTouchCancel = React.useCallback<TouchCancelHandler>(() => {
950
+ "background only";
951
+ finishSwipe();
952
+ }, [finishSwipe]);
953
+
681
954
  const handleWillChange = React.useCallback(
682
955
  (event: ViewPagerWillChangeEvent) => {
683
956
  "background only";
684
957
  bindwillchange?.(event);
685
958
  if (event.detail.isDragged) {
686
959
  tabsContext.handlePagerWillChange(event.detail.index);
687
- if (!swipingRef.current) {
960
+ if (carouselContext.swipeable && !swipingRef.current) {
688
961
  swipingRef.current = true;
689
962
  carouselContext.onSwipeStart?.();
690
963
  }
691
964
  }
692
965
  },
693
- [bindwillchange, carouselContext.onSwipeStart, tabsContext.handlePagerWillChange],
966
+ [
967
+ bindwillchange,
968
+ carouselContext.onSwipeStart,
969
+ carouselContext.swipeable,
970
+ tabsContext.handlePagerWillChange,
971
+ ],
694
972
  );
695
973
 
696
974
  const handleChange = React.useCallback(
@@ -699,17 +977,8 @@ export const TabsCarouselCamera = React.forwardRef<unknown, TabsCarouselCameraPr
699
977
  bindchange?.(event);
700
978
  tabsContext.handlePagerChange(event.detail.index);
701
979
  carouselContext.onSettle?.();
702
- if (swipingRef.current || event.detail.isDragged) {
703
- swipingRef.current = false;
704
- carouselContext.onSwipeEnd?.();
705
- }
706
980
  },
707
- [
708
- bindchange,
709
- carouselContext.onSettle,
710
- carouselContext.onSwipeEnd,
711
- tabsContext.handlePagerChange,
712
- ],
981
+ [bindchange, carouselContext.onSettle, tabsContext.handlePagerChange],
713
982
  );
714
983
 
715
984
  const handleOffsetChange = React.useCallback(
@@ -748,6 +1017,8 @@ export const TabsCarouselCamera = React.forwardRef<unknown, TabsCarouselCameraPr
748
1017
  {...mergeProps(
749
1018
  {
750
1019
  ref: mergedRef,
1020
+ bindtouchend: handleTouchEnd,
1021
+ bindtouchcancel: handleTouchCancel,
751
1022
  bindwillchange: handleWillChange,
752
1023
  bindchange: handleChange,
753
1024
  bindoffsetchange: handleOffsetChange,