@optionfactory/ful 0.102.0 → 0.103.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 {
@@ -1043,464 +1043,158 @@ var ful = (function (exports) {
1043
1043
  }
1044
1044
  }
1045
1045
 
1046
- class Fragments {
1047
- /**
1048
- *
1049
- * @param {...string} html
1050
- * @returns
1051
- */
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;
1086
- }
1087
- }
1046
+ class Bindings {
1088
1047
 
1089
- class Attributes {
1090
- static id = 0;
1091
1048
  /**
1092
- *
1093
- * @param {string} prefix
1094
- * @returns
1049
+ * @param {{ [x: string]: any; }} obj
1050
+ * @param {string} prefix
1051
+ * @return {{ [x: string]: any; }}
1095
1052
  */
1096
- static uid(prefix) {
1097
- return `${prefix}-${++Attributes.id}`;
1098
- }
1099
- /**
1100
- *
1101
- * @param {HTMLElement} el
1102
- * @param {string} k
1103
- * @param {string} v
1104
- * @returns
1105
- */
1106
- static defaultValue(el, k, v) {
1107
- if (!el.hasAttribute(k)) {
1108
- el.setAttribute(k, v);
1109
- }
1110
- return el.getAttribute(k);
1053
+ static flatten(obj, prefix) {
1054
+ return Object.keys(obj).reduce((acc, k) => {
1055
+ const pre = prefix.length ? prefix + '.' : '';
1056
+ if (typeof obj[k] === 'object' && obj[k] !== null) {
1057
+ Object.assign(acc, Bindings.flatten(obj[k], pre + k));
1058
+ } else {
1059
+ acc[pre + k] = obj[k];
1060
+ }
1061
+ return acc;
1062
+ }, {});
1111
1063
  }
1064
+
1112
1065
  /**
1113
- *
1114
- * @param {string} prefix
1115
- * @param {HTMLElement} from
1116
- * @param {HTMLElement} to
1066
+ * @param {any} result
1067
+ * @param {string} path
1068
+ * @param {any} value
1117
1069
  */
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;
1070
+ static providePath(result, path, value) {
1071
+ const keys = path.split(".").map((k) => /^[0-9]+$/.test(k) ? +k : k);
1072
+ let current = result ?? {};
1073
+ let previous = null;
1074
+ for (let i = 0; ; ++i) {
1075
+ const ckey = keys[i];
1076
+ const pkey = keys[i - 1];
1077
+ if (Number.isInteger(ckey) && !Array.isArray(current)) {
1078
+ if (previous !== null) {
1079
+ previous[pkey] = current = [];
1080
+ } else {
1081
+ result = current = [];
1127
1082
  }
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);
1143
- }
1144
- }
1145
- static flip(el, attr) {
1146
- if (el.hasAttribute(attr)) {
1147
- el.removeAttribute(attr);
1148
- } else {
1149
- el.setAttribute(attr, '');
1150
- }
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
1083
  }
1178
- slots[name].append(el);
1179
- }
1180
- return slots;
1181
- }
1182
- }
1183
-
1184
- class Nodes {
1185
- static isParsed(el) {
1186
- for (let c = el; c; c = c.parentNode) {
1187
- if (c.nextSibling) {
1188
- return true;
1084
+ if (i === keys.length - 1) {
1085
+ //when value is undefined we only want to define the property if it's not defined
1086
+ current[ckey] = value !== undefined ? value : (ckey in current ? current[ckey] : null);
1087
+ return result;
1189
1088
  }
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;
1089
+ if (current[ckey] === undefined) {
1090
+ current[ckey] = {};
1197
1091
  }
1092
+ previous = current;
1093
+ current = current[ckey];
1198
1094
  }
1199
- return null;
1200
1095
  }
1201
- static queryChildrenAll(node, selector) {
1202
- const r = [];
1203
- for (const c of node.children) {
1204
- if (c.matches(selector)) {
1205
- r.push(c);
1096
+ /**
1097
+ *
1098
+ * @param {Element & {dataset?: any} & {checked?: boolean} & {value?: any}} el
1099
+ * @returns
1100
+ */
1101
+ static extract(el) {
1102
+ if (el.getAttribute('type') === 'radio') {
1103
+ if (!el.checked) {
1104
+ return undefined;
1206
1105
  }
1106
+ return el.dataset['fulBindType'] === 'boolean' ? el.value === 'true' : el.value;
1207
1107
  }
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);
1220
- return;
1221
- }
1222
- this.#idToFragment[k] = fragment;
1223
- }
1224
- get(k) {
1225
- if (!this.#ec) {
1226
- throw new Error("TemplatesRegistry is not configured");
1108
+ if (el.getAttribute('type') === 'checkbox') {
1109
+ return el.checked;
1227
1110
  }
1228
- const tpl = this.#idToTemplate[k];
1229
- if (!tpl) {
1230
- throw new Error(`missing template: '${k}'`);
1111
+ if (el.dataset['fulBindType'] === 'boolean') {
1112
+ return !el.value ? null : el.value === 'true';
1231
1113
  }
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);
1114
+ if (el.tagName === 'INPUT' || el.tagName === 'SELECT') {
1115
+ return el.value === '' || el.value === undefined ? null : el.value;
1240
1116
  }
1117
+ return el.value;
1241
1118
  }
1242
- }
1243
-
1244
1119
 
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;
1120
+ static extractFrom(root, ignoredChildrenSelector){
1121
+ let result = {};
1122
+ for(const el of /** @type {NodeListOf<HTMLElement>} */(root.querySelectorAll('[name]'))){
1123
+ if (el.dataset['fulBindInclude'] === 'never') {
1124
+ continue;
1125
+ }
1126
+ if(ignoredChildrenSelector && el.dataset['fulBindInclude'] !== 'always' && el.closest(ignoredChildrenSelector) !== null){
1127
+ continue;
1128
+ }
1129
+ result = Bindings.providePath(result, /** @type {string} */(el.getAttribute('name')), Bindings.extract(el));
1266
1130
  }
1267
- customElements.define(tag, klass);
1268
- return this;
1131
+ return result;
1269
1132
  }
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];
1133
+
1134
+ /**
1135
+ *
1136
+ * @param {Element & {checked?: boolean} & {value?: any}} el
1137
+ * @returns
1138
+ */
1139
+ static mutate(el, raw) {
1140
+ if (el.getAttribute('type') === 'radio') {
1141
+ el.checked = el.getAttribute('value') === raw;
1142
+ return;
1275
1143
  }
1276
- this.#configured = true;
1277
- }
1278
- template(k) {
1279
- if (k === null || k === undefined) {
1280
- return undefined;
1144
+ if (el.getAttribute('type') === 'checkbox') {
1145
+ el.checked = raw;
1146
+ return;
1281
1147
  }
1282
- return this.#templates.get(k);
1148
+ el.value = raw;
1283
1149
  }
1284
- }
1285
1150
 
1286
- const elements = new ElementsRegistry();
1287
-
1288
-
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));
1151
+ static mutateIn(root, values){
1152
+ for (const [flattenedKey, value] of Object.entries(Bindings.flatten(values, ''))) {
1153
+ for(const el of root.querySelectorAll(`[name='${CSS.escape(flattenedKey)}']`)){
1154
+ Bindings.mutate(el, value);
1155
+ }
1297
1156
  }
1298
- this.#q.push(el);
1299
- }
1300
- dequeue() {
1301
- this.#q.splice(0).forEach(el => el.upgrade());
1302
1157
  }
