@panphora/clayjs 1.21.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 +8871 -8629
- package/dist/m/attrs/{save-freeze-MTFVVYV5.js → save-freeze-JZHKRQLR.js} +1 -1
- package/dist/m/{c-OABVRYOT.js → c-24PMUJIV.js} +1 -1
- package/dist/m/{c-EOCZ2YVL.js → c-2AYVYEI4.js} +1 -1
- package/dist/m/{c-UXEVJ63W.js → c-2L6O5PDS.js} +1 -1
- package/dist/m/{c-VBLFAQD2.js → c-2RYOGUQM.js} +1 -1
- package/dist/m/{c-LDUNOR57.js → c-2Z22FCFK.js} +13 -25
- package/dist/m/{c-NLZ5AN4L.js → c-2ZVOEU4M.js} +1 -1
- package/dist/m/{c-BJPZCWAV.js → c-37NAONZA.js} +1 -1
- package/dist/m/{c-ZE46QDIH.js → c-3TLG3OLG.js} +1 -1
- package/dist/m/{c-CSNE5EIF.js → c-4PO3YGDC.js} +1 -1
- package/dist/m/{c-OJYM642Q.js → c-5FKRZNJV.js} +1 -1
- package/dist/m/{c-SO63UGPQ.js → c-5OJ7YF2N.js} +1 -1
- package/dist/m/{c-CAWNFER6.js → c-5TIZ2I64.js} +1 -1
- package/dist/m/{c-7SXYQCN4.js → c-5WMXEKFH.js} +1 -1
- package/dist/m/{c-XQSXKIKM.js → c-6RDOKLN7.js} +1 -1
- package/dist/m/{c-BRLCTNMC.js → c-6TYGD5AB.js} +1 -1
- package/dist/m/c-A4IP6MBC.js +1 -0
- package/dist/m/{c-I2IFKJX2.js → c-ALDVFPWL.js} +1 -1
- package/dist/m/{c-LV5MABYD.js → c-AYVGD6A6.js} +1 -1
- package/dist/m/{c-LQU7TXIH.js → c-B6O7AJYG.js} +1 -1
- package/dist/m/{c-SBSMJIPC.js → c-BCGWJYXW.js} +1 -1
- package/dist/m/{c-RT3C74GY.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-67ILSNPR.js → c-DTMZ3ZHI.js} +1 -1
- package/dist/m/{c-EZ7B5AZT.js → c-ELYIIYRS.js} +1 -1
- package/dist/m/{c-VAK7SG37.js → c-EUGJVHDD.js} +1 -1
- package/dist/m/{c-67LGZ52V.js → c-FCSZ3DC5.js} +1 -1
- package/dist/m/{c-JUE7B4OX.js → c-FQGHR32O.js} +1 -1
- package/dist/m/{c-ZJLU56DV.js → c-G4PWUDD5.js} +1 -1
- package/dist/m/{c-CJFDLD43.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-44B5ODN7.js → c-HCNWJE4A.js} +2 -2
- package/dist/m/{c-BJ7YQ5QZ.js → c-IHUWUKIU.js} +1 -1
- package/dist/m/{c-LIBE4HPD.js → c-JCOQVZYS.js} +1 -1
- package/dist/m/{c-RG6XUZFA.js → c-JGHEACGK.js} +1 -1
- package/dist/m/{c-3GJ4XARY.js → c-JGLX6TBK.js} +1 -1
- package/dist/m/c-JNRKHYWO.js +1 -0
- package/dist/m/{c-PIVIYLTY.js → c-KDXIJEEF.js} +1 -1
- package/dist/m/{c-NFR673TL.js → c-L4KMT7FO.js} +2 -2
- package/dist/m/{c-E4IBSAQM.js → c-LCYPMULG.js} +1 -1
- package/dist/m/{c-SJPD46ZO.js → c-LIGQYZ7M.js} +4 -4
- package/dist/m/{c-U5X57T4L.js → c-LKIWEPAV.js} +1 -1
- package/dist/m/{c-EDSPKYMA.js → c-LUXAB2XI.js} +1 -1
- package/dist/m/{c-YSB6JKV2.js → c-LWIDDWAR.js} +2 -2
- package/dist/m/c-MPSKIU2I.js +1 -0
- package/dist/m/{c-DHXPDLW6.js → c-NK33MMYF.js} +1 -1
- package/dist/m/{c-GPGYKER7.js → c-NQLWOHLH.js} +1 -1
- package/dist/m/{c-LPUJXRXW.js → c-NTHUTABN.js} +1 -1
- package/dist/m/{c-Y33LUUIQ.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-3YH6JSIL.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-2ZZBC45O.js → c-PXLTUYZC.js} +1 -1
- package/dist/m/c-QEN6HZCA.js +1 -0
- package/dist/m/{c-JOH753EJ.js → c-QGAPG7OM.js} +1 -1
- package/dist/m/{c-W23KVGUO.js → c-QIR5CAKM.js} +1 -1
- package/dist/m/{c-GKEATME6.js → c-QM5LXRDT.js} +1 -1
- package/dist/m/c-QUGCKMWF.js +1 -0
- package/dist/m/{c-FNVQAACK.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-J3BXFTZS.js → c-SGRIYOAJ.js} +1 -1
- package/dist/m/c-SKF5HL4I.js +4 -0
- package/dist/m/{c-W3K23S72.js → c-SRPC3VBE.js} +1 -1
- package/dist/m/{c-K76AUL4I.js → c-T5NK2TVY.js} +1 -1
- package/dist/m/{c-EFD6QNTE.js → c-TANBH7FI.js} +1 -1
- package/dist/m/{c-5734MF2P.js → c-THGQQDYY.js} +1 -1
- package/dist/m/{c-YQUJ74R3.js → c-TSBVMWAH.js} +1 -1
- package/dist/m/{c-VY6XRBOW.js → c-UD5JZTDL.js} +1 -1
- package/dist/m/{c-M4FSPNQ2.js → c-UDGEFWNU.js} +1 -1
- package/dist/m/{c-ZSCBEB7I.js → c-UHUNXWKN.js} +1 -1
- package/dist/m/{c-BTLFLLML.js → c-VIRFF5HG.js} +1 -1
- package/dist/m/{c-2VB555FG.js → c-VYJ7O4RI.js} +1 -1
- package/dist/m/{c-TUGVINJR.js → c-W5E5P2TZ.js} +1 -1
- package/dist/m/{c-EOCKLMKX.js → c-W7IPVCAA.js} +1 -1
- package/dist/m/{c-5FEU5MMY.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-GVPNDIKJ.js → c-XCFQEWW6.js} +2 -2
- package/dist/m/c-XLQ3BDMM.js +1 -0
- package/dist/m/{c-XD5MOROJ.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-VPORYJSE.js → autosave-F5YQCKVL.js} +1 -1
- package/dist/m/core/{conflict-notice-JMAZLVRX.js → conflict-notice-42H4ASLA.js} +1 -1
- package/dist/m/core/{persist-3OPWJKZS.js → persist-CJXIL7D4.js} +1 -1
- package/dist/m/core/{save-AOJBDSOK.js → save-ESPSQPLV.js} +1 -1
- package/dist/m/core/{save-core-IOCMXO5L.js → save-core-6SQHOR6W.js} +1 -1
- package/dist/m/core/snapshot-FJP5IJL3.js +1 -0
- package/dist/m/core/{unsaved-warning-U5VUJFOE.js → unsaved-warning-HNQVTA2D.js} +1 -1
- package/dist/m/internals/{index-HDRANDWE.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 +1139 -1069
- package/dist/m/page/{editor-EY2WAAGB.js → editor-OSKOW6RS.js} +2 -2
- package/dist/m/plugins/ai-edit-VV4QTINH.js +6 -0
- package/dist/m/plugins/cms-H7DTMUQX.js +2235 -0
- package/dist/m/plugins/{indicator-4VDXWYTD.js → indicator-WVPGMOZ7.js} +1 -1
- package/dist/m/plugins/{live-5EZN2EYP.js → live-DCUBKXY4.js} +1 -1
- package/dist/m/plugins/menu-Q4MFFQ6E.js +1 -0
- package/dist/m/plugins/{page-LVW5DWA4.js → page-OAWUJQEL.js} +2 -2
- package/dist/m/plugins/{page-view-5KQ76IJB.js → page-view-DZ26ZDDQ.js} +1 -1
- package/dist/m/plugins/{people-C7TVBBMR.js → people-5XRNRP5U.js} +1 -1
- package/dist/m/plugins/{quickcrop-BQ5YYFCH.js → quickcrop-WWF5I422.js} +2 -2
- package/dist/m/plugins/{richclay-KLGETK77.js → richclay-PGOOJ4OK.js} +2 -2
- package/dist/m/plugins/{sortable-WJZZBM7R.js → sortable-266HY6TR.js} +1 -1
- package/dist/m/plugins/{source-CCEUM3OR.js → source-6BKMK4PO.js} +1 -1
- package/dist/m/plugins/{upgrade-NW556WJD.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-VRYPY6EJ.js → wire-W2EV36KZ.js} +1 -1
- package/dist/m/{reader-core-2GXY7SHY.js → reader-core-POFQCKRV.js} +2 -2
- package/dist/m/sync/{live-sync-SKPMFYOO.js → live-sync-XGUPHI3I.js} +1 -1
- package/dist/m/ui/{index-QNIPDHXQ.js → index-VGYAKAKI.js} +1 -1
- package/dist/offline-manifest.json +666 -606
- package/docs/guide.md +38 -27
- package/package.json +2 -2
- 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 +10 -3
- package/src/plugins/ai-edit.js +6 -17
- package/src/plugins/cms.js +3 -6
- 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/bevel.js +1 -1
- package/src/ui/page-colour.js +17 -9
- package/src/ui/skins/cms.js +1 -1
- package/src/ui/skins/quickcrop.js +1 -1
- package/src/ui/skins/richclay.js +1 -1
- package/src/vendor/hypercms.vendor.js +16 -69
- package/dist/m/c-AL4RBJF2.js +0 -1
- package/dist/m/c-DDDOKPI6.js +0 -3
- package/dist/m/c-EGTIAL6P.js +0 -1
- package/dist/m/c-FSWRGBVQ.js +0 -1
- package/dist/m/c-HDWZEJHM.js +0 -183
- package/dist/m/c-HMYWIHFG.js +0 -1
- package/dist/m/c-JH5FF4NL.js +0 -20
- package/dist/m/c-M6FNQCTQ.js +0 -1
- package/dist/m/c-MHI3GYKF.js +0 -1
- package/dist/m/c-MIL4B2JQ.js +0 -1
- package/dist/m/c-S5IEIZCN.js +0 -1
- package/dist/m/c-VSPAFOKU.js +0 -1
- package/dist/m/c-WFMRX5RZ.js +0 -1
- package/dist/m/core/snapshot-O7II2AGG.js +0 -1
- package/dist/m/loader-ZVPK4ZN3.js +0 -1
- package/dist/m/manifest-5498AFD3.js +0 -1
- package/dist/m/manifest-edit-62A5A00F.js +0 -1
- package/dist/m/plugins/ai-edit-J5VLSW5I.js +0 -6
- package/dist/m/plugins/cms-LRAOZHT7.js +0 -2293
- package/dist/m/plugins/menu-DM2JHPSR.js +0 -1
- package/dist/m/plugins/visitor-drafts-2AWNT23K.js +0 -20
- package/src/ui/skins/cms-toggle.js +0 -15
- /package/dist/m/plugins/{quickcrop-BQ5YYFCH.js.LEGAL.txt → quickcrop-WWF5I422.js.LEGAL.txt} +0 -0
- /package/dist/m/plugins/{richclay-KLGETK77.js.LEGAL.txt → richclay-PGOOJ4OK.js.LEGAL.txt} +0 -0
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
|
`;
|
|
@@ -5,6 +5,8 @@ import { bootPhase } from "../../core/boot-phase.js";
|
|
|
5
5
|
|
|
6
6
|
export const COPY = {
|
|
7
7
|
close: "Hide this introduction",
|
|
8
|
+
hide: "Hide intro",
|
|
9
|
+
show: "Show intro",
|
|
8
10
|
label: (appName) => `About ${appName}`,
|
|
9
11
|
};
|
|
10
12
|
|
|
@@ -43,17 +45,7 @@ function focusApp() {
|
|
|
43
45
|
else document.body.focus({ preventScroll: true });
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
if (!template || !template.content.textContent.trim() && !template.content.querySelector("img, svg, video, canvas")) return null;
|
|
48
|
-
if (!forced()) {
|
|
49
|
-
if (memory.get().intro.done || hadDraft) return null;
|
|
50
|
-
if (bootPhase.servedHash) return null;
|
|
51
|
-
if (window.matchMedia && window.matchMedia("print").matches) return null;
|
|
52
|
-
}
|
|
53
|
-
return mount({ memory, template, appName });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function mount({ memory, template, appName }) {
|
|
48
|
+
function buildSection({ template, appName, labelText, onClose }) {
|
|
57
49
|
stylesOnce();
|
|
58
50
|
|
|
59
51
|
const section = document.createElement("section");
|
|
@@ -66,11 +58,48 @@ function mount({ memory, template, appName }) {
|
|
|
66
58
|
const close = document.createElement("button");
|
|
67
59
|
close.type = "button";
|
|
68
60
|
close.className = "clay-intro__close";
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
if (labelText) {
|
|
62
|
+
close.textContent = labelText;
|
|
63
|
+
close.setAttribute("data-manual", "");
|
|
64
|
+
} else {
|
|
65
|
+
close.setAttribute("aria-label", COPY.close);
|
|
66
|
+
close.innerHTML = '<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true"><path d="M1.4 0 6 4.6 10.6 0 12 1.4 7.4 6 12 10.6 10.6 12 6 7.4 1.4 12 0 10.6 4.6 6 0 1.4z"/></svg>';
|
|
67
|
+
}
|
|
68
|
+
close.addEventListener("click", () => onClose());
|
|
72
69
|
mountEditorUI({ name: "intro-close", content: close, css: [CLOSE_CSS], parent: section });
|
|
73
70
|
|
|
71
|
+
section.addEventListener("click", (event) => {
|
|
72
|
+
if (event.target.closest("[visitor-intro-dismiss]")) {
|
|
73
|
+
onClose();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (event.target.closest("[visitor-intro-start]")) {
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
window.scrollTo({ top: window.scrollY + section.getBoundingClientRect().bottom, behavior: "smooth" });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return section;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function mountIntro({ memory, template, appName, hadDraft }) {
|
|
86
|
+
if (!template || !template.content.textContent.trim() && !template.content.querySelector("img, svg, video, canvas")) return null;
|
|
87
|
+
if (template.getAttribute("visitor-intro") === "manual") {
|
|
88
|
+
const printing = window.matchMedia && window.matchMedia("print").matches;
|
|
89
|
+
const open = forced() || !(memory.get().intro.done || hadDraft || bootPhase.servedHash || printing);
|
|
90
|
+
return mountManual({ memory, template, appName, open });
|
|
91
|
+
}
|
|
92
|
+
if (!forced()) {
|
|
93
|
+
if (memory.get().intro.done || hadDraft) return null;
|
|
94
|
+
if (bootPhase.servedHash) return null;
|
|
95
|
+
if (window.matchMedia && window.matchMedia("print").matches) return null;
|
|
96
|
+
}
|
|
97
|
+
return mount({ memory, template, appName });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function mount({ memory, template, appName }) {
|
|
101
|
+
const section = buildSection({ template, appName, onClose: () => dismiss("closed") });
|
|
102
|
+
|
|
74
103
|
let gone = false;
|
|
75
104
|
let frame = 0;
|
|
76
105
|
|
|
@@ -102,20 +131,43 @@ function mount({ memory, template, appName }) {
|
|
|
102
131
|
});
|
|
103
132
|
}
|
|
104
133
|
|
|
105
|
-
section.addEventListener("click", (event) => {
|
|
106
|
-
if (event.target.closest("[visitor-intro-dismiss]")) {
|
|
107
|
-
dismiss("closed");
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (event.target.closest("[visitor-intro-start]")) {
|
|
111
|
-
event.preventDefault();
|
|
112
|
-
window.scrollTo({ top: window.scrollY + section.getBoundingClientRect().bottom, behavior: "smooth" });
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
134
|
document.body.prepend(section);
|
|
117
135
|
announceBottomUi();
|
|
118
136
|
addEventListener("scroll", onScroll, { passive: true });
|
|
119
137
|
|
|
120
138
|
return section;
|
|
121
139
|
}
|
|
140
|
+
|
|
141
|
+
function mountManual({ memory, template, appName, open }) {
|
|
142
|
+
let section = null;
|
|
143
|
+
const showRow = (wanted) => {
|
|
144
|
+
const menu = window.clay?.menu;
|
|
145
|
+
if (!menu) return;
|
|
146
|
+
if (wanted) menu.addItem({ id: "clay.intro", label: COPY.show, run: show });
|
|
147
|
+
else menu.removeItem("clay.intro");
|
|
148
|
+
};
|
|
149
|
+
function attach() {
|
|
150
|
+
section ??= buildSection({ template, appName, labelText: COPY.hide, onClose: hide });
|
|
151
|
+
document.body.prepend(section);
|
|
152
|
+
announceBottomUi();
|
|
153
|
+
showRow(false);
|
|
154
|
+
}
|
|
155
|
+
function hide() {
|
|
156
|
+
memory.get().intro.done = "closed";
|
|
157
|
+
memory.save();
|
|
158
|
+
section.remove();
|
|
159
|
+
announceBottomUi();
|
|
160
|
+
showRow(true);
|
|
161
|
+
focusApp();
|
|
162
|
+
}
|
|
163
|
+
function show() {
|
|
164
|
+
memory.get().intro.done = null;
|
|
165
|
+
memory.save();
|
|
166
|
+
attach();
|
|
167
|
+
window.scrollTo(0, 0);
|
|
168
|
+
}
|
|
169
|
+
document.addEventListener("clay:menu-ready", () => showRow(!section?.isConnected));
|
|
170
|
+
if (open) attach();
|
|
171
|
+
else showRow(true);
|
|
172
|
+
return section;
|
|
173
|
+
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { isDraftMode } from "../../core/is-edit-mode.js";
|
|
2
2
|
import { BOTTOM_UI_CHANGED, announceBottomUi, bottomBarHeight, promptPanel } from "./bottom-ui.js";
|
|
3
3
|
import { mountEditorUI } from "../../ui/editor-ui.js";
|
|
4
|
+
import { followLook } from "../../ui/page-colour.js";
|
|
5
|
+
|
|
6
|
+
// The notice sits on the page for as long as the owner edits, so it takes most of the
|
|
7
|
+
// page's colour instead of Bevel's: a navy card on a brown page reads as a foreign object.
|
|
8
|
+
const OWNER_LEAN = 0.8;
|
|
4
9
|
|
|
5
10
|
export const COPY = {
|
|
6
11
|
draft: "You're seeing a private draft, like a visitor.",
|
|
@@ -11,29 +16,17 @@ export const COPY = {
|
|
|
11
16
|
};
|
|
12
17
|
|
|
13
18
|
const CHIP_CSS = `.clay-owner {
|
|
14
|
-
--hcp-
|
|
15
|
-
--hcp-
|
|
16
|
-
--hcp-
|
|
17
|
-
--hcp-ink: #
|
|
18
|
-
--hcp-
|
|
19
|
-
--hcp-
|
|
20
|
-
--hcp-
|
|
21
|
-
--hcp-
|
|
22
|
-
--hcp-brass: #8F6310;
|
|
23
|
-
--hcp-shadow: 0 1px 2px rgba(23, 28, 36, .06), 0 14px 34px -14px rgba(43, 36, 27, .32);
|
|
19
|
+
--hcp-surface: light-dark(#FFFCF6, #11131E);
|
|
20
|
+
--hcp-ground: var(--bevel-surface, var(--hcp-surface));
|
|
21
|
+
--hcp-ink: light-dark(#2B241B, #ECEAF2);
|
|
22
|
+
--hcp-ink-2: light-dark(#4A4033, #C9CDE0);
|
|
23
|
+
--hcp-muted: light-dark(#6B5942, #8A90AB);
|
|
24
|
+
--hcp-line-2: color-mix(in srgb, var(--hcp-ink) 22%, var(--hcp-ground));
|
|
25
|
+
--hcp-brass: light-dark(#8F6310, #D9A445);
|
|
26
|
+
--hcp-shadow: 0 1px 2px light-dark(rgba(23, 28, 36, .06), rgba(0, 0, 0, .4)), 0 14px 34px -14px light-dark(rgba(43, 36, 27, .32), rgba(0, 0, 0, .7));
|
|
24
27
|
--hcp-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
25
28
|
--hcp-lift: 0px;
|
|
26
|
-
color-scheme: light;
|
|
27
|
-
}
|
|
28
|
-
@media (prefers-color-scheme: dark) {
|
|
29
|
-
.clay-owner {
|
|
30
|
-
--hcp-surface: #11131E; --hcp-sunk: #1A1D2C;
|
|
31
|
-
--hcp-ink: #ECEAF2; --hcp-ink-2: #C9CDE0; --hcp-muted: #8A90AB;
|
|
32
|
-
--hcp-line: #262B42; --hcp-line-2: #353B52;
|
|
33
|
-
--hcp-brass: #D9A445;
|
|
34
|
-
--hcp-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 16px 38px -14px rgba(0, 0, 0, .7);
|
|
35
|
-
color-scheme: dark;
|
|
36
|
-
}
|
|
29
|
+
color-scheme: light dark;
|
|
37
30
|
}
|
|
38
31
|
.clay-owner, .clay-owner *, .clay-owner *::before, .clay-owner *::after {
|
|
39
32
|
box-sizing: border-box; margin: 0; padding: 0; border: 0;
|
|
@@ -46,8 +39,8 @@ const CHIP_CSS = `.clay-owner {
|
|
|
46
39
|
max-width: min(460px, calc(100vw - 24px));
|
|
47
40
|
padding: 8px 8px 8px 12px;
|
|
48
41
|
font-family: var(--hcp-sans); font-size: 13px; font-weight: 400; line-height: 1.45;
|
|
49
|
-
color: var(--hcp-ink-2); text-align: left; background: var(--hcp-
|
|
50
|
-
border: 1px solid var(--hcp-line-2);
|
|
42
|
+
color: var(--hcp-ink-2); text-align: left; background: var(--hcp-ground);
|
|
43
|
+
border: 1px solid var(--hcp-line-2);
|
|
51
44
|
box-shadow: var(--hcp-shadow);
|
|
52
45
|
-webkit-font-smoothing: antialiased;
|
|
53
46
|
}
|
|
@@ -151,6 +144,7 @@ export function mountOwnerChip() {
|
|
|
151
144
|
}
|
|
152
145
|
|
|
153
146
|
function liftOff() {
|
|
147
|
+
unfollow?.();
|
|
154
148
|
removeEventListener("scroll", lift);
|
|
155
149
|
removeEventListener("resize", lift);
|
|
156
150
|
document.removeEventListener(BOTTOM_UI_CHANGED, lift);
|
|
@@ -168,6 +162,7 @@ export function mountOwnerChip() {
|
|
|
168
162
|
// Transition events stop at a shadow root, so the chip's own changes are heard here.
|
|
169
163
|
chip.addEventListener("transitionend", lift);
|
|
170
164
|
lift();
|
|
165
|
+
const unfollow = followLook(chip, chip, undefined, OWNER_LEAN);
|
|
171
166
|
// Whoever removes the chip takes its listeners back with it: a chip mounted and unmounted
|
|
172
167
|
// once per mode session would otherwise leave six behind each time.
|
|
173
168
|
chip.liftOff = liftOff;
|
|
@@ -119,7 +119,7 @@ export async function mountPrompt({ memory, appName, link }) {
|
|
|
119
119
|
const t = e.target.closest("[data-hcp]");
|
|
120
120
|
if (t) {
|
|
121
121
|
const act = t.getAttribute("data-hcp");
|
|
122
|
-
if (act === "open") { set("card", "open"); linkEl.focus(); }
|
|
122
|
+
if (act === "open") { set("card", root.getAttribute("data-hcp-reason") === "error" ? undefined : "open"); linkEl.focus(); }
|
|
123
123
|
else if (act === "minimize") { set("mini"); tab.focus(); }
|
|
124
124
|
else if (act === "dismiss") {
|
|
125
125
|
if (root.getAttribute("data-hcp-reason") === "error") { set("mini"); tab.focus(); }
|
|
@@ -149,7 +149,7 @@ export async function mountPrompt({ memory, appName, link }) {
|
|
|
149
149
|
function clearError() {
|
|
150
150
|
if (root.getAttribute("data-hcp-reason") === "error") root.setAttribute("data-hcp-reason", "use");
|
|
151
151
|
}
|
|
152
|
-
const api = { set, root, showError, clearError };
|
|
152
|
+
const api = { set, root, showError, clearError, download: () => linkEl.click() };
|
|
153
153
|
|
|
154
154
|
document.addEventListener("input", onUse, true);
|
|
155
155
|
document.addEventListener("change", onUse, true);
|