@svelte-use/shared
Version:
292 lines (278 loc) • 7.61 kB
JavaScript
import { get_current_component } from 'svelte/internal';
import { writable as writable$1, get } from 'svelte/store';
import { onDestroy, onMount } from 'svelte';
/**
* Call onDestroy() if it's inside a component lifecycle, if not, do nothing.
*
* @param fn
*/
function tryOnDestroy(fn) {
if (tryGetCurrentComponent()) {
onDestroy(fn);
}
}
function writable(value, start = noop) {
const s = writable$1(value, start);
const runs = new Set();
const invalidates = new Set();
let unsubscribe;
const prepareUnsubscribe = () => {
unsubscribe = s.subscribe((val) => runAll([...runs], val), (val) => runAll([...invalidates], val));
};
if (tryGetCurrentComponent()) {
prepareUnsubscribe();
}
function set(newValue) {
s.set(newValue);
}
function update(fn) {
s.update(fn);
}
function subscribe(run = noop, invalidate = noop) {
if (!unsubscribe) {
prepareUnsubscribe();
}
runs.add(run);
invalidates.add(invalidate);
const v = get(s);
run(v);
invalidate(v);
return () => {
runs.delete(run);
invalidates.delete(invalidate);
};
}
const stop = () => {
runs.clear();
invalidates.clear();
if (unsubscribe) {
unsubscribe();
}
};
tryOnDestroy(stop);
return {
set,
update,
subscribe,
};
}
const isClient = typeof window !== 'undefined';
const isDef = (val) => typeof val !== 'undefined';
const assert = (condition, ...infos) => {
// eslint-disable-next-line no-console
if (!condition)
console.warn(...infos);
};
const toString = Object.prototype.toString;
const isBoolean = (val) => typeof val === 'boolean';
const isFunction = (val) => typeof val === 'function';
const isNumber = (val) => typeof val === 'number';
const isString = (val) => typeof val === 'string';
const isObject = (val) => toString.call(val) === '[object Object]';
const isWindow = (val) => typeof window !== 'undefined' && toString.call(val) === '[object Window]';
const isReadable = (store) => {
return store && isFunction(store.subscribe);
};
const isWritable = (store) => {
return (store && ['subscribe', 'set', 'update'].every((n) => isFunction(store[n])));
};
const now = () => Date.now();
const timestamp = () => +Date.now();
const clamp = (n, min, max) => Math.min(max, Math.max(min, n));
const noop = () => { };
const rand = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
/**
* Run a function
*/
const run = (fn, ...args) => {
fn(...args);
};
/**
* Run all functions
*/
const runAll = (fns, ...args) => {
fns.forEach((fn) => run(fn, ...args));
};
/**
* Silent `get_current_component`. Call `get_current_component()` without throw error.
*/
function tryGetCurrentComponent() {
let currentComponent;
try {
currentComponent = get_current_component();
}
catch (_) { }
return currentComponent;
}
function promiseTimeout(ms, throwOnTimeout = false, reason = 'Timeout') {
return new Promise((resolve, reject) => {
if (throwOnTimeout) {
setTimeout(() => reject(reason), ms);
}
else {
setTimeout(resolve, ms);
}
});
}
function unstore(val) {
return isReadable(val) ? get(val) : val;
}
function toReadable(val) {
if (isWritable(val)) {
return {
subscribe: val.subscribe,
};
}
return isReadable(val) ? val : readable(val);
}
function toWritable(val) {
return isWritable(val) ? val : writable(val);
}
function readable(value, start = noop) {
return {
subscribe: writable(value, start).subscribe,
};
}
/**
* Call `onMount()` if it's inside a component lifecycle, if not, run just call the function.
*
* @param fn
*/
function tryOnMount(fn) {
if (tryGetCurrentComponent()) {
onMount(fn);
}
else {
const cb = fn() || noop;
tryOnDestroy(cb);
}
}
/**
* Wrapper for `setInterval` with controls
*
* @param cb
* @param interval
* @param options
*/
function useIntervalFn(cb, interval = 1000, options = {}) {
const { immediate = true, immediateCallback = false } = options;
let timer = null;
const isActive = writable(false);
function clean() {
if (timer) {
clearInterval(timer);
timer = null;
}
}
function pause() {
isActive.set(false);
clean();
}
function resume() {
if (interval <= 0) {
return;
}
isActive.set(true);
if (immediateCallback) {
cb();
}
clean();
timer = setInterval(cb, interval);
}
if (immediate && isClient) {
resume();
}
tryOnDestroy(pause);
return {
isActive,
pause,
resume,
};
}
function useInterval(interval = 1000, options = {}) {
const { controls: exposeControls = false, immediate = true } = options;
const counter = writable(0);
const controls = useIntervalFn(() => {
counter.update((c) => (c += 1));
}, interval, { immediate });
if (exposeControls) {
return Object.assign({ counter }, controls);
}
return counter;
}
/**
* Wrapper for `setTimeout` with controls.
*
* @param fn
* @param interval
* @param options
*/
function useTimeoutFn(cb, interval, options = {}) {
const { immediate = true } = options;
const isPending = writable(false);
let timer = null;
function clear() {
if (timer) {
clearTimeout(timer);
timer = null;
}
}
function stop() {
isPending.set(false);
clear();
}
function start(...args) {
clear();
isPending.set(true);
timer = setTimeout(() => {
isPending.set(false);
timer = null;
cb(...args);
}, interval);
}
if (immediate) {
isPending.set(true);
if (isClient) {
start();
}
}
tryOnDestroy(stop);
return {
isPending,
start,
stop,
};
}
function useTimeout(interval = 1000, options = {}) {
const { controls: exposeControls = false } = options;
const controls = useTimeoutFn(noop, interval, options);
const ready = readable(true, (set) => {
controls.isPending.subscribe((value) => set(!value));
});
if (exposeControls) {
return Object.assign({ ready }, controls);
}
else {
return ready;
}
}
/**
* Shorthand for watching value to be truthy
*
* @see https://svelte-use.vercel.app/shared/whenever
*/
function whenever(source, cb) {
const store = toWritable(source);
let ov;
return store.subscribe((v) => {
if (v) {
cb(v, ov);
ov = v;
}
});
}
export { assert, clamp, isBoolean, isClient, isDef, isFunction, isNumber, isObject, isReadable, isString, isWindow, isWritable, noop, now, promiseTimeout, rand, readable, run, runAll, timestamp, toReadable, toWritable, tryGetCurrentComponent, tryOnDestroy, tryOnMount, unstore, useInterval, useIntervalFn, useTimeout, useTimeoutFn, whenever, writable };