@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 +155 -498
- package/dist/ful.iife.js.map +1 -1
- package/dist/ful.iife.min.js +1 -1
- package/dist/ful.iife.min.js.map +1 -1
- package/dist/ful.min.mjs +1 -1
- package/dist/ful.min.mjs.map +1 -1
- package/dist/ful.mjs +136 -469
- package/dist/ful.mjs.map +1 -1
- package/package.json +17 -8
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
|
|
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
|
-
* @
|
|
1049
|
+
* @param {{ [x: string]: any; }} obj
|
|
1050
|
+
* @param {string} prefix
|
|
1051
|
+
* @return {{ [x: string]: any; }}
|
|
1095
1052
|
*/
|
|
1096
|
-
static
|
|
1097
|
-
return
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
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}
|
|
1115
|
-
* @param {
|
|
1116
|
-
* @param {HTMLElement} to
|
|
1066
|
+
* @param {any} result
|
|
1067
|
+
* @param {string} path
|
|
1068
|
+
* @param {any} value
|
|
1117
1069
|
*/
|
|
1118
|
-
static
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
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
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1229
|
-
|
|
1230
|
-
throw new Error(`missing template: '${k}'`);
|
|
1111
|
+
if (el.dataset['fulBindType'] === 'boolean') {
|
|
1112
|
+
return !el.value ? null : el.value === 'true';
|
|
1231
1113
|
}
|
|
1232
|
-
|
|
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
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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
|
-
|
|
1268
|
-
return this;
|
|
1131
|
+
return result;
|
|
1269
1132
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
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
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
if (k === null || k === undefined) {
|
|
1280
|
-
return undefined;
|
|
1144
|
+
if (el.getAttribute('type') === 'checkbox') {
|
|
1145
|
+
el.checked = raw;
|
|
1146
|
+
return;
|
|
1281
1147
|
}
|
|
1282
|
-
|
|
1148
|
+
el.value = raw;
|
|
1283
1149
|
}
|
|
1284
|
-
}
|
|
1285
1150
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
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
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1577
|
-
|
|
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.
|
|
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(
|
|
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(
|
|
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.
|
|
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:
|
|
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(
|
|
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.
|
|
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
|
-
|
|
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(
|
|
1879
|
-
template.
|
|
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
|