@stackoverfloweth/vue-compositions
Version:
A collection of reusable vue compositions.
1 lines • 944 kB
Source Map (JSON)
{"version":3,"file":"vue-compositions.mjs","sources":["../src/useBoolean/useBoolean.ts","../src/useMutationObserver/useMutationObserver.ts","../src/useResizeObserver/useResizeObserver.ts","../src/utilities/global.ts","../src/utilities/window.ts","../src/useComputedStyle/useComputedStyle.ts","../src/useElementRect/useElementRect.ts","../src/useChildrenAreWrapped/useChildrenAreWrapped.ts","../src/utilities/tryOnScopeDispose.ts","../src/useEventListener/useEventListener.ts","../src/useGlobalEventListener/useGlobalEventListener.ts","../src/useClickOutside/useClickOutside.ts","../node_modules/lodash.debounce/index.js","../src/useDebouncedRef/useDebouncedRef.ts","../src/useElementWidth/useElementWidth.ts","../src/useIntersectionObserver/useIntersectionObserver.ts","../src/utilities/isSame.ts","../src/useIsSame/useIsSame.ts","../src/utilities/arrays.ts","../src/useKeyDown/useKeyDown.ts","../src/useMedia/useMedia.ts","../src/useMousePosition/useMousePosition.ts","../src/useNow/useNow.ts","../src/usePatchRef/usePatchRef.ts","../src/usePositionStickyObserver/usePositionStickyObserver.ts","../src/useScrollLinking/useScrollLinking.ts","../src/useStorage/storage.ts","../src/useStorage/useStorage.ts","../src/useSubscription/utilities/createActions.ts","../src/useSubscription/utilities/reactivity.ts","../src/useSubscription/utilities/subscriptions.ts","../node_modules/lodash/lodash.js","../src/useSubscription/useSubscriptionDevtools.ts","../src/useSubscription/models/subscription.ts","../src/useSubscription/models/channel.ts","../src/useSubscription/models/manager.ts","../src/utilities/functions.ts","../src/utilities/getValidWatchSource.ts","../node_modules/lodash.isequal/index.js","../src/utilities/dates.ts","../src/utilities/objects.ts","../src/utilities/uniqueValueWatcher.ts","../src/useSubscription/useSubscription.ts","../src/useSubscription/utilities/refresh.ts","../src/useSubscription/useSubscriptionWithDependencies.ts","../src/useValidation/ValidationAbortedError.ts","../src/useValidation/ValidationExecutor.ts","../src/useValidationObserver/useValidationObserver.ts","../src/utilities/injection.ts","../src/useValidation/useValidation.ts","../src/useVisibilityObserver/useVisibilityObserver.ts","../node_modules/@vue/devtools-api/lib/esm/env.js","../node_modules/@vue/devtools-api/lib/esm/const.js","../node_modules/@vue/devtools-api/lib/esm/time.js","../node_modules/@vue/devtools-api/lib/esm/proxy.js","../node_modules/@vue/devtools-api/lib/esm/index.js","../src/devtools.ts"],"sourcesContent":["import { ref, Ref, MaybeRef } from 'vue'\n\ntype UseBoolean = {\n value: Ref<boolean>,\n toggle: () => void,\n setTrue: () => void,\n setFalse: () => void,\n}\n\n/**\n * `useBoolean` is a utility composition for managing a boolean state.\n * It returns an object with `value`, `toggle`, `setTrue`, and `setFalse` properties.\n *\n * @param {MaybeRef<boolean>} [valueRef] - Optional parameter. A Vue ref object or a boolean that holds the initial state.\n *\n * @returns {UseBoolean} - An object with the following properties:\n * `value`: a Vue ref object that holds the current boolean state.\n * `toggle`: a method to toggle the state.\n * `setTrue`: a method to set the state to true.\n * `setFalse`: a method to set the state to false.\n *\n * @example\n * const { toggle, setTrue, setFalse, value } = useBoolean()\n * toggle() // toggle the state\n * setTrue() // set the state to true\n * setFalse() // set the state to false\n * console.log(value.value) // check the current state\n */\nexport function useBoolean(valueRef?: MaybeRef<boolean>): UseBoolean {\n const value = ref(valueRef ?? false)\n\n const toggle = (): void => {\n value.value = !value.value\n }\n\n const setTrue = (): void => {\n value.value = true\n }\n\n const setFalse = (): void => {\n value.value = false\n }\n\n return { value, toggle, setTrue, setFalse }\n}\n\n","import { onMounted, onUnmounted, ref, Ref } from 'vue'\n\nexport type UseMutationObserverResponse = {\n observe: (element: Element | Ref<Element | undefined>, options: MutationObserverInit) => void,\n disconnect: () => void,\n check: (element: Element | Ref<Element | undefined>, options: MutationObserverInit) => void,\n}\n\nexport function useMutationObserver(callback: MutationCallback): UseMutationObserverResponse {\n\n let mutationObserver: MutationObserver | null = null\n\n const observe: UseMutationObserverResponse['observe'] = (element, options) => {\n const elementRef = ref(element)\n const observer = getObserver()\n\n if (elementRef.value) {\n observer.observe(elementRef.value, options)\n }\n }\n\n const disconnect: UseMutationObserverResponse['disconnect'] = () => {\n const observer = getObserver()\n\n observer.disconnect()\n }\n\n const check: UseMutationObserverResponse['check'] = (element, options) => {\n const elementRef = ref(element)\n if (!elementRef.value) {\n return\n }\n\n const observer = new MutationObserver(callback)\n\n observer.observe(elementRef.value, options)\n\n setTimeout(() => observer.disconnect(), 100)\n }\n\n function getObserver(): MutationObserver {\n if (!mutationObserver) {\n createObserver()\n }\n\n return mutationObserver!\n }\n\n function createObserver(): void {\n mutationObserver = new MutationObserver(callback)\n }\n\n onMounted(() => {\n createObserver()\n })\n\n onUnmounted(() => {\n disconnect()\n })\n\n return {\n observe,\n disconnect,\n check,\n }\n}","import { onMounted, onUnmounted, ref, Ref } from 'vue'\n\nexport type UseResizeObserverResponse = {\n observe: (element: Element | Ref<Element | undefined>) => void,\n unobserve: (element: Element | Ref<Element | undefined>) => void,\n disconnect: () => void,\n check: (element: Element | Ref<Element | undefined>) => void,\n}\n\nexport type UseResizeObserverCallback = (entries: ResizeObserverEntry[]) => void\n\nexport function useResizeObserver(callback: UseResizeObserverCallback): UseResizeObserverResponse {\n\n let resizeObserver: ResizeObserver | null = null\n\n const observe: UseResizeObserverResponse['observe'] = (element) => {\n const elementRef = ref(element)\n const observer = getObserver()\n\n if (elementRef.value) {\n observer.observe(elementRef.value)\n }\n }\n\n const unobserve: UseResizeObserverResponse['unobserve'] = (element) => {\n const elementRef = ref(element)\n const observer = getObserver()\n\n if (elementRef.value) {\n observer.unobserve(elementRef.value)\n }\n }\n\n const disconnect: UseResizeObserverResponse['disconnect'] = () => {\n const observer = getObserver()\n\n observer.disconnect()\n }\n\n const check: UseResizeObserverResponse['check'] = (element) => {\n const elementRef = ref(element)\n if (!elementRef.value) {\n return\n }\n\n const observer = new ResizeObserver(callback)\n\n observer.observe(elementRef.value)\n\n setTimeout(() => observer.disconnect(), 100)\n }\n\n function getObserver(): ResizeObserver {\n if (!resizeObserver) {\n createObserver()\n }\n\n return resizeObserver!\n }\n\n function createObserver(): void {\n resizeObserver = new ResizeObserver(callback)\n }\n\n onMounted(() => {\n createObserver()\n })\n\n onUnmounted(() => {\n disconnect()\n })\n\n return {\n observe,\n disconnect,\n unobserve,\n check,\n }\n}","// https://stackoverflow.com/a/62557418/3511012\nexport function globalExists(varName: string): boolean {\n const globalEval = eval\n try {\n globalEval(varName)\n return true\n } catch {\n return false\n }\n}","import { globalExists } from '@/utilities/global'\n\nexport function getWindowComputedStyle(element: Element | undefined): CSSStyleDeclaration | undefined {\n if (!globalExists('window') || !element) {\n return undefined\n }\n\n return window.getComputedStyle(element)\n}","import { ref, Ref, watch } from 'vue'\nimport { useMutationObserver } from '@/useMutationObserver/useMutationObserver'\nimport { useResizeObserver } from '@/useResizeObserver/useResizeObserver'\nimport { getWindowComputedStyle } from '@/utilities/window'\n\nexport function useComputedStyle(element: Element | Ref<Element | undefined>): Ref<CSSStyleDeclaration | undefined> {\n const elementRef = ref(element)\n const initialStyle = getWindowComputedStyle(elementRef.value)\n const style = ref(initialStyle)\n\n function observerCallback([entry]: { target: Node }[]): void {\n if (nodeIsElement(entry.target)) {\n updateStyleRef(entry.target)\n }\n }\n\n function updateStyleRef(element: Element): void {\n const computedStyle = getWindowComputedStyle(element)\n\n if (computedStyle) {\n style.value = computedStyle\n }\n }\n\n const mutationObserver = useMutationObserver(observerCallback)\n const resizeObserver = useResizeObserver(observerCallback)\n\n watch(elementRef, element => {\n if (element) {\n updateStyleRef(element)\n\n mutationObserver.disconnect()\n mutationObserver.observe(elementRef, { attributes: true, childList: true })\n resizeObserver.disconnect()\n resizeObserver.observe(elementRef)\n }\n }, { immediate: true })\n\n return style\n}\n\nfunction nodeIsElement(node: Node): node is Element {\n const element = node as Element\n\n return !!element.attributes\n}","/* eslint-disable id-length */\nimport { ref, Ref, watch } from 'vue'\nimport { useResizeObserver } from '@/useResizeObserver/useResizeObserver'\n\ntype ElementRect = {\n height: Ref<number>,\n width: Ref<number>,\n x: Ref<number>,\n y: Ref<number>,\n left: Ref<number>,\n top: Ref<number>,\n right: Ref<number>,\n bottom: Ref<number>,\n}\n\nexport function useElementRect(element: Element | Ref<Element | undefined>): ElementRect {\n const elementRef = ref(element)\n const clientRect = {\n height: ref(0),\n width: ref(0),\n x: ref(0),\n y: ref(0),\n left: ref(0),\n top: ref(0),\n right: ref(0),\n bottom: ref(0),\n }\n\n function assignClientRect(element: Element): void {\n const rect = element.getBoundingClientRect()\n\n clientRect.height.value = rect.height\n clientRect.width.value = rect.width\n clientRect.x.value = rect.x\n clientRect.y.value = rect.y\n clientRect.left.value = rect.left\n clientRect.top.value = rect.top\n clientRect.right.value = rect.right\n clientRect.bottom.value = rect.bottom\n }\n\n const observer = useResizeObserver(([entry]) => assignClientRect(entry.target))\n\n watch(elementRef, element => {\n if (element) {\n assignClientRect(element)\n\n observer.disconnect()\n observer.observe(elementRef)\n }\n }, { immediate: true })\n\n return clientRect\n}","import { computed, ComputedRef, ref, Ref } from 'vue'\nimport { useComputedStyle } from '@/useComputedStyle/useComputedStyle'\nimport { useElementRect } from '@/useElementRect/useElementRect'\nimport { getWindowComputedStyle } from '@/utilities/window'\n\nexport function useChildrenAreWrapped(children: Element[] | Ref<Element[]>, container: Element | Ref<Element | undefined>): ComputedRef<boolean> {\n const childrenRef = ref(children)\n const containerRef = ref(container)\n\n const { width } = useElementRect(containerRef)\n const containerStyles = useComputedStyle(containerRef)\n\n return computed(() => {\n const paddingLeft = getPxInt(containerStyles.value?.paddingLeft)\n const paddingRight = getPxInt(containerStyles.value?.paddingRight)\n const containerGap = getPxInt(containerStyles.value?.columnGap)\n\n const containerWidth = width.value - paddingLeft - paddingRight\n const childrenWidth = getChildrenWidth(childrenRef.value, containerGap)\n\n return childrenWidth > containerWidth\n })\n}\n\nfunction getPxInt(style: string | undefined): number {\n if (!style) {\n return 0\n }\n\n return parseInt(style)\n}\n\nfunction getChildrenWidth(elements: Element[], gap: number): number {\n return elements\n .map(getWindowComputedStyle)\n .reduce((sum, style) => {\n if (style) {\n sum += parseInt(style.width)\n sum += parseInt(style.marginLeft) + parseInt(style.marginRight)\n\n if (style.boxSizing === 'border-box') {\n sum += parseInt(style.borderLeftWidth) + parseInt(style.borderRightWidth)\n }\n }\n\n return sum + gap\n }, 0)\n}","import { getCurrentScope, onScopeDispose } from 'vue'\n\nexport function tryOnScopeDispose(callback: () => void): boolean {\n if (getCurrentScope()) {\n onScopeDispose(callback)\n\n return true\n }\n\n return false\n}","import { ref, watch, toValue, MaybeRefOrGetter } from 'vue'\nimport { tryOnScopeDispose } from '@/utilities/tryOnScopeDispose'\n\nexport type UseEventListener = {\n add: () => void,\n remove: () => void,\n}\n\nexport type UseEventListenerOptions = AddEventListenerOptions & {\n immediate?: boolean,\n}\n\nconst defaultOptions: UseEventListenerOptions = {\n immediate: true,\n}\n\nexport function useEventListener<K extends keyof DocumentEventMap>(target: MaybeRefOrGetter<Document | undefined | null>, key: K, callback: (this: Document, event: DocumentEventMap[K]) => unknown, options?: UseEventListenerOptions): UseEventListener\nexport function useEventListener<K extends keyof HTMLElementEventMap>(target: MaybeRefOrGetter<HTMLElement | undefined | null>, key: K, callback: (this: HTMLElement, event: HTMLElementEventMap[K]) => unknown, options?: UseEventListenerOptions): UseEventListener\nexport function useEventListener<K extends keyof WindowEventMap>(target: MaybeRefOrGetter<Window | undefined | null>, key: K, callback: (this: Window, event: WindowEventMap[K]) => unknown, options?: UseEventListenerOptions): UseEventListener\n// eslint-disable-next-line max-params\nexport function useEventListener<K extends string>(target: MaybeRefOrGetter<Window | Node | undefined | null>, key: K, callback: (this: Node | Window, event: Event) => unknown, options: UseEventListenerOptions = {}): UseEventListener {\n const { immediate, ...listenerOptions } = { ...defaultOptions, ...options }\n const manualMode = ref(!immediate)\n\n function addEventListener(): void {\n toValue(target)?.addEventListener(key, callback, listenerOptions)\n }\n\n function removeEventListener(): void {\n toValue(target)?.removeEventListener(key, callback, listenerOptions)\n }\n\n tryOnScopeDispose(removeEventListener)\n\n watch(() => toValue(target), () => {\n if (!manualMode.value) {\n removeEventListener()\n addEventListener()\n }\n }, { immediate: true })\n\n return {\n add: () => {\n addEventListener()\n },\n remove: () => {\n manualMode.value = true\n removeEventListener()\n },\n }\n}","import { useEventListener } from '@/useEventListener'\n\ntype UseGlobalEventListener = {\n add: () => void,\n remove: () => void,\n}\n\n/**\n * `useGlobalEventListener` is a composition for managing global event listeners on the Document object.\n * It returns `add` and `remove` methods for manually adding and removing the associated event listener.\n *\n * @param {K} type - The type of the event. This is a key of DocumentEventMap.\n * @param {function} callback - The callback function to be executed when the event is triggered.\n * The context of this function is the Document object and it receives an event of type K.\n * @param {boolean | AddEventListenerOptions} [options] - Optional parameter. An options object that specifies\n * characteristics about the event listener.\n * If this parameter is a boolean, it indicates whether the\n * event should be executed in the capturing or in the bubbling phase.\n *\n * @returns {UseGlobalEventListener} - An object with two methods: `add` and `remove`.\n * `add` adds the event listener to the document and `remove` removes it.\n * The event listener is automatically added upon creation and removed when the scope is disposed.\n *\n * @example\n * function handleEvent(event: Event) {\n * // Respond to event\n * }\n * const { remove } = useGlobalEventListener('click', handleEvent)\n * remove() // remove the listener manually\n */\nexport function useGlobalEventListener<K extends keyof DocumentEventMap>(\n type: K,\n callback: (this: Document, event: DocumentEventMap[K]) => unknown,\n options?: AddEventListenerOptions,\n): UseGlobalEventListener {\n return useEventListener(document, type, callback, options)\n}\n\n","import { MaybeRefOrGetter, onScopeDispose, toValue } from 'vue'\nimport { useGlobalEventListener } from '@/useGlobalEventListener'\n\ntype ClickOutsideEntry = {\n element: MaybeRefOrGetter<Element>,\n callback: () => void,\n}\n\nconst callbacks = new Map<symbol, ClickOutsideEntry>()\n\nfunction handleClick(event: MouseEvent): void {\n for (const { element, callback } of callbacks.values()) {\n const elementValue = toValue(element)\n\n if (!elementValue.contains(event.target as Node)) {\n callback()\n }\n }\n}\n\nconst { add, remove } = useGlobalEventListener('click', handleClick, { capture: true })\n\nfunction tryTeardownEventListener(): void {\n if (callbacks.size > 0) {\n return\n }\n\n remove()\n}\n\nfunction tryAddEventListener(): void {\n if (callbacks.size > 0) {\n return\n }\n\n add()\n}\n\nexport type UseClickOutsideCallbackFunction = () => void\n\nexport type UseClickOutside = {\n off: () => void,\n on: () => void,\n}\n\nexport function useClickOutside(element: MaybeRefOrGetter<Element>, callback: UseClickOutsideCallbackFunction): UseClickOutside {\n const id = Symbol('useClickOutside')\n\n callbacks.set(id, { element, callback })\n\n tryAddEventListener()\n\n function off(): void {\n callbacks.delete(id)\n tryTeardownEventListener()\n }\n\n function on(): void {\n callbacks.set(id, { element, callback })\n tryAddEventListener()\n }\n\n onScopeDispose(() => {\n off()\n tryTeardownEventListener()\n })\n\n return {\n off,\n on,\n }\n}","/**\n * lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the `TypeError` message for \"Functions\" methods. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/** Used to match leading and trailing whitespace. */\nvar reTrim = /^\\s+|\\s+$/g;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max,\n nativeMin = Math.min;\n\n/**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\nvar now = function() {\n return root.Date.now();\n};\n\n/**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\nfunction debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n result = wait - timeSinceLastCall;\n\n return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;\n }\n\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n }\n\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && objectToString.call(value) == symbolTag);\n}\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = value.replace(reTrim, '');\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = debounce;\n","import debounce from 'lodash.debounce'\nimport { ref, Ref, watch, watchEffect, isReadonly } from 'vue'\n\nexport function useDebouncedRef<T>(input: Ref<T>, waitMs: Ref<number> | number): Ref<T> {\n const waitRef = ref(waitMs)\n const copy = ref(input.value) as Ref<T>\n const update = debounce((value: T) => {\n if (value !== copy.value) {\n copy.value = value\n }\n }, waitRef.value)\n\n watchEffect(() => update(input.value))\n\n if (!isReadonly(input)) {\n watch(copy, value => {\n if (value !== input.value) {\n input.value = value\n }\n }, { flush: 'sync' })\n }\n\n return copy\n}\n","import { ref, Ref, watchEffect } from 'vue'\nimport { useResizeObserver, UseResizeObserverCallback } from '@/useResizeObserver/useResizeObserver'\n\n/**\n * @deprecated use useElementRect instead\n */\nexport function useElementWidth(element: HTMLElement | undefined | Ref<HTMLElement | undefined>): Ref<number> {\n const elementRef = ref(element)\n const widthInPixels = ref<number>(0)\n\n const callback: UseResizeObserverCallback = function([entry]: ResizeObserverEntry[]) {\n const { width } = entry.target.getBoundingClientRect()\n\n widthInPixels.value = width\n }\n\n const observer = useResizeObserver(callback)\n\n watchEffect(() => {\n if (elementRef.value) {\n const { width } = elementRef.value.getBoundingClientRect()\n\n widthInPixels.value = width\n observer.disconnect()\n observer.observe(elementRef)\n }\n })\n\n return widthInPixels\n}","import { onMounted, onUnmounted, ref, Ref, unref, watch, toValue, MaybeRef, MaybeRefOrGetter } from 'vue'\n\nexport type UseIntersectionObserverResponse = {\n observe: (element: MaybeRefOrGetter<HTMLElement | undefined>) => void,\n unobserve: (element: MaybeRefOrGetter<HTMLElement | undefined>) => void,\n disconnect: () => void,\n check: (element: MaybeRefOrGetter<HTMLElement | undefined>) => void,\n}\n\nexport type UseIntersectionObserverOptions = {\n root?: Element | Document | null | Ref<Element | null>,\n rootMargin?: string,\n threshold?: number | number[],\n}\n\nexport type UseIntersectionObserverCallback = (entries: IntersectionObserverEntry[]) => void\n\nexport function useIntersectionObserver(callback: UseIntersectionObserverCallback, options: MaybeRef<UseIntersectionObserverOptions> = {}): UseIntersectionObserverResponse {\n const optionsRef = ref(options)\n const elements = new Set<HTMLElement>()\n\n let intersectionObserver: IntersectionObserver | null = null\n\n function observe(element: MaybeRefOrGetter<HTMLElement | undefined>): void {\n const value = toValue(element)\n const observer = getObserver()\n\n if (value) {\n observer.observe(value)\n elements.add(value)\n }\n }\n\n function unobserve(element: MaybeRefOrGetter<HTMLElement | undefined>): void {\n const value = toValue(element)\n const observer = getObserver()\n\n if (value) {\n observer.unobserve(value)\n elements.delete(value)\n }\n }\n\n function disconnect(): void {\n const observer = getObserver()\n\n observer.disconnect()\n elements.clear()\n }\n\n function getOptions({ root, rootMargin, threshold }: UseIntersectionObserverOptions): IntersectionObserverInit {\n return {\n root: unref(root),\n rootMargin,\n threshold,\n }\n }\n\n function check(element: MaybeRefOrGetter<HTMLElement | undefined>): void {\n const value = toValue(element)\n\n if (!value) {\n return\n }\n\n const observer = new IntersectionObserver(callback, getOptions(optionsRef.value))\n\n observer.observe(value)\n\n setTimeout(() => observer.disconnect(), 100)\n }\n\n function getObserver(): IntersectionObserver {\n if (!intersectionObserver) {\n createObserver()\n }\n\n return intersectionObserver!\n }\n\n function createObserver(): void {\n if (intersectionObserver) {\n intersectionObserver.disconnect()\n }\n\n intersectionObserver = new IntersectionObserver(callback, getOptions(optionsRef.value))\n\n elements.forEach(element => intersectionObserver!.observe(element))\n }\n\n onMounted(() => {\n createObserver()\n })\n\n onUnmounted(() => {\n disconnect()\n })\n\n watch(optionsRef, () => {\n createObserver()\n })\n\n return {\n observe,\n disconnect,\n unobserve,\n check,\n }\n}","export function isSame(valueA: unknown, valueB: unknown): boolean {\n if (stringifiesToNull(valueA) && stringifiesToNull(valueB)) {\n return valueA === valueB\n }\n\n return JSON.stringify(valueA) === JSON.stringify(valueB)\n}\n\n// JSON.stringify(null)\n// JSON.stringify(Infinity)\n// JSON.stringify(-Infinity)\nfunction stringifiesToNull(value: unknown): boolean {\n return JSON.stringify(value) === 'null'\n}","import { computed, ComputedRef, ref, MaybeRef } from 'vue'\nimport { isSame } from '@/utilities/isSame'\n\nexport function useIsSame(valueA: MaybeRef, valueB: MaybeRef): ComputedRef<boolean> {\n const valueARef = ref(valueA)\n const valueBRef = ref(valueB)\n\n return computed(() => isSame(valueARef.value, valueBRef.value))\n}","export function asArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value]\n}\n\nexport function isArray(value: unknown): value is unknown[] {\n return Array.isArray(value)\n}","import { ComputedRef, computed, reactive, unref, MaybeRef } from 'vue'\nimport { MaybeArray } from '@/types/maybe'\nimport { asArray } from '@/utilities/arrays'\nimport { tryOnScopeDispose } from '@/utilities/tryOnScopeDispose'\n\nexport type UseKeyDown = {\n down: ComputedRef<boolean>,\n connect: () => void,\n disconnect: () => void,\n}\n\nexport type UseKeyDownCallback = (event: KeyboardEvent) => void\n\nexport type UseKeyDownArgs = [key: MaybeRef<MaybeArray<string>>, callback?: UseKeyDownCallback]\n\nfunction useKeyDownFactory(): (...args: UseKeyDownArgs) => UseKeyDown {\n const downKeys = reactive<Set<string>>(new Set())\n const callbacks = new Set<UseKeyDownCallback>()\n\n function keyDownCallback(event: KeyboardEvent): void {\n downKeys.add(event.key)\n\n callbacks.forEach(callback => callback(event))\n }\n\n function keyUpCallback(event: KeyboardEvent): void {\n downKeys.delete(event.key)\n }\n\n document.addEventListener('keydown', keyDownCallback)\n document.addEventListener('keyup', keyUpCallback)\n\n return (...[key, callback]: UseKeyDownArgs): UseKeyDown => {\n const keys = computed(() => asArray(unref(key)))\n const down = computed(() => keys.value.some(key => downKeys.has(key)))\n\n const filteredCallback: UseKeyDownCallback = (event) => {\n if (keys.value.includes(event.key)) {\n callback?.(event)\n }\n }\n\n const connect = (): void => {\n callbacks.add(filteredCallback)\n }\n\n const disconnect = (): void => {\n callbacks.delete(filteredCallback)\n }\n\n connect()\n\n tryOnScopeDispose(disconnect)\n\n return {\n down,\n connect,\n disconnect,\n }\n }\n}\n\nexport const useKeyDown = useKeyDownFactory()","import { getCurrentInstance, isRef, onUnmounted, ref, Ref, unref, watch } from 'vue'\n\n/**\n * @deprecated use useMedia instead\n */\nexport const media = useMedia\n\nexport function useMedia(query: Ref<string> | string): Ref<boolean> {\n let mediaQuery = window.matchMedia(unref(query))\n const matches = ref(mediaQuery.matches)\n let unwatch: ReturnType<typeof watch> | undefined\n\n function updateMatches(event: MediaQueryListEvent): void {\n matches.value = event.matches\n }\n\n mediaQuery.addEventListener('change', updateMatches)\n\n if (isRef(query)) {\n unwatch = watch(query, () => {\n mediaQuery.removeEventListener('change', updateMatches)\n mediaQuery = window.matchMedia(unref(query))\n mediaQuery.addEventListener('change', updateMatches)\n })\n }\n\n if (getCurrentInstance()) {\n onUnmounted(() => {\n mediaQuery.removeEventListener('change', updateMatches)\n\n if (unwatch) {\n unwatch()\n }\n })\n }\n\n return matches\n}\n","import { onScopeDispose, reactive, ref } from 'vue'\nimport { useGlobalEventListener } from '@/useGlobalEventListener'\n\nexport type MousePosition = {\n x: number,\n y: number,\n}\n\nexport type UseMousePosition = {\n position: MousePosition,\n positionAtLastClick: MousePosition,\n}\n\nconst position = reactive<MousePosition>({ x: 0, y: 0 })\nconst positionAtLastClick = reactive<MousePosition>({ x: 0, y: 0 })\n\nconst updatePositionAtLastClick = (): void => {\n Object.assign(positionAtLastClick, position)\n}\n\nconst updateMousePosition = (event: MouseEvent): void => {\n position.x = event.clientX\n position.y = event.clientY\n\n if (positionAtLastClick.x === 0 && positionAtLastClick.y === 0) {\n updatePositionAtLastClick()\n }\n}\n\nconst listeners = ref(0)\n\nconst { add: addMouseMoveEventListener, remove: removeMouseMoveEventListener } = useGlobalEventListener('mousemove', updateMousePosition, { passive: true })\nconst { add: addClickEventListener, remove: removeClickEventListener } = useGlobalEventListener('click', updatePositionAtLastClick, { capture: true })\nconst { add: addContextMenuEventListener, remove: removeContextMenuEventListener } = useGlobalEventListener('contextmenu', updatePositionAtLastClick, { capture: true })\n\nfunction tryTeardownEventListeners(): void {\n if (listeners.value > 0) {\n return\n }\n\n removeMouseMoveEventListener()\n removeClickEventListener()\n removeContextMenuEventListener()\n}\n\nfunction addEventListeners(): void {\n // These have no effect if the event listeners are already added\n addMouseMoveEventListener()\n addClickEventListener()\n addContextMenuEventListener()\n}\n\nexport function useMousePosition(): UseMousePosition {\n listeners.value += 1\n\n addEventListeners()\n\n onScopeDispose(() => {\n listeners.value -= 1\n tryTeardownEventListeners()\n })\n\n return {\n position,\n positionAtLastClick,\n }\n}","import { ref, Ref, MaybeRef } from 'vue'\nimport { tryOnScopeDispose } from '@/utilities/tryOnScopeDispose'\n\nexport type UseNow = {\n now: Ref<Date>,\n pause: () => void,\n resume: () => void,\n}\n\nexport type UseNowArgs = {\n immediate?: boolean,\n interval?: MaybeRef<number>,\n}\n\nexport function useNow({\n immediate = true,\n interval = 0,\n}: UseNowArgs = {}): UseNow {\n const intervalRef = ref(interval)\n const response = ref(getNow())\n let id: null | number = null\n\n function getNow(): Date {\n if (intervalRef.value === 0) {\n return new Date()\n }\n\n const time = new Date().getTime()\n const nearest = Math.round(time / intervalRef.value) * intervalRef.value\n\n return new Date(nearest)\n }\n\n function update(): void {\n const now = getNow()\n\n if (response.value.getTime() !== now.getTime()) {\n response.value = now\n }\n\n id = window.requestAnimationFrame(update)\n }\n\n function pause(): void {\n if (id) {\n window.cancelAnimationFrame(id)\n }\n\n id = null\n }\n\n function resume(): void {\n id = window.requestAnimationFrame(update)\n }\n\n if (immediate) {\n resume()\n }\n\n tryOnScopeDispose(pause)\n\n return {\n now: response,\n resume,\n pause,\n }\n}","import { Ref, computed } from 'vue'\n\n/**\n * patch a specific property of an object ref\n * @param source an object ref\n * @param key the key to patch\n * @returns\n * @example\n * const source = ref({ a: 1, b: 2 })\n * const a = usePatchRef(source, 'a')\n * a.value = 3\n * console.log(source.value) // { a: 3, b: 2 }\n * console.log(a.value) // 3\n */\nexport function usePatchRef<T extends Record<string, unknown>, P extends keyof T>(source: Ref<T>, key: P): Ref<T[P]> {\n return computed({\n get() {\n return source.value[key]\n },\n set(value: T[P]) {\n source.value = {\n ...source.value,\n [key]: value,\n }\n },\n })\n}","import { Ref, computed, ref, watch, toRef, toValue, MaybeRefOrGetter } from 'vue'\nimport { useIntersectionObserver } from '@/useIntersectionObserver'\n\nexport type UsePositionStickyObserverResponse = {\n stuck: Ref<boolean>,\n}\n\nexport type UsePositionStickyObserverOptions = {\n rootMargin?: string,\n boundingElement?: HTMLElement,\n}\n\nconst usePositionStickyObserverDefaultOptions = {\n rootMargin: '-1px 0px 0px 0px',\n boundingElement: document.body,\n}\n\nexport function usePositionStickyObserver(\n element: MaybeRefOrGetter<HTMLElement | undefined>,\n options?: MaybeRefOrGetter<UsePositionStickyObserverOptions>,\n): UsePositionStickyObserverResponse {\n const elementRef = toRef(element)\n const stuck = ref(false)\n\n const observerOptions = computed(() => {\n const { rootMargin: rootMarginOption, boundingElement: boundingElementOption } = toValue(options ?? {})\n const rootMargin = rootMarginOption ?? usePositionStickyObserverDefaultOptions.rootMargin\n const root = boundingElementOption ?? usePositionStickyObserverDefaultOptions.boundingElement\n\n return {\n threshold: [1],\n rootMargin,\n root,\n }\n })\n\n function intersect(entries: IntersectionObserverEntry[]): void {\n entries.forEach(entry => {\n stuck.value = entry.intersectionRatio < 1\n })\n }\n\n const { observe, unobserve } = useIntersectionObserver(intersect, observerOptions)\n\n watch(elementRef, (newVal, oldVal) => {\n unobserve(oldVal)\n observe(newVal)\n }, { immediate: true })\n\n return {\n stuck,\n }\n}","import { onMounted, onUnmounted, ref, Ref, MaybeRef } from 'vue'\n\ntype DisconnectScrollLink = () => void\ntype UseScrollLinking = {\n disconnect: DisconnectScrollLink,\n source: Ref<HTMLElement | undefined>,\n target: Ref<HTMLElement | undefined>,\n}\n\n/**\n * The useScrollLinking composition takes 2 optional element references (source, target)\n * and attaches a scroll event listener to the source. When the scroll event of the\n * source element is fired, the scroll position of the target is updated to match, producing\n * a scroll linking effect.\n *\n * This composition will tear down when the calling component is unmounted but can be disconnected\n * early using the returned disconnect method.\n *\n * @param source MaybeRef<HTMLElement>\n * @param target MaybeRef<HTMLElement>\n * @returns UseScrollLinking\n */\nexport function useScrollLinking(\n source?: MaybeRef<HTMLElement>,\n target?: MaybeRef<HTMLElement>,\n): UseScrollLinking {\n const sourceRef = ref(source)\n const targetRef = ref(target)\n\n const handleScroll = (): void => {\n if (!sourceRef.value || !targetRef.value) {\n return\n }\n\n targetRef.value.scrollTop = sourceRef.value.scrollTop\n targetRef.value.scrollLeft = sourceRef.value.scrollLeft\n }\n\n const connect = (): void => {\n if (!sourceRef.value) {\n return\n }\n\n sourceRef.value.addEventListener('scroll', handleScroll)\n }\n\n const disconnect = (): void => {\n if (!sourceRef.value) {\n return\n }\n\n sourceRef.value.removeEventListener('scroll', handleScroll)\n }\n\n onMounted(connect)\n onUnmounted(disconnect)\n\n return {\n disconnect,\n source: sourceRef,\n target: targetRef,\n }\n}","/* eslint-disable no-dupe-class-members */\nimport { globalExists } from '@/utilities/global'\n\nexport type StorageType = 'session' | 'local'\n\nexport class StorageManager {\n private readonly type: StorageType\n\n public get length(): number {\n const storage = this.storage()\n\n if (!storage) {\n return 0\n }\n\n return storage.length\n }\n\n public constructor(type: StorageType) {\n this.type = type\n }\n\n public get<T>(key: string): T | null\n public get<T>(key: string, defaultValue: T): T\n public get<T>(key: string, defaultValue: T | null = null): T | null {\n const storage = this.storage()\n\n if (!storage) {\n return null\n }\n\n const value = storage.getItem(key)\n\n if (value === null) {\n return defaultValue\n }\n\n try {\n return JSON.parse(value) as T\n } catch {\n console.error(`Unable to parse current value for key ${key}, returning default instead`)\n return defaultValue\n }\n }\n\n public set<T>(key: string, value: T): void {\n const string = JSON.stringify(value)\n const storage = this.storage()\n\n if (!storage) {\n return\n }\n\n return storage.setItem(key, string)\n }\n\n public remove(key: string): void {\n const storage = this.storage()\n\n if (!storage) {\n return\n }\n\n return storage.removeItem(key)\n }\n\n public clear(): void {\n const storage = this.storage()\n\n if (!storage) {\n return\n }\n\n return storage.clear()\n }\n\n public key(index: number): string | null {\n const storage = this.storage()\n\n if (!storage) {\n return null\n }\n\n return storage.key(index)\n }\n\n private storage(): Storage | null {\n if (this.type === 'local' && globalExists('localStorage')) {\n return localStorage\n }\n\n if (this.type === 'session' && globalExists('sessionStorage')) {\n return sessionStorage\n }\n\n return null\n }\n}","/* eslint-disable no-redeclare */\nimport { ref, Ref, UnwrapRef, watchEffect } from 'vue'\nimport { StorageManager, StorageType } from '@/useStorage/storage'\n\ntype UseStorage<T> = {\n value: Ref<UnwrapRef<T>>,\n initialValue: T,\n remove: () => void,\n set: (value: UnwrapRef<T>) => void,\n}\n\ntype UseNullableStorage<T> = {\n value: Ref<UnwrapRef<T> | null>,\n initialValue: T | null,\n remove: () => void,\n set: (value: UnwrapRef<T> | null) => void,\n}\n\nexport function useStorage<T>(type: StorageType, key: string): UseNullableStorage<T>\nexport function useStorage<T>(type: StorageType, key: string, defaultValue: T): UseStorage<T>\nexport function useStorage<T>(type: StorageType, key: string, defaultValue: T | null = null): UseNullableStorage<T> {\n const storage = new StorageManager(type)\n const initialValue = storage.get(key, defaultValue)\n const data = ref(initialValue)\n let stopped = false\n\n const remove: UseNullableStorage<T>['remove'] = () => {\n stopped = true\n storage.remove(key)\n data.value = null\n }\n\n const set: UseNullableStorage<T>['set'] = (value) => {\n data.value = value\n }\n\n watchEffect(() => {\n if (stopped) {\n console.warn(`Storage for key ${key} as been removed and cannot be updated`)\n return\n }\n\n storage.set(key, data.value)\n })\n\n return {\n value: data,\n initialValue,\n remove,\n set,\n }\n}\n\nexport function useSessionStorage<T>(key: string): UseStorage<T | null>\nexport function useSessionStorage<T>(key: string, defaultValue: T): UseStorage<T>\nexport function useSessionStorage<T>(key: string, defaultValue: T | null = null): UseStorage<T | null> {\n return useStorage('session', key, defaultValue)\n}\n\nexport function useLocalStorage<T>(key: string): UseStorage<T | null>\nexport function useLocalStorage<T>(key: string, defaultValue: T): UseStorage<T>\nexport function useLocalStorage<T>(key: string, defaultValue: T | null = null): UseStorage<T | null> {\n return useStorage('local', key, defaultValue)\n}","// unknown breaks this for classes\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyFunction = (...args: any[]) => any\ntype Callable<T> = keyof {\n [P in keyof T as T[P] extends AnyFunction ? P : never]: T[P]\n}\nexport type CreateActions<T> = Pick<T, Callable<T>>\n\n// we do specifically want any here. unknown breaks this for classes\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function createActions<T extends Record<string, any>>(context: T): CreateActions<T> {\n const objectPrototypeKeys = Reflect.ownKeys(Object.prototype)\n const actions: Record<string, unknown> = {}\n let prototype = Reflect.getPrototypeOf(context)\n\n // properties\n Reflect.ownKeys(context).forEach(key => {\n if (typeof key === 'string' && typeof context[key] === 'function') {\n actions[key] = context[key].bind(context)\n }\n })\n\n // methods\n while (prototype && prototype !== Object.prototype) {\n Reflect.ownKeys(prototype).forEach(key => {\n if (typeof key === 'string' && typeof actions[key] === 'undefined' && typeof context[key] === 'function' && !objectPrototypeKeys.includes(