@repobit/dex-store-elements 1.7.3 → 2.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.
Files changed (227) hide show
  1. package/CHANGELOG.md +6 -906
  2. package/README.md +308 -283
  3. package/dist/src/actions/action.adapter.d.ts +36 -0
  4. package/dist/src/actions/action.adapter.js +183 -0
  5. package/dist/src/actions/action.adapter.js.map +1 -0
  6. package/dist/src/actions/action.attributes.d.ts +15 -0
  7. package/dist/src/actions/action.attributes.js +137 -0
  8. package/dist/src/actions/action.attributes.js.map +1 -0
  9. package/dist/src/actions/action.registry.d.ts +4 -0
  10. package/dist/src/actions/action.registry.js +119 -0
  11. package/dist/src/actions/action.registry.js.map +1 -0
  12. package/dist/src/actions/index.d.ts +3 -2
  13. package/dist/src/actions/index.js +3 -80
  14. package/dist/src/actions/index.js.map +1 -1
  15. package/dist/src/compute/compute.expand.d.ts +70 -0
  16. package/dist/src/compute/compute.expand.js +319 -0
  17. package/dist/src/compute/compute.expand.js.map +1 -0
  18. package/dist/src/compute/compute.scan.d.ts +54 -0
  19. package/dist/src/compute/compute.scan.js +160 -0
  20. package/dist/src/compute/compute.scan.js.map +1 -0
  21. package/dist/src/compute/option-resolver.d.ts +31 -0
  22. package/dist/src/compute/option-resolver.js +147 -0
  23. package/dist/src/compute/option-resolver.js.map +1 -0
  24. package/dist/src/contexts/context.compute.d.ts +12 -0
  25. package/dist/src/contexts/context.compute.js +4 -0
  26. package/dist/src/contexts/context.compute.js.map +1 -0
  27. package/dist/src/contexts/context.scope.d.ts +139 -0
  28. package/dist/src/contexts/context.scope.js +48 -0
  29. package/dist/src/contexts/context.scope.js.map +1 -0
  30. package/dist/src/controllers/controller.action-resolve.d.ts +110 -0
  31. package/dist/src/controllers/controller.action-resolve.js +203 -0
  32. package/dist/src/controllers/controller.action-resolve.js.map +1 -0
  33. package/dist/src/controllers/controller.action-topology.d.ts +59 -0
  34. package/dist/src/controllers/controller.action-topology.js +130 -0
  35. package/dist/src/controllers/controller.action-topology.js.map +1 -0
  36. package/dist/src/controllers/controller.compute.d.ts +72 -0
  37. package/dist/src/controllers/controller.compute.js +110 -0
  38. package/dist/src/controllers/controller.compute.js.map +1 -0
  39. package/dist/src/controllers/controller.resolve.d.ts +78 -0
  40. package/dist/src/controllers/controller.resolve.js +205 -0
  41. package/dist/src/controllers/controller.resolve.js.map +1 -0
  42. package/dist/src/core/action-topology.d.ts +62 -0
  43. package/dist/src/core/action-topology.js +67 -0
  44. package/dist/src/core/action-topology.js.map +1 -0
  45. package/dist/src/core/actions.d.ts +64 -0
  46. package/dist/src/core/actions.js +47 -0
  47. package/dist/src/core/actions.js.map +1 -0
  48. package/dist/src/core/node.element.d.ts +179 -0
  49. package/dist/src/core/node.element.js +573 -0
  50. package/dist/src/core/node.element.js.map +1 -0
  51. package/dist/src/core/node.scope.d.ts +374 -0
  52. package/dist/src/core/node.scope.js +558 -0
  53. package/dist/src/core/node.scope.js.map +1 -0
  54. package/dist/src/core/option-update.d.ts +11 -0
  55. package/dist/src/core/option-update.js +59 -0
  56. package/dist/src/core/option-update.js.map +1 -0
  57. package/dist/src/dsl/compilers/array/compiler.js +13 -14
  58. package/dist/src/dsl/compilers/array/compiler.js.map +1 -1
  59. package/dist/src/dsl/compilers/boolean/compiler.d.ts +1 -1
  60. package/dist/src/dsl/compilers/boolean/compiler.js +14 -27
  61. package/dist/src/dsl/compilers/boolean/compiler.js.map +1 -1
  62. package/dist/src/dsl/compilers/enum/compiler.js +7 -10
  63. package/dist/src/dsl/compilers/enum/compiler.js.map +1 -1
  64. package/dist/src/dsl/compilers/index.d.ts +1 -1
  65. package/dist/src/dsl/compilers/index.js +5 -5
  66. package/dist/src/dsl/compilers/index.js.map +1 -1
  67. package/dist/src/dsl/utilty.d.ts +0 -4
  68. package/dist/src/dsl/utilty.js +3 -30
  69. package/dist/src/dsl/utilty.js.map +1 -1
  70. package/dist/src/eta/batch.d.ts +123 -0
  71. package/dist/src/eta/batch.js +162 -0
  72. package/dist/src/eta/batch.js.map +1 -0
  73. package/dist/src/eta/batcher.d.ts +184 -0
  74. package/dist/src/eta/batcher.js +335 -0
  75. package/dist/src/eta/batcher.js.map +1 -0
  76. package/dist/src/eta/cache.d.ts +36 -0
  77. package/dist/src/eta/cache.js +56 -0
  78. package/dist/src/eta/cache.js.map +1 -0
  79. package/dist/src/eta/controller.d.ts +608 -0
  80. package/dist/src/eta/controller.js +1036 -0
  81. package/dist/src/eta/controller.js.map +1 -0
  82. package/dist/src/eta/factory.d.ts +74 -0
  83. package/dist/src/eta/factory.js +121 -0
  84. package/dist/src/eta/factory.js.map +1 -0
  85. package/dist/src/eta/idle.d.ts +39 -0
  86. package/dist/src/eta/idle.js +52 -0
  87. package/dist/src/eta/idle.js.map +1 -0
  88. package/dist/src/eta/index.d.ts +52 -0
  89. package/dist/src/eta/index.js +13 -0
  90. package/dist/src/eta/index.js.map +1 -0
  91. package/dist/src/eta/observer.d.ts +263 -0
  92. package/dist/src/eta/observer.js +263 -0
  93. package/dist/src/eta/observer.js.map +1 -0
  94. package/dist/src/eta/operations.d.ts +41 -0
  95. package/dist/src/eta/operations.js +59 -0
  96. package/dist/src/eta/operations.js.map +1 -0
  97. package/dist/src/eta/source-registry.d.ts +198 -0
  98. package/dist/src/eta/source-registry.js +267 -0
  99. package/dist/src/eta/source-registry.js.map +1 -0
  100. package/dist/src/eta/template-index.d.ts +285 -0
  101. package/dist/src/eta/template-index.js +642 -0
  102. package/dist/src/eta/template-index.js.map +1 -0
  103. package/dist/src/eta/types.d.ts +529 -0
  104. package/dist/src/eta/types.js +2 -0
  105. package/dist/src/eta/types.js.map +1 -0
  106. package/dist/src/index.d.ts +21 -9
  107. package/dist/src/index.js +22 -11
  108. package/dist/src/index.js.map +1 -1
  109. package/dist/src/nodes/node.context.d.ts +5 -3
  110. package/dist/src/nodes/node.context.js +48 -3
  111. package/dist/src/nodes/node.context.js.map +1 -1
  112. package/dist/src/nodes/node.option.d.ts +11 -31
  113. package/dist/src/nodes/node.option.js +158 -222
  114. package/dist/src/nodes/node.option.js.map +1 -1
  115. package/dist/src/nodes/node.product.d.ts +7 -11
  116. package/dist/src/nodes/node.product.js +85 -64
  117. package/dist/src/nodes/node.product.js.map +1 -1
  118. package/dist/src/renders/attributes/buyLink.d.ts +2 -2
  119. package/dist/src/renders/attributes/buyLink.js +14 -11
  120. package/dist/src/renders/attributes/buyLink.js.map +1 -1
  121. package/dist/src/renders/attributes/devices.d.ts +2 -2
  122. package/dist/src/renders/attributes/devices.js +9 -19
  123. package/dist/src/renders/attributes/devices.js.map +1 -1
  124. package/dist/src/renders/attributes/discount.d.ts +2 -2
  125. package/dist/src/renders/attributes/discount.js +56 -44
  126. package/dist/src/renders/attributes/discount.js.map +1 -1
  127. package/dist/src/renders/attributes/hide.d.ts +2 -2
  128. package/dist/src/renders/attributes/hide.js +49 -14
  129. package/dist/src/renders/attributes/hide.js.map +1 -1
  130. package/dist/src/renders/attributes/index.d.ts +3 -3
  131. package/dist/src/renders/attributes/index.js +2 -4
  132. package/dist/src/renders/attributes/index.js.map +1 -1
  133. package/dist/src/renders/attributes/price.d.ts +2 -2
  134. package/dist/src/renders/attributes/price.js +30 -44
  135. package/dist/src/renders/attributes/price.js.map +1 -1
  136. package/dist/src/renders/attributes/subscription.d.ts +2 -2
  137. package/dist/src/renders/attributes/subscription.js +14 -20
  138. package/dist/src/renders/attributes/subscription.js.map +1 -1
  139. package/dist/src/renders/attributes/utilty.d.ts +2 -0
  140. package/dist/src/renders/attributes/utilty.js +17 -4
  141. package/dist/src/renders/attributes/utilty.js.map +1 -1
  142. package/dist/src/renders/context.d.ts +122 -10
  143. package/dist/src/renders/context.js +140 -80
  144. package/dist/src/renders/context.js.map +1 -1
  145. package/dist/src/renders/format.js +2 -2
  146. package/dist/src/renders/format.js.map +1 -1
  147. package/dist/src/renders/index.d.ts +6 -2
  148. package/dist/src/renders/index.js +67 -29
  149. package/dist/src/renders/index.js.map +1 -1
  150. package/dist/src/renders/observe.d.ts +1 -4
  151. package/dist/src/renders/observe.js +10 -24
  152. package/dist/src/renders/observe.js.map +1 -1
  153. package/dist/src/renders/render.wrapper.d.ts +37 -0
  154. package/dist/src/renders/render.wrapper.js +147 -0
  155. package/dist/src/renders/render.wrapper.js.map +1 -0
  156. package/dist/src/templating/eta.js +1 -4
  157. package/dist/src/templating/eta.js.map +1 -1
  158. package/package.json +4 -5
  159. package/dist/src/actions/action.button.d.ts +0 -2
  160. package/dist/src/actions/action.button.js +0 -8
  161. package/dist/src/actions/action.button.js.map +0 -1
  162. package/dist/src/actions/action.input.d.ts +0 -2
  163. package/dist/src/actions/action.input.js +0 -43
  164. package/dist/src/actions/action.input.js.map +0 -1
  165. package/dist/src/actions/action.select.d.ts +0 -2
  166. package/dist/src/actions/action.select.js +0 -14
  167. package/dist/src/actions/action.select.js.map +0 -1
  168. package/dist/src/actions/utilty.d.ts +0 -16
  169. package/dist/src/actions/utilty.js +0 -149
  170. package/dist/src/actions/utilty.js.map +0 -1
  171. package/dist/src/contexts/context.datalayer.d.ts +0 -9
  172. package/dist/src/contexts/context.datalayer.js +0 -3
  173. package/dist/src/contexts/context.datalayer.js.map +0 -1
  174. package/dist/src/contexts/context.derived.d.ts +0 -12
  175. package/dist/src/contexts/context.derived.js +0 -3
  176. package/dist/src/contexts/context.derived.js.map +0 -1
  177. package/dist/src/contexts/context.option.d.ts +0 -5
  178. package/dist/src/contexts/context.option.js +0 -3
  179. package/dist/src/contexts/context.option.js.map +0 -1
  180. package/dist/src/contexts/context.product.d.ts +0 -5
  181. package/dist/src/contexts/context.product.js +0 -3
  182. package/dist/src/contexts/context.product.js.map +0 -1
  183. package/dist/src/contexts/context.state.d.ts +0 -28
  184. package/dist/src/contexts/context.state.js +0 -3
  185. package/dist/src/contexts/context.state.js.map +0 -1
  186. package/dist/src/contexts/context.store.d.ts +0 -5
  187. package/dist/src/contexts/context.store.js +0 -3
  188. package/dist/src/contexts/context.store.js.map +0 -1
  189. package/dist/src/controllers/collect-controller.d.ts +0 -39
  190. package/dist/src/controllers/collect-controller.js +0 -161
  191. package/dist/src/controllers/collect-controller.js.map +0 -1
  192. package/dist/src/controllers/compute-controller.d.ts +0 -55
  193. package/dist/src/controllers/compute-controller.js +0 -350
  194. package/dist/src/controllers/compute-controller.js.map +0 -1
  195. package/dist/src/controllers/eta-renderer/eta-dom-renderer.d.ts +0 -25
  196. package/dist/src/controllers/eta-renderer/eta-dom-renderer.js +0 -192
  197. package/dist/src/controllers/eta-renderer/eta-dom-renderer.js.map +0 -1
  198. package/dist/src/controllers/eta-renderer/eta-mutation-observer.d.ts +0 -28
  199. package/dist/src/controllers/eta-renderer/eta-mutation-observer.js +0 -206
  200. package/dist/src/controllers/eta-renderer/eta-mutation-observer.js.map +0 -1
  201. package/dist/src/controllers/eta-renderer/eta-renderer.d.ts +0 -21
  202. package/dist/src/controllers/eta-renderer/eta-renderer.js +0 -77
  203. package/dist/src/controllers/eta-renderer/eta-renderer.js.map +0 -1
  204. package/dist/src/controllers/eta-renderer/eta-template-cache.d.ts +0 -24
  205. package/dist/src/controllers/eta-renderer/eta-template-cache.js +0 -61
  206. package/dist/src/controllers/eta-renderer/eta-template-cache.js.map +0 -1
  207. package/dist/src/controllers/eta-renderer/eta-types.d.ts +0 -11
  208. package/dist/src/controllers/eta-renderer/eta-types.js +0 -1
  209. package/dist/src/controllers/eta-renderer/eta-types.js.map +0 -1
  210. package/dist/src/controllers/eta-renderer/index.d.ts +0 -2
  211. package/dist/src/controllers/eta-renderer/index.js +0 -2
  212. package/dist/src/controllers/eta-renderer/index.js.map +0 -1
  213. package/dist/src/controllers/event-pipeline-controller.d.ts +0 -28
  214. package/dist/src/controllers/event-pipeline-controller.js +0 -72
  215. package/dist/src/controllers/event-pipeline-controller.js.map +0 -1
  216. package/dist/src/events/events.d.ts +0 -75
  217. package/dist/src/events/events.js +0 -78
  218. package/dist/src/events/events.js.map +0 -1
  219. package/dist/src/nodes/node.root.d.ts +0 -16
  220. package/dist/src/nodes/node.root.js +0 -49
  221. package/dist/src/nodes/node.root.js.map +0 -1
  222. package/dist/src/nodes/node.state.d.ts +0 -88
  223. package/dist/src/nodes/node.state.js +0 -330
  224. package/dist/src/nodes/node.state.js.map +0 -1
  225. package/dist/src/renders/utility.d.ts +0 -32
  226. package/dist/src/renders/utility.js +0 -191
  227. package/dist/src/renders/utility.js.map +0 -1
