@tanstack/lit-store 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +13 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/tan-stack-store-atom.cjs +46 -0
- package/dist/tan-stack-store-atom.cjs.map +1 -0
- package/dist/tan-stack-store-atom.d.cts +38 -0
- package/dist/tan-stack-store-atom.d.ts +38 -0
- package/dist/tan-stack-store-atom.js +46 -0
- package/dist/tan-stack-store-atom.js.map +1 -0
- package/dist/tan-stack-store-selector.cjs +57 -0
- package/dist/tan-stack-store-selector.cjs.map +1 -0
- package/dist/tan-stack-store-selector.d.cts +21 -0
- package/dist/tan-stack-store-selector.d.ts +21 -0
- package/dist/tan-stack-store-selector.js +56 -0
- package/dist/tan-stack-store-selector.js.map +1 -0
- package/package.json +60 -0
- package/src/index.ts +4 -0
- package/src/tan-stack-store-atom.ts +56 -0
- package/src/tan-stack-store-selector.ts +79 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_tan_stack_store_selector = require('./tan-stack-store-selector.cjs');
|
|
3
|
+
const require_tan_stack_store_atom = require('./tan-stack-store-atom.cjs');
|
|
4
|
+
|
|
5
|
+
exports.TanStackStoreAtom = require_tan_stack_store_atom.TanStackStoreAtom;
|
|
6
|
+
exports.TanStackStoreSelector = require_tan_stack_store_selector.TanStackStoreSelector;
|
|
7
|
+
var _tanstack_store = require("@tanstack/store");
|
|
8
|
+
Object.keys(_tanstack_store).forEach(function (k) {
|
|
9
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function () { return _tanstack_store[k]; }
|
|
12
|
+
});
|
|
13
|
+
});
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const require_tan_stack_store_selector = require('./tan-stack-store-selector.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/tan-stack-store-atom.ts
|
|
4
|
+
/**
|
|
5
|
+
* Subscribes a Lit host to a writable atom and exposes its current value
|
|
6
|
+
* along with a stable setter.
|
|
7
|
+
*
|
|
8
|
+
* Use this when an element needs to both read and update the same writable
|
|
9
|
+
* atom. The host will only re-render when the atom's value actually changes
|
|
10
|
+
* (according to the configured `compare` function).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* class CounterEl extends LitElement {
|
|
15
|
+
* `#count` = new TanStackStoreAtom(this, () => countAtom)
|
|
16
|
+
*
|
|
17
|
+
* render() {
|
|
18
|
+
* return html`
|
|
19
|
+
* <button @click=${() => this.#count.set((prev) => prev + 1)}>
|
|
20
|
+
* ${this.#count.value}
|
|
21
|
+
* </button>
|
|
22
|
+
* `
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
var TanStackStoreAtom = class {
|
|
28
|
+
#getAtom;
|
|
29
|
+
constructor(host, getAtom, options) {
|
|
30
|
+
this.#getAtom = getAtom;
|
|
31
|
+
new require_tan_stack_store_selector.TanStackStoreSelector(host, getAtom, void 0, options);
|
|
32
|
+
this.set = this.set.bind(this);
|
|
33
|
+
}
|
|
34
|
+
get value() {
|
|
35
|
+
return this.#getAtom()?.get();
|
|
36
|
+
}
|
|
37
|
+
set(valueOrUpdater) {
|
|
38
|
+
const atom = this.#getAtom();
|
|
39
|
+
if (!atom) return;
|
|
40
|
+
atom.set(valueOrUpdater);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
exports.TanStackStoreAtom = TanStackStoreAtom;
|
|
46
|
+
//# sourceMappingURL=tan-stack-store-atom.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tan-stack-store-atom.cjs","names":["#getAtom","TanStackStoreSelector"],"sources":["../src/tan-stack-store-atom.ts"],"sourcesContent":["import { TanStackStoreSelector } from './tan-stack-store-selector.js'\nimport type { Atom } from '@tanstack/store'\nimport type { ReactiveControllerHost } from 'lit'\nimport type { UseSelectorOptions } from './tan-stack-store-selector.js'\n\n/**\n * Subscribes a Lit host to a writable atom and exposes its current value\n * along with a stable setter.\n *\n * Use this when an element needs to both read and update the same writable\n * atom. The host will only re-render when the atom's value actually changes\n * (according to the configured `compare` function).\n *\n * @example\n * ```ts\n * class CounterEl extends LitElement {\n * `#count` = new TanStackStoreAtom(this, () => countAtom)\n *\n * render() {\n * return html`\n * <button @click=${() => this.#count.set((prev) => prev + 1)}>\n * ${this.#count.value}\n * </button>\n * `\n * }\n * }\n * ```\n */\nexport class TanStackStoreAtom<TValue> {\n #getAtom: () => Atom<TValue> | undefined\n\n constructor(\n host: ReactiveControllerHost,\n getAtom: () => Atom<TValue> | undefined,\n options?: UseSelectorOptions<TValue>,\n ) {\n this.#getAtom = getAtom\n new TanStackStoreSelector(host, getAtom, undefined, options)\n\n this.set = this.set.bind(this)\n }\n\n get value(): TValue | undefined {\n return this.#getAtom()?.get()\n }\n\n set(value: TValue): void\n set(updater: (prev: TValue) => TValue): void\n set(valueOrUpdater: TValue | ((prev: TValue) => TValue)): void {\n const atom = this.#getAtom()\n if (!atom) return\n ;(atom.set as (v: TValue | ((prev: TValue) => TValue)) => void)(\n valueOrUpdater,\n )\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,oBAAb,MAAuC;CACrC;CAEA,YACE,MACA,SACA,SACA;AACA,QAAKA,UAAW;AAChB,MAAIC,uDAAsB,MAAM,SAAS,QAAW,QAAQ;AAE5D,OAAK,MAAM,KAAK,IAAI,KAAK,KAAK;;CAGhC,IAAI,QAA4B;AAC9B,SAAO,MAAKD,SAAU,EAAE,KAAK;;CAK/B,IAAI,gBAA2D;EAC7D,MAAM,OAAO,MAAKA,SAAU;AAC5B,MAAI,CAAC,KAAM;AACV,EAAC,KAAK,IACL,eACD"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { UseSelectorOptions } from "./tan-stack-store-selector.cjs";
|
|
2
|
+
import { Atom } from "@tanstack/store";
|
|
3
|
+
import { ReactiveControllerHost } from "lit";
|
|
4
|
+
|
|
5
|
+
//#region src/tan-stack-store-atom.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Subscribes a Lit host to a writable atom and exposes its current value
|
|
8
|
+
* along with a stable setter.
|
|
9
|
+
*
|
|
10
|
+
* Use this when an element needs to both read and update the same writable
|
|
11
|
+
* atom. The host will only re-render when the atom's value actually changes
|
|
12
|
+
* (according to the configured `compare` function).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* class CounterEl extends LitElement {
|
|
17
|
+
* `#count` = new TanStackStoreAtom(this, () => countAtom)
|
|
18
|
+
*
|
|
19
|
+
* render() {
|
|
20
|
+
* return html`
|
|
21
|
+
* <button @click=${() => this.#count.set((prev) => prev + 1)}>
|
|
22
|
+
* ${this.#count.value}
|
|
23
|
+
* </button>
|
|
24
|
+
* `
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare class TanStackStoreAtom<TValue> {
|
|
30
|
+
#private;
|
|
31
|
+
constructor(host: ReactiveControllerHost, getAtom: () => Atom<TValue> | undefined, options?: UseSelectorOptions<TValue>);
|
|
32
|
+
get value(): TValue | undefined;
|
|
33
|
+
set(value: TValue): void;
|
|
34
|
+
set(updater: (prev: TValue) => TValue): void;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { TanStackStoreAtom };
|
|
38
|
+
//# sourceMappingURL=tan-stack-store-atom.d.cts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { UseSelectorOptions } from "./tan-stack-store-selector.js";
|
|
2
|
+
import { Atom } from "@tanstack/store";
|
|
3
|
+
import { ReactiveControllerHost } from "lit";
|
|
4
|
+
|
|
5
|
+
//#region src/tan-stack-store-atom.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Subscribes a Lit host to a writable atom and exposes its current value
|
|
8
|
+
* along with a stable setter.
|
|
9
|
+
*
|
|
10
|
+
* Use this when an element needs to both read and update the same writable
|
|
11
|
+
* atom. The host will only re-render when the atom's value actually changes
|
|
12
|
+
* (according to the configured `compare` function).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* class CounterEl extends LitElement {
|
|
17
|
+
* `#count` = new TanStackStoreAtom(this, () => countAtom)
|
|
18
|
+
*
|
|
19
|
+
* render() {
|
|
20
|
+
* return html`
|
|
21
|
+
* <button @click=${() => this.#count.set((prev) => prev + 1)}>
|
|
22
|
+
* ${this.#count.value}
|
|
23
|
+
* </button>
|
|
24
|
+
* `
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare class TanStackStoreAtom<TValue> {
|
|
30
|
+
#private;
|
|
31
|
+
constructor(host: ReactiveControllerHost, getAtom: () => Atom<TValue> | undefined, options?: UseSelectorOptions<TValue>);
|
|
32
|
+
get value(): TValue | undefined;
|
|
33
|
+
set(value: TValue): void;
|
|
34
|
+
set(updater: (prev: TValue) => TValue): void;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { TanStackStoreAtom };
|
|
38
|
+
//# sourceMappingURL=tan-stack-store-atom.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { TanStackStoreSelector } from "./tan-stack-store-selector.js";
|
|
2
|
+
|
|
3
|
+
//#region src/tan-stack-store-atom.ts
|
|
4
|
+
/**
|
|
5
|
+
* Subscribes a Lit host to a writable atom and exposes its current value
|
|
6
|
+
* along with a stable setter.
|
|
7
|
+
*
|
|
8
|
+
* Use this when an element needs to both read and update the same writable
|
|
9
|
+
* atom. The host will only re-render when the atom's value actually changes
|
|
10
|
+
* (according to the configured `compare` function).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* class CounterEl extends LitElement {
|
|
15
|
+
* `#count` = new TanStackStoreAtom(this, () => countAtom)
|
|
16
|
+
*
|
|
17
|
+
* render() {
|
|
18
|
+
* return html`
|
|
19
|
+
* <button @click=${() => this.#count.set((prev) => prev + 1)}>
|
|
20
|
+
* ${this.#count.value}
|
|
21
|
+
* </button>
|
|
22
|
+
* `
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
var TanStackStoreAtom = class {
|
|
28
|
+
#getAtom;
|
|
29
|
+
constructor(host, getAtom, options) {
|
|
30
|
+
this.#getAtom = getAtom;
|
|
31
|
+
new TanStackStoreSelector(host, getAtom, void 0, options);
|
|
32
|
+
this.set = this.set.bind(this);
|
|
33
|
+
}
|
|
34
|
+
get value() {
|
|
35
|
+
return this.#getAtom()?.get();
|
|
36
|
+
}
|
|
37
|
+
set(valueOrUpdater) {
|
|
38
|
+
const atom = this.#getAtom();
|
|
39
|
+
if (!atom) return;
|
|
40
|
+
atom.set(valueOrUpdater);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { TanStackStoreAtom };
|
|
46
|
+
//# sourceMappingURL=tan-stack-store-atom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tan-stack-store-atom.js","names":["#getAtom"],"sources":["../src/tan-stack-store-atom.ts"],"sourcesContent":["import { TanStackStoreSelector } from './tan-stack-store-selector.js'\nimport type { Atom } from '@tanstack/store'\nimport type { ReactiveControllerHost } from 'lit'\nimport type { UseSelectorOptions } from './tan-stack-store-selector.js'\n\n/**\n * Subscribes a Lit host to a writable atom and exposes its current value\n * along with a stable setter.\n *\n * Use this when an element needs to both read and update the same writable\n * atom. The host will only re-render when the atom's value actually changes\n * (according to the configured `compare` function).\n *\n * @example\n * ```ts\n * class CounterEl extends LitElement {\n * `#count` = new TanStackStoreAtom(this, () => countAtom)\n *\n * render() {\n * return html`\n * <button @click=${() => this.#count.set((prev) => prev + 1)}>\n * ${this.#count.value}\n * </button>\n * `\n * }\n * }\n * ```\n */\nexport class TanStackStoreAtom<TValue> {\n #getAtom: () => Atom<TValue> | undefined\n\n constructor(\n host: ReactiveControllerHost,\n getAtom: () => Atom<TValue> | undefined,\n options?: UseSelectorOptions<TValue>,\n ) {\n this.#getAtom = getAtom\n new TanStackStoreSelector(host, getAtom, undefined, options)\n\n this.set = this.set.bind(this)\n }\n\n get value(): TValue | undefined {\n return this.#getAtom()?.get()\n }\n\n set(value: TValue): void\n set(updater: (prev: TValue) => TValue): void\n set(valueOrUpdater: TValue | ((prev: TValue) => TValue)): void {\n const atom = this.#getAtom()\n if (!atom) return\n ;(atom.set as (v: TValue | ((prev: TValue) => TValue)) => void)(\n valueOrUpdater,\n )\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,oBAAb,MAAuC;CACrC;CAEA,YACE,MACA,SACA,SACA;AACA,QAAKA,UAAW;AAChB,MAAI,sBAAsB,MAAM,SAAS,QAAW,QAAQ;AAE5D,OAAK,MAAM,KAAK,IAAI,KAAK,KAAK;;CAGhC,IAAI,QAA4B;AAC9B,SAAO,MAAKA,SAAU,EAAE,KAAK;;CAK/B,IAAI,gBAA2D;EAC7D,MAAM,OAAO,MAAKA,SAAU;AAC5B,MAAI,CAAC,KAAM;AACV,EAAC,KAAK,IACL,eACD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/tan-stack-store-selector.ts
|
|
3
|
+
function defaultCompare(a, b) {
|
|
4
|
+
return a === b;
|
|
5
|
+
}
|
|
6
|
+
function defaultSelector(snapshot) {
|
|
7
|
+
return snapshot;
|
|
8
|
+
}
|
|
9
|
+
var TanStackStoreSelector = class {
|
|
10
|
+
#host;
|
|
11
|
+
#getStore;
|
|
12
|
+
#selector;
|
|
13
|
+
#compare;
|
|
14
|
+
#unsubscribe;
|
|
15
|
+
#subscribedStore;
|
|
16
|
+
#hasSelected = false;
|
|
17
|
+
#lastSelected;
|
|
18
|
+
constructor(host, getStore, selector = defaultSelector, options) {
|
|
19
|
+
this.#host = host;
|
|
20
|
+
this.#getStore = getStore;
|
|
21
|
+
this.#selector = selector;
|
|
22
|
+
this.#compare = options?.compare ?? defaultCompare;
|
|
23
|
+
host.addController(this);
|
|
24
|
+
}
|
|
25
|
+
hostUpdate() {
|
|
26
|
+
const store = this.#getStore();
|
|
27
|
+
if (store === this.#subscribedStore) return;
|
|
28
|
+
this.#unsubscribe?.();
|
|
29
|
+
this.#subscribedStore = store;
|
|
30
|
+
if (!store) {
|
|
31
|
+
this.#unsubscribe = void 0;
|
|
32
|
+
this.#hasSelected = false;
|
|
33
|
+
this.#lastSelected = void 0;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.#lastSelected = this.#selector(store.get());
|
|
37
|
+
this.#hasSelected = true;
|
|
38
|
+
this.#unsubscribe = store.subscribe((value) => {
|
|
39
|
+
const next = this.#selector(value);
|
|
40
|
+
if (this.#hasSelected && this.#compare(this.#lastSelected, next)) return;
|
|
41
|
+
this.#lastSelected = next;
|
|
42
|
+
this.#hasSelected = true;
|
|
43
|
+
this.#host.requestUpdate();
|
|
44
|
+
}).unsubscribe;
|
|
45
|
+
}
|
|
46
|
+
hostDisconnected() {
|
|
47
|
+
this.#unsubscribe?.();
|
|
48
|
+
this.#unsubscribe = void 0;
|
|
49
|
+
this.#subscribedStore = void 0;
|
|
50
|
+
this.#hasSelected = false;
|
|
51
|
+
this.#lastSelected = void 0;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
exports.TanStackStoreSelector = TanStackStoreSelector;
|
|
57
|
+
//# sourceMappingURL=tan-stack-store-selector.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tan-stack-store-selector.cjs","names":["#host","#getStore","#selector","#compare","#subscribedStore","#unsubscribe","#hasSelected","#lastSelected"],"sources":["../src/tan-stack-store-selector.ts"],"sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit'\n\nexport interface UseSelectorOptions<TSelected> {\n compare?: (a: TSelected, b: TSelected) => boolean\n}\n\nfunction defaultCompare<T>(a: T, b: T) {\n return a === b\n}\n\nfunction defaultSelector<TSource, TSelected>(snapshot: TSource): TSelected {\n return snapshot as unknown as TSelected\n}\n\ntype SelectionSource<T> = {\n get: () => T\n subscribe: (listener: (value: T) => void) => {\n unsubscribe: () => void\n }\n}\n\nexport class TanStackStoreSelector<\n TSource,\n TSelected = NoInfer<TSource>,\n> implements ReactiveController {\n #host: ReactiveControllerHost\n #getStore: () => SelectionSource<TSource> | undefined\n #selector: (snapshot: TSource) => TSelected\n #compare: (a: TSelected, b: TSelected) => boolean\n #unsubscribe?: () => void\n #subscribedStore?: SelectionSource<TSource>\n #hasSelected = false\n #lastSelected?: TSelected\n\n constructor(\n host: ReactiveControllerHost,\n getStore: () => SelectionSource<TSource> | undefined,\n selector: (snapshot: TSource) => TSelected = defaultSelector,\n options?: UseSelectorOptions<TSelected>,\n ) {\n this.#host = host\n this.#getStore = getStore\n this.#selector = selector\n this.#compare = options?.compare ?? defaultCompare\n host.addController(this)\n }\n\n hostUpdate() {\n const store = this.#getStore()\n if (store === this.#subscribedStore) return\n this.#unsubscribe?.()\n this.#subscribedStore = store\n if (!store) {\n this.#unsubscribe = undefined\n this.#hasSelected = false\n this.#lastSelected = undefined\n return\n }\n this.#lastSelected = this.#selector(store.get())\n this.#hasSelected = true\n this.#unsubscribe = store.subscribe((value) => {\n const next = this.#selector(value)\n if (this.#hasSelected && this.#compare(this.#lastSelected!, next)) {\n return\n }\n this.#lastSelected = next\n this.#hasSelected = true\n this.#host.requestUpdate()\n }).unsubscribe\n }\n\n hostDisconnected() {\n this.#unsubscribe?.()\n this.#unsubscribe = undefined\n this.#subscribedStore = undefined\n this.#hasSelected = false\n this.#lastSelected = undefined\n }\n}\n"],"mappings":";;AAMA,SAAS,eAAkB,GAAM,GAAM;AACrC,QAAO,MAAM;;AAGf,SAAS,gBAAoC,UAA8B;AACzE,QAAO;;AAUT,IAAa,wBAAb,MAGgC;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA,eAAe;CACf;CAEA,YACE,MACA,UACA,WAA6C,iBAC7C,SACA;AACA,QAAKA,OAAQ;AACb,QAAKC,WAAY;AACjB,QAAKC,WAAY;AACjB,QAAKC,UAAW,SAAS,WAAW;AACpC,OAAK,cAAc,KAAK;;CAG1B,aAAa;EACX,MAAM,QAAQ,MAAKF,UAAW;AAC9B,MAAI,UAAU,MAAKG,gBAAkB;AACrC,QAAKC,eAAgB;AACrB,QAAKD,kBAAmB;AACxB,MAAI,CAAC,OAAO;AACV,SAAKC,cAAe;AACpB,SAAKC,cAAe;AACpB,SAAKC,eAAgB;AACrB;;AAEF,QAAKA,eAAgB,MAAKL,SAAU,MAAM,KAAK,CAAC;AAChD,QAAKI,cAAe;AACpB,QAAKD,cAAe,MAAM,WAAW,UAAU;GAC7C,MAAM,OAAO,MAAKH,SAAU,MAAM;AAClC,OAAI,MAAKI,eAAgB,MAAKH,QAAS,MAAKI,cAAgB,KAAK,CAC/D;AAEF,SAAKA,eAAgB;AACrB,SAAKD,cAAe;AACpB,SAAKN,KAAM,eAAe;IAC1B,CAAC;;CAGL,mBAAmB;AACjB,QAAKK,eAAgB;AACrB,QAAKA,cAAe;AACpB,QAAKD,kBAAmB;AACxB,QAAKE,cAAe;AACpB,QAAKC,eAAgB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ReactiveController, ReactiveControllerHost } from "lit";
|
|
2
|
+
|
|
3
|
+
//#region src/tan-stack-store-selector.d.ts
|
|
4
|
+
interface UseSelectorOptions<TSelected> {
|
|
5
|
+
compare?: (a: TSelected, b: TSelected) => boolean;
|
|
6
|
+
}
|
|
7
|
+
type SelectionSource<T> = {
|
|
8
|
+
get: () => T;
|
|
9
|
+
subscribe: (listener: (value: T) => void) => {
|
|
10
|
+
unsubscribe: () => void;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
declare class TanStackStoreSelector<TSource, TSelected = NoInfer<TSource>> implements ReactiveController {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(host: ReactiveControllerHost, getStore: () => SelectionSource<TSource> | undefined, selector?: (snapshot: TSource) => TSelected, options?: UseSelectorOptions<TSelected>);
|
|
16
|
+
hostUpdate(): void;
|
|
17
|
+
hostDisconnected(): void;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { TanStackStoreSelector, UseSelectorOptions };
|
|
21
|
+
//# sourceMappingURL=tan-stack-store-selector.d.cts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ReactiveController, ReactiveControllerHost } from "lit";
|
|
2
|
+
|
|
3
|
+
//#region src/tan-stack-store-selector.d.ts
|
|
4
|
+
interface UseSelectorOptions<TSelected> {
|
|
5
|
+
compare?: (a: TSelected, b: TSelected) => boolean;
|
|
6
|
+
}
|
|
7
|
+
type SelectionSource<T> = {
|
|
8
|
+
get: () => T;
|
|
9
|
+
subscribe: (listener: (value: T) => void) => {
|
|
10
|
+
unsubscribe: () => void;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
declare class TanStackStoreSelector<TSource, TSelected = NoInfer<TSource>> implements ReactiveController {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(host: ReactiveControllerHost, getStore: () => SelectionSource<TSource> | undefined, selector?: (snapshot: TSource) => TSelected, options?: UseSelectorOptions<TSelected>);
|
|
16
|
+
hostUpdate(): void;
|
|
17
|
+
hostDisconnected(): void;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { TanStackStoreSelector, UseSelectorOptions };
|
|
21
|
+
//# sourceMappingURL=tan-stack-store-selector.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/tan-stack-store-selector.ts
|
|
2
|
+
function defaultCompare(a, b) {
|
|
3
|
+
return a === b;
|
|
4
|
+
}
|
|
5
|
+
function defaultSelector(snapshot) {
|
|
6
|
+
return snapshot;
|
|
7
|
+
}
|
|
8
|
+
var TanStackStoreSelector = class {
|
|
9
|
+
#host;
|
|
10
|
+
#getStore;
|
|
11
|
+
#selector;
|
|
12
|
+
#compare;
|
|
13
|
+
#unsubscribe;
|
|
14
|
+
#subscribedStore;
|
|
15
|
+
#hasSelected = false;
|
|
16
|
+
#lastSelected;
|
|
17
|
+
constructor(host, getStore, selector = defaultSelector, options) {
|
|
18
|
+
this.#host = host;
|
|
19
|
+
this.#getStore = getStore;
|
|
20
|
+
this.#selector = selector;
|
|
21
|
+
this.#compare = options?.compare ?? defaultCompare;
|
|
22
|
+
host.addController(this);
|
|
23
|
+
}
|
|
24
|
+
hostUpdate() {
|
|
25
|
+
const store = this.#getStore();
|
|
26
|
+
if (store === this.#subscribedStore) return;
|
|
27
|
+
this.#unsubscribe?.();
|
|
28
|
+
this.#subscribedStore = store;
|
|
29
|
+
if (!store) {
|
|
30
|
+
this.#unsubscribe = void 0;
|
|
31
|
+
this.#hasSelected = false;
|
|
32
|
+
this.#lastSelected = void 0;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.#lastSelected = this.#selector(store.get());
|
|
36
|
+
this.#hasSelected = true;
|
|
37
|
+
this.#unsubscribe = store.subscribe((value) => {
|
|
38
|
+
const next = this.#selector(value);
|
|
39
|
+
if (this.#hasSelected && this.#compare(this.#lastSelected, next)) return;
|
|
40
|
+
this.#lastSelected = next;
|
|
41
|
+
this.#hasSelected = true;
|
|
42
|
+
this.#host.requestUpdate();
|
|
43
|
+
}).unsubscribe;
|
|
44
|
+
}
|
|
45
|
+
hostDisconnected() {
|
|
46
|
+
this.#unsubscribe?.();
|
|
47
|
+
this.#unsubscribe = void 0;
|
|
48
|
+
this.#subscribedStore = void 0;
|
|
49
|
+
this.#hasSelected = false;
|
|
50
|
+
this.#lastSelected = void 0;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
export { TanStackStoreSelector };
|
|
56
|
+
//# sourceMappingURL=tan-stack-store-selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tan-stack-store-selector.js","names":["#host","#getStore","#selector","#compare","#subscribedStore","#unsubscribe","#hasSelected","#lastSelected"],"sources":["../src/tan-stack-store-selector.ts"],"sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit'\n\nexport interface UseSelectorOptions<TSelected> {\n compare?: (a: TSelected, b: TSelected) => boolean\n}\n\nfunction defaultCompare<T>(a: T, b: T) {\n return a === b\n}\n\nfunction defaultSelector<TSource, TSelected>(snapshot: TSource): TSelected {\n return snapshot as unknown as TSelected\n}\n\ntype SelectionSource<T> = {\n get: () => T\n subscribe: (listener: (value: T) => void) => {\n unsubscribe: () => void\n }\n}\n\nexport class TanStackStoreSelector<\n TSource,\n TSelected = NoInfer<TSource>,\n> implements ReactiveController {\n #host: ReactiveControllerHost\n #getStore: () => SelectionSource<TSource> | undefined\n #selector: (snapshot: TSource) => TSelected\n #compare: (a: TSelected, b: TSelected) => boolean\n #unsubscribe?: () => void\n #subscribedStore?: SelectionSource<TSource>\n #hasSelected = false\n #lastSelected?: TSelected\n\n constructor(\n host: ReactiveControllerHost,\n getStore: () => SelectionSource<TSource> | undefined,\n selector: (snapshot: TSource) => TSelected = defaultSelector,\n options?: UseSelectorOptions<TSelected>,\n ) {\n this.#host = host\n this.#getStore = getStore\n this.#selector = selector\n this.#compare = options?.compare ?? defaultCompare\n host.addController(this)\n }\n\n hostUpdate() {\n const store = this.#getStore()\n if (store === this.#subscribedStore) return\n this.#unsubscribe?.()\n this.#subscribedStore = store\n if (!store) {\n this.#unsubscribe = undefined\n this.#hasSelected = false\n this.#lastSelected = undefined\n return\n }\n this.#lastSelected = this.#selector(store.get())\n this.#hasSelected = true\n this.#unsubscribe = store.subscribe((value) => {\n const next = this.#selector(value)\n if (this.#hasSelected && this.#compare(this.#lastSelected!, next)) {\n return\n }\n this.#lastSelected = next\n this.#hasSelected = true\n this.#host.requestUpdate()\n }).unsubscribe\n }\n\n hostDisconnected() {\n this.#unsubscribe?.()\n this.#unsubscribe = undefined\n this.#subscribedStore = undefined\n this.#hasSelected = false\n this.#lastSelected = undefined\n }\n}\n"],"mappings":";AAMA,SAAS,eAAkB,GAAM,GAAM;AACrC,QAAO,MAAM;;AAGf,SAAS,gBAAoC,UAA8B;AACzE,QAAO;;AAUT,IAAa,wBAAb,MAGgC;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA,eAAe;CACf;CAEA,YACE,MACA,UACA,WAA6C,iBAC7C,SACA;AACA,QAAKA,OAAQ;AACb,QAAKC,WAAY;AACjB,QAAKC,WAAY;AACjB,QAAKC,UAAW,SAAS,WAAW;AACpC,OAAK,cAAc,KAAK;;CAG1B,aAAa;EACX,MAAM,QAAQ,MAAKF,UAAW;AAC9B,MAAI,UAAU,MAAKG,gBAAkB;AACrC,QAAKC,eAAgB;AACrB,QAAKD,kBAAmB;AACxB,MAAI,CAAC,OAAO;AACV,SAAKC,cAAe;AACpB,SAAKC,cAAe;AACpB,SAAKC,eAAgB;AACrB;;AAEF,QAAKA,eAAgB,MAAKL,SAAU,MAAM,KAAK,CAAC;AAChD,QAAKI,cAAe;AACpB,QAAKD,cAAe,MAAM,WAAW,UAAU;GAC7C,MAAM,OAAO,MAAKH,SAAU,MAAM;AAClC,OAAI,MAAKI,eAAgB,MAAKH,QAAS,MAAKI,cAAgB,KAAK,CAC/D;AAEF,SAAKA,eAAgB;AACrB,SAAKD,cAAe;AACpB,SAAKN,KAAM,eAAe;IAC1B,CAAC;;CAGL,mBAAmB;AACjB,QAAKK,eAAgB;AACrB,QAAKA,cAAe;AACpB,QAAKD,kBAAmB;AACxB,QAAKE,cAAe;AACpB,QAAKC,eAAgB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tanstack/lit-store",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Framework agnostic type-safe store w/ reactive framework adapters",
|
|
5
|
+
"author": "tannerlinsley",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/TanStack/store.git",
|
|
10
|
+
"directory": "packages/lit-store"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://tanstack.com/store",
|
|
13
|
+
"funding": {
|
|
14
|
+
"type": "github",
|
|
15
|
+
"url": "https://github.com/sponsors/tannerlinsley"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"store",
|
|
19
|
+
"lit",
|
|
20
|
+
"typescript"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"clean": "premove ./build ./coverage",
|
|
24
|
+
"test:eslint": "eslint ./src ./tests",
|
|
25
|
+
"test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"",
|
|
26
|
+
"test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js",
|
|
27
|
+
"test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js",
|
|
28
|
+
"test:types:ts58": "node ../../node_modules/typescript58/lib/tsc.js",
|
|
29
|
+
"test:types:ts59": "tsc",
|
|
30
|
+
"test:lib": "vitest",
|
|
31
|
+
"test:lib:dev": "pnpm run test:lib --watch",
|
|
32
|
+
"test:build": "publint --strict",
|
|
33
|
+
"build": "tsdown --tsconfig tsconfig.build.json"
|
|
34
|
+
},
|
|
35
|
+
"type": "module",
|
|
36
|
+
"types": "./dist/index.d.cts",
|
|
37
|
+
"main": "./dist/index.cjs",
|
|
38
|
+
"module": "./dist/index.js",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"import": "./dist/index.js",
|
|
42
|
+
"require": "./dist/index.cjs"
|
|
43
|
+
},
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"sideEffects": false,
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"src"
|
|
50
|
+
],
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@tanstack/store": "workspace:*"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"lit": "^3.3.1"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"lit": "^3.0.0"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { TanStackStoreSelector } from './tan-stack-store-selector.js'
|
|
2
|
+
import type { Atom } from '@tanstack/store'
|
|
3
|
+
import type { ReactiveControllerHost } from 'lit'
|
|
4
|
+
import type { UseSelectorOptions } from './tan-stack-store-selector.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Subscribes a Lit host to a writable atom and exposes its current value
|
|
8
|
+
* along with a stable setter.
|
|
9
|
+
*
|
|
10
|
+
* Use this when an element needs to both read and update the same writable
|
|
11
|
+
* atom. The host will only re-render when the atom's value actually changes
|
|
12
|
+
* (according to the configured `compare` function).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* class CounterEl extends LitElement {
|
|
17
|
+
* `#count` = new TanStackStoreAtom(this, () => countAtom)
|
|
18
|
+
*
|
|
19
|
+
* render() {
|
|
20
|
+
* return html`
|
|
21
|
+
* <button @click=${() => this.#count.set((prev) => prev + 1)}>
|
|
22
|
+
* ${this.#count.value}
|
|
23
|
+
* </button>
|
|
24
|
+
* `
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export class TanStackStoreAtom<TValue> {
|
|
30
|
+
#getAtom: () => Atom<TValue> | undefined
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
host: ReactiveControllerHost,
|
|
34
|
+
getAtom: () => Atom<TValue> | undefined,
|
|
35
|
+
options?: UseSelectorOptions<TValue>,
|
|
36
|
+
) {
|
|
37
|
+
this.#getAtom = getAtom
|
|
38
|
+
new TanStackStoreSelector(host, getAtom, undefined, options)
|
|
39
|
+
|
|
40
|
+
this.set = this.set.bind(this)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get value(): TValue | undefined {
|
|
44
|
+
return this.#getAtom()?.get()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
set(value: TValue): void
|
|
48
|
+
set(updater: (prev: TValue) => TValue): void
|
|
49
|
+
set(valueOrUpdater: TValue | ((prev: TValue) => TValue)): void {
|
|
50
|
+
const atom = this.#getAtom()
|
|
51
|
+
if (!atom) return
|
|
52
|
+
;(atom.set as (v: TValue | ((prev: TValue) => TValue)) => void)(
|
|
53
|
+
valueOrUpdater,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit'
|
|
2
|
+
|
|
3
|
+
export interface UseSelectorOptions<TSelected> {
|
|
4
|
+
compare?: (a: TSelected, b: TSelected) => boolean
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function defaultCompare<T>(a: T, b: T) {
|
|
8
|
+
return a === b
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function defaultSelector<TSource, TSelected>(snapshot: TSource): TSelected {
|
|
12
|
+
return snapshot as unknown as TSelected
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type SelectionSource<T> = {
|
|
16
|
+
get: () => T
|
|
17
|
+
subscribe: (listener: (value: T) => void) => {
|
|
18
|
+
unsubscribe: () => void
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class TanStackStoreSelector<
|
|
23
|
+
TSource,
|
|
24
|
+
TSelected = NoInfer<TSource>,
|
|
25
|
+
> implements ReactiveController {
|
|
26
|
+
#host: ReactiveControllerHost
|
|
27
|
+
#getStore: () => SelectionSource<TSource> | undefined
|
|
28
|
+
#selector: (snapshot: TSource) => TSelected
|
|
29
|
+
#compare: (a: TSelected, b: TSelected) => boolean
|
|
30
|
+
#unsubscribe?: () => void
|
|
31
|
+
#subscribedStore?: SelectionSource<TSource>
|
|
32
|
+
#hasSelected = false
|
|
33
|
+
#lastSelected?: TSelected
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
host: ReactiveControllerHost,
|
|
37
|
+
getStore: () => SelectionSource<TSource> | undefined,
|
|
38
|
+
selector: (snapshot: TSource) => TSelected = defaultSelector,
|
|
39
|
+
options?: UseSelectorOptions<TSelected>,
|
|
40
|
+
) {
|
|
41
|
+
this.#host = host
|
|
42
|
+
this.#getStore = getStore
|
|
43
|
+
this.#selector = selector
|
|
44
|
+
this.#compare = options?.compare ?? defaultCompare
|
|
45
|
+
host.addController(this)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
hostUpdate() {
|
|
49
|
+
const store = this.#getStore()
|
|
50
|
+
if (store === this.#subscribedStore) return
|
|
51
|
+
this.#unsubscribe?.()
|
|
52
|
+
this.#subscribedStore = store
|
|
53
|
+
if (!store) {
|
|
54
|
+
this.#unsubscribe = undefined
|
|
55
|
+
this.#hasSelected = false
|
|
56
|
+
this.#lastSelected = undefined
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
this.#lastSelected = this.#selector(store.get())
|
|
60
|
+
this.#hasSelected = true
|
|
61
|
+
this.#unsubscribe = store.subscribe((value) => {
|
|
62
|
+
const next = this.#selector(value)
|
|
63
|
+
if (this.#hasSelected && this.#compare(this.#lastSelected!, next)) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
this.#lastSelected = next
|
|
67
|
+
this.#hasSelected = true
|
|
68
|
+
this.#host.requestUpdate()
|
|
69
|
+
}).unsubscribe
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
hostDisconnected() {
|
|
73
|
+
this.#unsubscribe?.()
|
|
74
|
+
this.#unsubscribe = undefined
|
|
75
|
+
this.#subscribedStore = undefined
|
|
76
|
+
this.#hasSelected = false
|
|
77
|
+
this.#lastSelected = undefined
|
|
78
|
+
}
|
|
79
|
+
}
|