@wildwinter/app-shell 0.49.0 → 0.50.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/chunk-JOFRUWTP.js +431 -0
- package/dist/chunk-JOFRUWTP.js.map +1 -0
- package/dist/{chunk-FGAPO4N5.js → chunk-TETY2BTN.js} +21 -12
- package/dist/chunk-TETY2BTN.js.map +1 -0
- package/dist/controls.css +76 -0
- package/dist/controls.css.map +1 -1
- package/dist/index.cjs +368 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{property-list-BPGU3rhF.d.cts → property-list-DlpxIkv6.d.cts} +9 -0
- package/dist/{property-list-BPGU3rhF.d.ts → property-list-DlpxIkv6.d.ts} +9 -0
- package/dist/property-list.cjs +18 -9
- package/dist/property-list.cjs.map +1 -1
- package/dist/property-list.d.cts +1 -1
- package/dist/property-list.d.ts +1 -1
- package/dist/property-list.js +2 -2
- package/dist/value-control.cjs +579 -0
- package/dist/value-control.cjs.map +1 -1
- package/dist/value-control.d.cts +53 -1
- package/dist/value-control.d.ts +53 -1
- package/dist/value-control.js +1 -1
- package/dist/window-guard.cjs +74 -0
- package/dist/window-guard.cjs.map +1 -0
- package/dist/window-guard.d.cts +32 -0
- package/dist/window-guard.d.ts +32 -0
- package/dist/window-guard.js +67 -0
- package/dist/window-guard.js.map +1 -0
- package/package.json +6 -1
- package/dist/chunk-EAE7UWQ5.js +0 -84
- package/dist/chunk-EAE7UWQ5.js.map +0 -1
- package/dist/chunk-FGAPO4N5.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -3198,6 +3198,353 @@ function rungStrip(opts) {
|
|
|
3198
3198
|
}
|
|
3199
3199
|
return strip;
|
|
3200
3200
|
}
|
|
3201
|
+
var fallbackFlags = (decl) => Array.isArray(decl.default) ? decl.default.filter((v) => typeof v === "string") : [];
|
|
3202
|
+
function fieldValueProblem(decl, value) {
|
|
3203
|
+
if (value === void 0 || value === null || value === "") return "";
|
|
3204
|
+
switch (decl.type) {
|
|
3205
|
+
case "number":
|
|
3206
|
+
return typeof value === "number" && Number.isFinite(value) ? "" : "Not a number.";
|
|
3207
|
+
case "boolean":
|
|
3208
|
+
return typeof value === "boolean" ? "" : "Not True or False.";
|
|
3209
|
+
case "enum":
|
|
3210
|
+
return decl.values && !decl.values.includes(String(value)) ? `"${String(value)}" is not one of the values.` : "";
|
|
3211
|
+
case "quality":
|
|
3212
|
+
return decl.stages && !decl.stages.includes(String(value)) ? `"${String(value)}" is not one of the stages.` : "";
|
|
3213
|
+
case "flags": {
|
|
3214
|
+
if (!Array.isArray(value)) return "Not a list of flags.";
|
|
3215
|
+
const strays = decl.values ? value.filter((v) => !decl.values.includes(String(v))) : [];
|
|
3216
|
+
if (strays.length === 0) return "";
|
|
3217
|
+
return strays.length === 1 ? `"${String(strays[0])}" is not one of the values.` : `${strays.map((s) => `"${String(s)}"`).join(", ")} are not among the values.`;
|
|
3218
|
+
}
|
|
3219
|
+
default:
|
|
3220
|
+
return "";
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
var isTextual = (type) => type !== "boolean" && type !== "enum" && type !== "flags" && type !== "quality";
|
|
3224
|
+
var controlShape = (o) => JSON.stringify([
|
|
3225
|
+
o.decl.type,
|
|
3226
|
+
o.decl.values ?? null,
|
|
3227
|
+
o.decl.stages ?? null,
|
|
3228
|
+
o.decl.default ?? null,
|
|
3229
|
+
o.onCommit === void 0,
|
|
3230
|
+
o.disabled === true,
|
|
3231
|
+
o.controlClassName ?? "",
|
|
3232
|
+
o.decl.type === "quality" && (o.decl.stages?.length ?? 0) === 0
|
|
3233
|
+
]);
|
|
3234
|
+
function fieldRow(initial) {
|
|
3235
|
+
let opts = initial;
|
|
3236
|
+
const row = el("div", `shell-fieldrow${opts.className ? ` ${opts.className}` : ""}`);
|
|
3237
|
+
const caption = el("span", `shell-fieldrow-label${opts.labelClassName ? ` ${opts.labelClassName}` : ""}`);
|
|
3238
|
+
caption.id = `shell-fr-${Math.random().toString(36).slice(2, 9)}`;
|
|
3239
|
+
const body = el("div", "shell-fieldrow-body");
|
|
3240
|
+
const purpose = el("p", "shell-fieldrow-purpose");
|
|
3241
|
+
const problem = el("p", "shell-fieldrow-problem");
|
|
3242
|
+
problem.setAttribute("role", "status");
|
|
3243
|
+
row.append(caption, body);
|
|
3244
|
+
let shape = "";
|
|
3245
|
+
let show2 = () => {
|
|
3246
|
+
};
|
|
3247
|
+
let typing = "";
|
|
3248
|
+
const commit = (v) => {
|
|
3249
|
+
opts.onCommit?.(v);
|
|
3250
|
+
};
|
|
3251
|
+
const editable = () => opts.onCommit !== void 0 && opts.disabled !== true;
|
|
3252
|
+
const ctl = (base) => opts.controlClassName ? `${base.replace(/^field /, "")} ${opts.controlClassName}` : base;
|
|
3253
|
+
const labelledBy = (node2) => {
|
|
3254
|
+
node2.setAttribute("aria-labelledby", caption.id);
|
|
3255
|
+
const described = [purpose.id, problem.id].filter(Boolean).join(" ");
|
|
3256
|
+
if (described) node2.setAttribute("aria-describedby", described);
|
|
3257
|
+
};
|
|
3258
|
+
purpose.id = `${caption.id}-p`;
|
|
3259
|
+
problem.id = `${caption.id}-m`;
|
|
3260
|
+
const sayProblem = () => {
|
|
3261
|
+
const words = [typing || fieldValueProblem(opts.decl, opts.value), opts.problem ?? ""].filter((w) => w !== "");
|
|
3262
|
+
problem.textContent = words.join(" ");
|
|
3263
|
+
problem.hidden = words.length === 0;
|
|
3264
|
+
row.classList.toggle("has-problem", words.length > 0);
|
|
3265
|
+
row.classList.toggle("shell-fieldrow-textual", isTextual(opts.decl.type));
|
|
3266
|
+
};
|
|
3267
|
+
const build = () => {
|
|
3268
|
+
const decl = opts.decl;
|
|
3269
|
+
typing = "";
|
|
3270
|
+
if (decl.type === "quality" && (decl.stages?.length ?? 0) > 0) {
|
|
3271
|
+
const holder = el("span", "shell-fieldrow-rungs");
|
|
3272
|
+
show2 = (value) => {
|
|
3273
|
+
holder.replaceChildren(rungStrip({
|
|
3274
|
+
stages: decl.stages,
|
|
3275
|
+
value: typeof value === "string" && value !== "" ? value : void 0,
|
|
3276
|
+
decl,
|
|
3277
|
+
...editable() ? { onPick: (stage) => {
|
|
3278
|
+
opts = { ...opts, value: stage };
|
|
3279
|
+
show2(stage);
|
|
3280
|
+
sayProblem();
|
|
3281
|
+
commit(stage);
|
|
3282
|
+
}, onClear: () => {
|
|
3283
|
+
opts = { ...opts, value: void 0 };
|
|
3284
|
+
show2(void 0);
|
|
3285
|
+
sayProblem();
|
|
3286
|
+
commit(void 0);
|
|
3287
|
+
} } : {}
|
|
3288
|
+
}));
|
|
3289
|
+
const strip = holder.firstElementChild;
|
|
3290
|
+
if (strip) {
|
|
3291
|
+
strip.setAttribute("role", "group");
|
|
3292
|
+
labelledBy(strip);
|
|
3293
|
+
}
|
|
3294
|
+
};
|
|
3295
|
+
return holder;
|
|
3296
|
+
}
|
|
3297
|
+
if (decl.type === "boolean" || decl.type === "enum") {
|
|
3298
|
+
const sel = el("select", ctl("field shell-fieldrow-select"));
|
|
3299
|
+
labelledBy(sel);
|
|
3300
|
+
sel.disabled = !editable();
|
|
3301
|
+
const choices = decl.type === "boolean" ? [["true", "True"], ["false", "False"]] : (decl.values ?? []).map((v) => [v, v]);
|
|
3302
|
+
show2 = (value) => {
|
|
3303
|
+
const current3 = value === void 0 || value === null || value === "" ? "" : String(value);
|
|
3304
|
+
sel.replaceChildren();
|
|
3305
|
+
const add = (v, label2) => {
|
|
3306
|
+
const o = el("option", void 0, label2);
|
|
3307
|
+
o.value = v;
|
|
3308
|
+
sel.append(o);
|
|
3309
|
+
};
|
|
3310
|
+
add("", defaultLabel(decl));
|
|
3311
|
+
for (const [v, label2] of choices) add(v, label2);
|
|
3312
|
+
if (current3 !== "" && !choices.some(([v]) => v === current3)) add(current3, current3);
|
|
3313
|
+
sel.value = current3;
|
|
3314
|
+
};
|
|
3315
|
+
sel.addEventListener("change", () => {
|
|
3316
|
+
const raw = sel.value;
|
|
3317
|
+
const v = raw === "" ? void 0 : decl.type === "boolean" ? raw === "true" : raw;
|
|
3318
|
+
opts = { ...opts, value: v };
|
|
3319
|
+
sayProblem();
|
|
3320
|
+
commit(v);
|
|
3321
|
+
});
|
|
3322
|
+
return sel;
|
|
3323
|
+
}
|
|
3324
|
+
if (decl.type === "flags" && (decl.values?.length ?? 0) > 0) {
|
|
3325
|
+
const holder = el("span", "shell-flags");
|
|
3326
|
+
holder.setAttribute("role", "group");
|
|
3327
|
+
labelledBy(holder);
|
|
3328
|
+
show2 = (value) => {
|
|
3329
|
+
const unset = !Array.isArray(value);
|
|
3330
|
+
const held = unset ? fallbackFlags(decl) : value.map(String);
|
|
3331
|
+
holder.replaceChildren();
|
|
3332
|
+
const chip2 = (text, cls, pressed, tip2, onClick) => {
|
|
3333
|
+
const b = el(onClick && editable() ? "button" : "span", { className: `shell-flag${cls}`, text, tip: tip2 });
|
|
3334
|
+
b.setAttribute("aria-label", `${text}: ${tip2}`);
|
|
3335
|
+
if (b instanceof HTMLButtonElement) {
|
|
3336
|
+
b.type = "button";
|
|
3337
|
+
b.addEventListener("click", onClick);
|
|
3338
|
+
}
|
|
3339
|
+
if (pressed !== void 0) b.setAttribute("aria-pressed", String(pressed));
|
|
3340
|
+
holder.append(b);
|
|
3341
|
+
};
|
|
3342
|
+
const set = (next) => {
|
|
3343
|
+
opts = { ...opts, value: next };
|
|
3344
|
+
show2(next);
|
|
3345
|
+
sayProblem();
|
|
3346
|
+
commit(next);
|
|
3347
|
+
};
|
|
3348
|
+
chip2(
|
|
3349
|
+
defaultLabel(decl),
|
|
3350
|
+
` shell-flag-default${unset ? " on" : ""}`,
|
|
3351
|
+
void 0,
|
|
3352
|
+
unset ? "Not set here: the declared default holds" : "Clear the value, back to the default",
|
|
3353
|
+
unset ? void 0 : () => set(void 0)
|
|
3354
|
+
);
|
|
3355
|
+
for (const v of decl.values) {
|
|
3356
|
+
const on = held.includes(v);
|
|
3357
|
+
chip2(
|
|
3358
|
+
v,
|
|
3359
|
+
`${on ? " on" : ""}${on && unset ? " fallback" : ""}`,
|
|
3360
|
+
on,
|
|
3361
|
+
on ? `Unset ${v}` : `Set ${v}`,
|
|
3362
|
+
() => set(on ? held.filter((x) => x !== v) : decl.values.filter((x) => x === v || held.includes(x)).concat(held.filter((x) => !decl.values.includes(x))))
|
|
3363
|
+
);
|
|
3364
|
+
}
|
|
3365
|
+
for (const v of held.filter((x) => !decl.values.includes(x))) {
|
|
3366
|
+
chip2(v, " stray on", true, `"${v}" is not one of the values: click to drop it`, () => set(held.filter((x) => x !== v)));
|
|
3367
|
+
}
|
|
3368
|
+
};
|
|
3369
|
+
return holder;
|
|
3370
|
+
}
|
|
3371
|
+
if (decl.type === "flags") {
|
|
3372
|
+
const holder = el("span", "shell-fieldrow-chips");
|
|
3373
|
+
show2 = (value) => {
|
|
3374
|
+
const box = { values: Array.isArray(value) ? value.map(String) : [] };
|
|
3375
|
+
const chips = tagChips(box, () => {
|
|
3376
|
+
const next = box.values.length > 0 ? [...box.values] : void 0;
|
|
3377
|
+
opts = { ...opts, value: next };
|
|
3378
|
+
sayProblem();
|
|
3379
|
+
commit(next);
|
|
3380
|
+
});
|
|
3381
|
+
const input = chips.querySelector("input");
|
|
3382
|
+
if (input) {
|
|
3383
|
+
labelledBy(input);
|
|
3384
|
+
input.placeholder = Array.isArray(value) ? "Add value" : defaultLabel(decl);
|
|
3385
|
+
input.disabled = !editable();
|
|
3386
|
+
}
|
|
3387
|
+
chips.querySelectorAll("button").forEach((b) => {
|
|
3388
|
+
b.disabled = !editable();
|
|
3389
|
+
});
|
|
3390
|
+
holder.replaceChildren(chips);
|
|
3391
|
+
};
|
|
3392
|
+
return holder;
|
|
3393
|
+
}
|
|
3394
|
+
const multiline = decl.type === "multiline";
|
|
3395
|
+
const field = multiline ? el("textarea", ctl("field shell-fieldrow-text")) : el("input", ctl("field shell-fieldrow-text"));
|
|
3396
|
+
if (field instanceof HTMLInputElement) {
|
|
3397
|
+
field.type = "text";
|
|
3398
|
+
if (decl.type === "number") field.inputMode = "decimal";
|
|
3399
|
+
} else field.rows = 3;
|
|
3400
|
+
labelledBy(field);
|
|
3401
|
+
field.placeholder = defaultLabel(decl);
|
|
3402
|
+
field.disabled = !editable();
|
|
3403
|
+
const asText = (value) => value === void 0 || value === null ? "" : Array.isArray(value) ? value.join(", ") : String(value);
|
|
3404
|
+
const read = (raw) => {
|
|
3405
|
+
if (raw.trim() === "") return {};
|
|
3406
|
+
if (decl.type !== "number") return { value: raw };
|
|
3407
|
+
const n = Number(raw.trim());
|
|
3408
|
+
return Number.isFinite(n) ? { value: n } : { bad: "Not a number." };
|
|
3409
|
+
};
|
|
3410
|
+
let atFocus = "";
|
|
3411
|
+
show2 = (value) => {
|
|
3412
|
+
if (field.ownerDocument.activeElement === field || keeper.restoring(field) || keeper.holds(row)) return;
|
|
3413
|
+
field.value = asText(value);
|
|
3414
|
+
};
|
|
3415
|
+
const take = () => {
|
|
3416
|
+
const r = read(field.value);
|
|
3417
|
+
typing = r.bad ?? "";
|
|
3418
|
+
if (r.bad === void 0) opts = { ...opts, value: r.value };
|
|
3419
|
+
sayProblem();
|
|
3420
|
+
if (r.bad === void 0) commit(r.value);
|
|
3421
|
+
};
|
|
3422
|
+
field.addEventListener("focus", () => {
|
|
3423
|
+
if (!keeper.restoring(field)) atFocus = field.value;
|
|
3424
|
+
});
|
|
3425
|
+
field.addEventListener("input", take);
|
|
3426
|
+
field.addEventListener("keydown", (e) => {
|
|
3427
|
+
if (e.key !== "Escape" || field.value === atFocus) return;
|
|
3428
|
+
field.value = atFocus;
|
|
3429
|
+
take();
|
|
3430
|
+
});
|
|
3431
|
+
return field;
|
|
3432
|
+
};
|
|
3433
|
+
const draw = (next) => {
|
|
3434
|
+
const decl = next.decl;
|
|
3435
|
+
const label2 = next.label ?? decl.name;
|
|
3436
|
+
if (typeof label2 === "string") caption.textContent = label2;
|
|
3437
|
+
else caption.replaceChildren(label2);
|
|
3438
|
+
caption.className = `shell-fieldrow-label${next.labelClassName ? ` ${next.labelClassName}` : ""}`;
|
|
3439
|
+
row.className = `shell-fieldrow${next.className ? ` ${next.className}` : ""}`;
|
|
3440
|
+
const nextShape = controlShape(next);
|
|
3441
|
+
opts = next;
|
|
3442
|
+
if (nextShape !== shape) {
|
|
3443
|
+
shape = nextShape;
|
|
3444
|
+
body.replaceChildren(build());
|
|
3445
|
+
}
|
|
3446
|
+
show2(next.value);
|
|
3447
|
+
purpose.textContent = decl.purpose ?? "";
|
|
3448
|
+
if (decl.purpose) body.append(purpose);
|
|
3449
|
+
else purpose.remove();
|
|
3450
|
+
body.append(problem);
|
|
3451
|
+
sayProblem();
|
|
3452
|
+
};
|
|
3453
|
+
draw(initial);
|
|
3454
|
+
keeper.watch(row);
|
|
3455
|
+
return { el: row, update: draw };
|
|
3456
|
+
}
|
|
3457
|
+
var keeper = /* @__PURE__ */ (() => {
|
|
3458
|
+
const memos = /* @__PURE__ */ new WeakMap();
|
|
3459
|
+
const restoringNow = /* @__PURE__ */ new WeakSet();
|
|
3460
|
+
const redrawn = /* @__PURE__ */ new WeakMap();
|
|
3461
|
+
const put = (row) => {
|
|
3462
|
+
const memo = memos.get(row);
|
|
3463
|
+
if (!memo) return;
|
|
3464
|
+
const doc = row.ownerDocument;
|
|
3465
|
+
const active2 = doc.activeElement;
|
|
3466
|
+
if (!row.isConnected || !memo.field.isConnected || !row.contains(memo.field)) return;
|
|
3467
|
+
if (active2 && active2 !== doc.body && !row.contains(active2)) {
|
|
3468
|
+
memos.delete(row);
|
|
3469
|
+
return;
|
|
3470
|
+
}
|
|
3471
|
+
if (active2 === memo.field && memo.field.matches(":focus")) return;
|
|
3472
|
+
restoringNow.add(memo.field);
|
|
3473
|
+
try {
|
|
3474
|
+
memo.field.focus({ preventScroll: true });
|
|
3475
|
+
if (!(memo.field instanceof HTMLSelectElement) && memo.start !== null) memo.field.setSelectionRange(memo.start, memo.end ?? memo.start);
|
|
3476
|
+
} finally {
|
|
3477
|
+
restoringNow.delete(memo.field);
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
return {
|
|
3481
|
+
watch(row) {
|
|
3482
|
+
const note = (e) => {
|
|
3483
|
+
const t = e.target;
|
|
3484
|
+
if (!(t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement)) return;
|
|
3485
|
+
const text = !(t instanceof HTMLSelectElement);
|
|
3486
|
+
memos.set(row, { field: t, start: text ? t.selectionStart : null, end: text ? t.selectionEnd : null });
|
|
3487
|
+
};
|
|
3488
|
+
row.addEventListener("focusin", note);
|
|
3489
|
+
row.addEventListener("input", note);
|
|
3490
|
+
row.addEventListener("keyup", note);
|
|
3491
|
+
row.addEventListener("mouseup", note);
|
|
3492
|
+
row.addEventListener("focusout", (e) => {
|
|
3493
|
+
const at = performance.now();
|
|
3494
|
+
const late = e.relatedTarget === null && at - (redrawn.get(row) ?? -Infinity) < 250;
|
|
3495
|
+
if (late && memos.has(row)) {
|
|
3496
|
+
queueMicrotask(() => put(row));
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
3499
|
+
queueMicrotask(() => {
|
|
3500
|
+
if ((redrawn.get(row) ?? -Infinity) >= at) {
|
|
3501
|
+
put(row);
|
|
3502
|
+
return;
|
|
3503
|
+
}
|
|
3504
|
+
if (row.isConnected && !row.contains(row.ownerDocument.activeElement)) memos.delete(row);
|
|
3505
|
+
});
|
|
3506
|
+
});
|
|
3507
|
+
},
|
|
3508
|
+
/** Give the focus back to the row, if the author was in it and has not gone anywhere else since. */
|
|
3509
|
+
restore(row) {
|
|
3510
|
+
if (!memos.has(row)) return;
|
|
3511
|
+
redrawn.set(row, performance.now());
|
|
3512
|
+
queueMicrotask(() => put(row));
|
|
3513
|
+
},
|
|
3514
|
+
restoring: (field) => restoringNow.has(field),
|
|
3515
|
+
/** Forget the row's focus if the author has since gone somewhere else (before the row is redrawn). */
|
|
3516
|
+
settle(row) {
|
|
3517
|
+
const active2 = row.ownerDocument.activeElement;
|
|
3518
|
+
if (active2 && active2 !== row.ownerDocument.body && !row.contains(active2)) memos.delete(row);
|
|
3519
|
+
},
|
|
3520
|
+
/** The row the author is in (before a redraw takes it out). */
|
|
3521
|
+
holds: (row) => memos.has(row)
|
|
3522
|
+
};
|
|
3523
|
+
})();
|
|
3524
|
+
function fieldRows() {
|
|
3525
|
+
const rows = /* @__PURE__ */ new Map();
|
|
3526
|
+
return {
|
|
3527
|
+
row(key, opts) {
|
|
3528
|
+
const had = rows.get(key);
|
|
3529
|
+
if (had) {
|
|
3530
|
+
had.used = true;
|
|
3531
|
+
keeper.settle(had.handle.el);
|
|
3532
|
+
had.handle.update(opts);
|
|
3533
|
+
keeper.restore(had.handle.el);
|
|
3534
|
+
return had.handle.el;
|
|
3535
|
+
}
|
|
3536
|
+
const handle = fieldRow(opts);
|
|
3537
|
+
rows.set(key, { handle, used: true });
|
|
3538
|
+
return handle.el;
|
|
3539
|
+
},
|
|
3540
|
+
sweep() {
|
|
3541
|
+
for (const [key, entry] of rows) {
|
|
3542
|
+
if (!entry.used) rows.delete(key);
|
|
3543
|
+
else entry.used = false;
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
};
|
|
3547
|
+
}
|
|
3201
3548
|
|
|
3202
3549
|
// src/property-list.ts
|
|
3203
3550
|
var PROPERTY_TYPES = [
|
|
@@ -3267,17 +3614,26 @@ function mountPropertyList(host, decls, opts = {}) {
|
|
|
3267
3614
|
const types = opts.types ?? PROPERTY_TYPES;
|
|
3268
3615
|
const list = el("div", "set-list");
|
|
3269
3616
|
const keep = (parts) => (parts ?? []).filter((p) => p !== null);
|
|
3617
|
+
const noun = opts.noun ?? "property";
|
|
3618
|
+
const Noun = noun.charAt(0).toUpperCase() + noun.slice(1);
|
|
3619
|
+
const freeNames = opts.names === "free";
|
|
3270
3620
|
const row = (p, i) => {
|
|
3271
3621
|
const name = el("input", "field set-name");
|
|
3272
3622
|
name.type = "text";
|
|
3273
|
-
name.placeholder =
|
|
3623
|
+
name.placeholder = `${Noun} name`;
|
|
3274
3624
|
name.value = p.name;
|
|
3275
3625
|
name.spellcheck = false;
|
|
3276
|
-
name.
|
|
3277
|
-
|
|
3278
|
-
p.name = v;
|
|
3626
|
+
if (freeNames) name.addEventListener("input", () => {
|
|
3627
|
+
p.name = name.value;
|
|
3279
3628
|
changed();
|
|
3280
|
-
}
|
|
3629
|
+
});
|
|
3630
|
+
else {
|
|
3631
|
+
name.dataset["tip"] = PROPERTY_NAME_HINT;
|
|
3632
|
+
bindPropertyName(name, (v) => {
|
|
3633
|
+
p.name = v;
|
|
3634
|
+
changed();
|
|
3635
|
+
}, { hint: PROPERTY_NAME_HINT });
|
|
3636
|
+
}
|
|
3281
3637
|
guard.track(name);
|
|
3282
3638
|
const type = el("select", "field set-type");
|
|
3283
3639
|
for (const [v, label2] of types) type.append(option(v, label2, v === p.type));
|
|
@@ -3310,7 +3666,7 @@ function mountPropertyList(host, decls, opts = {}) {
|
|
|
3310
3666
|
changed();
|
|
3311
3667
|
}
|
|
3312
3668
|
}, i === decls.length - 1);
|
|
3313
|
-
const del = iconBtn("close",
|
|
3669
|
+
const del = iconBtn("close", `Delete ${noun}`, () => {
|
|
3314
3670
|
decls.splice(i, 1);
|
|
3315
3671
|
render();
|
|
3316
3672
|
changed();
|
|
@@ -3335,7 +3691,7 @@ function mountPropertyList(host, decls, opts = {}) {
|
|
|
3335
3691
|
if (opts.purpose !== false) {
|
|
3336
3692
|
const purpose = el("input", "field set-purpose");
|
|
3337
3693
|
purpose.type = "text";
|
|
3338
|
-
purpose.placeholder =
|
|
3694
|
+
purpose.placeholder = `What this ${noun} is for`;
|
|
3339
3695
|
purpose.value = p.purpose ?? "";
|
|
3340
3696
|
purpose.addEventListener("input", () => {
|
|
3341
3697
|
if (purpose.value.trim()) p.purpose = purpose.value;
|
|
@@ -3365,9 +3721,9 @@ function mountPropertyList(host, decls, opts = {}) {
|
|
|
3365
3721
|
host.append(list, add);
|
|
3366
3722
|
return {
|
|
3367
3723
|
firstDuplicate: () => guard.firstDuplicate(),
|
|
3368
|
-
firstIllegalName: () => firstIllegalPropertyName(host),
|
|
3724
|
+
firstIllegalName: () => freeNames ? null : firstIllegalPropertyName(host),
|
|
3369
3725
|
// The Save gate takes both faults; the field's own rollover says which.
|
|
3370
|
-
firstInvalid: () => guard.firstDuplicate() ?? firstIllegalPropertyName(host),
|
|
3726
|
+
firstInvalid: () => guard.firstDuplicate() ?? (freeNames ? null : firstIllegalPropertyName(host)),
|
|
3371
3727
|
render
|
|
3372
3728
|
};
|
|
3373
3729
|
}
|
|
@@ -3480,6 +3836,9 @@ exports.el = el;
|
|
|
3480
3836
|
exports.expandableRow = expandableRow;
|
|
3481
3837
|
exports.fallbackText = fallbackText;
|
|
3482
3838
|
exports.feedUpdaterDownloadProgress = feedUpdaterDownloadProgress;
|
|
3839
|
+
exports.fieldRow = fieldRow;
|
|
3840
|
+
exports.fieldRows = fieldRows;
|
|
3841
|
+
exports.fieldValueProblem = fieldValueProblem;
|
|
3483
3842
|
exports.firstIllegalPropertyName = firstIllegalPropertyName;
|
|
3484
3843
|
exports.focusNewRow = focusNewRow;
|
|
3485
3844
|
exports.foldVc = foldVc;
|