@seed-design/lynx-react 0.7.1 → 0.8.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.
- package/lib/components/Accordion/Accordion.jsx +2 -2
- package/lib/components/ActionButton/ActionButton.jsx +1 -1
- package/lib/components/AlertDialog/AlertDialog.d.ts +64 -0
- package/lib/components/AlertDialog/AlertDialog.d.ts.map +1 -0
- package/lib/components/AlertDialog/AlertDialog.jsx +138 -0
- package/lib/components/AlertDialog/AlertDialog.namespace.d.ts +2 -0
- package/lib/components/AlertDialog/AlertDialog.namespace.d.ts.map +1 -0
- package/lib/components/AlertDialog/AlertDialog.namespace.js +17 -0
- package/lib/components/AlertDialog/index.d.ts +3 -0
- package/lib/components/AlertDialog/index.d.ts.map +1 -0
- package/lib/components/AppBar/AppBar.d.ts +14 -1
- package/lib/components/AppBar/AppBar.d.ts.map +1 -1
- package/lib/components/AppBar/AppBar.jsx +39 -8
- package/lib/components/AppBar/AppBar.namespace.d.ts +1 -1
- package/lib/components/AppBar/AppBar.namespace.d.ts.map +1 -1
- package/lib/components/AppBar/index.d.ts +1 -1
- package/lib/components/AppBar/index.d.ts.map +1 -1
- package/lib/components/AttachmentDisplay/AttachmentDisplay.d.ts +182 -0
- package/lib/components/AttachmentDisplay/AttachmentDisplay.d.ts.map +1 -0
- package/lib/components/AttachmentDisplay/AttachmentDisplay.jsx +444 -0
- package/lib/components/AttachmentDisplay/index.d.ts +2 -0
- package/lib/components/AttachmentDisplay/index.d.ts.map +1 -0
- package/lib/components/AttachmentInput/AttachmentInput.d.ts +179 -0
- package/lib/components/AttachmentInput/AttachmentInput.d.ts.map +1 -0
- package/lib/components/AttachmentInput/AttachmentInput.jsx +520 -0
- package/lib/components/AttachmentInput/index.d.ts +2 -0
- package/lib/components/AttachmentInput/index.d.ts.map +1 -0
- package/lib/components/BottomSheet/BottomSheet.d.ts +1 -1
- package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/lib/components/BottomSheet/BottomSheet.jsx +1 -7
- package/lib/components/Callout/Callout.jsx +2 -2
- package/lib/components/Checkbox/Checkbox.jsx +2 -2
- package/lib/components/Chip/Chip.jsx +2 -2
- package/lib/components/ChipTabs/ChipTabs.d.ts +41 -0
- package/lib/components/ChipTabs/ChipTabs.d.ts.map +1 -0
- package/lib/components/ChipTabs/ChipTabs.jsx +39 -0
- package/lib/components/ChipTabs/ChipTabs.namespace.d.ts +2 -0
- package/lib/components/ChipTabs/ChipTabs.namespace.d.ts.map +1 -0
- package/lib/components/ChipTabs/ChipTabs.namespace.js +13 -0
- package/lib/components/ChipTabs/index.d.ts +3 -0
- package/lib/components/ChipTabs/index.d.ts.map +1 -0
- package/lib/components/ContextualFloatingButton/ContextualFloatingButton.d.ts +18 -0
- package/lib/components/ContextualFloatingButton/ContextualFloatingButton.d.ts.map +1 -0
- package/lib/components/ContextualFloatingButton/ContextualFloatingButton.jsx +113 -0
- package/lib/components/ContextualFloatingButton/index.d.ts +2 -0
- package/lib/components/ContextualFloatingButton/index.d.ts.map +1 -0
- package/lib/components/Dialog/Dialog.d.ts +87 -0
- package/lib/components/Dialog/Dialog.d.ts.map +1 -0
- package/lib/components/Dialog/Dialog.jsx +147 -0
- package/lib/components/Dialog/Dialog.namespace.d.ts +2 -0
- package/lib/components/Dialog/Dialog.namespace.d.ts.map +1 -0
- package/lib/components/Dialog/Dialog.namespace.js +18 -0
- package/lib/components/Dialog/index.d.ts +3 -0
- package/lib/components/Dialog/index.d.ts.map +1 -0
- package/lib/components/FloatingActionButton/FloatingActionButton.d.ts +23 -0
- package/lib/components/FloatingActionButton/FloatingActionButton.d.ts.map +1 -0
- package/lib/components/FloatingActionButton/FloatingActionButton.jsx +76 -0
- package/lib/components/FloatingActionButton/FloatingActionButton.namespace.d.ts +2 -0
- package/lib/components/FloatingActionButton/FloatingActionButton.namespace.d.ts.map +1 -0
- package/lib/components/FloatingActionButton/FloatingActionButton.namespace.js +10 -0
- package/lib/components/FloatingActionButton/index.d.ts +3 -0
- package/lib/components/FloatingActionButton/index.d.ts.map +1 -0
- package/lib/components/HelpBubble/HelpBubble.d.ts +67 -0
- package/lib/components/HelpBubble/HelpBubble.d.ts.map +1 -0
- package/lib/components/HelpBubble/HelpBubble.jsx +600 -0
- package/lib/components/HelpBubble/HelpBubble.namespace.d.ts +2 -0
- package/lib/components/HelpBubble/HelpBubble.namespace.d.ts.map +1 -0
- package/lib/components/HelpBubble/HelpBubble.namespace.js +18 -0
- package/lib/components/HelpBubble/index.d.ts +3 -0
- package/lib/components/HelpBubble/index.d.ts.map +1 -0
- package/lib/components/Icon/Icon.d.ts +1 -0
- package/lib/components/Icon/Icon.d.ts.map +1 -1
- package/lib/components/Icon/Icon.jsx +2 -1
- package/lib/components/IdentityPlaceholder/IdentityPlaceholder.d.ts +28 -0
- package/lib/components/IdentityPlaceholder/IdentityPlaceholder.d.ts.map +1 -0
- package/lib/components/IdentityPlaceholder/IdentityPlaceholder.jsx +37 -0
- package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.d.ts +2 -0
- package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.d.ts.map +1 -0
- package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.js +9 -0
- package/lib/components/IdentityPlaceholder/identity-placeholder-business.js +4 -0
- package/lib/components/IdentityPlaceholder/identity-placeholder-person.js +4 -0
- package/lib/components/IdentityPlaceholder/index.d.ts +3 -0
- package/lib/components/IdentityPlaceholder/index.d.ts.map +1 -0
- package/lib/components/InputButton/InputButton.d.ts +2 -0
- package/lib/components/InputButton/InputButton.d.ts.map +1 -1
- package/lib/components/InputButton/InputButton.jsx +40 -8
- package/lib/components/List/List.d.ts +1 -1
- package/lib/components/List/List.d.ts.map +1 -1
- package/lib/components/List/List.jsx +26 -12
- package/lib/components/Menu/Menu.d.ts.map +1 -1
- package/lib/components/Menu/Menu.jsx +15 -5
- package/lib/components/PageBanner/PageBanner.d.ts +3 -0
- package/lib/components/PageBanner/PageBanner.d.ts.map +1 -1
- package/lib/components/PageBanner/PageBanner.jsx +49 -11
- package/lib/components/QuantityPicker/QuantityPicker.d.ts +53 -0
- package/lib/components/QuantityPicker/QuantityPicker.d.ts.map +1 -0
- package/lib/components/QuantityPicker/QuantityPicker.jsx +282 -0
- package/lib/components/QuantityPicker/QuantityPicker.namespace.d.ts +2 -0
- package/lib/components/QuantityPicker/QuantityPicker.namespace.d.ts.map +1 -0
- package/lib/components/QuantityPicker/QuantityPicker.namespace.js +11 -0
- package/lib/components/QuantityPicker/index.d.ts +3 -0
- package/lib/components/QuantityPicker/index.d.ts.map +1 -0
- package/lib/components/RadioGroup/RadioGroup.jsx +2 -2
- package/lib/components/ReactionButton/ReactionButton.d.ts +1 -1
- package/lib/components/ReactionButton/ReactionButton.d.ts.map +1 -1
- package/lib/components/ReactionButton/ReactionButton.jsx +22 -13
- package/lib/components/ScrollFog/ScrollFog.d.ts +41 -0
- package/lib/components/ScrollFog/ScrollFog.d.ts.map +1 -0
- package/lib/components/ScrollFog/ScrollFog.jsx +65 -0
- package/lib/components/ScrollFog/index.d.ts +2 -0
- package/lib/components/ScrollFog/index.d.ts.map +1 -0
- package/lib/components/SegmentedControl/SegmentedControl.jsx +2 -2
- package/lib/components/Select/Select.d.ts +112 -0
- package/lib/components/Select/Select.d.ts.map +1 -0
- package/lib/components/Select/Select.jsx +885 -0
- package/lib/components/Select/Select.namespace.d.ts +2 -0
- package/lib/components/Select/Select.namespace.d.ts.map +1 -0
- package/lib/components/Select/Select.namespace.js +23 -0
- package/lib/components/Select/index.d.ts +3 -0
- package/lib/components/Select/index.d.ts.map +1 -0
- package/lib/components/Skeleton/Skeleton.d.ts +15 -0
- package/lib/components/Skeleton/Skeleton.d.ts.map +1 -0
- package/lib/components/Skeleton/Skeleton.jsx +18 -0
- package/lib/components/Skeleton/index.d.ts +2 -0
- package/lib/components/Skeleton/index.d.ts.map +1 -0
- package/lib/components/Slider/Slider.d.ts +101 -0
- package/lib/components/Slider/Slider.d.ts.map +1 -0
- package/lib/components/Slider/Slider.jsx +573 -0
- package/lib/components/Slider/Slider.namespace.d.ts +2 -0
- package/lib/components/Slider/Slider.namespace.d.ts.map +1 -0
- package/lib/components/Slider/Slider.namespace.js +19 -0
- package/lib/components/Slider/Slider.utils.d.ts +19 -0
- package/lib/components/Slider/Slider.utils.d.ts.map +1 -0
- package/lib/components/Slider/Slider.utils.js +106 -0
- package/lib/components/Slider/index.d.ts +3 -0
- package/lib/components/Slider/index.d.ts.map +1 -0
- package/lib/components/SwipeableMenuSheet/SwipeableMenuSheet.d.ts.map +1 -1
- package/lib/components/SwipeableMenuSheet/SwipeableMenuSheet.jsx +22 -8
- package/lib/components/Switch/Switch.d.ts.map +1 -1
- package/lib/components/Switch/Switch.jsx +5 -3
- package/lib/components/Tabs/Tabs.d.ts +34 -2
- package/lib/components/Tabs/Tabs.d.ts.map +1 -1
- package/lib/components/Tabs/Tabs.jsx +186 -56
- package/lib/components/Tabs/Tabs.utils.d.ts +14 -1
- package/lib/components/Tabs/Tabs.utils.d.ts.map +1 -1
- package/lib/components/Tabs/Tabs.utils.js +24 -3
- package/lib/components/Tabs/index.d.ts +1 -1
- package/lib/components/Tabs/index.d.ts.map +1 -1
- package/lib/components/index.d.ts +14 -0
- package/lib/components/index.d.ts.map +1 -1
- package/lib/hooks/useControllableState.d.ts +1 -14
- package/lib/hooks/useControllableState.d.ts.map +1 -1
- package/lib/hooks/usePressTap.d.ts +1 -27
- package/lib/hooks/usePressTap.d.ts.map +1 -1
- package/lib/index.js +29 -6
- package/lib/{hooks/useControllableState.js → packages/lynx-react-headless/use-controllable-state/lib/index.js} +2 -3
- package/lib/{hooks/usePressTap.js → packages/lynx-react-headless/use-press-tap/lib/index.js} +13 -10
- package/lib/types.d.ts +1 -0
- package/lib/types.d.ts.map +1 -1
- package/package.json +9 -5
- package/src/components/AlertDialog/AlertDialog.namespace.ts +22 -0
- package/src/components/AlertDialog/AlertDialog.test.tsx +226 -0
- package/src/components/AlertDialog/AlertDialog.tsx +374 -0
- package/src/components/AlertDialog/index.ts +24 -0
- package/src/components/AppBar/AppBar.namespace.ts +1 -0
- package/src/components/AppBar/AppBar.test.tsx +231 -0
- package/src/components/AppBar/AppBar.tsx +105 -13
- package/src/components/AppBar/index.ts +1 -0
- package/src/components/AttachmentDisplay/AttachmentDisplay.tsx +886 -0
- package/src/components/AttachmentDisplay/index.ts +48 -0
- package/src/components/AttachmentInput/AttachmentInput.tsx +1001 -0
- package/src/components/AttachmentInput/index.ts +49 -0
- package/src/components/BottomSheet/BottomSheet.test.tsx +22 -7
- package/src/components/BottomSheet/BottomSheet.tsx +1 -19
- package/src/components/ChipTabs/ChipTabs.namespace.ts +14 -0
- package/src/components/ChipTabs/ChipTabs.tsx +119 -0
- package/src/components/ChipTabs/index.ts +16 -0
- package/src/components/ContextualFloatingButton/ContextualFloatingButton.test.tsx +63 -0
- package/src/components/ContextualFloatingButton/ContextualFloatingButton.tsx +232 -0
- package/src/components/ContextualFloatingButton/index.ts +4 -0
- package/src/components/Dialog/Dialog.namespace.ts +24 -0
- package/src/components/Dialog/Dialog.test.tsx +172 -0
- package/src/components/Dialog/Dialog.tsx +373 -0
- package/src/components/Dialog/index.ts +26 -0
- package/src/components/FloatingActionButton/FloatingActionButton.namespace.ts +8 -0
- package/src/components/FloatingActionButton/FloatingActionButton.test.tsx +59 -0
- package/src/components/FloatingActionButton/FloatingActionButton.tsx +207 -0
- package/src/components/FloatingActionButton/index.ts +10 -0
- package/src/components/HelpBubble/HelpBubble.namespace.ts +24 -0
- package/src/components/HelpBubble/HelpBubble.tsx +998 -0
- package/src/components/HelpBubble/index.ts +26 -0
- package/src/components/Icon/Icon.test.tsx +0 -3
- package/src/components/Icon/Icon.tsx +11 -1
- package/src/components/IdentityPlaceholder/IdentityPlaceholder.namespace.ts +6 -0
- package/src/components/IdentityPlaceholder/IdentityPlaceholder.tsx +103 -0
- package/src/components/IdentityPlaceholder/assets.d.ts +4 -0
- package/src/components/IdentityPlaceholder/identity-placeholder-business.webp +0 -0
- package/src/components/IdentityPlaceholder/identity-placeholder-person.webp +0 -0
- package/src/components/IdentityPlaceholder/index.ts +8 -0
- package/src/components/InputButton/InputButton.test.tsx +128 -1
- package/src/components/InputButton/InputButton.tsx +60 -9
- package/src/components/List/List.test.tsx +54 -2
- package/src/components/List/List.tsx +43 -10
- package/src/components/Menu/Menu.tsx +14 -4
- package/src/components/Menu/__tests__/Menu.test.tsx +34 -0
- package/src/components/PageBanner/PageBanner.test.tsx +110 -3
- package/src/components/PageBanner/PageBanner.tsx +83 -19
- package/src/components/QuantityPicker/QuantityPicker.namespace.ts +10 -0
- package/src/components/QuantityPicker/QuantityPicker.test.tsx +235 -0
- package/src/components/QuantityPicker/QuantityPicker.tsx +605 -0
- package/src/components/QuantityPicker/index.ts +14 -0
- package/src/components/ReactionButton/ReactionButton.feedback.test.tsx +50 -0
- package/src/components/ReactionButton/ReactionButton.test.tsx +5 -3
- package/src/components/ReactionButton/ReactionButton.tsx +20 -12
- package/src/components/ScrollFog/ScrollFog.test.tsx +222 -0
- package/src/components/ScrollFog/ScrollFog.tsx +172 -0
- package/src/components/ScrollFog/index.ts +1 -0
- package/src/components/Select/Select.namespace.ts +34 -0
- package/src/components/Select/Select.test.tsx +297 -0
- package/src/components/Select/Select.tsx +1483 -0
- package/src/components/Select/index.ts +39 -0
- package/src/components/Skeleton/Skeleton.test.tsx +64 -0
- package/src/components/Skeleton/Skeleton.tsx +45 -0
- package/src/components/Skeleton/index.ts +1 -0
- package/src/components/Slider/Slider.namespace.ts +26 -0
- package/src/components/Slider/Slider.test.tsx +260 -0
- package/src/components/Slider/Slider.tsx +897 -0
- package/src/components/Slider/Slider.utils.ts +167 -0
- package/src/components/Slider/index.ts +30 -0
- package/src/components/SwipeableMenuSheet/SwipeableMenuSheet.test.tsx +21 -1
- package/src/components/SwipeableMenuSheet/SwipeableMenuSheet.tsx +30 -7
- package/src/components/Switch/Switch.tsx +14 -13
- package/src/components/Tabs/Tabs.test.tsx +218 -1
- package/src/components/Tabs/Tabs.tsx +346 -75
- package/src/components/Tabs/Tabs.utils.ts +51 -1
- package/src/components/Tabs/index.ts +17 -1
- package/src/components/index.ts +14 -0
- package/src/hooks/useControllableState.ts +1 -43
- package/src/hooks/usePressTap.test.ts +23 -11
- package/src/hooks/usePressTap.ts +1 -60
- package/src/types.ts +1 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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
|
-
} =
|
|
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
|
|
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
|
-
|
|
287
|
-
}, [selectedPagerIndex, selectedOffset]);
|
|
347
|
+
}, [selectedPagerIndex]);
|
|
288
348
|
|
|
289
349
|
const contextValue = React.useMemo<TabsContextValue>(
|
|
290
350
|
() => ({
|
|
291
351
|
value,
|
|
292
352
|
visualValue,
|
|
293
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
|
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 =
|
|
379
|
-
const
|
|
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:
|
|
389
|
-
[ref
|
|
614
|
+
() => mergeProps({ ref: scrollRef }, { ref: ref as LynxViewRef }).ref,
|
|
615
|
+
[ref],
|
|
390
616
|
);
|
|
391
617
|
|
|
392
618
|
return (
|
|
393
619
|
<scroll-view
|
|
394
|
-
{...mergeProps(
|
|
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
|
|
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
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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 =
|
|
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
|
-
|
|
508
|
-
<
|
|
509
|
-
|
|
510
|
-
|
|
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
|
|
526
|
-
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
-
[
|
|
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,
|