@bquery/bquery 1.3.0 → 1.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.
Files changed (71) hide show
  1. package/README.md +527 -501
  2. package/dist/{batch-4LAvfLE7.js → batch-x7b2eZST.js} +2 -2
  3. package/dist/{batch-4LAvfLE7.js.map → batch-x7b2eZST.js.map} +1 -1
  4. package/dist/component.es.mjs +1 -1
  5. package/dist/core/collection.d.ts +19 -3
  6. package/dist/core/collection.d.ts.map +1 -1
  7. package/dist/core/element.d.ts +23 -4
  8. package/dist/core/element.d.ts.map +1 -1
  9. package/dist/core/index.d.ts +1 -0
  10. package/dist/core/index.d.ts.map +1 -1
  11. package/dist/core/utils/function.d.ts +21 -4
  12. package/dist/core/utils/function.d.ts.map +1 -1
  13. package/dist/{core-COenAZjD.js → core-BhpuvPhy.js} +62 -37
  14. package/dist/core-BhpuvPhy.js.map +1 -0
  15. package/dist/core.es.mjs +174 -131
  16. package/dist/core.es.mjs.map +1 -1
  17. package/dist/full.es.mjs +7 -7
  18. package/dist/full.iife.js +2 -2
  19. package/dist/full.iife.js.map +1 -1
  20. package/dist/full.umd.js +2 -2
  21. package/dist/full.umd.js.map +1 -1
  22. package/dist/index.es.mjs +7 -7
  23. package/dist/motion.es.mjs.map +1 -1
  24. package/dist/{persisted-Dz_ryNuC.js → persisted-DHoi3uEs.js} +4 -4
  25. package/dist/{persisted-Dz_ryNuC.js.map → persisted-DHoi3uEs.js.map} +1 -1
  26. package/dist/platform/storage.d.ts.map +1 -1
  27. package/dist/platform.es.mjs +12 -7
  28. package/dist/platform.es.mjs.map +1 -1
  29. package/dist/reactive/core.d.ts +12 -0
  30. package/dist/reactive/core.d.ts.map +1 -1
  31. package/dist/reactive/effect.d.ts.map +1 -1
  32. package/dist/reactive/internals.d.ts +6 -0
  33. package/dist/reactive/internals.d.ts.map +1 -1
  34. package/dist/reactive.es.mjs +6 -6
  35. package/dist/router.es.mjs +1 -1
  36. package/dist/{sanitize-1FBEPAFH.js → sanitize-Cxvxa-DX.js} +50 -39
  37. package/dist/sanitize-Cxvxa-DX.js.map +1 -0
  38. package/dist/security/sanitize-core.d.ts.map +1 -1
  39. package/dist/security.es.mjs +2 -2
  40. package/dist/store.es.mjs +2 -2
  41. package/dist/type-guards-BdKlYYlS.js +32 -0
  42. package/dist/type-guards-BdKlYYlS.js.map +1 -0
  43. package/dist/untrack-DNnnqdlR.js +6 -0
  44. package/dist/{untrack-BuEQKH7_.js.map → untrack-DNnnqdlR.js.map} +1 -1
  45. package/dist/view/evaluate.d.ts.map +1 -1
  46. package/dist/view.es.mjs +157 -151
  47. package/dist/view.es.mjs.map +1 -1
  48. package/dist/{watch-CXyaBC_9.js → watch-DXXv3iAI.js} +3 -3
  49. package/dist/{watch-CXyaBC_9.js.map → watch-DXXv3iAI.js.map} +1 -1
  50. package/package.json +132 -132
  51. package/src/core/collection.ts +628 -588
  52. package/src/core/element.ts +774 -746
  53. package/src/core/index.ts +48 -47
  54. package/src/core/utils/function.ts +151 -110
  55. package/src/motion/animate.ts +113 -113
  56. package/src/motion/flip.ts +176 -176
  57. package/src/motion/scroll.ts +57 -57
  58. package/src/motion/spring.ts +150 -150
  59. package/src/motion/timeline.ts +246 -246
  60. package/src/motion/transition.ts +51 -51
  61. package/src/platform/storage.ts +215 -208
  62. package/src/reactive/core.ts +114 -93
  63. package/src/reactive/effect.ts +54 -43
  64. package/src/reactive/internals.ts +122 -105
  65. package/src/security/sanitize-core.ts +364 -343
  66. package/src/view/evaluate.ts +290 -274
  67. package/dist/core-COenAZjD.js.map +0 -1
  68. package/dist/sanitize-1FBEPAFH.js.map +0 -1
  69. package/dist/type-guards-DRma3-Kc.js +0 -16
  70. package/dist/type-guards-DRma3-Kc.js.map +0 -1
  71. package/dist/untrack-BuEQKH7_.js +0 -6
