@panphora/clayjs 1.12.0 → 1.12.1
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/clay.standalone.js +245 -372
- package/package.json +1 -1
- package/src/plugins/visitor-drafts/bottom-ui.js +31 -0
- package/src/plugins/visitor-drafts/intro-style.js +17 -154
- package/src/plugins/visitor-drafts/intro.js +56 -169
- package/src/plugins/visitor-drafts/owner-chip.js +46 -19
- package/src/plugins/visitor-drafts/prompt-style.js +36 -11
- package/src/plugins/visitor-drafts/prompt.js +53 -27
- package/src/plugins/visitor-drafts.js +2 -2
package/package.json
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const BOTTOM_UI_CHANGED = "clay:bottom-ui-changed";
|
|
2
|
+
|
|
3
|
+
export function announceBottomUi() {
|
|
4
|
+
document.dispatchEvent(new CustomEvent(BOTTOM_UI_CHANGED));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function bottomBarHeight(x, skip) {
|
|
8
|
+
const h = window.innerHeight;
|
|
9
|
+
let raise = 0;
|
|
10
|
+
const hits = document.elementsFromPoint ? document.elementsFromPoint(x, h - 3) : [];
|
|
11
|
+
for (const hit of hits) {
|
|
12
|
+
for (let el = hit; el && el !== document.body && el !== document.documentElement; el = el.parentElement) {
|
|
13
|
+
if (skip(el)) break;
|
|
14
|
+
const position = getComputedStyle(el).position;
|
|
15
|
+
if (position === "fixed" || position === "sticky") {
|
|
16
|
+
const height = Math.ceil(h - el.getBoundingClientRect().top);
|
|
17
|
+
if (height < h / 3) raise = Math.max(raise, height);
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return raise;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function promptPanel(box) {
|
|
26
|
+
const panel = document.querySelector("#hcp:not([hidden])");
|
|
27
|
+
if (!panel) return 0;
|
|
28
|
+
const r = panel.getBoundingClientRect();
|
|
29
|
+
if (!r.width || !r.height || r.right <= box.left || r.left >= box.right) return 0;
|
|
30
|
+
return Math.ceil(window.innerHeight - r.top);
|
|
31
|
+
}
|
|
@@ -1,156 +1,19 @@
|
|
|
1
|
-
export const INTRO_CSS =
|
|
2
|
-
|
|
3
|
-
--hcp-surface: #FFFCF6;
|
|
4
|
-
--hcp-sunk: #F4ECDF;
|
|
5
|
-
--hcp-ink: #2B241B;
|
|
6
|
-
--hcp-ink-2: #4A4033;
|
|
7
|
-
--hcp-muted: #6B5942;
|
|
8
|
-
--hcp-line: #E8DCC6;
|
|
9
|
-
--hcp-line-2: #D8C8AF;
|
|
10
|
-
--hcp-brass: #8F6310;
|
|
11
|
-
--hcp-brass-soft: #F0E4CC;
|
|
12
|
-
--hcp-face: #F1E7D4;
|
|
13
|
-
--hcp-edge-hi: #FFF9ED;
|
|
14
|
-
--hcp-edge-lo: #D6C3A5;
|
|
15
|
-
--hcp-shadow: 0 1px 2px rgba(23, 28, 36, .06), 0 14px 34px -14px rgba(43, 36, 27, .32);
|
|
16
|
-
--hcp-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
17
|
-
--hcp-serif: Newsreader, Georgia, "Times New Roman", serif;
|
|
18
|
-
--hcp-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;
|
|
19
|
-
color-scheme: light;
|
|
20
|
-
}
|
|
21
|
-
@media (prefers-color-scheme: dark) {
|
|
22
|
-
.clay-intro, .clay-about {
|
|
23
|
-
--hcp-ground: #0B0C13; --hcp-surface: #11131E; --hcp-sunk: #1A1D2C;
|
|
24
|
-
--hcp-ink: #ECEAF2; --hcp-ink-2: #C9CDE0; --hcp-muted: #8A90AB;
|
|
25
|
-
--hcp-line: #262B42; --hcp-line-2: #353B52;
|
|
26
|
-
--hcp-brass: #D9A445; --hcp-brass-soft: #3A2F17;
|
|
27
|
-
--hcp-face: #22273E; --hcp-edge-hi: #3C4260; --hcp-edge-lo: #1B2033;
|
|
28
|
-
--hcp-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 16px 38px -14px rgba(0, 0, 0, .7);
|
|
29
|
-
color-scheme: dark;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !important; min-height: 100%; overflow-y: auto !important; }
|
|
34
|
-
|
|
35
|
-
.clay-intro, .clay-intro *, .clay-intro *::before, .clay-intro *::after,
|
|
36
|
-
.clay-about, .clay-about *::before, .clay-about *::after {
|
|
37
|
-
box-sizing: border-box; margin: 0; padding: 0; border: 0;
|
|
38
|
-
font: inherit; letter-spacing: normal; text-transform: none; text-shadow: none;
|
|
39
|
-
line-height: inherit; color: inherit; background: none; float: none; max-width: none;
|
|
40
|
-
}
|
|
1
|
+
export const INTRO_CSS = `html:has(> body > .clay-intro) { height: auto !important; overflow-y: auto !important; }
|
|
2
|
+
body:has(> .clay-intro) { height: auto !important; overflow: visible !important; }
|
|
41
3
|
.clay-intro {
|
|
42
|
-
position: relative;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
.clay-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
.clay-intro__name {
|
|
59
|
-
font-family: var(--hcp-serif); font-size: clamp(30px, 7vw, 44px); font-weight: 500; line-height: 1.12;
|
|
60
|
-
color: var(--hcp-ink); font-variation-settings: "opsz" 48;
|
|
61
|
-
}
|
|
62
|
-
.clay-intro__inner > p { font-size: 18px; line-height: 1.55; color: var(--hcp-ink-2); }
|
|
63
|
-
.clay-intro__inner > a { color: var(--hcp-brass); text-decoration: underline; text-underline-offset: 2px; }
|
|
64
|
-
.clay-intro__inner > video, .clay-intro__inner > img {
|
|
65
|
-
display: block; width: 100%; height: auto; aspect-ratio: 16 / 9;
|
|
66
|
-
background: var(--hcp-sunk); border: 1px solid var(--hcp-line);
|
|
67
|
-
}
|
|
68
|
-
.clay-intro__inner > ul, .clay-intro__inner > ol { padding-left: 22px; font-size: 18px; line-height: 1.55; color: var(--hcp-ink-2); }
|
|
69
|
-
.clay-intro__inner > ul { list-style: disc; }
|
|
70
|
-
.clay-intro__inner > ol { list-style: decimal; }
|
|
71
|
-
|
|
72
|
-
.clay-intro__actions { display: flex; flex-direction: column; gap: 14px; align-items: stretch; }
|
|
73
|
-
.clay-intro__download {
|
|
74
|
-
align-self: flex-start;
|
|
75
|
-
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
|
76
|
-
min-height: 44px; padding: 0 18px;
|
|
77
|
-
font-family: var(--hcp-sans); font-size: 15px; font-weight: 500; line-height: 1.5;
|
|
78
|
-
white-space: nowrap; text-decoration: none; cursor: pointer; user-select: none;
|
|
79
|
-
color: var(--hcp-ground); background: var(--hcp-ink);
|
|
80
|
-
border: 2px solid;
|
|
81
|
-
border-color: color-mix(in srgb, var(--hcp-ink), white 24%) color-mix(in srgb, var(--hcp-ink), black 32%) color-mix(in srgb, var(--hcp-ink), black 32%) color-mix(in srgb, var(--hcp-ink), white 24%);
|
|
82
|
-
transition: background .2s ease;
|
|
83
|
-
}
|
|
84
|
-
.clay-intro__download:hover { background: color-mix(in srgb, var(--hcp-ink), var(--hcp-ground) 12%); }
|
|
85
|
-
|
|
86
|
-
.clay-intro__signup {
|
|
87
|
-
display: flex; flex-direction: column; gap: 9px;
|
|
88
|
-
padding: 14px 16px;
|
|
89
|
-
background: var(--hcp-surface); border: 1px solid var(--hcp-line); border-left: 3px solid var(--hcp-brass);
|
|
90
|
-
}
|
|
91
|
-
.clay-intro__signup label { font-size: 14px; line-height: 1.45; color: var(--hcp-ink-2); }
|
|
92
|
-
.clay-intro__row { display: flex; gap: 8px; }
|
|
93
|
-
.clay-intro__row input {
|
|
94
|
-
flex: 1 1 auto; min-width: 0; height: 40px; padding: 0 10px;
|
|
95
|
-
font-family: var(--hcp-sans); font-size: 15px; color: var(--hcp-ink);
|
|
96
|
-
background: var(--hcp-ground); border: 1px solid var(--hcp-line-2);
|
|
97
|
-
}
|
|
98
|
-
.clay-intro__row input::placeholder { color: var(--hcp-muted); }
|
|
99
|
-
.clay-intro__row button {
|
|
100
|
-
flex: none; height: 40px; padding: 0 14px;
|
|
101
|
-
font-family: var(--hcp-sans); font-size: 14px; font-weight: 500;
|
|
102
|
-
color: var(--hcp-ink); background: var(--hcp-face); cursor: pointer;
|
|
103
|
-
border: 2px solid; border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
|
|
104
|
-
}
|
|
105
|
-
.clay-intro__row button:hover { background: color-mix(in srgb, var(--hcp-face), var(--hcp-edge-hi) 45%); }
|
|
106
|
-
.clay-intro__hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
|
|
107
|
-
.clay-intro__msg { font-size: 14px; line-height: 1.5; color: var(--hcp-ink-2); }
|
|
108
|
-
.clay-intro__msg[hidden] { display: none; }
|
|
109
|
-
.clay-intro__row[hidden] { display: none; }
|
|
110
|
-
|
|
111
|
-
.clay-intro__start {
|
|
112
|
-
margin-top: auto;
|
|
113
|
-
display: inline-flex; align-items: center; justify-content: center; gap: 10px;
|
|
114
|
-
width: 100%; min-height: 56px; padding: 0 20px;
|
|
115
|
-
font-family: var(--hcp-sans); font-size: 16px; font-weight: 500;
|
|
116
|
-
color: var(--hcp-ground); background: var(--hcp-ink); cursor: pointer;
|
|
117
|
-
border: 2px solid;
|
|
118
|
-
border-color: color-mix(in srgb, var(--hcp-ink), white 24%) color-mix(in srgb, var(--hcp-ink), black 32%) color-mix(in srgb, var(--hcp-ink), black 32%) color-mix(in srgb, var(--hcp-ink), white 24%);
|
|
119
|
-
transition: background .2s ease;
|
|
120
|
-
}
|
|
121
|
-
.clay-intro__start::after { content: "\\2193"; font-size: 15px; line-height: 1; }
|
|
122
|
-
.clay-intro__start:hover { background: color-mix(in srgb, var(--hcp-ink), var(--hcp-ground) 12%); }
|
|
123
|
-
.clay-intro__download:focus-visible, .clay-intro__start:focus-visible,
|
|
124
|
-
.clay-intro__row button:focus-visible, .clay-intro__row input:focus-visible,
|
|
125
|
-
.clay-about:focus-visible { outline: 2px solid var(--hcp-brass); outline-offset: 2px; }
|
|
126
|
-
|
|
127
|
-
.clay-about {
|
|
128
|
-
position: fixed; top: 12px; right: 12px; z-index: 2147482000;
|
|
129
|
-
display: inline-flex; align-items: center; justify-content: center; gap: 7px;
|
|
130
|
-
height: 34px; padding: 0 12px;
|
|
131
|
-
font-family: var(--hcp-sans); font-size: 13px; font-weight: 500; line-height: 1.5;
|
|
132
|
-
white-space: nowrap; cursor: pointer; user-select: none;
|
|
133
|
-
color: var(--hcp-ink); background: var(--hcp-face);
|
|
134
|
-
border: 2px solid; border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
|
|
135
|
-
box-shadow: var(--hcp-shadow);
|
|
136
|
-
transition: background .2s ease;
|
|
137
|
-
}
|
|
138
|
-
.clay-about:hover { background: color-mix(in srgb, var(--hcp-face), var(--hcp-edge-hi) 45%); }
|
|
139
|
-
|
|
140
|
-
@media (min-width: 560px) {
|
|
141
|
-
.clay-intro__actions { flex-direction: row; flex-wrap: wrap; align-items: center; }
|
|
142
|
-
.clay-intro__signup { flex: 1 1 320px; max-width: 520px; }
|
|
143
|
-
}
|
|
144
|
-
@media (pointer: coarse) {
|
|
145
|
-
.clay-intro__download, .clay-intro__start, .clay-intro__row input, .clay-intro__row button { min-height: 44px; }
|
|
146
|
-
.clay-about { min-height: 44px; }
|
|
147
|
-
}
|
|
148
|
-
@media (prefers-reduced-motion: reduce) {
|
|
149
|
-
.clay-intro, .clay-intro *, .clay-about { transition: none; animation: none; }
|
|
150
|
-
}
|
|
151
|
-
@media (forced-colors: active) {
|
|
152
|
-
.clay-intro__download, .clay-intro__start, .clay-intro__row button, .clay-about { border: 1px solid ButtonText; }
|
|
153
|
-
.clay-intro__signup { border: 1px solid CanvasText; }
|
|
154
|
-
}
|
|
155
|
-
@media print { .clay-intro, .clay-about { display: none; } }
|
|
4
|
+
position: relative;
|
|
5
|
+
display: block;
|
|
6
|
+
}
|
|
7
|
+
.clay-intro__close {
|
|
8
|
+
position: absolute; top: 10px; right: 10px; z-index: 1;
|
|
9
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
10
|
+
width: 28px; height: 28px; min-height: 0; margin: 0; padding: 0;
|
|
11
|
+
border: 0; border-radius: 4px; background: none; box-shadow: none;
|
|
12
|
+
color: inherit; opacity: .55; cursor: pointer; line-height: 1;
|
|
13
|
+
}
|
|
14
|
+
.clay-intro .clay-intro__close:hover { opacity: 1; background: none; color: inherit; }
|
|
15
|
+
.clay-intro__close:focus-visible { opacity: 1; outline: 2px solid currentColor; outline-offset: 2px; }
|
|
16
|
+
.clay-intro__close svg { display: block; width: 12px; height: 12px; fill: currentColor; stroke: none; }
|
|
17
|
+
@media (pointer: coarse) { .clay-intro__close { width: 44px; height: 44px; top: 4px; right: 4px; } }
|
|
18
|
+
@media print { .clay-intro { display: none; } }
|
|
156
19
|
`;
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { INTRO_CSS } from "./intro-style.js";
|
|
2
|
+
import { announceBottomUi } from "./bottom-ui.js";
|
|
2
3
|
|
|
3
4
|
export const COPY = {
|
|
4
|
-
|
|
5
|
-
download: "Download free",
|
|
6
|
-
signupLabel: "I make one of these about every two weeks. Get the next one by email.",
|
|
7
|
-
emailPlaceholder: "you@example.com",
|
|
8
|
-
signup: "Send me the next one",
|
|
9
|
-
signupThanks: "Thanks. Check your email for a link to confirm.",
|
|
10
|
-
signupFailed: "That didn't go through. Check the address and try again.",
|
|
11
|
-
start: (appName) => `Start using ${appName}`,
|
|
12
|
-
about: "About this app",
|
|
5
|
+
close: "Hide this introduction",
|
|
13
6
|
label: (appName) => `About ${appName}`,
|
|
14
7
|
};
|
|
15
8
|
|
|
16
9
|
const FOCUSABLE = "a[href], button, input, select, textarea, [tabindex]:not([tabindex='-1'])";
|
|
10
|
+
const TEXT_ENTRY = "textarea, select";
|
|
11
|
+
const NON_TEXT_INPUT = ["checkbox", "radio", "button", "submit", "reset", "file"];
|
|
12
|
+
|
|
13
|
+
function isTextEntry(el) {
|
|
14
|
+
if (el.isContentEditable || el.closest(TEXT_ENTRY)) return true;
|
|
15
|
+
if (el.tagName !== "INPUT") return false;
|
|
16
|
+
return NON_TEXT_INPUT.indexOf(el.type) < 0;
|
|
17
|
+
}
|
|
17
18
|
|
|
18
19
|
function forced() {
|
|
19
20
|
try {
|
|
@@ -34,40 +35,23 @@ function stylesOnce() {
|
|
|
34
35
|
|
|
35
36
|
function focusApp() {
|
|
36
37
|
const next = [...document.querySelectorAll(FOCUSABLE)].find(
|
|
37
|
-
(el) => !el.disabled && !el.closest("[clay~='editor-ui']") && (typeof el.checkVisibility !== "function" || el.checkVisibility())
|
|
38
|
+
(el) => !el.disabled && !isTextEntry(el) && !el.closest("[clay~='editor-ui']") && (typeof el.checkVisibility !== "function" || el.checkVisibility())
|
|
38
39
|
);
|
|
39
|
-
if (next) next.focus();
|
|
40
|
-
else document.body.focus();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function mountIntro({ memory, template, appName, link, tag, source, hadDraft, prompt, snapshot }) {
|
|
44
|
-
if (!template) return null;
|
|
45
|
-
const opts = { memory, template, appName, link, tag, source, hadDraft, prompt, snapshot };
|
|
46
|
-
if (forced()) return mount(opts);
|
|
47
|
-
if (memory.get().intro.done || hadDraft) return mountAbout(opts);
|
|
48
|
-
if (window.location.hash) return null;
|
|
49
|
-
if (window.matchMedia && window.matchMedia("print").matches) return null;
|
|
50
|
-
return mount(opts);
|
|
40
|
+
if (next) next.focus({ preventScroll: true });
|
|
41
|
+
else document.body.focus({ preventScroll: true });
|
|
51
42
|
}
|
|
52
43
|
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
button.addEventListener("click", () => {
|
|
62
|
-
button.remove();
|
|
63
|
-
mount({ ...opts, fromAbout: true });
|
|
64
|
-
});
|
|
65
|
-
document.body.appendChild(button);
|
|
66
|
-
return button;
|
|
44
|
+
export function mountIntro({ memory, template, appName, hadDraft }) {
|
|
45
|
+
if (!template || !template.content.textContent.trim() && !template.content.querySelector("img, svg, video, canvas")) return null;
|
|
46
|
+
if (!forced()) {
|
|
47
|
+
if (memory.get().intro.done || hadDraft) return null;
|
|
48
|
+
if (window.location.hash) return null;
|
|
49
|
+
if (window.matchMedia && window.matchMedia("print").matches) return null;
|
|
50
|
+
}
|
|
51
|
+
return mount({ memory, template, appName });
|
|
67
52
|
}
|
|
68
53
|
|
|
69
|
-
function mount(
|
|
70
|
-
const { memory, template, appName, link, tag, source, prompt, snapshot, fromAbout } = opts;
|
|
54
|
+
function mount({ memory, template, appName }) {
|
|
71
55
|
stylesOnce();
|
|
72
56
|
|
|
73
57
|
const section = document.createElement("section");
|
|
@@ -75,157 +59,60 @@ function mount(opts) {
|
|
|
75
59
|
section.setAttribute("clay", "editor-ui");
|
|
76
60
|
section.setAttribute("sap-ignore", "");
|
|
77
61
|
section.setAttribute("aria-label", COPY.label(appName));
|
|
62
|
+
section.appendChild(template.content.cloneNode(true));
|
|
78
63
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const name = document.createElement("h1");
|
|
87
|
-
name.className = "clay-intro__name";
|
|
88
|
-
name.textContent = appName;
|
|
89
|
-
|
|
90
|
-
const actions = document.createElement("div");
|
|
91
|
-
actions.className = "clay-intro__actions";
|
|
92
|
-
|
|
93
|
-
const download = document.createElement("a");
|
|
94
|
-
download.className = "clay-intro__download";
|
|
95
|
-
download.href = link;
|
|
96
|
-
download.textContent = COPY.download;
|
|
97
|
-
|
|
98
|
-
const form = document.createElement("form");
|
|
99
|
-
form.className = "clay-intro__signup";
|
|
100
|
-
form.noValidate = true;
|
|
101
|
-
|
|
102
|
-
const label = document.createElement("label");
|
|
103
|
-
label.setAttribute("for", "clay-intro-email");
|
|
104
|
-
label.textContent = COPY.signupLabel;
|
|
105
|
-
|
|
106
|
-
const row = document.createElement("div");
|
|
107
|
-
row.className = "clay-intro__row";
|
|
108
|
-
|
|
109
|
-
const email = document.createElement("input");
|
|
110
|
-
email.id = "clay-intro-email";
|
|
111
|
-
email.type = "email";
|
|
112
|
-
email.autocomplete = "email";
|
|
113
|
-
email.required = true;
|
|
114
|
-
email.placeholder = COPY.emailPlaceholder;
|
|
115
|
-
|
|
116
|
-
const send = document.createElement("button");
|
|
117
|
-
send.type = "submit";
|
|
118
|
-
send.textContent = COPY.signup;
|
|
64
|
+
const close = document.createElement("button");
|
|
65
|
+
close.type = "button";
|
|
66
|
+
close.className = "clay-intro__close";
|
|
67
|
+
close.setAttribute("aria-label", COPY.close);
|
|
68
|
+
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>';
|
|
69
|
+
section.appendChild(close);
|
|
119
70
|
|
|
120
|
-
row.append(email, send);
|
|
121
|
-
|
|
122
|
-
const honey = document.createElement("input");
|
|
123
|
-
honey.className = "clay-intro__hp";
|
|
124
|
-
honey.name = "website";
|
|
125
|
-
honey.tabIndex = -1;
|
|
126
|
-
honey.autocomplete = "off";
|
|
127
|
-
honey.setAttribute("aria-hidden", "true");
|
|
128
|
-
|
|
129
|
-
const message = document.createElement("p");
|
|
130
|
-
message.className = "clay-intro__msg";
|
|
131
|
-
message.setAttribute("role", "status");
|
|
132
|
-
message.hidden = true;
|
|
133
|
-
|
|
134
|
-
form.append(label, row, honey, message);
|
|
135
|
-
actions.append(download, form);
|
|
136
|
-
|
|
137
|
-
const start = document.createElement("button");
|
|
138
|
-
start.type = "button";
|
|
139
|
-
start.className = "clay-intro__start";
|
|
140
|
-
start.textContent = COPY.start(appName);
|
|
141
|
-
|
|
142
|
-
inner.append(kicker, name, template.content.cloneNode(true), actions, start);
|
|
143
|
-
section.appendChild(inner);
|
|
144
|
-
|
|
145
|
-
let observer = null;
|
|
146
71
|
let gone = false;
|
|
72
|
+
let frame = 0;
|
|
147
73
|
|
|
148
|
-
function
|
|
149
|
-
document.removeEventListener("input", onUse, true);
|
|
150
|
-
document.removeEventListener("change", onUse, true);
|
|
151
|
-
document.removeEventListener("clay:dirty", onUse, true);
|
|
152
|
-
if (observer) { observer.disconnect(); observer = null; }
|
|
153
|
-
if (prompt) prompt.hideForIntro(false);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function dismiss(reason, target) {
|
|
74
|
+
function dismiss(reason) {
|
|
157
75
|
if (gone) return;
|
|
158
76
|
gone = true;
|
|
159
|
-
|
|
160
|
-
|
|
77
|
+
removeEventListener("scroll", onScroll);
|
|
78
|
+
if (frame) cancelAnimationFrame(frame);
|
|
79
|
+
const height = section.getBoundingClientRect().height;
|
|
80
|
+
const before = window.scrollY;
|
|
161
81
|
memory.get().intro.done = reason;
|
|
162
82
|
memory.save();
|
|
163
|
-
stop();
|
|
164
83
|
section.remove();
|
|
165
|
-
if (reason === "
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
} else if (anchor && anchor.getBoundingClientRect) {
|
|
169
|
-
window.scrollBy(0, anchor.getBoundingClientRect().top - before);
|
|
84
|
+
if (reason === "scrolled") {
|
|
85
|
+
const locked = /hidden|clip/.test(getComputedStyle(document.body).overflowY + getComputedStyle(document.documentElement).overflowY);
|
|
86
|
+
window.scrollTo(0, locked ? 0 : Math.max(0, before - height));
|
|
170
87
|
}
|
|
171
|
-
|
|
88
|
+
announceBottomUi();
|
|
89
|
+
if (reason !== "scrolled") focusApp();
|
|
172
90
|
}
|
|
173
91
|
|
|
174
|
-
function
|
|
175
|
-
|
|
92
|
+
function onScroll() {
|
|
93
|
+
if (frame) return;
|
|
94
|
+
frame = requestAnimationFrame(() => {
|
|
95
|
+
frame = 0;
|
|
96
|
+
const bottom = section.getBoundingClientRect().bottom;
|
|
97
|
+
const atEnd = scrollY > 0 && scrollY + innerHeight >= document.documentElement.scrollHeight - 1;
|
|
98
|
+
if (bottom < 1 || (atEnd && bottom < innerHeight)) dismiss("scrolled");
|
|
99
|
+
});
|
|
176
100
|
}
|
|
177
101
|
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
if (!unchanged()) dismiss("use", event.target);
|
|
102
|
+
section.addEventListener("click", (event) => {
|
|
103
|
+
if (event.target.closest(".clay-intro__close, [visitor-intro-dismiss]")) {
|
|
104
|
+
dismiss("closed");
|
|
182
105
|
return;
|
|
183
106
|
}
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
107
|
+
if (event.target.closest("[visitor-intro-start]")) {
|
|
108
|
+
event.preventDefault();
|
|
109
|
+
window.scrollTo({ top: window.scrollY + section.getBoundingClientRect().bottom, behavior: "smooth" });
|
|
187
110
|
}
|
|
188
|
-
const target = event.target;
|
|
189
|
-
setTimeout(() => {
|
|
190
|
-
if (!gone && !unchanged()) dismiss("use", target);
|
|
191
|
-
}, 0);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
form.addEventListener("submit", (event) => {
|
|
195
|
-
event.preventDefault();
|
|
196
|
-
fetch("/_/newsletter/subscribe", {
|
|
197
|
-
method: "POST",
|
|
198
|
-
headers: { "Content-Type": "application/json" },
|
|
199
|
-
credentials: "same-origin",
|
|
200
|
-
body: JSON.stringify({ email: email.value.trim(), tags: [tag], source, website: honey.value }),
|
|
201
|
-
}).then((response) => {
|
|
202
|
-
if (!response.ok) throw new Error("signup failed");
|
|
203
|
-
row.hidden = true;
|
|
204
|
-
message.textContent = COPY.signupThanks;
|
|
205
|
-
message.hidden = false;
|
|
206
|
-
}).catch(() => {
|
|
207
|
-
message.textContent = COPY.signupFailed;
|
|
208
|
-
message.hidden = false;
|
|
209
|
-
});
|
|
210
111
|
});
|
|
211
112
|
|
|
212
|
-
start.addEventListener("click", () => dismiss("button"));
|
|
213
|
-
|
|
214
|
-
const baseline = snapshot ? snapshot() : null;
|
|
215
113
|
document.body.prepend(section);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
document.addEventListener("clay:dirty", onUse, true);
|
|
219
|
-
|
|
220
|
-
if (prompt) {
|
|
221
|
-
prompt.hideForIntro(true);
|
|
222
|
-
if (typeof IntersectionObserver === "function") {
|
|
223
|
-
observer = new IntersectionObserver((entries) => {
|
|
224
|
-
for (const entry of entries) prompt.hideForIntro(entry.intersectionRatio > 0.3);
|
|
225
|
-
});
|
|
226
|
-
observer.observe(section);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
114
|
+
announceBottomUi();
|
|
115
|
+
addEventListener("scroll", onScroll, { passive: true });
|
|
229
116
|
|
|
230
117
|
return section;
|
|
231
118
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDraftMode } from "../../core/is-edit-mode.js";
|
|
2
|
+
import { BOTTOM_UI_CHANGED, announceBottomUi, bottomBarHeight, promptPanel } from "./bottom-ui.js";
|
|
2
3
|
|
|
3
4
|
export const COPY = {
|
|
4
5
|
draft: "You're seeing a private draft, like a visitor.",
|
|
@@ -50,17 +51,23 @@ const CHIP_CSS = `.clay-owner {
|
|
|
50
51
|
-webkit-font-smoothing: antialiased;
|
|
51
52
|
}
|
|
52
53
|
.clay-owner__action {
|
|
53
|
-
flex: none; font-weight: 600; color: var(--hcp-brass); cursor: pointer;
|
|
54
|
+
flex: none; min-height: 0; font-weight: 600; color: var(--hcp-brass); cursor: pointer;
|
|
54
55
|
text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--hcp-line-2);
|
|
55
56
|
white-space: nowrap;
|
|
56
57
|
}
|
|
57
|
-
.clay-owner__action:hover { color: var(--hcp-ink); }
|
|
58
|
+
.clay-owner .clay-owner__action:hover { color: var(--hcp-ink); background: none; }
|
|
58
59
|
.clay-owner__x {
|
|
59
60
|
flex: none; display: inline-flex; align-items: center; justify-content: center;
|
|
60
|
-
width: 24px; height: 24px; font-size: 15px; color: var(--hcp-muted); cursor: pointer;
|
|
61
|
+
width: 24px; height: 24px; min-height: 0; font-size: 15px; color: var(--hcp-muted); cursor: pointer;
|
|
61
62
|
}
|
|
62
|
-
.clay-owner__x:hover { color: var(--hcp-ink); }
|
|
63
|
+
.clay-owner .clay-owner__x:hover { color: var(--hcp-ink); background: none; }
|
|
63
64
|
.clay-owner__action:focus-visible, .clay-owner__x:focus-visible { outline: 2px solid var(--hcp-brass); outline-offset: 2px; }
|
|
65
|
+
|
|
66
|
+
.clay-owner svg {
|
|
67
|
+
display: block; flex: none; width: 1em; height: 1em; line-height: 1;
|
|
68
|
+
fill: currentColor; stroke: none; stroke-width: 0; stroke-linecap: butt; stroke-linejoin: miter;
|
|
69
|
+
}
|
|
70
|
+
.clay-owner button, .clay-owner a { border-radius: 0; line-height: 1.45; }
|
|
64
71
|
@media (pointer: coarse) {
|
|
65
72
|
.clay-owner__action, .clay-owner__x { min-height: 44px; }
|
|
66
73
|
.clay-owner__x { width: 44px; }
|
|
@@ -111,40 +118,60 @@ export function mountOwnerChip() {
|
|
|
111
118
|
close.className = "clay-owner__x";
|
|
112
119
|
close.setAttribute("aria-label", COPY.close);
|
|
113
120
|
close.textContent = "\u00d7";
|
|
114
|
-
close.addEventListener("click", () => { chip.remove(); liftOff(); });
|
|
121
|
+
close.addEventListener("click", () => { chip.remove(); liftOff(); release(); });
|
|
115
122
|
|
|
116
123
|
chip.append(lead, action, close);
|
|
117
124
|
document.body.appendChild(chip);
|
|
118
125
|
|
|
126
|
+
const vars = document.createElement("style");
|
|
127
|
+
vars.id = "clay-owner-vars";
|
|
128
|
+
vars.setAttribute("clay", "editor-ui");
|
|
129
|
+
document.head.appendChild(vars);
|
|
130
|
+
|
|
131
|
+
let published = "";
|
|
132
|
+
function publish(lift) {
|
|
133
|
+
const next = `:root { --clay-owner-room: ${Math.ceil(chip.getBoundingClientRect().height) + 16}px; }`;
|
|
134
|
+
if (vars.textContent !== next) vars.textContent = next;
|
|
135
|
+
if (published === next + lift) return;
|
|
136
|
+
published = next + lift;
|
|
137
|
+
announceBottomUi();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function release() {
|
|
141
|
+
vars.remove();
|
|
142
|
+
announceBottomUi();
|
|
143
|
+
}
|
|
144
|
+
|
|
119
145
|
let frame = 0;
|
|
120
146
|
function lift() {
|
|
121
147
|
if (frame) return;
|
|
122
148
|
frame = requestAnimationFrame(() => {
|
|
123
149
|
frame = 0;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (position === "fixed" || position === "sticky") {
|
|
132
|
-
raise = Math.max(raise, Math.ceil(window.innerHeight - el.getBoundingClientRect().top));
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
chip.style.setProperty("--hcp-lift", (raise > 0 && raise < window.innerHeight / 3 ? raise : 0) + "px");
|
|
150
|
+
let lift = bottomBarHeight(24, (el) => chip.contains(el) || el.closest("#hcp"));
|
|
151
|
+
const panel = promptPanel(chip.getBoundingClientRect());
|
|
152
|
+
const covered = panel >= window.innerHeight / 3;
|
|
153
|
+
if (panel && !covered) lift = Math.max(lift, panel - 4);
|
|
154
|
+
chip.style.visibility = covered ? "hidden" : "";
|
|
155
|
+
chip.style.setProperty("--hcp-lift", lift + "px");
|
|
156
|
+
publish(covered ? -1 : lift);
|
|
138
157
|
});
|
|
139
158
|
}
|
|
140
159
|
|
|
141
160
|
function liftOff() {
|
|
142
161
|
removeEventListener("scroll", lift);
|
|
143
162
|
removeEventListener("resize", lift);
|
|
163
|
+
document.removeEventListener(BOTTOM_UI_CHANGED, lift);
|
|
164
|
+
document.removeEventListener("transitionend", lift, true);
|
|
165
|
+
document.removeEventListener("pointerup", lift, true);
|
|
166
|
+
window.removeEventListener("hashchange", lift);
|
|
144
167
|
}
|
|
145
168
|
|
|
146
169
|
addEventListener("scroll", lift, { passive: true });
|
|
147
170
|
addEventListener("resize", lift);
|
|
171
|
+
document.addEventListener(BOTTOM_UI_CHANGED, lift);
|
|
172
|
+
document.addEventListener("transitionend", lift, true);
|
|
173
|
+
document.addEventListener("pointerup", lift, true);
|
|
174
|
+
window.addEventListener("hashchange", lift);
|
|
148
175
|
lift();
|
|
149
176
|
|
|
150
177
|
return chip;
|