@muze-labs/simplyflow 0.9.0 → 0.10.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.
@@ -5,9 +5,9 @@
5
5
  __defProp(target, name, { get: all[name], enumerable: true });
6
6
  };
7
7
 
8
- // src/state.mjs
9
- var state_exports = {};
10
- __export(state_exports, {
8
+ // ../state/src/index.mjs
9
+ var src_exports = {};
10
+ __export(src_exports, {
11
11
  addTracer: () => addTracer,
12
12
  batch: () => batch,
13
13
  clockEffect: () => clockEffect,
@@ -29,7 +29,7 @@
29
29
  untracked: () => untracked
30
30
  });
31
31
 
32
- // src/symbols.mjs
32
+ // ../state/src/symbols.mjs
33
33
  var DEP = {
34
34
  ITERATE: Symbol.for("@simplyedit/simplyflow.iterate"),
35
35
  XRAY: Symbol.for("@simplyedit/simplyflow.xRay"),
@@ -40,7 +40,7 @@
40
40
  SIZE: "size"
41
41
  };
42
42
 
43
- // src/state.mjs
43
+ // ../state/src/index.mjs
44
44
  var MAP_READS_KEY = /* @__PURE__ */ new Set(["get", "has"]);
45
45
  var MAP_READS_ITERATION = /* @__PURE__ */ new Set(["keys", "values", "entries", "forEach", Symbol.iterator]);
46
46
  var MAP_WRITES = /* @__PURE__ */ new Set(["set", "delete", "clear"]);
@@ -666,7 +666,6 @@
666
666
  runTracked(compute, connectedSignal, fn, throttledEffect);
667
667
  hasChange = false;
668
668
  throttledUntil = Date.now() + throttleTime;
669
- schedule();
670
669
  };
671
670
  function schedule() {
672
671
  if (timeout) {
@@ -983,29 +982,7 @@
983
982
  return cloneValue(value);
984
983
  }
985
984
 
986
- // src/bind.transformers.mjs
987
- function escape_html(context, next) {
988
- let content = context.value?.innerHTML;
989
- if (typeof context.value == "string") {
990
- content = context.value;
991
- context.value = { innerHTML: content };
992
- }
993
- if (content) {
994
- content = content.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
995
- context.value.innerHTML = content;
996
- }
997
- next(context);
998
- }
999
- function fixed_content(context, next) {
1000
- if (typeof context.value == "string") {
1001
- context.value = {};
1002
- } else {
1003
- delete context.value?.innerHTML;
1004
- }
1005
- next(context);
1006
- }
1007
-
1008
- // src/dom.mjs
985
+ // ../bind/src/dom.mjs
1009
986
  var dom_exports = {};
1010
987
  __export(dom_exports, {
1011
988
  findAttribute: () => findAttribute,
@@ -1013,210 +990,8 @@
1013
990
  trackDomField: () => trackDomField,
1014
991
  trackDomList: () => trackDomList
1015
992
  });
1016
- var domSignals = /* @__PURE__ */ new WeakMap();
1017
- var observers = /* @__PURE__ */ new WeakMap();
1018
- var domSignalHandler = {
1019
- get: (target, property, receiver) => {
1020
- const value = target?.[property];
1021
- notifyGet(receiver, property);
1022
- if (typeof value === "function") {
1023
- return value.bind(target);
1024
- }
1025
- if (value && typeof value == "object") {
1026
- return signal(value);
1027
- }
1028
- return value;
1029
- },
1030
- set: (target, property, value, receiver) => {
1031
- const current = target[property];
1032
- target[property] = value;
1033
- const now = target[property];
1034
- if (!Object.is(current, now)) {
1035
- notifySet(receiver, makeContext(property, { was: current, now }));
1036
- }
1037
- return true;
1038
- },
1039
- has: (target, property) => {
1040
- const receiver = getSignal(target);
1041
- if (receiver) {
1042
- notifyGet(receiver, property);
1043
- }
1044
- return Reflect.has(target, property);
1045
- },
1046
- ownKeys: (target) => {
1047
- const receiver = getSignal(target);
1048
- if (receiver) {
1049
- notifyGet(receiver, DEP.ITERATE);
1050
- }
1051
- return Reflect.ownKeys(target);
1052
- }
1053
- };
1054
- function signal2(el, options) {
1055
- if (isSignal(el)) {
1056
- return el;
1057
- }
1058
- const existing = getSignal(el);
1059
- if (existing) {
1060
- return existing;
1061
- }
1062
- return createSignal(el, domSignalHandler, (target, proxy) => {
1063
- domListen(target, proxy, options);
1064
- });
1065
- }
1066
- function domListen(el, signal3, options) {
1067
- const defaultOptions = {
1068
- characterData: true,
1069
- subtree: true,
1070
- attributes: true,
1071
- attributesOldValue: true,
1072
- childList: true
1073
- };
1074
- if (!options) {
1075
- options = defaultOptions;
1076
- }
1077
- let oldContentHTML = el.innerHTML;
1078
- let oldContentText = el.innerText;
1079
- if (!observers.has(el)) {
1080
- const observer = new MutationObserver((mutationList, observer2) => {
1081
- const changes = {};
1082
- for (const mutation of mutationList) {
1083
- if (mutation.type === "attributes") {
1084
- changes[mutation.attributeName] = mutation.attributeOldValue;
1085
- } else if (mutation.type === "subtree" || mutation.type === "characterData") {
1086
- if (el.innerHTML != oldContentHTML) {
1087
- changes.innerHTML = oldContentHTML;
1088
- oldContentHTML = el.innerHTML;
1089
- }
1090
- if (el.innerText != oldContentText) {
1091
- changes.innerText = oldContentText;
1092
- oldContentText = el.innerText;
1093
- }
1094
- } else if (mutation.type === "childList") {
1095
- changes.children = {
1096
- //FIXME: overwrites changes in this list path if list is rendered multiple times
1097
- was: Array.from(el.children)
1098
- //FIXME; fill in 'now'
1099
- };
1100
- changes.length = -1;
1101
- if (el.innerHTML != oldContentHTML) {
1102
- changes.innerHTML = oldContentHTML;
1103
- oldContentHTML = el.innerHTML;
1104
- }
1105
- if (el.innerText != oldContentText) {
1106
- changes.innerText = oldContentText;
1107
- oldContentText = el.innerText;
1108
- }
1109
- } else {
1110
- console.log("nothing to do for", el, mutation.type);
1111
- }
1112
- }
1113
- for (const prop in changes) {
1114
- notifySet(signal3, makeContext(prop, { was: changes[prop], now: el[prop] }));
1115
- }
1116
- });
1117
- observer.observe(el, options);
1118
- observers.set(el, observer);
1119
- if (el.matches("input, textarea, select")) {
1120
- let prevValue = el.value;
1121
- let prevChecked = el.checked;
1122
- const notifyFormValue = () => {
1123
- notifySet(signal3, makeContext("value", { was: prevValue, now: el.value }));
1124
- prevValue = el.value;
1125
- if ("checked" in el) {
1126
- notifySet(signal3, makeContext("checked", { was: prevChecked, now: el.checked }));
1127
- prevChecked = el.checked;
1128
- }
1129
- };
1130
- el.addEventListener("change", notifyFormValue);
1131
- if (el.matches("input, textarea")) {
1132
- el.addEventListener("input", notifyFormValue);
1133
- }
1134
- }
1135
- }
1136
- }
1137
- function trackDomList(element2) {
1138
- const path2 = this.getBindingPath(element2);
1139
- if (!path2) {
1140
- throw new Error("Could not find binding path for element", { cause: element2 });
1141
- }
1142
- const s = signal2(element2, {
1143
- childList: true
1144
- });
1145
- throttledEffect(() => {
1146
- const children = Array.from(s.children);
1147
- untracked(() => {
1148
- batch(() => {
1149
- let key = 0;
1150
- const currentList = getValueByPath(this.options.root, path2);
1151
- const source = currentList.slice();
1152
- for (const item of children) {
1153
- if (item.tagName === "TEMPLATE") {
1154
- continue;
1155
- }
1156
- if (item.dataset.flowKey) {
1157
- if (item.dataset.flowKey != key) {
1158
- setValueByPath(
1159
- this.options.root,
1160
- path2 + "." + key,
1161
- source[item.dataset.flowKey]
1162
- );
1163
- }
1164
- key++;
1165
- }
1166
- }
1167
- if (currentList.length > key) {
1168
- currentList.length = key;
1169
- }
1170
- });
1171
- });
1172
- }, 50);
1173
- return s;
1174
- }
1175
- function trackDomField(element2, props, valueIsString, stringProperty = "innerHTML", getUpdateValue) {
1176
- if (domSignals.has(element2)) {
1177
- return;
1178
- }
1179
- const path2 = this.getBindingPath(element2);
1180
- if (!path2) {
1181
- throw new Error("Could not find binding path for element", { cause: element2 });
1182
- }
1183
- const s = signal2(element2);
1184
- domSignals.set(element2, s);
1185
- batch(() => {
1186
- throttledEffect(() => {
1187
- let updateValue;
1188
- if (getUpdateValue) {
1189
- for (const prop of props) {
1190
- s[prop];
1191
- }
1192
- } else {
1193
- updateValue = s[stringProperty];
1194
- if (!valueIsString) {
1195
- updateValue = getProperties(s, ...props);
1196
- }
1197
- }
1198
- untracked(() => {
1199
- const currentValue = getValueByPath(this.options.root, path2);
1200
- if (getUpdateValue) {
1201
- updateValue = getUpdateValue.call(this, s, currentValue);
1202
- }
1203
- if (typeof updateValue === "undefined") {
1204
- return;
1205
- }
1206
- if (valueIsString && !Object.is(currentValue, updateValue) && String(currentValue) === updateValue) {
1207
- return;
1208
- }
1209
- setValueByPath(this.options.root, path2, updateValue);
1210
- });
1211
- }, 50);
1212
- });
1213
- return s;
1214
- }
1215
- function findAttribute(el, attr) {
1216
- return el.closest("[" + attr + "]")?.getAttribute(attr);
1217
- }
1218
993
 
1219
- // src/bind.render.mjs
994
+ // ../bind/src/render.mjs
1220
995
  function writesFromDom(binding, context) {
1221
996
  return binding.options.twoway || context.edit;
1222
997
  }
@@ -1260,7 +1035,7 @@
1260
1035
  return true;
1261
1036
  }
1262
1037
  }