1303
- }
1304
-
1305
- const upgradeQueue = new UpgradeQueue();
1306
-
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
1158
 
1332
- const templateId = elements.defineTemplate(template);
1333
1159
 
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);
1160
+ static errors(root, es, invalidClass){
1161
+ const fieldErrors = es.filter(e => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT');
1162
+ const globalErrors = es.filter(e => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
1163
+ root.querySelectorAll(`.${CSS.escape(invalidClass)}`).forEach(el => el.classList.remove(invalidClass));
1164
+ root.querySelectorAll("ful-errors").forEach(el => {
1165
+ el.replaceChildren();
1166
+ el.setAttribute('hidden', '');
1167
+ });
1168
+ fieldErrors.forEach(e => {
1169
+ const name = e.context.replace("[", ".").replace("].", ".");
1170
+ const validationTargetsSelector = `[name='${CSS.escape(name)}'] [ful-validation-target],[name='${CSS.escape(name)}']:not(:has([ful-validation-target]))`;
1171
+ root.querySelectorAll(validationTargetsSelector).forEach(input => input.classList.add(invalidClass));
1172
+ const fieldErrorsSelector = `ful-field-error[field='${CSS.escape(name)}']`;
1173
+ root.querySelectorAll(fieldErrorsSelector).forEach(el => {
1174
+ const hel = /** @type HTMLElement} */ (el);
1175
+ hel.innerText = e.reason;
1370
1176
  });
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
1177
  });
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 = [];
1178
+ root.querySelectorAll("ful-errors").forEach(el => {
1179
+ const hel = /** @type HTMLElement} */ (el);
1180
+ hel.innerText = globalErrors.map(e => e.reason).join("\n");
1181
+ if (globalErrors.length !== 0) {
1182
+ el.removeAttribute('hidden');
1450
1183
  }
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
- }
1184
+ });
1464
1185
 
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
1186
 
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
1187
  }
1493
- el.value = raw;
1494
1188
  }
