@gemboss/website-ui 0.10.0 → 0.11.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/CHANGELOG.md +59 -0
- package/README.md +2 -2
- package/bin/lib/check.mjs +6 -2
- package/css/base.css +2 -1
- package/css/effects.css +1 -1
- package/css/header.css +335 -115
- package/css/tokens.css +22 -3
- package/demo/article.html +58 -70
- package/demo/blog.html +58 -70
- package/demo/brand-profiles.html +57 -27
- package/demo/footer.html +58 -70
- package/demo/forms.html +58 -70
- package/demo/header.html +58 -108
- package/demo/index.html +58 -70
- package/demo/spotlight.html +58 -71
- package/demo/theme-example.html +1 -1
- package/docs/components.md +17 -12
- package/docs/markup-contract.md +45 -19
- package/js/website.js +284 -41
- package/js/website.mjs +314 -42
- package/package.json +1 -1
- package/system.json +20 -5
- package/templates/header.html +47 -24
- package/templates/page.html +54 -20
- package/website.css +361 -121
- package/website.min.css +1 -1
package/docs/markup-contract.md
CHANGED
|
@@ -44,8 +44,8 @@ the spotlight (0.9.0). It comes in two entries built from one source
|
|
|
44
44
|
to the page (Escape, the outside click, the busy-button guard, the system theme, the scroll) are installed once.
|
|
45
45
|
- **`destroy(root = document)`** takes back what `init` wired under `root`: its listeners and its marks. An open
|
|
46
46
|
mega menu or mobile menu is closed first, so an unmounted menu cannot leave the page locked.
|
|
47
|
-
- **`version`**: `'0.
|
|
48
|
-
- **`data-gw-ready`**: on each wired element, the parts wired on it (`mega`, `menu`, `lang`, `theme`, `reveal`,
|
|
47
|
+
- **`version`**: `'0.11.0'`. The classic script exposes the three as `window.gwWebsite`.
|
|
48
|
+
- **`data-gw-ready`**: on each wired element, the parts wired on it (`mega`, on the `.gw-header` since 0.11.0, `menu`, `lang`, `theme`, `reveal`,
|
|
49
49
|
`header`, `segmented`, `toc`, `table`, `fold`, `spot`). A drill-down menu is wired with its toggle (`menu`). It is a mark for people and tests, not a switch: markup copied from a live page, attribute and all,
|
|
50
50
|
is still wired.
|
|
51
51
|
- **One copy per page.** The first copy that runs owns the page (`window.gwWebsite`); another one (the classic
|
|
@@ -92,7 +92,7 @@ The script tells the page's own code what changed. Every event is a `CustomEvent
|
|
|
92
92
|
|
|
93
93
|
| event | target | `detail` | fires when |
|
|
94
94
|
|---|---|---|---|
|
|
95
|
-
| `gw:mega` | the
|
|
95
|
+
| `gw:mega` | the panel's name (`.gw-nav__trigger`, or the `[data-gw-mega]` of a 0.10.0 header) | `{ id, open }`: the panel's `id`, `true` when it opened | a panel opens or closes: the pointer resting on a name or leaving it, the keyboard's focus, ArrowDown, Escape, a click on a `<button>` name, a link inside, a click outside, another panel opening (in this header or another), `destroy` of an open one. Moving to another menu fires a close, then an open |
|
|
96
96
|
| `gw:theme` | `<html>` | `{ theme: 'light' \| 'dark' }`: the theme the page now shows | a press on a theme toggle; also a change of the system's scheme while the page follows it (no `data-theme`) |
|
|
97
97
|
| `gw:segmented` | the group (`[data-gw-segmented]`) | `{ name, value }`: the group's name, the chosen option's `value` | the reader chooses an option (click, arrows, Home, End) |
|
|
98
98
|
| `gw:menu` | the mobile menu (`.gw-mobile-menu`) | `{ open, level }`: `true` while it is open; `level` is the title of the drill-down level in view, `''` for the top level (and for a 0.7.0 menu) | the menu opens or closes (the toggle, Escape, a link inside, resizing past 1080px, `destroy` of an open one), and a drill-down menu moves to a level or back to the top. Closing from a level fires only the close (0.8.0) |
|
|
@@ -108,43 +108,59 @@ document.addEventListener('gw:menu', function (e) { if (e.detail.level) analytic
|
|
|
108
108
|
|
|
109
109
|
```html
|
|
110
110
|
<header class="gw-header gw-header--float?" data-gw-header> <!-- --float: a brand's header profile (0.3.0) -->
|
|
111
|
-
<div class="gw-
|
|
112
|
-
<a class="gw-brand" href="/"><img class="gw-brand__mark" src="…" alt="" width="
|
|
111
|
+
<div class="gw-header__bar"> <!-- 0.11.0: no .gw-wrap; the bar is wrap + 12px -->
|
|
112
|
+
<a class="gw-brand" href="/"><img class="gw-brand__mark" src="…" alt="" width="34" height="34"><span class="gw-brand__name">GemBoss</span></a>
|
|
113
113
|
<nav class="gw-nav" aria-label="Main">
|
|
114
114
|
<ul class="gw-nav__list">
|
|
115
115
|
<li class="gw-nav__item">
|
|
116
|
-
<
|
|
117
|
-
<div class="gw-mega" id="gw-mega-product" hidden>
|
|
118
|
-
<div class="gw-
|
|
119
|
-
<div class="gw-
|
|
120
|
-
<
|
|
116
|
+
<a class="gw-nav__trigger" href="/product" aria-current?="true" aria-expanded="false" aria-controls="gw-mega-product">Product <svg class="gw-nav__chev" aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6"><path d="M6 9l6 6 6-6"/></svg></a>
|
|
117
|
+
<div class="gw-mega" id="gw-mega-product" style="--gw-mega-w: 1120px; --gw-mega-pv: 380px; --gw-mega-cols?: 2" hidden>
|
|
118
|
+
<div class="gw-mega__body">
|
|
119
|
+
<div class="gw-mega__grid">
|
|
120
|
+
<div class="gw-mega__col"><p class="gw-mega__heading">Build</p>
|
|
121
|
+
<a class="gw-mega__link" href="…" data-gw-preview?="pv-funnel"><span><b>Title</b><i>One line</i></span></a>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
<div class="gw-mega__aside"> <!-- optional: the pictures -->
|
|
125
|
+
<a class="gw-mega__card" href="…"><span class="gw-mega__art">…on the Stage…</span><span class="gw-mega__card-text"><b>Title</b><i>One line</i><span class="gw-mega__go">See it →</span></span></a>
|
|
121
126
|
</div>
|
|
122
|
-
<a class="gw-mega__feature" href="…">…</a> <!-- optional card on the right -->
|
|
123
127
|
</div>
|
|
128
|
+
<div class="gw-mega__foot">…</div> <!-- optional strip: __foot-start, __foot-end, a.gw-mega__cta, .gw-mega__new -->
|
|
124
129
|
</div>
|
|
125
130
|
</li>
|
|
126
131
|
<li class="gw-nav__item"><a class="gw-nav__link" href="/pricing" aria-current?="page">Pricing</a></li>
|
|
127
132
|
</ul>
|
|
128
133
|
</nav>
|
|
129
134
|
<div class="gw-header__actions">
|
|
130
|
-
<button class="gw-theme-toggle" type="button" data-gw-theme-toggle aria-label="Switch to dark theme">svg</button>
|
|
131
135
|
<div class="gw-lang" data-gw-lang hidden> … </div> <!-- optional: the language switcher (0.8.0, below) -->
|
|
132
136
|
<a class="gw-nav__link gw-header__signin" href="…">Sign in</a>
|
|
133
|
-
<a class="gw-
|
|
137
|
+
<a class="gw-nav__link gw-header__link" href="…">Book a demo</a> <!-- 0.11.0: a second text action, from 1181px -->
|
|
134
138
|
<a class="gw-btn gw-btn--sm" href="…">Start free</a>
|
|
135
139
|
<button class="gw-menu-toggle" type="button" aria-expanded="false" aria-controls="gw-mobile-menu" data-gw-menu-toggle aria-label="Open menu">svg</button>
|
|
136
140
|
</div>
|
|
137
141
|
</div>
|
|
138
142
|
<div class="gw-mobile-menu" id="gw-mobile-menu" data-gw-menu?="drill" hidden> …the same links as lists, and the actions… </div>
|
|
143
|
+
<template id="pv-funnel">…the row's preview, for the Stage…</template>
|
|
139
144
|
</header>
|
|
140
145
|
```
|
|
141
146
|
|
|
142
147
|
Script behaviour:
|
|
143
|
-
- **Mega menu
|
|
144
|
-
`.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
- **Mega menu** (0.11.0, gemboss.ai's own, from 1081px; one record per header, the `mega` mark on the `.gw-header`):
|
|
149
|
+
the script marks the header `.has-mega`, takes `hidden` off the panels and keeps a closed one `inert`, and draws
|
|
150
|
+
`.gw-mega-surface`, `.gw-mega-caret` and `.gw-nav__pill` in the header and `.gw-scrim` in the body (all
|
|
151
|
+
`aria-hidden`). A section's name is a link to its page: the pointer resting on it 200ms opens its panel (90ms to
|
|
152
|
+
switch once one is open; a touch is ignored), the keyboard's focus opens it at once, ArrowDown opens it and puts the
|
|
153
|
+
focus on its first row; 350ms after the pointer leaves it closes. Open: `aria-expanded="true"`, `.is-open` on the
|
|
154
|
+
`.gw-nav__item`, `.is-mega-open` on the header, the scrim on; the surface takes the panel's size and place (centred
|
|
155
|
+
under the name, 8px inside the bar's edges) and the panels crossfade on it. A row under the pointer or the focus is
|
|
156
|
+
lit (`.gw-mega__hl`) and shows its preview: the `<template>` its `data-gw-preview` names, cloned once into a
|
|
157
|
+
`.gw-mega__card` (its title the row's, `aria-hidden`, out of the Tab order); `data-gw-preview="feature"`, the
|
|
158
|
+
panel's own card. A card's pictures load the first time it shows: `data-gw-bg="url"` becomes its background,
|
|
159
|
+
`<template data-gw-hydrate>` is added to its parent (`.is-hydrated` on both). `Escape` closes and gives the focus
|
|
160
|
+
back to the name; a link inside or a click outside closes. One panel open on the page, across every header. A
|
|
161
|
+
`<button>` name (0.10.0 markup) also opens its panel with a click and keeps it open when the pointer leaves; a click
|
|
162
|
+
on it again closes it. Each change fires `gw:mega`. `destroy` takes all of it back.
|
|
163
|
+
- **Mobile menu** (up to 1080px the `.gw-nav` and sign-in hide, `.gw-menu-toggle` shows): the toggle flips `hidden`
|
|
148
164
|
on the menu its `aria-controls` names (`#gw-mobile-menu`), `aria-expanded`, `.is-menu-open` on `<html>` (the CSS
|
|
149
165
|
locks scroll), and its `aria-label` (Open menu / Close menu). `Escape` closes. Resizing above 1080px closes it.
|
|
150
166
|
- **Labels in other languages**: the script never writes English over a translated page. Give the toggles their
|
|
@@ -160,6 +176,8 @@ Script behaviour:
|
|
|
160
176
|
- **Header**: `.is-scrolled` on `[data-gw-header]` once the page scrolled more than 8px (the CSS may use it for the
|
|
161
177
|
hairline only; the bar does not change height or colour). The floating header needs nothing more from the script:
|
|
162
178
|
it is the same element with one more class.
|
|
179
|
+
- **Current section** (0.11.0): a section's name with `aria-current` in the markup (`"true"`: the page is in that
|
|
180
|
+
section) is drawn like the current page's link, in the accent's strong ink at weight 600.
|
|
163
181
|
- **Current page**: the script marks `aria-current="page"` on `.gw-nav__link` / `.gw-mega__link` and the mobile
|
|
164
182
|
menu's links whose `href` pathname equals `location.pathname` (when the markup did not already). Each `init` takes
|
|
165
183
|
its own marks back first, so after a client-side navigation they follow the new path; a mark in the markup stays.
|
|
@@ -217,6 +235,14 @@ top-level links and the actions are children of the sheet's `<nav>`.
|
|
|
217
235
|
- `check` reports a group of a drill menu that is not a `<details>`, has no `<summary>`, no back button, no title or a
|
|
218
236
|
title without `tabindex="-1"`, and `data-gw-menu` with another value (`menu-markup`).
|
|
219
237
|
|
|
238
|
+
**0.11.0, as gemboss.ai's phone menu:** the sheet is 24px in from both edges and its links are set at 18px, weight
|
|
239
|
+
500, 0.005em tighter. A level may end with its panel's card (the same `.gw-mega__card` markup as in the panel: its
|
|
240
|
+
pictures load the first time the level shows) and `.gw-mobile-menu__all`, the link to the section's own page ("All of
|
|
241
|
+
Product →", the arrow drawn after a space). A row may carry a 48x32 picture (`<img>`) instead of its icon. A row under
|
|
242
|
+
the pointer or the keyboard takes the soft ground, moves 2px right, its icon tile in the accent. In the action bar,
|
|
243
|
+
`.gw-mobile-menu__link--muted` is a quieter link before Sign in (What's new); the buttons are 8px apart. The current
|
|
244
|
+
page is not marked on the phone.
|
|
245
|
+
|
|
220
246
|
### Language switcher (0.8.0)
|
|
221
247
|
|
|
222
248
|
```html
|
|
@@ -232,7 +258,7 @@ top-level links and the actions are children of the sheet's `<nav>`.
|
|
|
232
258
|
</div>
|
|
233
259
|
```
|
|
234
260
|
|
|
235
|
-
- **Where**: in `.gw-header__actions`, before Sign in (drawn from
|
|
261
|
+
- **Where**: in `.gw-header__actions`, before Sign in (drawn from 1081px), and a second one, with its own list `id`, in
|
|
236
262
|
the mobile menu's `.gw-mobile-menu__actions` (its list opens upward there). Both are filled the same way.
|
|
237
263
|
- **The languages are the head's** `<link rel="alternate" hreflang href>`, not `x-default`, the first link of each
|
|
238
264
|
`hreflang`: the links search engines read. The current language is the one whose `hreflang` is `<html lang>`, else the
|
package/js/website.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/*! @gemboss/website-ui 0.
|
|
1
|
+
/*! @gemboss/website-ui 0.11.0 website.js | GENERATED by scripts/build.mjs from js/website.mjs (the source, with its comments): edit that file | spec: docs/markup-contract.md | load with defer */
|
|
2
2
|
(function () {
|
|
3
3
|
'use strict';
|
|
4
4
|
if (window.gwWebsite) return;
|
|
5
5
|
|
|
6
|
-
var version = '0.
|
|
6
|
+
var version = '0.11.0';
|
|
7
7
|
var api = { version: version, init: init, destroy: destroy };
|
|
8
|
-
var doc, root, mine, fine, sys, wide, mid, live = [],
|
|
8
|
+
var doc, root, mine, fine, sys, wide, desk, mid, live = [], queued = false;
|
|
9
9
|
var still, lights;
|
|
10
10
|
var READY = 'data-gw-ready';
|
|
11
11
|
|
|
@@ -46,46 +46,286 @@ function drop(r) {
|
|
|
46
46
|
}
|
|
47
47
|
function records(kind, fn) { live.slice().forEach(function (r) { if (r.kind === kind) fn(r); }); }
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
setMega(m, true, mode);
|
|
62
|
-
}
|
|
63
|
-
function hideMega(focus) {
|
|
64
|
-
var m = megaOpen;
|
|
65
|
-
clearTimeout(megaTimer);
|
|
66
|
-
if (!m) return;
|
|
67
|
-
setMega(m, false);
|
|
68
|
-
if (focus === true) m.t.focus();
|
|
49
|
+
var hydrated = new WeakSet();
|
|
50
|
+
function hydrate(card) {
|
|
51
|
+
if (!card || hydrated.has(card)) return;
|
|
52
|
+
hydrated.add(card);
|
|
53
|
+
within('[data-gw-bg]', card).forEach(function (el) {
|
|
54
|
+
el.style.backgroundImage = 'url("' + attr(el, 'data-gw-bg', '') + '")';
|
|
55
|
+
el.classList.add('is-hydrated');
|
|
56
|
+
});
|
|
57
|
+
within('template[data-gw-hydrate]', card).forEach(function (t) {
|
|
58
|
+
t.parentNode.appendChild(t.content.cloneNode(true));
|
|
59
|
+
t.parentNode.classList.add('is-hydrated');
|
|
60
|
+
});
|
|
69
61
|
}
|
|
70
|
-
function inside(m, el) { return !!el && (m.item.contains(el) || m.p.contains(el)); }
|
|
71
62
|
function mega(ctx) {
|
|
72
|
-
each(ctx, '
|
|
73
|
-
var
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
63
|
+
each(ctx, '.gw-header', 'mega', function (header, on) {
|
|
64
|
+
var items = [];
|
|
65
|
+
all('.gw-nav__item', header).forEach(function (item) {
|
|
66
|
+
var t = item.querySelector('[data-gw-mega], .gw-nav__trigger');
|
|
67
|
+
var p = t && doc.getElementById(attr(t, 'data-gw-mega', attr(t, 'aria-controls', '')));
|
|
68
|
+
if (p) items.push({ item: item, t: t, p: p, body: p.querySelector('.gw-mega__body'), foot: p.querySelector('.gw-mega__foot'),
|
|
69
|
+
grid: p.querySelector('.gw-mega__grid'), rows: all('.gw-mega__link', p), pane: p.querySelector('.gw-mega__aside') });
|
|
70
|
+
});
|
|
71
|
+
if (!items.length) return;
|
|
72
|
+
var nav = header.querySelector('.gw-nav') || header;
|
|
73
|
+
var bar = header.querySelector('.gw-header__bar') || header;
|
|
74
|
+
var mk = function (tag, cls) { var n = doc.createElement(tag); n.className = cls; n.setAttribute('aria-hidden', 'true'); return n; };
|
|
75
|
+
var scrim = mk('div', 'gw-scrim'), bg = mk('div', 'gw-mega-surface'), caret = mk('div', 'gw-mega-caret'), pill = mk('span', 'gw-nav__pill');
|
|
76
|
+
var made = [scrim, bg, caret, pill], cards = new Map(), cur = null, openT = 0, closeT = 0, pvT = 0, pvTok = 0, quiet = false, rec;
|
|
77
|
+
doc.body.appendChild(scrim);
|
|
78
|
+
header.appendChild(pill);
|
|
79
|
+
header.appendChild(bg);
|
|
80
|
+
header.appendChild(caret);
|
|
81
|
+
header.classList.add('has-mega');
|
|
82
|
+
items.forEach(function (m) {
|
|
83
|
+
m.hid = m.p.hidden;
|
|
84
|
+
m.p.hidden = false;
|
|
85
|
+
m.p.inert = true;
|
|
86
|
+
m.feat = m.pane && m.pane.querySelector('.gw-mega__card');
|
|
87
|
+
});
|
|
88
|
+
var still2 = function () { return still.matches; };
|
|
89
|
+
function snap(els, fn) {
|
|
90
|
+
els.forEach(function (n) { if (n) n.style.transition = 'none'; });
|
|
91
|
+
fn();
|
|
92
|
+
void header.offsetWidth;
|
|
93
|
+
els.forEach(function (n) { if (n) n.style.transition = ''; });
|
|
94
|
+
}
|
|
95
|
+
function cardFor(m, row) {
|
|
96
|
+
var k = attr(row, 'data-gw-preview', '');
|
|
97
|
+
if (k === 'feature') return m.feat || null;
|
|
98
|
+
if (cards.has(row)) return cards.get(row);
|
|
99
|
+
var tpl = k && doc.getElementById(k);
|
|
100
|
+
if (!tpl || !tpl.content || !m.pane) return null;
|
|
101
|
+
var a = doc.createElement('a'), art = doc.createElement('span'), txt = doc.createElement('span'), go = doc.createElement('span');
|
|
102
|
+
var b = row.querySelector('b'), t = b ? b.cloneNode(true) : doc.createElement('b'), line = row.querySelector('i');
|
|
103
|
+
a.className = 'gw-mega__card';
|
|
104
|
+
a.href = attr(row, 'href', '#');
|
|
105
|
+
if (row.rel) a.rel = row.rel;
|
|
106
|
+
a.tabIndex = -1;
|
|
107
|
+
a.setAttribute('aria-hidden', 'true');
|
|
108
|
+
within('[aria-hidden="true"]', t).forEach(function (n) { if (n !== t) n.remove(); });
|
|
109
|
+
go.className = 'gw-mega__go';
|
|
110
|
+
go.textContent = /^https?:/.test(a.getAttribute('href')) ? '\u2197' : '\u2192';
|
|
111
|
+
t.appendChild(go);
|
|
112
|
+
art.className = 'gw-mega__art';
|
|
113
|
+
art.appendChild(tpl.content.cloneNode(true));
|
|
114
|
+
txt.className = 'gw-mega__card-text';
|
|
115
|
+
txt.appendChild(t);
|
|
116
|
+
if (line) txt.appendChild(line.cloneNode(true));
|
|
117
|
+
a.appendChild(art);
|
|
118
|
+
a.appendChild(txt);
|
|
119
|
+
m.pane.appendChild(a);
|
|
120
|
+
cards.set(row, a);
|
|
121
|
+
return a;
|
|
122
|
+
}
|
|
123
|
+
function build(m) {
|
|
124
|
+
if (m.built) return;
|
|
125
|
+
m.built = true;
|
|
126
|
+
if (m.grid) { m.hl = mk('span', 'gw-mega__hl'); m.grid.insertBefore(m.hl, m.grid.firstChild); made.push(m.hl); }
|
|
127
|
+
hydrate(m.feat);
|
|
128
|
+
if (m.pane && !m.feat && m.rows[0]) m.feat = cardFor(m, m.rows[0]);
|
|
129
|
+
var idle = window.requestIdleCallback || function (f) { return setTimeout(f, 200); };
|
|
130
|
+
if (m.pane) idle(function () { if (header.classList.contains('has-mega')) m.rows.forEach(function (row) { cardFor(m, row); }); });
|
|
131
|
+
}
|
|
132
|
+
function preview(m, row, now) {
|
|
133
|
+
clearTimeout(pvT);
|
|
134
|
+
var tok = ++pvTok;
|
|
135
|
+
var go = function () {
|
|
136
|
+
var c = row ? cardFor(m, row) : m.feat;
|
|
137
|
+
if (!c || c === m.on) return;
|
|
138
|
+
var swap = function () {
|
|
139
|
+
if (tok !== pvTok || m.on === c) return;
|
|
140
|
+
if (m.on) m.on.classList.remove('is-on');
|
|
141
|
+
c.classList.add('is-on');
|
|
142
|
+
m.on = c;
|
|
143
|
+
};
|
|
144
|
+
var wait = [].filter.call(c.querySelectorAll('img'), function (i) { return !i.complete && i.decode; });
|
|
145
|
+
if (!wait.length || !window.Promise) { swap(); return; }
|
|
146
|
+
var t = setTimeout(swap, 240);
|
|
147
|
+
Promise.all(wait.map(function (i) { return i.decode().catch(function () {}); })).then(function () { clearTimeout(t); swap(); });
|
|
148
|
+
};
|
|
149
|
+
if (now) go(); else pvT = setTimeout(go, 80);
|
|
150
|
+
}
|
|
151
|
+
function light(m, row) {
|
|
152
|
+
if (!m.hl) return;
|
|
153
|
+
if (!row) { m.hl.classList.remove('is-on'); return; }
|
|
154
|
+
var place = function () {
|
|
155
|
+
m.hl.style.translate = row.offsetLeft + 'px ' + row.offsetTop + 'px';
|
|
156
|
+
m.hl.style.width = row.offsetWidth + 'px';
|
|
157
|
+
m.hl.style.height = row.offsetHeight + 'px';
|
|
158
|
+
};
|
|
159
|
+
if (m.hl.classList.contains('is-on') && !still2()) place(); else snap([m.hl], place);
|
|
160
|
+
m.hl.classList.add('is-on');
|
|
161
|
+
}
|
|
162
|
+
function movePill(t) {
|
|
163
|
+
if (!t) { pill.classList.remove('is-on'); return; }
|
|
164
|
+
var hr = header.getBoundingClientRect(), r = t.getBoundingClientRect();
|
|
165
|
+
var place = function () {
|
|
166
|
+
pill.style.translate = Math.round(r.left - hr.left) + 'px ' + Math.round(r.top - hr.top) + 'px';
|
|
167
|
+
pill.style.width = Math.round(r.width) + 'px';
|
|
168
|
+
pill.style.height = Math.round(r.height) + 'px';
|
|
169
|
+
};
|
|
170
|
+
if (pill.classList.contains('is-on') && !still2()) place(); else snap([pill], place);
|
|
171
|
+
pill.classList.add('is-on');
|
|
172
|
+
}
|
|
173
|
+
function measure() {
|
|
174
|
+
var hr = header.getBoundingClientRect(), inner = bar.getBoundingClientRect();
|
|
175
|
+
var lo = inner.left - hr.left + 8, hi = inner.right - hr.left - 8;
|
|
176
|
+
header.style.setProperty('--gw-mega-top', (header.clientHeight + 8) + 'px');
|
|
177
|
+
items.forEach(function (m) {
|
|
178
|
+
var t = m.t.getBoundingClientRect(), w = m.p.offsetWidth, h = m.p.offsetHeight;
|
|
179
|
+
var cx = t.left + t.width / 2 - hr.left;
|
|
180
|
+
var x = Math.round(Math.max(lo, Math.min(cx - w / 2, hi - w)));
|
|
181
|
+
m.x = x; m.w = w; m.h = h;
|
|
182
|
+
m.cx = Math.round(Math.max(x + 24, Math.min(cx, x + w - 24)));
|
|
183
|
+
m.p.style.left = x + 'px';
|
|
184
|
+
m.p.style.transformOrigin = (m.cx - x) + 'px 0';
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function clipTo(m, r) {
|
|
188
|
+
if (!r) return 'inset(0px 0px 0px 0px round 20px)';
|
|
189
|
+
var l = Math.max(0, r.x - m.x), rt = Math.max(0, m.x + m.w - (r.x + r.w)), b = Math.max(0, m.h - r.h);
|
|
190
|
+
return 'inset(0px ' + Math.round(rt) + 'px ' + Math.round(b) + 'px ' + Math.round(l) + 'px round 20px)';
|
|
191
|
+
}
|
|
192
|
+
function surface(m, y) {
|
|
193
|
+
bg.style.left = m.x + 'px';
|
|
194
|
+
bg.style.translate = '0px ' + y + 'px';
|
|
195
|
+
bg.style.width = m.w + 'px';
|
|
196
|
+
bg.style.height = m.h + 'px';
|
|
197
|
+
bg.style.transformOrigin = (m.cx - m.x) + 'px 0';
|
|
198
|
+
caret.style.translate = (m.cx - 6) + 'px ' + y + 'px';
|
|
199
|
+
}
|
|
200
|
+
function show(m) {
|
|
201
|
+
clearTimeout(closeT);
|
|
202
|
+
if (cur === m) return;
|
|
203
|
+
var prev = cur;
|
|
204
|
+
cur = m;
|
|
205
|
+
if (prev) {
|
|
206
|
+
prev.pinned = false;
|
|
207
|
+
prev.item.classList.remove('is-open');
|
|
208
|
+
prev.t.setAttribute('aria-expanded', 'false');
|
|
209
|
+
prev.p.inert = true;
|
|
210
|
+
emit(prev.t, 'gw:mega', { id: prev.p.id, open: false });
|
|
211
|
+
}
|
|
212
|
+
if (!m) {
|
|
213
|
+
header.classList.remove('is-mega-open');
|
|
214
|
+
scrim.classList.remove('is-on');
|
|
215
|
+
if (prev) { bg.style.translate = '0px -6px'; caret.style.translate = (prev.cx - 6) + 'px -6px'; }
|
|
216
|
+
movePill(nav.querySelector('.gw-nav__trigger:hover, .gw-nav__link:hover'));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
build(m);
|
|
220
|
+
records('mega', function (o) { if (o !== rec && o.open()) o.close(); });
|
|
221
|
+
preview(m, null, true);
|
|
222
|
+
light(m, null);
|
|
223
|
+
if (!prev) {
|
|
224
|
+
measure();
|
|
225
|
+
snap([bg, caret, m.p, m.body, m.foot], function () {
|
|
226
|
+
surface(m, -6);
|
|
227
|
+
m.p.style.clipPath = clipTo(m, null);
|
|
228
|
+
m.p.style.setProperty('--gw-mega-dx', '0px');
|
|
229
|
+
});
|
|
230
|
+
header.classList.add('is-mega-open');
|
|
231
|
+
surface(m, 0);
|
|
232
|
+
} else {
|
|
233
|
+
var hr = header.getBoundingClientRect(), br = bg.getBoundingClientRect();
|
|
234
|
+
var from = { x: br.left - hr.left, w: br.width, h: br.height };
|
|
235
|
+
var dir = items.indexOf(m) > items.indexOf(prev) ? 1 : -1;
|
|
236
|
+
if (!still2()) {
|
|
237
|
+
snap([m.p, m.body, m.foot], function () {
|
|
238
|
+
m.p.style.clipPath = clipTo(m, from);
|
|
239
|
+
m.p.style.setProperty('--gw-mega-dx', (dir * 8) + 'px');
|
|
240
|
+
});
|
|
241
|
+
prev.p.style.clipPath = clipTo(prev, m);
|
|
242
|
+
prev.p.style.setProperty('--gw-mega-dx', (-dir * 8) + 'px');
|
|
243
|
+
}
|
|
244
|
+
m.p.style.clipPath = clipTo(m, null);
|
|
245
|
+
m.p.style.setProperty('--gw-mega-dx', '0px');
|
|
246
|
+
surface(m, 0);
|
|
247
|
+
}
|
|
248
|
+
m.p.inert = false;
|
|
249
|
+
m.item.classList.add('is-open');
|
|
250
|
+
m.t.setAttribute('aria-expanded', 'true');
|
|
251
|
+
scrim.classList.add('is-on');
|
|
252
|
+
movePill(m.t);
|
|
253
|
+
emit(m.t, 'gw:mega', { id: m.p.id, open: true });
|
|
254
|
+
}
|
|
255
|
+
function hideSoon() { clearTimeout(openT); clearTimeout(closeT); closeT = setTimeout(function () { show(null); }, 350); }
|
|
256
|
+
items.forEach(function (m) {
|
|
257
|
+
on(m.p, 'transitionend', function (e) {
|
|
258
|
+
if (e.target === m.p && e.propertyName === 'opacity' && !m.item.classList.contains('is-open')) m.p.style.clipPath = '';
|
|
259
|
+
});
|
|
260
|
+
on(m.item, 'pointerenter', function (e) {
|
|
261
|
+
if (!desk.matches || e.pointerType === 'touch') return;
|
|
262
|
+
clearTimeout(closeT);
|
|
263
|
+
clearTimeout(openT);
|
|
264
|
+
openT = setTimeout(function () { show(m); }, cur ? 90 : 200);
|
|
265
|
+
});
|
|
266
|
+
on(m.item, 'pointerleave', function () { if (desk.matches && !m.pinned) hideSoon(); });
|
|
267
|
+
on(m.item, 'focusin', function () { if (desk.matches && !quiet) show(m); });
|
|
268
|
+
on(m.item, 'focusout', function (e) { if (desk.matches && !m.item.contains(e.relatedTarget)) hideSoon(); });
|
|
269
|
+
on(m.t, 'keydown', function (e) {
|
|
270
|
+
if (!desk.matches || e.key !== 'ArrowDown') return;
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
show(m);
|
|
273
|
+
if (m.rows[0]) m.rows[0].focus();
|
|
80
274
|
});
|
|
81
|
-
on(
|
|
82
|
-
if (
|
|
275
|
+
if (m.t.tagName === 'BUTTON') on(m.t, 'click', function () {
|
|
276
|
+
if (!desk.matches) return;
|
|
277
|
+
clearTimeout(openT);
|
|
278
|
+
if (cur === m && m.pinned) { show(null); return; }
|
|
279
|
+
show(m);
|
|
280
|
+
m.pinned = true;
|
|
83
281
|
});
|
|
282
|
+
on(m.p, 'click', function (e) { if (link(e)) show(null); });
|
|
283
|
+
m.rows.forEach(function (row) {
|
|
284
|
+
on(row, 'pointerenter', function () { if (!desk.matches || cur !== m) return; light(m, row); preview(m, row, false); });
|
|
285
|
+
on(row, 'focus', function () { if (!desk.matches) return; if (cur !== m) show(m); light(m, row); preview(m, row, true); });
|
|
286
|
+
});
|
|
287
|
+
if (m.feat && m.feat.tagName === 'A') on(m.feat, 'focus', function () { if (!desk.matches) return; light(m, null); preview(m, null, true); });
|
|
288
|
+
});
|
|
289
|
+
all('.gw-nav__trigger, .gw-nav__link', nav).forEach(function (t) {
|
|
290
|
+
on(t, 'pointerenter', function (e) { if (desk.matches && e.pointerType !== 'touch') movePill(t); });
|
|
291
|
+
on(t, 'focus', function () { if (desk.matches) movePill(t); });
|
|
84
292
|
});
|
|
85
|
-
on(
|
|
86
|
-
on(
|
|
87
|
-
|
|
88
|
-
|
|
293
|
+
on(nav, 'pointerleave', function () { if (desk.matches && !cur) movePill(null); });
|
|
294
|
+
on(nav, 'focusout', function (e) { if (desk.matches && !cur && !nav.contains(e.relatedTarget)) movePill(null); });
|
|
295
|
+
on(window, 'resize', function () {
|
|
296
|
+
if (!desk.matches) { show(null); movePill(null); return; }
|
|
297
|
+
if (cur) { measure(); snap([bg, caret], function () { surface(cur, 0); }); movePill(cur.t); }
|
|
298
|
+
});
|
|
299
|
+
rec = {
|
|
300
|
+
open: function () { return !!cur; },
|
|
301
|
+
has: function (el) { return !!cur && !!el && (cur.item.contains(el) || cur.p.contains(el)); },
|
|
302
|
+
close: function (focus) {
|
|
303
|
+
if (!cur) return;
|
|
304
|
+
var t = cur.t;
|
|
305
|
+
quiet = true;
|
|
306
|
+
show(null);
|
|
307
|
+
if (focus) t.focus();
|
|
308
|
+
quiet = false;
|
|
309
|
+
if (focus) movePill(t);
|
|
310
|
+
},
|
|
311
|
+
stop: function () {
|
|
312
|
+
clearTimeout(openT); clearTimeout(closeT); clearTimeout(pvT);
|
|
313
|
+
if (cur) show(null);
|
|
314
|
+
made.forEach(function (n) { n.remove(); });
|
|
315
|
+
cards.forEach(function (c) { c.remove(); });
|
|
316
|
+
items.forEach(function (m) {
|
|
317
|
+
m.p.hidden = m.hid;
|
|
318
|
+
m.p.inert = false;
|
|
319
|
+
['left', 'transformOrigin', 'clipPath'].forEach(function (k) { m.p.style[k] = ''; });
|
|
320
|
+
m.p.style.removeProperty('--gw-mega-dx');
|
|
321
|
+
if (m.on) m.on.classList.remove('is-on');
|
|
322
|
+
m.built = false;
|
|
323
|
+
});
|
|
324
|
+
header.style.removeProperty('--gw-mega-top');
|
|
325
|
+
header.classList.remove('has-mega', 'is-mega-open');
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
return rec;
|
|
89
329
|
});
|
|
90
330
|
}
|
|
91
331
|
|
|
@@ -117,6 +357,7 @@ function mobile(ctx) {
|
|
|
117
357
|
if (g === cur) return;
|
|
118
358
|
cur = g;
|
|
119
359
|
groups.forEach(function (x) { x.open = x === g; });
|
|
360
|
+
if (g) hydrate(g.querySelector('.gw-mega__card'));
|
|
120
361
|
menu.classList.toggle('is-drilled', !!g);
|
|
121
362
|
menu.classList.toggle('is-back', !g && r.open());
|
|
122
363
|
menu.scrollTop = 0;
|
|
@@ -428,13 +669,15 @@ function install() {
|
|
|
428
669
|
fine = mq('(hover: hover) and (pointer: fine)'); sys = mq('(prefers-color-scheme: dark)');
|
|
429
670
|
var stored = null;
|
|
430
671
|
wide = mq('(min-width: 1080px)');
|
|
672
|
+
desk = mq('(min-width: 1081px)');
|
|
431
673
|
mid = mq('(min-width: 621px)');
|
|
432
|
-
on(doc, 'click', function (e) { if (
|
|
433
|
-
on(doc, 'keydown', function (e) { if (
|
|
674
|
+
on(doc, 'click', function (e) { records('mega', function (r) { if (r.open() && !r.has(e.target)) r.close(); }); });
|
|
675
|
+
on(doc, 'keydown', function (e) { if (esc(e)) records('mega', function (r) { if (r.open()) r.close(true); }); });
|
|
434
676
|
on(doc, 'keydown', function (e) { if (esc(e)) closeMenus(true); });
|
|
435
677
|
on(doc, 'keydown', function (e) { if (e.key === 'Tab') records('menu', function (r) { if (r.trap && r.open()) r.trap(e); }); });
|
|
436
678
|
on(doc, 'click', function (e) { records('lang', function (r) { if (r.open() && !r.el.contains(e.target)) r.set(false); }); });
|
|
437
|
-
watch(wide, function () {
|
|
679
|
+
watch(wide, function () { records('toc', function (r) { r.fit(); }); });
|
|
680
|
+
watch(desk, function () { if (desk.matches) closeMenus(); });
|
|
438
681
|
watch(mid, function () { records('fold', function (r) { r.fit(); }); });
|
|
439
682
|
still = mq('(prefers-reduced-motion: reduce)');
|
|
440
683
|
var css = window.CSS;
|