(node: T, immediate = true) {
2 | let stop: () => void;
3 |
4 | const destroy = () => {
5 | stop && stop();
6 | };
7 |
8 | const update = () => {
9 | destroy();
10 |
11 | function mutationCallback() {
12 | const { clientHeight, scrollHeight } = node;
13 | if (scrollHeight > clientHeight) {
14 | if (node.scrollTo) {
15 | node.scrollTo({
16 | behavior: "smooth",
17 | top: scrollHeight,
18 | });
19 | } else {
20 | node.scrollTop = scrollHeight;
21 | }
22 | }
23 | }
24 |
25 | const mutationObserver = new MutationObserver(mutationCallback);
26 | mutationObserver.observe(node, { childList: true, subtree: true });
27 |
28 | if (immediate) mutationCallback();
29 |
30 | stop = () => {
31 | mutationObserver.disconnect();
32 | };
33 | };
34 |
35 | update();
36 |
37 | return {
38 | update,
39 | destroy,
40 | };
41 | }
42 |
--------------------------------------------------------------------------------
/src/lib/actions/scrollToBottomAction/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Also known as chat-scroll, an action to scroll to bottom of an element when it's scrollHeight or content changes."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/actions/scrollToBottomAction/usage.txt:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 | {#each messages as message}
10 |
11 | {/each}
12 |
--------------------------------------------------------------------------------
/src/lib/actions/sortableTableAction/demo.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | Try clicking on the columns to sort the rows
8 |
9 |
10 |
11 | Column 1
12 | Column 2
13 | Column 3
14 |
15 |
16 |
17 |
18 | Row 1
19 | Row 1
20 | Row 1
21 |
22 |
23 | Row 2
24 | Row 2
25 | Row 2
26 |
27 |
28 | Row 3
29 | Row 3
30 | Row 3
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/lib/actions/sortableTableAction/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Sort tables by clicking on the column headers"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/actions/sortableTableAction/usage.txt:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 | Column 1
9 | Column 2
10 | Column 3
11 |
12 |
13 |
14 |
15 | Row 1
16 | Row 1
17 | Row 1
18 |
19 |
20 | Row 2
21 | Row 2
22 | Row 2
23 |
24 |
25 | Row 3
26 | Row 3
27 | Row 3
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/lib/actions/textareaAutosizeAction/demo.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/actions/textareaAutosizeAction/index.ts:
--------------------------------------------------------------------------------
1 | import { eventListenerStore } from "$lib/stores/eventListenerStore";
2 | import ProxyTextareaElement from "./core";
3 |
4 | export function textareaAutosizeAction(node: HTMLTextAreaElement) {
5 | function init() {
6 | const isTextarea = node instanceof HTMLTextAreaElement;
7 | if (!isTextarea) return;
8 |
9 | const proxy = new ProxyTextareaElement();
10 |
11 | proxy.start(node);
12 |
13 | function handleChange() {
14 | proxy.onUpdateText(node.value);
15 | }
16 |
17 | const { stop } = eventListenerStore("input", handleChange, node);
18 |
19 | function cleanUp() {
20 | proxy.cleanUp();
21 | stop();
22 | }
23 |
24 | // initialize
25 | handleChange();
26 |
27 | return cleanUp;
28 | }
29 |
30 | const stop = init();
31 |
32 | return {
33 | destroy() {
34 | stop && stop();
35 | },
36 | };
37 | }
38 |
--------------------------------------------------------------------------------
/src/lib/actions/textareaAutosizeAction/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "An action that converts a textarea into an auto resizeable textarea."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/actions/textareaAutosizeAction/usage.txt:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/src/lib/actions/tooltipAction/demo.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
13 | top-left
14 |
15 |
19 | center
20 |
21 |
25 | right
26 |
27 |
28 |
29 |
33 | without pointer
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/lib/actions/tooltipAction/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "An action to display simple tooltips."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/actions/tooltipAction/usage.txt:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Default Options
7 |
8 |
9 |
10 | With Options
11 |
--------------------------------------------------------------------------------
/src/lib/derivatives/first/demo.svelte:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 | Store data:
26 |
27 | {JSON.stringify($data, null, 2)}
28 |
29 |
30 |
31 |
32 | First item:
33 |
34 | {JSON.stringify($firstElement, null, 2)}
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/lib/derivatives/first/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 |
3 | function isIterable(obj: any) {
4 | // checks for null and undefined
5 | if (obj == null) {
6 | return false;
7 | }
8 | return typeof obj[Symbol.iterator] === "function";
9 | }
10 |
11 | export function first>(store: Readable>) {
12 | return derived(store, ($store) => {
13 | if (!isIterable($store)) {
14 | throw new Error("store data is not iterable");
15 | }
16 |
17 | if ($store instanceof Array) {
18 | return $store[0];
19 | }
20 |
21 | if ($store instanceof Set) {
22 | return $store.values().next().value;
23 | }
24 |
25 | if ($store instanceof Map) {
26 | return $store.entries().next().value;
27 | }
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/src/lib/derivatives/first/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A helpful utility to pick first item in your array of data."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/derivatives/first/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/derivatives/index.ts:
--------------------------------------------------------------------------------
1 | export { pick } from "./pick";
2 | export { pickArray } from "./pickArray";
3 | export { sizeOf } from "./sizeOf";
4 | export { toNumber } from "./toNumber";
5 |
--------------------------------------------------------------------------------
/src/lib/derivatives/pick/demo.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | Store data:
21 |
22 | {JSON.stringify($data, null, 2)}
23 |
24 |
25 |
26 |
27 | Picked Data 'name':
28 |
29 | {JSON.stringify($pickData, null, 2)}
30 |
31 |
32 |
33 |
34 | Picked Data 'name' and 'createdAt':
35 |
36 | {JSON.stringify($nameAndAuthor, null, 2)}
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/lib/derivatives/pick/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 |
3 | export function pick(store: Readable, ...keys: (keyof T)[]) {
4 | return derived(store, ($store) => {
5 | if (keys.length === 1) {
6 | return $store[keys[0]];
7 | }
8 |
9 | return keys
10 | .map((key) => [key, $store[key]] as const)
11 | .reduce((map, pair) => {
12 | const [key, value] = pair;
13 | map[key] = value;
14 | return map;
15 | }, {} as Partial);
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/src/lib/derivatives/pick/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A helpful utility to pick data from a store by keys. If only one key is specified the derivative will be the value else the derivative will a partial record of the store value."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/derivatives/pick/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/derivatives/pickArray/demo.svelte:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 |
27 | Store data:
28 |
29 | {JSON.stringify($data, null, 2)}
30 |
31 |
32 |
33 |
34 | Picked Data 'name':
35 |
36 | {JSON.stringify($names, null, 2)}
37 |
38 |
39 |
40 |
41 | Picked Data 'name' and 'author':
42 |
43 | {JSON.stringify($namesAndAuthors, null, 2)}
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/lib/derivatives/pickArray/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 |
3 | export function pickArray(store: Readable, ...keys: (keyof T)[]) {
4 | return derived(store, ($store) => {
5 | return $store.map((value) => {
6 | if (keys.length === 1) {
7 | return value[keys[0]];
8 | }
9 |
10 | return keys
11 | .map((key) => [key, value[key]] as const)
12 | .reduce((map, pair) => {
13 | const [key, value] = pair;
14 | map[key] = value;
15 | return map;
16 | }, {} as Partial);
17 | });
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/src/lib/derivatives/pickArray/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A helpful utility to pick data from a store of list of objects by keys. If only one key is specified the derivative will be the array of values of that key else the derivative will an array of partial records of the keys mapped to store's list of values."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/derivatives/pickArray/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/derivatives/sizeOf/demo.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 | List Size: {$listSize}
24 |
25 | {JSON.stringify($list)}
26 |
27 |
28 |
29 |
30 | Set Size: {$setSize}
31 |
32 | {@html "{1, 2, 3, 4}"}
33 |
34 |
35 |
36 |
37 | Map Size: {$mapSize}
38 |
39 | {@html "{1 => 3, 2 => 4}"}
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/lib/derivatives/sizeOf/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 |
3 | export function sizeOf(store: Readable) {
4 | return derived(store, ($store) => {
5 | if ($store instanceof Array || typeof $store === "string") {
6 | return $store.length;
7 | } else if ($store instanceof Set || $store instanceof Map) {
8 | return $store.size;
9 | } else {
10 | return 0;
11 | }
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/src/lib/derivatives/sizeOf/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A helpful utility to get size of the store which can be a String, Array, a Set or a Map of values."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/derivatives/sizeOf/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/derivatives/sort/demo.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 | Array data:
14 |
15 | {JSON.stringify($data)}
16 |
17 |
18 |
19 |
20 | Sorted data:
21 |
22 | {JSON.stringify($sorted)}
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/lib/derivatives/sort/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 |
3 | type ComparatorFn = (a: T, b: T) => number;
4 | type SortFn = (arr: T[], compareFn: ComparatorFn) => T[];
5 |
6 | function defaultSortFn(source: T[], compareFn: ComparatorFn) {
7 | return source.sort(compareFn);
8 | }
9 |
10 | const defaultCompare: ComparatorFn = (a: number, b: number) => {
11 | return a - b;
12 | };
13 |
14 | interface SortParams {
15 | sortFn?: SortFn;
16 | compareFn?: ComparatorFn;
17 | }
18 |
19 | export function sort(store: Readable, params: SortParams = {}) {
20 | const { sortFn = defaultSortFn, compareFn = defaultCompare } = params;
21 |
22 | return derived(store, ($store) => {
23 | if (!($store instanceof Array)) {
24 | throw new Error("Store must be an instance of Array");
25 | }
26 |
27 | return sortFn($store.slice(), compareFn);
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/src/lib/derivatives/sort/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A helpful utility to sort array data in your stores. If array items are string or numbers a default compare function which uses difference of 2 items, you can provide your own comparator or whole sort function as well."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/derivatives/sort/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/derivatives/toNumber/demo.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
Result: {$number}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/lib/derivatives/toNumber/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 |
3 | export interface UseToNumberOptions {
4 | /**
5 | * Method to use to convert the value to a number.
6 | *
7 | * @default 'parseFloat'
8 | */
9 | method?: "parseFloat" | "parseInt";
10 |
11 | /**
12 | * The base in mathematical numeral systems passed to `parseInt`.
13 | * Only works with `method: 'parseInt'`
14 | */
15 | radix?: number;
16 |
17 | /**
18 | * Replace NaN with zero
19 | *
20 | * @default false
21 | */
22 | nanToZero?: boolean;
23 | }
24 |
25 | export function toNumber(
26 | store: Readable,
27 | options: UseToNumberOptions = {}
28 | ) {
29 | const { method = "parseFloat", radix, nanToZero } = options;
30 | return derived(store, ($store) => {
31 | let resolved = $store;
32 | if (typeof $store === "string") resolved = Number[method](resolved, radix);
33 | if (nanToZero && isNaN(resolved)) resolved = 0;
34 | return resolved;
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/src/lib/derivatives/toNumber/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A helpful utility to reactively convert a string readable/writable to number."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/derivatives/toNumber/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/middlewares/history/demo.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
17 |
18 | {$counter}
19 |
20 |
21 |
$counter++}>Increment
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/lib/middlewares/history/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "This middleware makes it really easy to add undo/redo functionality to your app. It wraps any writable with any data type and provides an augmented store with an history API which has undo and redo methods and two derived store properties canUndo and canRedo."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/middlewares/history/usage.txt:
--------------------------------------------------------------------------------
1 |
8 |
9 | Undo
10 | Redo
11 |
12 | {$counter}
13 |
14 | $counter++}>Increment
--------------------------------------------------------------------------------
/src/lib/middlewares/index.ts:
--------------------------------------------------------------------------------
1 | export { history } from "./history";
2 | export { storage } from "./storage";
3 |
--------------------------------------------------------------------------------
/src/lib/middlewares/storage/demo.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 | Name:
13 |
14 |
15 |
16 |
17 | Check your local storage, on changing name value, name key should update!
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/middlewares/storage/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "This middleware makes it really easy to sync your writable store to localStorage. It wraps any writable with any data type and provided a key which is a string and writes back to localStorage on change. It also loads the store with the last value saved in localStorage."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/middlewares/storage/usage.txt:
--------------------------------------------------------------------------------
1 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/readables/mouseStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | Move mouse to change the values
9 |
10 |
X: {$position.x}
11 | Y: {$position.y}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/readables/mouseStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive mouse position"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/readables/mouseStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/shared/components/BooleanDisplay.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | {#if value}
7 | Yes
8 | {:else}
9 | No
10 | {/if}
11 |
12 |
--------------------------------------------------------------------------------
/src/lib/shared/components/DemoContainer.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/lib/shared/components/LoadingDots.svelte:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 | {#each dots as dot}
25 | {dot}
26 | {/each}
27 |
28 |
--------------------------------------------------------------------------------
/src/lib/shared/components/PrimaryButton.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/lib/shared/components/Text.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/lib/shared/icons/cross.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/lib/shared/icons/error.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | error-filled
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/lib/shared/icons/index.ts:
--------------------------------------------------------------------------------
1 | import info from './info.svg'
2 | import warn from './warn.svg'
3 | import success from './success.svg'
4 | import error from './error.svg'
5 | import cross from './cross.svg'
6 | export {
7 | info,
8 | warn,
9 | success,
10 | error,
11 | cross
12 | }
--------------------------------------------------------------------------------
/src/lib/shared/icons/info.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/lib/shared/icons/success.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | success-filled
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/lib/shared/tailwind.ts:
--------------------------------------------------------------------------------
1 | export const buildClassName = (className: string) => {
2 | return className
3 | .split("\n")
4 | .map((c) => c.trim())
5 | .filter(Boolean)
6 | .join(" ");
7 | };
8 |
9 | export const PrimaryButtonClassNames = `
10 | inline-block
11 | text-sm
12 | bg-prime
13 | text-white
14 | px-4
15 | py-2
16 | rounded-md
17 | cursor-pointer
18 | disabled:bg-slate-300
19 | disabled:pointer-events-none
20 | `;
21 |
22 | export const PrimaryButtonClassName = buildClassName(PrimaryButtonClassNames);
23 |
24 | export const InputClass = `px-3 py-2 border border-black rounded-md text-md`;
25 |
--------------------------------------------------------------------------------
/src/lib/shared/utils/types.ts:
--------------------------------------------------------------------------------
1 | import type { Readable } from "svelte/store";
2 |
3 | export type Fn = (...args: any) => void;
4 |
5 | export interface ConfigurableWindow {
6 | /*
7 | * Specify a custom `window` instance, e.g. working with iframes or in testing environments.
8 | */
9 | window?: Window;
10 | }
11 |
12 | export interface ConfigurableDocument {
13 | /*
14 | * Specify a custom `document` instance, e.g. working with iframes or in testing environments.
15 | */
16 | document?: Document;
17 | }
18 |
19 | export interface Position {
20 | x: number;
21 | y: number;
22 | }
23 |
24 | export interface Size {
25 | width: number;
26 | height: number;
27 | }
28 |
29 | export interface Stoppable {
30 | isPending: Readable;
31 | stop: Fn;
32 | start: Fn;
33 | }
34 |
35 | export interface Pausable {
36 | isActive: Readable;
37 | pause: Fn;
38 | resume: Fn;
39 | changeIntervalTime: (time: number) => void;
40 | }
41 |
--------------------------------------------------------------------------------
/src/lib/shared/utils/utils.ts:
--------------------------------------------------------------------------------
1 | import { type Readable } from "svelte/store";
2 |
3 | export function isUndefined(value: T) {
4 | return value === undefined;
5 | }
6 |
7 | export interface SingletonPromiseReturn {
8 | (): Promise;
9 | /**
10 | * Reset current staled promise.
11 | * await it to have proper shutdown.
12 | */
13 | reset: () => Promise;
14 | }
15 |
16 | /**
17 | * Create singleton promise function
18 | *
19 | * @example
20 | * ```
21 | * const promise = createSingletonPromise(async () => { ... })
22 | *
23 | * await promise()
24 | * await promise() // all of them will be bind to a single promise instance
25 | * await promise() // and be resolved together
26 | * ```
27 | */
28 | export function createSingletonPromise(fn: () => Promise): SingletonPromiseReturn {
29 | let _promise: Promise | undefined;
30 |
31 | function wrapper() {
32 | if (!_promise) _promise = fn();
33 | return _promise;
34 | }
35 | wrapper.reset = async () => {
36 | const _prev = _promise;
37 | _promise = undefined;
38 | if (_prev) await _prev;
39 | };
40 |
41 | return wrapper;
42 | }
43 |
44 | export function noop() {}
45 |
--------------------------------------------------------------------------------
/src/lib/stores/activeElementStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | Select the inputs below to see the changes
13 |
14 | {#each Array(4).fill(0) as el, index}
15 |
16 |
21 |
22 | {/each}
23 |
24 |
25 |
26 | Current active element id: {id}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/stores/activeElementStore/index.ts:
--------------------------------------------------------------------------------
1 | import type { ConfigurableWindow } from "$lib/shared/utils/types";
2 | import { defaultWindow } from "$lib/shared";
3 | import { readable, type Readable } from "svelte/store";
4 |
5 | function getCurrentActiveElement(window = defaultWindow): Element | null {
6 | if (!window) return null;
7 | if (!window.document) return null;
8 |
9 | return window.document.activeElement;
10 | }
11 |
12 | export function activeElementStore({
13 | window = defaultWindow,
14 | }: ConfigurableWindow = {}): Readable {
15 | const activeElement = readable(getCurrentActiveElement(), (set) => {
16 | function handler() {
17 | set(document.activeElement);
18 | }
19 |
20 | if (window) {
21 | window.document.addEventListener("focus", handler, true);
22 | window.document.addEventListener("blur", handler, true);
23 |
24 | return () => {
25 | window.document.removeEventListener("focus", handler);
26 | window.document.removeEventListener("blur", handler);
27 | };
28 | }
29 | });
30 |
31 | return activeElement;
32 | }
33 |
--------------------------------------------------------------------------------
/src/lib/stores/activeElementStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive document.activeElement"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/activeElementStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/counterStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | Counter: {$counter}
11 |
12 |
inc()}>Increment
13 |
dec()}>Decrement
14 |
inc(5)}>Increment (+5)
15 |
dec(5)}>Decrement (-5)
16 |
set(100)}>Set (100)
17 |
reset()}>Reset
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/lib/stores/counterStore/index.ts:
--------------------------------------------------------------------------------
1 | import { writable } from "svelte/store";
2 |
3 | export interface UseCounterOptions {
4 | min?: number;
5 | max?: number;
6 | }
7 |
8 | export function counterStore(initialValue = 0, options: UseCounterOptions = {}) {
9 | const store = writable(initialValue);
10 |
11 | const { min = -Infinity, max = Infinity } = options;
12 |
13 | const inc = (incrementBy = 1) => {
14 | store.update((counter) => Math.min(max, counter + incrementBy));
15 | };
16 | const dec = (decrementBy = 1) => {
17 | store.update((counter) => Math.max(min, counter - decrementBy));
18 | };
19 | const set = (value: number) => {
20 | store.set(Math.min(max, Math.max(min, value)));
21 | };
22 | const reset = () => {
23 | set(initialValue);
24 | };
25 |
26 | return {
27 | counter: { subscribe: store.subscribe },
28 | inc,
29 | dec,
30 | set,
31 | reset,
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib/stores/counterStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive counter"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/counterStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/cssVarStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
Color Value for --bg
11 |
12 |
13 |
17 | background: var(--bg)
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/lib/stores/cssVarStore/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultDocument } from "$lib/shared";
2 | import { writable } from "svelte/store";
3 |
4 | interface CSSVarStoreOptions {
5 | el?: T;
6 | initialValue?: string;
7 | }
8 |
9 | export function cssVarStore(
10 | name: string,
11 | { el, initialValue }: CSSVarStoreOptions = {}
12 | ) {
13 | const target = el || defaultDocument?.documentElement;
14 | const store = writable(initialValue);
15 |
16 | function updateCSSVar(value: string) {
17 | target?.style.setProperty(name, value);
18 | }
19 |
20 | function set(value: string) {
21 | updateCSSVar(value);
22 | store.set(value);
23 | }
24 |
25 | if (initialValue) {
26 | updateCSSVar(initialValue);
27 | }
28 |
29 | return {
30 | subscribe: store.subscribe,
31 | update: store.update,
32 | set,
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/src/lib/stores/cssVarStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A writable store to manipulate CSS variables."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/cssVarStore/usage.txt:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/lib/stores/dateStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | The date is {$dateStore}
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/stores/dateStore/index.ts:
--------------------------------------------------------------------------------
1 | import { readable } from "svelte/store";
2 |
3 | /**
4 | * Gets the current date in a Svelte readable store. The date is updated every 10ms.
5 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
6 | *
7 | *
8 | * @example
9 | * ```ts
10 | * import { dateStore } from 'svelte-legos';
11 | *
12 | * $: console.log($dateStore); // Fri Sep 15 2023 11:43:12 GMT+0100 (British Summer Time)
13 | * ```
14 | */
15 | export const dateStore = readable(new Date(), (set) => {
16 | const interval = setInterval(() => {
17 | set(new Date());
18 | }, 10);
19 |
20 | return () => clearInterval(interval);
21 | });
22 |
--------------------------------------------------------------------------------
/src/lib/stores/dateStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive date"
3 | }
--------------------------------------------------------------------------------
/src/lib/stores/dateStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/documentVisibilityStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 | {message}
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/stores/documentVisibilityStore/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultDocument } from "$lib/shared";
2 | import type { ConfigurableDocument } from "$lib/shared/utils/types";
3 | import { readable, type Readable } from "svelte/store";
4 |
5 | function getCurrentDocumentVisibility(document = defaultDocument): DocumentVisibilityState {
6 | if (!document) return "visible";
7 |
8 | return document.visibilityState;
9 | }
10 |
11 | export function documentVisibilityStore({
12 | document = defaultDocument,
13 | }: ConfigurableDocument = {}): Readable {
14 | const visibility = readable(getCurrentDocumentVisibility(document), (set) => {
15 | function handler() {
16 | set(getCurrentDocumentVisibility());
17 | }
18 |
19 | if (document) {
20 | document.addEventListener("visibilitychange", handler);
21 |
22 | return () => {
23 | document.removeEventListener("visibilitychange", handler);
24 | };
25 | }
26 | });
27 |
28 | return visibility;
29 | }
30 |
--------------------------------------------------------------------------------
/src/lib/stores/documentVisibilityStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive document visibility"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/documentVisibilityStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/elementBoundingStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/lib/stores/elementBoundingStore/index.ts:
--------------------------------------------------------------------------------
1 | import { writableToReadable } from "$lib/shared";
2 | import { writable, type Readable } from "svelte/store";
3 | import { resizeObserverStore } from "../resizeObserverStore";
4 |
5 | function getBounding(target: Element) {
6 | return target.getBoundingClientRect();
7 | }
8 |
9 | /**
10 | * Reactive size of an HTML element.
11 | *
12 | * @param target
13 | * @param callback
14 | * @param options
15 | */
16 | export function elementBoundingStore(target: T): Readable {
17 | const size = writable(getBounding(target));
18 | resizeObserverStore(target, () => {
19 | size.set(getBounding(target));
20 | });
21 |
22 | return writableToReadable(size);
23 | }
24 |
--------------------------------------------------------------------------------
/src/lib/stores/elementBoundingStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive element bounding"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/elementBoundingStore/usage.txt:
--------------------------------------------------------------------------------
1 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/stores/elementSizeStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | Resize the box to see changes
13 |
18 |
19 |
--------------------------------------------------------------------------------
/src/lib/stores/elementSizeStore/index.ts:
--------------------------------------------------------------------------------
1 | import { writableToReadable } from "$lib/shared";
2 | import type { Size } from "$lib/shared/utils/types";
3 | import { writable } from "svelte/store";
4 | import { resizeObserverStore } from "../resizeObserverStore";
5 |
6 | /**
7 | * Reactive size of an HTML element.
8 | *
9 | * @param target
10 | * @param callback
11 | * @param options
12 | */
13 | export function elementSizeStore(target: T) {
14 | const size = writable({ width: 0, height: 0 });
15 | if (target !== null) {
16 | resizeObserverStore(target, ([entry]) => {
17 | size.set({
18 | width: entry.contentRect.width,
19 | height: entry.contentRect.height,
20 | });
21 | });
22 | }
23 |
24 | return writableToReadable(size);
25 | }
26 |
--------------------------------------------------------------------------------
/src/lib/stores/elementSizeStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive element size"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/elementSizeStore/usage.txt:
--------------------------------------------------------------------------------
1 |
11 |
12 |
--------------------------------------------------------------------------------
/src/lib/stores/elementVisibilityStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
16 | Target Element (scroll down)
17 |
18 |
19 |
Scroll the window and watch element status in the bottom right corner.
20 |
Info on the right bottom corner
21 |
22 |
23 |
24 |
25 | Element is
26 | {$isVisible ? "inside" : "outside"}
27 | the viewport
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/lib/stores/elementVisibilityStore/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow, tryOnDestroy, writableToReadable } from "$lib/shared";
2 | import type { ConfigurableWindow } from "$lib/shared/utils/types";
3 | import { writable, type Readable } from "svelte/store";
4 |
5 | export function elementVisibilityStore(
6 | target: Element | null,
7 | { window = defaultWindow }: ConfigurableWindow = {}
8 | ) {
9 | const store = writable(false);
10 |
11 | function stop() {
12 | window?.removeEventListener("scroll", check);
13 | }
14 |
15 | function check() {
16 | if (!window) return;
17 | if (target === null) return;
18 |
19 | const document = window.document;
20 | const rect = target.getBoundingClientRect();
21 |
22 | const test =
23 | rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
24 | rect.left <= (window.innerWidth || document.documentElement.clientWidth) &&
25 | rect.bottom >= 0 &&
26 | rect.right >= 0;
27 | store.set(test);
28 | }
29 |
30 | function start() {
31 | stop();
32 | window?.addEventListener("scroll", check, { capture: false, passive: true });
33 | check();
34 | }
35 |
36 | start();
37 |
38 | tryOnDestroy(stop);
39 |
40 | return { isVisible: writableToReadable(store), stop };
41 | }
42 |
--------------------------------------------------------------------------------
/src/lib/stores/elementVisibilityStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Tracks the visibility of an element within the viewport."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/elementVisibilityStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/eventListenerStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
36 |
37 |
38 | Check console for logs.
39 | Click me
40 |
41 |
--------------------------------------------------------------------------------
/src/lib/stores/eventListenerStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Use EventListener with simplicity. Supports Window, Element and Document and custom events with almost the same parameters as the native addEventListener options. See examples below."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/eventListenerStore/usage.txt:
--------------------------------------------------------------------------------
1 |
33 |
34 | Click me
--------------------------------------------------------------------------------
/src/lib/stores/eyeDropperStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | Open EyeDropper
11 |
12 |
16 | Color will appear here.
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/lib/stores/eyeDropperStore/index.ts:
--------------------------------------------------------------------------------
1 | import { writableToReadable } from "$lib/shared";
2 | import { writable } from "svelte/store";
3 |
4 | export interface EyeDropperOpenOptions {
5 | /**
6 | * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
7 | */
8 | signal?: AbortSignal;
9 | }
10 |
11 | export interface EyeDropper {
12 | // eslint-disable-next-line @typescript-eslint/no-misused-new
13 | new (): EyeDropper;
14 | open: (options?: EyeDropperOpenOptions) => Promise<{ sRGBHex: string }>;
15 | [Symbol.toStringTag]: "EyeDropper";
16 | }
17 |
18 | export function eyeDropperStore(initialColor?: string) {
19 | const store = writable(initialColor);
20 |
21 | const open = () => {
22 | const isSupported = typeof window !== "undefined" && "EyeDropper" in window;
23 | if (!isSupported) {
24 | console.warn("Eyedropeer is not supported");
25 | return;
26 | }
27 |
28 | const eyeDropper: EyeDropper = new (window as any).EyeDropper();
29 | eyeDropper
30 | .open()
31 | .then((result) => {
32 | store.set(result.sRGBHex);
33 | })
34 | .catch((e) => {
35 | console.warn(e);
36 | });
37 | };
38 |
39 | return {
40 | open,
41 | color: writableToReadable(store),
42 | };
43 | }
44 |
--------------------------------------------------------------------------------
/src/lib/stores/eyeDropperStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A store with open action and a color readable whose value will be set to eye dropper selection."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/eyeDropperStore/usage.txt:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | Open EyeDropper
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/lib/stores/geolocationStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | Status: {status}
11 | Latitude: {position?.coords.latitude}
12 | Longitude: {position?.coords.longitude}
13 | Error: {JSON.stringify(error)}
14 |
15 |
--------------------------------------------------------------------------------
/src/lib/stores/geolocationStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Uses the Geolocation API to get and/or watch the user's location data"
3 | }
--------------------------------------------------------------------------------
/src/lib/stores/geolocationStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/hoverStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
16 | Hover me
17 |
18 | Mouse in the element: {$isHover}
19 |
20 |
--------------------------------------------------------------------------------
/src/lib/stores/hoverStore/index.ts:
--------------------------------------------------------------------------------
1 | import type { Readable } from "svelte/store";
2 | import { eventListenerStore } from "../eventListenerStore";
3 | import { toggleStore } from "../toggleStore";
4 |
5 | export function hoverStore(
6 | ref: T | undefined | null
7 | ): Readable {
8 | const { value, on, toggle } = toggleStore();
9 |
10 | if (ref && ref !== null) {
11 | eventListenerStore("mouseenter", on, ref);
12 | eventListenerStore("mouseleave", toggle, ref);
13 | }
14 |
15 | return value;
16 | }
17 |
--------------------------------------------------------------------------------
/src/lib/stores/hoverStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "React UI sensor hook that uses Javascript/Typescript instead of CSS to figure out if the mouse element is in the hover element. So, you can separate the logic from the user interface (UI)."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/hoverStore/usage.txt:
--------------------------------------------------------------------------------
1 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/stores/intervalFnStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 |
32 |
33 |
Interval:
34 |
35 |
36 |
37 |
38 |
39 | {#if $isActive}
40 |
Pause
41 | {:else}
42 |
Resume
43 | {/if}
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/lib/stores/intervalFnStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Wrapper for setInterval with controls"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/intervalFnStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/intervalStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 | {$counter}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/stores/intervalStore/index.ts:
--------------------------------------------------------------------------------
1 | import { readable, type Readable } from "svelte/store";
2 | import { intervalFnStore } from "../intervalFnStore";
3 |
4 | /**
5 | * Wrapper for `setInterval` with controls.
6 | *
7 | * @param cb
8 | * @param interval
9 | * @param options
10 | */
11 | export function intervalStore(interval: number | Readable | undefined): Readable {
12 | if (interval === undefined) {
13 | interval = 1000;
14 | }
15 |
16 | let value = 0;
17 |
18 | const counter = readable(value, (set) => {
19 | function handler() {
20 | value++;
21 | set(value);
22 | }
23 |
24 | const { pause } = intervalFnStore(handler, interval);
25 |
26 | return pause;
27 | });
28 |
29 | return counter;
30 | }
31 |
--------------------------------------------------------------------------------
/src/lib/stores/intervalStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive counter increases on every interval"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/intervalStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/memoryStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {#if $memoryState.isSupported && $memoryState.memory}
15 |
16 |
Used
17 |
{size($memoryState.memory.usedJSHeapSize)}
18 |
Allocated
19 |
{size($memoryState.memory.totalJSHeapSize)}
20 |
Limit
21 |
{size($memoryState.memory.jsHeapSizeLimit)}
22 |
23 | {:else}
24 | Your browser does not support performance memory API
25 | {/if}
26 |
27 |
--------------------------------------------------------------------------------
/src/lib/stores/memoryStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A readable store to easily access the current memory usage of your browser."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/memoryStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/messagesStore/index.ts:
--------------------------------------------------------------------------------
1 | import type { MessageType } from "./Message";
2 | import MessageManager from "./MessageManager";
3 |
4 | export function messagesStore(message: string, type?: MessageType) {
5 | MessageManager.getInstance().createMessage(message, type);
6 | }
7 |
--------------------------------------------------------------------------------
/src/lib/stores/messagesStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Different from messageAction, messagesStore allow you to show message anytime you want."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/messagesStore/usage.txt:
--------------------------------------------------------------------------------
1 |
12 |
13 |
16 | Show Message
17 |
--------------------------------------------------------------------------------
/src/lib/stores/mouseLeftPage/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | Mouse has left: {#if $hasLeft}
11 | Yes
12 | {:else}
13 | No
14 | {/if}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/lib/stores/mouseLeftPage/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow } from "$lib/shared";
2 | import { readable, type Readable } from "svelte/store";
3 |
4 | export function mouseLeftPage(): Readable {
5 | const window = defaultWindow;
6 |
7 | const position = readable(false, (set) => {
8 | function handler(event: MouseEvent) {
9 | if (!window) return;
10 |
11 | event = event || (window.event as any);
12 | // @ts-expect-error missing types
13 | const from = event.relatedTarget || event.toElement;
14 | set(!from);
15 | }
16 |
17 | if (window) {
18 | window.addEventListener("mouseout", handler as () => void);
19 | window.document.addEventListener("mouseleave", handler as () => void);
20 | window.document.addEventListener("mouseenter", handler as () => void);
21 |
22 | return () => {
23 | window.removeEventListener("mouseout", handler as () => void);
24 | window.document.removeEventListener("mouseleave", handler as () => void);
25 | window.document.removeEventListener("mouseenter", handler as () => void);
26 | };
27 | }
28 | });
29 |
30 | return position;
31 | }
32 |
--------------------------------------------------------------------------------
/src/lib/stores/mouseLeftPage/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive state to show whether the mouse leaves the page."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/mouseLeftPage/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/mouseStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | Move mouse to change the values
9 |
10 |
X: {$position.x}
11 | Y: {$position.y}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/stores/mouseStore/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow } from "$lib/shared";
2 | import type { ConfigurableWindow, Position } from "$lib/shared/utils/types";
3 | import { readable, type Readable } from "svelte/store";
4 |
5 | export interface UseMouseOptions extends ConfigurableWindow {
6 | /**
7 | * Initial values
8 | */
9 | initialValue?: Position;
10 | }
11 |
12 | const initialValue = {
13 | x: 0,
14 | y: 0,
15 | };
16 |
17 | function getCurrentMousePosition(e?: MouseEvent): Position {
18 | if (!e) return initialValue;
19 |
20 | return {
21 | x: e.pageX,
22 | y: e.pageY,
23 | };
24 | }
25 |
26 | export function mouseStore(options: UseMouseOptions = {}): Readable {
27 | const { window = defaultWindow } = options;
28 |
29 | const position = readable(getCurrentMousePosition(), (set) => {
30 | function handler(e: MouseEvent) {
31 | set(getCurrentMousePosition(e));
32 | }
33 |
34 | if (window) {
35 | window.addEventListener("mousemove", handler as () => void);
36 |
37 | return () => {
38 | window.removeEventListener("mousemove", handler as () => void);
39 | };
40 | }
41 | });
42 |
43 | return position;
44 | }
45 |
--------------------------------------------------------------------------------
/src/lib/stores/mouseStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive mouse position"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/mouseStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/networkStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | Try toggling your network connectivity using Network in Devtools.
13 |
14 | {networkStateText}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/lib/stores/networkStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A readable store to easily access the current state of the network. The Network Information API provides information about the system's connection in terms of general connection type (e.g., 'wifi', 'cellular', etc.). This can be used to select high definition content or low definition content based on the user's connection. The entire API consists of the addition of the NetworkInformation interface and a single property to the Navigator interface: Navigator.connection."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/networkStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/onlineStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 | Try switching to offline using Network tab in Devtools or turning off your Wifi.
12 |
13 |
14 | Is Online: {$isOnline}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/lib/stores/onlineStore/index.ts:
--------------------------------------------------------------------------------
1 | import { readable } from "svelte/store";
2 | import { networkStore } from "../networkStore";
3 |
4 | export function onlineStore() {
5 | return readable(true, (set) => {
6 | const { subscribe } = networkStore();
7 | const unsub = subscribe((networkState) => {
8 | set(networkState.isOnline);
9 | });
10 | return unsub;
11 | });
12 | }
13 |
--------------------------------------------------------------------------------
/src/lib/stores/onlineStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A readable boolean store to easily access the current state of the network, whether it is online or offline. \nUses networkStore internally."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/onlineStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/pointerStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | Move pointer to change the values
9 |
10 |
X: {$position.x}
11 | Y: {$position.y}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/stores/pointerStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive pointer position"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/pointerStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/rafFnStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 | {counter}
18 |
19 |
20 |
Pause
21 |
Resume
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/lib/stores/rafFnStore/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow, tryOnDestroy } from "$lib/shared";
2 | import type { ConfigurableWindow, Fn, Pausable } from "$lib/shared/utils/types";
3 |
4 | /**
5 | * Wrapper for `requestAnimationFrame` with controls.
6 | *
7 | * @param cb
8 | * @param interval
9 | * @param options
10 | */
11 | export function rafFnStore(fn: Fn, { window = defaultWindow }: ConfigurableWindow = {}) {
12 | let timerId: ReturnType | undefined = undefined;
13 |
14 | function clean() {
15 | if (timerId) {
16 | window?.cancelAnimationFrame(timerId);
17 | timerId = undefined;
18 | }
19 | }
20 |
21 | function pause() {
22 | clean();
23 | }
24 |
25 | function loop() {
26 | if (timerId) {
27 | fn();
28 | timerId = window?.requestAnimationFrame(loop);
29 | }
30 | }
31 |
32 | function resume() {
33 | clean();
34 | timerId = window?.requestAnimationFrame(loop);
35 | }
36 |
37 | tryOnDestroy(pause);
38 | resume();
39 |
40 | return {
41 | resume,
42 | pause,
43 | };
44 | }
45 |
--------------------------------------------------------------------------------
/src/lib/stores/rafFnStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Call function on every requestAnimationFrame. With controls of pausing and resuming."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/rafFnStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/resizeObserverStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 | Resize the box to see changes
21 |
26 |
27 |
--------------------------------------------------------------------------------
/src/lib/stores/resizeObserverStore/index.ts:
--------------------------------------------------------------------------------
1 | import { tryOnDestroy } from "$lib/shared";
2 |
3 | export function resizeObserverStore(
4 | target: Element,
5 | callback: (entries: ResizeObserverEntry[]) => void
6 | ) {
7 | let observer: ResizeObserver | null = null;
8 |
9 | function cleanUp() {
10 | if (observer !== null) {
11 | observer.disconnect();
12 | observer = null;
13 | }
14 | }
15 |
16 | function connect() {
17 | cleanUp();
18 | observer = new ResizeObserver(callback);
19 | observer.observe(target);
20 | }
21 |
22 | function stop() {
23 | cleanUp();
24 | }
25 |
26 | tryOnDestroy(cleanUp);
27 |
28 | connect();
29 |
30 | return {
31 | stop,
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib/stores/resizeObserverStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Easy way to create ResizeObservers."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/resizeObserverStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/scrollStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive scroll position and state.."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/scrollStore/usage.txt:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/lib/stores/textSelectionStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
You can select any text on the page.
15 |
16 | Selected Text:
17 | {#if text}
19 | {text}
20 | {:else}
21 | No selected
22 | {/if}
24 |
25 |
26 |
Selected rects:
27 |
{JSON.stringify(rects, null, 2)}
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/lib/stores/textSelectionStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive element size"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/textSelectionStore/usage.txt:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 | {$selectionStore.text}
14 |
15 |
--------------------------------------------------------------------------------
/src/lib/stores/timeoutFnStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 | {text}
22 | Restart
23 |
24 |
--------------------------------------------------------------------------------
/src/lib/stores/timeoutFnStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Wrapper for setTimeout with controls."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/timeoutFnStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/timeoutStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | Ready: {$ready}
11 | Restart
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/stores/timeoutStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Update value after a given time with controls."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/timeoutStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/toggleStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | Value: {stringify}
13 |
14 |
Toggle
15 |
Set On
16 |
Set off
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/lib/stores/toggleStore/index.ts:
--------------------------------------------------------------------------------
1 | import { writableToReadable } from "$lib/shared";
2 | import { writable } from "svelte/store";
3 |
4 | export function toggleStore(initialValue = false) {
5 | const store = writable(initialValue);
6 |
7 | const toggle = () => {
8 | store.update((value) => !value);
9 | };
10 | const set = (value: boolean) => {
11 | store.set(value);
12 | };
13 | const on = () => {
14 | set(true);
15 | };
16 | const off = () => {
17 | set(false);
18 | };
19 |
20 | return {
21 | value: writableToReadable(store),
22 | toggle,
23 | on,
24 | off,
25 | };
26 | }
27 |
--------------------------------------------------------------------------------
/src/lib/stores/toggleStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive boolean value with helper actions."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/toggleStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/windowFocusStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 | {message}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/lib/stores/windowFocusStore/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow } from "$lib/shared";
2 | import type { ConfigurableWindow } from "$lib/shared/utils/types";
3 | import { readable, type Readable } from "svelte/store";
4 |
5 | function isCurrentWindowFocused(window = defaultWindow): boolean {
6 | if (!window) return false;
7 | if (!window.document) return false;
8 | if (typeof window.document.hasFocus !== "function") return false;
9 |
10 | return window.document.hasFocus();
11 | }
12 |
13 | export function windowFocusStore({
14 | window = defaultWindow,
15 | }: ConfigurableWindow = {}): Readable {
16 | const visibility = readable(isCurrentWindowFocused(), (set) => {
17 | function handler() {
18 | set(isCurrentWindowFocused());
19 | }
20 |
21 | if (window) {
22 | window.addEventListener("focus", handler);
23 | window.addEventListener("blur", handler);
24 |
25 | return () => {
26 | window.removeEventListener("focus", handler);
27 | window.removeEventListener("blur", handler);
28 | };
29 | }
30 | });
31 |
32 | return visibility;
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib/stores/windowFocusStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactively track window focus with window.onfocus and window.onblur events."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/windowFocusStore/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/stores/windowScrollStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
22 |
23 |
39 |
--------------------------------------------------------------------------------
/src/lib/stores/windowScrollStore/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow } from "$lib/shared";
2 | import type { ConfigurableWindow, Position } from "$lib/shared/utils/types";
3 | import { readable, type Readable } from "svelte/store";
4 |
5 | const initialValue: Position = {
6 | x: 0,
7 | y: 0,
8 | };
9 |
10 | function getCurrentScroll(window = defaultWindow): Position {
11 | if (!window) return initialValue;
12 | return {
13 | x: window.scrollX,
14 | y: window.scrollY,
15 | };
16 | }
17 |
18 | export function windowScrollStore({
19 | window = defaultWindow,
20 | }: ConfigurableWindow = {}): Readable {
21 | const position = readable(getCurrentScroll(), (set) => {
22 | function handler() {
23 | set(getCurrentScroll());
24 | }
25 |
26 | if (window) {
27 | window.addEventListener("scroll", handler);
28 |
29 | return () => {
30 | window.removeEventListener("scroll", handler);
31 | };
32 | }
33 | });
34 |
35 | return position;
36 | }
37 |
--------------------------------------------------------------------------------
/src/lib/stores/windowScrollStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive window scroll"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/windowScrollStore/usage.txt:
--------------------------------------------------------------------------------
1 |
6 |
7 | X: {$position.x}
8 | Y: {$position.y}
--------------------------------------------------------------------------------
/src/lib/stores/windowSizeStore/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | {$size.width} X {$size.height}
11 |
12 |
13 |
14 | Try resizing the window
15 |
16 | (Cmd with (-/+))
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/lib/stores/windowSizeStore/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow } from "$lib/shared";
2 | import type { ConfigurableWindow } from "$lib/shared/utils/types";
3 | import { readable } from "svelte/store";
4 |
5 | function getCurrentWindowDimenstions() {
6 | if (typeof window === "object" && "innerWidth" in window && "innerHeight" in window) {
7 | return {
8 | width: window.innerWidth,
9 | height: window.innerHeight,
10 | };
11 | }
12 |
13 | return {
14 | width: 0,
15 | height: 0,
16 | };
17 | }
18 |
19 | export function windowSizeStore({ window = defaultWindow }: ConfigurableWindow = {}) {
20 | const size = readable(getCurrentWindowDimenstions(), (set) => {
21 | let stop = () => {};
22 |
23 | function handler() {
24 | set(getCurrentWindowDimenstions());
25 | }
26 |
27 | let cleanup = Function.prototype;
28 |
29 | if (window) {
30 | window.addEventListener("resize", handler);
31 | cleanup = () => {
32 | window.removeEventListener("resize", handler);
33 | }
34 | }
35 |
36 | return () => {
37 | cleanup();
38 | };
39 | });
40 |
41 | return size;
42 | }
43 |
--------------------------------------------------------------------------------
/src/lib/stores/windowSizeStore/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive window size"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/stores/windowSizeStore/usage.txt:
--------------------------------------------------------------------------------
1 |
6 |
7 | Width: {$size.width}
8 | Height: {$size.height}
--------------------------------------------------------------------------------
/src/lib/transitions/roll/index.ts:
--------------------------------------------------------------------------------
1 | import type { EasingFunction, TransitionConfig } from "svelte/transition";
2 | import { linear } from "svelte/easing";
3 |
4 | type Direction = "top" | "right" | "bottom" | "left";
5 |
6 | interface SlideParams {
7 | delay?: number;
8 | duration?: number;
9 | easing?: EasingFunction;
10 | direction?: Direction;
11 | }
12 |
13 | function getTransform(direction: Direction, value: number) {
14 | switch (direction) {
15 | case "top":
16 | return `translateY(-${value}%)`;
17 | case "bottom":
18 | return `translateY(${value}%)`;
19 | case "left":
20 | return `translateX(-${value}%)`;
21 | case "right":
22 | return `translateX(${value}%)`;
23 | }
24 | }
25 |
26 | export function roll(node: HTMLElement, params: SlideParams = {}): TransitionConfig {
27 | const { delay = 0, duration = 600, easing = linear, direction = "top" } = params;
28 |
29 | return {
30 | delay,
31 | duration,
32 | easing,
33 | css: (t, u) =>
34 | `transform: ${getTransform(direction, u * 100)} rotate(${t * 360}deg); opacity: ${t}`,
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/src/lib/transitions/roll/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A custom Svelte transition utility to roll the element in top, left, right and bottom directions."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/transitions/roll/usage.txt:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/lib/transitions/slide/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A custom Svelte transition utility to slide the element in top, left, right, bottom, top-left, top-right, bottom-left and bottom-right directions."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/transitions/slide/usage.txt:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/lib/transitions/swirl/index.ts:
--------------------------------------------------------------------------------
1 | import type { EasingFunction, TransitionConfig } from "svelte/transition";
2 | import { linear } from "svelte/easing";
3 |
4 | interface SwirlParams {
5 | delay?: number;
6 | duration?: number;
7 | easing?: EasingFunction;
8 | }
9 |
10 | export function swirl(node: HTMLElement, params: SwirlParams = {}): TransitionConfig {
11 | const { delay = 0, duration = 300, easing = linear } = params;
12 |
13 | return {
14 | delay,
15 | duration,
16 | easing,
17 | css: (t, u) => `transform: rotate(${t * -540}deg) scale(${t}); opacity: ${t}`,
18 | };
19 | }
20 |
--------------------------------------------------------------------------------
/src/lib/transitions/swirl/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A custom Svelte transition utility."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/transitions/swirl/usage.txt:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/lib/utilities/battery/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
Is Charging?:
12 |
{$data.charging ? "Yes" : "No"}
13 |
14 |
15 |
Battery Remaining
16 |
17 |
{$data.level * 100}%
18 |
19 |
20 |
Discharging Time:
21 |
{$data.dischargingTime} seconds
22 |
23 |
24 |
Charging Time:
25 |
{$data.chargingTime} seconds
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/utilities/battery/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive Battery Status API, more often referred to as the Battery API, provides information about the system's battery charge level and lets you be notified by events that are sent when the battery level or charging status change. This can be used to adjust your app's resource usage to reduce battery drain when the battery is low, or to save changes before the battery runs out in order to prevent data loss.."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/battery/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/clipboard/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive Clipboard API. Provides the ability to respond to clipboard commands (cut, copy, and paste) as well as to asynchronously read from and write to the system clipboard. Access to the contents of the clipboard is gated behind the Permissions API. Without user permission, reading or altering the clipboard contents is not permitted."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/clipboard/usage.txt:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 | {#if isSupported}
17 |
18 |
19 | Current copied: {copiedText}
20 |
21 |
22 |
copy(value)}>
23 |
24 | {#if !copied}
25 | Copy
26 | {:else}
27 | Copied!
28 | {/if}
29 |
30 |
31 | {:else}
32 |
Your browser does not support Clipboard API
33 | {/if}
34 |
35 |
--------------------------------------------------------------------------------
/src/lib/utilities/fetchWithTimeoutAndRetry/index.ts:
--------------------------------------------------------------------------------
1 | export interface FetchWithTimeoutAndRetryParams {
2 | timeout?: number;
3 | retryCount?: number;
4 | onRetry?: () => void;
5 | }
6 |
7 | export async function fetchWithTimeoutAndRetry(
8 | input: RequestInfo | URL,
9 | options: RequestInit & FetchWithTimeoutAndRetryParams = {}
10 | ): ReturnType {
11 | const { timeout = 5000, retryCount = 3, onRetry } = options;
12 | try {
13 | const controller = new AbortController();
14 | const timeoutId = setTimeout(() => {
15 | controller.abort();
16 | }, timeout);
17 | const response = await fetch(input, { ...options, signal: controller.signal });
18 | clearTimeout(timeoutId);
19 | if (!response.ok) {
20 | throw new Error(`Fetch error: ${response.status} ${response.statusText}`);
21 | }
22 | return response;
23 | } catch (error) {
24 | if (retryCount > 0) {
25 | console.error(`Fetch error: ${error}. Retrying in 1 second...`);
26 | onRetry && onRetry();
27 | await new Promise((resolve) => setTimeout(resolve, 1000));
28 | return fetchWithTimeoutAndRetry(input, { ...options, retryCount: retryCount - 1 });
29 | }
30 | throw error;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/lib/utilities/fetchWithTimeoutAndRetry/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A handy utility to add timeout and retry behavior to your fetch requests."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/fetchWithTimeoutAndRetry/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/hasPermission/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive Permissions API. The Permissions API provides the tools to allow developers to implement a better user experience as far as permissions are concerned."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/hasPermission/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/mediaQuery/demo.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
isLargeScreen:
13 |
{$isLargeScreen}
14 |
15 |
16 |
prefersDark:
17 |
{$prefersDark}
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/utilities/mediaQuery/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow, tryOnDestroy } from "$lib/shared";
2 | import { readable } from "svelte/store";
3 |
4 | export function mediaQuery(query: string) {
5 | return readable(false, (set) => {
6 | const window = defaultWindow;
7 | const isSupported = window && "matchMedia" in window && typeof window.matchMedia === "function";
8 |
9 | let mediaQuery: MediaQueryList | undefined;
10 |
11 | function cleanup() {
12 | if (!mediaQuery) return;
13 | if ("removeEventListener" in mediaQuery)
14 | // eslint-disable-next-line @typescript-eslint/no-use-before-define
15 | mediaQuery.removeEventListener("change", update);
16 | // @ts-expect-error deprecated API
17 | // eslint-disable-next-line @typescript-eslint/no-use-before-define
18 | else mediaQuery.removeListener(update);
19 | }
20 |
21 | function update() {
22 | if (!isSupported) return;
23 |
24 | cleanup();
25 |
26 | mediaQuery = window!.matchMedia(query);
27 | set(mediaQuery.matches);
28 |
29 | if ("addEventListener" in mediaQuery) mediaQuery.addEventListener("change", update);
30 | // @ts-expect-error deprecated API
31 | else mediaQuery.addListener(update);
32 | }
33 |
34 | update();
35 |
36 | return cleanup;
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/src/lib/utilities/mediaQuery/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Readable Media Query. Once you've created a MediaQueryList object, you can check the result of the query or receive notifications when the result changes."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/mediaQuery/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/parseSearchParams/demo.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 | {#each Object.keys($params) as key}
14 |
15 | {key}={$params[key]}
16 |
17 | {/each}
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/utilities/parseSearchParams/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Reactive URLSearchParams."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/parseSearchParams/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredColorScheme/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | Preferred Color Scheme:
11 | {$colorScheme}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredColorScheme/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 | import { mediaQuery } from "../mediaQuery";
3 |
4 | export type ColorSchemeType = "dark" | "light" | "no-preference";
5 |
6 | export function preferredColorScheme(): Readable {
7 | const isLight = mediaQuery("(prefers-color-scheme: light)");
8 | const isDark = mediaQuery("(prefers-color-scheme: dark)");
9 |
10 | return derived([isLight, isDark], ([$isLight, $isDark]) => {
11 | if ($isDark) return "dark";
12 | if ($isLight) return "light";
13 | return "no-preference";
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredColorScheme/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Readable prefers-color-scheme media query."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredColorScheme/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredContrast/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | Preferred contrast:
11 | {$contrast}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredContrast/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 | import { mediaQuery } from "../mediaQuery";
3 |
4 | export type ContrastType = "more" | "less" | "custom" | "no-preference";
5 |
6 | export function preferredContrast(): Readable {
7 | const isMore = mediaQuery("(prefers-contrast: more)");
8 | const isLess = mediaQuery("(prefers-contrast: less)");
9 | const isCustom = mediaQuery("(prefers-contrast: custom)");
10 |
11 | return derived([isMore, isLess, isCustom], ([$isMore, $isLess, $isCustom]) => {
12 | if ($isMore) return "more";
13 | if ($isLess) return "less";
14 | if ($isCustom) return "custom";
15 | return "no-preference";
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredContrast/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Readable prefers-contrast media query."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredContrast/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredDark/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | Prefers Dark:
11 | {$prefersDark}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredDark/index.ts:
--------------------------------------------------------------------------------
1 | import { mediaQuery } from "../mediaQuery";
2 |
3 | export function preferredDark() {
4 | return mediaQuery("(prefers-color-scheme: dark)");
5 | }
6 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredDark/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Readable dark theme preference."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredDark/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredLanguages/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
Preferred Languages:
11 |
12 | {#each $languages as lang (lang)}
13 | {lang}
14 | {/each}
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredLanguages/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow } from "$lib/shared";
2 | import { readable, type Readable } from "svelte/store";
3 |
4 | export function preferredLanguages(): Readable {
5 | return readable(["en"] as readonly string[], (set) => {
6 | const window = defaultWindow;
7 |
8 | let cleanup = () => {};
9 |
10 | if (window) {
11 | const navigator = window.navigator;
12 | set(navigator.languages);
13 |
14 | function update() {
15 | set(navigator.languages);
16 | }
17 |
18 | window.addEventListener("languagechange", update);
19 |
20 | cleanup = () => {
21 | window.removeEventListener("languagechange", update);
22 | }
23 | }
24 |
25 | return cleanup;
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredLanguages/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Readable Navigator Languages. It provides web developers with information about the user's preferred languages. For example, this may be useful to adjust the language of the user interface based on the user's preferred languages in order to provide better experience."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredLanguages/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredReduceMotion/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | Preferred Motion:
11 | {$motion}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredReduceMotion/index.ts:
--------------------------------------------------------------------------------
1 | import { derived } from "svelte/store";
2 | import { mediaQuery } from "../mediaQuery";
3 |
4 | export type ReducedMotionType = "reduce" | "no-preference";
5 |
6 | export function preferredReduceMotion() {
7 | const isReduced = mediaQuery("(prefers-reduced-motion: reduce)");
8 |
9 | return derived(isReduced, ($isReduced) => {
10 | if ($isReduced) return "reduce";
11 | return "no-preference";
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredReduceMotion/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Readable prefers-reduced-motion media query."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/preferredReduceMotion/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/reduceable/demo.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
{$counter}
23 |
24 |
25 |
counter.dispatch("inc")}>+
26 |
counter.dispatch("dec")}>-
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/utilities/reduceable/index.ts:
--------------------------------------------------------------------------------
1 | import { writable } from "svelte/store";
2 |
3 | type ReducerAction = any;
4 |
5 | type ReducerFunction = (state: T, action: ReducerAction) => T;
6 |
7 | export function reduceable(reducerFunction: ReducerFunction, initialState: T) {
8 | const { subscribe, update } = writable(initialState);
9 |
10 | function dispatch(action: ReducerAction) {
11 | update((state) => reducerFunction(state, action));
12 | }
13 |
14 | return {
15 | subscribe,
16 | dispatch,
17 | };
18 | }
19 |
--------------------------------------------------------------------------------
/src/lib/utilities/reduceable/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A helpful utility to use a sveltejs store as a reduceable store. It's like a useReducer hook in React. Define a reducer function and an initial state and you can use a regular `$` syntax to subscribe to state updates and a `dispatch` method to dispatch actions."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/reduceable/usage.txt:
--------------------------------------------------------------------------------
1 |
17 |
18 | {$counter}
19 | counter.dispatch("inc")}>+
20 | counter.dispatch("dec")}>-
--------------------------------------------------------------------------------
/src/lib/utilities/screenOrientation/demo.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 | For best results, please use a mobile or tablet device (or use your browser's native inspector
13 | to simulate an orientation change)
14 |
15 |
16 | isSupported: {#if isSupported}
17 | Yes
18 | {:else}
19 | No
20 | {/if}
21 |
22 |
Orientation Type: {orientation}
23 |
Orientation Angle: {angle}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/lib/utilities/screenOrientation/index.ts:
--------------------------------------------------------------------------------
1 | import { defaultWindow } from "$lib/shared";
2 | import { readable } from "svelte/store";
3 |
4 | export function screenOrientation() {
5 | return readable({ isSupported: false, orientation: "portrait-primary", angle: 0 }, (set) => {
6 | const window = defaultWindow;
7 | let stop = () => {};
8 |
9 | function update() {
10 | const isSupported = !!(window && "screen" in window && "orientation" in window.screen);
11 | if (!isSupported) return;
12 |
13 | const screenOrientation = isSupported
14 | ? window!.screen.orientation
15 | : ({} as ScreenOrientation);
16 | const orientation = screenOrientation.type;
17 | const angle = screenOrientation.angle || 0;
18 | set({
19 | isSupported,
20 | orientation,
21 | angle,
22 | });
23 | }
24 |
25 | if (window) {
26 | window.addEventListener("orientationchange", update);
27 | stop = () => {
28 | window.removeEventListener("orientationchange", update);
29 | }
30 | }
31 |
32 | update();
33 |
34 | return stop;
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/src/lib/utilities/screenOrientation/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Readable Screen Orientation API. It provides web developers with information about the user's current screen orientation."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/screenOrientation/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/utilities/startViewTransition/demo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 | View transitions will not be enabled
10 |
11 |
--------------------------------------------------------------------------------
/src/lib/utilities/startViewTransition/index.ts:
--------------------------------------------------------------------------------
1 | import { onNavigate } from "$app/navigation";
2 |
3 | /**
4 | * Starts the view transition API inside SvelteKit.
5 | * @see https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
6 | *
7 | *
8 | * @example
9 | * ```ts
10 | * import { startViewTransition } from 'svelte-legos';
11 | *
12 | * startViewTransition();
13 | * ```
14 | */
15 | export function startViewTransition() {
16 | onNavigate(({ complete }) => {
17 | if (!document.startViewTransition) return;
18 |
19 | return new Promise((resolve) => {
20 | document.startViewTransition(async () => {
21 | resolve();
22 | await complete;
23 | });
24 | });
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/src/lib/utilities/startViewTransition/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A utility to quickly enable the view transitions API"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/utilities/startViewTransition/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/watchers/pausableWatch/index.ts:
--------------------------------------------------------------------------------
1 | import { tryOnDestroy, writableToReadable } from "$lib/shared";
2 | import { writable, type Readable, get } from "svelte/store";
3 |
4 | type CallbackFunction = (newValue: T, oldVal: T) => void;
5 |
6 | interface WatcherParams {
7 | immediate?: boolean;
8 | }
9 |
10 | export function pausableWatch(
11 | store: Readable,
12 | callback: CallbackFunction,
13 | params: WatcherParams = {}
14 | ) {
15 | const { immediate = false } = params;
16 | let times = 0;
17 | let oldVal: T;
18 | let stopped = false;
19 | let isActive = writable(true);
20 |
21 | function pause() {
22 | !stopped && isActive.set(false);
23 | }
24 |
25 | function resume() {
26 | !stopped && isActive.set(true);
27 | }
28 |
29 | const __stop = store.subscribe((value) => {
30 | if (immediate && times === 0 && get(isActive)) {
31 | callback(value, oldVal);
32 | } else if (times > 0 && get(isActive)) {
33 | callback(value, oldVal);
34 | }
35 | oldVal = value;
36 | times++;
37 | });
38 |
39 | function stop() {
40 | stopped = true;
41 | __stop();
42 | }
43 |
44 | tryOnDestroy(stop);
45 |
46 | return { pause, resume, stop, isActive: writableToReadable(isActive) };
47 | }
48 |
--------------------------------------------------------------------------------
/src/lib/watchers/pausableWatch/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Pausable watch"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/watchers/pausableWatch/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/watchers/takeUntil/demo.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
Store value:
14 |
15 | {$counter}
16 |
17 |
18 |
19 |
$counter++}>+
20 |
$counter--}>-
21 |
22 |
23 |
Take until counter is less than 10:
24 |
25 | {$moreThan10}
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/watchers/takeUntil/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 |
3 | type PredicateFunction = (value: T) => boolean;
4 |
5 | export function takeUntil(store: Readable, predicate: PredicateFunction | boolean) {
6 | return derived(store, ($store, set) => {
7 | if (typeof predicate === "function") {
8 | if (!predicate($store)) {
9 | set($store);
10 | }
11 | } else {
12 | if (!predicate) {
13 | set($store);
14 | }
15 | }
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/src/lib/watchers/takeUntil/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A derived utility for stores to take values until the predicate function or value is false"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/watchers/takeUntil/usage.txt:
--------------------------------------------------------------------------------
1 |
8 |
9 | Store value:
10 | {$counter}
11 |
12 | $counter++}>+
13 | $counter--}>-
14 |
15 | Take until counter is less than 10:
16 | {$moreThan10}
--------------------------------------------------------------------------------
/src/lib/watchers/takeWhile/demo.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
Store value:
14 |
15 | {$counter}
16 |
17 |
18 |
19 |
$counter++}>+
20 |
$counter--}>-
21 |
22 |
23 |
Take while counter is less than 10:
24 |
25 | {$lessThan10}
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/watchers/takeWhile/index.ts:
--------------------------------------------------------------------------------
1 | import { derived, type Readable } from "svelte/store";
2 |
3 | type PredicateFunction = (value: T) => boolean;
4 |
5 | export function takeWhile(store: Readable, predicate: PredicateFunction | boolean) {
6 | return derived(store, ($store, set) => {
7 | if (typeof predicate === "function") {
8 | if (predicate($store)) {
9 | set($store);
10 | }
11 | } else {
12 | if (predicate) {
13 | set($store);
14 | }
15 | }
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/src/lib/watchers/takeWhile/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A derived utility for stores to take values while the predicate function or value is true"
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/watchers/takeWhile/usage.txt:
--------------------------------------------------------------------------------
1 |
8 |
9 | Store value:
10 | {$counter}
11 |
12 | $counter++}>+
13 | $counter--}>-
14 |
15 | Take until counter is less than 10:
16 | {$lessThan10}
--------------------------------------------------------------------------------
/src/lib/watchers/watch/demo.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
Store value:
16 |
17 | {$counter}
18 |
19 |
20 |
21 |
$counter++}>+
22 |
$counter--}>-
23 |
24 | (Check console)
25 |
26 |
--------------------------------------------------------------------------------
/src/lib/watchers/watch/index.ts:
--------------------------------------------------------------------------------
1 | import { tryOnDestroy } from "$lib/shared";
2 | import type { Readable } from "svelte/store";
3 |
4 | type CallbackFunction = (newValue: T, oldVal: T) => void;
5 |
6 | interface WatcherParams {
7 | immediate?: boolean;
8 | }
9 |
10 | export function watch(
11 | store: Readable,
12 | callback: CallbackFunction,
13 | params: WatcherParams = {}
14 | ) {
15 | const { immediate = false } = params;
16 | let times = 0;
17 | let oldVal: T;
18 | const unsub = store.subscribe((value) => {
19 | if (immediate && times === 0) {
20 | callback(value, oldVal);
21 | } else if (times > 0) {
22 | callback(value, oldVal);
23 | }
24 | oldVal = value;
25 | times++;
26 | });
27 |
28 | tryOnDestroy(unsub);
29 | }
30 |
--------------------------------------------------------------------------------
/src/lib/watchers/watch/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A simple watcher to watch your readables."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/watchers/watch/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/watchers/watchOnce/demo.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
Store value:
16 |
17 | {$counter}
18 |
19 |
20 |
21 |
$counter++}>+
22 |
$counter--}>-
23 |
24 | (Check console)
25 |
26 |
--------------------------------------------------------------------------------
/src/lib/watchers/watchOnce/index.ts:
--------------------------------------------------------------------------------
1 | import { tryOnDestroy } from "$lib/shared";
2 | import type { Readable } from "svelte/store";
3 |
4 | type CallbackFunction = (newValue: T, oldVal: T) => void;
5 |
6 | export function watchOnce(store: Readable, callback: CallbackFunction) {
7 | let isStopped = false;
8 | let times = 0;
9 | let oldVal: T;
10 | const unsub = store.subscribe((value) => {
11 | if (times === 1) {
12 | callback(value, oldVal);
13 | isStopped = true;
14 | unsub();
15 | }
16 | oldVal = value;
17 | times++;
18 | });
19 |
20 | tryOnDestroy(() => {
21 | if (!isStopped) unsub();
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/src/lib/watchers/watchOnce/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A watcher for readables but runs only once."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/watchers/watchOnce/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/watchers/watchWithFilter/demo.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
Store value:
18 |
19 | {$counter}
20 |
21 |
22 |
23 |
$counter++}>+
24 |
$counter--}>-
25 |
26 | (Check console)
27 |
28 |
--------------------------------------------------------------------------------
/src/lib/watchers/watchWithFilter/index.ts:
--------------------------------------------------------------------------------
1 | import { tryOnDestroy } from "$lib/shared";
2 | import type { Readable } from "svelte/store";
3 |
4 | type CallbackFunction = (newValue: T, oldVal: T) => void;
5 | type PredicateFunction = (value: T) => boolean;
6 |
7 | interface WatcherParams {
8 | immediate?: boolean;
9 | }
10 |
11 | export function watchWithFilter(
12 | store: Readable,
13 | predicateFn: PredicateFunction,
14 | callback: CallbackFunction,
15 | params: WatcherParams = {}
16 | ) {
17 | const { immediate = false } = params;
18 | let times = 0;
19 | let oldVal: T;
20 | const unsub = store.subscribe((value) => {
21 | if (immediate && times === 0) {
22 | predicateFn(value) && callback(value, oldVal);
23 | } else if (times > 0) {
24 | predicateFn(value) && callback(value, oldVal);
25 | }
26 | oldVal = value;
27 | times++;
28 | });
29 |
30 | tryOnDestroy(unsub);
31 |
32 | return unsub;
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib/watchers/watchWithFilter/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A watcher for readables but with filter predicate."
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/watchers/watchWithFilter/usage.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/routes/+error.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 | Svelte Legos
13 |
14 | {errorCode} - {errorMessage}
15 |
16 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | {SiteName} • {SiteTitle}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/routes/+layout.ts:
--------------------------------------------------------------------------------
1 | // This can be false if you're using a fallback (i.e. SPA mode)
2 | export const prerender = true;
3 | export const trailingSlash = "always";
4 |
--------------------------------------------------------------------------------
/src/routes/+page.server.ts:
--------------------------------------------------------------------------------
1 | import { totalUtilsLength } from "./directories";
2 |
3 | export async function load() {
4 | return {
5 | utilsLength: totalUtilsLength,
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/src/routes/DiscordIcon.svelte:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/src/routes/Feature.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
{icon}
9 |
{title}
10 |
{description}
11 |
12 |
--------------------------------------------------------------------------------
/src/routes/GithubIcon.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | GitHub
10 |
--------------------------------------------------------------------------------
/src/routes/SearchIcon.svelte:
--------------------------------------------------------------------------------
1 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/src/routes/TwitterIcon.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | Twitter
10 |
--------------------------------------------------------------------------------
/src/routes/constants.ts:
--------------------------------------------------------------------------------
1 | export const SiteName = "SvelteLegos";
2 | export const SiteTitle = "Collection of Essential Svelte utilities with Actions & Stores";
3 | export const SiteDescription = "SvelteLegos is a Collection of Essential Svelte Utilities";
4 |
--------------------------------------------------------------------------------
/src/routes/guides/+layout.server.ts:
--------------------------------------------------------------------------------
1 | import {
2 | totalUtilsLength,
3 | stores,
4 | actions,
5 | utilities,
6 | transitions,
7 | middlewares,
8 | derivatives,
9 | watchers,
10 | } from "../directories";
11 |
12 | export async function load() {
13 | return {
14 | totalUtilsLength,
15 | stores,
16 | actions,
17 | utilities,
18 | transitions,
19 | middlewares,
20 | derivatives,
21 | watchers,
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/src/routes/guides/+page.svelte:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
Getting Started
6 |
7 | SvelteLegos is a collection of utility functions based on Svelte
8 | APIs. These utilities will help you bring the Browser APIs into your svelte components without writing
9 | a lot of boilerplate code.
10 |
11 |
12 |
13 |
Installation
14 |
npm i svelte-legos
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/routes/guides/MenuIcon.svelte:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/routes/guides/layout-hook.server.ts:
--------------------------------------------------------------------------------
1 | import Prism from "prismjs";
2 | import "../../prism-svelte";
3 | import fs from "fs";
4 | export const prerender = true;
5 |
6 | const REPO_BASE_URL = "https://github.com/ankurrsinghal/svelte-legos/tree/master/src";
7 | const REPO_HOOKS_URL = (itemType: string) => REPO_BASE_URL + "/lib/" + itemType;
8 |
9 | function last(arr: T[]) {
10 | return arr[arr.length - 1];
11 | }
12 |
13 | function typeOfObject(path: string) {
14 | return path.split("/")[2];
15 | }
16 |
17 | export async function load({ route }: any) {
18 | const itemType = typeOfObject(route.id);
19 | const hookName = last(route.id.split("/"));
20 | let code = undefined;
21 | let meta = {};
22 | try {
23 | meta = JSON.parse(
24 | fs.readFileSync(`./src/lib/${itemType.toLowerCase()}/${hookName}/meta.json`).toString()
25 | );
26 | code = fs.readFileSync(`./src/lib/${itemType.toLowerCase()}/${hookName}/usage.txt`).toString();
27 | } catch (e) {}
28 | return {
29 | hookName,
30 | meta,
31 | code: code && Prism.highlight(code, Prism.languages.svelte, "svelte").trim(),
32 | sourceCodeURL: REPO_HOOKS_URL(itemType.toLowerCase()) + `/${hookName}/index.ts`,
33 | demoCodeURL: REPO_HOOKS_URL(itemType.toLowerCase()) + `/${hookName}/demo.svelte`,
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/src/routes/search/+page.server.ts:
--------------------------------------------------------------------------------
1 | import { all } from "../directories";
2 |
3 | export const load = ({ url }) => {
4 | const bricks = Object.entries(all)
5 | .map(([category, bricks]) => {
6 | return bricks.map((brick) => {
7 | return {
8 | url: `/guides/${category}/${brick}`,
9 | text: brick,
10 | };
11 | });
12 | })
13 | .flat();
14 |
15 | try {
16 | return { bricks, query: url.searchParams.get("query")?.trim() || "" };
17 | } catch (e) {
18 | return { bricks, query: "" };
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankurrsinghal/svelte-legos/c76f7ed24e700cfcb83047f2c2cfdcd8c3ec7f1f/static/favicon.png
--------------------------------------------------------------------------------
/static/fonts/fira-mono-latin-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankurrsinghal/svelte-legos/c76f7ed24e700cfcb83047f2c2cfdcd8c3ec7f1f/static/fonts/fira-mono-latin-400.woff2
--------------------------------------------------------------------------------
/static/fonts/overpass-latin-300.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankurrsinghal/svelte-legos/c76f7ed24e700cfcb83047f2c2cfdcd8c3ec7f1f/static/fonts/overpass-latin-300.woff2
--------------------------------------------------------------------------------
/static/fonts/overpass-latin-600.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankurrsinghal/svelte-legos/c76f7ed24e700cfcb83047f2c2cfdcd8c3ec7f1f/static/fonts/overpass-latin-600.woff2
--------------------------------------------------------------------------------
/static/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankurrsinghal/svelte-legos/c76f7ed24e700cfcb83047f2c2cfdcd8c3ec7f1f/static/logo.jpg
--------------------------------------------------------------------------------
/static/logo.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankurrsinghal/svelte-legos/c76f7ed24e700cfcb83047f2c2cfdcd8c3ec7f1f/static/logo.psd
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-static';
2 | import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors
7 | // for more information about preprocessors
8 | preprocess: vitePreprocess(),
9 |
10 | kit: {
11 | adapter: adapter()
12 | },
13 | vite: {
14 | ssr: {
15 | noExternal: ['prismjs', 'prism-svelte'],
16 | },
17 | },
18 | };
19 |
20 | export default config;
21 |
--------------------------------------------------------------------------------
/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ['./src/**/*.{html,js,svelte,ts}'],
4 | theme: {
5 | extend: {
6 | colors: {
7 | back: '#ffffff',
8 | 'back-light': '#f6fafd',
9 | 'back-api': '#e5eef5',
10 | prime: '#ff3e00',
11 | second: '#676778',
12 | flash: '#40b3ff',
13 | highlight: '#ffff82',
14 | heading: '#222',
15 | text: '#444',
16 | 'second-text': '#7b7766',
17 | 'sidebar-text': 'rgba(255, 255, 255, .9)'
18 | },
19 | fontWeight: {
20 | 300: 300,
21 | 600: 600
22 | },
23 | fontFamily: {
24 | fira: 'Fira'
25 | }
26 | }
27 | },
28 | plugins: []
29 | };
30 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true
12 | }
13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
14 | //
15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
16 | // from the referenced tsconfig.json - TypeScript does not merge them in
17 | }
18 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import fs from 'fs';
3 | import type { UserConfig } from 'vite';
4 |
5 | const config: UserConfig = {
6 | plugins: [sveltekit()],
7 | test: {
8 | include: ['src/**/*.{test,spec}.{js,ts}']
9 | }
10 | };
11 |
12 | export default config;
13 |
--------------------------------------------------------------------------------