@optionfactory/ful 0.102.0 → 0.104.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/dist/ful.iife.js CHANGED
@@ -1,4 +1,4 @@
1
- var ful = (function (exports) {
1
+ var ful = (function (exports, ftl, TomSelect) {
2
2
  'use strict';
3
3
 
4
4
  class Base64 {
@@ -1034,473 +1034,158 @@ var ful = (function (exports) {
1034
1034
  }
1035
1035
  };
1036
1036
 
1037
- class Deferred {
1038
- constructor() {
1039
- this.promise = new Promise((resolve, reject) => {
1040
- this.reject = reject;
1041
- this.resolve = resolve;
1042
- });
1043
- }
1044
- }
1037
+ class Bindings {
1045
1038
 
1046
- class Fragments {
1047
1039
  /**
1048
- *
1049
- * @param {...string} html
1050
- * @returns
1040
+ * @param {{ [x: string]: any; }} obj
1041
+ * @param {string} prefix
1042
+ * @return {{ [x: string]: any; }}
1051
1043
  */
1052
- static fromHtml(...html) {
1053
- const el = document.createElement("template");
1054
- el.innerHTML = html.join("");
1055
- return document.adoptNode(el.content);
1056
- }
1057
- /**
1058
- *
1059
- * @param {DocumentFragment} fragment
1060
- * @returns
1061
- */
1062
- static toHtml(fragment) {
1063
- var el = document.createElement("template");
1064
- el.content.appendChild(fragment);
1065
- return el.innerHTML;
1066
- }
1067
- /**
1068
- *
1069
- * @param {...Node} nodes
1070
- * @returns
1071
- */
1072
- static from(...nodes) {
1073
- const fragment = new DocumentFragment();
1074
- fragment.append(...nodes);
1075
- return fragment;
1076
- }
1077
- /**
1078
- *
1079
- * @param {HTMLElement} el
1080
- * @returns
1081
- */
1082
- static fromChildNodes(el) {
1083
- const fragment = new DocumentFragment();
1084
- fragment.append(...el.childNodes);
1085
- return fragment;
1044
+ static flatten(obj, prefix) {
1045
+ return Object.keys(obj).reduce((acc, k) => {
1046
+ const pre = prefix.length ? prefix + '.' : '';
1047
+ if (typeof obj[k] === 'object' && obj[k] !== null) {
1048
+ Object.assign(acc, Bindings.flatten(obj[k], pre + k));
1049
+ } else {
1050
+ acc[pre + k] = obj[k];
1051
+ }
1052
+ return acc;
1053
+ }, {});
1086
1054
  }
1087
- }
1088
-
1089
- class Attributes {
1090
- static id = 0;
1055
+
1091
1056
  /**
1092
- *
1093
- * @param {string} prefix
1094
- * @returns
1057
+ * @param {any} result
1058
+ * @param {string} path
1059
+ * @param {any} value
1095
1060
  */
1096
- static uid(prefix) {
1097
- return `${prefix}-${++Attributes.id}`;
1061
+ static providePath(result, path, value) {
1062
+ const keys = path.split(".").map((k) => /^[0-9]+$/.test(k) ? +k : k);
1063
+ let current = result ?? {};
1064
+ let previous = null;
1065
+ for (let i = 0; ; ++i) {
1066
+ const ckey = keys[i];
1067
+ const pkey = keys[i - 1];
1068
+ if (Number.isInteger(ckey) && !Array.isArray(current)) {
1069
+ if (previous !== null) {
1070
+ previous[pkey] = current = [];
1071
+ } else {
1072
+ result = current = [];
1073
+ }
1074
+ }
1075
+ if (i === keys.length - 1) {
1076
+ //when value is undefined we only want to define the property if it's not defined
1077
+ current[ckey] = value !== undefined ? value : (ckey in current ? current[ckey] : null);
1078
+ return result;
1079
+ }
1080
+ if (current[ckey] === undefined) {
1081
+ current[ckey] = {};
1082
+ }
1083
+ previous = current;
1084
+ current = current[ckey];
1085
+ }
1098
1086
  }
1099
1087
  /**
1100
1088
  *
1101
- * @param {HTMLElement} el
1102
- * @param {string} k
1103
- * @param {string} v
1089
+ * @param {Element & {dataset?: any} & {checked?: boolean} & {value?: any}} el
1104
1090
  * @returns
1105
1091
  */
1106
- static defaultValue(el, k, v) {
1107
- if (!el.hasAttribute(k)) {
1108
- el.setAttribute(k, v);
1092
+ static extract(el) {
1093
+ if (el.getAttribute('type') === 'radio') {
1094
+ if (!el.checked) {
1095
+ return undefined;
1096
+ }
1097
+ return el.dataset['fulBindType'] === 'boolean' ? el.value === 'true' : el.value;
1109
1098
  }
1110
- return el.getAttribute(k);
1111
- }
1112
- /**
1113
- *
1114
- * @param {string} prefix
1115
- * @param {HTMLElement} from
1116
- * @param {HTMLElement} to
1117
- */
1118
- static forward(prefix, from, to) {
1119
- from.getAttributeNames()
1120
- .filter(a => a.startsWith(prefix))
1121
- .forEach(a => {
1122
- const target = a.substring(prefix.length);
1123
- if (target === 'class') {
1124
- const classes = from.getAttribute(prefix + "class")?.split(" ").filter(a => a.length) ?? [];
1125
- to.classList.add(...classes);
1126
- return;
1127
- }
1128
- // @ts-ignore
1129
- to.setAttribute(target, from.getAttribute(a));
1130
- });
1131
- }
1132
- /**
1133
- *
1134
- * @param {HTMLElement} el
1135
- * @param {string} attr
1136
- * @param {boolean} value
1137
- */
1138
- static toggle(el, attr, value) {
1139
- if (value) {
1140
- el.setAttribute(attr, '');
1141
- } else {
1142
- el.removeAttribute(attr);
1099
+ if (el.getAttribute('type') === 'checkbox') {
1100
+ return el.checked;
1143
1101
  }
1144
- }
1145
- static flip(el, attr) {
1146
- if (el.hasAttribute(attr)) {
1147
- el.removeAttribute(attr);
1148
- } else {
1149
- el.setAttribute(attr, '');
1102
+ if (el.dataset['fulBindType'] === 'boolean') {
1103
+ return !el.value ? null : el.value === 'true';
1150
1104
  }
1151
- }
1152
-
1153
- }
1154
-
1155
- class LightSlots {
1156
- /**
1157
- *
1158
- * @param {HTMLElement} el
1159
- * @returns the slots
1160
- */
1161
- static from(el) {
1162
- /** @type [string, Element][] */
1163
- const namedSlots = Array.from(el.children)
1164
- .filter(el => el.matches('[slot]'))
1165
- .map(el => {
1166
- el.remove();
1167
- const slot = el.getAttribute("slot");
1168
- el.removeAttribute("slot");
1169
- return [slot ?? 'unnamed', el];
1170
- });
1171
- const slots = {};
1172
- slots.default = new DocumentFragment();
1173
- slots.default.append(...el.childNodes);
1174
- for (const [name, el] of namedSlots) {
1175
- if (!(name in slots)) {
1176
- slots[name] = new DocumentFragment();
1177
- }
1178
- slots[name].append(el);
1105
+ if (el.tagName === 'INPUT' || el.tagName === 'SELECT') {
1106
+ return el.value === '' || el.value === undefined ? null : el.value;
1179
1107
  }
1180
- return slots;
1108
+ return el.value;
1181
1109
  }
1182
- }
1183
1110
 
1184
- class Nodes {
1185
- static isParsed(el) {
1186
- for (let c = el; c; c = c.parentNode) {
1187
- if (c.nextSibling) {
1188
- return true;
1111
+ static extractFrom(root, ignoredChildrenSelector){
1112
+ let result = {};
1113
+ for(const el of /** @type {NodeListOf<HTMLElement>} */(root.querySelectorAll('[name]'))){
1114
+ if (el.dataset['fulBindInclude'] === 'never') {
1115
+ continue;
1189
1116
  }
1190
- }
1191
- return false;
1192
- }
1193
- static queryChildren(node, selector) {
1194
- for (const c of node.children) {
1195
- if (c.matches(selector)) {
1196
- return c;
1117
+ if(ignoredChildrenSelector && el.dataset['fulBindInclude'] !== 'always' && el.closest(ignoredChildrenSelector) !== null){
1118
+ continue;
1197
1119
  }
1120
+ result = Bindings.providePath(result, /** @type {string} */(el.getAttribute('name')), Bindings.extract(el));
1198
1121
  }
1199
- return null;
1122
+ return result;
1200
1123
  }
1201
- static queryChildrenAll(node, selector) {
1202
- const r = [];
1203
- for (const c of node.children) {
1204
- if (c.matches(selector)) {
1205
- r.push(c);
1206
- }
1207
- }
1208
- return r;
1209
- }
1210
- }
1211
-
1212
- class TemplatesRegistry {
1213
- #idToFragment = {};
1214
- #idToTemplate = {};
1215
- #ec;
1216
- put(k, fragment) {
1217
- if (this.#ec) {
1218
- // @ts-ignore
1219
- this.#idToTemplate[k] = ftl.Template.fromFragment(fragment, this.#ec);
1124
+
1125
+ /**
1126
+ *
1127
+ * @param {Element & {checked?: boolean} & {value?: any}} el
1128
+ * @returns
1129
+ */
1130
+ static mutate(el, raw) {
1131
+ if (el.getAttribute('type') === 'radio') {
1132
+ el.checked = el.getAttribute('value') === raw;
1220
1133
  return;
1221
1134
  }
1222
- this.#idToFragment[k] = fragment;
1223
- }
1224
- get(k) {
1225
- if (!this.#ec) {
1226
- throw new Error("TemplatesRegistry is not configured");
1227
- }
1228
- const tpl = this.#idToTemplate[k];
1229
- if (!tpl) {
1230
- throw new Error(`missing template: '${k}'`);
1231
- }
1232
- return tpl;
1233
- }
1234
- configure(ec, ...data) {
1235
- this.#ec = ec;
1236
- for (const [k, fragment] of Object.entries(this.#idToFragment)) {
1237
- delete this.#idToFragment[k];
1238
- // @ts-ignore
1239
- this.#idToTemplate[k] = ftl.Template.fromFragment(fragment, ec, ...data);
1240
- }
1241
- }
1242
- }
1243
-
1244
-
1245
- class ElementsRegistry {
1246
- #templates;
1247
- #tagToclass;
1248
- #configured;
1249
- #id = 0;
1250
- constructor() {
1251
- this.#templates = new TemplatesRegistry();
1252
- this.#tagToclass = {};
1253
- }
1254
- defineTemplate(html) {
1255
- if (html === null || html === undefined) {
1256
- return undefined;
1257
- }
1258
- const name = `unnamed-${++this.#id}`;
1259
- this.#templates.put(name, Fragments.fromHtml(html));
1260
- return name;
1261
- }
1262
- define(tag, klass) {
1263
- if (!this.#configured) {
1264
- this.#tagToclass[tag] = klass;
1265
- return this;
1266
- }
1267
- customElements.define(tag, klass);
1268
- return this;
1269
- }
1270
- configure(ec, ...data) {
1271
- this.#templates.configure(ec, ...data);
1272
- for (const [tag, klass] of Object.entries(this.#tagToclass)) {
1273
- customElements.define(tag, klass);
1274
- delete this.#tagToclass[tag];
1275
- }
1276
- this.#configured = true;
1277
- }
1278
- template(k) {
1279
- if (k === null || k === undefined) {
1280
- return undefined;
1135
+ if (el.getAttribute('type') === 'checkbox') {
1136
+ el.checked = raw;
1137
+ return;
1281
1138
  }
1282
- return this.#templates.get(k);
1139
+ el.value = raw;
1283
1140
  }
1284
- }
1285
-
1286
- const elements = new ElementsRegistry();
1287
-
1288
1141
 
1289
- class UpgradeQueue {
1290
- #q = [];
1291
- constructor() {
1292
- document.addEventListener('DOMContentLoaded', this.dequeue.bind(this));
1293
- }
1294
- enqueue(el) {
1295
- if (!this.#q.length) {
1296
- requestAnimationFrame(this.dequeue.bind(this));
1142
+ static mutateIn(root, values){
1143
+ for (const [flattenedKey, value] of Object.entries(Bindings.flatten(values, ''))) {
1144
+ for(const el of root.querySelectorAll(`[name='${CSS.escape(flattenedKey)}']`)){
1145
+ Bindings.mutate(el, value);
1146
+ }
1297
1147
  }
1298
- this.#q.push(el);
1299
1148
  }
1300
- dequeue() {
1301
- this.#q.splice(0).forEach(el => el.upgrade());
1302
- }
1303
- }
1304
1149
 
1305
- const upgradeQueue = new UpgradeQueue();
1306
1150
 
1307
- const mappers = {
1308
- 'string': attr => attr,
1309
- 'number': attr => attr === null ? null : Number(attr),
1310
- 'presence': attr => attr !== null,
1311
- 'state': attr => attr !== null,
1312
- 'bool': attr => attr === 'true',
1313
- 'json': attr => JSON.parse(attr)
1314
- };
1315
-
1316
- const ParsedElement = (conf) => {
1317
- const { observed, template, slots } = conf ?? {};
1318
-
1319
- const attrsAndTypes = (observed ?? []).map(a => {
1320
- const [attr, maybeType] = a.split(":");
1321
- const type = maybeType?.trim() ?? 'string';
1322
- if (!(type in mappers)) {
1323
- throw new Error(`unsupported attribute type: ${type}`);
1324
- }
1325
- return [attr.trim(), type];
1326
- });
1327
-
1328
- const attrsAndMappers = attrsAndTypes.map(([attr, type]) => [attr, mappers[type]]);
1329
-
1330
- const attrToMapper = Object.fromEntries(attrsAndMappers);
1331
-
1332
- const templateId = elements.defineTemplate(template);
1333
-
1334
- const k = class extends HTMLElement {
1335
- static get observedAttributes() {
1336
- return Object.keys(attrToMapper);
1337
- }
1338
- #parsed;
1339
- #initialized;
1340
- #reflecting;
1341
- #internals;
1342
- constructor() {
1343
- super();
1344
- this.#internals = this.attachInternals();
1345
- }
1346
- get initialized() {
1347
- return this.#initialized;
1348
- }
1349
- get internals() {
1350
- return this.#internals;
1351
- }
1352
- connectedCallback() {
1353
- if (this.#parsed) {
1354
- return;
1355
- }
1356
- if (this.ownerDocument.readyState === 'complete' || Nodes.isParsed(this)) {
1357
- upgradeQueue.enqueue(this);
1358
- return;
1359
- }
1360
- this.ownerDocument.addEventListener('DOMContentLoaded', () => {
1361
- observer.disconnect();
1362
- upgradeQueue.enqueue(this);
1363
- });
1364
- const observer = new MutationObserver(() => {
1365
- if (!Nodes.isParsed(this)) {
1366
- return;
1367
- }
1368
- observer.disconnect();
1369
- upgradeQueue.enqueue(this);
1151
+ static errors(root, es, invalidClass){
1152
+ const fieldErrors = es.filter(e => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT');
1153
+ const globalErrors = es.filter(e => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
1154
+ root.querySelectorAll(`.${CSS.escape(invalidClass)}`).forEach(el => el.classList.remove(invalidClass));
1155
+ root.querySelectorAll("ful-errors").forEach(el => {
1156
+ el.replaceChildren();
1157
+ el.setAttribute('hidden', '');
1158
+ });
1159
+ fieldErrors.forEach(e => {
1160
+ const name = e.context.replace("[", ".").replace("].", ".");
1161
+ const validationTargetsSelector = `[name='${CSS.escape(name)}'] [ful-validation-target],[name='${CSS.escape(name)}']:not(:has([ful-validation-target]))`;
1162
+ root.querySelectorAll(validationTargetsSelector).forEach(input => input.classList.add(invalidClass));
1163
+ const fieldErrorsSelector = `ful-field-error[field='${CSS.escape(name)}']`;
1164
+ root.querySelectorAll(fieldErrorsSelector).forEach(el => {
1165
+ const hel = /** @type HTMLElement} */ (el);
1166
+ hel.innerText = e.reason;
1370
1167
  });
1371
- // @ts-ignore
1372
- observer.observe(this.parentNode, { childList: true, subtree: true });
1373
- }
1374
- attributeChangedCallback(attr, oldValue, newValue) {
1375
- if (!this.#parsed || oldValue === newValue) {
1376
- return;
1377
- }
1378
- if (this.#reflecting) {
1379
- return;
1380
- }
1381
- const mapper = attrToMapper[attr];
1382
- this[attr] = mapper(newValue);
1383
- }
1384
- reflect(fn) {
1385
- this.#reflecting = true;
1386
- try {
1387
- fn();
1388
- } finally {
1389
- this.#reflecting = false;
1390
- }
1391
- }
1392
- async upgrade() {
1393
- if (this.#parsed) {
1394
- return;
1395
- }
1396
- this.#parsed = true;
1397
- // @ts-ignore
1398
- await this.render(elements.template(templateId), slots ? LightSlots.from(this) : undefined);
1399
-
1400
- for (const [attr, mapper] of attrsAndMappers) {
1401
- if (this.hasAttribute(attr)) {
1402
- this[attr] = mapper(this.getAttribute(attr));
1403
- }
1404
- }
1405
- this.#initialized = true;
1406
- }
1407
- };
1408
-
1409
- for (const [attr, type] of attrsAndTypes.filter(([a, t]) => t === 'state')) {
1410
- Object.defineProperty(k.prototype, attr, {
1411
- enumerable: true,
1412
- configurable: true,
1413
- get() {
1414
- return this.internals.states ? this.internals.states.has(`--${attr}`) : this.hasAttribute(attr);
1415
- },
1416
- set(value) {
1417
- this.internals.states?.[value ? 'add' : 'delete'](`--${attr}`);
1418
- this.reflect(() => Attributes.toggle(this, attr, value));
1419
- }
1420
1168
  });
1421
- }
1422
-
1423
- return k;
1424
- };
1425
-
1426
- function flatten(obj, prefix) {
1427
- return Object.keys(obj).reduce((acc, k) => {
1428
- const pre = prefix.length ? prefix + '.' : '';
1429
- if (typeof obj[k] === 'object' && obj[k] !== null) {
1430
- Object.assign(acc, flatten(obj[k], pre + k));
1431
- } else {
1432
- acc[pre + k] = obj[k];
1433
- }
1434
- return acc;
1435
- }, {});
1436
- }
1437
-
1438
- function providePath(result, path, value) {
1439
- const keys = path.split(".").map((k) => k.match(/^[0-9]+$/) ? +k : k);
1440
- let current = result;
1441
- let previous = null;
1442
- for (let i = 0; ; ++i) {
1443
- const ckey = keys[i];
1444
- const pkey = keys[i - 1];
1445
- if (Number.isInteger(ckey) && !Array.isArray(current)) {
1446
- if (previous !== null) {
1447
- previous[pkey] = current = [];
1448
- } else {
1449
- result = current = [];
1169
+ root.querySelectorAll("ful-errors").forEach(el => {
1170
+ const hel = /** @type HTMLElement} */ (el);
1171
+ hel.innerText = globalErrors.map(e => e.reason).join("\n");
1172
+ if (globalErrors.length !== 0) {
1173
+ el.removeAttribute('hidden');
1450
1174
  }
1451
- }
1452
- if (i === keys.length - 1) {
1453
- //when value is undefined we only want to define the property if it's not defined
1454
- current[ckey] = value !== undefined ? value : (ckey in current ? current[ckey] : null);
1455
- return result;
1456
- }
1457
- if (current[ckey] === undefined) {
1458
- current[ckey] = {};
1459
- }
1460
- previous = current;
1461
- current = current[ckey];
1462
- }
1463
- }
1175
+ });
1464
1176
 
1465
- function extract(el) {
1466
- if (el.getAttribute('type') === 'radio') {
1467
- if (!el.checked) {
1468
- return undefined;
1469
- }
1470
- return el.dataset['fulBindType'] === 'boolean' ? el.value === 'true' : el.value;
1471
- }
1472
- if (el.getAttribute('type') === 'checkbox') {
1473
- return el.checked;
1474
- }
1475
- if (el.dataset['fulBindType'] === 'boolean') {
1476
- return !el.value ? null : el.value === 'true';
1477
- }
1478
- if (el.tagName === 'INPUT' || el.tagName === 'SELECT'){
1479
- return el.value === '' || el.value === undefined ? null : el.value;
1480
- }
1481
- return el.value;
1482
- }
1483
1177
 
1484
- function mutate(el, raw) {
1485
- if (el.getAttribute('type') === 'radio') {
1486
- el.checked = el.getAttribute('value') === raw;
1487
- return;
1488
- }
1489
- if (el.getAttribute('type') === 'checkbox') {
1490
- el.checked = raw;
1491
- return;
1492
1178
  }
1493
- el.value = raw;
1494
1179
  }
1495
1180
 
1496
- class Form extends ParsedElement() {
1181
+ class Form extends ftl.ParsedElement() {
1497
1182
  static IGNORED_CHILDREN_SELECTOR = '.d-none, [hidden]';
1498
1183
  static SCROLL_OFFSET = 50;
1499
1184
  static INVALID_CLASS = 'is-invalid';
1500
1185
  submitter;
1501
1186
  render() {
1502
1187
  const form = document.createElement('form');
1503
- Attributes.forward('form-', this, form);
1188
+ ftl.Attributes.forward('form-', this, form);
1504
1189
  form.replaceChildren(...this.childNodes);
1505
1190
  form.addEventListener('submit', async (e) => {
1506
1191
  e.preventDefault();
@@ -1508,12 +1193,12 @@ var ful = (function (exports) {
1508
1193
  await this.submitter?.(this.values, this);
1509
1194
  });
1510
1195
  });
1511
- if(this.hasAttribute("clear-invalid-on-change")){
1196
+ if (this.hasAttribute("clear-invalid-on-change")) {
1512
1197
  this.addEventListener('change', evt => {
1513
1198
  const target = /** @type HTMLElement */ (evt.target);
1514
1199
  target?.querySelectorAll(`.${CSS.escape(Form.INVALID_CLASS)}`).forEach(el => {
1515
1200
  el.classList.remove(Form.INVALID_CLASS);
1516
- });
1201
+ });
1517
1202
  });
1518
1203
  }
1519
1204
  this.replaceChildren(form);
@@ -1542,7 +1227,7 @@ var ful = (function (exports) {
1542
1227
  this.spinner(true);
1543
1228
  try {
1544
1229
  await this.remoting(fn);
1545
- } catch(e) {
1230
+ } catch (e) {
1546
1231
  this.spinner(false);
1547
1232
  throw e;
1548
1233
  }
@@ -1556,48 +1241,14 @@ var ful = (function (exports) {
1556
1241
  }
1557
1242
  }
1558
1243
  set values(vs) {
1559
- for (const [flattenedKey, value] of Object.entries(flatten(vs, ''))) {
1560
- this.querySelectorAll(`[name='${CSS.escape(flattenedKey)}']`).forEach(el => mutate(el, value));
1561
- }
1244
+ Bindings.mutateIn(this, vs);
1562
1245
  }
1563
1246
  get values() {
1564
- return Array.from(/** @type {NodeListOf<HTMLElement>} */ (this.querySelectorAll('[name]')))
1565
- .filter(el => {
1566
- if (el.dataset['fulBindInclude'] === 'never') {
1567
- return false;
1568
- }
1569
- return el.dataset['fulBindInclude'] === 'always' || el.closest(Form.IGNORED_CHILDREN_SELECTOR) === null;
1570
- })
1571
- .reduce((result, el) => {
1572
- return providePath(result, el.getAttribute('name'), extract(el));
1573
- }, {});
1247
+ return Bindings.extractFrom(this, Form.IGNORED_CHILDREN_SELECTOR);
1574
1248
  }
1575
1249
  set errors(es) {
1576
- const fieldErrors = es.filter(e => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT');
1577
- const globalErrors = es.filter(e => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
1578
- this.querySelectorAll(`.${Form.INVALID_CLASS}`).forEach(el => el.classList.remove(Form.INVALID_CLASS));
1579
- this.querySelectorAll("ful-errors").forEach(el => {
1580
- el.replaceChildren();
1581
- el.setAttribute('hidden', '');
1582
- });
1583
- fieldErrors.forEach(e => {
1584
- const name = e.context.replace("[", ".").replace("].", ".");
1585
- const validationTargetsSelector = `[name='${CSS.escape(name)}'] [ful-validation-target],[name='${CSS.escape(name)}']:not(:has([ful-validation-target]))`;
1586
- this.querySelectorAll(validationTargetsSelector).forEach(input => input.classList.add(Form.INVALID_CLASS));
1587
- const fieldErrorsSelector = `ful-field-error[field='${CSS.escape(name)}']`;
1588
- this.querySelectorAll(fieldErrorsSelector).forEach(el => {
1589
- const hel = /** @type HTMLElement} */ (el);
1590
- hel.innerText = e.reason;
1591
- });
1592
- });
1593
- this.querySelectorAll("ful-errors").forEach(el => {
1594
- const hel = /** @type HTMLElement} */ (el);
1595
- hel.innerText = globalErrors.map(e => e.reason).join("\n");
1596
- if (globalErrors.length !== 0) {
1597
- el.removeAttribute('hidden');
1598
- }
1599
- });
1600
- if (!this.hasAttribute('scroll-on-error')) {
1250
+ Bindings.errors(this, es, Form.INVALID_CLASS);
1251
+ if (es.length == 0 || !this.hasAttribute('scroll-on-error')) {
1601
1252
  return;
1602
1253
  }
1603
1254
  const ys = Array.from(this.querySelectorAll(`ful-errors:not([hidden]), [ful-validated-field]:has(.${Form.INVALID_CLASS}) ful-field-error`))
@@ -1641,23 +1292,23 @@ var ful = (function (exports) {
1641
1292
  }
1642
1293
  }));
1643
1294
  });
1644
- const id = input.getAttribute('id') ?? el.getAttribute('input-id') ?? Attributes.uid('ful-input');
1645
- Attributes.forward('input-', el, slots.input);
1646
- Attributes.defaultValue(slots.input, "id", id);
1647
- Attributes.defaultValue(slots.input, "type", "text");
1648
- Attributes.defaultValue(slots.input, "placeholder", " ");
1295
+ const id = input.getAttribute('id') ?? el.getAttribute('input-id') ?? ftl.Attributes.uid('ful-input');
1296
+ ftl.Attributes.forward('input-', el, slots.input);
1297
+ ftl.Attributes.defaultValue(slots.input, "id", id);
1298
+ ftl.Attributes.defaultValue(slots.input, "type", "text");
1299
+ ftl.Attributes.defaultValue(slots.input, "placeholder", " ");
1649
1300
  const name = el.getAttribute('name');
1650
- return template.render(el, { id, name, slots });
1301
+ return template.withOverlay(el, { id, name, slots }).render();
1651
1302
  };
1652
1303
 
1653
- class Input extends ParsedElement({
1304
+ class Input extends ftl.ParsedElement({
1654
1305
  observed: ['value'],
1655
1306
  slots: true,
1656
1307
  template: INPUT_TEMPLATE
1657
1308
  }){
1658
1309
  input;
1659
- render(template, slots) {
1660
- const fragment = makeInputFragment(this, template, slots);
1310
+ render({slots}) {
1311
+ const fragment = makeInputFragment(this, this.template(), slots);
1661
1312
  this.replaceChildren(fragment);
1662
1313
  }
1663
1314
  get value() {
@@ -1673,7 +1324,7 @@ var ful = (function (exports) {
1673
1324
  * <link href="tom-select.bootstrap5.css" rel="stylesheet" />
1674
1325
  */
1675
1326
 
1676
- class Select extends ParsedElement({
1327
+ class Select extends ftl.ParsedElement({
1677
1328
  observed: ["value"],
1678
1329
  slots: true,
1679
1330
  template: `
@@ -1693,11 +1344,12 @@ var ful = (function (exports) {
1693
1344
  }) {
1694
1345
  shouldLoad;
1695
1346
  _unwrappedRemoteLoad;
1347
+ ts;
1696
1348
  constructor(tsConfig) {
1697
1349
  super();
1698
1350
  this.tsConfig = tsConfig;
1699
1351
  }
1700
- render(template, slots) {
1352
+ render({slots}) {
1701
1353
  const type = this.getAttribute("type") ?? 'local';
1702
1354
  const remote = type != 'local';
1703
1355
  const loadOnce = this.getAttribute('load') != 'always';
@@ -1707,15 +1359,15 @@ var ful = (function (exports) {
1707
1359
  })();
1708
1360
  input.setAttribute('ful-validation-target', '');
1709
1361
 
1710
- const id = input.getAttribute('id') ?? this.getAttribute('input-id') ?? Attributes.uid('ful-select');
1362
+ const id = input.getAttribute('id') ?? this.getAttribute('input-id') ?? ftl.Attributes.uid('ful-select');
1711
1363
  const tsId = `${id}-ts-control`;
1712
- Attributes.forward('input-', this, input);
1713
- Attributes.defaultValue(input, "id", id);
1714
- Attributes.defaultValue(input, "placeholder", " ");
1364
+ ftl.Attributes.forward('input-', this, input);
1365
+ ftl.Attributes.defaultValue(input, "id", id);
1366
+ ftl.Attributes.defaultValue(input, "placeholder", " ");
1715
1367
 
1716
1368
  //tomselect needs the input to have a parent.
1717
1369
  //se we move the input to a fragment
1718
- slots.input = Fragments.from(input);
1370
+ slots.input = ftl.Fragments.from(input);
1719
1371
 
1720
1372
  this.loaded = !remote;
1721
1373
 
@@ -1744,7 +1396,6 @@ var ful = (function (exports) {
1744
1396
  }
1745
1397
  callback(data);
1746
1398
  };
1747
- // @ts-ignore
1748
1399
  this.ts = new TomSelect(input, Object.assign(remote ? {
1749
1400
  preload: 'focus',
1750
1401
  load: this._unwrappedRemoteLoad,
@@ -1764,7 +1415,7 @@ var ful = (function (exports) {
1764
1415
  evt.stopPropagation();
1765
1416
  });
1766
1417
  input.remove();
1767
- template.renderTo(this, { id, tsId, name, input, slots });
1418
+ this.template().withOverlay({ id, tsId, name, input, slots }).renderTo(this);
1768
1419
  }
1769
1420
  #loader;
1770
1421
  set loader(l) {
@@ -1789,8 +1440,8 @@ var ful = (function (exports) {
1789
1440
  }
1790
1441
  }
1791
1442
 
1792
- class RadioGroup extends ParsedElement({
1793
- observed: ['value', 'disabled:state'],
1443
+ class RadioGroup extends ftl.ParsedElement({
1444
+ observed: ['value', 'disabled:presence'],
1794
1445
  slots: true,
1795
1446
  template: `
1796
1447
  <fieldset ful-validated-field>
@@ -1815,14 +1466,14 @@ var ful = (function (exports) {
1815
1466
  </fieldset>
1816
1467
  `
1817
1468
  }) {
1818
- render(template, slots) {
1819
- const name = this.getAttribute('name') ?? Attributes.uid('ful-radiogroup');
1469
+ render({slots}) {
1470
+ const name = this.getAttribute('name') ?? ftl.Attributes.uid('ful-radiogroup');
1820
1471
  const radioEls = Array.from(slots.default.querySelectorAll('ful-radio'));
1821
1472
  const inputsAndLabels = radioEls.map(el => {
1822
1473
  const input = document.createElement('input');
1823
1474
  input.setAttribute('type', 'radio');
1824
- Attributes.forward('input-', this, input);
1825
- Attributes.forward('', el, input);
1475
+ ftl.Attributes.forward('input-', this, input);
1476
+ ftl.Attributes.forward('', el, input);
1826
1477
  input.setAttribute('name', `${name}-ignore`);
1827
1478
  input.setAttribute('ful-validation-target', '');
1828
1479
  input.dataset['fulBindInclude'] = 'never';
@@ -1837,13 +1488,19 @@ var ful = (function (exports) {
1837
1488
  }
1838
1489
  }));
1839
1490
  });
1840
- const label = Fragments.fromChildNodes(el);
1491
+ const label = ftl.Fragments.fromChildNodes(el);
1841
1492
  return [input, label];
1842
1493
  });
1843
1494
 
1844
1495
  radioEls.forEach(el => el.remove());
1845
- template.renderTo(this, { name, slots, inputsAndLabels });
1496
+ this.template().withOverlay({ name, slots, inputsAndLabels }).renderTo(this);
1497
+ }
1498
+ get disabled() {
1499
+ return this.hasAttribute('disabled');
1846
1500
  }
1501
+ set disabled(value) {
1502
+ this.reflect(() => ftl.Attributes.toggle(this, 'disabled', value));
1503
+ }
1847
1504
  get value() {
1848
1505
  /** @type {HTMLInputElement|null} */
1849
1506
  const checked = this.querySelector('input[type=radio]:checked');
@@ -1851,10 +1508,8 @@ var ful = (function (exports) {
1851
1508
  }
1852
1509
  set value(value) {
1853
1510
  if (value === null) {
1854
- /** @type {HTMLInputElement[]} */
1855
1511
  this.querySelectorAll(`input[type=radio]`).forEach(el => {
1856
- // @ts-ignore
1857
- el.checked = false;
1512
+ (/** @type {HTMLInputElement} */(el)).checked = false;
1858
1513
  });
1859
1514
  return;
1860
1515
  }
@@ -1866,7 +1521,7 @@ var ful = (function (exports) {
1866
1521
  }
1867
1522
  }
1868
1523
 
1869
- class Spinner extends ParsedElement({
1524
+ class Spinner extends ftl.ParsedElement({
1870
1525
  slots: true,
1871
1526
  template: `
1872
1527
  <div class="ful-spinner-wrapper">
@@ -1875,42 +1530,34 @@ var ful = (function (exports) {
1875
1530
  </div>
1876
1531
  `
1877
1532
  }) {
1878
- render(template, slots) {
1879
- template.renderTo(this, { slots });
1533
+ render({slots}) {
1534
+ this.template().withOverlay({ slots }).renderTo(this);
1880
1535
  }
1881
1536
  }
1882
1537
 
1883
- exports.Attributes = Attributes;
1884
1538
  exports.AuthorizationCodeFlow = AuthorizationCodeFlow;
1885
1539
  exports.AuthorizationCodeFlowInterceptor = AuthorizationCodeFlowInterceptor;
1886
1540
  exports.AuthorizationCodeFlowSession = AuthorizationCodeFlowSession;
1887
1541
  exports.Base64 = Base64;
1888
- exports.Deferred = Deferred;
1889
- exports.ElementsRegistry = ElementsRegistry;
1542
+ exports.Bindings = Bindings;
1890
1543
  exports.Failure = Failure;
1891
1544
  exports.Form = Form;
1892
- exports.Fragments = Fragments;
1893
1545
  exports.Hex = Hex;
1894
1546
  exports.HttpClient = HttpClient;
1895
1547
  exports.HttpClientError = HttpClientError;
1896
1548
  exports.INPUT_TEMPLATE = INPUT_TEMPLATE;
1897
1549
  exports.Input = Input;
1898
- exports.LightSlots = LightSlots;
1899
1550
  exports.LocalStorage = LocalStorage;
1900
1551
  exports.MediaType = MediaType;
1901
- exports.Nodes = Nodes;
1902
- exports.ParsedElement = ParsedElement;
1903
1552
  exports.RadioGroup = RadioGroup;
1904
1553
  exports.Select = Select;
1905
1554
  exports.SessionStorage = SessionStorage;
1906
1555
  exports.Spinner = Spinner;
1907
- exports.TemplatesRegistry = TemplatesRegistry;
1908
1556
  exports.VersionedStorage = VersionedStorage;
1909
- exports.elements = elements;
1910
1557
  exports.makeInputFragment = makeInputFragment;
1911
1558
  exports.timing = timing;
1912
1559
 
1913
1560
  return exports;
1914
1561
 
1915
- })({});
1562
+ })({}, ftl, window?.TomSelect || {});
1916
1563
  //# sourceMappingURL=ful.iife.js.map