simplyflow 0.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.
@@ -0,0 +1,1053 @@
1
+ (() => {
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
7
+
8
+ // src/state.mjs
9
+ var state_exports = {};
10
+ __export(state_exports, {
11
+ batch: () => batch,
12
+ clockEffect: () => clockEffect,
13
+ destroy: () => destroy,
14
+ effect: () => effect,
15
+ signal: () => signal,
16
+ throttledEffect: () => throttledEffect,
17
+ untracked: () => untracked
18
+ });
19
+ var iterate = Symbol("iterate");
20
+ if (!Symbol.xRay) {
21
+ Symbol.xRay = Symbol("xRay");
22
+ }
23
+ var signalHandler = {
24
+ get: (target, property, receiver) => {
25
+ if (property === Symbol.xRay) {
26
+ return target;
27
+ }
28
+ const value = target?.[property];
29
+ notifyGet(receiver, property);
30
+ if (typeof value === "function") {
31
+ if (Array.isArray(target)) {
32
+ return (...args) => {
33
+ let l = target.length;
34
+ let result = value.apply(receiver, args);
35
+ if (l != target.length) {
36
+ notifySet(receiver, makeContext("length", { was: l, now: target.length }));
37
+ }
38
+ return result;
39
+ };
40
+ } else if (target instanceof Set || target instanceof Map) {
41
+ return (...args) => {
42
+ let s = target.size;
43
+ let result = value.apply(target, args);
44
+ if (s != target.size) {
45
+ notifySet(receiver, makeContext("size", { was: s, now: target.size }));
46
+ }
47
+ if (["set", "add", "clear", "delete"].includes(property)) {
48
+ notifySet(receiver, makeContext({ entries: {}, forEach: {}, has: {}, keys: {}, values: {}, [Symbol.iterator]: {} }));
49
+ }
50
+ return result;
51
+ };
52
+ } else if (target instanceof HTMLElement || target instanceof Number || target instanceof String || target instanceof Boolean) {
53
+ return value.bind(target);
54
+ } else {
55
+ return value.bind(receiver);
56
+ }
57
+ }
58
+ if (value && typeof value == "object") {
59
+ return signal(value);
60
+ }
61
+ return value;
62
+ },
63
+ set: (target, property, value, receiver) => {
64
+ value = value?.[Symbol.xRay] || value;
65
+ let current = target[property];
66
+ if (current !== value) {
67
+ target[property] = value;
68
+ notifySet(receiver, makeContext(property, { was: current, now: value }));
69
+ }
70
+ if (typeof current === "undefined") {
71
+ notifySet(receiver, makeContext(iterate, {}));
72
+ }
73
+ return true;
74
+ },
75
+ has: (target, property) => {
76
+ let receiver = signals.get(target);
77
+ if (receiver) {
78
+ notifyGet(receiver, property);
79
+ }
80
+ return Object.hasOwn(target, property);
81
+ },
82
+ deleteProperty: (target, property) => {
83
+ if (typeof target[property] !== "undefined") {
84
+ let current = target[property];
85
+ delete target[property];
86
+ let receiver = signals.get(target);
87
+ notifySet(receiver, makeContext(property, { delete: true, was: current }));
88
+ }
89
+ return true;
90
+ },
91
+ defineProperty: (target, property, descriptor) => {
92
+ if (typeof target[property] === "undefined") {
93
+ let receiver = signals.get(target);
94
+ notifySet(receiver, makeContext(iterate, {}));
95
+ }
96
+ return Object.defineProperty(target, property, descriptor);
97
+ },
98
+ ownKeys: (target) => {
99
+ let receiver = signals.get(target);
100
+ notifyGet(receiver, iterate);
101
+ return Reflect.ownKeys(target);
102
+ }
103
+ };
104
+ var signals = /* @__PURE__ */ new WeakMap();
105
+ function signal(v) {
106
+ if (!signals.has(v)) {
107
+ signals.set(v, new Proxy(v, signalHandler));
108
+ }
109
+ return signals.get(v);
110
+ }
111
+ var batchedListeners = /* @__PURE__ */ new Set();
112
+ var batchMode = 0;
113
+ function notifySet(self, context = {}) {
114
+ let listeners = [];
115
+ context.forEach((change, property) => {
116
+ let propListeners = getListeners(self, property);
117
+ if (propListeners?.length) {
118
+ for (let listener of propListeners) {
119
+ addContext(listener, makeContext(property, change));
120
+ }
121
+ listeners = listeners.concat(propListeners);
122
+ }
123
+ });
124
+ listeners = new Set(listeners.filter(Boolean));
125
+ if (listeners) {
126
+ if (batchMode) {
127
+ batchedListeners = batchedListeners.union(listeners);
128
+ } else {
129
+ const currentEffect = computeStack[computeStack.length - 1];
130
+ for (let listener of Array.from(listeners)) {
131
+ if (listener != currentEffect && listener?.needsUpdate) {
132
+ listener();
133
+ }
134
+ clearContext(listener);
135
+ }
136
+ }
137
+ }
138
+ }
139
+ function makeContext(property, change) {
140
+ let context = /* @__PURE__ */ new Map();
141
+ if (typeof property === "object") {
142
+ for (let prop in property) {
143
+ context.set(prop, property[prop]);
144
+ }
145
+ } else {
146
+ context.set(property, change);
147
+ }
148
+ return context;
149
+ }
150
+ function addContext(listener, context) {
151
+ if (!listener.context) {
152
+ listener.context = context;
153
+ } else {
154
+ context.forEach((change, property) => {
155
+ listener.context.set(property, change);
156
+ });
157
+ }
158
+ listener.needsUpdate = true;
159
+ }
160
+ function clearContext(listener) {
161
+ delete listener.context;
162
+ delete listener.needsUpdate;
163
+ }
164
+ function notifyGet(self, property) {
165
+ let currentCompute = computeStack[computeStack.length - 1];
166
+ if (currentCompute) {
167
+ setListeners(self, property, currentCompute);
168
+ }
169
+ }
170
+ var listenersMap = /* @__PURE__ */ new WeakMap();
171
+ var computeMap = /* @__PURE__ */ new WeakMap();
172
+ function getListeners(self, property) {
173
+ let listeners = listenersMap.get(self);
174
+ return listeners ? Array.from(listeners.get(property) || []) : [];
175
+ }
176
+ function setListeners(self, property, compute) {
177
+ if (!listenersMap.has(self)) {
178
+ listenersMap.set(self, /* @__PURE__ */ new Map());
179
+ }
180
+ let listeners = listenersMap.get(self);
181
+ if (!listeners.has(property)) {
182
+ listeners.set(property, /* @__PURE__ */ new Set());
183
+ }
184
+ listeners.get(property).add(compute);
185
+ if (!computeMap.has(compute)) {
186
+ computeMap.set(compute, /* @__PURE__ */ new Map());
187
+ }
188
+ let connectedSignals = computeMap.get(compute);
189
+ if (!connectedSignals.has(property)) {
190
+ connectedSignals.set(property, /* @__PURE__ */ new Set());
191
+ }
192
+ connectedSignals.get(property).add(self);
193
+ }
194
+ function clearListeners(compute) {
195
+ let connectedSignals = computeMap.get(compute);
196
+ if (connectedSignals) {
197
+ connectedSignals.forEach((property) => {
198
+ property.forEach((s) => {
199
+ let listeners = listenersMap.get(s);
200
+ if (listeners.has(property)) {
201
+ listeners.get(property).delete(compute);
202
+ }
203
+ });
204
+ });
205
+ }
206
+ }
207
+ var computeStack = [];
208
+ var effectStack = [];
209
+ var effectMap = /* @__PURE__ */ new WeakMap();
210
+ var signalStack = [];
211
+ function effect(fn) {
212
+ if (effectStack.findIndex((f) => fn == f) !== -1) {
213
+ throw new Error("Recursive update() call", { cause: fn });
214
+ }
215
+ effectStack.push(fn);
216
+ let connectedSignal = signals.get(fn);
217
+ if (!connectedSignal) {
218
+ connectedSignal = signal({
219
+ current: null
220
+ });
221
+ signals.set(fn, connectedSignal);
222
+ }
223
+ const computeEffect = function computeEffect2() {
224
+ if (signalStack.findIndex((s) => s == connectedSignal) !== -1) {
225
+ throw new Error("Cyclical dependency in update() call", { cause: fn });
226
+ }
227
+ clearListeners(computeEffect2);
228
+ computeStack.push(computeEffect2);
229
+ signalStack.push(connectedSignal);
230
+ let result;
231
+ try {
232
+ result = fn(computeEffect2, computeStack, signalStack);
233
+ } finally {
234
+ computeStack.pop();
235
+ signalStack.pop();
236
+ if (result instanceof Promise) {
237
+ result.then((result2) => {
238
+ connectedSignal.current = result2;
239
+ });
240
+ } else {
241
+ connectedSignal.current = result;
242
+ }
243
+ }
244
+ };
245
+ computeEffect.fn = fn;
246
+ effectMap.set(connectedSignal, computeEffect);
247
+ computeEffect();
248
+ return connectedSignal;
249
+ }
250
+ function destroy(connectedSignal) {
251
+ const computeEffect = effectMap.get(connectedSignal)?.deref();
252
+ if (!computeEffect) {
253
+ return;
254
+ }
255
+ clearListeners(computeEffect);
256
+ let fn = computeEffect.fn;
257
+ signals.remove(fn);
258
+ effectMap.delete(connectedSignal);
259
+ }
260
+ function batch(fn) {
261
+ batchMode++;
262
+ let result;
263
+ try {
264
+ result = fn();
265
+ } finally {
266
+ if (result instanceof Promise) {
267
+ result.then(() => {
268
+ batchMode--;
269
+ if (!batchMode) {
270
+ runBatchedListeners();
271
+ }
272
+ });
273
+ } else {
274
+ batchMode--;
275
+ if (!batchMode) {
276
+ runBatchedListeners();
277
+ }
278
+ }
279
+ }
280
+ return result;
281
+ }
282
+ function runBatchedListeners() {
283
+ let copyBatchedListeners = Array.from(batchedListeners);
284
+ batchedListeners = /* @__PURE__ */ new Set();
285
+ const currentEffect = computeStack[computeStack.length - 1];
286
+ for (let listener of copyBatchedListeners) {
287
+ if (listener != currentEffect && listener?.needsUpdate) {
288
+ listener();
289
+ }
290
+ clearContext(listener);
291
+ }
292
+ }
293
+ function throttledEffect(fn, throttleTime) {
294
+ if (effectStack.findIndex((f) => fn == f) !== -1) {
295
+ throw new Error("Recursive update() call", { cause: fn });
296
+ }
297
+ effectStack.push(fn);
298
+ let connectedSignal = signals.get(fn);
299
+ if (!connectedSignal) {
300
+ connectedSignal = signal({
301
+ current: null
302
+ });
303
+ signals.set(fn, connectedSignal);
304
+ }
305
+ let throttled = false;
306
+ let hasChange = true;
307
+ const computeEffect = function computeEffect2() {
308
+ if (signalStack.findIndex((s) => s == connectedSignal) !== -1) {
309
+ throw new Error("Cyclical dependency in update() call", { cause: fn });
310
+ }
311
+ if (throttled && throttled > Date.now()) {
312
+ hasChange = true;
313
+ return;
314
+ }
315
+ clearListeners(computeEffect2);
316
+ computeStack.push(computeEffect2);
317
+ signalStack.push(connectedSignal);
318
+ let result;
319
+ try {
320
+ result = fn(computeEffect2, computeStack, signalStack);
321
+ } finally {
322
+ hasChange = false;
323
+ computeStack.pop();
324
+ signalStack.pop();
325
+ if (result instanceof Promise) {
326
+ result.then((result2) => {
327
+ connectedSignal.current = result2;
328
+ });
329
+ } else {
330
+ connectedSignal.current = result;
331
+ }
332
+ }
333
+ throttled = Date.now() + throttleTime;
334
+ globalThis.setTimeout(() => {
335
+ if (hasChange) {
336
+ computeEffect2();
337
+ }
338
+ }, throttleTime);
339
+ };
340
+ computeEffect();
341
+ return connectedSignal;
342
+ }
343
+ function clockEffect(fn, clock) {
344
+ let connectedSignal = signals.get(fn);
345
+ if (!connectedSignal) {
346
+ connectedSignal = signal({
347
+ current: null
348
+ });
349
+ signals.set(fn, connectedSignal);
350
+ }
351
+ let lastTick = -1;
352
+ let hasChanged = true;
353
+ const computeEffect = function computeEffect2() {
354
+ if (lastTick < clock.time) {
355
+ if (hasChanged) {
356
+ clearListeners(computeEffect2);
357
+ computeStack.push(computeEffect2);
358
+ lastTick = clock.time;
359
+ let result;
360
+ try {
361
+ result = fn(computeEffect2, computeStack);
362
+ } finally {
363
+ computeStack.pop();
364
+ if (result instanceof Promise) {
365
+ result.then((result2) => {
366
+ connectedSignal.current = result2;
367
+ });
368
+ } else {
369
+ connectedSignal.current = result;
370
+ }
371
+ hasChanged = false;
372
+ }
373
+ } else {
374
+ lastTick = clock.time;
375
+ }
376
+ } else {
377
+ hasChanged = true;
378
+ }
379
+ };
380
+ computeEffect();
381
+ return connectedSignal;
382
+ }
383
+ function untracked(fn) {
384
+ const remember = computeStack.slice();
385
+ computeStack = [];
386
+ try {
387
+ return fn();
388
+ } finally {
389
+ computeStack = remember;
390
+ }
391
+ }
392
+
393
+ // src/bind.mjs
394
+ var SimplyBind = class {
395
+ constructor(options) {
396
+ this.bindings = /* @__PURE__ */ new Map();
397
+ const defaultOptions = {
398
+ container: document.body,
399
+ attribute: "data-bind",
400
+ transformers: [],
401
+ defaultTransformers: {
402
+ field: [defaultFieldTransformer],
403
+ list: [defaultListTransformer],
404
+ map: [defaultMapTransformer]
405
+ }
406
+ };
407
+ if (!options?.root) {
408
+ throw new Error("bind needs at least options.root set");
409
+ }
410
+ this.options = Object.assign({}, defaultOptions, options);
411
+ const attribute = this.options.attribute;
412
+ const bindAttributes = [attribute + "-field", attribute + "-list", attribute + "-map"];
413
+ const bindSelector = `[${attribute}-field],[${attribute}-list],[${attribute}-map]`;
414
+ const getBindingAttribute = (el) => {
415
+ const foundAttribute = bindAttributes.find((attr) => el.hasAttribute(attr));
416
+ if (!foundAttribute) {
417
+ console.error("No matching attribute found", el);
418
+ }
419
+ return foundAttribute;
420
+ };
421
+ const render = (el) => {
422
+ this.bindings.set(el, throttledEffect(() => {
423
+ const context = {
424
+ templates: el.querySelectorAll(":scope > template"),
425
+ attribute: getBindingAttribute(el)
426
+ };
427
+ context.path = this.getBindingPath(el);
428
+ context.value = getValueByPath(this.options.root, context.path);
429
+ context.element = el;
430
+ runTransformers(context);
431
+ }, 100));
432
+ };
433
+ const runTransformers = (context) => {
434
+ let transformers;
435
+ switch (context.attribute) {
436
+ case this.options.attribute + "-field":
437
+ transformers = this.options.defaultTransformers.field || [];
438
+ break;
439
+ case this.options.attribute + "-list":
440
+ transformers = this.options.defaultTransformers.list || [];
441
+ break;
442
+ case this.options.attribute + "-map":
443
+ transformers = this.options.defaultTransformers.map || [];
444
+ break;
445
+ }
446
+ if (context.element.dataset.transform) {
447
+ context.element.dataset.transform.split(" ").filter(Boolean).forEach((t) => {
448
+ if (this.options.transformers[t]) {
449
+ transformers.push(this.options.transformers[t]);
450
+ } else {
451
+ console.warn("No transformer with name " + t + " configured", { cause: context.element });
452
+ }
453
+ });
454
+ }
455
+ let next;
456
+ for (let transformer of transformers) {
457
+ next = /* @__PURE__ */ ((next2, transformer2) => {
458
+ return (context2) => {
459
+ return transformer2.call(this, context2, next2);
460
+ };
461
+ })(next, transformer);
462
+ }
463
+ next(context);
464
+ };
465
+ const applyBindings = (bindings2) => {
466
+ for (let bindingEl of bindings2) {
467
+ render(bindingEl);
468
+ }
469
+ };
470
+ const updateBindings = (changes) => {
471
+ const selector = `[${attribute}-field],[${attribute}-list],[${attribute}-map]`;
472
+ for (const change of changes) {
473
+ if (change.type == "childList" && change.addedNodes) {
474
+ for (let node of change.addedNodes) {
475
+ if (node instanceof HTMLElement) {
476
+ let bindings2 = Array.from(node.querySelectorAll(selector));
477
+ if (node.matches(selector)) {
478
+ bindings2.unshift(node);
479
+ }
480
+ if (bindings2.length) {
481
+ applyBindings(bindings2);
482
+ }
483
+ }
484
+ }
485
+ }
486
+ }
487
+ };
488
+ this.observer = new MutationObserver((changes) => {
489
+ updateBindings(changes);
490
+ });
491
+ this.observer.observe(options.container, {
492
+ subtree: true,
493
+ childList: true
494
+ });
495
+ const bindings = this.options.container.querySelectorAll(
496
+ "[" + this.options.attribute + "-field],[" + this.options.attribute + "-list],[" + this.options.attribute + "-map]"
497
+ );
498
+ if (bindings.length) {
499
+ applyBindings(bindings);
500
+ }
501
+ }
502
+ /**
503
+ * Finds the first matching template and creates a new DocumentFragment
504
+ * with the correct data bind attributes in it (prepends the current path)
505
+ */
506
+ applyTemplate(context) {
507
+ const path = context.path;
508
+ const templates = context.templates;
509
+ const list = context.list;
510
+ const index = context.index;
511
+ const parent = context.parent;
512
+ const value = list ? list[index] : context.value;
513
+ let template = this.findTemplate(templates, value);
514
+ if (!template) {
515
+ let result = new DocumentFragment();
516
+ result.innerHTML = "<!-- no matching template -->";
517
+ return result;
518
+ }
519
+ let clone = template.content.cloneNode(true);
520
+ if (!clone.children?.length) {
521
+ return clone;
522
+ }
523
+ if (clone.children.length > 1) {
524
+ throw new Error("template must contain a single root node", { cause: template });
525
+ }
526
+ const attribute = this.options.attribute;
527
+ const attributes = [attribute + "-field", attribute + "-list", attribute + "-map"];
528
+ const bindings = clone.querySelectorAll(`[${attribute}-field],[${attribute}-list],[${attribute}-map]`);
529
+ for (let binding of bindings) {
530
+ const attr = attributes.find((attr2) => binding.hasAttribute(attr2));
531
+ const bind2 = binding.getAttribute(attr);
532
+ if (bind2.substring(0, ":root.".length) == ":root.") {
533
+ binding.setAttribute(attr, bind2.substring(":root.".length));
534
+ } else if (bind2 == ":value" && index != null) {
535
+ binding.setAttribute(attr, path + "." + index);
536
+ } else if (index != null) {
537
+ binding.setAttribute(attr, path + "." + index + "." + bind2);
538
+ } else {
539
+ binding.setAttribute(attr, parent + "." + bind2);
540
+ }
541
+ }
542
+ if (typeof index !== "undefined") {
543
+ clone.children[0].setAttribute(attribute + "-key", index);
544
+ }
545
+ clone.children[0].$bindTemplate = template;
546
+ return clone;
547
+ }
548
+ getBindingPath(el) {
549
+ const attributes = [
550
+ this.options.attribute + "-field",
551
+ this.options.attribute + "-list",
552
+ this.options.attribute + "-map"
553
+ ];
554
+ for (let attr of attributes) {
555
+ if (el.hasAttribute(attr)) {
556
+ return el.getAttribute(attr);
557
+ }
558
+ }
559
+ }
560
+ /**
561
+ * Finds the first template from an array of templates that
562
+ * matches the given value.
563
+ */
564
+ findTemplate(templates, value) {
565
+ const templateMatches = (t) => {
566
+ let path = this.getBindingPath(t);
567
+ let currentItem;
568
+ if (path) {
569
+ if (path.substr(0, 6) == ":root.") {
570
+ currentItem = getValueByPath(this.options.root, path);
571
+ } else {
572
+ currentItem = getValueByPath(value, path);
573
+ }
574
+ } else {
575
+ currentItem = value;
576
+ }
577
+ const strItem = "" + currentItem;
578
+ let matches = t.getAttribute(this.options.attribute + "-match");
579
+ if (matches) {
580
+ if (matches === ":empty" && !currentItem) {
581
+ return t;
582
+ } else if (matches === ":notempty" && currentItem) {
583
+ return t;
584
+ }
585
+ if (strItem.match(matches)) {
586
+ return t;
587
+ }
588
+ }
589
+ if (!matches && currentItem !== null && currentItem !== void 0) {
590
+ return t;
591
+ }
592
+ };
593
+ let template = Array.from(templates).find(templateMatches);
594
+ let rel = template?.getAttribute("rel");
595
+ if (rel) {
596
+ let replacement = document.querySelector("template#" + rel);
597
+ if (!replacement) {
598
+ throw new Error("Could not find template with id " + rel);
599
+ }
600
+ template = replacement;
601
+ }
602
+ return template;
603
+ }
604
+ destroy() {
605
+ this.bindings.forEach((binding) => {
606
+ destroy(binding);
607
+ });
608
+ this.bindings = /* @__PURE__ */ new Map();
609
+ this.observer.disconnect();
610
+ }
611
+ };
612
+ function bind(options) {
613
+ return new SimplyBind(options);
614
+ }
615
+ function matchValue(a, b) {
616
+ if (a == ":empty" && !b) {
617
+ return true;
618
+ }
619
+ if (b == ":empty" && !a) {
620
+ return true;
621
+ }
622
+ if ("" + a == "" + b) {
623
+ return true;
624
+ }
625
+ return false;
626
+ }
627
+ function getValueByPath(root, path) {
628
+ let parts = path.split(".");
629
+ let curr = root;
630
+ let part, prevPart;
631
+ while (parts.length && curr) {
632
+ part = parts.shift();
633
+ if (part == ":key") {
634
+ return prevPart;
635
+ } else if (part == ":value") {
636
+ return curr;
637
+ } else if (part == ":root") {
638
+ curr = root;
639
+ } else {
640
+ part = decodeURIComponent(part);
641
+ curr = curr[part];
642
+ prevPart = part;
643
+ }
644
+ }
645
+ return curr;
646
+ }
647
+ function defaultFieldTransformer(context) {
648
+ const el = context.element;
649
+ const templates = context.templates;
650
+ const templatesCount = templates.length;
651
+ const path = context.path;
652
+ const value = context.value;
653
+ const attribute = this.options.attribute;
654
+ if (templates?.length) {
655
+ transformLiteralByTemplates.call(this, context);
656
+ } else if (el.tagName == "INPUT") {
657
+ transformInput.call(this, context);
658
+ } else if (el.tagName == "BUTTON") {
659
+ transformButton.call(this, context);
660
+ } else if (el.tagName == "SELECT") {
661
+ transformSelect.call(this, context);
662
+ } else if (el.tagName == "A") {
663
+ transformAnchor.call(this, context);
664
+ } else {
665
+ transformElement.call(this, context);
666
+ }
667
+ return context;
668
+ }
669
+ function defaultListTransformer(context) {
670
+ const el = context.element;
671
+ const templates = context.templates;
672
+ const templatesCount = templates.length;
673
+ const path = context.path;
674
+ const value = context.value;
675
+ const attribute = this.options.attribute;
676
+ if (!Array.isArray(value)) {
677
+ console.error("Value is not an array.", el, value);
678
+ } else if (!templates?.length) {
679
+ console.error("No templates found in", el);
680
+ } else {
681
+ transformArrayByTemplates.call(this, context);
682
+ }
683
+ return context;
684
+ }
685
+ function defaultMapTransformer(context) {
686
+ const el = context.element;
687
+ const templates = context.templates;
688
+ const templatesCount = templates.length;
689
+ const path = context.path;
690
+ const value = context.value;
691
+ const attribute = this.options.attribute;
692
+ if (typeof value != "object") {
693
+ console.error("Value is not an object.", el, value);
694
+ } else if (!templates?.length) {
695
+ console.error("No templates found in", el);
696
+ } else {
697
+ transformObjectByTemplates.call(this, context);
698
+ }
699
+ return context;
700
+ }
701
+ function transformArrayByTemplates(context) {
702
+ const el = context.element;
703
+ const templates = context.templates;
704
+ const templatesCount = templates.length;
705
+ const path = context.path;
706
+ const value = context.value;
707
+ const attribute = this.options.attribute;
708
+ let items = el.querySelectorAll(":scope > [" + attribute + "-key]");
709
+ let lastKey = 0;
710
+ let skipped = 0;
711
+ context.list = value;
712
+ for (let item2 of items) {
713
+ let currentKey = parseInt(item2.getAttribute(attribute + "-key"));
714
+ if (currentKey > lastKey) {
715
+ context.index = lastKey;
716
+ el.insertBefore(this.applyTemplate(context), item2);
717
+ } else if (currentKey < lastKey) {
718
+ item2.remove();
719
+ } else {
720
+ let bindings = Array.from(item2.querySelectorAll(`[${attribute}]`));
721
+ if (item2.matches(`[${attribute}]`)) {
722
+ bindings.unshift(item2);
723
+ }
724
+ let needsReplacement = bindings.find((b) => {
725
+ let databind = b.getAttribute(attribute);
726
+ return databind.substr(0, 5) !== ":root" && databind.substr(0, path.length) !== path;
727
+ });
728
+ if (!needsReplacement) {
729
+ if (item2.$bindTemplate) {
730
+ let newTemplate = this.findTemplate(templates, value[lastKey]);
731
+ if (newTemplate != item2.$bindTemplate) {
732
+ needsReplacement = true;
733
+ if (!newTemplate) {
734
+ skipped++;
735
+ }
736
+ }
737
+ }
738
+ }
739
+ if (needsReplacement) {
740
+ context.index = lastKey;
741
+ el.replaceChild(this.applyTemplate(context), item2);
742
+ }
743
+ }
744
+ lastKey++;
745
+ if (lastKey >= value.length) {
746
+ break;
747
+ }
748
+ }
749
+ items = el.querySelectorAll(":scope > [" + attribute + "-key]");
750
+ let length = items.length + skipped;
751
+ if (length > value.length) {
752
+ while (length > value.length) {
753
+ let child = el.querySelectorAll(":scope > :not(template)")?.[length - 1];
754
+ child?.remove();
755
+ length--;
756
+ }
757
+ } else if (length < value.length) {
758
+ while (length < value.length) {
759
+ context.index = length;
760
+ el.appendChild(this.applyTemplate(context));
761
+ length++;
762
+ }
763
+ }
764
+ }
765
+ function transformObjectByTemplates(context) {
766
+ const el = context.element;
767
+ const templates = context.templates;
768
+ const templatesCount = templates.length;
769
+ const path = context.path;
770
+ const value = context.value;
771
+ const attribute = this.options.attribute;
772
+ context.list = value;
773
+ let items = Array.from(el.querySelectorAll(":scope > [" + attribute + "-key]"));
774
+ for (let key in context.list) {
775
+ context.index = key;
776
+ let item2 = items.shift();
777
+ if (!item2) {
778
+ let clone = this.applyTemplate(context);
779
+ if (clone.firstElementChild) {
780
+ el.appendChild(clone);
781
+ }
782
+ continue;
783
+ }
784
+ if (item2.getAttribute[attribute + "-key"] != key) {
785
+ items.unshift(item2);
786
+ let outOfOrderItem = el.querySelector(":scope > [" + attribute + '-key="' + key + '"]');
787
+ if (!outOfOrderItem) {
788
+ let clone = this.applyTemplate(context);
789
+ if (clone.firstElementChild) {
790
+ el.insertBefore(clone, item2);
791
+ }
792
+ continue;
793
+ } else {
794
+ el.insertBefore(outOfOrderItem, item2);
795
+ item2 = outOfOrderItem;
796
+ items = items.filter((i) => i != outOfOrderItem);
797
+ }
798
+ }
799
+ let newTemplate = this.findTemplate(templates, value[key]);
800
+ if (newTemplate != item2.$bindTemplate) {
801
+ let clone = this.applyTemplate(context);
802
+ el.replaceChild(clone, item2);
803
+ }
804
+ }
805
+ while (items.length) {
806
+ item = items.shift();
807
+ item.remove();
808
+ }
809
+ }
810
+ function getParentPath(el, attribute) {
811
+ const parentEl = el.parentElement?.closest(`[${attribute}-list],[${attribute}-map]`);
812
+ if (!parentEl) {
813
+ return ":root";
814
+ }
815
+ if (parentEl.hasAttribute(`${attribute}-list`)) {
816
+ return parentEl.getAttribute(`${attribute}-list`);
817
+ }
818
+ return parentEl.getAttribute(`${attribute}-map`);
819
+ }
820
+ function transformLiteralByTemplates(context) {
821
+ const el = context.element;
822
+ const templates = context.templates;
823
+ const value = context.value;
824
+ const attribute = this.options.attribute;
825
+ const rendered = el.querySelector(":scope > :not(template)");
826
+ const template = this.findTemplate(templates, value);
827
+ context.parent = getParentPath(el, attribute);
828
+ if (rendered) {
829
+ if (template) {
830
+ if (rendered?.$bindTemplate != template) {
831
+ const clone = this.applyTemplate(context);
832
+ el.replaceChild(clone, rendered);
833
+ }
834
+ } else {
835
+ el.removeChild(rendered);
836
+ }
837
+ } else if (template) {
838
+ const clone = this.applyTemplate(context);
839
+ el.appendChild(clone);
840
+ }
841
+ }
842
+ function transformInput(context) {
843
+ const el = context.element;
844
+ const value = context.value;
845
+ if (el.type == "checkbox" || el.type == "radio") {
846
+ if (matchValue(el.value, value)) {
847
+ el.checked = true;
848
+ } else {
849
+ el.checked = false;
850
+ }
851
+ } else if (!matchValue(el.value, value)) {
852
+ el.value = "" + value;
853
+ }
854
+ }
855
+ function transformButton(context) {
856
+ const el = context.element;
857
+ const value = context.value;
858
+ if (!matchValue(el.value, value)) {
859
+ el.value = "" + value;
860
+ }
861
+ }
862
+ function transformSelect(context) {
863
+ const el = context.element;
864
+ const value = context.value;
865
+ if (el.multiple) {
866
+ if (Array.isArray(value)) {
867
+ for (let option of el.options) {
868
+ if (value.indexOf(option.value) === false) {
869
+ option.selected = false;
870
+ } else {
871
+ option.selected = true;
872
+ }
873
+ }
874
+ }
875
+ } else {
876
+ let option = el.options.find((o) => matchValue(o.value, value));
877
+ if (option) {
878
+ option.selected = true;
879
+ }
880
+ }
881
+ }
882
+ function transformAnchor(context) {
883
+ const el = context.element;
884
+ const value = context.value;
885
+ if (value?.innerHTML && !matchValue(el.innerHTML, value.innerHTML)) {
886
+ el.innerHTML = "" + value.innerHTML;
887
+ }
888
+ if (value?.href && !matchValue(el.href, value.href)) {
889
+ el.href = "" + value.href;
890
+ }
891
+ }
892
+ function transformElement(context) {
893
+ const el = context.element;
894
+ const value = context.value;
895
+ if (!matchValue(el.innerHTML, value)) {
896
+ if (typeof value == "undefined" || value == null) {
897
+ el.innerHTML = "";
898
+ } else {
899
+ el.innerHTML = "" + value;
900
+ }
901
+ }
902
+ }
903
+
904
+ // src/model.mjs
905
+ var model_exports = {};
906
+ __export(model_exports, {
907
+ columns: () => columns,
908
+ filter: () => filter,
909
+ model: () => model,
910
+ paging: () => paging,
911
+ sort: () => sort
912
+ });
913
+ var SimplyModel = class {
914
+ /**
915
+ * Creates a new datamodel, with a state property that contains
916
+ * all the data passed to this constructor
917
+ * @param state Object with all the data for this model
918
+ */
919
+ constructor(state) {
920
+ this.state = signal(state);
921
+ if (!this.state.options) {
922
+ this.state.options = {};
923
+ }
924
+ this.effects = [{ current: state.data }];
925
+ this.view = signal(state.data);
926
+ }
927
+ /**
928
+ * Adds an effect to run whenever a signal it depends on
929
+ * changes. this.state is the usual signal.
930
+ * The `fn` function param is not itself an effect, but must return
931
+ * and effect function. `fn` takes one param, which is the data signal.
932
+ * This signal will always have at least a `current` property.
933
+ * The result of the effect function is pushed on to the this.effects
934
+ * list. And the last effect added is set as this.view
935
+ */
936
+ addEffect(fn) {
937
+ const dataSignal = this.effects[this.effects.length - 1];
938
+ this.view = fn.call(this, dataSignal);
939
+ this.effects.push(this.view);
940
+ }
941
+ };
942
+ function model(options) {
943
+ return new SimplyModel(options);
944
+ }
945
+ function sort(options = {}) {
946
+ return function(data) {
947
+ this.state.options.sort = Object.assign({
948
+ direction: "asc",
949
+ sortBy: null,
950
+ sortFn: (a, b) => {
951
+ const sort2 = this.state.options.sort;
952
+ const sortBy = sort2.sortBy;
953
+ if (!sort2.sortBy) {
954
+ return 0;
955
+ }
956
+ const larger = sort2.direction == "asc" ? 1 : -1;
957
+ const smaller = sort2.direction == "asc" ? -1 : 1;
958
+ if (typeof a?.[sortBy] === "undefined") {
959
+ if (typeof b?.[sortBy] === "undefined") {
960
+ return 0;
961
+ }
962
+ return larger;
963
+ }
964
+ if (typeof b?.[sortBy] === "undefined") {
965
+ return smaller;
966
+ }
967
+ if (a[sortBy] < b[sortBy]) {
968
+ return smaller;
969
+ } else if (a[sortBy] > b[sortBy]) {
970
+ return larger;
971
+ } else {
972
+ return 0;
973
+ }
974
+ }
975
+ }, options);
976
+ return effect(() => {
977
+ const sort2 = this.state.options.sort;
978
+ if (sort2?.sortBy && sort2?.direction) {
979
+ return data.current.toSorted(sort2?.sortFn);
980
+ }
981
+ return data.current;
982
+ });
983
+ };
984
+ }
985
+ function paging(options = {}) {
986
+ return function(data) {
987
+ this.state.options.paging = Object.assign({
988
+ page: 1,
989
+ pageSize: 20,
990
+ max: 1
991
+ }, options);
992
+ return effect(() => {
993
+ return batch(() => {
994
+ const paging2 = this.state.options.paging;
995
+ if (!paging2.pageSize) {
996
+ paging2.pageSize = 20;
997
+ }
998
+ paging2.max = Math.ceil(this.state.data.length / paging2.pageSize);
999
+ paging2.page = Math.max(1, Math.min(paging2.max, paging2.page));
1000
+ const start = (paging2.page - 1) * paging2.pageSize;
1001
+ const end = start + paging2.pageSize;
1002
+ return data.current.slice(start, end);
1003
+ });
1004
+ });
1005
+ };
1006
+ }
1007
+ function filter(options) {
1008
+ if (!options?.name || typeof options.name !== "string") {
1009
+ throw new Error("filter requires options.name to be a string");
1010
+ }
1011
+ if (!options.matches || typeof options.matches !== "function") {
1012
+ throw new Error("filter requires options.matches to be a function");
1013
+ }
1014
+ return function(data) {
1015
+ this.state.options[options.name] = options;
1016
+ return effect(() => {
1017
+ if (this.state.options[options.name].enabled) {
1018
+ return data.filter(this.state.options.matches);
1019
+ }
1020
+ });
1021
+ };
1022
+ }
1023
+ function columns(options = {}) {
1024
+ if (!options || typeof options !== "object" || Object.keys(options).length === 0) {
1025
+ throw new Error("columns requires options to be an object with at least one property");
1026
+ }
1027
+ return function(data) {
1028
+ this.state.options.columns = options;
1029
+ return effect(() => {
1030
+ return data.current.map((input) => {
1031
+ let result = {};
1032
+ for (let key of Object.keys(this.state.options.columns)) {
1033
+ if (!this.state.options.columns[key].hidden) {
1034
+ result[key] = input[key];
1035
+ }
1036
+ }
1037
+ return result;
1038
+ });
1039
+ });
1040
+ };
1041
+ }
1042
+
1043
+ // src/flow.mjs
1044
+ if (!window.simply) {
1045
+ window.simply = {};
1046
+ }
1047
+ Object.assign(window.simply, {
1048
+ bind,
1049
+ model: model_exports,
1050
+ state: state_exports
1051
+ });
1052
+ var flow_default = window.simply;
1053
+ })();