1495
1189
 
1496
- class Form extends ParsedElement() {
1190
+ class Form extends ftl.ParsedElement() {
1497
1191
  static IGNORED_CHILDREN_SELECTOR = '.d-none, [hidden]';
1498
1192
  static SCROLL_OFFSET = 50;
1499
1193
  static INVALID_CLASS = 'is-invalid';
1500
1194
  submitter;
1501
1195
  render() {
1502
1196
  const form = document.createElement('form');
1503
- Attributes.forward('form-', this, form);
1197
+ ftl.Attributes.forward('form-', this, form);
1504
1198
  form.replaceChildren(...this.childNodes);
1505
1199
  form.addEventListener('submit', async (e) => {
1506
1200
  e.preventDefault();
@@ -1508,12 +1202,12 @@ var ful = (function (exports) {
1508
1202
  await this.submitter?.(this.values, this);
1509
1203
  });
1510
1204
  });
1511
- if(this.hasAttribute("clear-invalid-on-change")){
1205
+ if (this.hasAttribute("clear-invalid-on-change")) {
1512
1206
  this.addEventListener('change', evt => {
1513
1207
  const target = /** @type HTMLElement */ (evt.target);
1514
1208
  target?.querySelectorAll(`.${CSS.escape(Form.INVALID_CLASS)}`).forEach(el => {
1515
1209
  el.classList.remove(Form.INVALID_CLASS);
1516
- });
1210
+ });
1517
1211
  });
1518
1212
  }
1519
1213
  this.replaceChildren(form);
@@ -1542,7 +1236,7 @@ var ful = (function (exports) {
1542
1236
  this.spinner(true);
1543
1237
  try {
1544
1238
  await this.remoting(fn);
1545
- } catch(e) {
1239
+ } catch (e) {
1546
1240
  this.spinner(false);
1547
1241
  throw e;
1548
1242
  }
@@ -1556,48 +1250,14 @@ var ful = (function (exports) {
1556
1250
  }
1557
1251
  }
1558
1252
  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
- }
1253
+ Bindings.mutateIn(this, vs);
1562
1254
  }
1563
1255
  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
- }, {});
1256
+ return Bindings.extractFrom(this, Form.IGNORED_CHILDREN_SELECTOR);
1574
1257
  }
1575
1258
  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')) {
1259
+ Bindings.errors(this, es, Form.INVALID_CLASS);
1260
+ if (es.length == 0 || !this.hasAttribute('scroll-on-error')) {
1601
1261
  return;
1602
1262
  }
1603
1263
  const ys = Array.from(this.querySelectorAll(`ful-errors:not([hidden]), [ful-validated-field]:has(.${Form.INVALID_CLASS}) ful-field-error`))
@@ -1641,23 +1301,23 @@ var ful = (function (exports) {
1641
1301
  }
1642
1302
  }));
1643
1303
  });
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", " ");
1304
+ const id = input.getAttribute('id') ?? el.getAttribute('input-id') ?? ftl.Attributes.uid('ful-input');
1305
+ ftl.Attributes.forward('input-', el, slots.input);
1306
+ ftl.Attributes.defaultValue(slots.input, "id", id);
1307
+ ftl.Attributes.defaultValue(slots.input, "type", "text");
1308
+ ftl.Attributes.defaultValue(slots.input, "placeholder", " ");
1649
1309
  const name = el.getAttribute('name');
1650
- return template.render(el, { id, name, slots });
1310
+ return template.withOverlay(el, { id, name, slots }).render();
1651
1311
  };
1652
1312
 
