From 648aae7e31db02f6d69dba138b98e7cbfd398902 Mon Sep 17 00:00:00 2001 From: rahim Date: Tue, 6 Jan 2026 22:26:09 +1100 Subject: [PATCH] feat(store): lit bindings (#289) --- .claude/plans/store-bindings.md | 211 ++---------- CLAUDE.md | 35 +- eslint.config.mjs | 7 + packages/core/src/dom/index.ts | 1 - packages/core/src/dom/predicate.ts | 20 -- packages/core/src/dom/tests/predicate.test.ts | 77 ----- packages/store/README.md | 36 +-- packages/store/src/core/queue.ts | 118 ++++--- packages/store/src/core/request.ts | 2 +- packages/store/src/core/store.ts | 16 +- packages/store/src/core/tests/queue.test.ts | 123 +++---- .../store/src/core/tests/queue.types.test.ts | 156 +++++++++ packages/store/src/core/tests/request.test.ts | 16 +- .../store/src/core/tests/slice.types.test.ts | 147 +++++++++ .../store/src/core/tests/store.types.test.ts | 253 +++++++++++++++ packages/store/src/lit/controllers/index.ts | 23 ++ .../lit/controllers/mutation-controller.ts | 139 ++++++++ .../lit/controllers/optimistic-controller.ts | 165 ++++++++++ .../src/lit/controllers/request-controller.ts | 37 +++ .../lit/controllers/selector-controller.ts | 54 ++++ .../src/lit/controllers/tasks-controller.ts | 55 ++++ .../tests/mutation-controller.test.ts | 252 +++++++++++++++ .../tests/optimistic-controller.test.ts | 302 ++++++++++++++++++ .../tests/request-controller.test.ts | 45 +++ .../tests/selector-controller.test.ts | 117 +++++++ .../tests/tasks-controller.test.ts | 94 ++++++ .../src/lit/controllers/tests/types.test.ts | 143 +++++++++ packages/store/src/lit/create-store.ts | 134 ++++++++ packages/store/src/lit/index.ts | 20 ++ packages/store/src/lit/mixins/attach-mixin.ts | 89 ++++++ .../store/src/lit/mixins/combined-mixin.ts | 41 +++ packages/store/src/lit/mixins/index.ts | 3 + .../store/src/lit/mixins/provider-mixin.ts | 82 +++++ .../src/lit/mixins/tests/attach-mixin.test.ts | 30 ++ .../lit/mixins/tests/combined-mixin.test.ts | 162 ++++++++++ .../lit/mixins/tests/provider-mixin.test.ts | 114 +++++++ .../store/src/lit/mixins/tests/types.test.ts | 32 ++ .../store/src/lit/tests/create-store.test.ts | 115 +++++++ packages/store/src/lit/tests/test-utils.ts | 163 ++++++++++ packages/store/src/react/context.tsx | 2 + packages/store/src/react/hooks.ts | 1 + packages/utils/src/dom/index.ts | 3 + packages/utils/src/dom/predicates.ts | 11 + packages/utils/src/dom/slotted.ts | 42 +++ .../utils/src/dom/tests/predicates.test.ts | 77 +++++ packages/utils/src/dom/tests/slotted.test.ts | 175 ++++++++++ packages/utils/src/dom/types.ts | 17 + packages/utils/src/function/index.ts | 1 + packages/utils/src/function/noop.ts | 1 + packages/utils/src/types/types.ts | 14 + 50 files changed, 3561 insertions(+), 412 deletions(-) delete mode 100644 packages/core/src/dom/predicate.ts delete mode 100644 packages/core/src/dom/tests/predicate.test.ts create mode 100644 packages/store/src/core/tests/queue.types.test.ts create mode 100644 packages/store/src/core/tests/slice.types.test.ts create mode 100644 packages/store/src/core/tests/store.types.test.ts create mode 100644 packages/store/src/lit/controllers/index.ts create mode 100644 packages/store/src/lit/controllers/mutation-controller.ts create mode 100644 packages/store/src/lit/controllers/optimistic-controller.ts create mode 100644 packages/store/src/lit/controllers/request-controller.ts create mode 100644 packages/store/src/lit/controllers/selector-controller.ts create mode 100644 packages/store/src/lit/controllers/tasks-controller.ts create mode 100644 packages/store/src/lit/controllers/tests/mutation-controller.test.ts create mode 100644 packages/store/src/lit/controllers/tests/optimistic-controller.test.ts create mode 100644 packages/store/src/lit/controllers/tests/request-controller.test.ts create mode 100644 packages/store/src/lit/controllers/tests/selector-controller.test.ts create mode 100644 packages/store/src/lit/controllers/tests/tasks-controller.test.ts create mode 100644 packages/store/src/lit/controllers/tests/types.test.ts create mode 100644 packages/store/src/lit/create-store.ts create mode 100644 packages/store/src/lit/mixins/attach-mixin.ts create mode 100644 packages/store/src/lit/mixins/combined-mixin.ts create mode 100644 packages/store/src/lit/mixins/index.ts create mode 100644 packages/store/src/lit/mixins/provider-mixin.ts create mode 100644 packages/store/src/lit/mixins/tests/attach-mixin.test.ts create mode 100644 packages/store/src/lit/mixins/tests/combined-mixin.test.ts create mode 100644 packages/store/src/lit/mixins/tests/provider-mixin.test.ts create mode 100644 packages/store/src/lit/mixins/tests/types.test.ts create mode 100644 packages/store/src/lit/tests/create-store.test.ts create mode 100644 packages/store/src/lit/tests/test-utils.ts create mode 100644 packages/utils/src/dom/predicates.ts create mode 100644 packages/utils/src/dom/slotted.ts create mode 100644 packages/utils/src/dom/tests/predicates.test.ts create mode 100644 packages/utils/src/dom/tests/slotted.test.ts create mode 100644 packages/utils/src/dom/types.ts create mode 100644 packages/utils/src/function/noop.ts diff --git a/.claude/plans/store-bindings.md b/.claude/plans/store-bindings.md index 098f0e01..e4ffeaa7 100644 --- a/.claude/plans/store-bindings.md +++ b/.claude/plans/store-bindings.md @@ -75,165 +75,20 @@ Types live next to implementations (no separate `types.ts`). --- -## Phase 2: Lit Bindings (`@videojs/store/lit`) +## Phase 2: Lit Bindings (`@videojs/store/lit`) [DONE] -All DOM/Lit bindings live together since they all depend on `@lit/context`. +> Refer to [PR #289](https://github.com/videojs/v10/pull/289) for implementation details. -### 2.0 @lit/context Research +Basic Lit bindings for the store: -Key findings from analyzing `@lit/context@1.1.6`: +- Controllers: `SelectorController`, `RequestController`, `TasksController` for reactive state +- Mixins: `StoreProviderMixin`, `StoreAttachMixin`, `StoreMixin` (combined) for custom elements +- `createStore()` factory returning typed mixins, context, and `create()` function +- Auto-attach media elements via slot change observation +- Proper cleanup on disconnect (slot listeners, subscriptions) +- Sync controller values on reconnect to avoid stale state -**Dynamic Value Updates:** - -- `ContextProvider.setValue(newValue, force?)` notifies all subscribed consumers -- Uses `Object.is()` for equality - swapping store objects triggers updates automatically -- `force = true` needed only for in-place mutations (same reference) - -**Subscription Model:** - -- Consumers must opt-in: `subscribe: true` in `@consume()` or `ContextConsumer` -- Without subscription, consumers only receive initial value -- Provider stores callbacks in `Map` -- `updateObservers()` iterates all callbacks on value change - -**Store Swapping Pattern (validated):** - -```typescript -class MySkin extends HTMLElement { - #provider = new ContextProvider(this, { context: storeContext }); - - set store(newStore: Store) { - this.#provider.setValue(newStore); // Notifies all subscribers - } -} -``` - -### 2.1 `createStore` - -**File:** `packages/store/src/lit/create-store.ts` - -```typescript -import type { Context } from '@lit/context'; -import type { ReactiveControllerHost } from '@lit/reactive-element'; -import type { AnySlice, InferSliceTarget, StoreConfig } from '../core'; - -import { createContext } from '@lit/context'; - -export interface CreateStoreConfig extends StoreConfig< - InferSliceTarget, - Slices -> {} - -export interface CreateStoreResult { - /** Combined mixin: provides store via context AND auto-attaches slotted media */ - StoreMixin: >(Base: T) => T; - /** Mixin that provides store via context (no auto-attach) */ - StoreProviderMixin: >(Base: T) => T; - /** Mixin that auto-attaches slotted media elements (requires store from context) */ - StoreAttachMixin: >(Base: T) => T; - /** Context for consuming store in controllers */ - context: Context, Slices>>; - /** Creates a store instance for imperative access */ - create: () => Store, Slices>; -} - -export function createStore(config: CreateStoreConfig): CreateStoreResult; -``` - -**Implementation details:** - -- Uses `@lit/context` for W3C Context Protocol -- Context key auto-generated per `createStore()` call (unique Symbol) -- `StoreMixin`: Combined mixin, equivalent to `StoreAttachMixin(StoreProviderMixin(Base))` -- `StoreProviderMixin`: Mixin that: - - Creates store instance - - Uses `ContextProvider` internally - - Exposes `store` setter that calls `provider.setValue(newStore)` -- `StoreAttachMixin`: Mixin that: - - Consumes store from context - - Observes slotted elements for `