@cdx-ui/components 0.0.1-beta.121 → 0.0.1-beta.123
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/commonjs/components/ScrollShadow/index.js +82 -0
- package/lib/commonjs/components/ScrollShadow/index.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/styles.js +54 -0
- package/lib/commonjs/components/ScrollShadow/styles.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +83 -0
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -0
- package/lib/commonjs/components/Tabs/index.js +525 -0
- package/lib/commonjs/components/Tabs/index.js.map +1 -0
- package/lib/commonjs/components/Tabs/styles.js +333 -0
- package/lib/commonjs/components/Tabs/styles.js.map +1 -0
- package/lib/commonjs/components/index.js +24 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/module/components/ScrollShadow/index.js +78 -0
- package/lib/module/components/ScrollShadow/index.js.map +1 -0
- package/lib/module/components/ScrollShadow/styles.js +50 -0
- package/lib/module/components/ScrollShadow/styles.js.map +1 -0
- package/lib/module/components/ScrollShadow/useScrollShadow.js +79 -0
- package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -0
- package/lib/module/components/Tabs/index.js +520 -0
- package/lib/module/components/Tabs/index.js.map +1 -0
- package/lib/module/components/Tabs/styles.js +330 -0
- package/lib/module/components/Tabs/styles.js.map +1 -0
- package/lib/module/components/index.js +2 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/typescript/components/ScrollShadow/index.d.ts +29 -0
- package/lib/typescript/components/ScrollShadow/index.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/styles.d.ts +12 -0
- package/lib/typescript/components/ScrollShadow/styles.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts +17 -0
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -0
- package/lib/typescript/components/Tabs/index.d.ts +48 -0
- package/lib/typescript/components/Tabs/index.d.ts.map +1 -0
- package/lib/typescript/components/Tabs/styles.d.ts +107 -0
- package/lib/typescript/components/Tabs/styles.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +2 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/components/ScrollShadow/index.tsx +119 -0
- package/src/components/ScrollShadow/styles.ts +57 -0
- package/src/components/ScrollShadow/useScrollShadow.ts +110 -0
- package/src/components/Tabs/index.tsx +694 -0
- package/src/components/Tabs/styles.ts +340 -0
- package/src/components/index.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdx-ui/components",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.123",
|
|
4
4
|
"main": "lib/commonjs/index.js",
|
|
5
5
|
"module": "lib/module/index.js",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -72,13 +72,13 @@
|
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@gorhom/bottom-sheet": "
|
|
75
|
+
"@gorhom/bottom-sheet": "5.2.14",
|
|
76
76
|
"class-variance-authority": "^0.7.1",
|
|
77
77
|
"react-native-reanimated-carousel": "5.0.0-beta.5",
|
|
78
78
|
"uniwind": "1.10.0",
|
|
79
|
-
"@cdx-ui/primitives": "0.0.1-beta.
|
|
80
|
-
"@cdx-ui/
|
|
81
|
-
"@cdx-ui/
|
|
79
|
+
"@cdx-ui/primitives": "0.0.1-beta.123",
|
|
80
|
+
"@cdx-ui/utils": "0.0.1-beta.123",
|
|
81
|
+
"@cdx-ui/icons": "0.0.1-beta.123"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@react-navigation/bottom-tabs": "*",
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Children, cloneElement, isValidElement, type ReactNode } from 'react';
|
|
2
|
+
import { StyleSheet, View, type ViewStyle } from 'react-native';
|
|
3
|
+
import { cn } from '@cdx-ui/utils';
|
|
4
|
+
import {
|
|
5
|
+
scrollShadowContainerVariants,
|
|
6
|
+
scrollShadowOverlayVariants,
|
|
7
|
+
scrollShadowVariants,
|
|
8
|
+
type ScrollShadowVariantProps,
|
|
9
|
+
} from './styles';
|
|
10
|
+
import { useScrollShadow, type ScrollShadowCapability } from './useScrollShadow';
|
|
11
|
+
|
|
12
|
+
export type { ScrollShadowCapability };
|
|
13
|
+
|
|
14
|
+
export interface ScrollShadowProps extends ScrollShadowVariantProps {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
className?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Tailwind `from-*` utility for edge overlays — match the surface behind the scroll area.
|
|
19
|
+
* Pair with a low opacity (e.g. `from-surface-secondary/40`) for a light fade.
|
|
20
|
+
* @default 'from-background/35'
|
|
21
|
+
*/
|
|
22
|
+
fadeFromClassName?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Shadow fade size in pixels.
|
|
25
|
+
* @default 64
|
|
26
|
+
*/
|
|
27
|
+
size?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Scroll offset before showing edge shadows.
|
|
30
|
+
* @default 0
|
|
31
|
+
*/
|
|
32
|
+
offset?: number;
|
|
33
|
+
onCapabilityChange?: (capability: ScrollShadowCapability) => void;
|
|
34
|
+
style?: ViewStyle;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ScrollShadowChildProps {
|
|
38
|
+
className?: string;
|
|
39
|
+
style?: ViewStyle;
|
|
40
|
+
onLayout?: (event: Parameters<ReturnType<typeof useScrollShadow>['handleLayout']>[0]) => void;
|
|
41
|
+
onContentSizeChange?: (width: number, height: number) => void;
|
|
42
|
+
onScroll?: (event: Parameters<ReturnType<typeof useScrollShadow>['handleScroll']>[0]) => void;
|
|
43
|
+
ref?: React.Ref<unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function ScrollShadow({
|
|
47
|
+
children,
|
|
48
|
+
className,
|
|
49
|
+
fadeFromClassName = 'from-background/35',
|
|
50
|
+
hideScrollBar = false,
|
|
51
|
+
orientation = 'horizontal',
|
|
52
|
+
offset = 0,
|
|
53
|
+
onCapabilityChange,
|
|
54
|
+
size = 64,
|
|
55
|
+
style,
|
|
56
|
+
}: ScrollShadowProps) {
|
|
57
|
+
const resolvedOrientation = orientation ?? 'horizontal';
|
|
58
|
+
const child = Children.only(children);
|
|
59
|
+
|
|
60
|
+
if (!isValidElement<ScrollShadowChildProps>(child)) {
|
|
61
|
+
throw new Error('ScrollShadow expects a single React element child.');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { capability, handleContentSizeChange, handleLayout, handleScroll } = useScrollShadow({
|
|
65
|
+
offset,
|
|
66
|
+
onCapabilityChange,
|
|
67
|
+
orientation: resolvedOrientation,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const overlayFade = cn(fadeFromClassName, 'to-transparent');
|
|
71
|
+
const overlayAxisSize = resolvedOrientation === 'horizontal' ? { width: size } : { height: size };
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<View className={cn(scrollShadowContainerVariants(), className)} style={style}>
|
|
75
|
+
{cloneElement(child, {
|
|
76
|
+
className: cn(scrollShadowVariants({ hideScrollBar }), child.props.className),
|
|
77
|
+
onContentSizeChange: (width: number, height: number) => {
|
|
78
|
+
handleContentSizeChange(width, height);
|
|
79
|
+
child.props.onContentSizeChange?.(width, height);
|
|
80
|
+
},
|
|
81
|
+
onLayout: (event) => {
|
|
82
|
+
handleLayout(event);
|
|
83
|
+
child.props.onLayout?.(event);
|
|
84
|
+
},
|
|
85
|
+
onScroll: (event) => {
|
|
86
|
+
handleScroll(event);
|
|
87
|
+
child.props.onScroll?.(event);
|
|
88
|
+
},
|
|
89
|
+
style: StyleSheet.flatten([child.props.style]),
|
|
90
|
+
})}
|
|
91
|
+
{capability.canScrollBefore ? (
|
|
92
|
+
<View
|
|
93
|
+
pointerEvents="none"
|
|
94
|
+
className={cn(
|
|
95
|
+
scrollShadowOverlayVariants({
|
|
96
|
+
orientation: resolvedOrientation,
|
|
97
|
+
edge: 'start',
|
|
98
|
+
}),
|
|
99
|
+
overlayFade,
|
|
100
|
+
)}
|
|
101
|
+
style={overlayAxisSize}
|
|
102
|
+
/>
|
|
103
|
+
) : null}
|
|
104
|
+
{capability.canScrollAfter ? (
|
|
105
|
+
<View
|
|
106
|
+
pointerEvents="none"
|
|
107
|
+
className={cn(
|
|
108
|
+
scrollShadowOverlayVariants({
|
|
109
|
+
orientation: resolvedOrientation,
|
|
110
|
+
edge: 'end',
|
|
111
|
+
}),
|
|
112
|
+
overlayFade,
|
|
113
|
+
)}
|
|
114
|
+
style={overlayAxisSize}
|
|
115
|
+
/>
|
|
116
|
+
) : null}
|
|
117
|
+
</View>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
export const scrollShadowVariants = cva([], {
|
|
4
|
+
variants: {
|
|
5
|
+
orientation: {
|
|
6
|
+
horizontal: '',
|
|
7
|
+
vertical: '',
|
|
8
|
+
},
|
|
9
|
+
hideScrollBar: {
|
|
10
|
+
true: 'web:[scrollbar-width:none] web:[&::-webkit-scrollbar]:hidden',
|
|
11
|
+
false: '',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: {
|
|
15
|
+
orientation: 'horizontal',
|
|
16
|
+
hideScrollBar: false,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const scrollShadowContainerVariants = cva('relative min-w-0 w-full');
|
|
21
|
+
|
|
22
|
+
export const scrollShadowOverlayVariants = cva('pointer-events-none absolute z-10', {
|
|
23
|
+
variants: {
|
|
24
|
+
orientation: {
|
|
25
|
+
horizontal: '',
|
|
26
|
+
vertical: '',
|
|
27
|
+
},
|
|
28
|
+
edge: {
|
|
29
|
+
start: '',
|
|
30
|
+
end: '',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
compoundVariants: [
|
|
34
|
+
{
|
|
35
|
+
orientation: 'horizontal',
|
|
36
|
+
edge: 'start',
|
|
37
|
+
className: 'left-0 top-0 bottom-0 bg-gradient-to-r',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
orientation: 'horizontal',
|
|
41
|
+
edge: 'end',
|
|
42
|
+
className: 'right-0 top-0 bottom-0 bg-gradient-to-l',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
orientation: 'vertical',
|
|
46
|
+
edge: 'start',
|
|
47
|
+
className: 'left-0 right-0 top-0 bg-gradient-to-b',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
orientation: 'vertical',
|
|
51
|
+
edge: 'end',
|
|
52
|
+
className: 'left-0 right-0 bottom-0 bg-gradient-to-t',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export type ScrollShadowVariantProps = VariantProps<typeof scrollShadowVariants>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import type { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface ScrollShadowCapability {
|
|
5
|
+
canScrollBefore: boolean;
|
|
6
|
+
canScrollAfter: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface UseScrollShadowOptions {
|
|
10
|
+
orientation?: 'horizontal' | 'vertical';
|
|
11
|
+
offset?: number;
|
|
12
|
+
onCapabilityChange?: (capability: ScrollShadowCapability) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ScrollMetrics {
|
|
16
|
+
scrollOffset: number;
|
|
17
|
+
viewportSize: number;
|
|
18
|
+
contentSize: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const INITIAL_METRICS: ScrollMetrics = {
|
|
22
|
+
scrollOffset: 0,
|
|
23
|
+
viewportSize: 0,
|
|
24
|
+
contentSize: 0,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function getScrollCapability(metrics: ScrollMetrics, offset: number): ScrollShadowCapability {
|
|
28
|
+
const { scrollOffset, viewportSize, contentSize } = metrics;
|
|
29
|
+
|
|
30
|
+
if (viewportSize <= 0 || contentSize <= viewportSize) {
|
|
31
|
+
return { canScrollBefore: false, canScrollAfter: false };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
canScrollBefore: scrollOffset > offset,
|
|
36
|
+
canScrollAfter: scrollOffset + viewportSize + offset < contentSize,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function useScrollShadow({
|
|
41
|
+
orientation = 'horizontal',
|
|
42
|
+
offset = 0,
|
|
43
|
+
onCapabilityChange,
|
|
44
|
+
}: UseScrollShadowOptions = {}) {
|
|
45
|
+
const metricsRef = useRef<ScrollMetrics>(INITIAL_METRICS);
|
|
46
|
+
const [capability, setCapability] = useState<ScrollShadowCapability>({
|
|
47
|
+
canScrollBefore: false,
|
|
48
|
+
canScrollAfter: false,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const updateCapability = useCallback(() => {
|
|
52
|
+
const nextCapability = getScrollCapability(metricsRef.current, offset);
|
|
53
|
+
|
|
54
|
+
setCapability((prev) => {
|
|
55
|
+
if (
|
|
56
|
+
prev.canScrollBefore === nextCapability.canScrollBefore &&
|
|
57
|
+
prev.canScrollAfter === nextCapability.canScrollAfter
|
|
58
|
+
) {
|
|
59
|
+
return prev;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onCapabilityChange?.(nextCapability);
|
|
63
|
+
return nextCapability;
|
|
64
|
+
});
|
|
65
|
+
}, [offset, onCapabilityChange]);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
updateCapability();
|
|
69
|
+
}, [updateCapability]);
|
|
70
|
+
|
|
71
|
+
const handleLayout = useCallback(
|
|
72
|
+
(event: LayoutChangeEvent) => {
|
|
73
|
+
const { width, height } = event.nativeEvent.layout;
|
|
74
|
+
metricsRef.current.viewportSize = orientation === 'vertical' ? height : width;
|
|
75
|
+
updateCapability();
|
|
76
|
+
},
|
|
77
|
+
[orientation, updateCapability],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const handleContentSizeChange = useCallback(
|
|
81
|
+
(width: number, height: number) => {
|
|
82
|
+
metricsRef.current.contentSize = orientation === 'vertical' ? height : width;
|
|
83
|
+
updateCapability();
|
|
84
|
+
},
|
|
85
|
+
[orientation, updateCapability],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const handleScroll = useCallback(
|
|
89
|
+
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
90
|
+
const { contentOffset, layoutMeasurement, contentSize } = event.nativeEvent;
|
|
91
|
+
|
|
92
|
+
metricsRef.current.scrollOffset =
|
|
93
|
+
orientation === 'vertical' ? contentOffset.y : contentOffset.x;
|
|
94
|
+
metricsRef.current.viewportSize =
|
|
95
|
+
orientation === 'vertical' ? layoutMeasurement.height : layoutMeasurement.width;
|
|
96
|
+
metricsRef.current.contentSize =
|
|
97
|
+
orientation === 'vertical' ? contentSize.height : contentSize.width;
|
|
98
|
+
|
|
99
|
+
updateCapability();
|
|
100
|
+
},
|
|
101
|
+
[orientation, updateCapability],
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
capability,
|
|
106
|
+
handleLayout,
|
|
107
|
+
handleContentSizeChange,
|
|
108
|
+
handleScroll,
|
|
109
|
+
};
|
|
110
|
+
}
|