@@ -1,176 +1,176 @@
1
- /**
2
- * FLIP animation helpers.
3
- *
4
- * @module bquery/motion
5
- */
6
-
7
- import type { ElementBounds, FlipGroupOptions, FlipOptions } from './types';
8
-
9
- /**
10
- * Capture the current bounds of an element for FLIP animation.
11
- *
12
- * @param element - The DOM element to measure
13
- * @returns The element's current position and size
14
- */
15
- export const capturePosition = (element: Element): ElementBounds => {
16
- const rect = element.getBoundingClientRect();
17
- return {
18
- top: rect.top,
19
- left: rect.left,
20
- width: rect.width,
21
- height: rect.height,
22
- };
23
- };
24
-
25
- /**
26
- * Perform a FLIP (First, Last, Invert, Play) animation.
27
- * Animates an element from its captured position to its current position.
28
- *
29
- * @param element - The element to animate
30
- * @param firstBounds - The previously captured bounds
31
- * @param options - Animation configuration
32
- * @returns Promise that resolves when animation completes
33
- *
34
- * @example
35
- * ```ts
36
- * const first = capturePosition(element);
37
- * // ... DOM changes that move the element ...
38
- * await flip(element, first, { duration: 300 });
39
- * ```
40
- */
41
- export const flip = (
42
- element: Element,
43
- firstBounds: ElementBounds,
44
- options: FlipOptions = {}
45
- ): Promise<void> => {
46
- const { duration = 300, easing = 'ease-out', onComplete } = options;
47
-
48
- // Last: Get current position
49
- const lastBounds = capturePosition(element);
50
-
51
- // Skip animation if element has zero dimensions (avoid division by zero)
52
- if (lastBounds.width === 0 || lastBounds.height === 0) {
53
- onComplete?.();
54
- return Promise.resolve();
55
- }
56
-
57
- // Invert: Calculate the delta
58
- const deltaX = firstBounds.left - lastBounds.left;
59
- const deltaY = firstBounds.top - lastBounds.top;
60
- const deltaW = firstBounds.width / lastBounds.width;
61
- const deltaH = firstBounds.height / lastBounds.height;
62
-
63
- // Skip animation if no change
64
- if (deltaX === 0 && deltaY === 0 && deltaW === 1 && deltaH === 1) {
65
- onComplete?.();
66
- return Promise.resolve();
67
- }
68
-
69
- const htmlElement = element as HTMLElement;
70
-
71
- // Feature check: fallback if Web Animations API is unavailable
72
- if (typeof htmlElement.animate !== 'function') {
73
- onComplete?.();
74
- return Promise.resolve();
75
- }
76
-
77
- // Apply inverted transform
78
- htmlElement.style.transform = `translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`;
79
- htmlElement.style.transformOrigin = 'top left';
80
-
81
- // Force reflow
82
- void htmlElement.offsetHeight;
83
-
84
- // Play: Animate back to current position
85
- return new Promise((resolve) => {
86
- const animation = htmlElement.animate(
87
- [
88
- {
89
- transform: `translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`,
90
- },
91
- { transform: 'translate(0, 0) scale(1, 1)' },
92
- ],
93
- { duration, easing, fill: 'forwards' }
94
- );
95
-
96
- let finalized = false;
97
- const finalize = () => {
98
- if (finalized) return;
99
- finalized = true;
100
- htmlElement.style.transform = '';
101
- htmlElement.style.transformOrigin = '';
102
- onComplete?.();
103
- resolve();
104
- };
105
-
106
- animation.onfinish = finalize;
107
- // Handle cancel/rejection via the finished promise
108
- if (animation.finished) {
109
- animation.finished.then(finalize).catch(finalize);
110
- }
111
- });
112
- };
113
-
114
- /**
115
- * FLIP helper for animating a list of elements.
116
- * Useful for reordering lists with smooth animations.
117
- *
118
- * @param elements - Array of elements to animate
119
- * @param performUpdate - Function that performs the DOM update
120
- * @param options - Animation configuration
121
- *
122
- * @example
123
- * ```ts
124
- * await flipList(listItems, () => {
125
- * container.appendChild(container.firstChild); // Move first to last
126
- * });
127
- * ```
128
- */
129
- export const flipList = async (
130
- elements: Element[],
131
- performUpdate: () => void,
132
- options: FlipOptions = {}
133
- ): Promise<void> => {
134
- await flipElements(elements, performUpdate, options);
135
- };
136
-
137
- /**
138
- * FLIP helper with optional stagger support.
139
- *
140
- * @param elements - Array of elements to animate
141
- * @param performUpdate - Function that performs the DOM update
142
- * @param options - Animation configuration
143
- */
144
- export const flipElements = async (
145
- elements: Element[],
146
- performUpdate: () => void,
147
- options: FlipGroupOptions = {}
148
- ): Promise<void> => {
149
- const { stagger, ...flipOptions } = options;
150
-
151
- // First: Capture all positions
152
- const positions = new Map<Element, ElementBounds>();
153
- for (const el of elements) {
154
- positions.set(el, capturePosition(el));
155
- }
156
-
157
- // Perform DOM update
158
- performUpdate();
159
-
160
- const total = elements.length;
161
-
162
- // Animate each element
163
- const animations = elements.map((el, index) => {
164
- const first = positions.get(el);
165
- if (!first) return Promise.resolve();
166
- const delay = stagger ? stagger(index, total) : 0;
167
- if (delay > 0) {
168
- return new Promise((resolve) => setTimeout(resolve, delay)).then(() =>
169
- flip(el, first, flipOptions)
170
- );
171
- }
172
- return flip(el, first, flipOptions);
173
- });
174
-
175
- await Promise.all(animations);
176
- };
1
+ /**
2
+ * FLIP animation helpers.
3
+ *
4
+ * @module bquery/motion
5
+ */
6
+
7
+ import type { ElementBounds, FlipGroupOptions, FlipOptions } from './types';
8
+
9
+ /**
10
+ * Capture the current bounds of an element for FLIP animation.
11
+ *
12
+ * @param element - The DOM element to measure
13
+ * @returns The element's current position and size
14
+ */
15
+ export const capturePosition = (element: Element): ElementBounds => {
16
+ const rect = element.getBoundingClientRect();
17
+ return {
18
+ top: rect.top,
19
+ left: rect.left,
20
+ width: rect.width,
21
+ height: rect.height,
22
+ };
23
+ };
24
+
25
+ /**
26
+ * Perform a FLIP (First, Last, Invert, Play) animation.
27
+ * Animates an element from its captured position to its current position.
28
+ *
29
+ * @param element - The element to animate
30
+ * @param firstBounds - The previously captured bounds
31
+ * @param options - Animation configuration
32
+ * @returns Promise that resolves when animation completes
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const first = capturePosition(element);
37
+ * // ... DOM changes that move the element ...
38
+ * await flip(element, first, { duration: 300 });
39
+ * ```
40
+ */
41
+ export const flip = (
42
+ element: Element,
43
+ firstBounds: ElementBounds,
44
+ options: FlipOptions = {}
45
+ ): Promise<void> => {
46
+ const { duration = 300, easing = 'ease-out', onComplete } = options;
47
+
48
+ // Last: Get current position
49
+ const lastBounds = capturePosition(element);
50
+
51
+ // Skip animation if element has zero dimensions (avoid division by zero)
52
+ if (lastBounds.width === 0 || lastBounds.height === 0) {
53
+ onComplete?.();
54
+ return Promise.resolve();
55
+ }
56
+
57
+ // Invert: Calculate the delta
58
+ const deltaX = firstBounds.left - lastBounds.left;
59
+ const deltaY = firstBounds.top - lastBounds.top;
60
+ const deltaW = firstBounds.width / lastBounds.width;
61
+ const deltaH = firstBounds.height / lastBounds.height;
62
+
63
+ // Skip animation if no change
64
+ if (deltaX === 0 && deltaY === 0 && deltaW === 1 && deltaH === 1) {
65
+ onComplete?.();
66
+ return Promise.resolve();
67
+ }
68
+
69
+ const htmlElement = element as HTMLElement;
70
+
71
+ // Feature check: fallback if Web Animations API is unavailable
72
+ if (typeof htmlElement.animate !== 'function') {
73
+ onComplete?.();
74
+ return Promise.resolve();
75
+ }
76
+
77
+ // Apply inverted transform
78
+ htmlElement.style.transform = `translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`;
79
+ htmlElement.style.transformOrigin = 'top left';
80
+
81
+ // Force reflow
82
+ void htmlElement.offsetHeight;
83
+
84
+ // Play: Animate back to current position
85
+ return new Promise((resolve) => {
86
+ const animation = htmlElement.animate(
87
+ [
88
+ {
89
+ transform: `translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`,
90
+ },
91
+ { transform: 'translate(0, 0) scale(1, 1)' },
92
+ ],
93
+ { duration, easing, fill: 'forwards' }
94
+ );
95
+
96
+ let finalized = false;
97
+ const finalize = () => {
98
+ if (finalized) return;
99
+ finalized = true;
100
+ htmlElement.style.transform = '';
101
+ htmlElement.style.transformOrigin = '';
102
+ onComplete?.();
103
+ resolve();
104
+ };
105
+
106
+ animation.onfinish = finalize;
107
+ // Handle cancel/rejection via the finished promise
108
+ if (animation.finished) {
109
+ animation.finished.then(finalize).catch(finalize);
110
+ }
111
+ });
112
+ };
113
+
114
+ /**
115
+ * FLIP helper for animating a list of elements.
116
+ * Useful for reordering lists with smooth animations.
117
+ *
118
+ * @param elements - Array of elements to animate
119
+ * @param performUpdate - Function that performs the DOM update
120
+ * @param options - Animation configuration
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * await flipList(listItems, () => {
125
+ * container.appendChild(container.firstChild); // Move first to last
126
+ * });
127
+ * ```
128
+ */
129
+ export const flipList = async (
130
+ elements: Element[],
131
+ performUpdate: () => void,
132
+ options: FlipOptions = {}
133
+ ): Promise<void> => {
134
+ await flipElements(elements, performUpdate, options);
135
+ };
136
+
137
+ /**
138
+ * FLIP helper with optional stagger support.
139
+ *
140
+ * @param elements - Array of elements to animate
141
+ * @param performUpdate - Function that performs the DOM update
142
+ * @param options - Animation configuration
143
+ */
144
+ export const flipElements = async (
145
+ elements: Element[],
146
+ performUpdate: () => void,
147
+ options: FlipGroupOptions = {}
148
+ ): Promise<void> => {
149
+ const { stagger, ...flipOptions } = options;
150
+
151
+ // First: Capture all positions
152
+ const positions = new Map<Element, ElementBounds>();
153
+ for (const el of elements) {
154
+ positions.set(el, capturePosition(el));
155
+ }
156
+
157
+ // Perform DOM update
158
+ performUpdate();
159
+
160
+ const total = elements.length;
161
+
162
+ // Animate each element
163
+ const animations = elements.map((el, index) => {
164
+ const first = positions.get(el);
165
+ if (!first) return Promise.resolve();
166
+ const delay = stagger ? stagger(index, total) : 0;
167
+ if (delay > 0) {
168
+ return new Promise((resolve) => setTimeout(resolve, delay)).then(() =>
169
+ flip(el, first, flipOptions)
170
+ );
171
+ }
172
+ return flip(el, first, flipOptions);
173
+ });
174
+
175
+ await Promise.all(animations);
176
+ };
@@ -1,57 +1,57 @@
1
- /**
2
- * Scroll-triggered animation helpers.
3
- *
4
- * @module bquery/motion
5
- */
6
-
7
- import { animate } from './animate';
8
- import type { ScrollAnimateCleanup, ScrollAnimateOptions } from './types';
9
-
10
- const resolveElements = (elements: Element | Iterable<Element> | ArrayLike<Element>): Element[] => {
11
- if (typeof Element !== 'undefined' && elements instanceof Element) return [elements];
12
- return Array.from(elements as Iterable<Element>);
13
- };
14
-
15
- /**
16
- * Animate elements when they enter the viewport.
17
- *
18
- * @param elements - Target element(s)
19
- * @param options - Scroll animation configuration
20
- * @returns Cleanup function to disconnect observers
21
- */
22
- export const scrollAnimate = (
23
- elements: Element | Iterable<Element> | ArrayLike<Element>,
24
- options: ScrollAnimateOptions
25
- ): ScrollAnimateCleanup => {
26
- const targets = resolveElements(elements);
27
- if (!targets.length) return () => undefined;
28
-
29
- const { root = null, rootMargin, threshold, once = true, onEnter, ...animationConfig } = options;
30
-
31
- if (typeof IntersectionObserver === 'undefined') {
32
- targets.forEach((element) => {
33
- onEnter?.(element);
34
- void animate(element, animationConfig);
35
- });
36
- return () => undefined;
37
- }
38
-
39
- const observer = new IntersectionObserver(
40
- (entries) => {
41
- entries.forEach((entry) => {
42
- if (!entry.isIntersecting) return;
43
- const element = entry.target as Element;
44
- onEnter?.(element);
45
- void animate(element, animationConfig);
46
- if (once) {
47
- observer.unobserve(element);
48
- }
49
- });
50
- },
51
- { root, rootMargin, threshold }
52
- );
53
-
54
- targets.forEach((element) => observer.observe(element));
55
-
56
- return () => observer.disconnect();
57
- };
1
+ /**
2
+ * Scroll-triggered animation helpers.
3
+ *
4
+ * @module bquery/motion
5
+ */
6
+
7
+ import { animate } from './animate';
8
+ import type { ScrollAnimateCleanup, ScrollAnimateOptions } from './types';
9
+
10
+ const resolveElements = (elements: Element | Iterable<Element> | ArrayLike<Element>): Element[] => {
11
+ if (typeof Element !== 'undefined' && elements instanceof Element) return [elements];
12
+ return Array.from(elements as Iterable<Element>);
13
+ };
14
+
15
+ /**
16
+ * Animate elements when they enter the viewport.
17
+ *
18
+ * @param elements - Target element(s)
19
+ * @param options - Scroll animation configuration
20
+ * @returns Cleanup function to disconnect observers
21
+ */
22
+ export const scrollAnimate = (
23
+ elements: Element | Iterable<Element> | ArrayLike<Element>,
24
+ options: ScrollAnimateOptions
25
+ ): ScrollAnimateCleanup => {
26
+ const targets = resolveElements(elements);
27
+ if (!targets.length) return () => undefined;
28
+
29
+ const { root = null, rootMargin, threshold, once = true, onEnter, ...animationConfig } = options;
30
+
31
+ if (typeof IntersectionObserver === 'undefined') {
32
+ targets.forEach((element) => {
33
+ onEnter?.(element);
34
+ void animate(element, animationConfig);
35
+ });
36
+ return () => undefined;
37
+ }
38
+
39
+ const observer = new IntersectionObserver(
40
+ (entries) => {
41
+ entries.forEach((entry) => {
42
+ if (!entry.isIntersecting) return;
43
+ const element = entry.target as Element;
44
+ onEnter?.(element);
45
+ void animate(element, animationConfig);
46
+ if (once) {
47
+ observer.unobserve(element);
48
+ }
49
+ });
50
+ },
51
+ { root, rootMargin, threshold }
52
+ );
53
+
54
+ targets.forEach((element) => observer.observe(element));
55
+
56
+ return () => observer.disconnect();
57
+ };