@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 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
+ });
@@ -0,0 +1,4 @@
1
+ import { TanStackStoreSelector, UseSelectorOptions } from "./tan-stack-store-selector.cjs";
2
+ import { TanStackStoreAtom } from "./tan-stack-store-atom.cjs";
3
+ export * from "@tanstack/store";
4
+ export { TanStackStoreAtom, TanStackStoreSelector, UseSelectorOptions };
@@ -0,0 +1,4 @@
1
+ import { TanStackStoreSelector, UseSelectorOptions } from "./tan-stack-store-selector.js";
2
+ import { TanStackStoreAtom } from "./tan-stack-store-atom.js";
3
+ export * from "@tanstack/store";
4
+ export { TanStackStoreAtom, TanStackStoreSelector, UseSelectorOptions };
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { TanStackStoreSelector } from "./tan-stack-store-selector.js";
2
+ import { TanStackStoreAtom } from "./tan-stack-store-atom.js";
3
+
4
+ export * from "@tanstack/store"
5
+
6
+ export { TanStackStoreAtom, TanStackStoreSelector };
@@ -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,4 @@
1
+ export * from '@tanstack/store'
2
+
3
+ export * from './tan-stack-store-selector.js'
4
+ export * from './tan-stack-store-atom.js'
@@ -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
+ }