package/README.md CHANGED
@@ -1,15 +1,17 @@
1
1
  # @repobit/dex-store-elements
2
2
 
3
- Lightweight HTML custom elements + attribute renderers for building dynamic pricing UIs on top of `@repobit/dex-store`.
3
+ Reactive HTML custom elements + declarative attribute renderers for building dynamic pricing UIs on top of `@repobit/dex-store`.
4
4
 
5
- - Custom elements: `<bd-root>`, `<bd-product>`, `<bd-option>`, `<bd-state>`, `<bd-context>`
6
- - Unified attribute-based renderers (no framework required)
7
- - Eta templates for text/HTML and attributes
8
- - Single, merged data context across product, option and state
9
- - Extensible “derived” variables/functions you can compute and use anywhere
5
+ - Custom elements: `<bd-product>`, `<bd-option>`, `<bd-context>` (a Lit-based, scope-providing node tree)
6
+ - Declarative renderers via `data-store-*` attributes no framework glue code
7
+ - Eta templates for text and attributes with a unified DSL context
8
+ - Compute layer (min/max over the reachable option space) feeds a flat state DTO into templates and the hide DSL
9
+ - Composable event topology: ignore-list filtering via `data-store-event-id` + `ignore-events`
10
10
 
11
11
  ## Requirements
