@udixio/ui-react 1.6.4 → 2.1.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 (44) hide show
  1. package/CHANGELOG.md +51 -3
  2. package/dist/index.cjs +3 -3
  3. package/dist/index.js +1699 -1466
  4. package/dist/lib/config/config.interface.d.ts +4 -0
  5. package/dist/lib/config/config.interface.d.ts.map +1 -0
  6. package/dist/lib/config/define-config.d.ts +4 -0
  7. package/dist/lib/config/define-config.d.ts.map +1 -0
  8. package/dist/lib/config/index.d.ts +3 -0
  9. package/dist/lib/config/index.d.ts.map +1 -0
  10. package/dist/lib/effects/AnimateOnScroll.d.ts +7 -0
  11. package/dist/lib/effects/AnimateOnScroll.d.ts.map +1 -0
  12. package/dist/lib/effects/ThemeProvider.d.ts +3 -2
  13. package/dist/lib/effects/ThemeProvider.d.ts.map +1 -1
  14. package/dist/lib/effects/block-scroll.effect.d.ts +22 -0
  15. package/dist/lib/effects/block-scroll.effect.d.ts.map +1 -0
  16. package/dist/lib/effects/custom-scroll/custom-scroll.effect.d.ts.map +1 -1
  17. package/dist/lib/effects/index.d.ts +1 -0
  18. package/dist/lib/effects/index.d.ts.map +1 -1
  19. package/dist/lib/effects/scrollDriven.d.ts +5 -0
  20. package/dist/lib/effects/scrollDriven.d.ts.map +1 -0
  21. package/dist/lib/effects/smooth-scroll.effect.d.ts +5 -5
  22. package/dist/lib/effects/smooth-scroll.effect.d.ts.map +1 -1
  23. package/dist/lib/index.d.ts +1 -0
  24. package/dist/lib/index.d.ts.map +1 -1
  25. package/dist/lib/styles/fab.style.d.ts.map +1 -1
  26. package/dist/scrollDriven-AP2yWhzi.js +121 -0
  27. package/dist/scrollDriven-DWAu7CR0.cjs +1 -0
  28. package/package.json +5 -3
  29. package/src/lib/config/config.interface.ts +9 -0
  30. package/src/lib/config/define-config.ts +16 -0
  31. package/src/lib/config/index.ts +2 -0
  32. package/src/lib/effects/AnimateOnScroll.ts +267 -0
  33. package/src/lib/effects/ThemeProvider.tsx +78 -52
  34. package/src/lib/effects/block-scroll.effect.tsx +174 -0
  35. package/src/lib/effects/custom-scroll/custom-scroll.effect.tsx +16 -5
  36. package/src/lib/effects/index.ts +1 -0
  37. package/src/lib/effects/scrollDriven.ts +239 -0
  38. package/src/lib/effects/smooth-scroll.effect.tsx +105 -72
  39. package/src/lib/index.ts +1 -0
  40. package/src/lib/styles/card.style.ts +1 -1
  41. package/src/lib/styles/fab.style.ts +9 -17
  42. package/src/lib/styles/slider.style.ts +2 -2
  43. package/src/lib/styles/tab.style.ts +1 -1
  44. package/src/lib/styles/tabs.style.ts +3 -3
@@ -1,91 +1,124 @@
1
- import { ReactNode, useRef, useState } from 'react';
2
- import {
3
- motion,
4
- motionValue,
5
- useMotionValueEvent,
6
- useTransform,
7
- } from 'motion/react';
8
- import { CustomScroll, CustomScrollInterface } from './custom-scroll';
9
- import { classNames, ReactProps } from '../utils';
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { CustomScrollInterface } from './custom-scroll';
3
+ import { ReactProps } from '../utils';
4
+ import { BlockScroll } from './block-scroll.effect';
5
+ import { animate, AnimationPlaybackControls } from 'motion';
10
6
 
