UNPKG

@svelte-use/shared

Version:
292 lines (278 loc) 7.61 kB
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 };