@8ux-co/eelzap 0.10.1 → 0.10.2
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/CHANGELOG.md +17 -1
- package/dist/{chunk-7ZPXZZUT.js → chunk-4ET7VBJA.js} +60 -6
- package/dist/chunk-4ET7VBJA.js.map +1 -0
- package/dist/{chunk-7CXQK2PR.js → chunk-T6S3S5RH.js} +2 -2
- package/dist/{chunk-7CXQK2PR.js.map → chunk-T6S3S5RH.js.map} +1 -1
- package/dist/preview.js +6 -3
- package/dist/preview.js.map +1 -1
- package/dist/react.js +2 -2
- package/dist/react.js.map +1 -1
- package/dist/{signin-QT7YANBN.js → signin-XOGBCGBL.js} +4 -8
- package/dist/signin-XOGBCGBL.js.map +1 -0
- package/dist/{suggest-JZ63L3WP.js → suggest-KLS6B7AM.js} +467 -75
- package/dist/suggest-KLS6B7AM.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-7ZPXZZUT.js.map +0 -1
- package/dist/signin-QT7YANBN.js.map +0 -1
- package/dist/suggest-JZ63L3WP.js.map +0 -1
|
@@ -13,16 +13,17 @@ import {
|
|
|
13
13
|
parseRecordRef,
|
|
14
14
|
reveal,
|
|
15
15
|
tagGuard
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-T6S3S5RH.js";
|
|
17
17
|
import {
|
|
18
18
|
SUGGEST_MESSAGES,
|
|
19
19
|
TOKENS,
|
|
20
20
|
el,
|
|
21
21
|
icon,
|
|
22
22
|
initials,
|
|
23
|
+
kbd,
|
|
23
24
|
mountHost,
|
|
24
25
|
readTokenAnswer
|
|
25
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-4ET7VBJA.js";
|
|
26
27
|
import {
|
|
27
28
|
EXPIRY_MARGIN_MS,
|
|
28
29
|
accessLive,
|
|
@@ -55,6 +56,10 @@ async function call(ctx, token, path, init) {
|
|
|
55
56
|
const seconds = Number.parseInt(response.headers.get("Retry-After") ?? "", 10);
|
|
56
57
|
return { ok: false, kind: "rate", retryAfter: seconds > 0 ? Math.min(seconds, 3600) : 30 };
|
|
57
58
|
}
|
|
59
|
+
if (response.status === 409) {
|
|
60
|
+
const body = await response.json().catch(() => null);
|
|
61
|
+
if (body?.error?.code === "NO_CHANGE") return { ok: false, kind: "unchanged" };
|
|
62
|
+
}
|
|
58
63
|
if (!response.ok) return { ok: false, kind: "invalid" };
|
|
59
64
|
try {
|
|
60
65
|
return { ok: true, data: await response.json() };
|
|
@@ -374,6 +379,9 @@ function inDraftSession(win, now = Date.now()) {
|
|
|
374
379
|
const value = readJson(win, DRAFT_SESSION_KEY);
|
|
375
380
|
return !!value && typeof value.exp === "number" && value.exp > now && value.origin === win.location.origin;
|
|
376
381
|
}
|
|
382
|
+
function rememberEditIntent(win, now = Date.now()) {
|
|
383
|
+
write2(win, EDIT_INTENT_KEY, { path: win.location.pathname, at: now });
|
|
384
|
+
}
|
|
377
385
|
function takeEditIntent(win, now = Date.now()) {
|
|
378
386
|
const value = readJson(win, EDIT_INTENT_KEY);
|
|
379
387
|
remove(win, EDIT_INTENT_KEY);
|
|
@@ -465,12 +473,112 @@ async function leaveDraftSession(ctx, token, tagged) {
|
|
|
465
473
|
}
|
|
466
474
|
|
|
467
475
|
// src/preview/site/suggestion.ts
|
|
476
|
+
var SUGGESTIBLE_TYPES = ["TEXT", "LONG_TEXT", "NUMBER", "URL"];
|
|
477
|
+
var INPUT_TYPES = /* @__PURE__ */ new Set(["URL", "NUMBER", "INTEGER", "CURRENCY", "DATE", "DATETIME"]);
|
|
478
|
+
function proposableField(tagged, field) {
|
|
479
|
+
return !!tagged && !!field && !tagged.html && SUGGESTIBLE_TYPES.includes(field.type);
|
|
480
|
+
}
|
|
468
481
|
function editKindFor(tagged, field) {
|
|
469
482
|
if (!tagged || !field || tagged.html) return null;
|
|
470
483
|
if (field.type === "TEXT" || field.type === "LONG_TEXT") return "text";
|
|
471
|
-
if (
|
|
484
|
+
if (INPUT_TYPES.has(field.type)) return "input";
|
|
485
|
+
if (field.type === "ENUM") return field.options?.length ? "choice" : null;
|
|
486
|
+
if (field.type === "BOOLEAN") return "switch";
|
|
472
487
|
return null;
|
|
473
488
|
}
|
|
489
|
+
var ZERO_DIGITS = /* @__PURE__ */ new Set([
|
|
490
|
+
"BIF",
|
|
491
|
+
"CLP",
|
|
492
|
+
"DJF",
|
|
493
|
+
"GNF",
|
|
494
|
+
"ISK",
|
|
495
|
+
"JPY",
|
|
496
|
+
"KMF",
|
|
497
|
+
"KRW",
|
|
498
|
+
"PYG",
|
|
499
|
+
"RWF",
|
|
500
|
+
"UGX",
|
|
501
|
+
"UYI",
|
|
502
|
+
"VND",
|
|
503
|
+
"VUV",
|
|
504
|
+
"XAF",
|
|
505
|
+
"XOF",
|
|
506
|
+
"XPF"
|
|
507
|
+
]);
|
|
508
|
+
var THREE_DIGITS = /* @__PURE__ */ new Set(["BHD", "IQD", "JOD", "KWD", "LYD", "OMR", "TND"]);
|
|
509
|
+
function currencyDigits(code) {
|
|
510
|
+
const upper = (code ?? "").toUpperCase();
|
|
511
|
+
if (ZERO_DIGITS.has(upper)) return 0;
|
|
512
|
+
if (THREE_DIGITS.has(upper)) return 3;
|
|
513
|
+
return 2;
|
|
514
|
+
}
|
|
515
|
+
var pad = (n) => String(n).padStart(2, "0");
|
|
516
|
+
function inputSpec(field, value) {
|
|
517
|
+
const digits = currencyDigits(field.currency);
|
|
518
|
+
const limits = { min: field.min, max: field.max };
|
|
519
|
+
const number = typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
520
|
+
switch (field.type) {
|
|
521
|
+
case "NUMBER":
|
|
522
|
+
return {
|
|
523
|
+
type: "number",
|
|
524
|
+
text: number === null ? "" : String(number),
|
|
525
|
+
...limits,
|
|
526
|
+
step: field.step ?? "any"
|
|
527
|
+
};
|
|
528
|
+
case "INTEGER":
|
|
529
|
+
return { type: "number", text: number === null ? "" : String(number), ...limits, step: 1 };
|
|
530
|
+
case "CURRENCY":
|
|
531
|
+
return {
|
|
532
|
+
type: "number",
|
|
533
|
+
text: number === null ? "" : (number / 10 ** digits).toFixed(digits),
|
|
534
|
+
...limits,
|
|
535
|
+
step: field.step ?? 10 ** -digits
|
|
536
|
+
};
|
|
537
|
+
case "DATE":
|
|
538
|
+
return { type: "date", text: typeof value === "string" ? value.slice(0, 10) : "" };
|
|
539
|
+
case "DATETIME": {
|
|
540
|
+
const at = typeof value === "string" ? new Date(value) : null;
|
|
541
|
+
return {
|
|
542
|
+
type: "datetime-local",
|
|
543
|
+
text: at && Number.isFinite(at.getTime()) ? `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())}T${pad(at.getHours())}:${pad(at.getMinutes())}` : ""
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
default:
|
|
547
|
+
return { type: "text", text: typeof value === "string" ? value : "" };
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function parseInput(field, raw) {
|
|
551
|
+
const text = cleanStega(raw).trim();
|
|
552
|
+
if (field.type === "URL") return { ok: true, value: normalizeText(text) };
|
|
553
|
+
if (text === "") return { ok: true, value: null };
|
|
554
|
+
const number = Number(text.replace(",", "."));
|
|
555
|
+
switch (field.type) {
|
|
556
|
+
case "NUMBER":
|
|
557
|
+
return Number.isFinite(number) ? { ok: true, value: number } : { ok: false };
|
|
558
|
+
case "INTEGER":
|
|
559
|
+
return Number.isInteger(number) ? { ok: true, value: number } : { ok: false };
|
|
560
|
+
case "CURRENCY":
|
|
561
|
+
return Number.isFinite(number) ? { ok: true, value: Math.round(number * 10 ** currencyDigits(field.currency)) } : { ok: false };
|
|
562
|
+
case "DATE":
|
|
563
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(text) && Number.isFinite(Date.parse(text)) ? { ok: true, value: text } : { ok: false };
|
|
564
|
+
case "DATETIME": {
|
|
565
|
+
const at = new Date(text);
|
|
566
|
+
return Number.isFinite(at.getTime()) ? { ok: true, value: at.toISOString() } : { ok: false };
|
|
567
|
+
}
|
|
568
|
+
default:
|
|
569
|
+
return { ok: true, value: text };
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function sameRaw(type, a, b) {
|
|
573
|
+
const before = a ?? null;
|
|
574
|
+
if (typeof before === "string" && typeof b === "string") {
|
|
575
|
+
if (type === "DATETIME") {
|
|
576
|
+
return Math.floor(Date.parse(before) / 6e4) === Math.floor(Date.parse(b) / 6e4);
|
|
577
|
+
}
|
|
578
|
+
return normalizeText(before) === normalizeText(b);
|
|
579
|
+
}
|
|
580
|
+
return before === b;
|
|
581
|
+
}
|
|
474
582
|
function fieldOf(data, tagged) {
|
|
475
583
|
if (!data || !tagged) return null;
|
|
476
584
|
const field = data.fields?.[tagged.recordRef]?.[tagged.fieldKey];
|
|
@@ -549,7 +657,7 @@ function buildDraftBody(input) {
|
|
|
549
657
|
...recordKeys(input.recordRef),
|
|
550
658
|
pageUrl: input.pageUrl,
|
|
551
659
|
anchors: input.anchors.slice(0, 20),
|
|
552
|
-
body: String(input.proposal.value).slice(0, 1e4) || " ",
|
|
660
|
+
body: (input.display ?? String(input.proposal.value ?? "")).slice(0, 1e4) || " ",
|
|
553
661
|
...proposedPart(input.proposal)
|
|
554
662
|
};
|
|
555
663
|
}
|
|
@@ -781,6 +889,7 @@ var OVERLAY_MODE = {
|
|
|
781
889
|
var PENDING_PIN = "eelzap:pending";
|
|
782
890
|
var LARGE_SHARE = 0.5;
|
|
783
891
|
var TOAST_MS = 8e3;
|
|
892
|
+
var RELOAD_AFTER_SAVE_MS = 700;
|
|
784
893
|
var SAVED_MS = 4e3;
|
|
785
894
|
function start(ctx, options = {}) {
|
|
786
895
|
const { win, doc } = ctx;
|
|
@@ -795,6 +904,7 @@ function start(ctx, options = {}) {
|
|
|
795
904
|
const m = SUGGEST_MESSAGES[ctx.locale];
|
|
796
905
|
const dm = DRAFT_SESSION_MESSAGES[ctx.locale];
|
|
797
906
|
let reopenEdit = takeEditIntent(win);
|
|
907
|
+
let retryDraft = !reopenEdit;
|
|
798
908
|
const host = mountHost(doc, CSS + DRAG_CSS);
|
|
799
909
|
const layer = el(doc, "div", { class: "layer" });
|
|
800
910
|
const marks = el(doc, "div", { class: "marks" });
|
|
@@ -813,6 +923,7 @@ function start(ctx, options = {}) {
|
|
|
813
923
|
let editing = null;
|
|
814
924
|
let list = null;
|
|
815
925
|
let toast = null;
|
|
926
|
+
let info = null;
|
|
816
927
|
let toastTimer = null;
|
|
817
928
|
let status = null;
|
|
818
929
|
let statusTimer = null;
|
|
@@ -834,7 +945,10 @@ function start(ctx, options = {}) {
|
|
|
834
945
|
};
|
|
835
946
|
win.addEventListener("click", onClickFirst, true);
|
|
836
947
|
const onPressFirst = (event) => {
|
|
837
|
-
if (
|
|
948
|
+
if (event.target === host.host) return;
|
|
949
|
+
if (menuOpen) setMenu(false);
|
|
950
|
+
if (editing?.card && !editing.done) commitEdit(editing);
|
|
951
|
+
if (info) closeInfo();
|
|
838
952
|
};
|
|
839
953
|
win.addEventListener("pointerdown", onPressFirst, true);
|
|
840
954
|
layer.addEventListener("pointerdown", (event) => {
|
|
@@ -969,12 +1083,14 @@ function start(ctx, options = {}) {
|
|
|
969
1083
|
function setMode(next) {
|
|
970
1084
|
if (next === "edit" && !canEdit() || paused && next !== "navigate") return;
|
|
971
1085
|
if (status === "preparing") return;
|
|
972
|
-
if (next === "edit" && !index.elements.length && !inDraftSession(win)) {
|
|
1086
|
+
if (next === "edit" && !index.elements.length && (retryDraft || !inDraftSession(win))) {
|
|
1087
|
+
retryDraft = false;
|
|
973
1088
|
void prepareEdit();
|
|
974
1089
|
return;
|
|
975
1090
|
}
|
|
976
1091
|
endEdit();
|
|
977
1092
|
closeComposer();
|
|
1093
|
+
closeInfo();
|
|
978
1094
|
mode = next;
|
|
979
1095
|
selection = [];
|
|
980
1096
|
overlay.clearSelection();
|
|
@@ -1075,7 +1191,7 @@ function start(ctx, options = {}) {
|
|
|
1075
1191
|
function withTip(target, text, end) {
|
|
1076
1192
|
const show = () => {
|
|
1077
1193
|
tip.replaceChildren(text);
|
|
1078
|
-
if (key) tip.append(
|
|
1194
|
+
if (key) tip.append(kbd(doc, "Shift", key));
|
|
1079
1195
|
tip.classList.add("on");
|
|
1080
1196
|
const rect = target.getBoundingClientRect();
|
|
1081
1197
|
const vw = doc.documentElement.clientWidth || win.innerWidth;
|
|
@@ -1494,6 +1610,13 @@ function start(ctx, options = {}) {
|
|
|
1494
1610
|
box.style.top = `${rect.top - 3}px`;
|
|
1495
1611
|
box.style.width = `${rect.width + 6}px`;
|
|
1496
1612
|
box.style.height = `${rect.height + 6}px`;
|
|
1613
|
+
if (editing?.card)
|
|
1614
|
+
box.append(
|
|
1615
|
+
el(doc, "span", {
|
|
1616
|
+
class: "mark-chip",
|
|
1617
|
+
text: editing.field.label || editing.tagged.fieldKey
|
|
1618
|
+
})
|
|
1619
|
+
);
|
|
1497
1620
|
return box;
|
|
1498
1621
|
})
|
|
1499
1622
|
);
|
|
@@ -1505,18 +1628,42 @@ function start(ctx, options = {}) {
|
|
|
1505
1628
|
frame = null;
|
|
1506
1629
|
drawMarks();
|
|
1507
1630
|
if (composer) position(composer.card, anchorRect(composer));
|
|
1508
|
-
if (editing?.card)
|
|
1631
|
+
if (editing?.card)
|
|
1632
|
+
position(editing.card, editing.element.getBoundingClientRect(), editing.card.offsetWidth);
|
|
1633
|
+
if (info) position(info.card, info.tagged.element.getBoundingClientRect(), 300);
|
|
1509
1634
|
});
|
|
1510
1635
|
}
|
|
1511
|
-
function position(card, rect) {
|
|
1512
|
-
const
|
|
1513
|
-
const
|
|
1514
|
-
const
|
|
1515
|
-
const top = below + height < win.innerHeight - 90 ? below : Math.max(8, rect.top - height - 10);
|
|
1516
|
-
const left = Math.min(Math.max(8, rect.left), Math.max(8, win.innerWidth - width - 8));
|
|
1636
|
+
function position(card, rect, preferred = 420) {
|
|
1637
|
+
const vw = win.innerWidth;
|
|
1638
|
+
const vh = win.innerHeight;
|
|
1639
|
+
const width = Math.min(preferred, vw - 16);
|
|
1517
1640
|
card.style.width = `${width}px`;
|
|
1518
|
-
card.
|
|
1519
|
-
|
|
1641
|
+
const height = card.offsetHeight || 260;
|
|
1642
|
+
const bar = bottom.querySelector(".toolbar, .pill")?.getBoundingClientRect();
|
|
1643
|
+
const clampX = (x) => Math.min(Math.max(8, x), Math.max(8, vw - width - 8));
|
|
1644
|
+
const hits = (x, y) => !!bar && x < bar.right + 8 && x + width > bar.left - 8 && y < bar.bottom + 8 && y + height > bar.top - 8;
|
|
1645
|
+
const fits = (y) => y >= 8 && y + height <= vh - 8;
|
|
1646
|
+
let place = null;
|
|
1647
|
+
for (const y of [rect.bottom + 10, rect.top - height - 10]) {
|
|
1648
|
+
if (!fits(y)) continue;
|
|
1649
|
+
for (const x of [
|
|
1650
|
+
clampX(rect.left),
|
|
1651
|
+
clampX((bar?.right ?? 0) + 8),
|
|
1652
|
+
clampX((bar?.left ?? 0) - 8 - width)
|
|
1653
|
+
]) {
|
|
1654
|
+
if (!hits(x, y)) {
|
|
1655
|
+
place = [x, y];
|
|
1656
|
+
break;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
if (place) break;
|
|
1660
|
+
}
|
|
1661
|
+
if (!place) {
|
|
1662
|
+
const y = fits(rect.bottom + 10) ? rect.bottom + 10 : Math.max(8, rect.top - height - 10);
|
|
1663
|
+
place = [clampX(rect.left), y];
|
|
1664
|
+
}
|
|
1665
|
+
card.style.left = `${place[0]}px`;
|
|
1666
|
+
card.style.top = `${place[1]}px`;
|
|
1520
1667
|
}
|
|
1521
1668
|
function onSelect(elements) {
|
|
1522
1669
|
if (mode !== "comment" || composer) return;
|
|
@@ -1562,7 +1709,7 @@ function start(ctx, options = {}) {
|
|
|
1562
1709
|
const own = single ? index.get(single) : null;
|
|
1563
1710
|
const tagged = own && own.recordRef === recordRef ? own : null;
|
|
1564
1711
|
const field = fieldOf(data, tagged);
|
|
1565
|
-
const proposable =
|
|
1712
|
+
const proposable = proposableField(tagged, field);
|
|
1566
1713
|
const target = single;
|
|
1567
1714
|
const before = field?.type === "URL" && target?.localName === "a" ? target.getAttribute("href") ?? "" : normalizeText(target?.textContent ?? "");
|
|
1568
1715
|
const spotPin = point ? anchors[0] : null;
|
|
@@ -1834,13 +1981,17 @@ function start(ctx, options = {}) {
|
|
|
1834
1981
|
const field = fieldOf(data, tagged);
|
|
1835
1982
|
const kind = editKindFor(tagged, field);
|
|
1836
1983
|
if (!kind || !field) {
|
|
1837
|
-
|
|
1984
|
+
openInfoCard(tagged);
|
|
1838
1985
|
return;
|
|
1839
1986
|
}
|
|
1987
|
+
closeInfo();
|
|
1840
1988
|
const element = tagged.element;
|
|
1841
1989
|
const pageUrl = currentPageUrl(win);
|
|
1842
1990
|
const anchors = [toApiAnchor(buildAnchor(element, index, pageUrl, doc, win), tagged.recordRef)];
|
|
1843
1991
|
const before = field.type === "URL" && element.localName === "a" ? element.getAttribute("href") ?? "" : normalizeText(element.textContent ?? "");
|
|
1992
|
+
const stored2 = data?.values?.[tagged.recordRef]?.[tagged.fieldKey];
|
|
1993
|
+
const legacy = field.type === "NUMBER" && stored2 === void 0 ? parseProposed("NUMBER", before) : null;
|
|
1994
|
+
const raw = legacy?.ok ? legacy.value : stored2;
|
|
1844
1995
|
const inline = kind === "text" ? beginInlineEdit(element) : null;
|
|
1845
1996
|
const session = {
|
|
1846
1997
|
element,
|
|
@@ -1851,6 +2002,9 @@ function start(ctx, options = {}) {
|
|
|
1851
2002
|
pageUrl,
|
|
1852
2003
|
anchors,
|
|
1853
2004
|
before,
|
|
2005
|
+
raw,
|
|
2006
|
+
picked: raw,
|
|
2007
|
+
returnFocus: doc.activeElement,
|
|
1854
2008
|
revert: inline?.revert ?? (() => {
|
|
1855
2009
|
}),
|
|
1856
2010
|
keep: inline?.keep ?? (() => {
|
|
@@ -1863,58 +2017,228 @@ function start(ctx, options = {}) {
|
|
|
1863
2017
|
if (inline) {
|
|
1864
2018
|
element.addEventListener("blur", () => commitEdit(session), { once: true });
|
|
1865
2019
|
element.focus?.();
|
|
2020
|
+
} else if (kind === "choice") {
|
|
2021
|
+
openChoiceCard(session);
|
|
2022
|
+
} else if (kind === "switch") {
|
|
2023
|
+
openSwitchCard(session);
|
|
1866
2024
|
} else {
|
|
1867
2025
|
openInputCard(session);
|
|
1868
2026
|
}
|
|
1869
2027
|
renderBottom();
|
|
1870
2028
|
}
|
|
2029
|
+
function openValueCard(session, body, focus, width) {
|
|
2030
|
+
const label = session.field.label || session.tagged.fieldKey;
|
|
2031
|
+
const card = el(
|
|
2032
|
+
doc,
|
|
2033
|
+
"div",
|
|
2034
|
+
{
|
|
2035
|
+
class: "card editor",
|
|
2036
|
+
attrs: { role: "dialog", "aria-label": label },
|
|
2037
|
+
on: {
|
|
2038
|
+
keydown: (event) => {
|
|
2039
|
+
const e = event;
|
|
2040
|
+
if (e.key !== "Enter" || e.target.getAttribute("role") === "option") return;
|
|
2041
|
+
e.preventDefault();
|
|
2042
|
+
commitEdit(session);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
},
|
|
2046
|
+
[
|
|
2047
|
+
body,
|
|
2048
|
+
el(doc, "div", { class: "keys" }, [
|
|
2049
|
+
kbd(doc, "Enter"),
|
|
2050
|
+
m.enterSaves,
|
|
2051
|
+
kbd(doc, "Esc"),
|
|
2052
|
+
m.escCancels
|
|
2053
|
+
])
|
|
2054
|
+
]
|
|
2055
|
+
);
|
|
2056
|
+
card.style.width = `${width}px`;
|
|
2057
|
+
session.card = card;
|
|
2058
|
+
layer.append(card);
|
|
2059
|
+
position(card, session.element.getBoundingClientRect(), width);
|
|
2060
|
+
focus.focus();
|
|
2061
|
+
}
|
|
1871
2062
|
function openInputCard(session) {
|
|
2063
|
+
const { field } = session;
|
|
2064
|
+
const spec = field.type === "URL" ? { type: "url", text: session.before, min: void 0, max: void 0, step: void 0 } : inputSpec(field, session.raw);
|
|
1872
2065
|
const input = el(doc, "input", {
|
|
1873
2066
|
class: "value",
|
|
1874
2067
|
attrs: {
|
|
1875
|
-
"aria-label":
|
|
1876
|
-
type:
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
input.value = session.before;
|
|
1881
|
-
input.addEventListener("keydown", (event) => {
|
|
1882
|
-
if (event.key === "Enter") {
|
|
1883
|
-
event.preventDefault();
|
|
1884
|
-
commitEdit(session);
|
|
2068
|
+
"aria-label": field.label || session.tagged.fieldKey,
|
|
2069
|
+
type: spec.type,
|
|
2070
|
+
min: spec.min === void 0 ? null : String(spec.min),
|
|
2071
|
+
max: spec.max === void 0 ? null : String(spec.max),
|
|
2072
|
+
step: spec.step === void 0 ? null : String(spec.step)
|
|
1885
2073
|
}
|
|
1886
2074
|
});
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
closeButton(m.close, () => cancelEdit(session))
|
|
1894
|
-
]),
|
|
1895
|
-
el(doc, "div", { class: "content" }, [
|
|
2075
|
+
input.value = spec.text;
|
|
2076
|
+
session.input = input;
|
|
2077
|
+
const unit2 = field.type === "CURRENCY" ? field.currency ?? null : null;
|
|
2078
|
+
openValueCard(
|
|
2079
|
+
session,
|
|
2080
|
+
unit2 ? el(doc, "div", { class: "with-unit" }, [
|
|
1896
2081
|
input,
|
|
1897
|
-
el(doc, "
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2082
|
+
el(doc, "span", { class: "muted", text: unit2 })
|
|
2083
|
+
]) : input,
|
|
2084
|
+
input,
|
|
2085
|
+
field.type === "URL" ? 280 : 220
|
|
2086
|
+
);
|
|
2087
|
+
}
|
|
2088
|
+
function openChoiceCard(session) {
|
|
2089
|
+
const buttons = (session.field.options ?? []).map(
|
|
2090
|
+
(option) => el(
|
|
2091
|
+
doc,
|
|
2092
|
+
"button",
|
|
2093
|
+
{
|
|
2094
|
+
class: "option",
|
|
2095
|
+
attrs: {
|
|
2096
|
+
type: "button",
|
|
2097
|
+
role: "option",
|
|
2098
|
+
"aria-selected": String(option.id === session.raw),
|
|
2099
|
+
tabindex: "-1"
|
|
2100
|
+
},
|
|
2101
|
+
on: {
|
|
2102
|
+
click: () => {
|
|
2103
|
+
session.picked = option.id;
|
|
2104
|
+
commitEdit(session);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
},
|
|
2108
|
+
[
|
|
2109
|
+
el(doc, "span", { class: "option-label", text: option.label }),
|
|
2110
|
+
option.id === session.raw ? icon(doc, "tick", 14) : null
|
|
2111
|
+
]
|
|
2112
|
+
)
|
|
2113
|
+
);
|
|
2114
|
+
const first = buttons.find((button) => button.getAttribute("aria-selected") === "true") ?? buttons[0];
|
|
2115
|
+
first.tabIndex = 0;
|
|
2116
|
+
const list2 = el(
|
|
2117
|
+
doc,
|
|
2118
|
+
"div",
|
|
2119
|
+
{
|
|
2120
|
+
class: "options",
|
|
2121
|
+
attrs: { role: "listbox", "aria-label": m.options(session.field.label) },
|
|
2122
|
+
on: {
|
|
2123
|
+
keydown: (event) => {
|
|
2124
|
+
const e = event;
|
|
2125
|
+
const at = buttons.indexOf(host.root.activeElement);
|
|
2126
|
+
const to = { ArrowDown: at + 1, ArrowUp: at - 1, Home: 0, End: buttons.length - 1 }[e.key];
|
|
2127
|
+
if (to === void 0) return;
|
|
2128
|
+
e.preventDefault();
|
|
2129
|
+
buttons[Math.min(buttons.length - 1, Math.max(0, to))]?.focus();
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
},
|
|
2133
|
+
buttons
|
|
2134
|
+
);
|
|
2135
|
+
openValueCard(session, list2, first, 230);
|
|
2136
|
+
}
|
|
2137
|
+
function openSwitchCard(session) {
|
|
2138
|
+
session.picked = session.raw === true;
|
|
2139
|
+
const label = session.field.label || session.tagged.fieldKey;
|
|
2140
|
+
const toggle = el(
|
|
2141
|
+
doc,
|
|
2142
|
+
"button",
|
|
2143
|
+
{
|
|
2144
|
+
class: "switch",
|
|
2145
|
+
attrs: {
|
|
2146
|
+
type: "button",
|
|
2147
|
+
role: "switch",
|
|
2148
|
+
"aria-checked": String(session.picked),
|
|
2149
|
+
"aria-label": label
|
|
2150
|
+
},
|
|
2151
|
+
on: {
|
|
2152
|
+
click: () => {
|
|
2153
|
+
session.picked = !session.picked;
|
|
2154
|
+
toggle.setAttribute("aria-checked", String(session.picked));
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
},
|
|
2158
|
+
[el(doc, "span", { class: "knob" })]
|
|
2159
|
+
);
|
|
2160
|
+
openValueCard(
|
|
2161
|
+
session,
|
|
2162
|
+
el(doc, "div", { class: "switch-row" }, [
|
|
2163
|
+
el(doc, "span", { class: "title", text: label }),
|
|
2164
|
+
toggle
|
|
2165
|
+
]),
|
|
2166
|
+
toggle,
|
|
2167
|
+
220
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
function openInfoCard(tagged) {
|
|
2171
|
+
closeInfo();
|
|
2172
|
+
const field = fieldOf(data, tagged);
|
|
2173
|
+
const label = field?.label || tagged.fieldKey;
|
|
2174
|
+
const card = el(
|
|
2175
|
+
doc,
|
|
2176
|
+
"div",
|
|
2177
|
+
{ class: "card info", attrs: { role: "dialog", "aria-label": m.editInZap } },
|
|
2178
|
+
[
|
|
2179
|
+
icon(doc, "info", 16),
|
|
2180
|
+
el(doc, "div", { class: "toast-text" }, [
|
|
2181
|
+
el(doc, "span", { class: "title", text: m.editInZap }),
|
|
2182
|
+
el(doc, "span", {
|
|
2183
|
+
class: "muted",
|
|
2184
|
+
text: m.editInZapBody(label, field?.type ?? "", tagged.recordRef.startsWith("doc:"))
|
|
1903
2185
|
}),
|
|
1904
|
-
el(doc, "
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
2186
|
+
el(doc, "div", { class: "actions start" }, [
|
|
2187
|
+
el(
|
|
2188
|
+
doc,
|
|
2189
|
+
"button",
|
|
2190
|
+
{
|
|
2191
|
+
class: "btn btn-outline",
|
|
2192
|
+
attrs: { type: "button", "data-action": "open-field" },
|
|
2193
|
+
on: {
|
|
2194
|
+
click: () => {
|
|
2195
|
+
closeInfo();
|
|
2196
|
+
win.open(fieldEditorUrl(tagged), "_blank", "noopener");
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
},
|
|
2200
|
+
[icon(doc, "external", 14), m.openInZap]
|
|
2201
|
+
),
|
|
2202
|
+
el(
|
|
2203
|
+
doc,
|
|
2204
|
+
"button",
|
|
2205
|
+
{
|
|
2206
|
+
class: "btn btn-ghost",
|
|
2207
|
+
attrs: { type: "button", "data-action": "comment-field" },
|
|
2208
|
+
on: {
|
|
2209
|
+
click: () => {
|
|
2210
|
+
closeInfo();
|
|
2211
|
+
setMode("comment");
|
|
2212
|
+
if (mode === "comment") openComposer([tagged.element], null);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
},
|
|
2216
|
+
[icon(doc, "commentPlus", 14), m.comment]
|
|
2217
|
+
)
|
|
2218
|
+
])
|
|
1910
2219
|
])
|
|
1911
|
-
]
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
session.input = input;
|
|
2220
|
+
]
|
|
2221
|
+
);
|
|
2222
|
+
info = { tagged, card };
|
|
1915
2223
|
layer.append(card);
|
|
1916
|
-
position(card,
|
|
1917
|
-
|
|
2224
|
+
position(card, tagged.element.getBoundingClientRect(), 300);
|
|
2225
|
+
}
|
|
2226
|
+
function closeInfo() {
|
|
2227
|
+
info?.card.remove();
|
|
2228
|
+
info = null;
|
|
2229
|
+
}
|
|
2230
|
+
function fieldEditorUrl(tagged) {
|
|
2231
|
+
const own = !data?.records?.[tagged.recordRef];
|
|
2232
|
+
const base = data?.editorUrls?.[tagged.recordRef] ?? (own ? data?.viewer?.editorUrl : null) ?? null;
|
|
2233
|
+
const safe = zapUrl(base, ctx.zapOrigin);
|
|
2234
|
+
if (!base) return safe;
|
|
2235
|
+
try {
|
|
2236
|
+
const url = new URL(safe);
|
|
2237
|
+
url.searchParams.set("field", tagged.fieldKey);
|
|
2238
|
+
return url.href;
|
|
2239
|
+
} catch {
|
|
2240
|
+
return safe;
|
|
2241
|
+
}
|
|
1918
2242
|
}
|
|
1919
2243
|
function beginInlineEdit(target) {
|
|
1920
2244
|
const children = Array.from(target.childNodes);
|
|
@@ -1956,6 +2280,8 @@ function start(ctx, options = {}) {
|
|
|
1956
2280
|
session.card?.remove();
|
|
1957
2281
|
if (editing === session) editing = null;
|
|
1958
2282
|
renderBottom();
|
|
2283
|
+
const back = session.returnFocus;
|
|
2284
|
+
if (session.card && back?.isConnected) back.focus?.({ preventScroll: true });
|
|
1959
2285
|
}
|
|
1960
2286
|
function cancelEdit(session) {
|
|
1961
2287
|
if (session.done) return;
|
|
@@ -1963,42 +2289,77 @@ function start(ctx, options = {}) {
|
|
|
1963
2289
|
}
|
|
1964
2290
|
function commitEdit(session) {
|
|
1965
2291
|
if (session.done) return;
|
|
1966
|
-
const
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
2292
|
+
const { field, kind } = session;
|
|
2293
|
+
const text = kind === "text" || field.type === "URL";
|
|
2294
|
+
let value;
|
|
2295
|
+
if (text) {
|
|
2296
|
+
const parsed = parseProposed(field.type, typedValue(session));
|
|
2297
|
+
const before = parseProposed(field.type, session.before);
|
|
2298
|
+
if (parsed.ok && before.ok && sameValue(before.value, parsed.value)) {
|
|
2299
|
+
finishEdit(session, false);
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
if (!parsed.ok) return invalid(session);
|
|
2303
|
+
value = parsed.value;
|
|
2304
|
+
} else if (kind === "input") {
|
|
2305
|
+
if (session.input?.validity?.badInput) return invalid(session);
|
|
2306
|
+
const parsed = parseInput(field, typedValue(session));
|
|
2307
|
+
if (!parsed.ok) return invalid(session);
|
|
2308
|
+
value = parsed.value;
|
|
2309
|
+
} else {
|
|
2310
|
+
value = session.picked ?? null;
|
|
1971
2311
|
}
|
|
1972
|
-
|
|
1973
|
-
if (before.ok && sameValue(before.value, parsed.value)) {
|
|
2312
|
+
if (!text && sameRaw(field.type, session.raw, value)) {
|
|
1974
2313
|
finishEdit(session, false);
|
|
1975
2314
|
return;
|
|
1976
2315
|
}
|
|
1977
2316
|
finishEdit(session, true);
|
|
1978
|
-
void save(
|
|
1979
|
-
|
|
1980
|
-
locale: session.tagged.locale,
|
|
1981
|
-
value:
|
|
1982
|
-
|
|
2317
|
+
void save(
|
|
2318
|
+
session,
|
|
2319
|
+
{ fieldKey: session.tagged.fieldKey, locale: session.tagged.locale, value },
|
|
2320
|
+
kind === "choice" ? field.options?.find((option) => option.id === value)?.label : kind === "switch" ? value ? m.yes : m.no : void 0
|
|
2321
|
+
);
|
|
1983
2322
|
}
|
|
1984
|
-
|
|
2323
|
+
function invalid(session) {
|
|
2324
|
+
finishEdit(session, false);
|
|
2325
|
+
setToast({ title: m.saveFailed, body: m.invalidNumber, error: true });
|
|
2326
|
+
}
|
|
2327
|
+
async function save(session, proposal, display) {
|
|
1985
2328
|
setStatus("saving");
|
|
1986
2329
|
const body = buildDraftBody({
|
|
1987
2330
|
recordRef: session.recordRef,
|
|
1988
2331
|
pageUrl: session.pageUrl,
|
|
1989
2332
|
anchors: session.anchors,
|
|
1990
|
-
proposal
|
|
2333
|
+
proposal,
|
|
2334
|
+
display
|
|
1991
2335
|
});
|
|
1992
2336
|
const draftKey = newIdempotencyKey(win);
|
|
1993
2337
|
const result = await auth.call((token) => saveToDraft(ctx, token, body, draftKey));
|
|
1994
2338
|
if (destroyed) return;
|
|
1995
2339
|
if (!result.ok) {
|
|
1996
|
-
session.revert();
|
|
1997
2340
|
setStatus(null);
|
|
2341
|
+
if (result.kind === "unchanged") return;
|
|
2342
|
+
session.revert();
|
|
1998
2343
|
onFailure(result, "edit");
|
|
1999
2344
|
return;
|
|
2000
2345
|
}
|
|
2001
2346
|
setStatus("saved");
|
|
2347
|
+
if (data && session.raw !== void 0)
|
|
2348
|
+
(data.values ?? (data.values = {}))[session.recordRef] = {
|
|
2349
|
+
...data.values[session.recordRef],
|
|
2350
|
+
[session.tagged.fieldKey]: proposal.value
|
|
2351
|
+
};
|
|
2352
|
+
showSaved(session, display);
|
|
2353
|
+
}
|
|
2354
|
+
function showSaved(session, display) {
|
|
2355
|
+
const { field, tagged, element } = session;
|
|
2356
|
+
if (session.kind === "text" || field.type === "URL") return;
|
|
2357
|
+
if (field.type === "ENUM" && display !== void 0 && tagged.source === "attr" && element.children.length === 0) {
|
|
2358
|
+
element.textContent = display;
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
rememberEditIntent(win);
|
|
2362
|
+
win.setTimeout(() => win.location.reload(), RELOAD_AFTER_SAVE_MS);
|
|
2002
2363
|
}
|
|
2003
2364
|
function endEdit() {
|
|
2004
2365
|
if (editing && !editing.done) commitEdit(editing);
|
|
@@ -2019,6 +2380,11 @@ function start(ctx, options = {}) {
|
|
|
2019
2380
|
closeComposer();
|
|
2020
2381
|
return;
|
|
2021
2382
|
}
|
|
2383
|
+
if (info && event.key === "Escape") {
|
|
2384
|
+
event.preventDefault();
|
|
2385
|
+
closeInfo();
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2022
2388
|
const session = editing;
|
|
2023
2389
|
if (!session || session.done) return;
|
|
2024
2390
|
if (event.key === "Escape") {
|
|
@@ -2124,11 +2490,9 @@ var CSS = `
|
|
|
2124
2490
|
border-radius: 7px; background: #101828; color: #FFFFFF; font-weight: 500; font-size: 12px; line-height: 18px;
|
|
2125
2491
|
white-space: nowrap; pointer-events: none; }
|
|
2126
2492
|
.tip:not(.on) { display: none; }
|
|
2127
|
-
.kbd { font-size: 11px; line-height: 16px; color: #CBD5E1; padding: 1px 5px; border-radius: 4px;
|
|
2128
|
-
background: rgba(255,255,255,.12); }
|
|
2129
2493
|
[data-enter] { animation: eelzap-in .16s cubic-bezier(.4,0,.2,1); }
|
|
2130
2494
|
@keyframes eelzap-in { from { opacity: 0; transform: scale(.96); } }
|
|
2131
|
-
@media (prefers-reduced-motion: reduce) { .seg, .tool, .grip, .pill { transition: none; } [data-enter] { animation: none; } }
|
|
2495
|
+
@media (prefers-reduced-motion: reduce) { .seg, .tool, .grip, .pill, .switch, .knob { transition: none; } [data-enter] { animation: none; } }
|
|
2132
2496
|
.tool.strong { color: ${TOKENS.fg}; }
|
|
2133
2497
|
.divider { width: 1px; height: 22px; background: ${TOKENS.border}; }
|
|
2134
2498
|
.avatar { width: 26px; height: 26px; border-radius: 7px; background: #DBEAFE; color: #1D4ED8;
|
|
@@ -2152,7 +2516,35 @@ var CSS = `
|
|
|
2152
2516
|
.refused { align-self: center; margin-right: 0; width: min(420px, calc(100vw - 16px)); align-items: flex-start; }
|
|
2153
2517
|
.toast > .icon { color: #16A34A; }
|
|
2154
2518
|
.refused > .icon, .toast-error > .icon { color: ${TOKENS.danger}; }
|
|
2155
|
-
.paused > .icon { color: ${TOKENS.muted}; }
|
|
2519
|
+
.paused > .icon, .info > .icon { color: ${TOKENS.muted}; }
|
|
2520
|
+
.editor { min-width: max-content; }
|
|
2521
|
+
.editor, .info { position: fixed; padding: 8px; border-radius: 7px;
|
|
2522
|
+
box-shadow: 0 4px 6px -1px rgba(15,23,42,.08), 0 2px 4px -2px rgba(15,23,42,.06); }
|
|
2523
|
+
.info { display: flex; gap: 10px; padding: 12px 14px; }
|
|
2524
|
+
.info > .icon { margin-top: 1px; }
|
|
2525
|
+
.info .title { font-weight: 500; }
|
|
2526
|
+
.info .muted { font-size: 12px; }
|
|
2527
|
+
.actions.start { justify-content: flex-start; margin-top: 10px; gap: 6px; }
|
|
2528
|
+
.actions.start .btn { height: 28px; gap: 7px; font-size: 12px; }
|
|
2529
|
+
.btn-ghost { border: 1px solid transparent; }
|
|
2530
|
+
.btn-ghost:hover { background: ${TOKENS.subtle}; }
|
|
2531
|
+
.keys { display: flex; align-items: center; gap: 5px; margin: 8px 2px 0; font-size: 12px; color: ${TOKENS.muted}; white-space: nowrap; }
|
|
2532
|
+
.editor input.value { height: 34px; border-radius: 8px; }
|
|
2533
|
+
.with-unit { display: flex; align-items: center; gap: 8px; }
|
|
2534
|
+
.options { display: flex; flex-direction: column; max-height: 260px; overflow: auto; }
|
|
2535
|
+
.option { width: 100%; height: 32px; gap: 8px; padding: 0 8px; border-radius: 6px; font-weight: 400; text-align: left; }
|
|
2536
|
+
.option:hover, .option:focus-visible { background: #F1F5F9; box-shadow: none; }
|
|
2537
|
+
.option-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
2538
|
+
.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 2px 2px 0; }
|
|
2539
|
+
.switch-row .title { font-weight: 500; }
|
|
2540
|
+
.switch { position: relative; width: 32px; height: 18px; border-radius: 999px; background: #CBD5E1; transition: background .15s; }
|
|
2541
|
+
.switch[aria-checked="true"] { background: ${TOKENS.primary}; }
|
|
2542
|
+
.knob { position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 999px; background: ${TOKENS.bg};
|
|
2543
|
+
box-shadow: 0 1px 2px rgba(0,0,0,.15); transition: left .15s; }
|
|
2544
|
+
.switch[aria-checked="true"] .knob { left: 16px; }
|
|
2545
|
+
.mark-chip { position: absolute; right: -5px; top: -27px; display: inline-flex; align-items: center; height: 20px;
|
|
2546
|
+
padding: 0 7px; border-radius: 6px; background: ${TOKENS.gold}; color: #FFFFFF; font: 600 12px Poppins, ui-sans-serif, system-ui, sans-serif;
|
|
2547
|
+
white-space: nowrap; }
|
|
2156
2548
|
.toast-text { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
|
|
2157
2549
|
.title { font-weight: 600; }
|
|
2158
2550
|
.head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
@@ -2188,4 +2580,4 @@ export {
|
|
|
2188
2580
|
start,
|
|
2189
2581
|
zapUrl
|
|
2190
2582
|
};
|
|
2191
|
-
//# sourceMappingURL=suggest-
|
|
2583
|
+
//# sourceMappingURL=suggest-KLS6B7AM.js.map
|