1263
- function setValueByPath(root, path2, value) {
1038
+ function setValueByPath(root, path2, value, options = {}) {
1264
1039
  batch(() => {
1265
1040
  let parts = path2.split(".");
1266
1041
  let curr = root;
@@ -1296,7 +1071,9 @@
1296
1071
  curr = prevCurr[prevPart];
1297
1072
  }
1298
1073
  }
1299
- if (prev && prevPart && prev[prevPart] !== value) {
1074
+ if (prev && prevPart && options.replace) {
1075
+ prev[prevPart] = value;
1076
+ } else if (prev && prevPart && prev[prevPart] !== value) {
1300
1077
  if (Array.isArray(value)) {
1301
1078
  prev[prevPart] = value;
1302
1079
  } else if (value && typeof value == "object") {
@@ -1318,8 +1095,8 @@
1318
1095
  }
1319
1096
  function arrayByTemplates(context) {
1320
1097
  const attribute = this.options.attribute;
1321
- const attributes = [attribute + "-field", attribute + "-edit", attribute + "-list", attribute + "-map", attribute + "-value-path"];
1322
- const attrQuery = "[" + attributes.join("],[") + "]";
1098
+ const attributes2 = [attribute + "-field", attribute + "-edit", attribute + "-list", attribute + "-map", attribute + "-value-path"];
1099
+ const attrQuery = "[" + attributes2.join("],[") + "]";
1323
1100
  const keyAttribute = attribute + "-key";
1324
1101
  const items = Array.from(context.element.querySelectorAll(":scope > [" + keyAttribute + "]"));
1325
1102
  const usedItems = /* @__PURE__ */ new Set();
@@ -1340,8 +1117,10 @@
1340
1117
  if (newTemplate != reusableItem[DEP.TEMPLATE]) {
1341
1118
  context.element.replaceChild(this.applyTemplate(context), reusableItem);
1342
1119
  } else {
1343
- context.element.insertBefore(reusableItem, item);
1344
- updateItemKey(reusableItem, index, context.path, keyAttribute, attributes, attrQuery);
1120
+ if (reusableItem !== item) {
1121
+ context.element.insertBefore(reusableItem, item);
1122
+ }
1123
+ updateItemKey(reusableItem, index, context.path, keyAttribute, attributes2, attrQuery);
1345
1124
  reusableItem[DEP.VALUE] = value;
1346
1125
  }
1347
1126
  usedItems.add(reusableItem);
@@ -1378,7 +1157,7 @@
1378
1157
  }
1379
1158
  }
1380
1159
  }
1381
- function updateItemKey(item, key, path2, keyAttribute, attributes, attrQuery) {
1160
+ function updateItemKey(item, key, path2, keyAttribute, attributes2, attrQuery) {
1382
1161
  const oldKey = item.getAttribute(keyAttribute);
1383
1162
  const newKey = "" + key;
1384
1163
  if (oldKey === newKey) {
@@ -1392,7 +1171,7 @@
1392
1171
  bindings.unshift(item);
1393
1172
  }
1394
1173
  for (let binding of bindings) {
1395
- for (let attr of attributes) {
1174
+ for (let attr of attributes2) {
1396
1175
  const bindPath = binding.getAttribute(attr);
1397
1176
  if (!bindPath || bindPath.substr(0, 5) === ":root") {
1398
1177
  continue;
@@ -1407,8 +1186,8 @@
1407
1186
  }
1408
1187
  function objectByTemplates(context) {
1409
1188
  const attribute = this.options.attribute;
1410
- const attributes = [attribute + "-field", attribute + "-edit", attribute + "-list", attribute + "-map", attribute + "-value-path"];
1411
- const attrQuery = "[" + attributes.join("],[") + "]";
1189
+ const attributes2 = [attribute + "-field", attribute + "-edit", attribute + "-list", attribute + "-map", attribute + "-value-path"];
1190
+ const attrQuery = "[" + attributes2.join("],[") + "]";
1412
1191
  const keyAttribute = attribute + "-key";
1413
1192
  const items = Array.from(context.element.querySelectorAll(":scope > [" + keyAttribute + "]"));
1414
1193
  const usedItems = /* @__PURE__ */ new Set();
@@ -1433,8 +1212,10 @@
1433
1212
  if (newTemplate != reusableItem[DEP.TEMPLATE]) {
1434
1213
  context.element.replaceChild(this.applyTemplate(context), reusableItem);
1435
1214
  } else {
1436
- context.element.insertBefore(reusableItem, item);
1437
- updateItemKey(reusableItem, key, context.path, keyAttribute, attributes, attrQuery);
1215
+ if (reusableItem !== item) {
1216
+ context.element.insertBefore(reusableItem, item);
1217
+ }
1218
+ updateItemKey(reusableItem, key, context.path, keyAttribute, attributes2, attrQuery);
1438
1219
  reusableItem[DEP.VALUE] = value;
1439
1220
  }
1440
1221
  usedItems.add(reusableItem);
@@ -1506,11 +1287,11 @@
1506
1287
  }
1507
1288
  if (writesFromDom(this, context)) {
1508
1289
  if (el.type == "checkbox") {
1509
- trackDomField.call(this, context.element, ["checked"], true, "checked", checkboxEditValue);
1290
+ trackDomField.call(this, context.element, ["checked"], true, "checked", checkboxEditValue, context);
1510
1291
  } else if (el.type == "radio") {
1511
- trackDomField.call(this, context.element, ["checked"], true, "checked", radioEditValue);
1292
+ trackDomField.call(this, context.element, ["checked"], true, "checked", radioEditValue, context);
1512
1293
  } else {
1513
- trackDomField.call(this, context.element, ["value"], true, "value");
1294
+ trackDomField.call(this, context.element, ["value"], true, "value", void 0, context);
1514
1295
  }
1515
1296
  }
1516
1297
  }
@@ -1581,9 +1362,9 @@
1581
1362
  }
1582
1363
  if (writesFromDom(this, context)) {
1583
1364
  if (el.multiple) {
1584
- trackDomField.call(this, context.element, ["value"], true, "value", selectMultipleEditValue);
1365
+ trackDomField.call(this, context.element, ["value"], true, "value", selectMultipleEditValue, context);
1585
1366
  } else {
1586
- trackDomField.call(this, context.element, ["value"], true, "value");
1367
+ trackDomField.call(this, context.element, ["value"], true, "value", void 0, context);
1587
1368
  }
1588
1369
  }
1589
1370
  }
@@ -1661,7 +1442,7 @@
1661
1442
  const props = ["innerHTML", "title", "id", "className"].concat(extraprops);
1662
1443
  setProperties(el, value, ...props);
1663
1444
  if (writesFromDom(this, context)) {
1664
- trackDomField.call(this, context.element, props, valueIsString);
1445
+ trackDomField.call(this, context.element, props, valueIsString, "innerHTML", void 0, context);
1665
1446
  }
1666
1447
  }
1667
1448
  function setProperties(el, data, ...properties) {
@@ -1683,7 +1464,7 @@
1683
1464
  }
1684
1465
  }
1685
1466
  function updateProperties(context, properties) {
1686
- trackDomField.call(this, context.element, properties, false);
1467
+ trackDomField.call(this, context.element, properties, false, "innerHTML", void 0, context);
1687
1468
  }
1688
1469
  function getProperties(el, ...properties) {
1689
1470
  const result = {};
@@ -1709,7 +1490,321 @@
1709
1490
  return false;
1710
1491
  }
1711
1492
 
1712
- // src/bind.mjs
1493
+ // ../bind/src/dom.mjs
1494
+ var domSignals = /* @__PURE__ */ new WeakMap();
1495
+ var observers = /* @__PURE__ */ new WeakMap();
1496
+ var domSignalHandler = {
1497
+ get: (target, property, receiver) => {
1498
+ const value = target?.[property];
1499
+ notifyGet(receiver, property);
1500
+ if (typeof value === "function") {
1501
+ return value.bind(target);
1502
+ }
1503
+ if (value && typeof value == "object") {
1504
+ return signal(value);
1505
+ }
1506
+ return value;
1507
+ },
1508
+ set: (target, property, value, receiver) => {
1509
+ const current = target[property];
1510
+ target[property] = value;
1511
+ const now = target[property];
1512
+ if (!Object.is(current, now)) {
1513
+ notifySet(receiver, makeContext(property, { was: current, now }));
1514
+ }
1515
+ return true;
1516
+ },
1517
+ has: (target, property) => {
1518
+ const receiver = getSignal(target);
1519
+ if (receiver) {
1520
+ notifyGet(receiver, property);
1521
+ }
1522
+ return Reflect.has(target, property);
1523
+ },
1524
+ ownKeys: (target) => {
1525
+ const receiver = getSignal(target);
1526
+ if (receiver) {
1527
+ notifyGet(receiver, DEP.ITERATE);
1528
+ }
1529
+ return Reflect.ownKeys(target);
1530
+ }
1531
+ };
1532
+ function signal2(el, options) {
1533
+ if (isSignal(el)) {
1534
+ return el;
1535
+ }
1536
+ const existing = getSignal(el);
1537
+ if (existing) {
1538
+ return existing;
1539
+ }
1540
+ return createSignal(el, domSignalHandler, (target, proxy) => {
1541
+ domListen(target, proxy, options);
1542
+ });
1543
+ }
1544
+ function domListen(el, signal3, options) {
1545
+ const defaultOptions = {
1546
+ characterData: true,
1547
+ subtree: true,
1548
+ attributes: true,
1549
+ attributesOldValue: true,
1550
+ childList: true
1551
+ };
1552
+ if (!options) {
1553
+ options = defaultOptions;
1554
+ }
1555
+ let oldContentHTML = el.innerHTML;
1556
+ let oldContentText = el.innerText;
1557
+ if (!observers.has(el)) {
1558
+ const observer = new MutationObserver((mutationList, observer2) => {
1559
+ const changes = {};
1560
+ for (const mutation of mutationList) {
1561
+ if (mutation.type === "attributes") {
1562
+ changes[mutation.attributeName] = mutation.attributeOldValue;
1563
+ } else if (mutation.type === "subtree" || mutation.type === "characterData") {
1564
+ if (el.innerHTML != oldContentHTML) {
1565
+ changes.innerHTML = oldContentHTML;
1566
+ oldContentHTML = el.innerHTML;
1567
+ }
1568
+ if (el.innerText != oldContentText) {
1569
+ changes.innerText = oldContentText;
1570
+ oldContentText = el.innerText;
1571
+ }
1572
+ } else if (mutation.type === "childList") {
1573
+ changes.children = {
1574
+ //FIXME: overwrites changes in this list path if list is rendered multiple times
1575
+ was: Array.from(el.children)
1576
+ //FIXME; fill in 'now'
1577
+ };
1578
+ changes.length = -1;
1579
+ if (el.innerHTML != oldContentHTML) {
1580
+ changes.innerHTML = oldContentHTML;
1581
+ oldContentHTML = el.innerHTML;
1582
+ }
1583
+ if (el.innerText != oldContentText) {
1584
+ changes.innerText = oldContentText;
1585
+ oldContentText = el.innerText;
1586
+ }
1587
+ } else {
1588
+ console.log("nothing to do for", el, mutation.type);
1589
+ }
1590
+ }
1591
+ for (const prop in changes) {
1592
+ notifySet(signal3, makeContext(prop, { was: changes[prop], now: el[prop] }));
1593
+ }
1594
+ });
1595
+ observer.observe(el, options);
1596
+ observers.set(el, observer);
1597
+ if (el.matches("input, textarea, select")) {
1598
+ let prevValue = el.value;
1599
+ let prevChecked = el.checked;
1600
+ const notifyFormValue = () => {
1601
+ notifySet(signal3, makeContext("value", { was: prevValue, now: el.value }));
1602
+ prevValue = el.value;
1603
+ if ("checked" in el) {
1604
+ notifySet(signal3, makeContext("checked", { was: prevChecked, now: el.checked }));
1605
+ prevChecked = el.checked;
1606
+ }
1607
+ };
1608
+ el.addEventListener("change", notifyFormValue);
1609
+ if (el.matches("input, textarea")) {
1610
+ el.addEventListener("input", notifyFormValue);
1611
+ }
1612
+ }
1613
+ }
1614
+ }
1615
+ function trackDomList(element2) {
1616
+ const path2 = this.getBindingPath(element2);
1617
+ if (!path2) {
1618
+ throw new Error("Could not find binding path for element", { cause: element2 });
1619
+ }
1620
+ const s = signal2(element2, {
1621
+ childList: true
1622
+ });
1623
+ throttledEffect(() => {
1624
+ const children = Array.from(s.children);
1625
+ untracked(() => {
1626
+ batch(() => {
1627
+ let key = 0;
1628
+ const currentList = getValueByPath(this.options.root, path2);
1629
+ const source = currentList.slice();
1630
+ for (const item of children) {
1631
+ if (item.tagName === "TEMPLATE") {
1632
+ continue;
1633
+ }
1634
+ if (item.dataset.flowKey) {
1635
+ if (item.dataset.flowKey != key) {
1636
+ setValueByPath(
1637
+ this.options.root,
1638
+ path2 + "." + key,
1639
+ source[item.dataset.flowKey]
1640
+ );
1641
+ }
1642
+ key++;
1643
+ }
1644
+ }
1645
+ if (currentList.length > key) {
1646
+ currentList.length = key;
1647
+ }
1648
+ });
1649
+ });
1650
+ }, 50);
1651
+ return s;
1652
+ }
1653
+ function trackDomField(element2, props, valueIsString, stringProperty = "innerHTML", getUpdateValue, context) {
1654
+ if (domSignals.has(element2)) {
1655
+ return;
1656
+ }
1657
+ const path2 = this.getBindingPath(element2);
1658
+ if (!path2) {
1659
+ throw new Error("Could not find binding path for element", { cause: element2 });
1660
+ }
1661
+ const s = signal2(element2);
1662
+ domSignals.set(element2, s);
1663
+ batch(() => {
1664
+ throttledEffect(() => {
1665
+ let updateValue;
1666
+ if (getUpdateValue) {
1667
+ for (const prop of props) {
1668
+ s[prop];
1669
+ }
1670
+ } else {
1671
+ updateValue = s[stringProperty];
1672
+ if (!valueIsString) {
1673
+ updateValue = getProperties(s, ...props);
1674
+ }
1675
+ }
1676
+ untracked(() => {
1677
+ const currentValue = getValueByPath(this.options.root, path2);
1678
+ if (getUpdateValue) {
1679
+ updateValue = getUpdateValue.call(this, s, currentValue);
1680
+ }
1681
+ if (typeof updateValue === "undefined") {
1682
+ return;
1683
+ }
1684
+ updateValue = this.extractValue?.(context, updateValue, currentValue) ?? updateValue;
1685
+ if (typeof updateValue === "undefined") {
1686
+ return;
1687
+ }
1688
+ if (valueIsString && !Object.is(currentValue, updateValue) && String(currentValue) === updateValue) {
1689
+ return;
1690
+ }
1691
+ setValueByPath(this.options.root, path2, updateValue, { replace: context?.replaceValue });
1692
+ });
1693
+ }, 50);
1694
+ });
1695
+ return s;
1696
+ }
1697
+ function findAttribute(el, attr) {
1698
+ return el.closest("[" + attr + "]")?.getAttribute(attr);
1699
+ }
1700
+
1701
+ // ../bind/src/transformers.mjs
1702
+ var escape_html = {
1703
+ render(context, next) {
1704
+ if (typeof context.value !== "string") {
1705
+ return next(context);
1706
+ }
1707
+ if (usesValueProperty(context.element)) {
1708
+ context.value = { value: context.value };
1709
+ } else {
1710
+ context.value = { innerHTML: escapeHTML(context.value) };
1711
+ }
1712
+ return next(context);
1713
+ },
1714
+ extract(context, next) {
1715
+ if (typeof context.value === "string") {
1716
+ context.value = unescapeHTML(context.value);
1717
+ } else if (context.value && typeof context.value === "object") {
1718
+ if (typeof context.value.innerHTML === "string") {
1719
+ context.value = unescapeHTML(context.value.innerHTML);
1720
+ } else if (typeof context.value.value === "string") {
1721
+ context.value = context.value.value;
1722
+ }
1723
+ }
1724
+ return next(context);
1725
+ }
1726
+ };
1727
+ function usesValueProperty(element2) {
1728
+ return element2?.tagName === "INPUT" || element2?.tagName === "TEXTAREA";
1729
+ }
1730
+ function escapeHTML(value) {
1731
+ return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1732
+ }
1733
+ function unescapeHTML(value) {
1734
+ const textarea = document.createElement("textarea");
1735
+ textarea.innerHTML = value;
1736
+ return textarea.value;
1737
+ }
1738
+ function fixed_content(context, next) {
1739
+ if (typeof context.value == "string") {
1740
+ context.value = {};
1741
+ } else {
1742
+ delete context.value?.innerHTML;
1743
+ }
1744
+ next(context);
1745
+ }
1746
+ var attributes = {
1747
+ render(context) {
1748
+ const names = getAttributeNames.call(this, context);
1749
+ setAttributes(context.element, context.value, names);
1750
+ if (context.edit) {
1751
+ trackDomField.call(
1752
+ this,
1753
+ context.element,
1754
+ names,
1755
+ false,
1756
+ "innerHTML",
1757
+ () => readAttributes(context.element, names),
1758
+ context
1759
+ );
1760
+ }
1761
+ return context;
1762
+ },
1763
+ extract(context, next) {
1764
+ const names = getAttributeNames.call(this, context);
1765
+ context.value = readAttributes(context.element, names);
1766
+ context.replaceValue = true;
1767
+ return next ? next(context) : context;
1768
+ }
1769
+ };
1770
+ function getAttributeNames(context) {
1771
+ const attribute = this.options.attribute + "-attributes";
1772
+ const configured = context.element.getAttribute(attribute);
1773
+ if (configured) {
1774
+ return configured.split(/[\s,]+/).filter(Boolean);
1775
+ }
1776
+ if (context.value && typeof context.value === "object" && !Array.isArray(context.value)) {
1777
+ return Object.keys(context.value);
1778
+ }
1779
+ if (context.currentValue && typeof context.currentValue === "object" && !Array.isArray(context.currentValue)) {
1780
+ return Object.keys(context.currentValue);
1781
+ }
1782
+ return [];
1783
+ }
1784
+ function setAttributes(element2, data, names) {
1785
+ if (!names.length || !data || typeof data !== "object" || Array.isArray(data)) {
1786
+ return;
1787
+ }
1788
+ for (const name of names) {
1789
+ const value = data[name];
1790
+ if (typeof value === "undefined" || value === null) {
1791
+ element2.removeAttribute(name);
1792
+ } else if (element2.getAttribute(name) !== "" + value) {
1793
+ element2.setAttribute(name, "" + value);
1794
+ }
1795
+ }
1796
+ }
1797
+ function readAttributes(element2, names) {
1798
+ const data = {};
1799
+ for (const name of names) {
1800
+ if (element2.hasAttribute(name)) {
1801
+ data[name] = element2.getAttribute(name);
1802
+ }
1803
+ }
1804
+ return data;
1805
+ }
1806
+
1807
+ // ../bind/src/index.mjs
1713
1808
  var SimplyBind = class {
1714
1809
  /**
1715
1810
  * @param Object options - a set of options for this instance, options may include:
@@ -1723,7 +1818,8 @@
1723
1818
  this.bindings = /* @__PURE__ */ new Map();
1724
1819
  const defaultTransformers = {
1725
1820
  escape_html,
1726
- fixed_content
1821
+ fixed_content,
1822
+ attributes
1727
1823
  };
1728
1824
  const defaultOptions = {
1729
1825
  container: document.body,
@@ -1754,10 +1850,11 @@
1754
1850
  this.options = Object.assign({}, defaultOptions, options);
1755
1851
  if (options.transformers) {
1756
1852
  this.options.transformers = Object.assign({}, defaultTransformers, options?.transformers);
1853
+ } else {
1854
+ this.options.transformers = defaultTransformers;
1757
1855
  }
1758
1856
  const attribute = this.options.attribute;
1759
1857
  const bindAttributes = [attribute + "-field", attribute + "-edit", attribute + "-list", attribute + "-map"];
1760
- const transformAttribute = attribute + "-transform";
1761
1858
  const getBindingAttribute = (el) => {
1762
1859
  const foundAttribute = bindAttributes.find((attr) => el.hasAttribute(attr));
1763
1860
  if (!foundAttribute) {
@@ -1805,24 +1902,8 @@
1805
1902
  throw new Error("no valid context attribute specified", context);
1806
1903
  break;
1807
1904
  }
1808
- if (context.element.hasAttribute(transformAttribute)) {
1809
- context.element.getAttribute(transformAttribute).split(" ").filter(Boolean).forEach((t) => {
1810
- if (this.options.transformers[t]) {
1811
- transformers.push(this.options.transformers[t]);
1812
- } else {
1813
- console.warn("No transformer with name " + t + " configured", { cause: context.element });
1814
- }
1815
- });
1816
- }
1817
- let next;
1818
- for (let transformer of transformers) {
1819
- next = /* @__PURE__ */ ((next2, transformer2) => {
1820
- return (context2) => {
1821
- return transformer2.call(this, context2, next2);
1822
- };
1823
- })(next, transformer);
1824
- }
1825
- next(context);
1905
+ transformers.push(...this.getNamedTransformers(context.element).map((transformer) => getTransformerPhase(transformer, "render")).filter(Boolean));
1906
+ runTransformerStack.call(this, transformers, context);
1826
1907
  };
1827
1908
  const applyBindings = (bindings2) => {
1828
1909
  for (let bindingEl of bindings2) {
@@ -1895,13 +1976,13 @@
1895
1976
  throw new Error("template must contain a single root node", { cause: template });
1896
1977
  }
1897
1978
  const attribute = this.options.attribute;
1898
- const attributes = [attribute + "-field", attribute + "-edit", attribute + "-list", attribute + "-map"];
1979
+ const attributes2 = [attribute + "-field", attribute + "-edit", attribute + "-list", attribute + "-map"];
1899
1980
  const bindings = clone2.querySelectorAll(`[${attribute}-field],[${attribute}-edit],[${attribute}-list],[${attribute}-map]`);
1900
1981
  for (let binding of bindings) {
1901
1982
  if (binding.tagName == "TEMPLATE") {
1902
1983
  continue;
1903
1984
  }
1904
- const attr = attributes.find((attr2) => binding.hasAttribute(attr2));
1985
+ const attr = attributes2.find((attr2) => binding.hasAttribute(attr2));
1905
1986
  let bind2 = binding.getAttribute(attr);
1906
1987
  bind2 = this.applyLinks(template.links, bind2);
1907
1988
  if (bind2.substring(0, ":root.".length) == ":root.") {
@@ -1967,18 +2048,50 @@
1967
2048
  * @return string The path referenced, or void
1968
2049
  */
1969
2050
  getBindingPath(el) {
1970
- const attributes = [
2051
+ const attributes2 = [
1971
2052
  this.options.attribute + "-field",
1972
2053
  this.options.attribute + "-edit",
1973
2054
  this.options.attribute + "-list",
1974
2055
  this.options.attribute + "-map"
1975
2056
  ];
1976
- for (let attr of attributes) {
2057
+ for (let attr of attributes2) {
1977
2058
  if (el.hasAttribute(attr)) {
1978
2059
  return el.getAttribute(attr);
1979
2060
  }
1980
2061
  }
1981
2062
  }
2063
+ getNamedTransformers(el) {
2064
+ const transformAttribute = this.options.attribute + "-transform";
2065
+ if (!el.hasAttribute(transformAttribute)) {
2066
+ return [];
2067
+ }
2068
+ return el.getAttribute(transformAttribute).split(" ").filter(Boolean).map((name) => {
2069
+ const transformer = this.options.transformers[name];
2070
+ if (!transformer) {
2071
+ console.warn("No transformer with name " + name + " configured", { cause: el });
2072
+ return null;
2073
+ }
2074
+ return transformer;
2075
+ }).filter(Boolean);
2076
+ }
2077
+ extractValue(context, value, currentValue) {
2078
+ if (!context?.element) {
2079
+ return value;
2080
+ }
2081
+ const transformers = this.getNamedTransformers(context.element).map((transformer) => getTransformerPhase(transformer, "extract")).filter(Boolean).reverse();
2082
+ if (!transformers.length) {
2083
+ return value;
2084
+ }
2085
+ delete context.replaceValue;
2086
+ const extractContext = Object.assign({}, context, {
2087
+ value,
2088
+ currentValue,
2089
+ originalValue: currentValue
2090
+ });
2091
+ runTransformerStack.call(this, transformers, extractContext);
2092
+ context.replaceValue = extractContext.replaceValue;
2093
+ return extractContext.value;
2094
+ }
1982
2095
  /**
1983
2096
  * Finds the first template from an array of templates that
1984
2097
  * matches the given value.
@@ -2042,6 +2155,26 @@
2042
2155
  function bind(options) {
2043
2156
  return new SimplyBind(options);
2044
2157
  }
2158
+ function getTransformerPhase(transformer, phase) {
2159
+ if (typeof transformer === "function") {
2160
+ return phase === "render" ? transformer : null;
2161
+ }
2162
+ if (transformer && typeof transformer[phase] === "function") {
2163
+ return transformer[phase];
2164
+ }
2165
+ return null;
2166
+ }
2167
+ function runTransformerStack(transformers, context) {
2168
+ let next = (context2) => context2;
2169
+ for (let transformer of transformers) {
2170
+ next = /* @__PURE__ */ ((next2, transformer2) => {
2171
+ return (context2) => {
2172
+ return transformer2.call(this, context2, next2);
2173
+ };
2174
+ })(next, transformer);
2175
+ }
2176
+ return next?.(context);
2177
+ }
2045
2178
  var tracking = /* @__PURE__ */ new Map();
2046
2179
  function track(el, context) {
2047
2180
  untrack(el);
@@ -2118,9 +2251,9 @@
2118
2251
  return curr;
2119
2252
  }
2120
2253
 
2121
- // src/model.mjs
2122
- var model_exports = {};
2123
- __export(model_exports, {
2254
+ // ../model/src/index.mjs
2255
+ var src_exports2 = {};
2256
+ __export(src_exports2, {
2124
2257
  columns: () => columns,
2125
2258
  filter: () => filter,
2126
2259
  model: () => model,
@@ -2272,12 +2405,35 @@
2272
2405
  }
2273
2406
  return function(data) {
2274
2407
  this.state.options.columns = columnOptions;
2408
+ const projections = /* @__PURE__ */ new WeakMap();
2275
2409
  return throttledEffect(() => {
2410
+ const visibleKeys = [];
2411
+ const visible = /* @__PURE__ */ new Set();
2412
+ const columns2 = this.state.options.columns;
2413
+ for (let key of Object.keys(columns2)) {
2414
+ if (columns2[key]?.visible !== false) {
2415
+ visibleKeys.push(key);
2416
+ visible.add(key);
2417
+ }
2418
+ }
2276
2419
  return data.current.map((input2) => {
2277
- let result = {};
2278
- for (let key of Object.keys(this.state.options.columns)) {
2279
- if (!this.state.options.columns[key]?.hidden) {
2280
- result[key] = input2[key] ?? null;
2420
+ const source = raw(input2);
2421
+ let result = source && typeof source === "object" ? projections.get(source) : null;
2422
+ if (!result) {
2423
+ result = {};
2424
+ if (source && typeof source === "object") {
2425
+ projections.set(source, result);
2426
+ }
2427
+ }
2428
+ for (let key of visibleKeys) {
2429
+ const value = input2?.[key] ?? null;
2430
+ if (result[key] !== value) {
2431
+ result[key] = value;
2432
+ }
2433
+ }
2434
+ for (let key of Object.keys(result)) {
2435
+ if (!visible.has(key)) {
2436
+ delete result[key];
2281
2437
  }
2282
2438
  }
2283
2439
  return result;
@@ -2376,7 +2532,7 @@
2376
2532
  customElements.define("simply-render", SimplyRender);
2377
2533
  }
2378
2534
 
2379
- // src/suggest.mjs
2535
+ // ../app/src/suggest.mjs
2380
2536
  function closest(name, options, { maxDistance = 2, minLength = 4 } = {}) {
2381
2537
  if (name.length < minLength) {
2382
2538
  return;
@@ -2414,7 +2570,7 @@
2414
2570
  return previous[b.length];
2415
2571
  }
2416
2572
 
2417
- // src/route.mjs
2573
+ // ../app/src/route.mjs
2418
2574
  function routes(options) {
2419
2575
  return new SimplyRoute(options);
2420
2576
  }
@@ -2759,7 +2915,7 @@
2759
2915
  return routeInfo;
2760
2916
  }
2761
2917
 
2762
- // src/path.mjs
2918
+ // ../app/src/path.mjs
2763
2919
  var path = {
2764
2920
  get(dataset, pointer) {
2765
2921
  if (typeof pointer !== "string") {
@@ -2807,7 +2963,7 @@
2807
2963
  };
2808
2964
  var path_default = path;
2809
2965
 
2810
- // src/command.mjs
2966
+ // ../app/src/command.mjs
2811
2967
  var commandState = /* @__PURE__ */ new WeakMap();
2812
2968
  var COMMAND_OPTIONS = [
2813
2969
  "commands",
@@ -3006,7 +3162,7 @@
3006
3162
  });
3007
3163
  }
3008
3164
 
3009
- // src/action.mjs
3165
+ // ../app/src/action.mjs
3010
3166
  var warnedUnknownActions = /* @__PURE__ */ new WeakMap();
3011
3167
  function actions(options) {
3012
3168
  if (options.app) {
@@ -3061,7 +3217,7 @@
3061
3217
  console.warn(`simplyflow/action: unknown action "${property}"${suffix}`);
3062
3218
  }
3063
3219
 
3064
- // src/shortcut.mjs
3220
+ // ../app/src/shortcut.mjs
3065
3221
  var shortcutState = /* @__PURE__ */ new WeakMap();
3066
3222
  var accesskeyState = /* @__PURE__ */ new WeakMap();
3067
3223
  var KEY = Object.freeze({
@@ -3191,7 +3347,7 @@
3191
3347
  accesskeyState.delete(accesskeyApi);
3192
3348
  }
3193
3349
 
3194
- // src/behavior.mjs
3350
+ // ../app/src/behavior.mjs
3195
3351
  var BEHAVIOR_SELECTOR = "[data-simply-behavior]";
3196
3352
  var SimplyBehaviors = class {
3197
3353
  constructor(options = {}) {
@@ -3288,7 +3444,7 @@
3288
3444
  return nodes;
3289
3445
  }
3290
3446
 
3291
- // src/include.mjs
3447
+ // ../app/src/include.mjs
3292
3448
  function throttle(callbackFunction, intervalTime) {
3293
3449
  let eventId = 0;
3294
3450
  return function throttledCallback(...params) {
@@ -3486,7 +3642,7 @@
3486
3642
  links: (links) => defaultInclude().includeLinks(Array.from(links))
3487
3643
  };
3488
3644
 
3489
- // src/app.mjs
3645
+ // ../app/src/index.mjs
3490
3646
  var APP_OPTIONS = [
3491
3647
  "container",
3492
3648
  "data",
@@ -3500,7 +3656,8 @@
3500
3656
  "commands",
3501
3657
  "shortcuts",
3502
3658
  "routes",
3503
- "actions"
3659
+ "actions",
3660
+ "transformers"
3504
3661
  ];
3505
3662
  var SimplyApp = class {
3506
3663
  constructor(options = {}) {
@@ -3517,6 +3674,7 @@
3517
3674
  this.onError = options.onError;
3518
3675
  this.components = options.components;
3519
3676
  this.baseURL = options.baseURL;
3677
+ this.transformers = options.transformers;
3520
3678
  installTemplates(this.container, options.templates);
3521
3679
  installStyles(this.container, options.styles);
3522
3680
  for (const key of Object.keys(options)) {
@@ -3529,6 +3687,7 @@
3529
3687
  case "onError":
3530
3688
  case "components":
3531
3689
  case "baseURL":
3690
+ case "transformers":
3532
3691
  break;
3533
3692
  case "commands":
3534
3693
  this.commands = commands({ app: this, container: this.container, commands: options.commands });
@@ -3557,7 +3716,8 @@
3557
3716
  this.binding = bind({
3558
3717
  root: this.data,
3559
3718
  container: this.container,
3560
- attribute: "data-simply"
3719
+ attribute: "data-simply",
3720
+ transformers: this.transformers
3561
3721
  });
3562
3722
  this.includes = includes({ container: this.container });
3563
3723
  this.accesskeys = accesskeys({ app: this, container: this.container });
@@ -3724,7 +3884,7 @@
3724
3884
  }
3725
3885
  }
3726
3886
 
3727
- // src/highlight.mjs
3887
+ // ../app/src/highlight.mjs
3728
3888
  function html(strings, ...values) {
3729
3889
  const outputArray = values.map(
3730
3890
  (value, index) => `${strings[index]}${value}`
@@ -3735,7 +3895,7 @@
3735
3895
  return html(strings, ...values);
3736
3896
  }
3737
3897
 
3738
- // src/flow.mjs
3898
+ // src/index.mjs
3739
3899
  if (!globalThis.simply) {
3740
3900
  globalThis.simply = {};
3741
3901
  }
@@ -3753,7 +3913,7 @@
3753
3913
  app,
3754
3914
  bind,
3755
3915
  model: modelApi,
3756
- state: state_exports,
3916
+ state: src_exports,
3757
3917
  signal,
3758
3918
  effect,
3759
3919
  batch,
@@ -3776,5 +3936,5 @@
3776
3936
  routes
3777
3937
  });
3778
3938
  delete globalThis.simply.advanced;
3779
- var flow_default = globalThis.simply;
3939
+ var index_default = globalThis.simply;
3780
3940
  })();