@0001-labs/design-system-1 0.3.0-alpha.16 → 0.3.0-alpha.18
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/DS1/0-face/{drawer.ts → attach-overlay.ts} +17 -17
- package/DS1/0-face/{dialogs.ts → feedback-overlays.ts} +8 -8
- package/DS1/0-face/feedback.ts +3 -3
- package/DS1/0-face/header.ts +3 -3
- package/DS1/0-face/{dialog-swipe.ts → overlay-swipe.ts} +47 -44
- package/DS1/1-root/cjk.css +1 -1
- package/DS1/1-root/context.css +151 -0
- package/DS1/1-root/controls.css +35 -27
- package/DS1/1-root/fonts.css +1 -1
- package/DS1/1-root/header.css +4 -4
- package/DS1/1-root/one.css +1 -1
- package/DS1/1-root/one.semantic.css +1 -1
- package/DS1/1-root/overlay.css +283 -0
- package/DS1/1-root/row.css +65 -10
- package/DS1/1-root/table.css +12 -0
- package/DS1/2-core/ds-image.ts +42 -0
- package/DS1/2-core/generated/ds-icon-map.ts +6 -0
- package/DS1/2-core/generated/icons-extra.ts +3 -0
- package/DS1/2-core/generated/icons-immediate.ts +1 -1
- package/DS1/2-core/styles/ds-image.css +14 -0
- package/DS1/3-unit/context-trigger.ts +4 -1
- package/DS1/3-unit/ds-calendar-large.ts +5 -1
- package/DS1/3-unit/ds-context.ts +102 -0
- package/DS1/3-unit/ds-menu.ts +61 -15
- package/DS1/3-unit/ds-row.ts +6 -2
- package/DS1/3-unit/ds-select.ts +212 -0
- package/DS1/3-unit/ds-token-field.ts +2 -2
- package/DS1/3-unit/styles/ds-calendar-large.css +13 -0
- package/DS1/3-unit/styles/ds-context.css +109 -0
- package/DS1/3-unit/styles/ds-row.css +15 -0
- package/DS1/{2-core → 3-unit}/styles/ds-select.css +71 -6
- package/DS1/3-unit/styles/ds-tabs.css +1 -1
- package/DS1/4-page/ds-header.ts +5 -5
- package/DS1/4-page/{ds-dialog.ts → ds-overlay.ts} +77 -51
- package/DS1/4-page/ds-page.ts +5 -5
- package/DS1/4-page/styles/ds-header.css +2 -2
- package/DS1/4-page/styles/ds-overlay.css +88 -0
- package/DS1/4-page/styles/ds-page.css +1 -1
- package/DS1/components.ts +2 -1
- package/DS1/index.ts +5 -2
- package/DS1/react/choice.ts +2 -14
- package/DS1/react/{context-menu.ts → context.ts} +35 -35
- package/DS1/react/image.ts +10 -0
- package/DS1/react/{menu.ts → menu-engine.ts} +10 -3
- package/DS1/react/{dialog.ts → overlay.ts} +40 -37
- package/DS1/react/select.ts +118 -0
- package/DS1/x-icon/dot-happy.svg +3 -0
- package/DS1/x-icon/dot-neutral.svg +3 -0
- package/DS1/x-icon/dot-sad.svg +3 -0
- package/dist/0-face/{drawer.d.ts → attach-overlay.d.ts} +8 -8
- package/dist/0-face/attach-overlay.d.ts.map +1 -0
- package/dist/0-face/{drawer.js → attach-overlay.js} +15 -15
- package/dist/0-face/{dialogs.d.ts → feedback-overlays.d.ts} +1 -1
- package/dist/0-face/feedback-overlays.d.ts.map +1 -0
- package/dist/0-face/{dialogs.js → feedback-overlays.js} +8 -8
- package/dist/0-face/feedback.d.ts +3 -3
- package/dist/0-face/feedback.d.ts.map +1 -1
- package/dist/0-face/feedback.js +2 -2
- package/dist/0-face/header.d.ts +2 -2
- package/dist/0-face/header.d.ts.map +1 -1
- package/dist/0-face/header.js +3 -3
- package/dist/0-face/{dialog-swipe.d.ts → overlay-swipe.d.ts} +16 -14
- package/dist/0-face/overlay-swipe.d.ts.map +1 -0
- package/dist/0-face/{dialog-swipe.js → overlay-swipe.js} +44 -42
- package/dist/2-core/ds-image.d.ts +59 -0
- package/dist/2-core/ds-image.d.ts.map +1 -0
- package/dist/2-core/ds-image.js +42 -0
- package/dist/2-core/generated/ds-icon-map.d.ts +4 -1
- package/dist/2-core/generated/ds-icon-map.d.ts.map +1 -1
- package/dist/2-core/generated/ds-icon-map.js +6 -0
- package/dist/2-core/generated/icons-extra.d.ts.map +1 -1
- package/dist/2-core/generated/icons-extra.js +3 -0
- package/dist/2-core/generated/icons-immediate.d.ts +1 -1
- package/dist/2-core/generated/icons-immediate.d.ts.map +1 -1
- package/dist/2-core/generated/icons-immediate.js +1 -1
- package/dist/3-unit/context-trigger.d.ts +2 -0
- package/dist/3-unit/context-trigger.d.ts.map +1 -1
- package/dist/3-unit/context-trigger.js +3 -1
- package/dist/3-unit/ds-calendar-large.d.ts +5 -0
- package/dist/3-unit/ds-calendar-large.d.ts.map +1 -1
- package/dist/3-unit/ds-calendar-large.js +4 -1
- package/dist/3-unit/ds-context.d.ts +103 -0
- package/dist/3-unit/ds-context.d.ts.map +1 -0
- package/dist/3-unit/ds-context.js +104 -0
- package/dist/3-unit/ds-menu.d.ts +23 -4
- package/dist/3-unit/ds-menu.d.ts.map +1 -1
- package/dist/3-unit/ds-menu.js +64 -11
- package/dist/3-unit/ds-row.d.ts +6 -0
- package/dist/3-unit/ds-row.d.ts.map +1 -1
- package/dist/3-unit/ds-row.js +5 -2
- package/dist/3-unit/ds-select.d.ts +131 -0
- package/dist/3-unit/ds-select.d.ts.map +1 -0
- package/dist/3-unit/ds-select.js +222 -0
- package/dist/3-unit/ds-token-field.d.ts +1 -1
- package/dist/3-unit/ds-token-field.d.ts.map +1 -1
- package/dist/3-unit/ds-token-field.js +2 -2
- package/dist/4-page/ds-header.js +5 -5
- package/dist/4-page/{ds-dialog.d.ts → ds-overlay.d.ts} +43 -21
- package/dist/4-page/ds-overlay.d.ts.map +1 -0
- package/dist/4-page/{ds-dialog.js → ds-overlay.js} +64 -46
- package/dist/4-page/ds-page.d.ts +1 -1
- package/dist/4-page/ds-page.js +5 -5
- package/dist/components.d.ts +2 -1
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +2 -1
- package/dist/design-system-1.bundle.js +7760 -7369
- package/dist/design-system-1.bundle.js.map +1 -1
- package/dist/design-system-1.bundle.min.js +5003 -4785
- package/dist/design-system-1.bundle.min.js.map +1 -1
- package/dist/design-system-1.css +1 -1
- package/dist/feedback.js +7 -7
- package/dist/feedback.js.map +1 -1
- package/dist/{icons-extra-CiDDs7_-.js → icons-extra-BbbpXocy.js} +10 -1
- package/dist/{icons-extra-CiDDs7_-.js.map → icons-extra-BbbpXocy.js.map} +1 -1
- package/dist/{icons-extra-hauwCx07.js → icons-extra-C0E3yQpX.js} +4 -1
- package/dist/{icons-extra-hauwCx07.js.map → icons-extra-C0E3yQpX.js.map} +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/react/choice.d.ts +2 -14
- package/dist/react/choice.d.ts.map +1 -1
- package/dist/react/choice.js +1 -4
- package/dist/react/{context-menu.d.ts → context.d.ts} +22 -22
- package/dist/react/context.d.ts.map +1 -0
- package/dist/react/{context-menu.js → context.js} +29 -29
- package/dist/react/image.d.ts +29 -0
- package/dist/react/image.d.ts.map +1 -0
- package/dist/react/image.js +4 -0
- package/dist/react/{menu.d.ts → menu-engine.d.ts} +11 -2
- package/dist/react/menu-engine.d.ts.map +1 -0
- package/dist/react/{menu.js → menu-engine.js} +6 -2
- package/dist/react/{dialog.d.ts → overlay.d.ts} +25 -23
- package/dist/react/overlay.d.ts.map +1 -0
- package/dist/react/{dialog.js → overlay.js} +24 -23
- package/dist/react/select.d.ts +62 -0
- package/dist/react/select.d.ts.map +1 -0
- package/dist/react/select.js +80 -0
- package/package.json +1 -1
- package/DS1/1-root/dialog-placement.css +0 -193
- package/DS1/1-root/drawer.css +0 -83
- package/DS1/2-core/ds-select.ts +0 -44
- package/DS1/4-page/styles/ds-dialog.css +0 -94
- package/dist/0-face/dialog-swipe.d.ts.map +0 -1
- package/dist/0-face/dialogs.d.ts.map +0 -1
- package/dist/0-face/drawer.d.ts.map +0 -1
- package/dist/2-core/ds-select.d.ts +0 -30
- package/dist/2-core/ds-select.d.ts.map +0 -1
- package/dist/2-core/ds-select.js +0 -45
- package/dist/4-page/ds-dialog.d.ts.map +0 -1
- package/dist/react/context-menu.d.ts.map +0 -1
- package/dist/react/dialog.d.ts.map +0 -1
- package/dist/react/menu.d.ts.map +0 -1
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* from a menu button. Plain TypeScript with no Lit and no framework, so a page
|
|
4
4
|
* of native HTML uses it as the elements do.
|
|
5
5
|
*
|
|
6
|
-
* `
|
|
6
|
+
* `attachOverlay()` ties a toggle button to the parts it folds away (a
|
|
7
7
|
* header's navigation and actions, a page's navigation). Opening moves them
|
|
8
|
-
* into a native `<dialog class="ds-
|
|
9
|
-
* (1-root/
|
|
8
|
+
* into a native `<dialog class="ds-overlay ds-overlay--screen">` laid over the screen
|
|
9
|
+
* (1-root/overlay.css draws it), shown modally so the rest of the page is
|
|
10
10
|
* inert, with a close button where the toggle stood. While it is open:
|
|
11
11
|
*
|
|
12
12
|
* - Tab and Shift+Tab go round the panel's controls and never leave it;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* stays the same (Menu) open or closed.
|
|
20
20
|
*
|
|
21
21
|
* @example
|
|
22
|
-
* const drawer =
|
|
22
|
+
* const drawer = attachOverlay({ toggle, content: [nav, actions], closeLabel: "Close" });
|
|
23
23
|
* drawer.open();
|
|
24
24
|
*/
|
|
25
25
|
|
|
@@ -29,8 +29,8 @@ export interface DrawerOptions {
|
|
|
29
29
|
/** What the drawer holds while it is open: moved into the panel in this order and back on closing. */
|
|
30
30
|
content: readonly Element[] | (() => readonly Element[]);
|
|
31
31
|
/**
|
|
32
|
-
* The panel to open, with its close button marked `.ds-
|
|
33
|
-
* `<dialog class="ds-
|
|
32
|
+
* The panel to open, with its close button marked `.ds-overlay__close`. A
|
|
33
|
+
* `<dialog class="ds-overlay ds-overlay--screen">` is made beside the toggle when none
|
|
34
34
|
* is given, its close button a copy of the toggle.
|
|
35
35
|
*/
|
|
36
36
|
panel?: HTMLDialogElement;
|
|
@@ -116,20 +116,20 @@ export function tabbables(root: Element): HTMLElement[] {
|
|
|
116
116
|
return found;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
export function
|
|
119
|
+
export function attachOverlay(options: DrawerOptions): Drawer {
|
|
120
120
|
const { toggle } = options;
|
|
121
121
|
const document = toggle.ownerDocument;
|
|
122
122
|
const made = !options.panel;
|
|
123
123
|
const panel = options.panel ?? document.createElement("dialog");
|
|
124
124
|
let placeholders: [Element, Comment][] = [];
|
|
125
125
|
let open = false;
|
|
126
|
-
/** The button in the panel that closes it: made here, or the panel's own `.ds-
|
|
127
|
-
let closeButton: HTMLElement | null = options.panel?.querySelector<HTMLElement>(".ds-
|
|
126
|
+
/** The button in the panel that closes it: made here, or the panel's own `.ds-overlay__close`. */
|
|
127
|
+
let closeButton: HTMLElement | null = options.panel?.querySelector<HTMLElement>(".ds-overlay__close") ?? null;
|
|
128
128
|
|
|
129
129
|
if (made) {
|
|
130
|
-
panel.className = "ds-
|
|
130
|
+
panel.className = "ds-overlay ds-overlay--screen";
|
|
131
131
|
const bar = document.createElement("div");
|
|
132
|
-
bar.className = "ds-
|
|
132
|
+
bar.className = "ds-overlay__bar";
|
|
133
133
|
// The close button is a copy of the toggle, so it shows the same icons
|
|
134
134
|
// (the second one, close) in the same place.
|
|
135
135
|
closeButton = toggle.cloneNode(true) as HTMLElement;
|
|
@@ -137,8 +137,8 @@ export function attachDrawer(options: DrawerOptions): Drawer {
|
|
|
137
137
|
closeButton.removeAttribute("aria-controls");
|
|
138
138
|
closeButton.removeAttribute("aria-expanded");
|
|
139
139
|
closeButton.removeAttribute("aria-haspopup");
|
|
140
|
-
closeButton.classList.remove("ds-
|
|
141
|
-
closeButton.classList.add("ds-
|
|
140
|
+
closeButton.classList.remove("ds-overlay__toggle", "ds-header__toggle");
|
|
141
|
+
closeButton.classList.add("ds-overlay__close");
|
|
142
142
|
closeButton.setAttribute("aria-label", options.closeLabel || "Close");
|
|
143
143
|
if (closeButton instanceof HTMLButtonElement) closeButton.type = "button";
|
|
144
144
|
bar.append(closeButton);
|
|
@@ -146,14 +146,14 @@ export function attachDrawer(options: DrawerOptions): Drawer {
|
|
|
146
146
|
if (options.container) options.container.append(panel);
|
|
147
147
|
else toggle.after(panel);
|
|
148
148
|
}
|
|
149
|
-
panel.classList.add("ds-
|
|
149
|
+
panel.classList.add("ds-overlay--screen");
|
|
150
150
|
const label = () => options.label || toggle.getAttribute("aria-label") || toggle.textContent?.trim() || "";
|
|
151
151
|
if (!panel.hasAttribute("aria-label") && !panel.hasAttribute("aria-labelledby") && label()) panel.setAttribute("aria-label", label());
|
|
152
|
-
if (!panel.id) panel.id = `ds-
|
|
152
|
+
if (!panel.id) panel.id = `ds-overlay-${Math.random().toString(36).slice(2, 10)}`;
|
|
153
153
|
if (!toggle.hasAttribute("aria-controls")) toggle.setAttribute("aria-controls", panel.id);
|
|
154
154
|
toggle.setAttribute("aria-expanded", "false");
|
|
155
155
|
toggle.setAttribute("aria-haspopup", "dialog");
|
|
156
|
-
toggle.classList.add("ds-
|
|
156
|
+
toggle.classList.add("ds-overlay__toggle");
|
|
157
157
|
|
|
158
158
|
const parts = () => (typeof options.content === "function" ? options.content() : options.content).filter(Boolean);
|
|
159
159
|
|
|
@@ -162,7 +162,7 @@ export function attachDrawer(options: DrawerOptions): Drawer {
|
|
|
162
162
|
open = true;
|
|
163
163
|
const content = parts();
|
|
164
164
|
placeholders = content.map(element => {
|
|
165
|
-
const mark = document.createComment("ds-
|
|
165
|
+
const mark = document.createComment("ds-overlay");
|
|
166
166
|
element.before(mark);
|
|
167
167
|
return [element, mark];
|
|
168
168
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Dialogs that answer with a promise, in place of the browser's `alert()`,
|
|
3
3
|
* `confirm()` and `prompt()`, for pages without Lit or any other framework.
|
|
4
4
|
*
|
|
5
|
-
* Each is drawn on the native `dialog.ds-
|
|
5
|
+
* Each is drawn on the native `dialog.ds-overlay` frame from `1-root/controls.css`
|
|
6
6
|
* (which the page loads) with its native buttons, `.ds-button--primary`,
|
|
7
7
|
* `--secondary` and `--danger`, and opened with `showModal()`, so the browser
|
|
8
8
|
* keeps the rest of the page inert behind it. Their words default to the
|
|
@@ -100,32 +100,32 @@ function show(spec: Spec): Promise<boolean | string | null> {
|
|
|
100
100
|
const id = `ds-feedback-${++count}`;
|
|
101
101
|
const opener = focused();
|
|
102
102
|
const dialog = element("dialog", {
|
|
103
|
-
class: "ds-
|
|
103
|
+
class: "ds-overlay",
|
|
104
104
|
role: spec.kind === "prompt" ? "dialog" : "alertdialog",
|
|
105
105
|
"aria-labelledby": `${id}-title`,
|
|
106
106
|
"data-ds-feedback": spec.kind,
|
|
107
107
|
});
|
|
108
|
-
const header = element("div", { "data-ds-
|
|
109
|
-
header.appendChild(element("h2", { id: `${id}-title`, "data-ds-
|
|
108
|
+
const header = element("div", { "data-ds-overlay": "header" });
|
|
109
|
+
header.appendChild(element("h2", { id: `${id}-title`, "data-ds-overlay": "title" }, spec.title));
|
|
110
110
|
if (spec.text) {
|
|
111
|
-
header.appendChild(element("p", { id: `${id}-text`, "data-ds-
|
|
111
|
+
header.appendChild(element("p", { id: `${id}-text`, "data-ds-overlay": "description" }, spec.text));
|
|
112
112
|
dialog.setAttribute("aria-describedby", `${id}-text`);
|
|
113
113
|
}
|
|
114
114
|
dialog.appendChild(header);
|
|
115
115
|
|
|
116
116
|
let input: HTMLInputElement | undefined;
|
|
117
117
|
if (spec.field) {
|
|
118
|
-
const field = element("div", { "data-ds-
|
|
118
|
+
const field = element("div", { "data-ds-overlay": "field" });
|
|
119
119
|
input = element("input", { id: `${id}-field`, class: "ds-input", type: "text", autocomplete: "off" });
|
|
120
120
|
input.value = spec.field.value;
|
|
121
121
|
if (spec.field.placeholder) input.placeholder = spec.field.placeholder;
|
|
122
|
-
if (spec.field.label) field.appendChild(element("label", { for: input.id, "data-ds-
|
|
122
|
+
if (spec.field.label) field.appendChild(element("label", { for: input.id, "data-ds-overlay": "label" }, spec.field.label));
|
|
123
123
|
else input.setAttribute("aria-labelledby", `${id}-title`);
|
|
124
124
|
field.appendChild(input);
|
|
125
125
|
dialog.appendChild(field);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
const footer = element("div", { "data-ds-
|
|
128
|
+
const footer = element("div", { "data-ds-overlay": "footer" });
|
|
129
129
|
const cancel = spec.cancel === undefined ? undefined : element("button", { type: "button", class: "ds-button ds-button--secondary", value: "cancel" }, spec.cancel);
|
|
130
130
|
const confirm = element("button", { type: "button", class: spec.danger ? "ds-button ds-button--secondary ds-button--danger" : "ds-button ds-button--primary", value: "confirm" }, spec.confirm);
|
|
131
131
|
if (cancel) footer.appendChild(cancel);
|
package/DS1/0-face/feedback.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* (`./toaster.ts`) on a page without Lit and by `<ds-toast>` where the page
|
|
17
17
|
* has one. `configureToaster()` takes Sonner's `<Toaster />` props.
|
|
18
18
|
* - `alert()`, `confirm()` and `prompt()` are drawn on the native
|
|
19
|
-
* `dialog.ds-
|
|
19
|
+
* `dialog.ds-overlay` frame from `1-root/controls.css`, which the page loads.
|
|
20
20
|
* - Default words (`./feedback-words.ts`) are English, with no language
|
|
21
21
|
* runtime. Where the page loads the language module (`./i18n.ts`, Sync 1's
|
|
22
22
|
* `languages` export), they are the shared words in the page's language
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
export { configureToaster, pageToaster, toast } from "./toaster";
|
|
33
33
|
export type { ToasterOptions } from "./toaster";
|
|
34
34
|
export type { ToastButton, ToastData, ToastId, ToastOptions, ToastPosition, ToastPromiseMessages, ToastType } from "./toast-queue";
|
|
35
|
-
export { alert, confirm, prompt } from "./
|
|
36
|
-
export type { AlertOptions, ConfirmOptions, PromptOptions } from "./
|
|
35
|
+
export { alert, confirm, prompt } from "./feedback-overlays";
|
|
36
|
+
export type { AlertOptions, ConfirmOptions, PromptOptions } from "./feedback-overlays";
|
|
37
37
|
export { copyText } from "./copy-text";
|
|
38
38
|
export type { CopyTextOptions } from "./copy-text";
|
|
39
39
|
export { setFeedbackWords } from "./feedback-words";
|
package/DS1/0-face/header.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* draws them); call `startHeaders()` again for one added later.
|
|
5
5
|
*
|
|
6
6
|
* Each header's menu button (`.ds-header__toggle`) opens its navigation and
|
|
7
|
-
* actions in a drawer (`0-face/
|
|
7
|
+
* actions in a drawer (`0-face/attach-overlay.ts`): a panel over the screen with focus
|
|
8
8
|
* kept inside, closed by Escape, its close button, a link or the screen
|
|
9
9
|
* widening, the page's scroll held behind it. The close button is named by the
|
|
10
10
|
* toggle's `data-close-label`, "Close" without one; the page writes both names
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* </header>
|
|
26
26
|
* <script type="module">import "/ds1/0-face/header.ts";</script>
|
|
27
27
|
*/
|
|
28
|
-
import {
|
|
28
|
+
import { attachOverlay, type Drawer } from "./attach-overlay";
|
|
29
29
|
|
|
30
30
|
const started = new WeakMap<Element, Drawer>();
|
|
31
31
|
|
|
@@ -61,7 +61,7 @@ export function startHeaders(root: ParentNode = document): Drawer[] {
|
|
|
61
61
|
if (started.has(header)) continue;
|
|
62
62
|
const toggle = header.querySelector<HTMLElement>(":scope > .ds-header__toggle");
|
|
63
63
|
if (!toggle) continue;
|
|
64
|
-
const drawer =
|
|
64
|
+
const drawer = attachOverlay({
|
|
65
65
|
toggle,
|
|
66
66
|
content: () => [...header.querySelectorAll(":scope > :is(.ds-header__nav, .ds-header__actions)")],
|
|
67
67
|
closeLabel: toggle.dataset.closeLabel,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Swipe a sheet or a drawer away: a finger that drags a dialog towards the
|
|
3
3
|
* edge it came from moves it, and letting go far or fast enough dismisses it.
|
|
4
|
-
* `ds-
|
|
5
|
-
* without Lit binds its own `dialog.ds-
|
|
6
|
-
* with `
|
|
4
|
+
* `ds-overlay` binds it for `type="sheet"` and `type="drawer"`; a page
|
|
5
|
+
* without Lit binds its own `dialog.ds-overlay--sheet` (or `--drawer`, with
|
|
6
|
+
* `--start` for the start side) with `bindOverlaySwipe(dialog)`. Plain
|
|
7
|
+
* TypeScript with no framework.
|
|
7
8
|
*
|
|
8
9
|
* It follows the finger as touch does on Apple's platforms:
|
|
9
10
|
*
|
|
@@ -13,26 +14,27 @@
|
|
|
13
14
|
* from the dialog's place on screen, not from where it was going).
|
|
14
15
|
* - A drag along the content's own scroll is the content's: a sheet scrolled
|
|
15
16
|
* down scrolls back up first, a horizontal strip in a drawer scrolls, and a
|
|
16
|
-
* slider, a range input or anything marked `data-ds-
|
|
17
|
+
* slider, a range input or anything marked `data-ds-overlay-no-swipe` keeps
|
|
17
18
|
* its touches.
|
|
18
19
|
* - Dragged the other way, it gives a little and pulls back (rubber-banding).
|
|
19
20
|
* - On release it projects where the throw would come to rest (the
|
|
20
21
|
* deceleration of a scroll, rate 0.998) and dismisses when that is past half
|
|
21
22
|
* its size and the finger is not moving back; otherwise it settles home.
|
|
22
|
-
* - The backdrop fades as the dialog goes (`--ds-
|
|
23
|
+
* - The backdrop fades as the dialog goes (`--ds-overlay-progress`).
|
|
23
24
|
*
|
|
24
25
|
* Dismissing asks first, as a close button does: the element sends
|
|
25
26
|
* `ds-close-request` with the reason `"swipe"`, the native frame a cancelable
|
|
26
27
|
* `ds-close-request` on the dialog, and closes it when no listener prevents
|
|
27
28
|
* that. Declined (a form with unsaved changes asking first), it settles home.
|
|
28
|
-
* The motion itself is
|
|
29
|
+
* The motion itself is overlay.css's, in transform alone.
|
|
29
30
|
*/
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
/** The edge an overlay comes from and a finger swipes it back to: a sheet's foot, a drawer's side, or none. */
|
|
33
|
+
export type OverlayEdge = "none" | "bottom" | "end" | "start";
|
|
32
34
|
|
|
33
|
-
export interface
|
|
34
|
-
/**
|
|
35
|
-
|
|
35
|
+
export interface OverlaySwipeOptions {
|
|
36
|
+
/** Its edge, read at each touch; from `data-ds-edge` or its class by default. */
|
|
37
|
+
edge?: OverlayEdge | (() => OverlayEdge);
|
|
36
38
|
/**
|
|
37
39
|
* Called when a swipe would dismiss it. By default a cancelable
|
|
38
40
|
* `ds-close-request` (`detail.reason` `"swipe"`) on the dialog, then
|
|
@@ -51,14 +53,15 @@ const project = (velocity: number) => ((velocity / 1000) * DECELERATION) / (1 -
|
|
|
51
53
|
const rubberband = (overshoot: number, dimension: number, constant = 0.55) =>
|
|
52
54
|
(overshoot * dimension * constant) / (dimension + constant * Math.abs(overshoot));
|
|
53
55
|
|
|
54
|
-
const
|
|
56
|
+
const EDGES = new Set<OverlayEdge>(["none", "bottom", "end", "start"]);
|
|
55
57
|
|
|
56
|
-
/**
|
|
57
|
-
export function
|
|
58
|
-
const marked = dialog.getAttribute("data-ds-
|
|
59
|
-
if (marked &&
|
|
60
|
-
|
|
61
|
-
return "
|
|
58
|
+
/** An overlay's edge, from `data-ds-edge` (the element marks it) or its native classes. */
|
|
59
|
+
export function overlayEdge(dialog: HTMLDialogElement): OverlayEdge {
|
|
60
|
+
const marked = dialog.getAttribute("data-ds-edge") as OverlayEdge | null;
|
|
61
|
+
if (marked && EDGES.has(marked)) return marked;
|
|
62
|
+
if (dialog.classList.contains("ds-overlay--sheet")) return "bottom";
|
|
63
|
+
if (dialog.classList.contains("ds-overlay--drawer")) return dialog.classList.contains("ds-overlay--start") ? "start" : "end";
|
|
64
|
+
return "none";
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
type Gesture = {
|
|
@@ -77,10 +80,10 @@ type Gesture = {
|
|
|
77
80
|
};
|
|
78
81
|
|
|
79
82
|
/** The axis the dialog leaves along, and the sign of the way out on it. */
|
|
80
|
-
function direction(dialog: HTMLDialogElement,
|
|
81
|
-
if (
|
|
83
|
+
function direction(dialog: HTMLDialogElement, edge: OverlayEdge): { axis: "x" | "y"; sign: 1 | -1 } {
|
|
84
|
+
if (edge === "bottom" || edge === "none") return { axis: "y", sign: 1 };
|
|
82
85
|
const rtl = dialog.ownerDocument.defaultView?.getComputedStyle(dialog).direction === "rtl";
|
|
83
|
-
const right = (
|
|
86
|
+
const right = (edge === "end") !== rtl;
|
|
84
87
|
return { axis: "x", sign: right ? 1 : -1 };
|
|
85
88
|
}
|
|
86
89
|
|
|
@@ -123,10 +126,10 @@ function presented(dialog: HTMLDialogElement, axis: "x" | "y", sign: 1 | -1): nu
|
|
|
123
126
|
* Let a finger swipe a sheet or drawer away. Returns the function that
|
|
124
127
|
* unbinds it and clears what it set on the dialog.
|
|
125
128
|
*/
|
|
126
|
-
export function
|
|
127
|
-
const
|
|
128
|
-
const given = typeof options.
|
|
129
|
-
return given ??
|
|
129
|
+
export function bindOverlaySwipe(dialog: HTMLDialogElement, options: OverlaySwipeOptions = {}): () => void {
|
|
130
|
+
const edgeNow = (): OverlayEdge => {
|
|
131
|
+
const given = typeof options.edge === "function" ? options.edge() : options.edge;
|
|
132
|
+
return given ?? overlayEdge(dialog);
|
|
130
133
|
};
|
|
131
134
|
let gesture: Gesture | null = null;
|
|
132
135
|
let settleTimer = 0;
|
|
@@ -134,23 +137,23 @@ export function bindDialogSwipe(dialog: HTMLDialogElement, options: DialogSwipeO
|
|
|
134
137
|
const frame = (callback: () => void) => (view?.requestAnimationFrame ? view.requestAnimationFrame(callback) : view?.setTimeout(callback, 16) ?? 0);
|
|
135
138
|
|
|
136
139
|
const write = (offset: number, size: number, sign: 1 | -1) => {
|
|
137
|
-
dialog.style.setProperty("--ds-
|
|
138
|
-
dialog.style.setProperty("--ds-
|
|
140
|
+
dialog.style.setProperty("--ds-overlay-drag", `${sign * offset}px`);
|
|
141
|
+
dialog.style.setProperty("--ds-overlay-progress", String(size > 0 ? Math.min(1, Math.max(0, offset / size)) : 0));
|
|
139
142
|
};
|
|
140
143
|
const clear = () => {
|
|
141
|
-
dialog.style.removeProperty("--ds-
|
|
142
|
-
dialog.style.removeProperty("--ds-
|
|
143
|
-
dialog.removeAttribute("data-ds-
|
|
144
|
+
dialog.style.removeProperty("--ds-overlay-drag");
|
|
145
|
+
dialog.style.removeProperty("--ds-overlay-progress");
|
|
146
|
+
dialog.removeAttribute("data-ds-overlay-dragging");
|
|
144
147
|
};
|
|
145
148
|
/** Home: the stylesheet's transition takes it back from where it is. */
|
|
146
149
|
const settle = () => {
|
|
147
|
-
dialog.removeAttribute("data-ds-
|
|
148
|
-
dialog.style.setProperty("--ds-
|
|
149
|
-
dialog.style.setProperty("--ds-
|
|
150
|
+
dialog.removeAttribute("data-ds-overlay-dragging");
|
|
151
|
+
dialog.style.setProperty("--ds-overlay-drag", "0px");
|
|
152
|
+
dialog.style.setProperty("--ds-overlay-progress", "0");
|
|
150
153
|
};
|
|
151
154
|
|
|
152
155
|
const dismiss = () => {
|
|
153
|
-
dialog.removeAttribute("data-ds-
|
|
156
|
+
dialog.removeAttribute("data-ds-overlay-dragging");
|
|
154
157
|
if (options.onDismiss) options.onDismiss(dialog);
|
|
155
158
|
else {
|
|
156
159
|
const request = new CustomEvent("ds-close-request", { detail: { reason: "swipe" }, bubbles: true, composed: true, cancelable: true });
|
|
@@ -162,17 +165,17 @@ export function bindDialogSwipe(dialog: HTMLDialogElement, options: DialogSwipeO
|
|
|
162
165
|
};
|
|
163
166
|
|
|
164
167
|
const onStart = (event: TouchEvent) => {
|
|
165
|
-
const
|
|
166
|
-
if (!dialog.open ||
|
|
168
|
+
const edge = edgeNow();
|
|
169
|
+
if (!dialog.open || edge === "none") return;
|
|
167
170
|
if (event.touches.length !== 1) {
|
|
168
171
|
if (gesture?.mode === "drag") settle();
|
|
169
172
|
gesture = null;
|
|
170
173
|
return;
|
|
171
174
|
}
|
|
172
175
|
const path = event.composedPath();
|
|
173
|
-
if (path.some(node => node instanceof Element && node.matches('[data-ds-
|
|
176
|
+
if (path.some(node => node instanceof Element && node.matches('[data-ds-overlay-no-swipe], input[type="range"], [role="slider"]'))) return;
|
|
174
177
|
const touch = event.touches[0]!;
|
|
175
|
-
const { axis, sign } = direction(dialog,
|
|
178
|
+
const { axis, sign } = direction(dialog, edge);
|
|
176
179
|
gesture = { id: touch.identifier, x: touch.clientX, y: touch.clientY, base: Math.max(0, presented(dialog, axis, sign)), start: 0, mode: "pending", offset: 0, samples: [], target: event.target, path };
|
|
177
180
|
};
|
|
178
181
|
|
|
@@ -180,9 +183,9 @@ export function bindDialogSwipe(dialog: HTMLDialogElement, options: DialogSwipeO
|
|
|
180
183
|
if (!gesture || gesture.mode === "ignore") return;
|
|
181
184
|
const touch = [...event.touches].find(each => each.identifier === gesture!.id);
|
|
182
185
|
if (!touch) return;
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
185
|
-
const { axis, sign } = direction(dialog,
|
|
186
|
+
const edge = edgeNow();
|
|
187
|
+
if (edge === "none") { gesture = null; return; }
|
|
188
|
+
const { axis, sign } = direction(dialog, edge);
|
|
186
189
|
const dx = touch.clientX - gesture.x, dy = touch.clientY - gesture.y;
|
|
187
190
|
const along = axis === "y" ? dy * sign : dx * sign;
|
|
188
191
|
const across = axis === "y" ? dx : dy;
|
|
@@ -195,7 +198,7 @@ export function bindDialogSwipe(dialog: HTMLDialogElement, options: DialogSwipeO
|
|
|
195
198
|
gesture.mode = "drag";
|
|
196
199
|
gesture.start = along;
|
|
197
200
|
settleTimer++;
|
|
198
|
-
dialog.setAttribute("data-ds-
|
|
201
|
+
dialog.setAttribute("data-ds-overlay-dragging", "");
|
|
199
202
|
}
|
|
200
203
|
event.preventDefault();
|
|
201
204
|
const rect = dialog.getBoundingClientRect();
|
|
@@ -214,8 +217,8 @@ export function bindDialogSwipe(dialog: HTMLDialogElement, options: DialogSwipeO
|
|
|
214
217
|
const ended = gesture;
|
|
215
218
|
gesture = null;
|
|
216
219
|
if (ended.mode !== "drag") return;
|
|
217
|
-
const
|
|
218
|
-
const { axis } = direction(dialog,
|
|
220
|
+
const edge = edgeNow();
|
|
221
|
+
const { axis } = direction(dialog, edge);
|
|
219
222
|
const rect = dialog.getBoundingClientRect();
|
|
220
223
|
const size = axis === "y" ? rect.height : rect.width;
|
|
221
224
|
// The speed of the last tenth of a second: a finger held still before letting go throws nothing.
|
|
@@ -225,7 +228,7 @@ export function bindDialogSwipe(dialog: HTMLDialogElement, options: DialogSwipeO
|
|
|
225
228
|
const elapsed = first && last ? last.time - first.time : 0;
|
|
226
229
|
const velocity = first && last && elapsed > 0 ? ((last.offset - first.offset) / elapsed) * 1000 : 0;
|
|
227
230
|
const resting = ended.offset + project(velocity);
|
|
228
|
-
if (event.type !== "touchcancel" &&
|
|
231
|
+
if (event.type !== "touchcancel" && edge !== "none" && ended.offset > 0 && resting > size / 2 && velocity > -100) dismiss();
|
|
229
232
|
else settle();
|
|
230
233
|
};
|
|
231
234
|
|
package/DS1/1-root/cjk.css
CHANGED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
@import "./component-metrics.css";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* `.ds-context` is the `ds-context` element's look on a native menu, for a page
|
|
5
|
+
* without Lit or a menu a library draws: Ezo's gap face. The rows carry the
|
|
6
|
+
* colour and the menu only cuts the outer silhouette, so the page shows
|
|
7
|
+
* through a pixel between rows and two between groups. The ink is the rows'
|
|
8
|
+
* fill and the paper their words, turned over with the theme as the tooltip
|
|
9
|
+
* is. Every row is 19px with the pixel under it, a square's pitch, its words
|
|
10
|
+
* on the sixteen and an icon at its end. A Select's menu is the light face,
|
|
11
|
+
* `.ds-menu`; a Context's is this dark one.
|
|
12
|
+
*
|
|
13
|
+
* ds-context the menu (`role="menu"`); its rows are its buttons,
|
|
14
|
+
* links and menu items, or `ds-context__item`
|
|
15
|
+
* ds-context__item a row; `--danger` (or `data-danger`) in red
|
|
16
|
+
* ds-context__label a heading row, muted
|
|
17
|
+
* ds-context__shortcut a shortcut at the row's end, muted
|
|
18
|
+
* ds-context__separator a group gap (or an `hr`, or `role="separator"`):
|
|
19
|
+
* never a line; first, last or doubled it is quiet
|
|
20
|
+
* [data-ds-refused] a refused change: the row turns red
|
|
21
|
+
*
|
|
22
|
+
* The page places and opens it (a popover at the pointer, say) and owns its
|
|
23
|
+
* keys; the faces of hover, focus, disabled and the wait of a pending menu
|
|
24
|
+
* (`aria-busy="true"`) are here. Tokens as the element's: --ds-context-fill,
|
|
25
|
+
* --ds-context-ink, --ds-context-hover, --ds-context-danger,
|
|
26
|
+
* --ds-context-refused, --ds-context-radius.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
.ds-context {
|
|
30
|
+
--ds-context-row: calc(var(--ds-row-height) - 1px);
|
|
31
|
+
--ds-context-fill: light-dark(var(--ds-black, #1e1e1e), var(--ds-white, #ffffff));
|
|
32
|
+
--ds-context-ink: light-dark(var(--ds-white, #ffffff), var(--ds-black, #1e1e1e));
|
|
33
|
+
--ds-context-hover: color-mix(in srgb, var(--ds-context-fill) 78%, var(--ds-context-ink));
|
|
34
|
+
--ds-context-danger: light-dark(var(--ds-tuned-red), var(--ds-red-for-blood));
|
|
35
|
+
--ds-context-refused: var(--ds-tuned-red);
|
|
36
|
+
box-sizing: border-box;
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
gap: 1px;
|
|
40
|
+
width: var(--ds-menu-width, max-content);
|
|
41
|
+
min-width: calc(4 * var(--ds-unit));
|
|
42
|
+
max-height: var(--ds-menu-max-height, calc(16 * var(--ds-unit)));
|
|
43
|
+
overflow-y: auto;
|
|
44
|
+
margin: 0;
|
|
45
|
+
padding: 0;
|
|
46
|
+
border: 0;
|
|
47
|
+
background: transparent;
|
|
48
|
+
box-shadow: none;
|
|
49
|
+
clip-path: inset(0 round var(--ds-context-radius, var(--ds-radius, 4px)));
|
|
50
|
+
color: var(--ds-context-ink);
|
|
51
|
+
font-family: var(--ds-typeface-regular);
|
|
52
|
+
font-feature-settings: var(--ds-font-features);
|
|
53
|
+
font-size: var(--ds-type-size-default);
|
|
54
|
+
font-weight: var(--ds-type-weight-default);
|
|
55
|
+
line-height: var(--ds-type-lineheight-default);
|
|
56
|
+
letter-spacing: var(--ds-type-letterspacing-default);
|
|
57
|
+
list-style: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.ds-context > [role="group"] {
|
|
61
|
+
display: contents;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* A row: whatever the page or library styles its buttons with, one row on the sixteen. */
|
|
65
|
+
.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role="group"]) > :is(button, a, li, [role^="menuitem"]):not(.ds-context__label, .ds-context__separator, [role="separator"])) {
|
|
66
|
+
box-sizing: border-box !important;
|
|
67
|
+
display: flex !important;
|
|
68
|
+
flex: 0 0 auto !important;
|
|
69
|
+
align-items: flex-end !important;
|
|
70
|
+
justify-content: space-between !important;
|
|
71
|
+
gap: calc(4px * var(--ds-sf));
|
|
72
|
+
width: 100%;
|
|
73
|
+
height: var(--ds-context-row) !important;
|
|
74
|
+
min-height: var(--ds-context-row) !important;
|
|
75
|
+
max-height: var(--ds-context-row) !important;
|
|
76
|
+
margin: 0 !important;
|
|
77
|
+
padding: 0 calc(4px * var(--ds-sf)) !important;
|
|
78
|
+
border: 0 !important;
|
|
79
|
+
border-radius: 0 !important;
|
|
80
|
+
background: var(--ds-context-fill) !important;
|
|
81
|
+
color: var(--ds-context-ink) !important;
|
|
82
|
+
font: inherit;
|
|
83
|
+
line-height: var(--ds-type-lineheight-default) !important;
|
|
84
|
+
letter-spacing: inherit;
|
|
85
|
+
text-align: start;
|
|
86
|
+
text-decoration: none;
|
|
87
|
+
white-space: nowrap;
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
outline: none;
|
|
90
|
+
appearance: none;
|
|
91
|
+
-webkit-tap-highlight-color: transparent;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role="group"]) > :is(button, a, li, [role^="menuitem"])):is(:hover, :focus-visible, [data-highlighted]):not(:disabled, [aria-disabled="true"], [data-disabled]) {
|
|
95
|
+
background: var(--ds-context-hover) !important;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role="group"]) > :is(button, a, li, [role^="menuitem"])):focus-visible {
|
|
99
|
+
box-shadow: inset 0 0 0 2px var(--ds-focus-ring);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role="group"]) > :is(button, a, li, [role^="menuitem"])):is(:disabled, [aria-disabled="true"], [data-disabled]) {
|
|
103
|
+
cursor: default;
|
|
104
|
+
opacity: 0.5;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.ds-context[aria-busy="true"] > * {
|
|
108
|
+
cursor: wait !important;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Danger is red, on either fill. */
|
|
112
|
+
.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role="group"]) > :is(button, a, li, [role^="menuitem"])):is(.ds-context__item--danger, [data-danger], [data-variant="destructive"]) {
|
|
113
|
+
color: var(--ds-context-danger) !important;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* A refused change: its row turns red a moment. */
|
|
117
|
+
.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role="group"]) > :is(button, a, li, [role^="menuitem"]))[data-ds-refused] {
|
|
118
|
+
background: var(--ds-context-refused) !important;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Groups are a gap, never a line: the separator takes nothing but the gap after it. */
|
|
122
|
+
.ds-context > :is(hr, .ds-context__separator, [role="separator"]) {
|
|
123
|
+
flex: 0 0 0 !important;
|
|
124
|
+
height: 0 !important;
|
|
125
|
+
margin: 0 !important;
|
|
126
|
+
padding: 0 !important;
|
|
127
|
+
border: 0 !important;
|
|
128
|
+
background: none !important;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* A group left empty leaves no gap. */
|
|
132
|
+
.ds-context > :is(hr, .ds-context__separator, [role="separator"]):is(:first-child, :last-child),
|
|
133
|
+
.ds-context > :is(hr, .ds-context__separator, [role="separator"]) + :is(hr, .ds-context__separator, [role="separator"]) {
|
|
134
|
+
display: none !important;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.ds-context :is(.ds-context__label, [data-ds-menu-label]) {
|
|
138
|
+
box-sizing: border-box;
|
|
139
|
+
height: var(--ds-context-row);
|
|
140
|
+
/* The spare three above the sixteen. */
|
|
141
|
+
padding: calc(var(--ds-context-row) - var(--ds-type-lineheight-default)) calc(4px * var(--ds-sf)) 0;
|
|
142
|
+
background: var(--ds-context-fill);
|
|
143
|
+
color: color-mix(in srgb, var(--ds-context-ink) 60%, var(--ds-context-fill));
|
|
144
|
+
line-height: var(--ds-type-lineheight-default);
|
|
145
|
+
white-space: nowrap;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.ds-context__shortcut {
|
|
149
|
+
margin-left: auto;
|
|
150
|
+
color: color-mix(in srgb, var(--ds-context-ink) 60%, var(--ds-context-fill));
|
|
151
|
+
}
|