11
7
  export const SmoothScroll = ({
12
- children,
13
- transition = '.5s',
8
+ transition,
14
9
  orientation = 'vertical',
15
10
  throttleDuration = 25,
16
- ...restProps
17
11
  }: {
18
- children: ReactNode;
19
- transition?: string;
12
+ transition?: {
13
+ duration?: number;
14
+ };
20
15
  } & ReactProps<CustomScrollInterface>) => {
21
- const [scroll, setScroll] = useState<{
22
- scrollProgress: number;
23
- scrollTotal: number;
24
- scrollVisible: number;
25
- scroll: number;
26
- } | null>(null);
16
+ // Target value (instant), driven by wheel/touch/keyboard or native scroll sync
17
+ const [scrollY, setScrollY] = useState(0);
27
18
 
28
- const scrollProgress = motionValue(scroll?.scrollProgress ?? 0);
19
+ const [el, setEl] = useState<HTMLHtmlElement>();
29
20
 
30
- const transform = useTransform(
31
- scrollProgress,
32
- [0, 1],
33
- [0, 1 - (scroll?.scrollVisible ?? 0) / (scroll?.scrollTotal ?? 0)]
34
- );
21
+ const isScrolling = useRef(false);
22
+ const scrollTimeoutRef = useRef<NodeJS.Timeout>();
23
+ const lastAppliedYRef = useRef(0);
35
24
 
36
- const percentTransform = useTransform(
37
- transform,
38
- (value) => `${-value * 100}%`
39
- );
25
+ useEffect(() => {
26
+ setEl(document as unknown as HTMLHtmlElement);
27
+ const y = document.documentElement.scrollTop;
28
+ setScrollY(y);
29
+ lastAppliedYRef.current = y;
30
+ }, []);
31
+
32
+ // Sync native scroll (e.g., scrollbar, programmatic) back to target after a small delay
33
+ useEffect(() => {
34
+ const onScroll = () => {
35
+ if (isScrolling.current) return;
36
+ setScrollY(document.documentElement.scrollTop);
37
+ };
38
+
39
+ el?.addEventListener('scroll', onScroll as unknown as EventListener);
40
+ return () => {
41
+ if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
42
+ el?.removeEventListener('scroll', onScroll as unknown as EventListener);
43
+ };
44
+ }, [el]);
40
45
 
41
- const handleScroll = (args: {
42
- scrollProgress: number;
43
- scrollTotal: number;
44
- scrollVisible: number;
45
- scroll: number;
46
- }) => {
47
- scrollProgress.set(args.scrollProgress);
48
- if (args.scrollTotal > 0) {
49
- setScroll(args);
46
+ // Drive the spring when target changes
47
+ const currentY = useRef<number | null>();
48
+ const animationRef = useRef<AnimationPlaybackControls | null>(null);
49
+ useEffect(() => {
50
+ const y = scrollY;
51
+
52
+ if (animationRef.current) {
53
+ animationRef.current.stop();
54
+ animationRef.current = null;
50
55
  }
51
- };
52
- const lastChangeTimeRef = useRef<number | null>(null);
53
- useMotionValueEvent(percentTransform, 'change', (value) => {
54
- // Récupérer l'heure actuelle
55
- const now = performance.now();
56
56
 
57
- // Si une heure précédente existe, calculer le delta
58
- if (lastChangeTimeRef.current !== null) {
59
- const deltaTime = now - lastChangeTimeRef.current;
60
- console.log(`Delta temps : ${deltaTime} ms`);
57
+ if (!isScrolling.current) {
58
+ currentY.current = y;
59
+ return;
61
60
  }
61
+ animationRef.current = animate(currentY.current ?? y, y, {
62
+ duration: transition?.duration ?? 0.5,
63
+ ease: 'circOut',
64
+
65
+ onUpdate: (value) => {
66
+ if (scrollTimeoutRef.current) {
67
+ clearTimeout(scrollTimeoutRef.current);
68
+ }
69
+ currentY.current = value;
62
70
 
63
- // Mettre à jour l'heure du dernier changement
64
- lastChangeTimeRef.current = now;
71
+ const html = document.documentElement;
72
+ // Avoid micro-movements causing extra layout work
73
+ const rounded = Math.round(value * 1000) / 1000;
74
+ const last = lastAppliedYRef.current;
75
+ if (Math.abs(rounded - last) < 0.1) return;
76
+ lastAppliedYRef.current = rounded;
65
77
 
66
- console.log(value); // Affiche également la valeur si nécessaire
67
- });
78
+ if (isScrolling.current) {
79
+ html.scrollTo({ top: rounded });
80
+ }
81
+ },
82
+ onComplete: () => {
83
+ scrollTimeoutRef.current = setTimeout(() => {
84
+ isScrolling.current = false;
85
+ }, 300);
86
+ animationRef.current = null;
87
+ },
88
+ });
89
+ return () => {
90
+ // Safety: stop if effect re-runs quickly
91
+ if (animationRef.current) {
92
+ animationRef.current.stop();
93
+ animationRef.current = null;
94
+ }
95
+ };
96
+ }, [scrollY]);
97
+
98
+ if (!el) return null;
68
99
 
69
100
  return (
70
- <CustomScroll
71
- onScroll={handleScroll}
72
- throttleDuration={throttleDuration}
73
- {...restProps}
74
- >
75
- <motion.div
76
- className={classNames('transition-transform ease-out', {
77
- 'w-fit h-full': orientation === 'horizontal',
78
- 'h-fit w-full': orientation === 'vertical',
79
- })}
80
- style={{
81
- transitionDuration: transition,
82
- ...(orientation == 'vertical'
83
- ? { y: percentTransform }
84
- : { x: percentTransform }),
85
- }}
86
- >
87
- {children}
88
- </motion.div>
89
- </CustomScroll>
101
+ <BlockScroll
102
+ touch={false}
103
+ el={el as unknown as HTMLElement}
104
+ onScroll={(scroll) => {
105
+ if (
106
+ 'deltaY' in scroll &&
107
+ scroll.deltaY !== 0 &&
108
+ el &&
109
+ scrollY !== null
110
+ ) {
111
+ let y = scrollY + scroll.deltaY;
112
+ const html = el.querySelector('html');
113
+ if (html) {
114
+ y = Math.min(y, html.scrollHeight - html.clientHeight);
115
+ }
116
+ y = Math.max(y, 0);
117
+ setScrollY(y);
118
+
119
+ isScrolling.current = true;
120
+ }
121
+ }}
122
+ ></BlockScroll>
90
123
  );
91
124
  };
package/src/lib/index.ts CHANGED
@@ -4,3 +4,4 @@ export * from './icon';
4
4
  export * from './interfaces';
5
5
  export * from './styles';
6
6
  export * from './utils';
7
+ export * from './config';
@@ -5,7 +5,7 @@ export const cardStyle = defaultClassNames<CardInterface>(
5
5
  'card',
6
6
  ({ variant, isInteractive }) => ({
7
7
  card: classNames(
8
- 'card group/card rounded-xl overflow-hidden z-10',
8
+ 'group/card rounded-xl overflow-hidden z-10',
9
9
  variant === 'outlined' && 'bg-surface border border-outline-variant',
10
10
  variant === 'elevated' && 'bg-surface-container-low shadow-1',
11
11
  variant === 'filled' && 'bg-surface-container-highest',
@@ -20,6 +20,10 @@ export const fabStyle = defaultClassNames<FabInterface>(
20
20
  variant === 'primary' && 'bg-primary-container',
21
21
  variant === 'secondary' && 'bg-secondary-container',
22
22
  variant === 'tertiary' && 'bg-tertiary-container',
23
+ variant === 'surface' && 'text-primary',
24
+ variant === 'primary' && 'text-on-primary-container',
25
+ variant === 'secondary' && 'text-on-secondary-container',
26
+ variant === 'tertiary' && 'text-on-tertiary-container',
23
27
  ),
24
28
  stateLayer: classNames(
25
29
  'absolute w-full h-full overflow-hidden left-1/2 top-1/2 transform -translate-y-1/2 -translate-x-1/2',
@@ -33,23 +37,11 @@ export const fabStyle = defaultClassNames<FabInterface>(
33
37
  'group-hover:hover-state-on-tertiary-container group-focus-visible:focus-state-on-tertiary-container',
34
38
  ),
35
39
 
36
- icon: classNames(
37
- {
38
- 'size-6': size == 'small' || size == 'medium' || isExtended,
39
- 'size-9': size == 'large' && !isExtended,
40
- },
41
- variant === 'surface' && 'text-primary',
42
- variant === 'primary' && 'text-on-primary-container',
43
- variant === 'secondary' && 'text-on-secondary-container',
44
- variant === 'tertiary' && 'text-on-tertiary-container',
45
- ),
40
+ icon: classNames({
41
+ 'size-6': size == 'small' || size == 'medium' || isExtended,
42
+ 'size-9': size == 'large' && !isExtended,
43
+ }),
46
44
 
47
- label: classNames(
48
- 'text-title-medium text-nowrap',
49
- variant === 'surface' && 'text-primary',
50
- variant === 'primary' && 'text-on-primary-container',
51
- variant === 'secondary' && 'text-on-secondary-container',
52
- variant === 'tertiary' && 'text-on-tertiary-container',
53
- ),
45
+ label: classNames('text-title-medium text-nowrap'),
54
46
  }),
55
47
  );
@@ -5,7 +5,7 @@ export const sliderStyle = defaultClassNames<SliderInterface>(
5
5
  'slider',
6
6
  ({ isChanging }) => ({
7
7
  slider: classNames([
8
- 'relative w-full h-11 flex items-center rounded gap-x-1.5 cursor-pointer',
8
+ 'relative w-full h-11 flex items-center rounded gap-x-1.5 cursor-pointer min-w-32',
9
9
  ]),
10
10
  activeTrack: classNames([
11
11
  'h-4 relative transition-all duration-100 bg-primary overflow-hidden rounded-l-full ',
@@ -23,5 +23,5 @@ export const sliderStyle = defaultClassNames<SliderInterface>(
23
23
  dot: classNames([
24
24
  'h-1 w-1 absolute transform -translate-y-1/2 -translate-x-1/2 top-1/2 rounded-full',
25
25
  ]),
26
- })
26
+ }),
27
27
  );
@@ -6,7 +6,7 @@ export const tabStyle = defaultClassNames<TabInterface>(
6
6
  'tab',
7
7
  ({ isSelected, icon, label, variant }) => ({
8
8
  tab: classNames(
9
- 'bg-surface flex-1 group outline-none flex px-4 justify-center items-center cursor-pointer',
9
+ 'flex-1 group outline-none flex px-4 justify-center items-center cursor-pointer',
10
10
  { 'z-10': isSelected },
11
11
  Boolean(icon && label) && variant === 'primary' && 'h-16',
12
12
  !(Boolean(icon && label) && variant === 'primary') && 'h-12',
@@ -5,9 +5,9 @@ export const tabsStyle = defaultClassNames<TabsInterface>(
5
5
  'tabs',
6
6
  ({ scrollable }) => ({
7
7
  tabs: classNames(
8
- 'border-b border-surface-container-highest',
8
+ 'border-b border-surface-container-highest bg-surface',
9
9
  'flex relative ',
10
- { 'overflow-x-auto': scrollable }
10
+ { 'overflow-x-auto': scrollable },
11
11
  ),
12
- })
12
+ }),
13
13
  );