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