12
+
12
13
  - Node 18+
14
+ - A browser (or jsdom for tests)
13
15
 
14
16
  ## Install
15
17
 
@@ -17,80 +19,315 @@ Lightweight HTML custom elements + attribute renderers for building dynamic pric
17
19
  npm i @repobit/dex-store-elements @repobit/dex-store
18
20
  ```
19
21
 
20
- Peer dependencies are resolved automatically by npm; no extra install command is required.
21
-
22
22
  ## Quick start
23
23
 
24
24
  ```html
25
- <!-- index.html -->
26
25
  <script type="module">
27
- import { registerContextNodes, registerActionNodes, registerRenderNodes } from '@repobit/dex-store-elements';
26
+ import {
27
+ registerContextNodes,
28
+ registerActionNodes,
29
+ registerRenderNodes
30
+ } from '@repobit/dex-store-elements';
28
31
  import { Store } from '@repobit/dex-store';
29
32
 
30
- window.addEventListener('DOMContentLoaded', async () => {
33
+ window.addEventListener('DOMContentLoaded', () => {
31
34
  registerContextNodes();
32
- const root = document.querySelector('bd-root');
33
- root.store = new Store({
34
- locale: 'en-us',
35
+
36
+ const context = document.querySelector('bd-context');
37
+
38
+ context.store = new Store({
39
+ locale : 'en-us',
35
40
  provider: { name: 'vlaicu' }
36
41
  });
37
42
 
38
43
  // Optional: analytics data layer callback
39
- // Fires once per <bd-option> that sets `data-layer-event`
40
- root.dataLayer = ({ option, event }) => {
41
- // Example GTM-style push; adapt fields as needed
44
+ // Fires once per <bd-option> that declares `data-layer-event`.
45
+ context.dataLayer = ({ option, event }) => {
42
46
  window.dataLayer?.push({
43
47
  event,
44
48
  productId : option.getProduct().getId(),
45
49
  campaign : option.getProduct().getCampaign(),
46
- variation : option.getVariation(), // e.g. "5-12"
50
+ variation : option.getVariation(),
47
51
  devices : option.getDevices(),
48
52
  subscription: option.getSubscription(),
49
53
  price : option.getDiscountedPrice({ currency: false })
50
54
  });
51
55
  };
52
56
 
53
- // Optional: define derived values/functions for templates + hide DSL
54
- root.derived = async ({ option }) => ({
57
+ // Optional: derived values/functions available in templates + hide DSL.
58
+ context.derived = async ({ option }) => ({
55
59
  mails: (p) => ((option?.getDevices?.() ?? 0) / p) * 100
56
60
  });
57
61
 
58
- const disposeActions = registerActionNodes(root);
59
- const disposeRenders = registerRenderNodes(root);
62
+ const disposeActions = registerActionNodes(context);
63
+ const disposeRenders = registerRenderNodes(context);
60
64
 
61
- // Optional teardown (for SPA route changes/unmounts)
65
+ // Call disposers during teardown (SPA route changes, dynamic mounts).
62
66
  // disposeActions();
63
67
  // disposeRenders();
64
68
  });
65
69
  </script>
66
70
 
67
- <bd-root store-name="root">
68
- <bd-product store-name="product" product-id="com.bitdefender.tsmd.v2">
71
+ <bd-context>
72
+ <bd-product product-id="com.bitdefender.tsmd.v2">
69
73
  <bd-option devices="5" subscription="12" data-layer-event="info">
70
- <!-- Attribute renderers (see below) -->
74
+ <!-- Attribute renderers -->
71
75
  <div data-store-render data-store-devices></div>
72
- <div data-store-render data-store-subscription data-store-subscription-type="years"></div>
76
+ <div data-store-render
77
+ data-store-subscription
78
+ data-store-subscription-type="years"></div>
73
79
  <div data-store-render data-store-price="discounted || full"></div>
74
80
  <a data-store-render data-store-buy-link>Buy</a>
81
+
75
82
  <!-- Eta template (text) -->
76
83
  <p>Now at only {{= it.option.price.discounted }}!</p>
77
- <!-- Eta template (attribute, implicit) -->
84
+
85
+ <!-- Eta template (implicit attribute) -->
78
86
  <div title="Devices {{= it.option.devices }}"></div>
79
- <!-- Hide via DSL using merged context -->
80
- <div data-store-render data-store-hide="!it.option.price.discounted">
81
- Hidden when discounted price doesn't exists
87
+
88
+ <!-- Hide via DSL -->
89
+ <div data-store-render
90
+ data-store-hide="!it.option.price.discounted">
91
+ Hidden when no discounted price exists
82
92
  </div>
83
- <!-- Actions -->
84
- <button data-store-action data-store-set-devices="25">25 devices</button>
93
+
94
+ <!-- Action: emit a store event -->
95
+ <button data-store-action
96
+ data-store-set-devices="25"
97
+ data-store-event-id="devicesBtn">
98
+ 25 devices
99
+ </button>
85
100
  </bd-option>
86
101
  </bd-product>
87
- </bd-root>
102
+ </bd-context>
103
+ ```
104
+
105
+ ## Architecture in 30 seconds
106
+
107
+ - `bd-context` publishes a `BdScope` (store, derived, dataLayer, batch schedule) and is a **hard event boundary** — events from outside it cannot drive descendants inside it.
108
+ - `bd-product` consumes a scope, publishes `BdScope.product` (plus a normalised `option` field).
109
+ - `bd-option` consumes a scope and publishes `BdScope.option` after resolving the option from `store + devices + subscription + action`.
110
+ - `registerActionNodes(root)` attaches `BdActionElementAdapter`s to every element matching `[data-store-action]` (or that declares one of the `data-store-set-*` attributes). Adapters dispatch `bd-action-request` events on user interaction; the nearest `bd-*` ancestor catches them and drives its own scope.
111
+ - `registerRenderNodes(root)` walks every `[data-store-render]` element and runs the matching attribute handler whenever an observed attribute changes.
112
+ - Compute (`bd-compute`, opt-out via `compute-disabled`) reduces the reachable option space to a `BdComputeResult` with min/max formatted strings, available as `it.state.*` in templates.
113
+
114
+ ## Custom elements
115
+
116
+ Only three elements are registered. `bd-root` / `bd-state` from v1 are intentionally gone — `bd-context` is the scope-publishing root.
117
+
118
+ | Tag | Attributes | Notes |
119
+ | --- | --- | --- |
120
+ | `<bd-context>` | `ignore-events`, `ignore-events-parent` (always true), `eta-disabled`, `compute-disabled`, `topology-boundary` | Publishes `store`, `derived`, `dataLayer`, `mutationBatchSchedule`. |
121
+ | `<bd-product product-id="…" campaign="…" bundle>` | all the above + `product-id`, `campaign`, `bundle` | Reflects `product-id`, `campaign`, `bundle`. |
122
+ | `<bd-option devices="5" subscription="12" data-layer-event="info">` | all the above + `devices`, `subscription`, `data-layer-event` | Reflects `devices`, `subscription`. Fires `dataLayer` once on first load. |
123
+
124
+ All three extend `BdScopedElement` and pick up every scope-related property (`ignore-events`, `ignore-events-parent`, `eta-disabled`, `compute-disabled`, `topology-boundary`).
125
+
126
+ ### Scope-related properties
127
+
128
+ - `ignore-events="a, b, c"` — drop transitions whose `eventId` matches any of the comma-separated ids. Applies to this node **and** every descendant (the cascade is gated here).
129
+ - `ignore-events-parent` (boolean) — drop cascades from the parent scope; only react to local DOM events. `bd-context` enables this by default.
130
+ - `eta-disabled` / `compute-disabled` (boolean) — kill Eta rendering / compute for this subtree.
131
+ - `topology-boundary` (boolean) — on `BdNodeElement`, marks a topology boundary for the action registry.
132
+
133
+ ## Render attributes
134
+
135
+ Add `data-store-render` to any element you want re-rendered on scope changes. The matching handler is selected by the data attribute name.
136
+
137
+ | Attribute | Tokens / values | Source |
138
+ | --- | --- | --- |
139
+ | `data-store-devices` | — | current `option.getDevices()` |
140
+ | `data-store-subscription` | — | current `option.getSubscription()` |
141
+ | `data-store-subscription-type` | `months` \| `years` | affects text formatting only |
142
+ | `data-store-text-single` | label string | singular label |
143
+ | `data-store-text-many` | label string | plural label |
144
+ | `data-store-price` | `full`, `discounted`, `full-monthly`, `discounted-monthly` (supports `||` fallbacks) | current option |
145
+ | `data-store-discount` | `value`, `percentage`, `value-monthly`, `percentage-monthly` (supports `||` fallbacks) | current option |
146
+ | `data-store-context-price` | `min-full`, `max-full`, `min-discounted`, `max-discounted`, `min-full-monthly`, `max-full-monthly`, `min-discounted-monthly`, `max-discounted-monthly` | compute result |
147
+ | `data-store-context-discount` | `min-value`, `max-value`, `min-percentage`, `max-percentage`, `min-value-monthly`, `max-value-monthly`, `min-percentage-monthly`, `max-percentage-monthly` | compute result |
148
+ | `data-store-buy-link` | (optional trial duration, e.g. `"30 days"`) | buy URL, or trial URL when a duration is supplied |
149
+ | `data-store-hide` | boolean expression in the DSL | hide the element when truthy |
150
+ | `data-store-hide-type` | `display` (default) \| `opacity` \| `visibility` | how to hide |
151
+
152
+ > `data-store-trial-link` is gone — use `data-store-buy-link="30 days"` to pick a trial URL by duration. Falls back to the regular buy URL when no trial is configured.
153
+
154
+ ## Hide DSL
155
+
156
+ Boolean expression compiled once per change, evaluated against the unified context:
157
+
158
+ ```html
159
+ <div data-store-render data-store-hide="!it.option.price.discounted">…</div>
160
+ <div data-store-render data-store-hide="it.product.campaign === 'test'">…</div>
161
+ <div data-store-render
162
+ data-store-hide="it.state.discount.percentage.min && !it.mails(10)">
163
+
164
+ </div>
165
+ ```
166
+
167
+ The expression runs against the same context the Eta templates see (see `DSL context reference` below). Numeric comparisons on prices are not portable — they're formatted strings and vary by currency. Devices and subscription remain numeric.
168
+
169
+ ## Eta templates
170
+
171
+ The Eta context variable is `it` (Eta default). The available shape is identical to the hide DSL:
172
+
173
+ - `it.option.*` (only inside `<bd-option>`)
174
+ - `it.product.*` (only inside `<bd-product>`)
175
+ - `it.state.*` / `it.ctx.*` (everywhere — `ctx` is an alias of `state`)
176
+ - `it.derived.*` (whatever your `derived` factory returned — see below)
177
+ - `it.store` (the raw `@repobit/dex-store` `Store`)
178
+
179
+ Templates can appear in text content (`<p>…{{= it.option.price.discounted }}…</p>`) or implicitly in any attribute whose value contains `{{`.
180
+
181
+ ## Derived variables / functions
182
+
183
+ ```ts
184
+ context.derived = async ({ store, product, option }) => ({
185
+ mails: (p) => ((option?.getDevices?.() ?? 0) / p) * 100,
186
+ // Nested overrides are merged into their slot:
187
+ option: { someVar: 'hello' }
188
+ });
189
+ ```
190
+
191
+ Read from templates as `{{= it.derived.mails(10) }}`. Read from the hide DSL as `it.derived.mails(10)`. The factory is async, called whenever the scope inputs change.
192
+
193
+ `derived` lives in the **nested** `it.derived` slot (not merged into root) — this keeps store-provided fields distinct from user-provided ones. v1's root-merge behaviour is gone.
194
+
195
+ ## DSL context reference
196
+
197
+ The shape exposed to both Eta templates and the hide DSL:
198
+
199
+ ```ts
200
+ it = {
201
+ store, // raw Store | undefined
202
+ product: { // TemplateProductContext | undefined
203
+ id : string,
204
+ campaign: string | undefined,
205
+ name : string | undefined
206
+ },
207
+ option: { // TemplateOptionContext | undefined
208
+ price: {
209
+ full : string,
210
+ discounted : string | undefined,
211
+ fullMonthly : string,
212
+ discountedMonthly: string | undefined
213
+ },
214
+ discount: {
215
+ percentage : string,
216
+ percentageMonthly: string,
217
+ value : string,
218
+ valueMonthly : string
219
+ },
220
+ links: {
221
+ buy : string | undefined,
222
+ trial: (trialDuration: string) => string | undefined
223
+ },
224
+ devices : number,
225
+ subscription: number
226
+ },
227
+ state: { // TemplateStateContext | undefined
228
+ price: {
229
+ full: {
230
+ min: string, max: string,
231
+ monthly: { min: string, max: string }
232
+ },
233
+ discounted: {
234
+ min: string, max: string,
235
+ monthly: { min: string, max: string }
236
+ }
237
+ },
238
+ discount: {
239
+ percentage: {
240
+ min: string, max: string,
241
+ monthly: { min: string, max: string }
242
+ },
243
+ value: {
244
+ min: string, max: string,
245
+ monthly: { min: string, max: string }
246
+ }
247
+ },
248
+ scenarios: number
249
+ },
250
+ ctx, // alias of `state`
251
+ derived: Record<string, unknown> // from `bd-context.derived`
252
+ }
253
+ ```
254
+
255
+ Notes
256
+ - All price / discount values in the DSL are **formatted strings** (currency-aware). Don't perform numeric comparisons on them.
257
+ - `it.ctx` is an alias of `it.state` for convenience.
258
+ - `it.option.links.trial('30 days')` returns the trial URL for that duration, or `undefined` if no trial is configured.
259
+ - `it.derived.*` is the user-defined overlay from `bd-context.derived`.
260
+
261
+ ## Data layer
262
+
263
+ ```ts
264
+ context.dataLayer = ({ option, event }) => {
265
+ window.dataLayer?.push({
266
+ event,
267
+ productId: option.getProduct().getId(),
268
+ devices : option.getDevices(),
269
+ // …
270
+ });
271
+ };
272
+ ```
273
+
274
+ - Fires once per `<bd-option>` instance (first successful load). Re-firing requires re-mounting the node.
275
+ - `event` is the value of `data-layer-event` on the option element. Canonical values: `all`, `info`, `comparison`. Custom strings are accepted.
276
+
277
+ ## Actions
278
+
279
+ ```html
280
+ <button data-store-action
281
+ data-store-set-devices="25"
282
+ data-store-event-id="devicesBtn">25 devices</button>
283
+
284
+ <button data-store-action
285
+ data-store-set-type="devices"
286
+ data-store-set-delta="next"
287
+ data-store-set-min="1"
288
+ data-store-set-max="25">next</button>
88
289
  ```
89
290
 
90
- **Store Config**
91
- - `trialLinks` and `overrides` come from `@repobit/dex-store` and work transparently with these elements. You pass them when creating the `Store` and render trial links via `data-store-buy-link="30 days"` (trial duration as the attribute value) or rely on overridden option fields.
291
+ | Attribute | Effect |
292
+ | --- | --- |
293
+ | `data-store-action` | Marks the element as an action source. |
294
+ | `data-store-set-devices="N"` | Set devices = N. |
295
+ | `data-store-set-subscription="N"` | Set subscription = N. |
296
+ | `data-store-set-id="…"` | Switch the current product id. |
297
+ | `data-store-set-campaign="…"` | Switch the campaign for the current product. |
298
+ | `data-store-set-bundle` (boolean) | Toggle bundle inclusion. |
299
+ | `data-store-set-type="devices\|subscription"` | Required for delta updates. |
300
+ | `data-store-set-delta="next\|prev\|<number>"` | Move the option by N (or `next` / `prev`). |
301
+ | `data-store-set-min`, `data-store-set-max` | Bounds for delta updates. |
302
+ | `data-store-set-use-as-value` (boolean) | Treat `delta` as an absolute value rather than an increment. |
303
+ | `data-store-event-id="…"` | Tag the source element so `ignore-events` on `bd-*` nodes can drop its events. |
304
+
305
+ For `<input type="number|text">` and `<select>`, the adapter reads the current input value and dispatches a delta update. For `<input type="checkbox|radio">` it fires on click when `checked === true`.
306
+
307
+ ### Ignoring events on a subtree
308
+
309
+ ```html
310
+ <bd-option ignore-events="devicesBtn, subscriptionBtn">
311
+ <!-- ignores events from any source tagged with one of those ids -->
312
+ </bd-option>
313
+ ```
314
+
315
+ `ignore-events` cascades — descendants inherit it. `data-store-event-id` is the matching tag on the source element. (`data-store-id` from v1 is gone.)
316
+
317
+ ## Registration and initialization
92
318
 
93
- Example advanced config when constructing the store:
319
+ | Export | Purpose |
320
+ | --- | --- |
321
+ | `registerContextNodes()` | Register `<bd-context>`, `<bd-product>`, `<bd-option>` custom elements. Idempotent. |
322
+ | `registerActionNodes(root)` | Attach `BdActionElementAdapter`s to every action element under `root`. Returns `() => void` to disconnect. |
323
+ | `registerRenderNodes(root)` | Wire every `[data-store-render]` element under `root` to its attribute handler. Returns `() => void` to disconnect. |
324
+ | `elementDefinitions` | `{ 'bd-context': ContextNode, 'bd-product': ProductNode, 'bd-option': OptionNode }` — for advanced consumers that want to register tags themselves. |
325
+
326
+ The package is side-effect free; nothing is registered until you call `registerContextNodes()`.
327
+
328
+ ## Store config
329
+
330
+ `trialLinks` and `overrides` come from `@repobit/dex-store` and work transparently with these elements. Configure them on the `Store` and render via attributes:
94
331
 
95
332
  ```ts
96
333
  import { Store } from '@repobit/dex-store';
@@ -99,36 +336,26 @@ const store = new Store({
99
336
  locale : 'en-us',
100
337
  provider: { name: 'vlaicu' },
101
338
 
102
- // Map productId -> campaign -> optionVariation -> trial URL
103
- // productId is the final id after adaptor mapping (e.g. 'com.bitdefender.tsmd.v2').
104
- // optionVariation key format: '<devices>-<subscription>' (e.g. '5-12').
339
+ // productAlias -> campaign -> optionVariation -> trialDays -> URL
340
+ // productAlias is the id after adaptor mapping.
341
+ // optionVariation key: '<devices>-<subscription>' (e.g. '5-12').
105
342
  trialLinks: {
106
343
  'com.bitdefender.tsmd.v2': {
107
344
  default: {
108
- '5-12' : { '30 days': 'https://trial.example.com/default/5-12' },
109
- '10-12': { '30 days': 'https://trial.example.com/default/10-12' }
110
- },
111
- PromoX: {
112
- '5-12' : { '30 days': 'https://trial.example.com/promox/5-12' },
113
- '10-12': { '30 days': 'https://trial.example.com/promox/10-12' }
345
+ '5-12': { '30 days': 'https://trial.example.com/default/5-12' }
114
346
  }
115
347
  }
116
348
  },
117
349
 
118
- // Per-product overrides for campaign and/or options
119
- // - Set/redirect campaign via `default.campaign` or `[campaign].campaign`
120
- // - Merge option fields per variation; use `null` to remove an option
350
+ // Per-product overrides
121
351
  overrides: {
122
352
  'com.bitdefender.tsmd.v2': {
123
- // Applies when no explicit campaign is requested
124
- default: {
125
- campaign: 'OvDefault'
126
- },
353
+ default: { campaign: 'OvDefault' },
127
354
  PromoX: {
128
355
  campaign: 'PromoX',
129
356
  options : {
130
- '5-12' : { discountedPrice: 49.99, buyLink: 'https://example.com/override/buy' },
131
- '10-12': null // delete this variation
357
+ '5-12' : { discountedPrice: 49.99 },
358
+ '10-12': null // delete this variation
132
359
  }
133
360
  }
134
361
  }
@@ -136,242 +363,40 @@ const store = new Store({
136
363
  });
137
364
  ```
138
365
 
139
- Notes
140
- - `overrides.options` merges into each option; you can update `buyLink`, `discountedPrice`, etc., or delete an entire variation with `null`.
141
- - Keys are resolved against the product alias returned by the provider. If you don't use id mappings, it's the id you pass in `<bd-product product-id="...">`.
142
-
143
- ## Rendering model
144
- - Add `data-store-render` to any element you want updated by the pipeline.
145
- - A single binder subscribes to option, product and aggregated state contexts and renders attributes + Eta templates.
146
- - Scoping is natural: a node sees the nearest provider up the tree (e.g., `it.option.*` is only available inside `<bd-option>`).
147
-
148
- ## Supported attributes
149
-
150
- - `data-store-devices`
151
- - Renders option devices to text nodes, `<input>` value, or `<select>` options (adds `data-store-set-devices` on each option)
152
- - Optional label helpers: `data-store-text-single="device"`, `data-store-text-many="devices"`
153
-
154
- - `data-store-subscription`
155
- - Renders option subscription similarly; add `data-store-subscription-type="years|months"`
156
- - Label helpers: `data-store-text-single`, `data-store-text-many`
157
-
158
- - `data-store-price`
159
- - Allowed tokens: `full`, `discounted`, `full-monthly`, `discounted-monthly`
160
- - Supports OR semantics via `||` to choose the first available variant:
161
- - `data-store-price="discounted || full"`
162
-
163
- - `data-store-discount`
164
- - Allowed tokens: `value`, `percentage`, `value-monthly`, `percentage-monthly`
165
- - Supports `||` fallbacks
166
-
167
- - Aggregated state (min/max across options):
168
- - `data-store-context-price` tokens: `min-full`, `max-full`, `min-full-monthly`, `max-full-monthly`, `min-discounted`, `max-discounted`, `min-discounted-monthly`, `max-discounted-monthly`
169
- - `data-store-context-discount` tokens: `min-value`, `max-value`, `min-value-monthly`, `max-value-monthly`, `min-percentage`, `max-percentage`, `min-percentage-monthly`, `max-percentage-monthly`
170
-
171
- - Links
172
- - `data-store-buy-link` sets anchor `href` and useful `data-*` attributes. Accepts an optional trial-duration value (e.g. `data-store-buy-link="30 days"`): when provided, the trial link for that duration is used if one is configured for the current variation, otherwise falls back to the regular buy link.
173
-
174
- - Hide DSL
175
- - `data-store-hide="<boolean expression>"` with an optional `data-store-hide-type="display|opacity|visibility"`
176
- - Expression is compiled and evaluated against the unified context:
177
- - `it.option.*` current option data. Price- and discount-related fields are formatted strings (currency-aware). Do not rely on numeric math for prices; they vary by currency. Devices/subscription remain numeric.
178
- - `it.product.*` id/campaign/name
179
- - `it.state.*` aggregated min/max data (also available under `it.ctx`)
180
- - any keys returned from your `root.derived`
181
- - Examples:
182
- - `data-store-hide="!it.option.price.discounted"` (hide when no discounted price)
183
- - `data-store-hide="it.product.campaign === 'test'"`
184
-
185
- ## Eta templates
186
- - Text/HTML: any element that is not a provider and doesn’t contain nested providers is treated as a whole-template; `innerHTML` is compiled once and morphed via nanomorph. This preserves existing DOM event listeners and state.
187
- - Attributes:
188
- - Implicit: any attribute whose value contains `{{` is rendered via Eta
189
- - The Eta context variable is `it` (Eta default). It contains:
190
- - `it.option.*` (inside `<bd-option>`)
191
- - `it.product.*` (inside `<bd-product>`)
192
- - `it.state.*` and `it.ctx.*` (inside any provider subtree)
193
- - your derived overlay merged at top-level (see below)
194
-
195
- ## Derived variables/functions
196
- - Provide a function at the root: `root.derived = async ({ option, product, state }) => ({ ... })`
197
- - The returned object is merged into the Eta/DSL context:
198
- - Example: `({ mails: (p) => (option?.getDevices?.()/p)*100, option: { someVar: state.discount.value.min } })`
199
- - Use it in Eta: `{{= it.mails(10) }}` or `{{= it.option.someVar }}`
200
- - Use it in hide: `data-store-hide="it.mails(10) >= 50"`
201
-
202
- ## DSL context reference
203
- The DSL and Eta contexts use Eta’s default variable name `it`. The following keys are available:
204
-
205
- - it.option
206
- - price
207
- - full: formatted full price (string)
208
- - discounted: formatted discounted price (string)
209
- - fullMonthly: formatted monthly full price (string)
210
- - discountedMonthly: formatted monthly discounted price (string)
211
- - discount
212
- - value: formatted discount amount (string)
213
- - percentage: formatted percentage discount with symbol (string)
214
- - valueMonthly: formatted monthly discount amount (string)
215
- - percentageMonthly: formatted monthly percentage with symbol (string)
216
- - links
217
- - buy: buy URL (string)
218
- - trial: trial URL if available (string)
219
- - devices: number
220
- - subscription: number
221
-
222
- - it.product
223
- - id: string
224
- - campaign: string
225
- - name: string
226
-
227
- - it.state (also available as `it.ctx`)
228
- - price
229
- - full
230
- - min: formatted string
231
- - max: formatted string
232
- - monthly
233
- - min: formatted string
234
- - max: formatted string
235
- - discounted
236
- - min: formatted string
237
- - max: formatted string
238
- - monthly
239
- - min: formatted string
240
- - max: formatted string
241
- - discount
242
- - percentage
243
- - min: formatted string
244
- - max: formatted string
245
- - monthly
246
- - min: formatted string
247
- - max: formatted string
248
- - value
249
- - min: formatted string
250
- - max: formatted string
251
- - monthly
252
- - min: formatted string
253
- - max: formatted string
254
-
255
- Notes
256
- - All price/discount values in the DSL are formatted strings (currency-aware). Do not perform numeric comparisons on them. Prefer truthiness checks (e.g., `!it.option.price.discounted`).
257
- - `it.ctx` is an alias of `it.state` for convenience.
258
-
259
- ## Data Layer
260
- - Provide a function on `<bd-root>`: `root.dataLayer = ({ option, event }) => { ... }`.
261
- - Called once per `<bd-option>` instance that declares `data-layer-event` (on first successful load).
262
- - Safe if attached after the option loads; it still fires once when available.
263
- - Intended for analytics (e.g., pushing to `window.dataLayer`).
264
- - Event name is set on each `<bd-option>` with `data-layer-event`.
265
- - Canonical values: `all`, `info`, `comparison`. Any custom string is also accepted.
266
- - Payload shape passed to your callback:
267
- - `event`: string event name.
268
- - `option`: `ProductOption` from `@repobit/dex-store` with getters like `getProduct()`, `getVariation()`, `getDevices()`, `getSubscription()`, `getBuyLink()`, `getDiscountedPrice()`.
269
-
270
- Example:
366
+ Render via attributes:
271
367
 
272
368
  ```html
273
- <script type="module">
274
- import { registerContextNodes, registerActionNodes } from '@repobit/dex-store-elements';
275
- import { Store } from '@repobit/dex-store';
276
-
277
- window.addEventListener('DOMContentLoaded', async () => {
278
- registerContextNodes();
279
- const root = document.querySelector('bd-root');
280
- root.store = new Store({ locale: 'en-us', provider: { name: 'vlaicu' } });
281
-
282
- root.dataLayer = ({ option, event }) => {
283
- window.dataLayer = window.dataLayer || [];
284
- window.dataLayer.push({
285
- event,
286
- productId: option.getProduct().getId(),
287
- campaign : option.getProduct().getCampaign(),
288
- variation: option.getVariation(),
289
- devices : option.getDevices(),
290
- subscription: option.getSubscription()
291
- });
292
- };
293
-
294
- const disposeActions = registerActionNodes(root);
295
-
296
- // Optional teardown (for SPA route changes/unmounts)
297
- // disposeActions();
298
- });
299
- </script>
300
-
301
- <bd-root store-name="root">
302
- <bd-product store-name="product" product-id="com.bitdefender.tsmd.v2">
303
- <bd-option devices="5" subscription="12" data-layer-event="info">
304
- <!-- your UI here -->
305
- </bd-option>
306
- </bd-product>
307
- </bd-root>
369
+ <a data-store-render data-store-buy-link>Buy</a>
370
+ <a data-store-render data-store-buy-link="30 days">30-day trial</a>
308
371
  ```
309
372
 
310
- Behavior notes
311
- - Fires only once per `<bd-option>` instance (first load). Subsequent changes via actions or deltas do not trigger the callback again.
312
- - Set different `data-layer-event` values on different options if you need multiple distinct analytics events.
373
+ ## Caveats
313
374
 
314
- ## Actions
315
- - Add `data-store-action` to elements to emit store events.
316
- - Set absolute values: `data-store-set-devices`, `data-store-set-subscription`, `data-store-set-id`, `data-store-set-campaign`
317
- - Update by delta/sequence: `data-store-set-type="devices|subscription"`, `data-store-set-delta="next|prev|<number>"`
318
- - Source identifier: add `data-store-id="someId"` to tag the event's `storeId`. Providers with `ignore-events` that include `someId` will drop these events.
319
- - Initialize once per mount with:
320
- - `import { registerActionNodes } from '@repobit/dex-store-elements'`
321
- - `const disposeActions = registerActionNodes(root)`
322
- - Call `disposeActions()` on teardown/unmount to disconnect observers.
375
+ - Scoping: `it.option.*` is only available inside `<bd-option>`. `it.product.*` is only available inside `<bd-product>`. Everything else is available everywhere.
376
+ - Nested providers: an inner `bd-context` / `bd-product` / `bd-option` renders its own subtree; templates inside it never see the outer scope's `option` (only the inner one).
377
+ - Eta templates are scoped to non-provider elements — provider elements don't render text content as templates.
378
+ - Compute (state min/max) only runs when at least one `bd-context` / `bd-product` / `bd-option` enables it (default: enabled). Disable with `compute-disabled` on a provider to skip the reduction for preview widgets.
323
379
 
324
- ## Registration and initialization
325
- - Element registration:
326
- - `import { registerContextNodes } from '@repobit/dex-store-elements'; registerContextNodes();`
327
- - Rendering: `import { registerRenderNodes } from '@repobit/dex-store-elements'`
328
- - Actions: `import { registerActionNodes } from '@repobit/dex-store-elements'`
329
- - `registerRenderNodes(root)` returns a disposer: `() => void`
330
- - `registerActionNodes(root)` returns a disposer: `() => void`
331
- - Call disposers during teardown/unmount in long-lived apps (SPA route changes, dynamic mounts).
332
-
333
- Note: The package is side-effect free; elements are registered only when `registerContextNodes()` is called.
334
-
335
- ## State & event controls
336
- These attributes are available on `<bd-state>` and any element that extends it (`<bd-root>`, `<bd-product>`, `<bd-option>`).
337
- - `ignore-events="store-a, store-b"`
338
- - Comma-separated list of action ids. Events whose source element has a matching `data-store-id` are ignored by this node (and its subtree). Works for both action and delta events.
339
- - Coupling with `data-store-id` (on action elements):
340
- - Example:
341
- - `<button data-store-action data-store-id="devicesBtn" data-store-set-devices="25">` dispatches an event with `storeId: "devicesBtn"`.
342
- - `<bd-option ignore-events="devicesBtn">` ignores that event while still accepting events from other buttons.
343
- - You can list multiple ids: `ignore-events="devicesBtn, subscriptionBtn"`.
344
- - `ignore-events-parent`
345
- - Ignores events received from ancestor providers and only reacts to DOM-bubbled events that originate within the current subtree.
346
- - `<bd-context>` is a convenience element that defaults this behavior to enabled. It’s equivalent to `<bd-state ignore-events-parent>`.
347
- - Example:
348
- ```html
349
- <bd-state store-name="outer">
350
- <!-- Global action updates state here -->
351
- <button data-store-action data-store-set-devices="25"></button>
352
-
353
- <!-- Isolated island: only inner actions are observed -->
354
- <bd-context store-name="island">
355
- <button data-store-action data-store-set-devices="5"></button>
356
- </bd-context>
357
- </bd-state>
358
- ```
359
- In this setup, the inner `<bd-context>` ignores the outer button’s events.
360
- - `no-collect`
361
- - Turns off automatic option collection for aggregated state. Set this when you want the node to work locally without contributing to shared min/max computations (e.g., for preview widgets).
380
+ ## TypeScript
362
381
 
363
- ## Caveats
364
- - Scoping: attribute Eta and hide can only see contexts provided by ancestors. For example, `it.option.*` is only available inside `<bd-option>`.
365
- - Nested providers: inner providers render their own subtrees; outer nodes can still render attributes safely even when they contain nested providers.
382
+ ```ts
383
+ import type {
384
+ ContextNode,
385
+ ProductNode,
386
+ OptionNode,
387
+ BdScope,
388
+ BdScopeDeriveFn
389
+ } from '@repobit/dex-store-elements';
390
+
391
+ import {
392
+ registerContextNodes,
393
+ registerActionNodes,
394
+ registerRenderNodes
395
+ } from '@repobit/dex-store-elements';
396
+ ```
366
397
 
367
- ## TypeScript
368
- - Custom element classes are exported from a dedicated entry for advanced use:
369
- - `import { RootNode, ProductNode, OptionNode, StateNode } from '@repobit/dex-store-elements'`
370
- - The derived signature:
371
- ```ts
372
- import type { derivedContextType } from '@repobit/dex-store-elements/src/contexts/context.derived';
373
- const derived: derivedContextType = async ({ option, product, state, store }) => ({ ... });
374
- ```
398
+ The custom element classes extend `BdScopedElement` and `BdNodeElement` (Lit `LitElement` subclasses). They are exported from the main entry.
375
399
 
376
400
  ## License
401
+
377
402
  ISC