@panphora/clayjs 1.22.0 → 1.23.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/all.js +1 -1
- package/dist/clay-dom.js +1 -1
- package/dist/clay-events.js +1 -1
- package/dist/clay-internals.js +1 -1
- package/dist/clay-options.js +1 -1
- package/dist/clay-router.js +1 -1
- package/dist/clay-ui.js +1 -1
- package/dist/clay-utils.js +1 -1
- package/dist/clay.js +1 -1
- package/dist/clay.standalone.js +1228 -715
- package/dist/m/attrs/{save-freeze-MTFVVYV5.js → save-freeze-JZHKRQLR.js} +1 -1
- package/dist/m/{c-TV7JHSRL.js → c-24PMUJIV.js} +1 -1
- package/dist/m/{c-NBNRLZXO.js → c-2AYVYEI4.js} +1 -1
- package/dist/m/{c-VIW5X35H.js → c-2L6O5PDS.js} +1 -1
- package/dist/m/{c-FNIGX4FI.js → c-2RYOGUQM.js} +1 -1
- package/dist/m/{c-LDUNOR57.js → c-2Z22FCFK.js} +13 -25
- package/dist/m/{c-DKFSOJCV.js → c-2ZVOEU4M.js} +1 -1
- package/dist/m/{c-BJPZCWAV.js → c-37NAONZA.js} +1 -1
- package/dist/m/{c-CSNE5EIF.js → c-4PO3YGDC.js} +1 -1
- package/dist/m/{c-UWT4CXQ4.js → c-5OJ7YF2N.js} +1 -1
- package/dist/m/{c-CAWNFER6.js → c-5TIZ2I64.js} +1 -1
- package/dist/m/{c-ZO6VL2SC.js → c-5WMXEKFH.js} +1 -1
- package/dist/m/{c-4TWARTLS.js → c-6RDOKLN7.js} +1 -1
- package/dist/m/{c-6RC66MYY.js → c-6TYGD5AB.js} +1 -1
- package/dist/m/c-A4IP6MBC.js +1 -0
- package/dist/m/{c-ZAHTFCXQ.js → c-ALDVFPWL.js} +1 -1
- package/dist/m/{c-LV5MABYD.js → c-AYVGD6A6.js} +1 -1
- package/dist/m/{c-N33TPVTL.js → c-B6O7AJYG.js} +1 -1
- package/dist/m/{c-P3WATEDR.js → c-BCGWJYXW.js} +1 -1
- package/dist/m/{c-4OGEVKR3.js → c-BGKAT2FL.js} +1 -1
- package/dist/m/{c-N7Y52HJH.js → c-BXYSDKAN.js} +1 -1
- package/dist/m/c-CRPIDGXC.js +78 -0
- package/dist/m/c-CUMXGHVD.js +1 -0
- package/dist/m/{c-Q5KAQJAG.js → c-DTMZ3ZHI.js} +1 -1
- package/dist/m/{c-GY4ODHO2.js → c-ELYIIYRS.js} +1 -1
- package/dist/m/{c-67LGZ52V.js → c-FCSZ3DC5.js} +1 -1
- package/dist/m/{c-NLWEZYKJ.js → c-FQGHR32O.js} +1 -1
- package/dist/m/{c-PCEMXY2K.js → c-G4PWUDD5.js} +1 -1
- package/dist/m/{c-6KBKYLPO.js → c-G6COVIZB.js} +1 -1
- package/dist/m/c-GBTVCJXC.js +1 -0
- package/dist/m/c-GC2HICFJ.js +1 -0
- package/dist/m/{c-TMT5TL65.js → c-HCNWJE4A.js} +2 -2
- package/dist/m/{c-DZBZ57EE.js → c-JCOQVZYS.js} +1 -1
- package/dist/m/{c-FAXQU7MQ.js → c-JGHEACGK.js} +1 -1
- package/dist/m/{c-PWBJVHA4.js → c-JGLX6TBK.js} +1 -1
- package/dist/m/c-JNRKHYWO.js +1 -0
- package/dist/m/{c-6UEFPHHE.js → c-KDXIJEEF.js} +1 -1
- package/dist/m/{c-FMCPQ4VT.js → c-L4KMT7FO.js} +2 -2
- package/dist/m/{c-OI2BYWVV.js → c-LCYPMULG.js} +1 -1
- package/dist/m/{c-4RFZROCF.js → c-LIGQYZ7M.js} +4 -4
- package/dist/m/{c-QHVCUTJV.js → c-LKIWEPAV.js} +1 -1
- package/dist/m/{c-EDSPKYMA.js → c-LUXAB2XI.js} +1 -1
- package/dist/m/{c-DEAFCHDO.js → c-LWIDDWAR.js} +2 -2
- package/dist/m/c-MPSKIU2I.js +1 -0
- package/dist/m/{c-6PBXRWDX.js → c-NK33MMYF.js} +1 -1
- package/dist/m/{c-F5AER2ID.js → c-NQLWOHLH.js} +1 -1
- package/dist/m/{c-L7K5EXU2.js → c-NTHUTABN.js} +1 -1
- package/dist/m/{c-4PHDUV3H.js → c-NW54NHKE.js} +1 -1
- package/dist/m/{c-3AXJDL2P.js → c-OG6GFSUX.js} +1 -1
- package/dist/m/{c-MTQHWQC3.js → c-OKEGUICD.js} +1 -1
- package/dist/m/c-OPONJ2UK.js +128 -0
- package/dist/m/{c-LF6HNVPU.js → c-OWQWOGYI.js} +1 -1
- package/dist/m/{c-FDAXZ2PM.js → c-P6Q2ZTFQ.js} +1 -1
- package/dist/m/c-PKCCHJ5X.js +1 -0
- package/dist/m/c-PWJSJEJU.js +1 -0
- package/dist/m/{c-Z7RJANGO.js → c-PXLTUYZC.js} +1 -1
- package/dist/m/c-QEN6HZCA.js +1 -0
- package/dist/m/{c-26KVJS6J.js → c-QGAPG7OM.js} +1 -1
- package/dist/m/{c-HWR6GMGK.js → c-QIR5CAKM.js} +1 -1
- package/dist/m/{c-QJFYVCJO.js → c-QM5LXRDT.js} +1 -1
- package/dist/m/{c-MRURHLKV.js → c-QUGCKMWF.js} +1 -1
- package/dist/m/{c-KE33W4RE.js → c-QYYUVLVG.js} +1 -1
- package/dist/m/c-RO73MKKR.js +1 -0
- package/dist/m/c-SBCBXKOZ.js +26 -0
- package/dist/m/{c-XDVAZDUE.js → c-SGRIYOAJ.js} +1 -1
- package/dist/m/c-SKF5HL4I.js +4 -0
- package/dist/m/{c-IWTTOC2V.js → c-SRPC3VBE.js} +1 -1
- package/dist/m/{c-XZJ5IK6Q.js → c-T5NK2TVY.js} +1 -1
- package/dist/m/{c-VFES2BP5.js → c-TANBH7FI.js} +1 -1
- package/dist/m/{c-H6DXQEH2.js → c-THGQQDYY.js} +1 -1
- package/dist/m/{c-BV6FQQXM.js → c-TSBVMWAH.js} +1 -1
- package/dist/m/{c-QTC6VGA4.js → c-UD5JZTDL.js} +1 -1
- package/dist/m/{c-ZYEJLZEY.js → c-UDGEFWNU.js} +1 -1
- package/dist/m/{c-ZSCBEB7I.js → c-UHUNXWKN.js} +1 -1
- package/dist/m/{c-FWFJUU3T.js → c-VIRFF5HG.js} +1 -1
- package/dist/m/{c-GPTKI7AQ.js → c-VYJ7O4RI.js} +1 -1
- package/dist/m/{c-TUGVINJR.js → c-W5E5P2TZ.js} +1 -1
- package/dist/m/{c-WWE67K5Y.js → c-W7IPVCAA.js} +1 -1
- package/dist/m/{c-HAVGDXAL.js → c-WJ5DRBHT.js} +1 -1
- package/dist/m/{c-V2J5C5MT.js → c-WQZTI5P2.js} +1 -1
- package/dist/m/c-X5WC4PX5.js +1 -0
- package/dist/m/{c-ZKELCT4T.js → c-XCFQEWW6.js} +2 -2
- package/dist/m/c-XLQ3BDMM.js +1 -0
- package/dist/m/{c-FLBUYV3P.js → c-XX75FFNL.js} +1 -1
- package/dist/m/{c-JHJR5BQX.js → c-YK2L436K.js} +1 -1
- package/dist/m/{c-MEWUGR4D.js → c-YYMZWQLR.js} +1 -1
- package/dist/m/core/{admin-attrs-ZG7R5V3E.js → admin-attrs-U7IJNYBG.js} +1 -1
- package/dist/m/core/{autosave-GI3WNJYN.js → autosave-F5YQCKVL.js} +1 -1
- package/dist/m/core/{conflict-notice-CEI3XY6A.js → conflict-notice-42H4ASLA.js} +1 -1
- package/dist/m/core/{persist-3OPWJKZS.js → persist-CJXIL7D4.js} +1 -1
- package/dist/m/core/{save-OJ5NSLS7.js → save-ESPSQPLV.js} +1 -1
- package/dist/m/core/{save-core-DX3RFVO3.js → save-core-6SQHOR6W.js} +1 -1
- package/dist/m/core/snapshot-FJP5IJL3.js +1 -0
- package/dist/m/core/{unsaved-warning-HGYH7O4H.js → unsaved-warning-HNQVTA2D.js} +1 -1
- package/dist/m/internals/{index-V4WC44FU.js → index-NRPNDTSA.js} +1 -1
- package/dist/m/loader-FBDE2UAB.js +1 -0
- package/dist/m/manifest-1D2F5CA5.js +1 -0
- package/dist/m/manifest-edit-F7CC5CFD.js +1 -0
- package/dist/m/manifest.json +1090 -1020
- package/dist/m/page/{editor-BQEPCJLG.js → editor-OSKOW6RS.js} +2 -2
- package/dist/m/plugins/{ai-edit-45A2J6LC.js → ai-edit-VV4QTINH.js} +1 -1
- package/dist/m/plugins/{cms-IFTKWF4S.js → cms-H7DTMUQX.js} +1 -1
- package/dist/m/plugins/{indicator-IPZJ35K6.js → indicator-WVPGMOZ7.js} +1 -1
- package/dist/m/plugins/{live-GPQXSXSM.js → live-DCUBKXY4.js} +1 -1
- package/dist/m/plugins/menu-Q4MFFQ6E.js +1 -0
- package/dist/m/plugins/{page-BAVLWCP6.js → page-OAWUJQEL.js} +2 -2
- package/dist/m/plugins/{page-view-P3UGJFDZ.js → page-view-DZ26ZDDQ.js} +1 -1
- package/dist/m/plugins/{people-Z4RGVQAV.js → people-5XRNRP5U.js} +1 -1
- package/dist/m/plugins/{quickcrop-E34PUYA3.js → quickcrop-WWF5I422.js} +2 -2
- package/dist/m/plugins/{richclay-S4NVJYIK.js → richclay-PGOOJ4OK.js} +2 -2
- package/dist/m/plugins/{sortable-WJZZBM7R.js → sortable-266HY6TR.js} +1 -1
- package/dist/m/plugins/{source-KVN6YNTY.js → source-6BKMK4PO.js} +1 -1
- package/dist/m/plugins/{upgrade-KTSICB22.js → upgrade-BNZVFHMZ.js} +1 -1
- package/dist/m/plugins/visitor-drafts-2AUYKAIJ.js +20 -0
- package/dist/m/plugins/{visitor-drafts-owner-MZP45XSG.js → visitor-drafts-owner-Z5B2OQBR.js} +1 -1
- package/dist/m/plugins/visitor-menu-4LQMKLPP.js +1 -0
- package/dist/m/plugins/{wire-SYVIBNSE.js → wire-W2EV36KZ.js} +1 -1
- package/dist/m/{reader-core-RUIFH32Y.js → reader-core-POFQCKRV.js} +2 -2
- package/dist/m/sync/{live-sync-RGQMMWK2.js → live-sync-XGUPHI3I.js} +1 -1
- package/dist/m/ui/{index-EZGLEM5P.js → index-VGYAKAKI.js} +1 -1
- package/dist/offline-manifest.json +636 -570
- package/docs/guide.md +36 -25
- package/package.json +1 -1
- package/src/core/save-replace.js +5 -2
- package/src/core/serialize-steps.js +10 -2
- package/src/core/snapshot.js +37 -1
- package/src/core/upgrade-run.js +16 -2
- package/src/loader-logic.js +6 -2
- package/src/plugins/menu/dom-items.js +45 -0
- package/src/plugins/menu/looks.js +32 -0
- package/src/plugins/menu/shell-style.js +143 -0
- package/src/plugins/menu/shell.js +398 -0
- package/src/plugins/menu/style.js +15 -116
- package/src/plugins/menu/variants.js +56 -10
- package/src/plugins/menu/visitor.js +78 -0
- package/src/plugins/menu.js +21 -313
- package/src/plugins/visitor-drafts/inert.js +1 -0
- package/src/plugins/visitor-drafts/intro-style.js +6 -0
- package/src/plugins/visitor-drafts/intro.js +77 -25
- package/src/plugins/visitor-drafts/owner-chip.js +18 -23
- package/src/plugins/visitor-drafts/prompt.js +2 -2
- package/src/plugins/visitor-drafts/store.js +58 -5
- package/src/plugins/visitor-drafts.js +1 -0
- package/src/plugins/visitor-menu.js +13 -0
- package/src/sync/live-sync.js +2 -1
- package/src/sync/page-relay.js +2 -2
- package/src/ui/page-colour.js +17 -9
- package/dist/m/c-5AQYL27I.js +0 -1
- package/dist/m/c-DP4X4JID.js +0 -1
- package/dist/m/c-ECY5AZMH.js +0 -1
- package/dist/m/c-EGTIAL6P.js +0 -1
- package/dist/m/c-HDWZEJHM.js +0 -183
- package/dist/m/c-ILGQ3UXQ.js +0 -1
- package/dist/m/c-JFST6QE7.js +0 -1
- package/dist/m/c-JH5FF4NL.js +0 -20
- package/dist/m/c-KV7R47FR.js +0 -1
- package/dist/m/c-MHI3GYKF.js +0 -1
- package/dist/m/c-NNNR7OXA.js +0 -3
- package/dist/m/c-VSPAFOKU.js +0 -1
- package/dist/m/core/snapshot-O7II2AGG.js +0 -1
- package/dist/m/loader-V6VI46HU.js +0 -1
- package/dist/m/manifest-83C18DED.js +0 -1
- package/dist/m/manifest-edit-B28C1985.js +0 -1
- package/dist/m/plugins/menu-UN3KRCOF.js +0 -1
- package/dist/m/plugins/visitor-drafts-2AWNT23K.js +0 -20
- /package/dist/m/plugins/{quickcrop-E34PUYA3.js.LEGAL.txt → quickcrop-WWF5I422.js.LEGAL.txt} +0 -0
- /package/dist/m/plugins/{richclay-S4NVJYIK.js.LEGAL.txt → richclay-PGOOJ4OK.js.LEGAL.txt} +0 -0
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
// only. Keep deletes every other look, after which the page is an ordinary one-look
|
|
6
6
|
// document. A choice and Keep are undo entries and saves, like the SEO panel's Done.
|
|
7
7
|
import { modeRefusalText } from "../../core/mode-refusal.js";
|
|
8
|
+
import { OFF, labelOf, looks, nameOf, paintLook, recordLook } from "./looks.js";
|
|
9
|
+
|
|
10
|
+
export { looks };
|
|
8
11
|
|
|
9
|
-
const OFF = "not all";
|
|
10
12
|
const RECEIPT = "variants-choose";
|
|
11
13
|
const KEEP_ID = "clay.variants.keep";
|
|
12
14
|
const CHOOSE_LABEL = "Change the page look";
|
|
@@ -19,10 +21,13 @@ const NEXT =
|
|
|
19
21
|
'<svg viewBox="0 0 6 10" aria-hidden="true" focusable="false"><rect width="2" height="2"/><rect x="2" y="2" width="2" height="2"/>' +
|
|
20
22
|
'<rect x="4" y="4" width="2" height="2"/><rect x="2" y="6" width="2" height="2"/><rect y="8" width="2" height="2"/></svg>';
|
|
21
23
|
|
|
22
|
-
export const looks = () => [...document.querySelectorAll("head style[clay-variant]")];
|
|
23
|
-
const nameOf = (style) => style.getAttribute("clay-variant");
|
|
24
24
|
const names = () => looks().map(nameOf);
|
|
25
25
|
|
|
26
|
+
const labelFor = (name) => {
|
|
27
|
+
const style = looks().find((candidate) => nameOf(candidate) === name);
|
|
28
|
+
return style ? labelOf(style) : `Variant ${name}`;
|
|
29
|
+
};
|
|
30
|
+
|
|
26
31
|
// The first block without media="not all" is the saved choice. A merge or a hand edit
|
|
27
32
|
// can leave none or several; this tab paints the first one either way, and the next
|
|
28
33
|
// choice writes a clean state.
|
|
@@ -52,17 +57,20 @@ async function afterBaseline() {
|
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
function joinNames(list) {
|
|
55
|
-
const named = list.map(
|
|
60
|
+
const named = list.map(labelFor);
|
|
56
61
|
return named.length < 2 ? named.join("") : `${named.slice(0, -1).join(", ")} and ${named[named.length - 1]}`;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
export function attachVariants({ root, capsule, addItem, removeItem, note, currentMode }) {
|
|
65
|
+
paintLook();
|
|
66
|
+
|
|
60
67
|
let previewing = null;
|
|
61
68
|
let pending = null;
|
|
62
69
|
let copy = null;
|
|
63
70
|
let pill = null;
|
|
64
71
|
let drawn = null;
|
|
65
72
|
|
|
73
|
+
const wantsRow = Boolean(document.querySelector("[clay-menu-slot]")?.hasAttribute("clay-menu-looks"));
|
|
66
74
|
const isEdit = () => currentMode() === "edit";
|
|
67
75
|
const shown = () => previewing ?? savedName();
|
|
68
76
|
const storage = () => window.clay && window.clay.storage && window.clay.storage.session;
|
|
@@ -102,6 +110,7 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
|
|
|
102
110
|
if (!names().includes(name)) return { ok: false, code: "unknown-variant" };
|
|
103
111
|
pending = previewing = null;
|
|
104
112
|
writeHead(CHOOSE_LABEL, () => {
|
|
113
|
+
recordLook(name);
|
|
105
114
|
for (const style of looks()) {
|
|
106
115
|
if (nameOf(style) === name) style.removeAttribute("media");
|
|
107
116
|
else if (style.getAttribute("media") !== OFF) style.setAttribute("media", OFF);
|
|
@@ -121,6 +130,9 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
|
|
|
121
130
|
function keepNow(name) {
|
|
122
131
|
pending = previewing = null;
|
|
123
132
|
writeHead(KEEP_LABEL, () => {
|
|
133
|
+
// The kept block becomes an ordinary stylesheet, so `look` is what still names the
|
|
134
|
+
// theme: an upgrade brings the blocks back and paints this one from it.
|
|
135
|
+
recordLook(name);
|
|
124
136
|
for (const style of looks()) {
|
|
125
137
|
if (nameOf(style) === name) {
|
|
126
138
|
style.removeAttribute("clay-variant");
|
|
@@ -139,7 +151,7 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
|
|
|
139
151
|
const name = savedName();
|
|
140
152
|
const others = names().filter((other) => other !== name);
|
|
141
153
|
if (!others.length) return Promise.resolve({ ok: false, code: "unknown-variant" });
|
|
142
|
-
const text = `Keep
|
|
154
|
+
const text = `Keep ${labelFor(name)} and delete ${joinNames(others)}? The switcher goes away. Version history still has them.`;
|
|
143
155
|
return import("../../ui/dialogs.js")
|
|
144
156
|
.then((m) => m.consent(text))
|
|
145
157
|
.then(
|
|
@@ -220,20 +232,46 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
|
|
|
220
232
|
if (all.length < 2) {
|
|
221
233
|
pill?.remove();
|
|
222
234
|
pill = null;
|
|
235
|
+
removeItem("clay.look");
|
|
236
|
+
removeItem("clay.look.use");
|
|
223
237
|
removeItem(KEEP_ID);
|
|
224
238
|
return;
|
|
225
239
|
}
|
|
240
|
+
if (root.hasAttribute("data-embedded")) {
|
|
241
|
+
const name = shown();
|
|
242
|
+
if (wantsRow) {
|
|
243
|
+
addItem({
|
|
244
|
+
id: "clay.look",
|
|
245
|
+
section: "start",
|
|
246
|
+
stepper: {
|
|
247
|
+
label: "Switch look",
|
|
248
|
+
value: `${labelFor(name)} ${all.indexOf(name) + 1}/${all.length}`,
|
|
249
|
+
prev: () => step(-1),
|
|
250
|
+
next: () => step(1),
|
|
251
|
+
prevLabel: "Previous look",
|
|
252
|
+
nextLabel: "Next look",
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
removeItem("clay.look");
|
|
257
|
+
}
|
|
258
|
+
if (!isEdit() && previewing !== null) addItem({ id: "clay.look.use", section: "start", label: "Use this look", run: useShown });
|
|
259
|
+
else removeItem("clay.look.use");
|
|
260
|
+
if (isEdit()) addItem({ id: KEEP_ID, label: `Keep ${labelFor(savedName())}, remove the others…`, run: () => keep() });
|
|
261
|
+
else removeItem(KEEP_ID);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
226
264
|
if (!pill) pill = buildPill();
|
|
227
265
|
if (!pill.isConnected) root.insertBefore(pill, root.querySelector(".clay-roster") ?? capsule);
|
|
228
266
|
const name = shown();
|
|
229
267
|
const label = pill.querySelector(".clay-variants-name");
|
|
230
|
-
label.textContent =
|
|
268
|
+
label.textContent = `${labelFor(name)} `;
|
|
231
269
|
const count = document.createElement("small");
|
|
232
270
|
count.textContent = `(${all.indexOf(name) + 1}/${all.length})`;
|
|
233
271
|
label.append(count);
|
|
234
272
|
pill.querySelector(".clay-variants-use").hidden = isEdit() || previewing === null;
|
|
235
273
|
pill.querySelector(".clay-variants-keep").hidden = !isEdit();
|
|
236
|
-
if (isEdit()) addItem({ id: KEEP_ID, label: `Keep
|
|
274
|
+
if (isEdit()) addItem({ id: KEEP_ID, label: `Keep ${labelFor(savedName())}, remove the others…`, run: () => keep() });
|
|
237
275
|
else removeItem(KEEP_ID);
|
|
238
276
|
}
|
|
239
277
|
|
|
@@ -261,22 +299,30 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
|
|
|
261
299
|
refresh();
|
|
262
300
|
});
|
|
263
301
|
|
|
302
|
+
const followRecord = () => {
|
|
303
|
+
if (document.documentElement.hasAttribute("look") && names().length > 1) recordLook(savedName());
|
|
304
|
+
};
|
|
305
|
+
|
|
264
306
|
// A merge can rebuild a look block, which clears its CSSOM flag, or bring a peer's
|
|
265
307
|
// choice or Keep; painting again settles all three.
|
|
266
308
|
document.addEventListener("clay:sync-applied", () => {
|
|
267
|
-
if (current())
|
|
309
|
+
if (!current()) return;
|
|
310
|
+
followRecord();
|
|
311
|
+
refresh();
|
|
268
312
|
});
|
|
269
313
|
|
|
270
314
|
// An undo or redo of a choice or of Keep brings the blocks back or moves the saved one.
|
|
271
315
|
// Every other entry leaves the looks alone, so only a change redraws and announces.
|
|
272
316
|
document.addEventListener("clay:undo-change", () => {
|
|
273
|
-
if (current()
|
|
317
|
+
if (!current() || state() === drawn) return;
|
|
318
|
+
followRecord();
|
|
319
|
+
refresh();
|
|
274
320
|
});
|
|
275
321
|
|
|
276
322
|
api = window.clay.variants = {
|
|
277
323
|
list: () => {
|
|
278
324
|
const active = savedName();
|
|
279
|
-
return names().map((name) => ({ name, label:
|
|
325
|
+
return names().map((name) => ({ name, label: labelFor(name), active: name === active }));
|
|
280
326
|
},
|
|
281
327
|
get active() {
|
|
282
328
|
return savedName();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createShell } from "./shell.js";
|
|
2
|
+
import { attachDomItems } from "./dom-items.js";
|
|
3
|
+
import { MENU_SAFETY, SHELL_BASE, SHELL_FLOATING, MENU_SKIN, MENU_TOUCH } from "./shell-style.js";
|
|
4
|
+
import { activeLook, labelOf, looks, nameOf, paintLook } from "./looks.js";
|
|
5
|
+
|
|
6
|
+
function attachLooks({ addItem, removeItem, slot }) {
|
|
7
|
+
const announce = () => {
|
|
8
|
+
const name = activeLook() && nameOf(activeLook());
|
|
9
|
+
document.dispatchEvent(new CustomEvent("clay:variant-change", { detail: { active: name, shown: name } }));
|
|
10
|
+
};
|
|
11
|
+
const draw = () => {
|
|
12
|
+
const all = looks();
|
|
13
|
+
if (all.length < 2 || !slot?.hasAttribute("clay-menu-looks")) return removeItem("clay.look");
|
|
14
|
+
const current = activeLook();
|
|
15
|
+
const by = (delta) => () => choose(nameOf(all[(all.indexOf(activeLook()) + delta + all.length) % all.length]));
|
|
16
|
+
addItem({
|
|
17
|
+
id: "clay.look",
|
|
18
|
+
section: "start",
|
|
19
|
+
stepper: {
|
|
20
|
+
label: "Switch look",
|
|
21
|
+
value: `${labelOf(current)} ${all.indexOf(current) + 1}/${all.length}`,
|
|
22
|
+
prev: by(-1),
|
|
23
|
+
next: by(1),
|
|
24
|
+
prevLabel: "Previous look",
|
|
25
|
+
nextLabel: "Next look",
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const choose = (name) => {
|
|
30
|
+
if (!looks().some((style) => nameOf(style) === name)) return { ok: false, code: "unknown-variant" };
|
|
31
|
+
document.documentElement.setAttribute("look", name);
|
|
32
|
+
paintLook(name);
|
|
33
|
+
window.clay?.save?.soon?.();
|
|
34
|
+
draw();
|
|
35
|
+
announce();
|
|
36
|
+
return { ok: true };
|
|
37
|
+
};
|
|
38
|
+
if (looks().length >= 2) {
|
|
39
|
+
window.clay.variants = {
|
|
40
|
+
list: () => looks().map((style) => ({ name: nameOf(style), label: labelOf(style), active: style === activeLook() })),
|
|
41
|
+
get active() {
|
|
42
|
+
return activeLook() && nameOf(activeLook());
|
|
43
|
+
},
|
|
44
|
+
get shown() {
|
|
45
|
+
return activeLook() && nameOf(activeLook());
|
|
46
|
+
},
|
|
47
|
+
preview: choose,
|
|
48
|
+
choose,
|
|
49
|
+
keep: () => Promise.resolve({ ok: false, code: "not-editing" }),
|
|
50
|
+
};
|
|
51
|
+
document.addEventListener("clay:drafts-restored", () => {
|
|
52
|
+
draw();
|
|
53
|
+
announce();
|
|
54
|
+
});
|
|
55
|
+
draw();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const START_OVER = "Start over? Your changes in this browser are deleted, and the page goes back to how its owner saved it.";
|
|
60
|
+
|
|
61
|
+
function startOver() {
|
|
62
|
+
return import("../../ui/dialogs.js")
|
|
63
|
+
.then((m) => m.consent(START_OVER))
|
|
64
|
+
.then(() => window.clay.visitorDrafts.reset(), () => false);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function start() {
|
|
68
|
+
if (document.querySelector('clay-editor-ui[data-clay-ui="menu"]')) return;
|
|
69
|
+
const shell = createShell({ sections: [null] });
|
|
70
|
+
const { slot, embedded, skin, addItem, removeItem, open, close, note, goo } = shell;
|
|
71
|
+
shell.mount(embedded ? [MENU_SAFETY, SHELL_BASE, skin ?? MENU_SKIN, MENU_TOUCH] : [MENU_SAFETY, SHELL_BASE, SHELL_FLOATING, MENU_SKIN, MENU_TOUCH]);
|
|
72
|
+
attachLooks({ addItem, removeItem, slot });
|
|
73
|
+
addItem({ id: "clay.download", label: "Download a copy", run: () => window.clay.visitorDrafts.download() });
|
|
74
|
+
addItem({ id: "clay.start-over", label: "Start over", run: startOver });
|
|
75
|
+
attachDomItems(shell);
|
|
76
|
+
window.clay.menu = { addItem, removeItem, open, close, note, embedded, button: goo };
|
|
77
|
+
document.dispatchEvent(new CustomEvent("clay:menu-ready"));
|
|
78
|
+
}
|
package/src/plugins/menu.js
CHANGED
|
@@ -3,21 +3,18 @@
|
|
|
3
3
|
// file, a sync frame, the dirty check or undo.
|
|
4
4
|
import { canEdit, isDraftMode, isEditMode, mode as startMode } from "../core/is-edit-mode.js";
|
|
5
5
|
import onDomReady from "../lib/dom-ready.js";
|
|
6
|
-
import CLAYGOO from "./menu/claygoo.svg.js";
|
|
7
6
|
import { modeRefusalText } from "../core/mode-refusal.js";
|
|
8
7
|
import { MENU_BASE, MENU_FLOATING, MENU_SKIN, MENU_TOUCH, MENU_SURFACE } from "./menu/style.js";
|
|
9
8
|
import { attachPlacement } from "./menu/placement.js";
|
|
10
9
|
import { attachActions } from "./menu/actions.js";
|
|
11
10
|
import { attachPeople } from "./menu/people.js";
|
|
12
11
|
import { attachVariants } from "./menu/variants.js";
|
|
12
|
+
import { attachDomItems } from "./menu/dom-items.js";
|
|
13
13
|
import { followLook } from "../ui/page-colour.js";
|
|
14
|
-
import {
|
|
14
|
+
import { createShell, TRACK } from "./menu/shell.js";
|
|
15
|
+
import { MENU_SAFETY } from "./menu/shell-style.js";
|
|
15
16
|
|
|
16
17
|
const SEO_ITEM = true;
|
|
17
|
-
const SKIN_VERSION = "1";
|
|
18
|
-
const EDGE_GAP = 8;
|
|
19
|
-
const DISMISS_ICON = '<svg viewBox="0 0 10 10" aria-hidden="true" focusable="false"><path d="M1 0 5 4l4-4 1 1-4 4 4 4-1 1-4-4-4 4-1-1 4-4-4-4z"/></svg>';
|
|
20
|
-
const NOTE_MS = 6000;
|
|
21
18
|
const INTRO_GAP = 12;
|
|
22
19
|
const GRIP =
|
|
23
20
|
'<svg viewBox="0 0 6 12" aria-hidden="true" focusable="false"><rect width="2" height="2"/><rect x="4" width="2" height="2"/>' +
|
|
@@ -33,289 +30,46 @@ const SECTIONS = [
|
|
|
33
30
|
["clay.duplicate", "clay.download", "clay.export", "clay.reveal"],
|
|
34
31
|
["clay.versions"],
|
|
35
32
|
null,
|
|
36
|
-
["upgrade"],
|
|
33
|
+
["clay.mode", "upgrade"],
|
|
37
34
|
];
|
|
38
|
-
const SLOTTED = new Set(SECTIONS.flat().filter(Boolean));
|
|
39
35
|
|
|
40
36
|
// clay.mode is the live answer; the imported mode is the one the page booted with.
|
|
41
37
|
const currentMode = () =>
|
|
42
38
|
window.clay && typeof window.clay.mode === "string" ? window.clay.mode : startMode;
|
|
43
39
|
|
|
44
|
-
// The first [clay-menu-slot] on the page holds the menu. With none, it floats in a corner.
|
|
45
|
-
function findSlot() {
|
|
46
|
-
const slots = document.querySelectorAll("[clay-menu-slot]");
|
|
47
|
-
if (slots.length > 1) console.warn("[clay] This page has more than one clay-menu-slot. The menu uses the first.");
|
|
48
|
-
return slots[0] || null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// A skin the page wrote, in <style clay-menu-skin="1" media="not all">. The media keeps it
|
|
52
|
-
// off the page itself. An unknown version means the default skin.
|
|
53
|
-
function pageSkin() {
|
|
54
|
-
const style = document.querySelector("style[clay-menu-skin]");
|
|
55
|
-
if (!style) return null;
|
|
56
|
-
if (style.getAttribute("clay-menu-skin") !== SKIN_VERSION) {
|
|
57
|
-
console.warn(`[clay] This runtime reads clay-menu-skin="${SKIN_VERSION}". The menu keeps its default look.`);
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
return style.textContent;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
40
|
function mount() {
|
|
64
41
|
if (document.querySelector('clay-editor-ui[data-clay-ui="menu"]')) return;
|
|
65
42
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
43
|
+
const shell = createShell({
|
|
44
|
+
sections: SECTIONS,
|
|
45
|
+
capsuleStart:
|
|
46
|
+
`<button type="button" class="clay-menu-grip" aria-label="Move the menu. Drag it, or use the arrow keys to move it to another corner.">${GRIP}</button>` +
|
|
47
|
+
`<button type="button" class="clay-menu-edit" part="edit" role="switch" aria-checked="false">` +
|
|
48
|
+
TRACK +
|
|
49
|
+
`<span class="clay-menu-label">Edit</span>` +
|
|
50
|
+
`</button>` +
|
|
51
|
+
`<span class="clay-menu-divider" aria-hidden="true"></span>`,
|
|
52
|
+
});
|
|
53
|
+
const { root, goo, slot, embedded, skin, addItem, removeItem, open, close, note: showNote, beforeOpen, focusedEl, mount: mountShell } = shell;
|
|
69
54
|
const hideSwitch = embedded && slot.hasAttribute("no-edit-switch");
|
|
70
|
-
|
|
71
|
-
const root = document.createElement("div");
|
|
72
|
-
root.className = "clay-menu-dock";
|
|
73
|
-
root.setAttribute("clay", "editor-ui");
|
|
74
|
-
root.setAttribute("part", "dock");
|
|
75
|
-
if (embedded) root.dataset.embedded = "";
|
|
76
|
-
root.dataset.corner = "top-right";
|
|
77
|
-
root.innerHTML =
|
|
78
|
-
`<div class="clay-menu" part="capsule" clay="editor-ui">` +
|
|
79
|
-
`<button type="button" class="clay-menu-grip" aria-label="Move the menu. Drag it, or use the arrow keys to move it to another corner.">${GRIP}</button>` +
|
|
80
|
-
`<button type="button" class="clay-menu-edit" part="edit" role="switch" aria-checked="false">` +
|
|
81
|
-
`<span class="clay-menu-track" aria-hidden="true"><span class="clay-menu-thumb"></span></span>` +
|
|
82
|
-
`<span class="clay-menu-label">Edit</span>` +
|
|
83
|
-
`</button>` +
|
|
84
|
-
`<span class="clay-menu-divider" aria-hidden="true"></span>` +
|
|
85
|
-
`<button type="button" class="clay-menu-goo" part="button" aria-haspopup="menu" aria-expanded="false" aria-label="Clay menu">${CLAYGOO}</button>` +
|
|
86
|
-
`</div>` +
|
|
87
|
-
`<div class="clay-menu-list" part="list" role="menu" aria-label="Clay menu" hidden></div>` +
|
|
88
|
-
`<div class="clay-menu-note" part="note" role="status" hidden></div>`;
|
|
89
|
-
|
|
90
55
|
const grip = root.querySelector(".clay-menu-grip");
|
|
91
56
|
const edit = root.querySelector(".clay-menu-edit");
|
|
92
|
-
const goo = root.querySelector(".clay-menu-goo");
|
|
93
|
-
const list = root.querySelector(".clay-menu-list");
|
|
94
|
-
const noteEl = root.querySelector(".clay-menu-note");
|
|
95
|
-
const items = new Map();
|
|
96
57
|
|
|
97
58
|
if (embedded) {
|
|
98
59
|
grip.hidden = true;
|
|
99
|
-
const icon = slot.querySelector("template[clay-menu-icon]");
|
|
100
|
-
if (icon) goo.replaceChildren(icon.content.cloneNode(true));
|
|
101
|
-
const name = slot.getAttribute("clay-menu-label");
|
|
102
|
-
if (name) {
|
|
103
|
-
goo.setAttribute("aria-label", name);
|
|
104
|
-
goo.title = name;
|
|
105
|
-
list.setAttribute("aria-label", name);
|
|
106
|
-
}
|
|
107
60
|
if (hideSwitch) {
|
|
108
61
|
edit.hidden = true;
|
|
109
62
|
root.querySelector(".clay-menu-divider").hidden = true;
|
|
110
63
|
}
|
|
111
64
|
}
|
|
112
65
|
|
|
113
|
-
// Embedded, the list opens in the top layer, so a header's overflow cannot clip it.
|
|
114
|
-
const topLayer = embedded && typeof list.showPopover === "function";
|
|
115
|
-
if (topLayer) list.setAttribute("popover", "manual");
|
|
116
|
-
|
|
117
|
-
function placeList() {
|
|
118
|
-
const anchor = goo.getBoundingClientRect();
|
|
119
|
-
const { offsetWidth: width, offsetHeight: height } = list;
|
|
120
|
-
const left = Math.max(EDGE_GAP, Math.min(anchor.right - width, window.innerWidth - width - EDGE_GAP));
|
|
121
|
-
const below = anchor.bottom + EDGE_GAP;
|
|
122
|
-
const top = below + height > window.innerHeight - EDGE_GAP ? Math.max(EDGE_GAP, anchor.top - height - EDGE_GAP) : below;
|
|
123
|
-
list.style.left = `${Math.round(left)}px`;
|
|
124
|
-
list.style.top = `${Math.round(top)}px`;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Focus inside the shadow root: document.activeElement only ever names the host.
|
|
128
|
-
const focusedEl = () => root.getRootNode().activeElement || null;
|
|
129
|
-
const isOpen = () => !list.hidden;
|
|
130
|
-
|
|
131
|
-
function setExpanded(open) {
|
|
132
|
-
list.hidden = !open;
|
|
133
|
-
goo.setAttribute("aria-expanded", open ? "true" : "false");
|
|
134
|
-
if (!topLayer) return;
|
|
135
|
-
if (open) {
|
|
136
|
-
list.showPopover();
|
|
137
|
-
placeList();
|
|
138
|
-
} else if (list.matches(":popover-open")) {
|
|
139
|
-
list.hidePopover();
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const buttons = () => [...list.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')];
|
|
144
|
-
|
|
145
|
-
function focusItem(index) {
|
|
146
|
-
const all = buttons();
|
|
147
|
-
if (!all.length) return;
|
|
148
|
-
all[(index + all.length) % all.length].focus();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function close() {
|
|
152
|
-
if (!isOpen()) return;
|
|
153
|
-
setExpanded(false);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
66
|
let actions = null;
|
|
157
67
|
let people = null;
|
|
158
|
-
|
|
159
|
-
function open() {
|
|
160
|
-
if (isOpen()) return;
|
|
68
|
+
beforeOpen.push(() => {
|
|
161
69
|
people?.close({ restore: false });
|
|
162
70
|
actions?.refresh();
|
|
163
|
-
render();
|
|
164
|
-
setExpanded(true);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function itemButton(item) {
|
|
168
|
-
const link = item.href && !item.disabled;
|
|
169
|
-
const button = document.createElement(link ? "a" : "button");
|
|
170
|
-
if (link) {
|
|
171
|
-
button.href = item.href;
|
|
172
|
-
button.target = "_blank";
|
|
173
|
-
button.rel = "noopener";
|
|
174
|
-
} else {
|
|
175
|
-
button.type = "button";
|
|
176
|
-
}
|
|
177
|
-
button.setAttribute("role", "menuitem");
|
|
178
|
-
button.setAttribute("part", item.quiet ? "item quiet" : "item");
|
|
179
|
-
button.dataset.menuId = item.id;
|
|
180
|
-
if (item.badge && item.id === "upgrade") {
|
|
181
|
-
const label = document.createElement("span");
|
|
182
|
-
label.className = "clay-menu-upgrade";
|
|
183
|
-
label.textContent = item.label;
|
|
184
|
-
button.append(label);
|
|
185
|
-
} else {
|
|
186
|
-
button.textContent = item.label;
|
|
187
|
-
}
|
|
188
|
-
if (item.keys) {
|
|
189
|
-
const kbd = document.createElement("kbd");
|
|
190
|
-
kbd.textContent = item.keys;
|
|
191
|
-
button.append(kbd);
|
|
192
|
-
}
|
|
193
|
-
if (item.disabled) button.setAttribute("aria-disabled", "true");
|
|
194
|
-
button.addEventListener("click", () => {
|
|
195
|
-
if (item.disabled) return;
|
|
196
|
-
close();
|
|
197
|
-
if (item.run) item.run({ anchor: goo });
|
|
198
|
-
});
|
|
199
|
-
return button;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function itemNode(item) {
|
|
203
|
-
if (item.text) {
|
|
204
|
-
const line = document.createElement("p");
|
|
205
|
-
line.className = "clay-menu-version";
|
|
206
|
-
line.setAttribute("part", "text");
|
|
207
|
-
line.dataset.menuId = item.id;
|
|
208
|
-
line.textContent = item.label;
|
|
209
|
-
return line;
|
|
210
|
-
}
|
|
211
|
-
if (item.pair) {
|
|
212
|
-
const pair = document.createElement("div");
|
|
213
|
-
pair.className = "clay-menu-pair";
|
|
214
|
-
pair.dataset.menuId = item.id;
|
|
215
|
-
for (const part of item.pair) pair.append(itemButton(part));
|
|
216
|
-
return pair;
|
|
217
|
-
}
|
|
218
|
-
if (item.dismiss) return dismissibleRow(item);
|
|
219
|
-
return itemButton(item);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function dismissibleRow(item) {
|
|
223
|
-
const row = document.createElement("div");
|
|
224
|
-
row.className = "clay-menu-row";
|
|
225
|
-
row.dataset.menuId = item.id;
|
|
226
|
-
const dismiss = document.createElement("button");
|
|
227
|
-
dismiss.type = "button";
|
|
228
|
-
dismiss.setAttribute("role", "menuitem");
|
|
229
|
-
dismiss.setAttribute("part", "dismiss");
|
|
230
|
-
dismiss.dataset.menuId = `${item.id}.dismiss`;
|
|
231
|
-
dismiss.setAttribute("aria-label", item.dismiss.label);
|
|
232
|
-
dismiss.title = item.dismiss.label;
|
|
233
|
-
dismiss.innerHTML = DISMISS_ICON;
|
|
234
|
-
dismiss.addEventListener("click", () => {
|
|
235
|
-
removeItem(item.id);
|
|
236
|
-
item.dismiss.run?.();
|
|
237
|
-
});
|
|
238
|
-
row.append(itemButton(item), dismiss);
|
|
239
|
-
return row;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function sections() {
|
|
243
|
-
const inSection = (name) => [...items.values()].filter((item) => item.section === name);
|
|
244
|
-
const extra = [...items.keys()].filter((id) => !SLOTTED.has(id) && !items.get(id).section);
|
|
245
|
-
const fixed = SECTIONS.map((ids) => (ids || extra).map((id) => items.get(id)).filter(Boolean));
|
|
246
|
-
return [inSection("start"), ...fixed, inSection("end")].filter((group) => group.length);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function render() {
|
|
250
|
-
const now = focusedEl();
|
|
251
|
-
const focused = now && list.contains(now) ? now.dataset.menuId : null;
|
|
252
|
-
list.textContent = "";
|
|
253
|
-
sections().forEach((group, index) => {
|
|
254
|
-
if (index) {
|
|
255
|
-
const sep = document.createElement("div");
|
|
256
|
-
sep.className = "clay-menu-sep";
|
|
257
|
-
sep.setAttribute("role", "separator");
|
|
258
|
-
sep.setAttribute("part", "separator");
|
|
259
|
-
list.append(sep);
|
|
260
|
-
}
|
|
261
|
-
for (const item of group) list.append(itemNode(item));
|
|
262
|
-
});
|
|
263
|
-
const dot = goo.querySelector(".clay-menu-dot");
|
|
264
|
-
const wanted = [...items.values()].some((item) => item.badge);
|
|
265
|
-
if (wanted && !dot) {
|
|
266
|
-
const mark = document.createElement("span");
|
|
267
|
-
mark.className = "clay-menu-dot";
|
|
268
|
-
mark.setAttribute("aria-hidden", "true");
|
|
269
|
-
goo.append(mark);
|
|
270
|
-
} else if (!wanted && dot) {
|
|
271
|
-
dot.remove();
|
|
272
|
-
}
|
|
273
|
-
if (focused) {
|
|
274
|
-
const again = buttons().find((button) => button.dataset.menuId === focused);
|
|
275
|
-
if (again) again.focus();
|
|
276
|
-
else focusItem(0);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const clean = ({ id, label, run, badge, keys, disabled, href }) => ({
|
|
281
|
-
id,
|
|
282
|
-
href: typeof href === "string" ? href : "",
|
|
283
|
-
label: String(label ?? ""),
|
|
284
|
-
run: typeof run === "function" ? run : null,
|
|
285
|
-
badge: Boolean(badge),
|
|
286
|
-
keys: keys ? String(keys) : "",
|
|
287
|
-
disabled: Boolean(disabled),
|
|
288
71
|
});
|
|
289
72
|
|
|
290
|
-
function addItem({ id, label, run, badge, keys, disabled, text, pair, href, section, quiet, dismiss } = {}) {
|
|
291
|
-
if (!id) return null;
|
|
292
|
-
const item = clean({ id, label, run, badge, keys, disabled, href });
|
|
293
|
-
if (text) item.text = true;
|
|
294
|
-
if (Array.isArray(pair)) item.pair = pair.map(clean);
|
|
295
|
-
if (section === "start" || section === "end") item.section = section;
|
|
296
|
-
if (quiet) item.quiet = true;
|
|
297
|
-
if (dismiss) item.dismiss = { label: String(dismiss.label || "Dismiss"), run: typeof dismiss.run === "function" ? dismiss.run : null };
|
|
298
|
-
items.set(id, item);
|
|
299
|
-
render();
|
|
300
|
-
return id;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function removeItem(id) {
|
|
304
|
-
if (items.delete(id)) render();
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function showNote(text) {
|
|
308
|
-
if (!text) return;
|
|
309
|
-
noteEl.textContent = text;
|
|
310
|
-
noteEl.hidden = false;
|
|
311
|
-
clearTimeout(noteTimer);
|
|
312
|
-
noteTimer = setTimeout(() => {
|
|
313
|
-
noteEl.hidden = true;
|
|
314
|
-
noteEl.textContent = "";
|
|
315
|
-
}, NOTE_MS);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
let noteTimer = 0;
|
|
319
73
|
let busy = false;
|
|
320
74
|
|
|
321
75
|
const applyHidden = () => {
|
|
@@ -324,7 +78,7 @@ function mount() {
|
|
|
324
78
|
|
|
325
79
|
function setChecked() {
|
|
326
80
|
edit.setAttribute("aria-checked", currentMode() === "edit" ? "true" : "false");
|
|
327
|
-
if (hideSwitch) addItem({ id: "clay.mode", label:
|
|
81
|
+
if (hideSwitch) addItem({ id: "clay.mode", label: "Edit mode", toggle: true, checked: currentMode() === "edit", run: () => edit.click() });
|
|
328
82
|
}
|
|
329
83
|
setChecked();
|
|
330
84
|
|
|
@@ -363,50 +117,6 @@ function mount() {
|
|
|
363
117
|
}
|
|
364
118
|
});
|
|
365
119
|
|
|
366
|
-
goo.addEventListener("click", () => {
|
|
367
|
-
if (isOpen()) close();
|
|
368
|
-
else open();
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
document.addEventListener("click", (event) => {
|
|
372
|
-
if (isOpen() && !event.composedPath().includes(root)) close();
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
root.addEventListener("keydown", (event) => {
|
|
376
|
-
if (event.key === "Escape") {
|
|
377
|
-
if (!isOpen()) return;
|
|
378
|
-
event.preventDefault();
|
|
379
|
-
close();
|
|
380
|
-
goo.focus();
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
if (event.target === goo && (event.key === "Enter" || event.key === " ")) {
|
|
384
|
-
event.preventDefault();
|
|
385
|
-
if (isOpen()) close();
|
|
386
|
-
else {
|
|
387
|
-
open();
|
|
388
|
-
focusItem(0);
|
|
389
|
-
}
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
if (!isOpen()) return;
|
|
393
|
-
if (event.key === "Enter" && list.contains(event.target)) {
|
|
394
|
-
event.preventDefault();
|
|
395
|
-
event.target.click();
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
const all = buttons();
|
|
399
|
-
if (!all.length) return;
|
|
400
|
-
const at = all.indexOf(focusedEl());
|
|
401
|
-
if (event.key === "ArrowDown") {
|
|
402
|
-
event.preventDefault();
|
|
403
|
-
focusItem(at < 0 ? 0 : at + 1);
|
|
404
|
-
} else if (event.key === "ArrowUp") {
|
|
405
|
-
event.preventDefault();
|
|
406
|
-
focusItem(at < 0 ? all.length - 1 : at - 1);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
|
|
410
120
|
// The intro sits at the top right in normal flow, so the group moves below its
|
|
411
121
|
// bottom edge while it is on the page, and back when it goes.
|
|
412
122
|
const doc = document;
|
|
@@ -457,12 +167,9 @@ function mount() {
|
|
|
457
167
|
`:root { --clay-menu-width: ${Math.ceil(rect.width)}px; --clay-menu-height: ${Math.ceil(rect.height)}px; }`;
|
|
458
168
|
}
|
|
459
169
|
|
|
460
|
-
const css = embedded ? [MENU_BASE, skin ?? MENU_SKIN, MENU_TOUCH] : [MENU_BASE, MENU_FLOATING, MENU_SKIN, MENU_TOUCH];
|
|
461
|
-
const ui =
|
|
462
|
-
if (embedded) {
|
|
463
|
-
window.addEventListener("resize", close);
|
|
464
|
-
window.addEventListener("scroll", () => { if (topLayer && isOpen()) placeList(); }, { capture: true, passive: true });
|
|
465
|
-
} else {
|
|
170
|
+
const css = embedded ? [MENU_SAFETY, MENU_BASE, skin ?? MENU_SKIN, MENU_TOUCH] : [MENU_SAFETY, MENU_BASE, MENU_FLOATING, MENU_SKIN, MENU_TOUCH];
|
|
171
|
+
const ui = mountShell(css);
|
|
172
|
+
if (!embedded) {
|
|
466
173
|
attachPlacement({
|
|
467
174
|
dock: root,
|
|
468
175
|
container: ui.shadow,
|
|
@@ -487,6 +194,7 @@ function mount() {
|
|
|
487
194
|
actions = attachActions({ addItem, note: showNote, currentMode });
|
|
488
195
|
people = attachPeople({ root, capsule: root.querySelector(".clay-menu"), addItem, removeItem, closeMenu: close, note: showNote });
|
|
489
196
|
attachVariants({ root, capsule: root.querySelector(".clay-menu"), addItem, removeItem, note: showNote, currentMode });
|
|
197
|
+
attachDomItems({ addItem, removeItem, beforeOpen });
|
|
490
198
|
|
|
491
199
|
if (SEO_ITEM) {
|
|
492
200
|
addItem({
|
|
@@ -21,4 +21,10 @@ export const CLOSE_CSS = `:host { color: inherit !important; }
|
|
|
21
21
|
.clay-intro__close:focus-visible { opacity: 1; outline: 2px solid currentColor; outline-offset: 2px; }
|
|
22
22
|
.clay-intro__close svg { display: block; width: 12px; height: 12px; fill: currentColor; stroke: none; }
|
|
23
23
|
@media (pointer: coarse) { .clay-intro__close { width: 44px; height: 44px; top: 4px; right: 4px; } }
|
|
24
|
+
.clay-intro__close[data-manual] {
|
|
25
|
+
width: auto; height: 36px; padding: 0 14px;
|
|
26
|
+
border-radius: 999px; font-size: 12px; font-weight: 500;
|
|
27
|
+
text-transform: uppercase; letter-spacing: .04em; white-space: nowrap;
|
|
28
|
+
}
|
|
29
|
+
@media (pointer: coarse), (max-width: 680px) { .clay-intro__close[data-manual] { height: 44px; } }
|
|
24
30
|
`;
|