@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.
Files changed (43) hide show
  1. package/lib/commonjs/components/ScrollShadow/index.js +82 -0
  2. package/lib/commonjs/components/ScrollShadow/index.js.map +1 -0
  3. package/lib/commonjs/components/ScrollShadow/styles.js +54 -0
  4. package/lib/commonjs/components/ScrollShadow/styles.js.map +1 -0
  5. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +83 -0
  6. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -0
  7. package/lib/commonjs/components/Tabs/index.js +525 -0
  8. package/lib/commonjs/components/Tabs/index.js.map +1 -0
  9. package/lib/commonjs/components/Tabs/styles.js +333 -0
  10. package/lib/commonjs/components/Tabs/styles.js.map +1 -0
  11. package/lib/commonjs/components/index.js +24 -0
  12. package/lib/commonjs/components/index.js.map +1 -1
  13. package/lib/module/components/ScrollShadow/index.js +78 -0
  14. package/lib/module/components/ScrollShadow/index.js.map +1 -0
  15. package/lib/module/components/ScrollShadow/styles.js +50 -0
  16. package/lib/module/components/ScrollShadow/styles.js.map +1 -0
  17. package/lib/module/components/ScrollShadow/useScrollShadow.js +79 -0
  18. package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -0
  19. package/lib/module/components/Tabs/index.js +520 -0
  20. package/lib/module/components/Tabs/index.js.map +1 -0
  21. package/lib/module/components/Tabs/styles.js +330 -0
  22. package/lib/module/components/Tabs/styles.js.map +1 -0
  23. package/lib/module/components/index.js +2 -0
  24. package/lib/module/components/index.js.map +1 -1
  25. package/lib/typescript/components/ScrollShadow/index.d.ts +29 -0
  26. package/lib/typescript/components/ScrollShadow/index.d.ts.map +1 -0
  27. package/lib/typescript/components/ScrollShadow/styles.d.ts +12 -0
  28. package/lib/typescript/components/ScrollShadow/styles.d.ts.map +1 -0
  29. package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts +17 -0
  30. package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -0
  31. package/lib/typescript/components/Tabs/index.d.ts +48 -0
  32. package/lib/typescript/components/Tabs/index.d.ts.map +1 -0
  33. package/lib/typescript/components/Tabs/styles.d.ts +107 -0
  34. package/lib/typescript/components/Tabs/styles.d.ts.map +1 -0
  35. package/lib/typescript/components/index.d.ts +2 -0
  36. package/lib/typescript/components/index.d.ts.map +1 -1
  37. package/package.json +5 -5
  38. package/src/components/ScrollShadow/index.tsx +119 -0
  39. package/src/components/ScrollShadow/styles.ts +57 -0
  40. package/src/components/ScrollShadow/useScrollShadow.ts +110 -0
  41. package/src/components/Tabs/index.tsx +694 -0
  42. package/src/components/Tabs/styles.ts +340 -0
  43. 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.121",
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": "^5.2.6",
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.121",
80
- "@cdx-ui/icons": "0.0.1-beta.121",
81
- "@cdx-ui/utils": "0.0.1-beta.121"
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
+ }