@repobit/dex-store-elements 1.1.0
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/CHANGELOG.md +110 -0
- package/README.md +211 -0
- package/dist/src/actions/action.button.d.ts +1 -0
- package/dist/src/actions/action.button.js +14 -0
- package/dist/src/actions/action.button.js.map +1 -0
- package/dist/src/actions/action.input.d.ts +1 -0
- package/dist/src/actions/action.input.js +37 -0
- package/dist/src/actions/action.input.js.map +1 -0
- package/dist/src/actions/action.select.d.ts +1 -0
- package/dist/src/actions/action.select.js +12 -0
- package/dist/src/actions/action.select.js.map +1 -0
- package/dist/src/actions/index.d.ts +2 -0
- package/dist/src/actions/index.js +30 -0
- package/dist/src/actions/index.js.map +1 -0
- package/dist/src/actions/utilty.d.ts +9 -0
- package/dist/src/actions/utilty.js +88 -0
- package/dist/src/actions/utilty.js.map +1 -0
- package/dist/src/contexts/context.datalayer.d.ts +9 -0
- package/dist/src/contexts/context.datalayer.js +3 -0
- package/dist/src/contexts/context.datalayer.js.map +1 -0
- package/dist/src/contexts/context.derived.d.ts +12 -0
- package/dist/src/contexts/context.derived.js +3 -0
- package/dist/src/contexts/context.derived.js.map +1 -0
- package/dist/src/contexts/context.event.d.ts +5 -0
- package/dist/src/contexts/context.event.js +3 -0
- package/dist/src/contexts/context.event.js.map +1 -0
- package/dist/src/contexts/context.option.d.ts +5 -0
- package/dist/src/contexts/context.option.js +3 -0
- package/dist/src/contexts/context.option.js.map +1 -0
- package/dist/src/contexts/context.product.d.ts +5 -0
- package/dist/src/contexts/context.product.js +3 -0
- package/dist/src/contexts/context.product.js.map +1 -0
- package/dist/src/contexts/context.state.d.ts +28 -0
- package/dist/src/contexts/context.state.js +3 -0
- package/dist/src/contexts/context.state.js.map +1 -0
- package/dist/src/contexts/context.store.d.ts +5 -0
- package/dist/src/contexts/context.store.js +3 -0
- package/dist/src/contexts/context.store.js.map +1 -0
- package/dist/src/dsl/compilers/array/compiler.d.ts +1 -0
- package/dist/src/dsl/compilers/array/compiler.js +30 -0
- package/dist/src/dsl/compilers/array/compiler.js.map +1 -0
- package/dist/src/dsl/compilers/boolean/compiler.d.ts +7 -0
- package/dist/src/dsl/compilers/boolean/compiler.js +53 -0
- package/dist/src/dsl/compilers/boolean/compiler.js.map +1 -0
- package/dist/src/dsl/compilers/enum/compiler.d.ts +7 -0
- package/dist/src/dsl/compilers/enum/compiler.js +16 -0
- package/dist/src/dsl/compilers/enum/compiler.js.map +1 -0
- package/dist/src/dsl/compilers/index.d.ts +12 -0
- package/dist/src/dsl/compilers/index.js +9 -0
- package/dist/src/dsl/compilers/index.js.map +1 -0
- package/dist/src/dsl/utilty.d.ts +6 -0
- package/dist/src/dsl/utilty.js +32 -0
- package/dist/src/dsl/utilty.js.map +1 -0
- package/dist/src/events/events.d.ts +60 -0
- package/dist/src/events/events.js +56 -0
- package/dist/src/events/events.js.map +1 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/nodes/node.context.d.ts +3 -0
- package/dist/src/nodes/node.context.js +15 -0
- package/dist/src/nodes/node.context.js.map +1 -0
- package/dist/src/nodes/node.option.d.ts +32 -0
- package/dist/src/nodes/node.option.js +247 -0
- package/dist/src/nodes/node.option.js.map +1 -0
- package/dist/src/nodes/node.product.d.ts +14 -0
- package/dist/src/nodes/node.product.js +99 -0
- package/dist/src/nodes/node.product.js.map +1 -0
- package/dist/src/nodes/node.root.d.ts +16 -0
- package/dist/src/nodes/node.root.js +53 -0
- package/dist/src/nodes/node.root.js.map +1 -0
- package/dist/src/nodes/node.state.d.ts +112 -0
- package/dist/src/nodes/node.state.js +742 -0
- package/dist/src/nodes/node.state.js.map +1 -0
- package/dist/src/renders/attributes/buyLink.d.ts +2 -0
- package/dist/src/renders/attributes/buyLink.js +18 -0
- package/dist/src/renders/attributes/buyLink.js.map +1 -0
- package/dist/src/renders/attributes/devices.d.ts +2 -0
- package/dist/src/renders/attributes/devices.js +32 -0
- package/dist/src/renders/attributes/devices.js.map +1 -0
- package/dist/src/renders/attributes/discount.d.ts +2 -0
- package/dist/src/renders/attributes/discount.js +96 -0
- package/dist/src/renders/attributes/discount.js.map +1 -0
- package/dist/src/renders/attributes/hide.d.ts +2 -0
- package/dist/src/renders/attributes/hide.js +33 -0
- package/dist/src/renders/attributes/hide.js.map +1 -0
- package/dist/src/renders/attributes/index.d.ts +3 -0
- package/dist/src/renders/attributes/index.js +24 -0
- package/dist/src/renders/attributes/index.js.map +1 -0
- package/dist/src/renders/attributes/price.d.ts +2 -0
- package/dist/src/renders/attributes/price.js +96 -0
- package/dist/src/renders/attributes/price.js.map +1 -0
- package/dist/src/renders/attributes/subscription.d.ts +2 -0
- package/dist/src/renders/attributes/subscription.js +34 -0
- package/dist/src/renders/attributes/subscription.js.map +1 -0
- package/dist/src/renders/attributes/trialLink.d.ts +2 -0
- package/dist/src/renders/attributes/trialLink.js +13 -0
- package/dist/src/renders/attributes/trialLink.js.map +1 -0
- package/dist/src/renders/context.d.ts +11 -0
- package/dist/src/renders/context.js +88 -0
- package/dist/src/renders/context.js.map +1 -0
- package/dist/src/renders/format.d.ts +2 -0
- package/dist/src/renders/format.js +21 -0
- package/dist/src/renders/format.js.map +1 -0
- package/dist/src/renders/index.d.ts +2 -0
- package/dist/src/renders/index.js +30 -0
- package/dist/src/renders/index.js.map +1 -0
- package/dist/src/renders/observe.d.ts +4 -0
- package/dist/src/renders/observe.js +30 -0
- package/dist/src/renders/observe.js.map +1 -0
- package/dist/src/renders/utility.d.ts +28 -0
- package/dist/src/renders/utility.js +132 -0
- package/dist/src/renders/utility.js.map +1 -0
- package/dist/src/templating/eta.d.ts +3 -0
- package/dist/src/templating/eta.js +9 -0
- package/dist/src/templating/eta.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var StateNode_1;
|
|
8
|
+
import { derivedContext } from "../contexts/context.derived.js";
|
|
9
|
+
import { eventContext } from "../contexts/context.event.js";
|
|
10
|
+
import { stateContext } from "../contexts/context.state.js";
|
|
11
|
+
import { storeContext } from "../contexts/context.store.js";
|
|
12
|
+
import { ActionEvent, CollectActionEvent, CollectOptionEvent, CollectUpdateByDeltaEvent, UpdateByDeltaEvent } from "../events/events.js";
|
|
13
|
+
import { toDSLContext } from "../renders/context.js";
|
|
14
|
+
import eta from "../templating/eta.js";
|
|
15
|
+
import { consume, provide } from "@lit/context";
|
|
16
|
+
import { Task } from "@lit/task";
|
|
17
|
+
import { LitElement } from "lit";
|
|
18
|
+
import { customElement, property } from "lit/decorators.js";
|
|
19
|
+
import morph from 'nanomorph';
|
|
20
|
+
if (!window.Promise.withResolvers) {
|
|
21
|
+
window.Promise.withResolvers = function () {
|
|
22
|
+
let resolve, reject;
|
|
23
|
+
const promise = new Promise((res, rej) => {
|
|
24
|
+
resolve = res;
|
|
25
|
+
reject = rej;
|
|
26
|
+
});
|
|
27
|
+
return { resolve, reject, promise };
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
let StateNode = StateNode_1 = class StateNode extends LitElement {
|
|
31
|
+
constructor() {
|
|
32
|
+
super(...arguments);
|
|
33
|
+
this.autoForward = true;
|
|
34
|
+
this.noCollect = false;
|
|
35
|
+
this.storeName = Symbol("bd-state");
|
|
36
|
+
// If true, ignore events coming from parent context; only react to
|
|
37
|
+
// events originating inside this subtree (DOM-bubbled via listeners).
|
|
38
|
+
this.ignoreEventsParent = false;
|
|
39
|
+
/**
|
|
40
|
+
* all options as seens by this node
|
|
41
|
+
* options + partialOptions
|
|
42
|
+
*/
|
|
43
|
+
this._options = new Map();
|
|
44
|
+
/**
|
|
45
|
+
* all actions as seens by this node
|
|
46
|
+
*/
|
|
47
|
+
this._actions = new Map();
|
|
48
|
+
/**
|
|
49
|
+
* all delta updates as seens by this node
|
|
50
|
+
* changes that affect only devices and subscription
|
|
51
|
+
*/
|
|
52
|
+
this._deltaUpdates = new Map();
|
|
53
|
+
/**
|
|
54
|
+
* all product changes as seens by this node
|
|
55
|
+
*/
|
|
56
|
+
this._partialOptions = new Map();
|
|
57
|
+
/**
|
|
58
|
+
* all product changes as seens by this node
|
|
59
|
+
*/
|
|
60
|
+
this._partialBundleOptions = new Map();
|
|
61
|
+
/**
|
|
62
|
+
* Local state
|
|
63
|
+
*/
|
|
64
|
+
this.state = {
|
|
65
|
+
price: {
|
|
66
|
+
min: {
|
|
67
|
+
value: Number.MAX_SAFE_INTEGER,
|
|
68
|
+
fmt: ""
|
|
69
|
+
},
|
|
70
|
+
max: {
|
|
71
|
+
value: Number.MIN_SAFE_INTEGER,
|
|
72
|
+
fmt: ""
|
|
73
|
+
},
|
|
74
|
+
monthly: {
|
|
75
|
+
min: {
|
|
76
|
+
value: Number.MAX_SAFE_INTEGER,
|
|
77
|
+
fmt: ""
|
|
78
|
+
},
|
|
79
|
+
max: {
|
|
80
|
+
value: Number.MIN_SAFE_INTEGER,
|
|
81
|
+
fmt: ""
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
discountedPrice: {
|
|
86
|
+
min: {
|
|
87
|
+
value: Number.MAX_SAFE_INTEGER,
|
|
88
|
+
fmt: ""
|
|
89
|
+
},
|
|
90
|
+
max: {
|
|
91
|
+
value: Number.MIN_SAFE_INTEGER,
|
|
92
|
+
fmt: ""
|
|
93
|
+
},
|
|
94
|
+
monthly: {
|
|
95
|
+
min: {
|
|
96
|
+
value: Number.MAX_SAFE_INTEGER,
|
|
97
|
+
fmt: ""
|
|
98
|
+
},
|
|
99
|
+
max: {
|
|
100
|
+
value: Number.MIN_SAFE_INTEGER,
|
|
101
|
+
fmt: ""
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
discount: {
|
|
106
|
+
min: {
|
|
107
|
+
value: Number.MAX_SAFE_INTEGER,
|
|
108
|
+
fmt: ""
|
|
109
|
+
},
|
|
110
|
+
max: {
|
|
111
|
+
value: Number.MIN_SAFE_INTEGER,
|
|
112
|
+
fmt: ""
|
|
113
|
+
},
|
|
114
|
+
monthly: {
|
|
115
|
+
min: {
|
|
116
|
+
value: Number.MAX_SAFE_INTEGER,
|
|
117
|
+
fmt: ""
|
|
118
|
+
},
|
|
119
|
+
max: {
|
|
120
|
+
value: Number.MIN_SAFE_INTEGER,
|
|
121
|
+
fmt: ""
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
percentage: {
|
|
125
|
+
min: {
|
|
126
|
+
value: Number.MAX_SAFE_INTEGER,
|
|
127
|
+
fmt: ""
|
|
128
|
+
},
|
|
129
|
+
max: {
|
|
130
|
+
value: Number.MIN_SAFE_INTEGER,
|
|
131
|
+
fmt: ""
|
|
132
|
+
},
|
|
133
|
+
monthly: {
|
|
134
|
+
min: {
|
|
135
|
+
value: Number.MAX_SAFE_INTEGER,
|
|
136
|
+
fmt: ""
|
|
137
|
+
},
|
|
138
|
+
max: {
|
|
139
|
+
value: Number.MIN_SAFE_INTEGER,
|
|
140
|
+
fmt: ""
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
this._computeTask = new Task(this, {
|
|
147
|
+
task: async (_, { signal }) => {
|
|
148
|
+
const isActive = () => {
|
|
149
|
+
if (signal.aborted) {
|
|
150
|
+
throw new DOMException('Task aborted', `AbortError ${this.storeName.toString()}`);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
// runs whenever _store or version changes
|
|
154
|
+
try {
|
|
155
|
+
const computed = await this._computeState(isActive);
|
|
156
|
+
await this._computeContext(computed, isActive);
|
|
157
|
+
return computed ?? [];
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
159
|
+
}
|
|
160
|
+
catch (e) { /* empty */ }
|
|
161
|
+
},
|
|
162
|
+
args: () => [this.store]
|
|
163
|
+
});
|
|
164
|
+
// Default event forwarder for simple pass-through nodes (e.g., root/context)
|
|
165
|
+
this._defaultForwardEventTask = new Task(this, {
|
|
166
|
+
task: async ([evt, auto]) => {
|
|
167
|
+
if (!evt || !auto)
|
|
168
|
+
return;
|
|
169
|
+
this._forwardEvent(evt);
|
|
170
|
+
this._notifyParent();
|
|
171
|
+
},
|
|
172
|
+
args: () => [this._event, this.autoForward]
|
|
173
|
+
});
|
|
174
|
+
// Keep `_event` in sync with either DOM events or parent context,
|
|
175
|
+
// depending on `ignoreEventsParent`.
|
|
176
|
+
this._syncEventTask = new Task(this, {
|
|
177
|
+
task: async ([fromParent, fromDom, ignoreParent]) => {
|
|
178
|
+
// Track last-seen refs so we can choose the freshest source
|
|
179
|
+
const domChanged = fromDom !== this._prevDomEventRef;
|
|
180
|
+
const parentChanged = fromParent !== this._prevParentEventRef;
|
|
181
|
+
this._prevDomEventRef = fromDom;
|
|
182
|
+
this._prevParentEventRef = fromParent;
|
|
183
|
+
if (ignoreParent) {
|
|
184
|
+
this._event = fromDom ?? undefined;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (domChanged && !parentChanged) {
|
|
188
|
+
this._event = fromDom ?? undefined;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (parentChanged && !domChanged) {
|
|
192
|
+
this._event = fromParent ?? undefined;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (domChanged && parentChanged) {
|
|
196
|
+
// If both changed in the same microtask, prefer DOM-originated
|
|
197
|
+
this._event = fromDom ?? fromParent ?? undefined;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// Neither changed; keep current, but if nothing set yet, fall back
|
|
201
|
+
if (this._event === undefined) {
|
|
202
|
+
this._event = fromDom ?? fromParent ?? undefined;
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
args: () => [this._eventParent, this._eventDom, this.ignoreEventsParent]
|
|
206
|
+
});
|
|
207
|
+
this._collectToggleTask = new Task(this, {
|
|
208
|
+
task: async ([noCollect]) => {
|
|
209
|
+
const prev = this._prevCollect;
|
|
210
|
+
this._prevCollect = !noCollect; // track last 'collect' state for edge detection
|
|
211
|
+
if (prev === undefined) {
|
|
212
|
+
// Initial mount: register only if collecting (noCollect=false)
|
|
213
|
+
if (!noCollect)
|
|
214
|
+
this._notifyParent();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// prev represents prior collect state
|
|
218
|
+
const currentCollect = !noCollect;
|
|
219
|
+
if (currentCollect && !prev) {
|
|
220
|
+
this._notifyParent();
|
|
221
|
+
}
|
|
222
|
+
else if (!currentCollect && prev) {
|
|
223
|
+
this.dispatchEvent(new CollectOptionEvent({ name: this.storeName, options: null }));
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
args: () => [this.noCollect]
|
|
227
|
+
});
|
|
228
|
+
// Cache original templates per element so we can re-render from the original innerHTML
|
|
229
|
+
this._tplElementTemplates = new WeakMap();
|
|
230
|
+
this._tplAttrTemplates = new WeakMap();
|
|
231
|
+
this._etaRenderTask = new Task(this, {
|
|
232
|
+
task: async ([state, derived]) => {
|
|
233
|
+
if (!state)
|
|
234
|
+
return;
|
|
235
|
+
if (!this.shouldRunEtaStateRender())
|
|
236
|
+
return;
|
|
237
|
+
// Provide 'it' derived from DSL context
|
|
238
|
+
const it = await toDSLContext({ state, derived });
|
|
239
|
+
await this._renderEtaTemplates(it);
|
|
240
|
+
},
|
|
241
|
+
args: () => [this.state, this._derived]
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* all options computed by this node
|
|
246
|
+
* options * actions
|
|
247
|
+
*/
|
|
248
|
+
get computedOptions() {
|
|
249
|
+
return this._computeTask.value;
|
|
250
|
+
}
|
|
251
|
+
// Allow subclasses (e.g., OptionNode) to disable the base Eta render
|
|
252
|
+
// when they provide their own option-specific rendering pipeline.
|
|
253
|
+
shouldRunEtaStateRender() { return true; }
|
|
254
|
+
_isStateNodeElement(el) {
|
|
255
|
+
if (el instanceof StateNode_1)
|
|
256
|
+
return true;
|
|
257
|
+
const t = el.tagName;
|
|
258
|
+
return t === 'BD-STATE' || t === 'BD-PRODUCT' || t === 'BD-OPTION' || t === 'BD-CONTEXT';
|
|
259
|
+
}
|
|
260
|
+
_hasNestedStateNode(el) {
|
|
261
|
+
return !!el.querySelector?.('bd-state,bd-product,bd-option,bd-context');
|
|
262
|
+
}
|
|
263
|
+
_safeEtaRender(entry, data, { onErrorReturnInput = false } = {}) {
|
|
264
|
+
try {
|
|
265
|
+
if (!entry.fn) {
|
|
266
|
+
entry.fn = eta.compile(entry.src);
|
|
267
|
+
}
|
|
268
|
+
const out = eta.render(entry.fn, data);
|
|
269
|
+
return typeof out === 'string' ? out : String(out ?? '');
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
console.error('Eta render error:', err);
|
|
273
|
+
return onErrorReturnInput ? entry.src : '';
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async _renderEtaAttributes(el, data) {
|
|
277
|
+
// any attribute whose value contains '{{' is treated as an Eta template
|
|
278
|
+
const names = el.getAttributeNames();
|
|
279
|
+
let cache = this._tplAttrTemplates.get(el);
|
|
280
|
+
if (!cache) {
|
|
281
|
+
cache = new Map();
|
|
282
|
+
this._tplAttrTemplates.set(el, cache);
|
|
283
|
+
}
|
|
284
|
+
// Implicit any-attribute templates (heuristic: contains '{{')
|
|
285
|
+
for (const a of names) {
|
|
286
|
+
const raw = el.getAttribute(a);
|
|
287
|
+
if (!raw || !raw.includes('{{'))
|
|
288
|
+
continue;
|
|
289
|
+
const key = `imp:${a}`;
|
|
290
|
+
let entry = cache.get(key);
|
|
291
|
+
if (!entry || entry.src !== raw) {
|
|
292
|
+
entry = { src: raw };
|
|
293
|
+
cache.set(key, entry);
|
|
294
|
+
}
|
|
295
|
+
const rendered = this._safeEtaRender(entry, data, { onErrorReturnInput: true });
|
|
296
|
+
if (rendered !== raw) {
|
|
297
|
+
try {
|
|
298
|
+
el.setAttribute(a, rendered);
|
|
299
|
+
}
|
|
300
|
+
catch { /* ignore */ }
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
_hasRenderNodes(el) {
|
|
305
|
+
return !!el.querySelector?.('[data-store-render]');
|
|
306
|
+
}
|
|
307
|
+
async _morphElementFromHTML(el, html) {
|
|
308
|
+
try {
|
|
309
|
+
const wrapper = el.cloneNode(false);
|
|
310
|
+
wrapper.innerHTML = html;
|
|
311
|
+
morph(el, wrapper);
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
el.innerHTML = html;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async _renderEtaTemplates(context) {
|
|
318
|
+
// Traverse descendants; for any element that does NOT contain a nested state node,
|
|
319
|
+
// treat its innerHTML as a single Eta template and render/morph the entire subtree.
|
|
320
|
+
const visit = async (root) => {
|
|
321
|
+
for (const child of Array.from(root.children)) {
|
|
322
|
+
if (!(child instanceof HTMLElement))
|
|
323
|
+
continue;
|
|
324
|
+
// Always allow attribute-level Eta on any node in this subtree
|
|
325
|
+
await this._renderEtaAttributes(child, context);
|
|
326
|
+
if (this._isStateNodeElement(child) && child !== this) {
|
|
327
|
+
continue; // nested provider; let it handle its subtree
|
|
328
|
+
}
|
|
329
|
+
if (this._hasNestedStateNode(child)) {
|
|
330
|
+
// drill down until we reach leaves without nested state nodes
|
|
331
|
+
await visit(child);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
// If subtree contains render nodes, recurse into it but don't morph at this level
|
|
335
|
+
if (this._hasRenderNodes(child)) {
|
|
336
|
+
await visit(child);
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
// Skip elements managed by the render pipeline
|
|
340
|
+
if (child.hasAttribute('data-store-render')) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
let entry = this._tplElementTemplates.get(child);
|
|
344
|
+
if (!entry) {
|
|
345
|
+
const src = child.innerHTML ?? '';
|
|
346
|
+
entry = { src };
|
|
347
|
+
this._tplElementTemplates.set(child, entry);
|
|
348
|
+
}
|
|
349
|
+
const out = this._safeEtaRender(entry, context, { onErrorReturnInput: true });
|
|
350
|
+
await this._morphElementFromHTML(child, out);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
await visit(this);
|
|
354
|
+
}
|
|
355
|
+
connectedCallback() {
|
|
356
|
+
super.connectedCallback();
|
|
357
|
+
this.addEventListener(CollectActionEvent.eventName, this._collectActionEvent);
|
|
358
|
+
this.addEventListener(CollectUpdateByDeltaEvent.eventName, this._collectUpdateByDeltaEvent);
|
|
359
|
+
this.addEventListener(CollectOptionEvent.eventName, this._collectOptionEvent);
|
|
360
|
+
[ActionEvent, UpdateByDeltaEvent].forEach(e => this.addEventListener(e.eventName, this._eventChange));
|
|
361
|
+
}
|
|
362
|
+
remove() {
|
|
363
|
+
this.dispatchEvent(new CollectOptionEvent({ name: this.storeName, options: null }));
|
|
364
|
+
super.remove();
|
|
365
|
+
}
|
|
366
|
+
disconnectedCallback() {
|
|
367
|
+
this.removeEventListener(CollectActionEvent.eventName, this._collectActionEvent);
|
|
368
|
+
this.removeEventListener(CollectUpdateByDeltaEvent.eventName, this._collectUpdateByDeltaEvent);
|
|
369
|
+
this.removeEventListener(CollectOptionEvent.eventName, this._collectOptionEvent);
|
|
370
|
+
[ActionEvent, UpdateByDeltaEvent].forEach(e => this.removeEventListener(e.eventName, this._eventChange));
|
|
371
|
+
this._options.clear();
|
|
372
|
+
this._actions.clear();
|
|
373
|
+
this._partialOptions.clear();
|
|
374
|
+
super.disconnectedCallback();
|
|
375
|
+
}
|
|
376
|
+
_eventChange(e) {
|
|
377
|
+
e.stopPropagation();
|
|
378
|
+
// ignore if source matches ignore list
|
|
379
|
+
if (this._isIgnoredSource(e)) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
this._eventDom = e;
|
|
383
|
+
}
|
|
384
|
+
_forwardEvent(e = this._event) {
|
|
385
|
+
this._fEvent = e;
|
|
386
|
+
}
|
|
387
|
+
_collectOptionEvent(e) {
|
|
388
|
+
if (e.target === this) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
e.stopPropagation();
|
|
392
|
+
this.collectOption(e.detail);
|
|
393
|
+
}
|
|
394
|
+
collectOption({ name, options }) {
|
|
395
|
+
//node has disconected
|
|
396
|
+
if (options === null) {
|
|
397
|
+
this._options.delete(name);
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
this._options.set(name, options);
|
|
401
|
+
}
|
|
402
|
+
this._computeTask.abort();
|
|
403
|
+
this._computeTask.run();
|
|
404
|
+
this._notifyParent();
|
|
405
|
+
}
|
|
406
|
+
_collectActionEvent(e) {
|
|
407
|
+
if (e.target === this) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
e.stopPropagation();
|
|
411
|
+
const { name, action } = e.detail;
|
|
412
|
+
//node has disconected
|
|
413
|
+
if (action === null) {
|
|
414
|
+
this._actions.delete(name);
|
|
415
|
+
this._partialOptions.delete(name);
|
|
416
|
+
this._partialBundleOptions.delete(name);
|
|
417
|
+
}
|
|
418
|
+
else if (!action.id && (action.devices || action.subscription)) {
|
|
419
|
+
this._actions.set(name, action);
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
if (action.bundle) {
|
|
423
|
+
this._partialBundleOptions.set(name, action);
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
this._partialOptions.set(name, action);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
this._computeTask.abort();
|
|
430
|
+
this._computeTask.run();
|
|
431
|
+
this._notifyParent();
|
|
432
|
+
}
|
|
433
|
+
_collectUpdateByDeltaEvent(e) {
|
|
434
|
+
if (e.target === this) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const { name, update } = e.detail;
|
|
438
|
+
//node has disconected
|
|
439
|
+
if (update === null) {
|
|
440
|
+
this._deltaUpdates.delete(name);
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
this._deltaUpdates.set(name, update);
|
|
444
|
+
}
|
|
445
|
+
this._computeTask.abort();
|
|
446
|
+
this._computeTask.run();
|
|
447
|
+
this._notifyParent();
|
|
448
|
+
}
|
|
449
|
+
async _computeState(isActive) {
|
|
450
|
+
const computed = new Set();
|
|
451
|
+
// 1) collect base options + partialOptions
|
|
452
|
+
for (const optsPromise of this._options.values()) {
|
|
453
|
+
isActive();
|
|
454
|
+
const opts = await optsPromise;
|
|
455
|
+
for (const opt of opts) {
|
|
456
|
+
isActive();
|
|
457
|
+
computed.add(opt);
|
|
458
|
+
await this._applyPartials(opt, computed, isActive);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
// 2) apply full “actions”
|
|
462
|
+
for (const opt of [...computed]) {
|
|
463
|
+
isActive();
|
|
464
|
+
await this._applyActions(opt, computed, isActive);
|
|
465
|
+
}
|
|
466
|
+
// 3) apply full "delta updates"
|
|
467
|
+
for (const opt of [...computed]) {
|
|
468
|
+
isActive();
|
|
469
|
+
await this._applyDeltaUpdates(opt, computed, isActive);
|
|
470
|
+
}
|
|
471
|
+
// 3) combine bundles
|
|
472
|
+
for (const opt of [...computed]) {
|
|
473
|
+
isActive();
|
|
474
|
+
const bundles = [...this._partialBundleOptions.values()];
|
|
475
|
+
const combos = await this._applyBundles(opt, bundles);
|
|
476
|
+
combos.forEach(cb => cb && computed.add(cb));
|
|
477
|
+
}
|
|
478
|
+
return [...computed];
|
|
479
|
+
}
|
|
480
|
+
async _applyPartials(baseOpt, computed, isActive) {
|
|
481
|
+
for (const partial of this._partialOptions.values()) {
|
|
482
|
+
isActive();
|
|
483
|
+
if (!partial.id) {
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
const newProduct = await baseOpt.switchProduct({ id: partial.id, campaign: partial.campaign });
|
|
487
|
+
const newOpt = await newProduct?.getOption({ devices: partial.devices, subscription: partial.subscription });
|
|
488
|
+
if (newOpt)
|
|
489
|
+
computed.add(newOpt);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async _applyActions(baseOpt, computed, isActive) {
|
|
493
|
+
for (const action of this._actions.values()) {
|
|
494
|
+
isActive();
|
|
495
|
+
const newOpt = await baseOpt.getOption({
|
|
496
|
+
devices: action.devices ?? baseOpt.getDevices(),
|
|
497
|
+
subscription: action.subscription ?? baseOpt.getSubscription()
|
|
498
|
+
});
|
|
499
|
+
if (newOpt)
|
|
500
|
+
computed.add(newOpt);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
async _applyDeltaUpdates(baseOpt, computed, isActive) {
|
|
504
|
+
const product = baseOpt.getProduct();
|
|
505
|
+
const baseDevices = product.getDevices().values;
|
|
506
|
+
const baseSubscriptions = product.getSubscriptions().values;
|
|
507
|
+
let devices = baseOpt.getDevices();
|
|
508
|
+
let subscription = baseOpt.getSubscription();
|
|
509
|
+
//corner case for input types that define an interval by themselves
|
|
510
|
+
const getValue = (action, startValue, minValue) => {
|
|
511
|
+
if (action.useAsValue) {
|
|
512
|
+
action.delta = 1;
|
|
513
|
+
return Number(action.min) || minValue;
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
return startValue;
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
for (const action of this._deltaUpdates.values()) {
|
|
520
|
+
isActive();
|
|
521
|
+
// Loop until we can no longer apply the delta
|
|
522
|
+
while (true) {
|
|
523
|
+
isActive();
|
|
524
|
+
const values = action.type === "devices"
|
|
525
|
+
? baseDevices
|
|
526
|
+
: baseSubscriptions;
|
|
527
|
+
const current = action.type === "devices"
|
|
528
|
+
? getValue(action, devices, baseDevices[0])
|
|
529
|
+
: getValue(action, subscription, baseSubscriptions[0]);
|
|
530
|
+
const { newValue, done } = this._computeDelta(values, current, action);
|
|
531
|
+
if (done)
|
|
532
|
+
break;
|
|
533
|
+
// Update the appropriate variable
|
|
534
|
+
if (action.type === "devices") {
|
|
535
|
+
devices = newValue;
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
subscription = newValue;
|
|
539
|
+
}
|
|
540
|
+
// Fetch the new option and add it if exists
|
|
541
|
+
const newOpt = await baseOpt.getOption({ devices, subscription });
|
|
542
|
+
if (newOpt) {
|
|
543
|
+
computed.add(newOpt);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Attempts to apply the given delta action to the current value.
|
|
550
|
+
* Returns the updated value and whether we've exhausted variants.
|
|
551
|
+
*/
|
|
552
|
+
_computeDelta(values, current, action) {
|
|
553
|
+
const min = Number(action.min) || Number.MAX_SAFE_INTEGER;
|
|
554
|
+
const max = Number(action.max) || Number.MIN_SAFE_INTEGER;
|
|
555
|
+
// Handle "next" / "prev" stepping through a discrete list
|
|
556
|
+
if (action.delta === "next" || action.delta === "prev") {
|
|
557
|
+
const idx = values.findIndex(v => v === current);
|
|
558
|
+
const step = action.delta === "next" ? 1 : -1;
|
|
559
|
+
const candidate = values.at(idx + step);
|
|
560
|
+
if (candidate && (action.delta === "next" ? candidate <= min : candidate >= max)) {
|
|
561
|
+
return { newValue: candidate, done: false };
|
|
562
|
+
}
|
|
563
|
+
return { newValue: current, done: true };
|
|
564
|
+
}
|
|
565
|
+
// Handle numeric delta
|
|
566
|
+
const candidate = current + action.delta;
|
|
567
|
+
const isValid = action.delta > 0 ? candidate <= min : candidate >= max;
|
|
568
|
+
if (isValid) {
|
|
569
|
+
return { newValue: candidate, done: false };
|
|
570
|
+
}
|
|
571
|
+
return { newValue: current, done: true };
|
|
572
|
+
}
|
|
573
|
+
async _computeContext(options, isActive) {
|
|
574
|
+
function updateMinMax(range, value, formatted) {
|
|
575
|
+
if (range.min.value == null || value < range.min.value) {
|
|
576
|
+
range.min.value = value;
|
|
577
|
+
range.min.fmt = formatted;
|
|
578
|
+
}
|
|
579
|
+
if (range.max.value == null || value > range.max.value) {
|
|
580
|
+
range.max.value = value;
|
|
581
|
+
range.max.fmt = formatted;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
for (const option of options) {
|
|
585
|
+
isActive();
|
|
586
|
+
updateMinMax(this.state.price, option.getPrice({ currency: false }), option.getPrice());
|
|
587
|
+
updateMinMax(this.state.price.monthly, option.getPrice({ monthly: true, currency: false }), option.getPrice({ monthly: true }));
|
|
588
|
+
updateMinMax(this.state.discountedPrice, option.getDiscountedPrice({ currency: false }), option.getDiscountedPrice());
|
|
589
|
+
updateMinMax(this.state.discountedPrice.monthly, option.getDiscountedPrice({ monthly: true, currency: false }), option.getDiscountedPrice({ monthly: true }));
|
|
590
|
+
updateMinMax(this.state.discount, option.getDiscount({ symbol: false }), option.getDiscount());
|
|
591
|
+
updateMinMax(this.state.discount.monthly, option.getDiscount({ monthly: true, symbol: false }), option.getDiscount({ monthly: true }));
|
|
592
|
+
updateMinMax(this.state.discount.percentage, option.getDiscount({ percentage: true, symbol: false }), option.getDiscount({ percentage: true }));
|
|
593
|
+
updateMinMax(this.state.discount.percentage.monthly, option.getDiscount({ monthly: true, percentage: true, symbol: false }), option.getDiscount({ monthly: true, percentage: true }));
|
|
594
|
+
}
|
|
595
|
+
this.state = { ...this.state };
|
|
596
|
+
}
|
|
597
|
+
async _getOption({ id, campaign, devices, subscription }, bundle = []) {
|
|
598
|
+
if (id && devices && subscription) {
|
|
599
|
+
const product = await this.store?.getProduct({ id, campaign });
|
|
600
|
+
return await product?.getOption({ devices, subscription, bundle });
|
|
601
|
+
}
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
async _applyBundles(baseOpt, bundles) {
|
|
605
|
+
const result = [];
|
|
606
|
+
const recurse = async (prefix, start) => {
|
|
607
|
+
for (let i = start; i < bundles.length; i++) {
|
|
608
|
+
const bundleOption = await this._getOption({
|
|
609
|
+
id: bundles[i].id,
|
|
610
|
+
campaign: bundles[i].campaign,
|
|
611
|
+
devices: bundles[i].devices ?? baseOpt.getDevices(),
|
|
612
|
+
subscription: bundles[i].subscription ?? baseOpt.getSubscription()
|
|
613
|
+
}, []);
|
|
614
|
+
if (!bundleOption) {
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
const next = prefix.concat({
|
|
618
|
+
devicesFixed: Boolean(bundles[i].devices),
|
|
619
|
+
subscriptionFixed: Boolean(bundles[i].subscription),
|
|
620
|
+
option: bundleOption
|
|
621
|
+
});
|
|
622
|
+
result.push(next);
|
|
623
|
+
await recurse(next, i + 1);
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
const bundleOption = async (option, bundles = []) => {
|
|
627
|
+
for (const bundle of bundles) {
|
|
628
|
+
const newOpt = await option.toogleBundle(bundle);
|
|
629
|
+
if (newOpt) {
|
|
630
|
+
option = newOpt;
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return option;
|
|
637
|
+
};
|
|
638
|
+
await recurse([], 0);
|
|
639
|
+
const x = await Promise.all(result.map(bundles => bundleOption(baseOpt, bundles)));
|
|
640
|
+
return x;
|
|
641
|
+
}
|
|
642
|
+
_notifyParent() {
|
|
643
|
+
if (this.noCollect)
|
|
644
|
+
return;
|
|
645
|
+
this.dispatchEvent(new CollectOptionEvent({
|
|
646
|
+
name: this.storeName,
|
|
647
|
+
options: this._computeTask.taskComplete
|
|
648
|
+
}));
|
|
649
|
+
}
|
|
650
|
+
async getUpdateComplete() {
|
|
651
|
+
const result = await super.getUpdateComplete();
|
|
652
|
+
await this._computeTask.taskComplete;
|
|
653
|
+
await this._defaultForwardEventTask.taskComplete;
|
|
654
|
+
await this._syncEventTask.taskComplete;
|
|
655
|
+
await this._collectToggleTask.taskComplete;
|
|
656
|
+
await this._etaRenderTask.taskComplete;
|
|
657
|
+
return result;
|
|
658
|
+
}
|
|
659
|
+
isDeviceAndSubscriptionChange(evt) {
|
|
660
|
+
return evt instanceof ActionEvent
|
|
661
|
+
&& Boolean(evt.detail?.devices || evt.detail?.subscription)
|
|
662
|
+
&& !evt.detail.bundle;
|
|
663
|
+
}
|
|
664
|
+
isBundleToogle(evt) {
|
|
665
|
+
return evt instanceof ActionEvent
|
|
666
|
+
&& Boolean(evt.detail?.id)
|
|
667
|
+
&& Boolean(evt.detail.bundle);
|
|
668
|
+
}
|
|
669
|
+
isProductChange(evt) {
|
|
670
|
+
return evt instanceof ActionEvent
|
|
671
|
+
&& Boolean(evt.detail?.id)
|
|
672
|
+
&& !evt.detail.bundle;
|
|
673
|
+
}
|
|
674
|
+
isActionEvent(evt) {
|
|
675
|
+
return this.isProductChange(evt) || this.isDeviceAndSubscriptionChange(evt);
|
|
676
|
+
}
|
|
677
|
+
isDeltaUpdate(evt) {
|
|
678
|
+
return evt instanceof UpdateByDeltaEvent;
|
|
679
|
+
}
|
|
680
|
+
_isIgnoredSource(e) {
|
|
681
|
+
const set = this._getIgnoreSet();
|
|
682
|
+
if (set.size === 0)
|
|
683
|
+
return false;
|
|
684
|
+
const id = e.detail?.storeId || "";
|
|
685
|
+
return set.has(id);
|
|
686
|
+
}
|
|
687
|
+
_getIgnoreSet() {
|
|
688
|
+
const raw = this.ignoreEvents;
|
|
689
|
+
if (!raw)
|
|
690
|
+
return new Set();
|
|
691
|
+
return new Set(raw
|
|
692
|
+
.split(',')
|
|
693
|
+
.map(s => s.trim())
|
|
694
|
+
.filter(Boolean));
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
__decorate([
|
|
698
|
+
property({ attribute: false })
|
|
699
|
+
], StateNode.prototype, "autoForward", void 0);
|
|
700
|
+
__decorate([
|
|
701
|
+
property({ type: Boolean, attribute: 'nocollect' })
|
|
702
|
+
], StateNode.prototype, "noCollect", void 0);
|
|
703
|
+
__decorate([
|
|
704
|
+
property({ attribute: 'ignoreevents' })
|
|
705
|
+
], StateNode.prototype, "ignoreEvents", void 0);
|
|
706
|
+
__decorate([
|
|
707
|
+
property()
|
|
708
|
+
], StateNode.prototype, "storeName", void 0);
|
|
709
|
+
__decorate([
|
|
710
|
+
property({ type: Boolean, attribute: 'ignore-events-parent' })
|
|
711
|
+
], StateNode.prototype, "ignoreEventsParent", void 0);
|
|
712
|
+
__decorate([
|
|
713
|
+
consume({ context: storeContext, subscribe: true }),
|
|
714
|
+
property({ attribute: false })
|
|
715
|
+
], StateNode.prototype, "store", void 0);
|
|
716
|
+
__decorate([
|
|
717
|
+
consume({ context: eventContext, subscribe: true }),
|
|
718
|
+
property({ attribute: false })
|
|
719
|
+
], StateNode.prototype, "_eventParent", void 0);
|
|
720
|
+
__decorate([
|
|
721
|
+
consume({ context: derivedContext, subscribe: true }),
|
|
722
|
+
property({ attribute: false })
|
|
723
|
+
], StateNode.prototype, "_derived", void 0);
|
|
724
|
+
__decorate([
|
|
725
|
+
property({ attribute: false })
|
|
726
|
+
], StateNode.prototype, "_event", void 0);
|
|
727
|
+
__decorate([
|
|
728
|
+
property({ attribute: false })
|
|
729
|
+
], StateNode.prototype, "_eventDom", void 0);
|
|
730
|
+
__decorate([
|
|
731
|
+
provide({ context: eventContext }),
|
|
732
|
+
property({ attribute: false })
|
|
733
|
+
], StateNode.prototype, "_fEvent", void 0);
|
|
734
|
+
__decorate([
|
|
735
|
+
provide({ context: stateContext }),
|
|
736
|
+
property({ attribute: false })
|
|
737
|
+
], StateNode.prototype, "state", void 0);
|
|
738
|
+
StateNode = StateNode_1 = __decorate([
|
|
739
|
+
customElement('bd-state')
|
|
740
|
+
], StateNode);
|
|
741
|
+
export { StateNode };
|
|
742
|
+
//# sourceMappingURL=node.state.js.map
|