1653
- class Input extends ParsedElement({
1313
+ class Input extends ftl.ParsedElement({
1654
1314
  observed: ['value'],
1655
1315
  slots: true,
1656
1316
  template: INPUT_TEMPLATE
1657
1317
  }){
1658
1318
  input;
1659
- render(template, slots) {
1660
- const fragment = makeInputFragment(this, template, slots);
1319
+ render({slots}) {
1320
+ const fragment = makeInputFragment(this, this.template(), slots);
1661
1321
  this.replaceChildren(fragment);
1662
1322
  }
1663
1323
  get value() {
@@ -1673,7 +1333,7 @@ var ful = (function (exports) {
1673
1333
  * <link href="tom-select.bootstrap5.css" rel="stylesheet" />
1674
1334
  */
1675
1335
 
1676
- class Select extends ParsedElement({
1336
+ class Select extends ftl.ParsedElement({
1677
1337
  observed: ["value"],
1678
1338
  slots: true,
1679
1339
  template: `
@@ -1693,11 +1353,12 @@ var ful = (function (exports) {
1693
1353
  }) {
1694
1354
  shouldLoad;
1695
1355
  _unwrappedRemoteLoad;
1356
+ ts;
1696
1357
  constructor(tsConfig) {
1697
1358
  super();
1698
1359
  this.tsConfig = tsConfig;
1699
1360
  }
1700
- render(template, slots) {
1361
+ render({slots}) {
1701
1362
  const type = this.getAttribute("type") ?? 'local';
1702
1363
  const remote = type != 'local';
1703
1364
  const loadOnce = this.getAttribute('load') != 'always';
@@ -1707,15 +1368,15 @@ var ful = (function (exports) {
1707
1368
  })();
1708
1369
  input.setAttribute('ful-validation-target', '');
1709
1370
 
1710
- const id = input.getAttribute('id') ?? this.getAttribute('input-id') ?? Attributes.uid('ful-select');
1371
+ const id = input.getAttribute('id') ?? this.getAttribute('input-id') ?? ftl.Attributes.uid('ful-select');
1711
1372
  const tsId = `${id}-ts-control`;
1712
- Attributes.forward('input-', this, input);
1713
- Attributes.defaultValue(input, "id", id);
1714
- Attributes.defaultValue(input, "placeholder", " ");
1373
+ ftl.Attributes.forward('input-', this, input);
1374
+ ftl.Attributes.defaultValue(input, "id", id);
1375
+ ftl.Attributes.defaultValue(input, "placeholder", " ");
1715
1376
 
1716
1377
  //tomselect needs the input to have a parent.
1717
1378
  //se we move the input to a fragment
1718
- slots.input = Fragments.from(input);
1379
+ slots.input = ftl.Fragments.from(input);
1719
1380
 
1720
1381
  this.loaded = !remote;
1721
1382
 
@@ -1744,7 +1405,6 @@ var ful = (function (exports) {
1744
1405
  }
1745
1406
  callback(data);
1746
1407
  };
1747
- // @ts-ignore
1748
1408
  this.ts = new TomSelect(input, Object.assign(remote ? {
1749
1409
  preload: 'focus',
1750
1410
  load: this._unwrappedRemoteLoad,
@@ -1764,7 +1424,7 @@ var ful = (function (exports) {
1764
1424
  evt.stopPropagation();
1765
1425
  });
1766
1426
  input.remove();
1767
- template.renderTo(this, { id, tsId, name, input, slots });
1427
+ this.template().withOverlay({ id, tsId, name, input, slots }).renderTo(this);
1768
1428
  }
1769
1429
  #loader;
1770
1430
  set loader(l) {
@@ -1789,8 +1449,8 @@ var ful = (function (exports) {
1789
1449
  }
1790
1450
  }
1791
1451
 
1792
- class RadioGroup extends ParsedElement({
1793
- observed: ['value', 'disabled:state'],
1452
+ class RadioGroup extends ftl.ParsedElement({
1453
+ observed: ['value', 'disabled:presence'],
1794
1454
  slots: true,
1795
1455
  template: `
1796
1456
  <fieldset ful-validated-field>
@@ -1815,14 +1475,14 @@ var ful = (function (exports) {
1815
1475
  </fieldset>
1816
1476
  `
1817
1477
  }) {
1818
- render(template, slots) {
1819
- const name = this.getAttribute('name') ?? Attributes.uid('ful-radiogroup');
1478
+ render({slots}) {
1479
+ const name = this.getAttribute('name') ?? ftl.Attributes.uid('ful-radiogroup');
1820
1480
  const radioEls = Array.from(slots.default.querySelectorAll('ful-radio'));
1821
1481
  const inputsAndLabels = radioEls.map(el => {
1822
1482
  const input = document.createElement('input');
1823
1483
  input.setAttribute('type', 'radio');
1824
- Attributes.forward('input-', this, input);
1825
- Attributes.forward('', el, input);
1484
+ ftl.Attributes.forward('input-', this, input);
1485
+ ftl.Attributes.forward('', el, input);
1826
1486
  input.setAttribute('name', `${name}-ignore`);
1827
1487
  input.setAttribute('ful-validation-target', '');
1828
1488
  input.dataset['fulBindInclude'] = 'never';
@@ -1837,13 +1497,19 @@ var ful = (function (exports) {
1837
1497
  }
1838
1498
  }));
1839
1499
  });
1840
- const label = Fragments.fromChildNodes(el);
1500
+ const label = ftl.Fragments.fromChildNodes(el);
1841
1501
  return [input, label];
1842
1502
  });
1843
1503
 
1844
1504
  radioEls.forEach(el => el.remove());
1845
- template.renderTo(this, { name, slots, inputsAndLabels });
1505
+ this.template().withOverlay({ name, slots, inputsAndLabels }).renderTo(this);
1506
+ }
1507
+ get disabled() {
1508
+ return this.hasAttribute('disabled');
1846
1509
  }
1510
+ set disabled(value) {
1511
+ this.reflect(() => ftl.Attributes.toggle(this, 'disabled', value));
1512
+ }
1847
1513
  get value() {
1848
1514
  /** @type {HTMLInputElement|null} */
1849
1515
  const checked = this.querySelector('input[type=radio]:checked');
@@ -1851,10 +1517,8 @@ var ful = (function (exports) {
1851
1517
  }
1852
1518
  set value(value) {
1853
1519
  if (value === null) {
1854
- /** @type {HTMLInputElement[]} */
1855
1520
  this.querySelectorAll(`input[type=radio]`).forEach(el => {
1856
- // @ts-ignore
1857
- el.checked = false;
1521
+ (/** @type {HTMLInputElement} */(el)).checked = false;
1858
1522
  });
1859
1523
  return;
1860
1524
  }
@@ -1866,7 +1530,7 @@ var ful = (function (exports) {
1866
1530
  }
1867
1531
  }
1868
1532
 
1869
- class Spinner extends ParsedElement({
1533
+ class Spinner extends ftl.ParsedElement({
1870
1534
  slots: true,
1871
1535
  template: `
1872
1536
  <div class="ful-spinner-wrapper">
@@ -1875,42 +1539,35 @@ var ful = (function (exports) {
1875
1539
  </div>
1876
1540
  `
1877
1541
  }) {
1878
- render(template, slots) {
1879
- template.renderTo(this, { slots });
1542
+ render({slots}) {
1543
+ this.template().withOverlay({ slots }).renderTo(this);
1880
1544
  }
1881
1545
  }
1882
1546
 
1883
- exports.Attributes = Attributes;
1884
1547
  exports.AuthorizationCodeFlow = AuthorizationCodeFlow;
1885
1548
  exports.AuthorizationCodeFlowInterceptor = AuthorizationCodeFlowInterceptor;
1886
1549
  exports.AuthorizationCodeFlowSession = AuthorizationCodeFlowSession;
1887
1550
  exports.Base64 = Base64;
1551
+ exports.Bindings = Bindings;
1888
1552
  exports.Deferred = Deferred;
1889
- exports.ElementsRegistry = ElementsRegistry;
1890
1553
  exports.Failure = Failure;
1891
1554
  exports.Form = Form;
1892
- exports.Fragments = Fragments;
1893
1555
  exports.Hex = Hex;
1894
1556
  exports.HttpClient = HttpClient;
1895
1557
  exports.HttpClientError = HttpClientError;
1896
1558
  exports.INPUT_TEMPLATE = INPUT_TEMPLATE;
1897
1559
  exports.Input = Input;
1898
- exports.LightSlots = LightSlots;
1899
1560
  exports.LocalStorage = LocalStorage;
1900
1561
  exports.MediaType = MediaType;
1901
- exports.Nodes = Nodes;
1902
- exports.ParsedElement = ParsedElement;
1903
1562
  exports.RadioGroup = RadioGroup;
1904
1563
  exports.Select = Select;
1905
1564
  exports.SessionStorage = SessionStorage;
1906
1565
  exports.Spinner = Spinner;
1907
- exports.TemplatesRegistry = TemplatesRegistry;
1908
1566
  exports.VersionedStorage = VersionedStorage;
1909
- exports.elements = elements;
1910
1567
  exports.makeInputFragment = makeInputFragment;
1911
1568
  exports.timing = timing;
1912
1569
 
1913
1570
  return exports;
1914
1571
 
1915
- })({});
1572
+ })({}, ftl, window?.TomSelect || {});
1916
1573
  //# sourceMappingURL=ful.iife.js.map