@samline/notify 0.3.0 → 1.0.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.
- package/LICENSE +22 -0
- package/README.md +121 -150
- package/dist/browser-notify.js +69 -0
- package/dist/cc-2Yt7NqMX.mjs +21 -0
- package/dist/cc-B6peeNak.mjs +33 -0
- package/dist/cc-BWuAzFJ6.js +12 -0
- package/dist/cc-CaBHsjUt.js +34 -0
- package/dist/cc-DGff5sSY.js +21 -0
- package/dist/cc-he3fHS3P.mjs +12 -0
- package/dist/notify.d.mts +44 -0
- package/dist/notify.d.mts.map +1 -0
- package/dist/notify.d.ts +44 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +90 -0
- package/dist/notify.mjs +87 -0
- package/dist/react-notify-12s-7LOZlSBi.js +1068 -0
- package/dist/react-notify-12s-BjWbwTu8.mjs +1066 -0
- package/dist/react.d.mts +66 -0
- package/dist/react.d.mts.map +1 -0
- package/dist/react.d.ts +66 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +18 -0
- package/dist/react.mjs +10 -0
- package/dist/styles.css +477 -63
- package/dist/svelte.d.mts +45 -0
- package/dist/svelte.d.mts.map +1 -0
- package/dist/svelte.d.ts +45 -0
- package/dist/svelte.d.ts.map +1 -0
- package/dist/svelte.js +168 -0
- package/dist/svelte.mjs +165 -0
- package/dist/vue.d.mts +103 -0
- package/dist/vue.d.mts.map +1 -0
- package/dist/vue.d.ts +103 -0
- package/dist/vue.d.ts.map +1 -0
- package/dist/vue.js +2099 -0
- package/dist/vue.mjs +2096 -0
- package/package.json +95 -57
- package/dist/index.cjs.js +0 -1171
- package/dist/index.esm.js +0 -1164
- package/dist/notify.umd.js +0 -1177
- package/docs/browser.md +0 -99
- package/docs/react.md +0 -275
- package/docs/svelte.md +0 -267
- package/docs/vanilla.md +0 -256
- package/docs/vue.md +0 -301
- package/rollup.config.mjs +0 -56
- package/samline-notify-0.1.9.tgz +0 -0
package/dist/index.esm.js
DELETED
|
@@ -1,1164 +0,0 @@
|
|
|
1
|
-
class NotifyController {
|
|
2
|
-
constructor() {
|
|
3
|
-
this.toasts = [];
|
|
4
|
-
this.listeners = new Set();
|
|
5
|
-
this.idCounter = 1;
|
|
6
|
-
}
|
|
7
|
-
nextId() {
|
|
8
|
-
return `notify_${Date.now()}_${this.idCounter++}`;
|
|
9
|
-
}
|
|
10
|
-
subscribe(fn) {
|
|
11
|
-
this.listeners.add(fn);
|
|
12
|
-
fn(this.toasts.slice());
|
|
13
|
-
return () => this.listeners.delete(fn);
|
|
14
|
-
}
|
|
15
|
-
notify() {
|
|
16
|
-
const snapshot = this.toasts.slice();
|
|
17
|
-
this.listeners.forEach((l) => l(snapshot));
|
|
18
|
-
}
|
|
19
|
-
getToasts() {
|
|
20
|
-
return this.toasts.slice();
|
|
21
|
-
}
|
|
22
|
-
show(opts) {
|
|
23
|
-
// Normalizar props avanzadas
|
|
24
|
-
const { icon, fill, styles, roundness, autopilot, ...rest } = opts;
|
|
25
|
-
const id = this.nextId();
|
|
26
|
-
const item = {
|
|
27
|
-
id,
|
|
28
|
-
options: {
|
|
29
|
-
...rest,
|
|
30
|
-
...(icon !== undefined ? { icon } : {}),
|
|
31
|
-
...(fill !== undefined ? { fill } : {}),
|
|
32
|
-
...(styles !== undefined ? { styles } : {}),
|
|
33
|
-
...(roundness !== undefined ? { roundness } : {}),
|
|
34
|
-
...(autopilot !== undefined ? { autopilot } : {}),
|
|
35
|
-
},
|
|
36
|
-
createdAt: Date.now(),
|
|
37
|
-
};
|
|
38
|
-
this.toasts.push(item);
|
|
39
|
-
this.notify();
|
|
40
|
-
return id;
|
|
41
|
-
}
|
|
42
|
-
success(opts) {
|
|
43
|
-
return this.show({ ...opts, type: 'success' });
|
|
44
|
-
}
|
|
45
|
-
error(opts) {
|
|
46
|
-
return this.show({ ...opts, type: 'error' });
|
|
47
|
-
}
|
|
48
|
-
info(opts) {
|
|
49
|
-
return this.show({ ...opts, type: 'info' });
|
|
50
|
-
}
|
|
51
|
-
warning(opts) {
|
|
52
|
-
return this.show({ ...opts, type: 'warning' });
|
|
53
|
-
}
|
|
54
|
-
action(opts) {
|
|
55
|
-
return this.show({ ...opts, type: 'action' });
|
|
56
|
-
}
|
|
57
|
-
dismiss(id) {
|
|
58
|
-
const idx = this.toasts.findIndex((t) => t.id === id);
|
|
59
|
-
if (idx >= 0) {
|
|
60
|
-
this.toasts.splice(idx, 1);
|
|
61
|
-
this.notify();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
clear(position) {
|
|
65
|
-
if (position) {
|
|
66
|
-
this.toasts = this.toasts.filter((t) => t.options.position !== position);
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
this.toasts = [];
|
|
70
|
-
}
|
|
71
|
-
this.notify();
|
|
72
|
-
}
|
|
73
|
-
promise(p, opts) {
|
|
74
|
-
const loadingId = this.show({ ...(opts.loading || {}), type: 'loading', position: opts.position });
|
|
75
|
-
p.then((res) => {
|
|
76
|
-
this.dismiss(loadingId);
|
|
77
|
-
if (opts.action) {
|
|
78
|
-
const actionOpt = typeof opts.action === 'function' ? opts.action(res) : opts.action;
|
|
79
|
-
if (actionOpt) {
|
|
80
|
-
this.show({ ...(actionOpt || {}), type: 'action', position: opts.position });
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
const successOpt = typeof opts.success === 'function' ? opts.success(res) : opts.success;
|
|
85
|
-
if (successOpt)
|
|
86
|
-
this.show({ ...(successOpt || {}), type: 'success', position: opts.position });
|
|
87
|
-
}).catch((err) => {
|
|
88
|
-
this.dismiss(loadingId);
|
|
89
|
-
const errorOpt = typeof opts.error === 'function' ? opts.error(err) : opts.error;
|
|
90
|
-
if (errorOpt)
|
|
91
|
-
this.show({ ...(errorOpt || {}), type: 'error', position: opts.position });
|
|
92
|
-
});
|
|
93
|
-
return p;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
const notify$1 = new NotifyController();
|
|
97
|
-
// backward compatibility alias
|
|
98
|
-
const sileo = notify$1;
|
|
99
|
-
|
|
100
|
-
function addUniqueItem(array, item) {
|
|
101
|
-
array.indexOf(item) === -1 && array.push(item);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const clamp = (min, max, v) => Math.min(Math.max(v, min), max);
|
|
105
|
-
|
|
106
|
-
const defaults = {
|
|
107
|
-
duration: 0.3,
|
|
108
|
-
delay: 0,
|
|
109
|
-
endDelay: 0,
|
|
110
|
-
repeat: 0,
|
|
111
|
-
easing: "ease",
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const isNumber = (value) => typeof value === "number";
|
|
115
|
-
|
|
116
|
-
const isEasingList = (easing) => Array.isArray(easing) && !isNumber(easing[0]);
|
|
117
|
-
|
|
118
|
-
const wrap = (min, max, v) => {
|
|
119
|
-
const rangeSize = max - min;
|
|
120
|
-
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
function getEasingForSegment(easing, i) {
|
|
124
|
-
return isEasingList(easing) ? easing[wrap(0, easing.length, i)] : easing;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const mix = (min, max, progress) => -progress * min + progress * max + min;
|
|
128
|
-
|
|
129
|
-
const noop = () => { };
|
|
130
|
-
const noopReturn = (v) => v;
|
|
131
|
-
|
|
132
|
-
const progress = (min, max, value) => max - min === 0 ? 1 : (value - min) / (max - min);
|
|
133
|
-
|
|
134
|
-
function fillOffset(offset, remaining) {
|
|
135
|
-
const min = offset[offset.length - 1];
|
|
136
|
-
for (let i = 1; i <= remaining; i++) {
|
|
137
|
-
const offsetProgress = progress(0, remaining, i);
|
|
138
|
-
offset.push(mix(min, 1, offsetProgress));
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function defaultOffset(length) {
|
|
142
|
-
const offset = [0];
|
|
143
|
-
fillOffset(offset, length - 1);
|
|
144
|
-
return offset;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function interpolate(output, input = defaultOffset(output.length), easing = noopReturn) {
|
|
148
|
-
const length = output.length;
|
|
149
|
-
/**
|
|
150
|
-
* If the input length is lower than the output we
|
|
151
|
-
* fill the input to match. This currently assumes the input
|
|
152
|
-
* is an animation progress value so is a good candidate for
|
|
153
|
-
* moving outside the function.
|
|
154
|
-
*/
|
|
155
|
-
const remainder = length - input.length;
|
|
156
|
-
remainder > 0 && fillOffset(input, remainder);
|
|
157
|
-
return (t) => {
|
|
158
|
-
let i = 0;
|
|
159
|
-
for (; i < length - 2; i++) {
|
|
160
|
-
if (t < input[i + 1])
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
let progressInRange = clamp(0, 1, progress(input[i], input[i + 1], t));
|
|
164
|
-
const segmentEasing = getEasingForSegment(easing, i);
|
|
165
|
-
progressInRange = segmentEasing(progressInRange);
|
|
166
|
-
return mix(output[i], output[i + 1], progressInRange);
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const isCubicBezier = (easing) => Array.isArray(easing) && isNumber(easing[0]);
|
|
171
|
-
|
|
172
|
-
const isEasingGenerator = (easing) => typeof easing === "object" &&
|
|
173
|
-
Boolean(easing.createAnimation);
|
|
174
|
-
|
|
175
|
-
const isFunction = (value) => typeof value === "function";
|
|
176
|
-
|
|
177
|
-
const isString = (value) => typeof value === "string";
|
|
178
|
-
|
|
179
|
-
const time = {
|
|
180
|
-
ms: (seconds) => seconds * 1000,
|
|
181
|
-
s: (milliseconds) => milliseconds / 1000,
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
/*
|
|
185
|
-
Bezier function generator
|
|
186
|
-
|
|
187
|
-
This has been modified from Gaëtan Renaudeau's BezierEasing
|
|
188
|
-
https://github.com/gre/bezier-easing/blob/master/src/index.js
|
|
189
|
-
https://github.com/gre/bezier-easing/blob/master/LICENSE
|
|
190
|
-
|
|
191
|
-
I've removed the newtonRaphsonIterate algo because in benchmarking it
|
|
192
|
-
wasn't noticiably faster than binarySubdivision, indeed removing it
|
|
193
|
-
usually improved times, depending on the curve.
|
|
194
|
-
|
|
195
|
-
I also removed the lookup table, as for the added bundle size and loop we're
|
|
196
|
-
only cutting ~4 or so subdivision iterations. I bumped the max iterations up
|
|
197
|
-
to 12 to compensate and this still tended to be faster for no perceivable
|
|
198
|
-
loss in accuracy.
|
|
199
|
-
|
|
200
|
-
Usage
|
|
201
|
-
const easeOut = cubicBezier(.17,.67,.83,.67);
|
|
202
|
-
const x = easeOut(0.5); // returns 0.627...
|
|
203
|
-
*/
|
|
204
|
-
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
|
|
205
|
-
const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) * t;
|
|
206
|
-
const subdivisionPrecision = 0.0000001;
|
|
207
|
-
const subdivisionMaxIterations = 12;
|
|
208
|
-
function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
|
|
209
|
-
let currentX;
|
|
210
|
-
let currentT;
|
|
211
|
-
let i = 0;
|
|
212
|
-
do {
|
|
213
|
-
currentT = lowerBound + (upperBound - lowerBound) / 2.0;
|
|
214
|
-
currentX = calcBezier(currentT, mX1, mX2) - x;
|
|
215
|
-
if (currentX > 0.0) {
|
|
216
|
-
upperBound = currentT;
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
lowerBound = currentT;
|
|
220
|
-
}
|
|
221
|
-
} while (Math.abs(currentX) > subdivisionPrecision &&
|
|
222
|
-
++i < subdivisionMaxIterations);
|
|
223
|
-
return currentT;
|
|
224
|
-
}
|
|
225
|
-
function cubicBezier(mX1, mY1, mX2, mY2) {
|
|
226
|
-
// If this is a linear gradient, return linear easing
|
|
227
|
-
if (mX1 === mY1 && mX2 === mY2)
|
|
228
|
-
return noopReturn;
|
|
229
|
-
const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
|
|
230
|
-
// If animation is at start/end, return t without easing
|
|
231
|
-
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const steps = (steps, direction = "end") => (progress) => {
|
|
235
|
-
progress =
|
|
236
|
-
direction === "end"
|
|
237
|
-
? Math.min(progress, 0.999)
|
|
238
|
-
: Math.max(progress, 0.001);
|
|
239
|
-
const expanded = progress * steps;
|
|
240
|
-
const rounded = direction === "end" ? Math.floor(expanded) : Math.ceil(expanded);
|
|
241
|
-
return clamp(0, 1, rounded / steps);
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
const namedEasings = {
|
|
245
|
-
ease: cubicBezier(0.25, 0.1, 0.25, 1.0),
|
|
246
|
-
"ease-in": cubicBezier(0.42, 0.0, 1.0, 1.0),
|
|
247
|
-
"ease-in-out": cubicBezier(0.42, 0.0, 0.58, 1.0),
|
|
248
|
-
"ease-out": cubicBezier(0.0, 0.0, 0.58, 1.0),
|
|
249
|
-
};
|
|
250
|
-
const functionArgsRegex = /\((.*?)\)/;
|
|
251
|
-
function getEasingFunction(definition) {
|
|
252
|
-
// If already an easing function, return
|
|
253
|
-
if (isFunction(definition))
|
|
254
|
-
return definition;
|
|
255
|
-
// If an easing curve definition, return bezier function
|
|
256
|
-
if (isCubicBezier(definition))
|
|
257
|
-
return cubicBezier(...definition);
|
|
258
|
-
// If we have a predefined easing function, return
|
|
259
|
-
const namedEasing = namedEasings[definition];
|
|
260
|
-
if (namedEasing)
|
|
261
|
-
return namedEasing;
|
|
262
|
-
// If this is a steps function, attempt to create easing curve
|
|
263
|
-
if (definition.startsWith("steps")) {
|
|
264
|
-
const args = functionArgsRegex.exec(definition);
|
|
265
|
-
if (args) {
|
|
266
|
-
const argsArray = args[1].split(",");
|
|
267
|
-
return steps(parseFloat(argsArray[0]), argsArray[1].trim());
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
return noopReturn;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
class Animation {
|
|
274
|
-
constructor(output, keyframes = [0, 1], { easing, duration: initialDuration = defaults.duration, delay = defaults.delay, endDelay = defaults.endDelay, repeat = defaults.repeat, offset, direction = "normal", autoplay = true, } = {}) {
|
|
275
|
-
this.startTime = null;
|
|
276
|
-
this.rate = 1;
|
|
277
|
-
this.t = 0;
|
|
278
|
-
this.cancelTimestamp = null;
|
|
279
|
-
this.easing = noopReturn;
|
|
280
|
-
this.duration = 0;
|
|
281
|
-
this.totalDuration = 0;
|
|
282
|
-
this.repeat = 0;
|
|
283
|
-
this.playState = "idle";
|
|
284
|
-
this.finished = new Promise((resolve, reject) => {
|
|
285
|
-
this.resolve = resolve;
|
|
286
|
-
this.reject = reject;
|
|
287
|
-
});
|
|
288
|
-
easing = easing || defaults.easing;
|
|
289
|
-
if (isEasingGenerator(easing)) {
|
|
290
|
-
const custom = easing.createAnimation(keyframes);
|
|
291
|
-
easing = custom.easing;
|
|
292
|
-
keyframes = custom.keyframes || keyframes;
|
|
293
|
-
initialDuration = custom.duration || initialDuration;
|
|
294
|
-
}
|
|
295
|
-
this.repeat = repeat;
|
|
296
|
-
this.easing = isEasingList(easing) ? noopReturn : getEasingFunction(easing);
|
|
297
|
-
this.updateDuration(initialDuration);
|
|
298
|
-
const interpolate$1 = interpolate(keyframes, offset, isEasingList(easing) ? easing.map(getEasingFunction) : noopReturn);
|
|
299
|
-
this.tick = (timestamp) => {
|
|
300
|
-
var _a;
|
|
301
|
-
// TODO: Temporary fix for OptionsResolver typing
|
|
302
|
-
delay = delay;
|
|
303
|
-
let t = 0;
|
|
304
|
-
if (this.pauseTime !== undefined) {
|
|
305
|
-
t = this.pauseTime;
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
t = (timestamp - this.startTime) * this.rate;
|
|
309
|
-
}
|
|
310
|
-
this.t = t;
|
|
311
|
-
// Convert to seconds
|
|
312
|
-
t /= 1000;
|
|
313
|
-
// Rebase on delay
|
|
314
|
-
t = Math.max(t - delay, 0);
|
|
315
|
-
/**
|
|
316
|
-
* If this animation has finished, set the current time
|
|
317
|
-
* to the total duration.
|
|
318
|
-
*/
|
|
319
|
-
if (this.playState === "finished" && this.pauseTime === undefined) {
|
|
320
|
-
t = this.totalDuration;
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Get the current progress (0-1) of the animation. If t is >
|
|
324
|
-
* than duration we'll get values like 2.5 (midway through the
|
|
325
|
-
* third iteration)
|
|
326
|
-
*/
|
|
327
|
-
const progress = t / this.duration;
|
|
328
|
-
// TODO progress += iterationStart
|
|
329
|
-
/**
|
|
330
|
-
* Get the current iteration (0 indexed). For instance the floor of
|
|
331
|
-
* 2.5 is 2.
|
|
332
|
-
*/
|
|
333
|
-
let currentIteration = Math.floor(progress);
|
|
334
|
-
/**
|
|
335
|
-
* Get the current progress of the iteration by taking the remainder
|
|
336
|
-
* so 2.5 is 0.5 through iteration 2
|
|
337
|
-
*/
|
|
338
|
-
let iterationProgress = progress % 1.0;
|
|
339
|
-
if (!iterationProgress && progress >= 1) {
|
|
340
|
-
iterationProgress = 1;
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* If iteration progress is 1 we count that as the end
|
|
344
|
-
* of the previous iteration.
|
|
345
|
-
*/
|
|
346
|
-
iterationProgress === 1 && currentIteration--;
|
|
347
|
-
/**
|
|
348
|
-
* Reverse progress if we're not running in "normal" direction
|
|
349
|
-
*/
|
|
350
|
-
const iterationIsOdd = currentIteration % 2;
|
|
351
|
-
if (direction === "reverse" ||
|
|
352
|
-
(direction === "alternate" && iterationIsOdd) ||
|
|
353
|
-
(direction === "alternate-reverse" && !iterationIsOdd)) {
|
|
354
|
-
iterationProgress = 1 - iterationProgress;
|
|
355
|
-
}
|
|
356
|
-
const p = t >= this.totalDuration ? 1 : Math.min(iterationProgress, 1);
|
|
357
|
-
const latest = interpolate$1(this.easing(p));
|
|
358
|
-
output(latest);
|
|
359
|
-
const isAnimationFinished = this.pauseTime === undefined &&
|
|
360
|
-
(this.playState === "finished" || t >= this.totalDuration + endDelay);
|
|
361
|
-
if (isAnimationFinished) {
|
|
362
|
-
this.playState = "finished";
|
|
363
|
-
(_a = this.resolve) === null || _a === void 0 ? void 0 : _a.call(this, latest);
|
|
364
|
-
}
|
|
365
|
-
else if (this.playState !== "idle") {
|
|
366
|
-
this.frameRequestId = requestAnimationFrame(this.tick);
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
if (autoplay)
|
|
370
|
-
this.play();
|
|
371
|
-
}
|
|
372
|
-
play() {
|
|
373
|
-
const now = performance.now();
|
|
374
|
-
this.playState = "running";
|
|
375
|
-
if (this.pauseTime !== undefined) {
|
|
376
|
-
this.startTime = now - this.pauseTime;
|
|
377
|
-
}
|
|
378
|
-
else if (!this.startTime) {
|
|
379
|
-
this.startTime = now;
|
|
380
|
-
}
|
|
381
|
-
this.cancelTimestamp = this.startTime;
|
|
382
|
-
this.pauseTime = undefined;
|
|
383
|
-
this.frameRequestId = requestAnimationFrame(this.tick);
|
|
384
|
-
}
|
|
385
|
-
pause() {
|
|
386
|
-
this.playState = "paused";
|
|
387
|
-
this.pauseTime = this.t;
|
|
388
|
-
}
|
|
389
|
-
finish() {
|
|
390
|
-
this.playState = "finished";
|
|
391
|
-
this.tick(0);
|
|
392
|
-
}
|
|
393
|
-
stop() {
|
|
394
|
-
var _a;
|
|
395
|
-
this.playState = "idle";
|
|
396
|
-
if (this.frameRequestId !== undefined) {
|
|
397
|
-
cancelAnimationFrame(this.frameRequestId);
|
|
398
|
-
}
|
|
399
|
-
(_a = this.reject) === null || _a === void 0 ? void 0 : _a.call(this, false);
|
|
400
|
-
}
|
|
401
|
-
cancel() {
|
|
402
|
-
this.stop();
|
|
403
|
-
this.tick(this.cancelTimestamp);
|
|
404
|
-
}
|
|
405
|
-
reverse() {
|
|
406
|
-
this.rate *= -1;
|
|
407
|
-
}
|
|
408
|
-
commitStyles() { }
|
|
409
|
-
updateDuration(duration) {
|
|
410
|
-
this.duration = duration;
|
|
411
|
-
this.totalDuration = duration * (this.repeat + 1);
|
|
412
|
-
}
|
|
413
|
-
get currentTime() {
|
|
414
|
-
return this.t;
|
|
415
|
-
}
|
|
416
|
-
set currentTime(t) {
|
|
417
|
-
if (this.pauseTime !== undefined || this.rate === 0) {
|
|
418
|
-
this.pauseTime = t;
|
|
419
|
-
}
|
|
420
|
-
else {
|
|
421
|
-
this.startTime = performance.now() - t / this.rate;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
get playbackRate() {
|
|
425
|
-
return this.rate;
|
|
426
|
-
}
|
|
427
|
-
set playbackRate(rate) {
|
|
428
|
-
this.rate = rate;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* The MotionValue tracks the state of a single animatable
|
|
434
|
-
* value. Currently, updatedAt and current are unused. The
|
|
435
|
-
* long term idea is to use this to minimise the number
|
|
436
|
-
* of DOM reads, and to abstract the DOM interactions here.
|
|
437
|
-
*/
|
|
438
|
-
class MotionValue {
|
|
439
|
-
setAnimation(animation) {
|
|
440
|
-
this.animation = animation;
|
|
441
|
-
animation === null || animation === void 0 ? void 0 : animation.finished.then(() => this.clearAnimation()).catch(() => { });
|
|
442
|
-
}
|
|
443
|
-
clearAnimation() {
|
|
444
|
-
this.animation = this.generator = undefined;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
const data = new WeakMap();
|
|
449
|
-
function getAnimationData(element) {
|
|
450
|
-
if (!data.has(element)) {
|
|
451
|
-
data.set(element, {
|
|
452
|
-
transforms: [],
|
|
453
|
-
values: new Map(),
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
return data.get(element);
|
|
457
|
-
}
|
|
458
|
-
function getMotionValue(motionValues, name) {
|
|
459
|
-
if (!motionValues.has(name)) {
|
|
460
|
-
motionValues.set(name, new MotionValue());
|
|
461
|
-
}
|
|
462
|
-
return motionValues.get(name);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* A list of all transformable axes. We'll use this list to generated a version
|
|
467
|
-
* of each axes for each transform.
|
|
468
|
-
*/
|
|
469
|
-
const axes = ["", "X", "Y", "Z"];
|
|
470
|
-
/**
|
|
471
|
-
* An ordered array of each transformable value. By default, transform values
|
|
472
|
-
* will be sorted to this order.
|
|
473
|
-
*/
|
|
474
|
-
const order = ["translate", "scale", "rotate", "skew"];
|
|
475
|
-
const transformAlias = {
|
|
476
|
-
x: "translateX",
|
|
477
|
-
y: "translateY",
|
|
478
|
-
z: "translateZ",
|
|
479
|
-
};
|
|
480
|
-
const rotation = {
|
|
481
|
-
syntax: "<angle>",
|
|
482
|
-
initialValue: "0deg",
|
|
483
|
-
toDefaultUnit: (v) => v + "deg",
|
|
484
|
-
};
|
|
485
|
-
const baseTransformProperties = {
|
|
486
|
-
translate: {
|
|
487
|
-
syntax: "<length-percentage>",
|
|
488
|
-
initialValue: "0px",
|
|
489
|
-
toDefaultUnit: (v) => v + "px",
|
|
490
|
-
},
|
|
491
|
-
rotate: rotation,
|
|
492
|
-
scale: {
|
|
493
|
-
syntax: "<number>",
|
|
494
|
-
initialValue: 1,
|
|
495
|
-
toDefaultUnit: noopReturn,
|
|
496
|
-
},
|
|
497
|
-
skew: rotation,
|
|
498
|
-
};
|
|
499
|
-
const transformDefinitions = new Map();
|
|
500
|
-
const asTransformCssVar = (name) => `--motion-${name}`;
|
|
501
|
-
/**
|
|
502
|
-
* Generate a list of every possible transform key
|
|
503
|
-
*/
|
|
504
|
-
const transforms = ["x", "y", "z"];
|
|
505
|
-
order.forEach((name) => {
|
|
506
|
-
axes.forEach((axis) => {
|
|
507
|
-
transforms.push(name + axis);
|
|
508
|
-
transformDefinitions.set(asTransformCssVar(name + axis), baseTransformProperties[name]);
|
|
509
|
-
});
|
|
510
|
-
});
|
|
511
|
-
/**
|
|
512
|
-
* A function to use with Array.sort to sort transform keys by their default order.
|
|
513
|
-
*/
|
|
514
|
-
const compareTransformOrder = (a, b) => transforms.indexOf(a) - transforms.indexOf(b);
|
|
515
|
-
/**
|
|
516
|
-
* Provide a quick way to check if a string is the name of a transform
|
|
517
|
-
*/
|
|
518
|
-
const transformLookup = new Set(transforms);
|
|
519
|
-
const isTransform = (name) => transformLookup.has(name);
|
|
520
|
-
const addTransformToElement = (element, name) => {
|
|
521
|
-
// Map x to translateX etc
|
|
522
|
-
if (transformAlias[name])
|
|
523
|
-
name = transformAlias[name];
|
|
524
|
-
const { transforms } = getAnimationData(element);
|
|
525
|
-
addUniqueItem(transforms, name);
|
|
526
|
-
/**
|
|
527
|
-
* TODO: An optimisation here could be to cache the transform in element data
|
|
528
|
-
* and only update if this has changed.
|
|
529
|
-
*/
|
|
530
|
-
element.style.transform = buildTransformTemplate(transforms);
|
|
531
|
-
};
|
|
532
|
-
const buildTransformTemplate = (transforms) => transforms
|
|
533
|
-
.sort(compareTransformOrder)
|
|
534
|
-
.reduce(transformListToString, "")
|
|
535
|
-
.trim();
|
|
536
|
-
const transformListToString = (template, name) => `${template} ${name}(var(${asTransformCssVar(name)}))`;
|
|
537
|
-
|
|
538
|
-
const isCssVar = (name) => name.startsWith("--");
|
|
539
|
-
const registeredProperties = new Set();
|
|
540
|
-
function registerCssVariable(name) {
|
|
541
|
-
if (registeredProperties.has(name))
|
|
542
|
-
return;
|
|
543
|
-
registeredProperties.add(name);
|
|
544
|
-
try {
|
|
545
|
-
const { syntax, initialValue } = transformDefinitions.has(name)
|
|
546
|
-
? transformDefinitions.get(name)
|
|
547
|
-
: {};
|
|
548
|
-
CSS.registerProperty({
|
|
549
|
-
name,
|
|
550
|
-
inherits: false,
|
|
551
|
-
syntax,
|
|
552
|
-
initialValue,
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
catch (e) { }
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
const testAnimation = (keyframes, options) => document.createElement("div").animate(keyframes, options);
|
|
559
|
-
const featureTests = {
|
|
560
|
-
cssRegisterProperty: () => typeof CSS !== "undefined" &&
|
|
561
|
-
Object.hasOwnProperty.call(CSS, "registerProperty"),
|
|
562
|
-
waapi: () => Object.hasOwnProperty.call(Element.prototype, "animate"),
|
|
563
|
-
partialKeyframes: () => {
|
|
564
|
-
try {
|
|
565
|
-
testAnimation({ opacity: [1] });
|
|
566
|
-
}
|
|
567
|
-
catch (e) {
|
|
568
|
-
return false;
|
|
569
|
-
}
|
|
570
|
-
return true;
|
|
571
|
-
},
|
|
572
|
-
finished: () => Boolean(testAnimation({ opacity: [0, 1] }, { duration: 0.001 }).finished),
|
|
573
|
-
linearEasing: () => {
|
|
574
|
-
try {
|
|
575
|
-
testAnimation({ opacity: 0 }, { easing: "linear(0, 1)" });
|
|
576
|
-
}
|
|
577
|
-
catch (e) {
|
|
578
|
-
return false;
|
|
579
|
-
}
|
|
580
|
-
return true;
|
|
581
|
-
},
|
|
582
|
-
};
|
|
583
|
-
const results = {};
|
|
584
|
-
const supports = {};
|
|
585
|
-
for (const key in featureTests) {
|
|
586
|
-
supports[key] = () => {
|
|
587
|
-
if (results[key] === undefined)
|
|
588
|
-
results[key] =
|
|
589
|
-
featureTests[key]();
|
|
590
|
-
return results[key];
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// Create a linear easing point for every x second
|
|
595
|
-
const resolution = 0.015;
|
|
596
|
-
const generateLinearEasingPoints = (easing, duration) => {
|
|
597
|
-
let points = "";
|
|
598
|
-
const numPoints = Math.round(duration / resolution);
|
|
599
|
-
for (let i = 0; i < numPoints; i++) {
|
|
600
|
-
points += easing(progress(0, numPoints - 1, i)) + ", ";
|
|
601
|
-
}
|
|
602
|
-
return points.substring(0, points.length - 2);
|
|
603
|
-
};
|
|
604
|
-
const convertEasing = (easing, duration) => {
|
|
605
|
-
if (isFunction(easing)) {
|
|
606
|
-
return supports.linearEasing()
|
|
607
|
-
? `linear(${generateLinearEasingPoints(easing, duration)})`
|
|
608
|
-
: defaults.easing;
|
|
609
|
-
}
|
|
610
|
-
else {
|
|
611
|
-
return isCubicBezier(easing) ? cubicBezierAsString(easing) : easing;
|
|
612
|
-
}
|
|
613
|
-
};
|
|
614
|
-
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
615
|
-
|
|
616
|
-
function hydrateKeyframes(keyframes, readInitialValue) {
|
|
617
|
-
for (let i = 0; i < keyframes.length; i++) {
|
|
618
|
-
if (keyframes[i] === null) {
|
|
619
|
-
keyframes[i] = i ? keyframes[i - 1] : readInitialValue();
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
return keyframes;
|
|
623
|
-
}
|
|
624
|
-
const keyframesList = (keyframes) => Array.isArray(keyframes) ? keyframes : [keyframes];
|
|
625
|
-
|
|
626
|
-
function getStyleName(key) {
|
|
627
|
-
if (transformAlias[key])
|
|
628
|
-
key = transformAlias[key];
|
|
629
|
-
return isTransform(key) ? asTransformCssVar(key) : key;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
const style = {
|
|
633
|
-
get: (element, name) => {
|
|
634
|
-
name = getStyleName(name);
|
|
635
|
-
let value = isCssVar(name)
|
|
636
|
-
? element.style.getPropertyValue(name)
|
|
637
|
-
: getComputedStyle(element)[name];
|
|
638
|
-
// TODO Decide if value can be 0
|
|
639
|
-
if (!value && value !== 0) {
|
|
640
|
-
const definition = transformDefinitions.get(name);
|
|
641
|
-
if (definition)
|
|
642
|
-
value = definition.initialValue;
|
|
643
|
-
}
|
|
644
|
-
return value;
|
|
645
|
-
},
|
|
646
|
-
set: (element, name, value) => {
|
|
647
|
-
name = getStyleName(name);
|
|
648
|
-
if (isCssVar(name)) {
|
|
649
|
-
element.style.setProperty(name, value);
|
|
650
|
-
}
|
|
651
|
-
else {
|
|
652
|
-
element.style[name] = value;
|
|
653
|
-
}
|
|
654
|
-
},
|
|
655
|
-
};
|
|
656
|
-
|
|
657
|
-
function stopAnimation(animation, needsCommit = true) {
|
|
658
|
-
if (!animation || animation.playState === "finished")
|
|
659
|
-
return;
|
|
660
|
-
// Suppress error thrown by WAAPI
|
|
661
|
-
try {
|
|
662
|
-
if (animation.stop) {
|
|
663
|
-
animation.stop();
|
|
664
|
-
}
|
|
665
|
-
else {
|
|
666
|
-
needsCommit && animation.commitStyles();
|
|
667
|
-
animation.cancel();
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
catch (e) { }
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
function getUnitConverter(keyframes, definition) {
|
|
674
|
-
var _a;
|
|
675
|
-
let toUnit = (definition === null || definition === void 0 ? void 0 : definition.toDefaultUnit) || noopReturn;
|
|
676
|
-
const finalKeyframe = keyframes[keyframes.length - 1];
|
|
677
|
-
if (isString(finalKeyframe)) {
|
|
678
|
-
const unit = ((_a = finalKeyframe.match(/(-?[\d.]+)([a-z%]*)/)) === null || _a === void 0 ? void 0 : _a[2]) || "";
|
|
679
|
-
if (unit)
|
|
680
|
-
toUnit = (value) => value + unit;
|
|
681
|
-
}
|
|
682
|
-
return toUnit;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
function getDevToolsRecord() {
|
|
686
|
-
return window.__MOTION_DEV_TOOLS_RECORD;
|
|
687
|
-
}
|
|
688
|
-
function animateStyle(element, key, keyframesDefinition, options = {}, AnimationPolyfill) {
|
|
689
|
-
const record = getDevToolsRecord();
|
|
690
|
-
const isRecording = options.record !== false && record;
|
|
691
|
-
let animation;
|
|
692
|
-
let { duration = defaults.duration, delay = defaults.delay, endDelay = defaults.endDelay, repeat = defaults.repeat, easing = defaults.easing, persist = false, direction, offset, allowWebkitAcceleration = false, autoplay = true, } = options;
|
|
693
|
-
const data = getAnimationData(element);
|
|
694
|
-
const valueIsTransform = isTransform(key);
|
|
695
|
-
let canAnimateNatively = supports.waapi();
|
|
696
|
-
/**
|
|
697
|
-
* If this is an individual transform, we need to map its
|
|
698
|
-
* key to a CSS variable and update the element's transform style
|
|
699
|
-
*/
|
|
700
|
-
valueIsTransform && addTransformToElement(element, key);
|
|
701
|
-
const name = getStyleName(key);
|
|
702
|
-
const motionValue = getMotionValue(data.values, name);
|
|
703
|
-
/**
|
|
704
|
-
* Get definition of value, this will be used to convert numerical
|
|
705
|
-
* keyframes into the default value type.
|
|
706
|
-
*/
|
|
707
|
-
const definition = transformDefinitions.get(name);
|
|
708
|
-
/**
|
|
709
|
-
* Stop the current animation, if any. Because this will trigger
|
|
710
|
-
* commitStyles (DOM writes) and we might later trigger DOM reads,
|
|
711
|
-
* this is fired now and we return a factory function to create
|
|
712
|
-
* the actual animation that can get called in batch,
|
|
713
|
-
*/
|
|
714
|
-
stopAnimation(motionValue.animation, !(isEasingGenerator(easing) && motionValue.generator) &&
|
|
715
|
-
options.record !== false);
|
|
716
|
-
/**
|
|
717
|
-
* Batchable factory function containing all DOM reads.
|
|
718
|
-
*/
|
|
719
|
-
return () => {
|
|
720
|
-
const readInitialValue = () => { var _a, _b; return (_b = (_a = style.get(element, name)) !== null && _a !== void 0 ? _a : definition === null || definition === void 0 ? void 0 : definition.initialValue) !== null && _b !== void 0 ? _b : 0; };
|
|
721
|
-
/**
|
|
722
|
-
* Replace null values with the previous keyframe value, or read
|
|
723
|
-
* it from the DOM if it's the first keyframe.
|
|
724
|
-
*/
|
|
725
|
-
let keyframes = hydrateKeyframes(keyframesList(keyframesDefinition), readInitialValue);
|
|
726
|
-
/**
|
|
727
|
-
* Detect unit type of keyframes.
|
|
728
|
-
*/
|
|
729
|
-
const toUnit = getUnitConverter(keyframes, definition);
|
|
730
|
-
if (isEasingGenerator(easing)) {
|
|
731
|
-
const custom = easing.createAnimation(keyframes, key !== "opacity", readInitialValue, name, motionValue);
|
|
732
|
-
easing = custom.easing;
|
|
733
|
-
keyframes = custom.keyframes || keyframes;
|
|
734
|
-
duration = custom.duration || duration;
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* If this is a CSS variable we need to register it with the browser
|
|
738
|
-
* before it can be animated natively. We also set it with setProperty
|
|
739
|
-
* rather than directly onto the element.style object.
|
|
740
|
-
*/
|
|
741
|
-
if (isCssVar(name)) {
|
|
742
|
-
if (supports.cssRegisterProperty()) {
|
|
743
|
-
registerCssVariable(name);
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
canAnimateNatively = false;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* If we've been passed a custom easing function, and this browser
|
|
751
|
-
* does **not** support linear() easing, and the value is a transform
|
|
752
|
-
* (and thus a pure number) we can still support the custom easing
|
|
753
|
-
* by falling back to the animation polyfill.
|
|
754
|
-
*/
|
|
755
|
-
if (valueIsTransform &&
|
|
756
|
-
!supports.linearEasing() &&
|
|
757
|
-
(isFunction(easing) || (isEasingList(easing) && easing.some(isFunction)))) {
|
|
758
|
-
canAnimateNatively = false;
|
|
759
|
-
}
|
|
760
|
-
/**
|
|
761
|
-
* If we can animate this value with WAAPI, do so.
|
|
762
|
-
*/
|
|
763
|
-
if (canAnimateNatively) {
|
|
764
|
-
/**
|
|
765
|
-
* Convert numbers to default value types. Currently this only supports
|
|
766
|
-
* transforms but it could also support other value types.
|
|
767
|
-
*/
|
|
768
|
-
if (definition) {
|
|
769
|
-
keyframes = keyframes.map((value) => isNumber(value) ? definition.toDefaultUnit(value) : value);
|
|
770
|
-
}
|
|
771
|
-
/**
|
|
772
|
-
* If this browser doesn't support partial/implicit keyframes we need to
|
|
773
|
-
* explicitly provide one.
|
|
774
|
-
*/
|
|
775
|
-
if (keyframes.length === 1 &&
|
|
776
|
-
(!supports.partialKeyframes() || isRecording)) {
|
|
777
|
-
keyframes.unshift(readInitialValue());
|
|
778
|
-
}
|
|
779
|
-
const animationOptions = {
|
|
780
|
-
delay: time.ms(delay),
|
|
781
|
-
duration: time.ms(duration),
|
|
782
|
-
endDelay: time.ms(endDelay),
|
|
783
|
-
easing: !isEasingList(easing)
|
|
784
|
-
? convertEasing(easing, duration)
|
|
785
|
-
: undefined,
|
|
786
|
-
direction,
|
|
787
|
-
iterations: repeat + 1,
|
|
788
|
-
fill: "both",
|
|
789
|
-
};
|
|
790
|
-
animation = element.animate({
|
|
791
|
-
[name]: keyframes,
|
|
792
|
-
offset,
|
|
793
|
-
easing: isEasingList(easing)
|
|
794
|
-
? easing.map((thisEasing) => convertEasing(thisEasing, duration))
|
|
795
|
-
: undefined,
|
|
796
|
-
}, animationOptions);
|
|
797
|
-
/**
|
|
798
|
-
* Polyfill finished Promise in browsers that don't support it
|
|
799
|
-
*/
|
|
800
|
-
if (!animation.finished) {
|
|
801
|
-
animation.finished = new Promise((resolve, reject) => {
|
|
802
|
-
animation.onfinish = resolve;
|
|
803
|
-
animation.oncancel = reject;
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
const target = keyframes[keyframes.length - 1];
|
|
807
|
-
animation.finished
|
|
808
|
-
.then(() => {
|
|
809
|
-
if (persist)
|
|
810
|
-
return;
|
|
811
|
-
// Apply styles to target
|
|
812
|
-
style.set(element, name, target);
|
|
813
|
-
// Ensure fill modes don't persist
|
|
814
|
-
animation.cancel();
|
|
815
|
-
})
|
|
816
|
-
.catch(noop);
|
|
817
|
-
/**
|
|
818
|
-
* This forces Webkit to run animations on the main thread by exploiting
|
|
819
|
-
* this condition:
|
|
820
|
-
* https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp?rev=281238#L1099
|
|
821
|
-
*
|
|
822
|
-
* This fixes Webkit's timing bugs, like accelerated animations falling
|
|
823
|
-
* out of sync with main thread animations and massive delays in starting
|
|
824
|
-
* accelerated animations in WKWebView.
|
|
825
|
-
*/
|
|
826
|
-
if (!allowWebkitAcceleration)
|
|
827
|
-
animation.playbackRate = 1.000001;
|
|
828
|
-
/**
|
|
829
|
-
* If we can't animate the value natively then we can fallback to the numbers-only
|
|
830
|
-
* polyfill for transforms.
|
|
831
|
-
*/
|
|
832
|
-
}
|
|
833
|
-
else if (AnimationPolyfill && valueIsTransform) {
|
|
834
|
-
/**
|
|
835
|
-
* If any keyframe is a string (because we measured it from the DOM), we need to convert
|
|
836
|
-
* it into a number before passing to the Animation polyfill.
|
|
837
|
-
*/
|
|
838
|
-
keyframes = keyframes.map((value) => typeof value === "string" ? parseFloat(value) : value);
|
|
839
|
-
/**
|
|
840
|
-
* If we only have a single keyframe, we need to create an initial keyframe by reading
|
|
841
|
-
* the current value from the DOM.
|
|
842
|
-
*/
|
|
843
|
-
if (keyframes.length === 1) {
|
|
844
|
-
keyframes.unshift(parseFloat(readInitialValue()));
|
|
845
|
-
}
|
|
846
|
-
animation = new AnimationPolyfill((latest) => {
|
|
847
|
-
style.set(element, name, toUnit ? toUnit(latest) : latest);
|
|
848
|
-
}, keyframes, Object.assign(Object.assign({}, options), { duration,
|
|
849
|
-
easing }));
|
|
850
|
-
}
|
|
851
|
-
else {
|
|
852
|
-
const target = keyframes[keyframes.length - 1];
|
|
853
|
-
style.set(element, name, definition && isNumber(target)
|
|
854
|
-
? definition.toDefaultUnit(target)
|
|
855
|
-
: target);
|
|
856
|
-
}
|
|
857
|
-
if (isRecording) {
|
|
858
|
-
record(element, key, keyframes, {
|
|
859
|
-
duration,
|
|
860
|
-
delay: delay,
|
|
861
|
-
easing,
|
|
862
|
-
repeat,
|
|
863
|
-
offset,
|
|
864
|
-
}, "motion-one");
|
|
865
|
-
}
|
|
866
|
-
motionValue.setAnimation(animation);
|
|
867
|
-
if (animation && !autoplay)
|
|
868
|
-
animation.pause();
|
|
869
|
-
return animation;
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
const getOptions = (options, key) =>
|
|
874
|
-
/**
|
|
875
|
-
* TODO: Make test for this
|
|
876
|
-
* Always return a new object otherwise delay is overwritten by results of stagger
|
|
877
|
-
* and this results in no stagger
|
|
878
|
-
*/
|
|
879
|
-
options[key] ? Object.assign(Object.assign({}, options), options[key]) : Object.assign({}, options);
|
|
880
|
-
|
|
881
|
-
function resolveElements(elements, selectorCache) {
|
|
882
|
-
var _a;
|
|
883
|
-
if (typeof elements === "string") {
|
|
884
|
-
if (selectorCache) {
|
|
885
|
-
(_a = selectorCache[elements]) !== null && _a !== void 0 ? _a : (selectorCache[elements] = document.querySelectorAll(elements));
|
|
886
|
-
elements = selectorCache[elements];
|
|
887
|
-
}
|
|
888
|
-
else {
|
|
889
|
-
elements = document.querySelectorAll(elements);
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
else if (elements instanceof Element) {
|
|
893
|
-
elements = [elements];
|
|
894
|
-
}
|
|
895
|
-
/**
|
|
896
|
-
* Return an empty array
|
|
897
|
-
*/
|
|
898
|
-
return Array.from(elements || []);
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
const createAnimation = (factory) => factory();
|
|
902
|
-
const withControls = (animationFactory, options, duration = defaults.duration) => {
|
|
903
|
-
return new Proxy({
|
|
904
|
-
animations: animationFactory.map(createAnimation).filter(Boolean),
|
|
905
|
-
duration,
|
|
906
|
-
options,
|
|
907
|
-
}, controls);
|
|
908
|
-
};
|
|
909
|
-
/**
|
|
910
|
-
* TODO:
|
|
911
|
-
* Currently this returns the first animation, ideally it would return
|
|
912
|
-
* the first active animation.
|
|
913
|
-
*/
|
|
914
|
-
const getActiveAnimation = (state) => state.animations[0];
|
|
915
|
-
const controls = {
|
|
916
|
-
get: (target, key) => {
|
|
917
|
-
const activeAnimation = getActiveAnimation(target);
|
|
918
|
-
switch (key) {
|
|
919
|
-
case "duration":
|
|
920
|
-
return target.duration;
|
|
921
|
-
case "currentTime":
|
|
922
|
-
return time.s((activeAnimation === null || activeAnimation === void 0 ? void 0 : activeAnimation[key]) || 0);
|
|
923
|
-
case "playbackRate":
|
|
924
|
-
case "playState":
|
|
925
|
-
return activeAnimation === null || activeAnimation === void 0 ? void 0 : activeAnimation[key];
|
|
926
|
-
case "finished":
|
|
927
|
-
if (!target.finished) {
|
|
928
|
-
target.finished = Promise.all(target.animations.map(selectFinished)).catch(noop);
|
|
929
|
-
}
|
|
930
|
-
return target.finished;
|
|
931
|
-
case "stop":
|
|
932
|
-
return () => {
|
|
933
|
-
target.animations.forEach((animation) => stopAnimation(animation));
|
|
934
|
-
};
|
|
935
|
-
case "forEachNative":
|
|
936
|
-
/**
|
|
937
|
-
* This is for internal use only, fire a callback for each
|
|
938
|
-
* underlying animation.
|
|
939
|
-
*/
|
|
940
|
-
return (callback) => {
|
|
941
|
-
target.animations.forEach((animation) => callback(animation, target));
|
|
942
|
-
};
|
|
943
|
-
default:
|
|
944
|
-
return typeof (activeAnimation === null || activeAnimation === void 0 ? void 0 : activeAnimation[key]) ===
|
|
945
|
-
"undefined"
|
|
946
|
-
? undefined
|
|
947
|
-
: () => target.animations.forEach((animation) => animation[key]());
|
|
948
|
-
}
|
|
949
|
-
},
|
|
950
|
-
set: (target, key, value) => {
|
|
951
|
-
switch (key) {
|
|
952
|
-
case "currentTime":
|
|
953
|
-
value = time.ms(value);
|
|
954
|
-
// Fall-through
|
|
955
|
-
case "playbackRate":
|
|
956
|
-
for (let i = 0; i < target.animations.length; i++) {
|
|
957
|
-
target.animations[i][key] = value;
|
|
958
|
-
}
|
|
959
|
-
return true;
|
|
960
|
-
}
|
|
961
|
-
return false;
|
|
962
|
-
},
|
|
963
|
-
};
|
|
964
|
-
const selectFinished = (animation) => animation.finished;
|
|
965
|
-
|
|
966
|
-
function resolveOption(option, i, total) {
|
|
967
|
-
return isFunction(option) ? option(i, total) : option;
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
function createAnimate(AnimatePolyfill) {
|
|
971
|
-
return function animate(elements, keyframes, options = {}) {
|
|
972
|
-
elements = resolveElements(elements);
|
|
973
|
-
const numElements = elements.length;
|
|
974
|
-
/**
|
|
975
|
-
* Create and start new animations
|
|
976
|
-
*/
|
|
977
|
-
const animationFactories = [];
|
|
978
|
-
for (let i = 0; i < numElements; i++) {
|
|
979
|
-
const element = elements[i];
|
|
980
|
-
for (const key in keyframes) {
|
|
981
|
-
const valueOptions = getOptions(options, key);
|
|
982
|
-
valueOptions.delay = resolveOption(valueOptions.delay, i, numElements);
|
|
983
|
-
const animation = animateStyle(element, key, keyframes[key], valueOptions, AnimatePolyfill);
|
|
984
|
-
animationFactories.push(animation);
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
return withControls(animationFactories, options,
|
|
988
|
-
/**
|
|
989
|
-
* TODO:
|
|
990
|
-
* If easing is set to spring or glide, duration will be dynamically
|
|
991
|
-
* generated. Ideally we would dynamically generate this from
|
|
992
|
-
* animation.effect.getComputedTiming().duration but this isn't
|
|
993
|
-
* supported in iOS13 or our number polyfill. Perhaps it's possible
|
|
994
|
-
* to Proxy animations returned from animateStyle that has duration
|
|
995
|
-
* as a getter.
|
|
996
|
-
*/
|
|
997
|
-
options.duration);
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
const animate$1 = createAnimate(Animation);
|
|
1002
|
-
|
|
1003
|
-
function animateProgress(target, options = {}) {
|
|
1004
|
-
return withControls([
|
|
1005
|
-
() => {
|
|
1006
|
-
const animation = new Animation(target, [0, 1], options);
|
|
1007
|
-
animation.finished.catch(() => { });
|
|
1008
|
-
return animation;
|
|
1009
|
-
},
|
|
1010
|
-
], options, options.duration);
|
|
1011
|
-
}
|
|
1012
|
-
function animate(target, keyframesOrOptions, options) {
|
|
1013
|
-
const factory = isFunction(target) ? animateProgress : animate$1;
|
|
1014
|
-
return factory(target, keyframesOrOptions, options);
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
const POSITIONS = [
|
|
1018
|
-
'top-left',
|
|
1019
|
-
'top-center',
|
|
1020
|
-
'top-right',
|
|
1021
|
-
'bottom-left',
|
|
1022
|
-
'bottom-center',
|
|
1023
|
-
'bottom-right'
|
|
1024
|
-
];
|
|
1025
|
-
function createContainer(position, root, opts) {
|
|
1026
|
-
const c = document.createElement('div');
|
|
1027
|
-
c.className = 'sileo-toaster';
|
|
1028
|
-
c.setAttribute('data-position', position);
|
|
1029
|
-
c.setAttribute('role', 'status');
|
|
1030
|
-
c.setAttribute('aria-live', 'polite');
|
|
1031
|
-
if (opts === null || opts === void 0 ? void 0 : opts.theme)
|
|
1032
|
-
c.setAttribute('data-theme', opts.theme);
|
|
1033
|
-
// offset
|
|
1034
|
-
if ((opts === null || opts === void 0 ? void 0 : opts.offset) !== undefined) {
|
|
1035
|
-
if (typeof opts.offset === 'number' || typeof opts.offset === 'string') {
|
|
1036
|
-
c.style.margin = typeof opts.offset === 'number' ? `${opts.offset}px` : opts.offset;
|
|
1037
|
-
}
|
|
1038
|
-
else if (typeof opts.offset === 'object') {
|
|
1039
|
-
if (opts.offset.top !== undefined)
|
|
1040
|
-
c.style.marginTop = `${opts.offset.top}px`;
|
|
1041
|
-
if (opts.offset.right !== undefined)
|
|
1042
|
-
c.style.marginRight = `${opts.offset.right}px`;
|
|
1043
|
-
if (opts.offset.bottom !== undefined)
|
|
1044
|
-
c.style.marginBottom = `${opts.offset.bottom}px`;
|
|
1045
|
-
if (opts.offset.left !== undefined)
|
|
1046
|
-
c.style.marginLeft = `${opts.offset.left}px`;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
return c;
|
|
1050
|
-
}
|
|
1051
|
-
function renderToast(item) {
|
|
1052
|
-
const el = document.createElement('div');
|
|
1053
|
-
el.className = 'sileo-toast';
|
|
1054
|
-
el.dataset.id = item.id;
|
|
1055
|
-
el.setAttribute('data-type', item.options.type || 'info');
|
|
1056
|
-
el.style.opacity = '0';
|
|
1057
|
-
el.style.transform = 'translateY(-6px)';
|
|
1058
|
-
const body = document.createElement('div');
|
|
1059
|
-
body.style.display = 'flex';
|
|
1060
|
-
body.style.flexDirection = 'column';
|
|
1061
|
-
body.style.flex = '1';
|
|
1062
|
-
const header = document.createElement('div');
|
|
1063
|
-
header.className = 'sileo-toast-header';
|
|
1064
|
-
header.textContent = item.options.title || '';
|
|
1065
|
-
body.appendChild(header);
|
|
1066
|
-
if (item.options.description) {
|
|
1067
|
-
const desc = document.createElement('div');
|
|
1068
|
-
desc.className = 'sileo-toast-desc';
|
|
1069
|
-
desc.textContent = item.options.description;
|
|
1070
|
-
body.appendChild(desc);
|
|
1071
|
-
}
|
|
1072
|
-
el.appendChild(body);
|
|
1073
|
-
if (item.options.button) {
|
|
1074
|
-
const btn = document.createElement('button');
|
|
1075
|
-
btn.className = 'sileo-toast-btn';
|
|
1076
|
-
btn.textContent = item.options.button.title;
|
|
1077
|
-
btn.addEventListener('click', (e) => {
|
|
1078
|
-
e.stopPropagation();
|
|
1079
|
-
try {
|
|
1080
|
-
if (item.options.button && typeof item.options.button.onClick === 'function') {
|
|
1081
|
-
item.options.button.onClick();
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
catch (err) {
|
|
1085
|
-
console.error(err);
|
|
1086
|
-
}
|
|
1087
|
-
});
|
|
1088
|
-
el.appendChild(btn);
|
|
1089
|
-
}
|
|
1090
|
-
const close = document.createElement('button');
|
|
1091
|
-
close.className = 'sileo-toast-close';
|
|
1092
|
-
close.innerHTML = '×';
|
|
1093
|
-
close.addEventListener('click', () => sileo.dismiss(item.id));
|
|
1094
|
-
el.appendChild(close);
|
|
1095
|
-
if (typeof item.options.duration === 'number') {
|
|
1096
|
-
if (item.options.duration > 0) {
|
|
1097
|
-
setTimeout(() => sileo.dismiss(item.id), item.options.duration);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
return el;
|
|
1101
|
-
}
|
|
1102
|
-
function initToasters(root = document.body, positions = ['top-right'], opts) {
|
|
1103
|
-
const containers = {};
|
|
1104
|
-
positions.forEach((pos) => {
|
|
1105
|
-
const c = createContainer(pos, root, opts);
|
|
1106
|
-
root.appendChild(c);
|
|
1107
|
-
containers[pos] = c;
|
|
1108
|
-
});
|
|
1109
|
-
if (opts === null || opts === void 0 ? void 0 : opts.options)
|
|
1110
|
-
window.sileo._globalOptions = opts.options;
|
|
1111
|
-
if (opts === null || opts === void 0 ? void 0 : opts.theme)
|
|
1112
|
-
window.sileo._theme = opts.theme;
|
|
1113
|
-
// fallback dinámico: si solo hay una posición, usarla como default
|
|
1114
|
-
const fallbackPosition = positions.length === 1 ? positions[0] : 'top-right';
|
|
1115
|
-
function rerender(items) {
|
|
1116
|
-
// Lanzar advertencia para todos los toasts con posición no inicializada
|
|
1117
|
-
items.forEach((t) => {
|
|
1118
|
-
if (t.options.position && !containers[t.options.position]) {
|
|
1119
|
-
console.warn(`[sileo] Toast con posición "${t.options.position}" pero no se inicializó ningún contenedor para esa posición. Inicializa con initToasters(..., ['${t.options.position}']) para mostrarlo.`);
|
|
1120
|
-
}
|
|
1121
|
-
});
|
|
1122
|
-
positions.forEach((pos) => {
|
|
1123
|
-
const container = containers[pos];
|
|
1124
|
-
// fallback dinámico
|
|
1125
|
-
const visible = items.filter((t) => (t.options.position || fallbackPosition) === pos);
|
|
1126
|
-
// Diff existing children y animar salida de toasts removidos
|
|
1127
|
-
const visibleIds = new Set(visible.map((v) => v.id));
|
|
1128
|
-
const existing = Array.from(container.children);
|
|
1129
|
-
existing.forEach((child) => {
|
|
1130
|
-
const id = child.dataset.id;
|
|
1131
|
-
if (!id || !visibleIds.has(id)) {
|
|
1132
|
-
// animar salida y luego remover
|
|
1133
|
-
animate(child, { opacity: 0, y: -8 }, { duration: 0.18 }).finished.then(() => child.remove());
|
|
1134
|
-
}
|
|
1135
|
-
});
|
|
1136
|
-
// Añadir nuevos toasts
|
|
1137
|
-
visible.forEach((t) => {
|
|
1138
|
-
if (!container.querySelector(`[data-id="${t.id}"]`)) {
|
|
1139
|
-
const node = renderToast(t);
|
|
1140
|
-
container.appendChild(node);
|
|
1141
|
-
// animar entrada
|
|
1142
|
-
requestAnimationFrame(() => {
|
|
1143
|
-
animate(node, { opacity: [0, 1], y: [-6, 0] }, { duration: 0.24 });
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
});
|
|
1147
|
-
});
|
|
1148
|
-
}
|
|
1149
|
-
const unsub = sileo.subscribe(rerender);
|
|
1150
|
-
return {
|
|
1151
|
-
destroy() {
|
|
1152
|
-
unsub();
|
|
1153
|
-
Object.values(containers).forEach((c) => c.remove());
|
|
1154
|
-
}
|
|
1155
|
-
};
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
// convenience: keep the previous shape where `notify` was the quick-show function
|
|
1159
|
-
const notify = notify$1.show.bind(notify$1);
|
|
1160
|
-
const defaultExport = { sileo: sileo, initToasters, notify, controller: notify$1 };
|
|
1161
|
-
// backward compatibility: expose `notifications` key pointing to the same API object
|
|
1162
|
-
defaultExport.notifications = defaultExport;
|
|
1163
|
-
|
|
1164
|
-
export { POSITIONS, defaultExport as default, initToasters, notify };
|