orn-ui 0.2.7 → 0.2.9

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 (103) hide show
  1. package/AGENTS.md +25 -3
  2. package/README.md +9 -8
  3. package/lib/commonjs/atoms/Gradient.js +111 -0
  4. package/lib/commonjs/atoms/Gradient.js.map +1 -0
  5. package/lib/commonjs/atoms/ShinyText.js +188 -0
  6. package/lib/commonjs/atoms/ShinyText.js.map +1 -0
  7. package/lib/commonjs/atoms/index.js +14 -0
  8. package/lib/commonjs/atoms/index.js.map +1 -1
  9. package/lib/commonjs/organisms/Chart.js +947 -0
  10. package/lib/commonjs/organisms/Chart.js.map +1 -0
  11. package/lib/commonjs/organisms/OptionWheel.js +408 -0
  12. package/lib/commonjs/organisms/OptionWheel.js.map +1 -0
  13. package/lib/commonjs/organisms/Slides.js +406 -0
  14. package/lib/commonjs/organisms/Slides.js.map +1 -0
  15. package/lib/commonjs/organisms/Timeline.js +420 -0
  16. package/lib/commonjs/organisms/Timeline.js.map +1 -0
  17. package/lib/commonjs/organisms/chart/frame.js +373 -0
  18. package/lib/commonjs/organisms/chart/frame.js.map +1 -0
  19. package/lib/commonjs/organisms/chart/marks.js +316 -0
  20. package/lib/commonjs/organisms/chart/marks.js.map +1 -0
  21. package/lib/commonjs/organisms/chart/math.js +468 -0
  22. package/lib/commonjs/organisms/chart/math.js.map +1 -0
  23. package/lib/commonjs/organisms/chart/palette.js +120 -0
  24. package/lib/commonjs/organisms/chart/palette.js.map +1 -0
  25. package/lib/commonjs/organisms/index.js +28 -0
  26. package/lib/commonjs/organisms/index.js.map +1 -1
  27. package/lib/commonjs/organisms/useSlides.js +186 -0
  28. package/lib/commonjs/organisms/useSlides.js.map +1 -0
  29. package/lib/module/atoms/Gradient.js +106 -0
  30. package/lib/module/atoms/Gradient.js.map +1 -0
  31. package/lib/module/atoms/ShinyText.js +183 -0
  32. package/lib/module/atoms/ShinyText.js.map +1 -0
  33. package/lib/module/atoms/index.js +2 -0
  34. package/lib/module/atoms/index.js.map +1 -1
  35. package/lib/module/organisms/Chart.js +942 -0
  36. package/lib/module/organisms/Chart.js.map +1 -0
  37. package/lib/module/organisms/OptionWheel.js +403 -0
  38. package/lib/module/organisms/OptionWheel.js.map +1 -0
  39. package/lib/module/organisms/Slides.js +401 -0
  40. package/lib/module/organisms/Slides.js.map +1 -0
  41. package/lib/module/organisms/Timeline.js +415 -0
  42. package/lib/module/organisms/Timeline.js.map +1 -0
  43. package/lib/module/organisms/chart/frame.js +367 -0
  44. package/lib/module/organisms/chart/frame.js.map +1 -0
  45. package/lib/module/organisms/chart/marks.js +310 -0
  46. package/lib/module/organisms/chart/marks.js.map +1 -0
  47. package/lib/module/organisms/chart/math.js +445 -0
  48. package/lib/module/organisms/chart/math.js.map +1 -0
  49. package/lib/module/organisms/chart/palette.js +114 -0
  50. package/lib/module/organisms/chart/palette.js.map +1 -0
  51. package/lib/module/organisms/index.js +4 -0
  52. package/lib/module/organisms/index.js.map +1 -1
  53. package/lib/module/organisms/useSlides.js +182 -0
  54. package/lib/module/organisms/useSlides.js.map +1 -0
  55. package/lib/typescript/atoms/Gradient.d.ts +37 -0
  56. package/lib/typescript/atoms/Gradient.d.ts.map +1 -0
  57. package/lib/typescript/atoms/ShinyText.d.ts +48 -0
  58. package/lib/typescript/atoms/ShinyText.d.ts.map +1 -0
  59. package/lib/typescript/atoms/index.d.ts +4 -0
  60. package/lib/typescript/atoms/index.d.ts.map +1 -1
  61. package/lib/typescript/organisms/Chart.d.ts +208 -0
  62. package/lib/typescript/organisms/Chart.d.ts.map +1 -0
  63. package/lib/typescript/organisms/OptionWheel.d.ts +78 -0
  64. package/lib/typescript/organisms/OptionWheel.d.ts.map +1 -0
  65. package/lib/typescript/organisms/Slides.d.ts +69 -0
  66. package/lib/typescript/organisms/Slides.d.ts.map +1 -0
  67. package/lib/typescript/organisms/Timeline.d.ts +61 -0
  68. package/lib/typescript/organisms/Timeline.d.ts.map +1 -0
  69. package/lib/typescript/organisms/chart/frame.d.ts +85 -0
  70. package/lib/typescript/organisms/chart/frame.d.ts.map +1 -0
  71. package/lib/typescript/organisms/chart/marks.d.ts +88 -0
  72. package/lib/typescript/organisms/chart/marks.d.ts.map +1 -0
  73. package/lib/typescript/organisms/chart/math.d.ts +161 -0
  74. package/lib/typescript/organisms/chart/math.d.ts.map +1 -0
  75. package/lib/typescript/organisms/chart/palette.d.ts +6 -0
  76. package/lib/typescript/organisms/chart/palette.d.ts.map +1 -0
  77. package/lib/typescript/organisms/index.d.ts +8 -0
  78. package/lib/typescript/organisms/index.d.ts.map +1 -1
  79. package/lib/typescript/organisms/useSlides.d.ts +52 -0
  80. package/lib/typescript/organisms/useSlides.d.ts.map +1 -0
  81. package/llms.txt +6 -5
  82. package/package.json +33 -2
  83. package/registry/chart.json +35 -0
  84. package/registry/gradient.json +18 -0
  85. package/registry/index.json +79 -0
  86. package/registry/manifest.json +42 -0
  87. package/registry/option-wheel.json +19 -0
  88. package/registry/shiny-text.json +19 -0
  89. package/registry/slides.json +24 -0
  90. package/registry/timeline.json +20 -0
  91. package/src/atoms/Gradient.tsx +99 -0
  92. package/src/atoms/ShinyText.tsx +226 -0
  93. package/src/atoms/index.ts +4 -0
  94. package/src/organisms/Chart.tsx +1223 -0
  95. package/src/organisms/OptionWheel.tsx +476 -0
  96. package/src/organisms/Slides.tsx +413 -0
  97. package/src/organisms/Timeline.tsx +467 -0
  98. package/src/organisms/chart/frame.tsx +349 -0
  99. package/src/organisms/chart/marks.tsx +295 -0
  100. package/src/organisms/chart/math.ts +475 -0
  101. package/src/organisms/chart/palette.ts +112 -0
  102. package/src/organisms/index.ts +22 -0
  103. package/src/organisms/useSlides.ts +220 -0
