@udixio/ui-react 2.3.2 → 2.4.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.
@@ -1 +1 @@
1
- {"version":3,"file":"AnimateOnScroll.d.ts","sourceRoot":"","sources":["../../../src/lib/effects/AnimateOnScroll.ts"],"names":[],"mappings":"AAwHA,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,sBAA2B,GACnC,MAAM,IAAI,CAyIZ;AAGD,eAAO,MAAM,mBAAmB,4BAAsB,CAAC;AACvD,eAAO,MAAM,eAAe,4BAAsB,CAAC"}
1
+ {"version":3,"file":"AnimateOnScroll.d.ts","sourceRoot":"","sources":["../../../src/lib/effects/AnimateOnScroll.ts"],"names":[],"mappings":"AAkLA,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,sBAA2B,GACnC,MAAM,IAAI,CAgIZ;AAGD,eAAO,MAAM,mBAAmB,4BAAsB,CAAC;AACvD,eAAO,MAAM,eAAe,4BAAsB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@udixio/ui-react",
3
- "version": "2.3.2",
3
+ "version": "2.4.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -36,8 +36,8 @@
36
36
  "devDependencies": {
37
37
  "react": "^19.1.1",
38
38
  "react-dom": "^19.1.1",
39
- "@udixio/theme": "2.0.0",
40
- "@udixio/tailwind": "2.3.2"
39
+ "@udixio/theme": "2.1.0",
40
+ "@udixio/tailwind": "2.3.4"
41
41
  },
42
42
  "repository": {
43
43
  "type": "git",
@@ -9,7 +9,7 @@ import { TailwindPlugin } from '@udixio/tailwind';
9
9
 
10
10
  export function defineConfig(configObject: ConfigInterface): ConfigTheme {
11
11
  return defineConfigTheme({
12
- variant: Variants.Fidelity,
12
+ variant: Variants.Udixio,
13
13
  ...configObject,
14
14
  plugins: [new FontPlugin(configObject), new TailwindPlugin(configObject)],
15
15
  });
@@ -42,12 +42,8 @@ function isScrollDrivenCandidate(el: Element): boolean {
42
42
  function isJsObserverCandidate(el: Element): boolean {
43
43
  if (!(el instanceof HTMLElement)) return false;
44
44
  const cls = el.classList;
45
- const hasAnimation = Array.from(cls).some((className) =>
46
- className.startsWith('anim-'),
47
- );
48
- if (!hasAnimation) return false;
49
- // Not scroll-driven
50
- return !isScrollDrivenCandidate(el);
45
+
46
+ return Array.from(cls).some((className) => className.startsWith('anim-'));
51
47
  }
52
48
 
53
49
  function hydrateElement(el: HTMLElement, prefix: string): void {
@@ -101,7 +97,9 @@ function queryScrollDrivenCandidates(
101
97
  ): HTMLElement[] {
102
98
  // Select any elements that have an animation class and are marked as scroll-driven
103
99
  const animated = Array.from(
104
- root.querySelectorAll<HTMLElement>(`[class*="${prefix}-"]`),
100
+ root.querySelectorAll<HTMLElement>(
101
+ `[class*="${prefix}-"][class*="-scroll"]`,
102
+ ),
105
103
  );
106
104
  return animated.filter((el) => isScrollDrivenCandidate(el));
107
105
  }
@@ -110,14 +108,74 @@ function queryJsObserverCandidates(
110
108
  root: ParentNode = document,
111
109
  prefix: string,
112
110
  ): HTMLElement[] {
113
- // All anim-in/out that are NOT scroll-driven
111
+ // Observe any element that has `${prefix}-in` or `${prefix}-out` even if it's scroll-driven.
112
+ // Additionally, observe elements that have at least one non-scroll `${prefix}-*` class
113
+ // (e.g., `anim-fade`, `anim-scale-150`) even if they also include scroll-driven classes.
114
+ // This ensures default IN animations are triggered.
114
115
  const animated = Array.from(
115
- root.querySelectorAll<HTMLElement>(`[class*="anim-"]`),
116
+ root.querySelectorAll<HTMLElement>(`[class*="${prefix}-"]`),
116
117
  );
117
118
 
118
- return animated.filter((el) => !isScrollDrivenCandidate(el));
119
+ const reserved = new Set([
120
+ `${prefix}-run`,
121
+ `${prefix}-in`,
122
+ `${prefix}-out`,
123
+ `${prefix}-in-run`,
124
+ `${prefix}-out-run`,
125
+ `${prefix}-paused`,
126
+ `${prefix}-timeline`,
127
+ `${prefix}-timeline-inline`,
128
+ `${prefix}-timeline-block`,
129
+ `${prefix}-timeline-x`,
130
+ `${prefix}-timeline-y`,
131
+ `${prefix}-scroll`,
132
+ ]);
133
+
134
+ return animated.filter((el) => {
135
+ if (!(el instanceof HTMLElement)) return false;
136
+ const cls = el.classList;
137
+ const hasInOut =
138
+ cls.contains(`${prefix}-in`) || cls.contains(`${prefix}-out`);
139
+ if (hasInOut) return true;
140
+
141
+ // Check if element has any non-scroll animation class (not in reserved set and not containing "scroll")
142
+ const hasNonScrollAnim = Array.from(cls).some(
143
+ (c) =>
144
+ c.startsWith(`${prefix}-`) && !c.includes('scroll') && !reserved.has(c),
145
+ );
146
+
147
+ if (hasNonScrollAnim) return true;
148
+
149
+ // Otherwise only observe if it's not scroll-driven at all
150
+ return !isScrollDrivenCandidate(el);
151
+ });
119
152
  }
120
153
 
154
+ // Utility: identify presence of in/out classes
155
+ function hasInOutClass(cls: DOMTokenList, prefix: string): boolean {
156
+ return cls.contains(`${prefix}-in`) || cls.contains(`${prefix}-out`);
157
+ }
158
+
159
+ // Utility: set run flags for a given direction ("in" or "out"), always ensuring generic run flag exists
160
+ function setRunFlag(el: HTMLElement, prefix: string, dir: 'in' | 'out'): void {
161
+ el.setAttribute(`data-${prefix}-run`, ``);
162
+ el.setAttribute(`data-${prefix}-${dir}-run`, ``);
163
+ }
164
+
165
+ // Utility: reset run flags and restart animation timeline without changing computed styles
166
+ function resetRunFlags(el: HTMLElement, prefix: string): void {
167
+ const currentAnimationName = el.style.animationName;
168
+ el.style.animationName = 'none';
169
+ el.removeAttribute(`data-${prefix}-run`);
170
+ el.removeAttribute(`data-${prefix}-in-run`);
171
+ el.removeAttribute(`data-${prefix}-out-run`);
172
+ void (el as HTMLElement).offsetWidth; // reflow to restart animations
173
+ el.style.animationName = currentAnimationName;
174
+ }
175
+
176
+ // IO thresholds centralized for clarity
177
+ const IO_THRESHOLD: number[] = [0, 0.2];
178
+
121
179
  export type AnimateOnScrollOptions = {
122
180
  prefix?: string;
123
181
  once?: boolean;
@@ -144,29 +202,20 @@ export function initAnimateOnScroll(
144
202
 
145
203
  if (!isJsObserverCandidate(el)) continue;
146
204
 
147
- const cls = el.classList;
148
- const isOut = cls.contains(`${prefix}-out`);
149
- const isIn = !isOut;
205
+ const isOut = el.classList.contains(`${prefix}-out`);
150
206
 
151
- if (isIn && entry.isIntersecting) {
152
- el.setAttribute(`data-${prefix}-in-run`, ``);
207
+ if (!isOut && entry.isIntersecting) {
208
+ setRunFlag(el, prefix, 'in');
153
209
  if (once) io.unobserve(el);
154
210
  } else if (isOut && !entry.isIntersecting) {
155
- el.setAttribute(`data-${prefix}-out-run`, ``);
211
+ setRunFlag(el, prefix, 'out');
156
212
  if (once) io.unobserve(el);
157
- } else {
158
- if (!once) {
159
- const currentAnimationName = el.style.animationName;
160
- el.style.animationName = 'none';
161
- el.removeAttribute(`data-${prefix}-in-run`);
162
- el.removeAttribute(`data-${prefix}-out-run`);
163
- void el.offsetWidth; // reflow
164
- el.style.animationName = currentAnimationName;
165
- }
213
+ } else if (!once) {
214
+ resetRunFlags(el, prefix);
166
215
  }
167
216
  }
168
217
  },
169
- { threshold: [0, 0.2] },
218
+ { threshold: IO_THRESHOLD },
170
219
  );
171
220
 
172
221
  const observeJsCandidates = (root?: ParentNode) => {