@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 +154 -507
- 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 +135 -477
- 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;
|
|
@@ -1031,463 +1034,148 @@ const timing = {
|
|
|
1031
1034
|
}
|
|
1032
1035
|
};
|
|
1033
1036
|
|
|
1034
|
-
class
|
|
1035
|
-
constructor() {
|
|
1036
|
-
this.promise = new Promise((resolve, reject) => {
|
|
1037
|
-
this.reject = reject;
|
|
1038
|
-
this.resolve = resolve;
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1037
|
+
class Bindings {
|
|
1042
1038
|
|
|
1043
|
-
class Fragments {
|
|
1044
1039
|
/**
|
|
1045
|
-
*
|
|
1046
|
-
* @param
|
|
1047
|
-
* @
|
|
1040
|
+
* @param {{ [x: string]: any; }} obj
|
|
1041
|
+
* @param {string} prefix
|
|
1042
|
+
* @return {{ [x: string]: any; }}
|
|
1048
1043
|
*/
|
|
1049
|
-
static
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
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;
|
|
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
|
+
}, {});
|
|
1083
1054
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
class Attributes {
|
|
1087
|
-
static id = 0;
|
|
1055
|
+
|
|
1088
1056
|
/**
|
|
1089
|
-
*
|
|
1090
|
-
* @param {string}
|
|
1091
|
-
* @
|
|
1057
|
+
* @param {any} result
|
|
1058
|
+
* @param {string} path
|
|
1059
|
+
* @param {any} value
|
|
1092
1060
|
*/
|
|
1093
|
-
static
|
|
1094
|
-
|
|
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
|
+
}
|
|
1095
1086
|
}
|
|
1096
1087
|
/**
|
|
1097
1088
|
*
|
|
1098
|
-
* @param {
|
|
1099
|
-
* @param {string} k
|
|
1100
|
-
* @param {string} v
|
|
1089
|
+
* @param {Element & {dataset?: any} & {checked?: boolean} & {value?: any}} el
|
|
1101
1090
|
* @returns
|
|
1102
1091
|
*/
|
|
1103
|
-
static
|
|
1104
|
-
if (
|
|
1105
|
-
el.
|
|
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;
|
|
1106
1098
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
/**
|
|
1110
|
-
*
|
|
1111
|
-
* @param {string} prefix
|
|
1112
|
-
* @param {HTMLElement} from
|
|
1113
|
-
* @param {HTMLElement} to
|
|
1114
|
-
*/
|
|
1115
|
-
static forward(prefix, from, to) {
|
|
1116
|
-
from.getAttributeNames()
|
|
1117
|
-
.filter(a => a.startsWith(prefix))
|
|
1118
|
-
.forEach(a => {
|
|
1119
|
-
const target = a.substring(prefix.length);
|
|
1120
|
-
if (target === 'class') {
|
|
1121
|
-
const classes = from.getAttribute(prefix + "class")?.split(" ").filter(a => a.length) ?? [];
|
|
1122
|
-
to.classList.add(...classes);
|
|
1123
|
-
return;
|
|
1124
|
-
}
|
|
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);
|
|
1099
|
+
if (el.getAttribute('type') === 'checkbox') {
|
|
1100
|
+
return el.checked;
|
|
1140
1101
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
if (el.hasAttribute(attr)) {
|
|
1144
|
-
el.removeAttribute(attr);
|
|
1145
|
-
} else {
|
|
1146
|
-
el.setAttribute(attr, '');
|
|
1102
|
+
if (el.dataset['fulBindType'] === 'boolean') {
|
|
1103
|
+
return !el.value ? null : el.value === 'true';
|
|
1147
1104
|
}
|
|
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
|
-
}
|
|
1175
|
-
slots[name].append(el);
|
|
1105
|
+
if (el.tagName === 'INPUT' || el.tagName === 'SELECT') {
|
|
1106
|
+
return el.value === '' || el.value === undefined ? null : el.value;
|
|
1176
1107
|
}
|
|
1177
|
-
return
|
|
1108
|
+
return el.value;
|
|
1178
1109
|
}
|
|
1179
|
-
}
|
|
1180
1110
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
for
|
|
1184
|
-
if (
|
|
1185
|
-
|
|
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;
|
|
1186
1116
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
}
|
|
1190
|
-
static queryChildren(node, selector) {
|
|
1191
|
-
for (const c of node.children) {
|
|
1192
|
-
if (c.matches(selector)) {
|
|
1193
|
-
return c;
|
|
1117
|
+
if(ignoredChildrenSelector && el.dataset['fulBindInclude'] !== 'always' && el.closest(ignoredChildrenSelector) !== null){
|
|
1118
|
+
continue;
|
|
1194
1119
|
}
|
|
1120
|
+
result = Bindings.providePath(result, /** @type {string} */(el.getAttribute('name')), Bindings.extract(el));
|
|
1195
1121
|
}
|
|
1196
|
-
return
|
|
1122
|
+
return result;
|
|
1197
1123
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
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);
|
|
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;
|
|
1217
1133
|
return;
|
|
1218
1134
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
if (!this.#ec) {
|
|
1223
|
-
throw new Error("TemplatesRegistry is not configured");
|
|
1224
|
-
}
|
|
1225
|
-
const tpl = this.#idToTemplate[k];
|
|
1226
|
-
if (!tpl) {
|
|
1227
|
-
throw new Error(`missing template: '${k}'`);
|
|
1228
|
-
}
|
|
1229
|
-
return tpl;
|
|
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);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
class ElementsRegistry {
|
|
1243
|
-
#templates;
|
|
1244
|
-
#tagToclass;
|
|
1245
|
-
#configured;
|
|
1246
|
-
#id = 0;
|
|
1247
|
-
constructor() {
|
|
1248
|
-
this.#templates = new TemplatesRegistry();
|
|
1249
|
-
this.#tagToclass = {};
|
|
1250
|
-
}
|
|
1251
|
-
defineTemplate(html) {
|
|
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;
|
|
1263
|
-
}
|
|
1264
|
-
customElements.define(tag, klass);
|
|
1265
|
-
return this;
|
|
1266
|
-
}
|
|
1267
|
-
configure(ec, ...data) {
|
|
1268
|
-
this.#templates.configure(ec, ...data);
|
|
1269
|
-
for (const [tag, klass] of Object.entries(this.#tagToclass)) {
|
|
1270
|
-
customElements.define(tag, klass);
|
|
1271
|
-
delete this.#tagToclass[tag];
|
|
1272
|
-
}
|
|
1273
|
-
this.#configured = true;
|
|
1274
|
-
}
|
|
1275
|
-
template(k) {
|
|
1276
|
-
if (k === null || k === undefined) {
|
|
1277
|
-
return undefined;
|
|
1135
|
+
if (el.getAttribute('type') === 'checkbox') {
|
|
1136
|
+
el.checked = raw;
|
|
1137
|
+
return;
|
|
1278
1138
|
}
|
|
1279
|
-
|
|
1139
|
+
el.value = raw;
|
|
1280
1140
|
}
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
const elements = new ElementsRegistry();
|
|
1284
1141
|
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
}
|
|
1291
|
-
enqueue(el) {
|
|
1292
|
-
if (!this.#q.length) {
|
|
1293
|
-
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
|
+
}
|
|
1294
1147
|
}
|
|
1295
|
-
this.#q.push(el);
|
|
1296
|
-
}
|
|
1297
|
-
dequeue() {
|
|
1298
|
-
this.#q.splice(0).forEach(el => el.upgrade());
|
|
1299
1148
|
}
|
|
1300
|
-
}
|
|
1301
1149
|
|
|
1302
|
-
const upgradeQueue = new UpgradeQueue();
|
|
1303
1150
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
};
|
|
1312
|
-
|
|
1313
|
-
const
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
throw new Error(`unsupported attribute type: ${type}`);
|
|
1321
|
-
}
|
|
1322
|
-
return [attr.trim(), type];
|
|
1323
|
-
});
|
|
1324
|
-
|
|
1325
|
-
const attrsAndMappers = attrsAndTypes.map(([attr, type]) => [attr, mappers[type]]);
|
|
1326
|
-
|
|
1327
|
-
const attrToMapper = Object.fromEntries(attrsAndMappers);
|
|
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);
|
|
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;
|
|
1367
1167
|
});
|
|
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
1168
|
});
|
|
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 = [];
|
|
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');
|
|
1447
1174
|
}
|
|
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
|
-
}
|
|
1175
|
+
});
|
|
1461
1176
|
|
|
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
1177
|
|
|
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
1178
|
}
|
|
1490
|
-
el.value = raw;
|
|
1491
1179
|
}
|
|
1492
1180
|
|
|
1493
1181
|
class Form extends ParsedElement() {
|
|
@@ -1505,12 +1193,12 @@ class Form extends ParsedElement() {
|
|
|
1505
1193
|
await this.submitter?.(this.values, this);
|
|
1506
1194
|
});
|
|
1507
1195
|
});
|
|
1508
|
-
if(this.hasAttribute("clear-invalid-on-change")){
|
|
1196
|
+
if (this.hasAttribute("clear-invalid-on-change")) {
|
|
1509
1197
|
this.addEventListener('change', evt => {
|
|
1510
1198
|
const target = /** @type HTMLElement */ (evt.target);
|
|
1511
1199
|
target?.querySelectorAll(`.${CSS.escape(Form.INVALID_CLASS)}`).forEach(el => {
|
|
1512
1200
|
el.classList.remove(Form.INVALID_CLASS);
|
|
1513
|
-
});
|
|
1201
|
+
});
|
|
1514
1202
|
});
|
|
1515
1203
|
}
|
|
1516
1204
|
this.replaceChildren(form);
|
|
@@ -1539,7 +1227,7 @@ class Form extends ParsedElement() {
|
|
|
1539
1227
|
this.spinner(true);
|
|
1540
1228
|
try {
|
|
1541
1229
|
await this.remoting(fn);
|
|
1542
|
-
} catch(e) {
|
|
1230
|
+
} catch (e) {
|
|
1543
1231
|
this.spinner(false);
|
|
1544
1232
|
throw e;
|
|
1545
1233
|
}
|
|
@@ -1553,48 +1241,14 @@ class Form extends ParsedElement() {
|
|
|
1553
1241
|
}
|
|
1554
1242
|
}
|
|
1555
1243
|
set values(vs) {
|
|
1556
|
-
|
|
1557
|
-
this.querySelectorAll(`[name='${CSS.escape(flattenedKey)}']`).forEach(el => mutate(el, value));
|
|
1558
|
-
}
|
|
1244
|
+
Bindings.mutateIn(this, vs);
|
|
1559
1245
|
}
|
|
1560
1246
|
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
|
-
}, {});
|
|
1247
|
+
return Bindings.extractFrom(this, Form.IGNORED_CHILDREN_SELECTOR);
|
|
1571
1248
|
}
|
|
1572
1249
|
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')) {
|
|
1250
|
+
Bindings.errors(this, es, Form.INVALID_CLASS);
|
|
1251
|
+
if (es.length == 0 || !this.hasAttribute('scroll-on-error')) {
|
|
1598
1252
|
return;
|
|
1599
1253
|
}
|
|
1600
1254
|
const ys = Array.from(this.querySelectorAll(`ful-errors:not([hidden]), [ful-validated-field]:has(.${Form.INVALID_CLASS}) ful-field-error`))
|
|
@@ -1644,7 +1298,7 @@ const makeInputFragment = (el, template, slots) => {
|
|
|
1644
1298
|
Attributes.defaultValue(slots.input, "type", "text");
|
|
1645
1299
|
Attributes.defaultValue(slots.input, "placeholder", " ");
|
|
1646
1300
|
const name = el.getAttribute('name');
|
|
1647
|
-
return template.
|
|
1301
|
+
return template.withOverlay(el, { id, name, slots }).render();
|
|
1648
1302
|
};
|
|
1649
1303
|
|
|
1650
1304
|
class Input extends ParsedElement({
|
|
@@ -1653,8 +1307,8 @@ class Input extends ParsedElement({
|
|
|
1653
1307
|
template: INPUT_TEMPLATE
|
|
1654
1308
|
}){
|
|
1655
1309
|
input;
|
|
1656
|
-
render(
|
|
1657
|
-
const fragment = makeInputFragment(this, template, slots);
|
|
1310
|
+
render({slots}) {
|
|
1311
|
+
const fragment = makeInputFragment(this, this.template(), slots);
|
|
1658
1312
|
this.replaceChildren(fragment);
|
|
1659
1313
|
}
|
|
1660
1314
|
get value() {
|
|
@@ -1690,11 +1344,12 @@ class Select extends ParsedElement({
|
|
|
1690
1344
|
}) {
|
|
1691
1345
|
shouldLoad;
|
|
1692
1346
|
_unwrappedRemoteLoad;
|
|
1347
|
+
ts;
|
|
1693
1348
|
constructor(tsConfig) {
|
|
1694
1349
|
super();
|
|
1695
1350
|
this.tsConfig = tsConfig;
|
|
1696
1351
|
}
|
|
1697
|
-
render(
|
|
1352
|
+
render({slots}) {
|
|
1698
1353
|
const type = this.getAttribute("type") ?? 'local';
|
|
1699
1354
|
const remote = type != 'local';
|
|
1700
1355
|
const loadOnce = this.getAttribute('load') != 'always';
|
|
@@ -1741,7 +1396,6 @@ class Select extends ParsedElement({
|
|
|
1741
1396
|
}
|
|
1742
1397
|
callback(data);
|
|
1743
1398
|
};
|
|
1744
|
-
// @ts-ignore
|
|
1745
1399
|
this.ts = new TomSelect(input, Object.assign(remote ? {
|
|
1746
1400
|
preload: 'focus',
|
|
1747
1401
|
load: this._unwrappedRemoteLoad,
|
|
@@ -1761,7 +1415,7 @@ class Select extends ParsedElement({
|
|
|
1761
1415
|
evt.stopPropagation();
|
|
1762
1416
|
});
|
|
1763
1417
|
input.remove();
|
|
1764
|
-
template.
|
|
1418
|
+
this.template().withOverlay({ id, tsId, name, input, slots }).renderTo(this);
|
|
1765
1419
|
}
|
|
1766
1420
|
#loader;
|
|
1767
1421
|
set loader(l) {
|
|
@@ -1787,7 +1441,7 @@ class Select extends ParsedElement({
|
|
|
1787
1441
|
}
|
|
1788
1442
|
|
|
1789
1443
|
class RadioGroup extends ParsedElement({
|
|
1790
|
-
observed: ['value', 'disabled:
|
|
1444
|
+
observed: ['value', 'disabled:presence'],
|
|
1791
1445
|
slots: true,
|
|
1792
1446
|
template: `
|
|
1793
1447
|
<fieldset ful-validated-field>
|
|
@@ -1812,7 +1466,7 @@ class RadioGroup extends ParsedElement({
|
|
|
1812
1466
|
</fieldset>
|
|
1813
1467
|
`
|
|
1814
1468
|
}) {
|
|
1815
|
-
render(
|
|
1469
|
+
render({slots}) {
|
|
1816
1470
|
const name = this.getAttribute('name') ?? Attributes.uid('ful-radiogroup');
|
|
1817
1471
|
const radioEls = Array.from(slots.default.querySelectorAll('ful-radio'));
|
|
1818
1472
|
const inputsAndLabels = radioEls.map(el => {
|
|
@@ -1839,8 +1493,14 @@ class RadioGroup extends ParsedElement({
|
|
|
1839
1493
|
});
|
|
1840
1494
|
|
|
1841
1495
|
radioEls.forEach(el => el.remove());
|
|
1842
|
-
template.
|
|
1496
|
+
this.template().withOverlay({ name, slots, inputsAndLabels }).renderTo(this);
|
|
1497
|
+
}
|
|
1498
|
+
get disabled() {
|
|
1499
|
+
return this.hasAttribute('disabled');
|
|
1843
1500
|
}
|
|
1501
|
+
set disabled(value) {
|
|
1502
|
+
this.reflect(() => Attributes.toggle(this, 'disabled', value));
|
|
1503
|
+
}
|
|
1844
1504
|
get value() {
|
|
1845
1505
|
/** @type {HTMLInputElement|null} */
|
|
1846
1506
|
const checked = this.querySelector('input[type=radio]:checked');
|
|
@@ -1848,10 +1508,8 @@ class RadioGroup extends ParsedElement({
|
|
|
1848
1508
|
}
|
|
1849
1509
|
set value(value) {
|
|
1850
1510
|
if (value === null) {
|
|
1851
|
-
/** @type {HTMLInputElement[]} */
|
|
1852
1511
|
this.querySelectorAll(`input[type=radio]`).forEach(el => {
|
|
1853
|
-
|
|
1854
|
-
el.checked = false;
|
|
1512
|
+
(/** @type {HTMLInputElement} */(el)).checked = false;
|
|
1855
1513
|
});
|
|
1856
1514
|
return;
|
|
1857
1515
|
}
|
|
@@ -1872,10 +1530,10 @@ class Spinner extends ParsedElement({
|
|
|
1872
1530
|
</div>
|
|
1873
1531
|
`
|
|
1874
1532
|
}) {
|
|
1875
|
-
render(
|
|
1876
|
-
template.
|
|
1533
|
+
render({slots}) {
|
|
1534
|
+
this.template().withOverlay({ slots }).renderTo(this);
|
|
1877
1535
|
}
|
|
1878
1536
|
}
|
|
1879
1537
|
|
|
1880
|
-
export {
|
|
1538
|
+
export { AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, Bindings, Failure, Form, Hex, HttpClient, HttpClientError, INPUT_TEMPLATE, Input, LocalStorage, MediaType, RadioGroup, Select, SessionStorage, Spinner, VersionedStorage, makeInputFragment, timing };
|
|
1881
1539
|
//# sourceMappingURL=ful.mjs.map
|