@@ -0,0 +1,220 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import type { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, ScrollView } from 'react-native';
3
+ import { useReduceMotion } from '../atoms/Transition';
4
+
5
+ export type SlidesOrientation = 'horizontal' | 'vertical';
6
+
7
+ export interface UseSlidesOptions {
8
+ total: number;
9
+ orientation: SlidesOrientation;
10
+ loop: boolean;
11
+ autoPlay: boolean;
12
+ interval: number;
13
+ /** Índice controlado; sin él manda el estado interno. */
14
+ index?: number;
15
+ defaultIndex: number;
16
+ onIndexChange?: (index: number) => void;
17
+ }
18
+
19
+ /** Una página del scroll. Los clones del modo infinito repiten el `real` de un vecino. */
20
+ export interface SlidesPage {
21
+ real: number;
22
+ clone: 'head' | 'tail' | null;
23
+ }
24
+
25
+ export interface UseSlidesResult {
26
+ active: number;
27
+ pages: SlidesPage[];
28
+ isHorizontal: boolean;
29
+ isLoop: boolean;
30
+ size: { width: number; height: number };
31
+ /** Largo de una página sobre el eje del scroll. En 0 todavía no se midió. */
32
+ pageSize: number;
33
+ scrollRef: React.RefObject<ScrollView | null>;
34
+ autoPlayPaused: boolean;
35
+ /** El sistema pidió reducir movimiento: no hay autoPlay ni transiciones. */
36
+ motionReduced: boolean;
37
+ goTo: (index: number) => void;
38
+ toggleAutoPlay: () => void;
39
+ onLayout: (event: LayoutChangeEvent) => void;
40
+ onMomentumScrollEnd: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
41
+ onScrollBeginDrag: () => void;
42
+ onScrollEndDrag: () => void;
43
+ }
44
+
45
+ export function clampIndex(value: number, total: number): number {
46
+ if (total === 0) return 0;
47
+ if (value < 0) return 0;
48
+ if (value > total - 1) return total - 1;
49
+ return value;
50
+ }
51
+
52
+ /**
53
+ * useSlides
54
+ * Todo lo que en `Slides` no se ve: qué página está encuadrada, cuándo mover el
55
+ * scroll, cómo se mapean los clones del modo infinito a índices reales, y el
56
+ * reloj del avance automático.
57
+ *
58
+ * Trabaja sólo con índices, nunca con los ítems: así no necesita el genérico
59
+ * `T` del componente, y el UI resuelve `pages[i].real` contra su propio `data`.
60
+ */
61
+ export function useSlides({
62
+ total,
63
+ orientation,
64
+ loop,
65
+ autoPlay,
66
+ interval,
67
+ index,
68
+ defaultIndex,
69
+ onIndexChange,
70
+ }: UseSlidesOptions): UseSlidesResult {
71
+ const motionReduced = useReduceMotion();
72
+ const isHorizontal = orientation === 'horizontal';
73
+ // Un solo elemento no tiene a dónde ciclar: quedaría deslizable entre tres
74
+ // copias de lo mismo.
75
+ const isLoop = loop && total > 1;
76
+
77
+ const [uncontrolled, setUncontrolled] = useState(() => clampIndex(defaultIndex, total));
78
+ const active = clampIndex(index ?? uncontrolled, total);
79
+ const activeRef = useRef(active);
80
+ activeRef.current = active;
81
+
82
+ const [autoPlayPaused, setAutoPlayPaused] = useState(false);
83
+ const [size, setSize] = useState({ width: 0, height: 0 });
84
+ const pageSize = isHorizontal ? size.width : size.height;
85
+
86
+ const scrollRef = useRef<ScrollView>(null);
87
+ /** Página realmente visible (índice dentro de `pages`, clones incluidos). */
88
+ const offsetPageRef = useRef(isLoop ? 1 : 0);
89
+ const didInitRef = useRef(false);
90
+ const lastPageSizeRef = useRef(0);
91
+ const draggingRef = useRef(false);
92
+
93
+ const pages = useMemo<SlidesPage[]>(() => {
94
+ const real = Array.from({ length: total }, (_, i) => ({ real: i, clone: null }));
95
+ if (!isLoop) return real;
96
+ return [{ real: total - 1, clone: 'head' }, ...real, { real: 0, clone: 'tail' }];
97
+ }, [isLoop, total]);
98
+
99
+ const scrollToPage = useCallback(
100
+ (page: number, animated: boolean) => {
101
+ const offset = page * pageSize;
102
+ scrollRef.current?.scrollTo({
103
+ x: isHorizontal ? offset : 0,
104
+ y: isHorizontal ? 0 : offset,
105
+ animated,
106
+ });
107
+ },
108
+ [isHorizontal, pageSize]
109
+ );
110
+
111
+ const goTo = useCallback(
112
+ (next: number) => {
113
+ if (next === activeRef.current) return;
114
+ activeRef.current = next;
115
+ if (index == null) setUncontrolled(next);
116
+ onIndexChange?.(next);
117
+ },
118
+ [index, onIndexChange]
119
+ );
120
+
121
+ // Sólo actúa cuando el índice y la página visible discrepan: un cambio que
122
+ // vino del propio gesto no dispara un segundo scroll encima del que frenó.
123
+ useEffect(() => {
124
+ if (pageSize === 0 || total === 0) return;
125
+ const target = isLoop ? active + 1 : active;
126
+ const resized = lastPageSizeRef.current !== pageSize;
127
+ if (offsetPageRef.current === target && didInitRef.current && !resized) return;
128
+ // Ni el primer posicionamiento ni el reacomodo por rotación son un cambio
129
+ // de slide: animarlos sería un deslizamiento que nadie pidió.
130
+ const animated = didInitRef.current && !resized;
131
+ didInitRef.current = true;
132
+ lastPageSizeRef.current = pageSize;
133
+ offsetPageRef.current = target;
134
+ scrollToPage(target, animated);
135
+ }, [active, isLoop, pageSize, scrollToPage, total]);
136
+
137
+ const onLayout = useCallback((event: LayoutChangeEvent) => {
138
+ const { width, height } = event.nativeEvent.layout;
139
+ setSize((prev) => (prev.width === width && prev.height === height ? prev : { width, height }));
140
+ }, []);
141
+
142
+ const onMomentumScrollEnd = useCallback(
143
+ (event: NativeSyntheticEvent<NativeScrollEvent>) => {
144
+ draggingRef.current = false;
145
+ if (pageSize === 0) return;
146
+ const { contentOffset } = event.nativeEvent;
147
+ const page = Math.round((isHorizontal ? contentOffset.x : contentOffset.y) / pageSize);
148
+
149
+ if (isLoop) {
150
+ if (page === 0) {
151
+ offsetPageRef.current = total;
152
+ scrollToPage(total, false);
153
+ goTo(total - 1);
154
+ return;
155
+ }
156
+ if (page === total + 1) {
157
+ offsetPageRef.current = 1;
158
+ scrollToPage(1, false);
159
+ goTo(0);
160
+ return;
161
+ }
162
+ offsetPageRef.current = page;
163
+ goTo(page - 1);
164
+ return;
165
+ }
166
+
167
+ offsetPageRef.current = page;
168
+ goTo(clampIndex(page, total));
169
+ },
170
+ [goTo, isHorizontal, isLoop, pageSize, scrollToPage, total]
171
+ );
172
+
173
+ const onScrollBeginDrag = useCallback(() => {
174
+ draggingRef.current = true;
175
+ }, []);
176
+
177
+ const onScrollEndDrag = useCallback(() => {
178
+ // Un arrastre corto puede soltarse sin inercia y no emitir
179
+ // momentumScrollEnd: sin esto el autoPlay quedaría pausado para siempre.
180
+ draggingRef.current = false;
181
+ }, []);
182
+
183
+ const toggleAutoPlay = useCallback(() => setAutoPlayPaused((paused) => !paused), []);
184
+
185
+ useEffect(() => {
186
+ if (!autoPlay || autoPlayPaused || motionReduced || total < 2 || pageSize === 0) return;
187
+ const timer = setInterval(() => {
188
+ if (draggingRef.current) return;
189
+ const from = activeRef.current;
190
+ if (from < total - 1) {
191
+ goTo(from + 1);
192
+ return;
193
+ }
194
+ if (!isLoop) return;
195
+ // Última en modo infinito: avanza al clon de la primera en vez de
196
+ // rebobinar; el salto al original lo cierra momentumScrollEnd.
197
+ offsetPageRef.current = total + 1;
198
+ scrollToPage(total + 1, true);
199
+ }, interval);
200
+ return () => clearInterval(timer);
201
+ }, [autoPlay, autoPlayPaused, goTo, interval, isLoop, motionReduced, pageSize, scrollToPage, total]);
202
+
203
+ return {
204
+ active,
205
+ pages,
206
+ isHorizontal,
207
+ isLoop,
208
+ size,
209
+ pageSize,
210
+ scrollRef,
211
+ autoPlayPaused,
212
+ motionReduced,
213
+ goTo,
214
+ toggleAutoPlay,
215
+ onLayout,
216
+ onMomentumScrollEnd,
217
+ onScrollBeginDrag,
218
+ onScrollEndDrag,
219
+ };
220
+ }