@gallerist/plugin-shortcuts 0.0.0-stage → 0.4.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/LICENSE +21 -0
- package/README.md +123 -2
- package/dist/index.cjs +288 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +90 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.js +285 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shafayet Ahmmed
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,124 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @gallerist/plugin-shortcuts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Gallerist plugin — the viewer's **keyboard shortcuts** in a modal dialog, on **`?`**. It lists the
|
|
4
|
+
keys that work in the viewer as it is laid out right now, and on a touch screen the gestures instead.
|
|
5
|
+
|
|
6
|
+
Part of [Gallerist](https://github.com/shafayet9780/gallerist#readme) — a headless, framework-agnostic media viewer for grouped / nested galleries with image-space overlays and virtualized rendering.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i @gallerist/plugin-shortcuts
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createViewer } from '@gallerist/core'
|
|
18
|
+
import { shortcuts } from '@gallerist/plugin-shortcuts'
|
|
19
|
+
import { thumbnails } from '@gallerist/plugin-thumbnails'
|
|
20
|
+
import { view } from '@gallerist/plugin-view'
|
|
21
|
+
import { mount } from '@gallerist/renderer-dom'
|
|
22
|
+
import '@gallerist/theme-default/gallerist.css'
|
|
23
|
+
|
|
24
|
+
const viewer = createViewer({
|
|
25
|
+
groups,
|
|
26
|
+
plugins: [
|
|
27
|
+
thumbnails(),
|
|
28
|
+
view(), // its popover gets a last row, "Keyboard shortcuts ?"
|
|
29
|
+
shortcuts({ shortcuts: [{ keys: ['N'], label: 'Add a note', gesture: 'Long press' }] }),
|
|
30
|
+
],
|
|
31
|
+
})
|
|
32
|
+
mount(viewer, { preset: 'reviewer' })
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Nothing joins the toolbar (it is crowded enough). The list opens:
|
|
36
|
+
|
|
37
|
+
- on **`?`** while the viewer has focus (not while typing in a field in it, not with Ctrl, ⌘ or Alt);
|
|
38
|
+
- from the [View menu](../plugin-view/README.md)'s last row, when both plugins are registered;
|
|
39
|
+
- from the host: `shortcuts()` returns the plugin with `open(keys?)` and `close()`, for a help button
|
|
40
|
+
of your own (`open()` returns `false` when no viewer it is registered on is open). The instance
|
|
41
|
+
(named `shortcuts`, `ShortcutsInstance`) has the same `open()` / `close()`, `isOpen`, and `label`.
|
|
42
|
+
|
|
43
|
+
`open()` with no argument shows the gestures on a coarse pointer (a phone, a tablet) and the keys
|
|
44
|
+
otherwise; `open(true)` the keys, `open(false)` the gestures. `?` always shows the keys (a keyboard
|
|
45
|
+
pressed it).
|
|
46
|
+
|
|
47
|
+
## What it lists
|
|
48
|
+
|
|
49
|
+
The list is built each time it opens, from what the viewer does then, so it never lists a key that
|
|
50
|
+
does nothing:
|
|
51
|
+
|
|
52
|
+
- **Move.** With items grouped (a stacked or index rail, or none): ←/→ move between groups, ↑/↓
|
|
53
|
+
within one, Home/End to the first / last group, 1–9 to an item in the group. A row rail
|
|
54
|
+
(`groupDisplay: 'flat'`, or `linear`) walks every item in order: ←/→ and Home/End only. So does a
|
|
55
|
+
collection where every group holds one item. A side rail (`groupAxis: 'vertical'`) turns the axes
|
|
56
|
+
(↑/↓ between groups). Right to left, ← moves forward, and the list shows → ← for previous / next.
|
|
57
|
+
- **Zoom.** `+`, `−`, `0`: the renderer's keys, or [plugin-zoom](../plugin-zoom/README.md)'s stops
|
|
58
|
+
when it is registered. Only while the collection has an item that can zoom: an image, or with
|
|
59
|
+
plugin-zoom what its `zoomable` option accepts; the gestures leave out pinch and drag the same
|
|
60
|
+
way.
|
|
61
|
+
- **Viewer.** F for fullscreen ([plugin-fullscreen](../plugin-fullscreen/README.md), where the
|
|
62
|
+
browser can fullscreen an element), T to show or hide the thumbnails (with a rail), E for the
|
|
63
|
+
group's items (a stacked rail), G for the grid, P and C for pins (C with `onPlace`), D (or its
|
|
64
|
+
`key`) to download, `?` for this list, Esc to close (an overlay viewer). A plugin's key shows only
|
|
65
|
+
while that plugin holds it: a later plugin that took the key hides it. The renderer reads its own
|
|
66
|
+
keys first (the arrows, Home/End, `+` `=` `-` `0`, `t`, and 1–9 while items are grouped), so a
|
|
67
|
+
plugin given one of them (`grid({ key: 't' })`) never gets it and shouldn't be: the list would
|
|
68
|
+
show it. `?` typed with AltGr (Ctrl + Alt on Windows) counts as `?`.
|
|
69
|
+
- **Your own**, from `shortcuts`: `{ label, keys?, gesture?, section? }`. `keys` are drawn as key
|
|
70
|
+
caps; `section` names a built-in section (its title: `strings.move`, `zoom`, `viewer`) or a new one
|
|
71
|
+
after them; without it the entry joins Viewer. An entry with a `gesture` is listed with the
|
|
72
|
+
gestures; one without `keys` only there.
|
|
73
|
+
|
|
74
|
+
**Gestures** (one list): swipe left or right for previous / next; swipe up for the next item in a
|
|
75
|
+
group (grouped items only); pinch or double-tap to zoom; drag to move while zoomed; swipe down to
|
|
76
|
+
close (an overlay viewer); then yours. A note under them says `?` lists the keys with a keyboard.
|
|
77
|
+
|
|
78
|
+
## Options
|
|
79
|
+
|
|
80
|
+
| Option | Default | |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `shortcuts` | — | your own entries, as above |
|
|
83
|
+
| `strings` | English | every label, for translation; defaults in `DEFAULT_SHORTCUTS_STRINGS`, key names (`home`, `end`, `esc`) included. `toClose` is the close button's text, with `{key}` where the Esc key cap goes (`'{key} to close'`; `'Fermer avec {key}'`) |
|
|
84
|
+
|
|
85
|
+
## Keyboard, pointer and screen readers
|
|
86
|
+
|
|
87
|
+
The dialog is `role="dialog"` with `aria-modal="true"`, named by its title ("Keyboard shortcuts", or
|
|
88
|
+
"Gestures"). It sits in the viewer's root over a scrim that covers the viewer, and the rest of the
|
|
89
|
+
viewer is `inert` while it shows (what was inert already stays so). Focus moves to the dialog, so a
|
|
90
|
+
screen reader reads it, and **Tab** stays inside it: its close button, then the list, which takes
|
|
91
|
+
focus so the arrow keys, Page Up/Down and Home/End scroll it when the viewer is too short to show it
|
|
92
|
+
whole (it has no role or name of its own: the dialog's title is announced once) (the dialog keeps inside the viewer, an inline one included).
|
|
93
|
+
Each section is a heading
|
|
94
|
+
over a description list: what it does, then its keys.
|
|
95
|
+
|
|
96
|
+
- **Escape** closes the list, and only the list: the viewer stays open. **`?`** closes it too.
|
|
97
|
+
- The close button ("Esc to close"; an × named "Close" with the gestures), or a click or tap on the
|
|
98
|
+
scrim, closes it. Focus goes back where it was before it opened (the View tool, when opened from
|
|
99
|
+
its row). A press on the page outside the viewer closes it and leaves focus there.
|
|
100
|
+
- No other key acts while it is open: a `window` listener in the capture phase takes every key
|
|
101
|
+
pressed in the viewer first, so the viewer doesn't move, zoom or close under it, and plugin-zoom's
|
|
102
|
+
keys don't reach the image.
|
|
103
|
+
- It closes when the viewer closes, and when the plugin is disposed; if the renderer goes away with
|
|
104
|
+
it open, its listeners go at the next key or press.
|
|
105
|
+
|
|
106
|
+
## Look
|
|
107
|
+
|
|
108
|
+
A panel in `--gallerist-popover-bg` with a hairline border and `--gallerist-popover-shadow`, centred
|
|
109
|
+
over the viewer on `--gallerist-scrim`, up to 720px wide: a header (keyboard glyph, title, Esc to
|
|
110
|
+
close), then the sections side by side, Move widest, wrapping when there are more. Keys are key caps
|
|
111
|
+
in the skin's surface and monospace face. At 600px wide or less it is a sheet from the bottom edge,
|
|
112
|
+
one column. It fades in over `--gallerist-dur-exit`, and not at all under reduced motion; right to
|
|
113
|
+
left it mirrors. Every skin and scheme gets the styles (`@gallerist/theme-default`, `base.css`).
|
|
114
|
+
|
|
115
|
+
## Not in the CDN bundle
|
|
116
|
+
|
|
117
|
+
`@gallerist/cdn` (the one-`<script>` build) doesn't include this plugin: it would take that bundle
|
|
118
|
+
past its size budget. Install it from npm alongside the packages above.
|
|
119
|
+
|
|
120
|
+
Full docs: the [monorepo README](https://github.com/shafayet9780/gallerist#readme) and [`docs/`](https://github.com/shafayet9780/gallerist/tree/main/docs).
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT © Shafayet Ahmmed
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var internal = require('@gallerist/core/internal');
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
var DEFAULT_SHORTCUTS_STRINGS = {
|
|
7
|
+
title: "Keyboard shortcuts",
|
|
8
|
+
gestures: "Gestures",
|
|
9
|
+
toClose: "{key} to close",
|
|
10
|
+
close: "Close",
|
|
11
|
+
move: "Move",
|
|
12
|
+
zoom: "Zoom",
|
|
13
|
+
viewer: "Viewer",
|
|
14
|
+
prevNext: "Previous / next",
|
|
15
|
+
prevNextGroup: "Previous / next group",
|
|
16
|
+
prevNextItem: "Previous / next in the group",
|
|
17
|
+
nextItem: "Next in the group",
|
|
18
|
+
firstLast: "First / last",
|
|
19
|
+
firstLastGroup: "First / last group",
|
|
20
|
+
jump: "Item in the group",
|
|
21
|
+
zoomIn: "Zoom in",
|
|
22
|
+
zoomOut: "Zoom out",
|
|
23
|
+
fit: "Fit to screen",
|
|
24
|
+
pan: "Move while zoomed",
|
|
25
|
+
fullscreen: "Fullscreen",
|
|
26
|
+
rail: "Thumbnails",
|
|
27
|
+
items: "Item list",
|
|
28
|
+
grid: "Grid",
|
|
29
|
+
pins: "Pins",
|
|
30
|
+
addPin: "Add a comment",
|
|
31
|
+
download: "Download",
|
|
32
|
+
help: "This list",
|
|
33
|
+
exit: "Close",
|
|
34
|
+
swipe: "Swipe left or right",
|
|
35
|
+
swipeUp: "Swipe up",
|
|
36
|
+
swipeDown: "Swipe down",
|
|
37
|
+
pinch: "Pinch, or double-tap",
|
|
38
|
+
drag: "Drag",
|
|
39
|
+
keysNote: "With a keyboard, ? lists its shortcuts.",
|
|
40
|
+
home: "Home",
|
|
41
|
+
end: "End",
|
|
42
|
+
esc: "Esc"
|
|
43
|
+
};
|
|
44
|
+
var KEYBOARD = '<svg class="gallerist-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"/></svg>';
|
|
45
|
+
var X = '<svg class="gallerist-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M18 6 6 18M6 6l12 12"/></svg>';
|
|
46
|
+
var TABBABLE = 'button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])';
|
|
47
|
+
var lists = 0;
|
|
48
|
+
var el = (tag, className = "", text = "") => {
|
|
49
|
+
const node = document.createElement(tag);
|
|
50
|
+
if (className) node.className = className;
|
|
51
|
+
if (text) node.textContent = text;
|
|
52
|
+
return node;
|
|
53
|
+
};
|
|
54
|
+
var cap = (k) => k.length === 1 ? k.toUpperCase() : k;
|
|
55
|
+
var chord = (e) => (e.ctrlKey || e.metaKey || e.altKey) && !e.getModifierState?.("AltGraph");
|
|
56
|
+
function shortcuts(opts = {}) {
|
|
57
|
+
const t = { ...DEFAULT_SHORTCUTS_STRINGS, ...opts.strings };
|
|
58
|
+
const live = [];
|
|
59
|
+
const plugin = (viewer) => {
|
|
60
|
+
let anchor = null;
|
|
61
|
+
let scrim = null;
|
|
62
|
+
let back = null;
|
|
63
|
+
let inerted = [];
|
|
64
|
+
let unsub = null;
|
|
65
|
+
const id = `gallerist-shortcuts-${++lists}`;
|
|
66
|
+
const root = () => anchor?.closest(".gallerist") ?? null;
|
|
67
|
+
const coarse = () => !!window.matchMedia?.("(pointer: coarse)").matches;
|
|
68
|
+
const rows = (keys) => {
|
|
69
|
+
const r = root();
|
|
70
|
+
const plugins = internal.getInternals(viewer).getPlugins();
|
|
71
|
+
const map = {};
|
|
72
|
+
for (const p of plugins) Object.assign(map, p.keymap);
|
|
73
|
+
const named = (name) => plugins.find((p) => p.name === name);
|
|
74
|
+
const held = (name, k) => {
|
|
75
|
+
const own = named(name)?.keymap ?? {};
|
|
76
|
+
const key = k ?? Object.keys(own)[0];
|
|
77
|
+
return key && own[key] && map[key] === own[key] ? key : void 0;
|
|
78
|
+
};
|
|
79
|
+
const linear = r.hasAttribute("data-gallerist-linear");
|
|
80
|
+
const vertical = r.getAttribute("data-gallerist-axis") === "vertical";
|
|
81
|
+
const across = r.getAttribute("data-gallerist-dir") === "rtl" ? ["\u2192", "\u2190"] : ["\u2190", "\u2192"];
|
|
82
|
+
const along = ["\u2191", "\u2193"];
|
|
83
|
+
const grouped = !linear && viewer.getCollection().groups.some((g) => g.items.length > 1);
|
|
84
|
+
const overlay = r.getAttribute("data-gallerist-mode") === "overlay";
|
|
85
|
+
const rail = named("thumbnails")?.display;
|
|
86
|
+
const can = named("zoom")?.zoomable ?? ((i) => i.type === "image");
|
|
87
|
+
const zoom = viewer.getCollection().groups.some((g) => g.items.some((i) => can(i)));
|
|
88
|
+
let sections;
|
|
89
|
+
if (keys) {
|
|
90
|
+
const move = grouped ? [
|
|
91
|
+
[t.prevNextGroup, vertical ? along : across],
|
|
92
|
+
[t.prevNextItem, vertical ? across : along],
|
|
93
|
+
[t.firstLastGroup, [t.home, t.end]],
|
|
94
|
+
[t.jump, ["1\u20139"]]
|
|
95
|
+
] : [
|
|
96
|
+
[t.prevNext, vertical ? along : across],
|
|
97
|
+
[t.firstLast, [t.home, t.end]]
|
|
98
|
+
];
|
|
99
|
+
const fs = document.webkitFullscreenEnabled;
|
|
100
|
+
const tools = [
|
|
101
|
+
[t.fullscreen, document.fullscreenEnabled ?? fs ? held("fullscreen", "f") : void 0],
|
|
102
|
+
[t.rail, rail ? "t" : void 0],
|
|
103
|
+
[t.items, held("thumbnails", "e")],
|
|
104
|
+
[t.grid, held("grid")],
|
|
105
|
+
[t.pins, held("pins", "p")],
|
|
106
|
+
[t.addPin, held("pins", "c")],
|
|
107
|
+
[t.download, held("download")],
|
|
108
|
+
[t.help, "?"],
|
|
109
|
+
[t.exit, overlay ? t.esc : void 0]
|
|
110
|
+
];
|
|
111
|
+
sections = [
|
|
112
|
+
[t.move, move],
|
|
113
|
+
[
|
|
114
|
+
t.zoom,
|
|
115
|
+
zoom ? [
|
|
116
|
+
[t.zoomIn, ["+"]],
|
|
117
|
+
[t.zoomOut, ["\u2212"]],
|
|
118
|
+
[t.fit, ["0"]]
|
|
119
|
+
] : []
|
|
120
|
+
],
|
|
121
|
+
[t.viewer, tools.flatMap(([label, k]) => k ? [[label, [cap(k)]]] : [])]
|
|
122
|
+
];
|
|
123
|
+
} else {
|
|
124
|
+
sections = [
|
|
125
|
+
[
|
|
126
|
+
"",
|
|
127
|
+
[
|
|
128
|
+
[grouped ? t.prevNextGroup : t.prevNext, t.swipe],
|
|
129
|
+
...grouped ? [[t.nextItem, t.swipeUp]] : [],
|
|
130
|
+
...zoom ? [[t.zoom, t.pinch], [t.pan, t.drag]] : [],
|
|
131
|
+
...overlay ? [[t.exit, t.swipeDown]] : []
|
|
132
|
+
]
|
|
133
|
+
]
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
for (const x of opts.shortcuts ?? []) {
|
|
137
|
+
const what = keys ? x.keys : x.gesture;
|
|
138
|
+
if (!what?.length) continue;
|
|
139
|
+
const title = keys ? x.section ?? t.viewer : "";
|
|
140
|
+
let section = sections.find(([s]) => s === title);
|
|
141
|
+
if (!section) {
|
|
142
|
+
section = [title, []];
|
|
143
|
+
sections.push(section);
|
|
144
|
+
}
|
|
145
|
+
section[1].push([x.label, what]);
|
|
146
|
+
}
|
|
147
|
+
return sections.filter(([, list]) => list.length);
|
|
148
|
+
};
|
|
149
|
+
const close = (refocus = false) => {
|
|
150
|
+
if (!scrim) return;
|
|
151
|
+
scrim.remove();
|
|
152
|
+
scrim = null;
|
|
153
|
+
for (const node of inerted) node.removeAttribute("inert");
|
|
154
|
+
inerted = [];
|
|
155
|
+
window.removeEventListener("keydown", onKey, true);
|
|
156
|
+
document.removeEventListener("pointerdown", onPress, true);
|
|
157
|
+
if (refocus) (back?.isConnected ? back : root())?.focus({ preventScroll: true });
|
|
158
|
+
back = null;
|
|
159
|
+
};
|
|
160
|
+
const onKey = (e) => {
|
|
161
|
+
const r = root();
|
|
162
|
+
if (!scrim?.isConnected) return close();
|
|
163
|
+
if (!r || !e.composedPath().includes(r)) return;
|
|
164
|
+
e.stopPropagation();
|
|
165
|
+
if (e.key === "Escape" || e.key === "?" && !chord(e)) {
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
close(true);
|
|
168
|
+
} else if (e.key === "Tab") {
|
|
169
|
+
const box = scrim.firstElementChild;
|
|
170
|
+
const list = [...box.querySelectorAll(TABBABLE)];
|
|
171
|
+
const active = r.getRootNode().activeElement;
|
|
172
|
+
const first = list[0];
|
|
173
|
+
const last = list[list.length - 1];
|
|
174
|
+
const to = e.shiftKey ? active === first || active === box ? last : null : active === last ? first : null;
|
|
175
|
+
if (!first || to) {
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
to?.focus();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const onPress = (e) => {
|
|
182
|
+
if (!e.composedPath().includes(root())) close();
|
|
183
|
+
};
|
|
184
|
+
const open = (keys = !coarse()) => {
|
|
185
|
+
const r = root();
|
|
186
|
+
if (!r || !anchor?.isConnected || viewer.getState().phase !== "open") return false;
|
|
187
|
+
close();
|
|
188
|
+
back = r.getRootNode().activeElement;
|
|
189
|
+
const s = el("div", "gallerist-shortcuts-scrim");
|
|
190
|
+
const box = el("div", "gallerist-shortcuts");
|
|
191
|
+
box.setAttribute("role", "dialog");
|
|
192
|
+
box.setAttribute("aria-modal", "true");
|
|
193
|
+
box.setAttribute("aria-labelledby", id);
|
|
194
|
+
box.tabIndex = -1;
|
|
195
|
+
box.setAttribute("data-gallerist-list", keys ? "keys" : "gestures");
|
|
196
|
+
const head = el("div", "gallerist-shortcuts-head");
|
|
197
|
+
if (keys) head.innerHTML = KEYBOARD;
|
|
198
|
+
const title = head.appendChild(el("h2", "", keys ? t.title : t.gestures));
|
|
199
|
+
title.id = id;
|
|
200
|
+
const x = head.appendChild(el("button", "gallerist-shortcuts-close"));
|
|
201
|
+
x.type = "button";
|
|
202
|
+
if (keys) {
|
|
203
|
+
const [before, after] = t.toClose.split("{key}");
|
|
204
|
+
x.append(before, el("kbd", "", t.esc), after ?? "");
|
|
205
|
+
} else {
|
|
206
|
+
x.innerHTML = X;
|
|
207
|
+
x.setAttribute("aria-label", t.close);
|
|
208
|
+
}
|
|
209
|
+
x.addEventListener("click", () => close(true));
|
|
210
|
+
const body = el("div", "gallerist-shortcuts-body");
|
|
211
|
+
body.tabIndex = 0;
|
|
212
|
+
for (const [name, list] of rows(keys)) {
|
|
213
|
+
const section = body.appendChild(el("section"));
|
|
214
|
+
if (name) section.appendChild(el("h3", "", name));
|
|
215
|
+
const dl = section.appendChild(el("dl"));
|
|
216
|
+
for (const [label, what] of list) {
|
|
217
|
+
const row = dl.appendChild(el("div", "gallerist-shortcuts-row"));
|
|
218
|
+
row.appendChild(el("dt", "", label));
|
|
219
|
+
const dd = row.appendChild(el("dd"));
|
|
220
|
+
if (typeof what === "string") dd.textContent = what;
|
|
221
|
+
else for (const k of what) dd.appendChild(el("kbd", "", k));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
box.append(head, body);
|
|
225
|
+
if (!keys) box.appendChild(el("p", "gallerist-shortcuts-note", t.keysNote));
|
|
226
|
+
s.appendChild(box);
|
|
227
|
+
s.addEventListener("mousedown", (e) => e.target === s && e.preventDefault());
|
|
228
|
+
s.addEventListener("click", (e) => e.target === s && close(true));
|
|
229
|
+
for (const node of r.children)
|
|
230
|
+
if (!node.hasAttribute("inert")) {
|
|
231
|
+
node.setAttribute("inert", "");
|
|
232
|
+
inerted.push(node);
|
|
233
|
+
}
|
|
234
|
+
r.appendChild(s);
|
|
235
|
+
scrim = s;
|
|
236
|
+
window.addEventListener("keydown", onKey, true);
|
|
237
|
+
document.addEventListener("pointerdown", onPress, true);
|
|
238
|
+
box.focus({ preventScroll: true });
|
|
239
|
+
return true;
|
|
240
|
+
};
|
|
241
|
+
const instance = {
|
|
242
|
+
name: "shortcuts",
|
|
243
|
+
get label() {
|
|
244
|
+
return coarse() ? t.gestures : t.title;
|
|
245
|
+
},
|
|
246
|
+
get isOpen() {
|
|
247
|
+
return !!scrim;
|
|
248
|
+
},
|
|
249
|
+
open,
|
|
250
|
+
close: () => close(true),
|
|
251
|
+
init() {
|
|
252
|
+
live.push(instance);
|
|
253
|
+
unsub = viewer.subscribe((st) => {
|
|
254
|
+
if (st.phase === "closing" || st.phase === "closed") close();
|
|
255
|
+
});
|
|
256
|
+
},
|
|
257
|
+
destroy() {
|
|
258
|
+
close();
|
|
259
|
+
unsub?.();
|
|
260
|
+
unsub = anchor = null;
|
|
261
|
+
const at = live.indexOf(instance);
|
|
262
|
+
if (at >= 0) live.splice(at, 1);
|
|
263
|
+
},
|
|
264
|
+
keymap: { "?": () => open(true) },
|
|
265
|
+
// a hidden node, to find the viewer's root by (marked off: a bar shows not for it)
|
|
266
|
+
regions: {
|
|
267
|
+
toolbar: () => {
|
|
268
|
+
anchor = el("span");
|
|
269
|
+
anchor.hidden = true;
|
|
270
|
+
anchor.setAttribute("data-gallerist-off", "");
|
|
271
|
+
return anchor;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
return instance;
|
|
276
|
+
};
|
|
277
|
+
return Object.assign(plugin, {
|
|
278
|
+
open: (keys) => live.reduceRight((done, i) => done || i.open(keys), false),
|
|
279
|
+
close: () => {
|
|
280
|
+
for (const i of live) i.close();
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
exports.DEFAULT_SHORTCUTS_STRINGS = DEFAULT_SHORTCUTS_STRINGS;
|
|
286
|
+
exports.shortcuts = shortcuts;
|
|
287
|
+
//# sourceMappingURL=index.cjs.map
|
|
288
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["getInternals"],"mappings":"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","file":"index.cjs","sourcesContent":["// @gallerist/plugin-shortcuts — the viewer's keyboard shortcuts in a modal dialog, on `?`.\n//\n// The list is built each time it opens, from what the viewer does then: the renderer's own keys as\n// the layout has them (a row rail walks every item with ←/→ and has no ↑/↓; a side rail turns the\n// axes; right to left mirrors ←/→), the official plugins' keys while they are registered and hold\n// them (the aggregated keymap, so a key another plugin took isn't listed), and the host's entries.\n// Opened by a touch (the View menu's row, `open()`, on a coarse pointer) it lists the renderer's\n// gestures instead. `?` (with the viewer focused, not in a text field) opens the keys.\n//\n// The dialog sits in the viewer's root over a scrim that covers the viewer: `aria-modal`, the rest\n// of the root inert while it shows, Tab kept inside, focus back where it was on close. Keys pressed\n// in it never reach the viewer (a window listener in the capture phase takes them first): Escape and\n// `?` close it, and nothing else acts. The root is found from a hidden node in the toolbar region.\n\nimport type { Item, Plugin, PluginInstance, ViewerApi } from '@gallerist/core'\nimport { getInternals } from '@gallerist/core/internal'\n\n/** A host's own shortcut, listed with the built-in ones. */\nexport interface ShortcutEntry {\n /** what it does */\n label: string\n /** the keys, each drawn as a key cap (`['N']`, `['Shift', 'N']`); without them it's listed only\n * with the gestures */\n keys?: readonly string[]\n /** the gesture that does it on a touch screen (`'Long press'`); without one it's listed only with\n * the keys */\n gesture?: string\n /** the section it joins: a built-in one by its title (`strings.move`, `zoom`, `viewer`) or a new\n * one after them. Default: the Viewer section. */\n section?: string\n}\n\nexport interface ShortcutsStrings {\n /** the dialog's title with the keys, and the View menu's row */\n title: string\n /** the dialog's title with the gestures (and the View menu's row on a touch screen) */\n gestures: string\n /** the close button's text, `{key}` where the Esc key cap goes (\"{key} to close\") */\n toClose: string\n close: string\n /** section titles */\n move: string\n zoom: string\n viewer: string\n /** moving */\n prevNext: string\n prevNextGroup: string\n prevNextItem: string\n nextItem: string\n firstLast: string\n firstLastGroup: string\n jump: string\n /** zooming */\n zoomIn: string\n zoomOut: string\n fit: string\n pan: string\n /** the viewer, and the plugins' keys */\n fullscreen: string\n rail: string\n items: string\n grid: string\n pins: string\n addPin: string\n download: string\n help: string\n exit: string\n /** gestures */\n swipe: string\n swipeUp: string\n swipeDown: string\n pinch: string\n drag: string\n /** under the gestures */\n keysNote: string\n /** key names on their caps */\n home: string\n end: string\n esc: string\n}\n\nexport const DEFAULT_SHORTCUTS_STRINGS: ShortcutsStrings = {\n title: 'Keyboard shortcuts',\n gestures: 'Gestures',\n toClose: '{key} to close',\n close: 'Close',\n move: 'Move',\n zoom: 'Zoom',\n viewer: 'Viewer',\n prevNext: 'Previous / next',\n prevNextGroup: 'Previous / next group',\n prevNextItem: 'Previous / next in the group',\n nextItem: 'Next in the group',\n firstLast: 'First / last',\n firstLastGroup: 'First / last group',\n jump: 'Item in the group',\n zoomIn: 'Zoom in',\n zoomOut: 'Zoom out',\n fit: 'Fit to screen',\n pan: 'Move while zoomed',\n fullscreen: 'Fullscreen',\n rail: 'Thumbnails',\n items: 'Item list',\n grid: 'Grid',\n pins: 'Pins',\n addPin: 'Add a comment',\n download: 'Download',\n help: 'This list',\n exit: 'Close',\n swipe: 'Swipe left or right',\n swipeUp: 'Swipe up',\n swipeDown: 'Swipe down',\n pinch: 'Pinch, or double-tap',\n drag: 'Drag',\n keysNote: 'With a keyboard, ? lists its shortcuts.',\n home: 'Home',\n end: 'End',\n esc: 'Esc',\n}\n\nexport interface ShortcutsOptions {\n /** the host's own shortcuts, listed after the built-in ones */\n shortcuts?: readonly ShortcutEntry[]\n /** UI text overrides, for translation; merged over English. */\n strings?: Partial<ShortcutsStrings>\n}\n\n/** The plugin's instance on a viewer (named `shortcuts`); the View menu finds it by that name. */\nexport interface ShortcutsInstance extends PluginInstance {\n /** what `open()` shows now: the title of the keys, or of the gestures on a coarse pointer */\n readonly label: string\n readonly isOpen: boolean\n /** Opens the list on the open viewer: the keys (`true`), the gestures (`false`), or by the\n * pointer (default). False when there is no open viewer to show it in. */\n open(keys?: boolean): boolean\n close(): void\n}\n\n/** `shortcuts()`: a plugin, with `open()` / `close()` for the host (a help button of its own). */\nexport interface ShortcutsPlugin extends Plugin {\n /** Opens the list on the viewer this plugin was last registered on that is open. */\n open(keys?: boolean): boolean\n close(): void\n}\n\ntype Row = [label: string, keys: readonly string[] | string]\n\nconst KEYBOARD =\n '<svg class=\"gallerist-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"/><path d=\"M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10\"/></svg>'\nconst X =\n '<svg class=\"gallerist-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>'\nconst TABBABLE = 'button:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])'\n\nlet lists = 0 // ids, for aria-labelledby\n\nconst el = <K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className = '',\n text = '',\n): HTMLElementTagNameMap[K] => {\n const node = document.createElement(tag)\n if (className) node.className = className\n if (text) node.textContent = text\n return node\n}\nconst cap = (k: string) => (k.length === 1 ? k.toUpperCase() : k)\n// `?` typed with AltGr (Ctrl + Alt on Windows) is still `?`; another chord is the browser's\nconst chord = (e: KeyboardEvent) =>\n (e.ctrlKey || e.metaKey || e.altKey) && !e.getModifierState?.('AltGraph')\n\nexport function shortcuts(opts: ShortcutsOptions = {}): ShortcutsPlugin {\n const t: ShortcutsStrings = { ...DEFAULT_SHORTCUTS_STRINGS, ...opts.strings }\n const live: ShortcutsInstance[] = [] // registered instances, the latest last\n\n const plugin = (viewer: ViewerApi): ShortcutsInstance => {\n let anchor: HTMLElement | null = null\n let scrim: HTMLElement | null = null\n let back: HTMLElement | null = null // focus before it opened\n let inerted: Element[] = []\n let unsub: (() => void) | null = null\n const id = `gallerist-shortcuts-${++lists}`\n const root = () => anchor?.closest<HTMLElement>('.gallerist') ?? null\n const coarse = () => !!window.matchMedia?.('(pointer: coarse)').matches\n\n // What the viewer does now, read from its root and its plugins.\n const rows = (keys: boolean): Array<[string, Row[]]> => {\n const r = root() as HTMLElement\n const plugins = getInternals(viewer).getPlugins()\n const map: Record<string, unknown> = {}\n for (const p of plugins) Object.assign(map, p.keymap)\n const named = (name: string) => plugins.find((p) => p.name === name)\n // the key `k` of the plugin `name`, while it holds it (none: its first key)\n const held = (name: string, k?: string) => {\n const own = named(name)?.keymap ?? {}\n const key = k ?? Object.keys(own)[0]\n return key && own[key] && map[key] === own[key] ? key : undefined\n }\n const linear = r.hasAttribute('data-gallerist-linear')\n const vertical = r.getAttribute('data-gallerist-axis') === 'vertical'\n const across = r.getAttribute('data-gallerist-dir') === 'rtl' ? ['→', '←'] : ['←', '→']\n const along = ['↑', '↓']\n const grouped = !linear && viewer.getCollection().groups.some((g) => g.items.length > 1)\n const overlay = r.getAttribute('data-gallerist-mode') === 'overlay'\n const rail = (named('thumbnails') as { display?: string } | undefined)?.display\n\n // zooming, while an item can zoom: plugin-zoom's `zoomable`, else an image\n const can =\n (named('zoom') as { zoomable?: (item: Item) => boolean } | undefined)?.zoomable ??\n ((i: Item) => i.type === 'image')\n const zoom = viewer.getCollection().groups.some((g) => g.items.some((i) => can(i)))\n let sections: Array<[string, Row[]]>\n if (keys) {\n const move: Row[] = grouped\n ? [\n [t.prevNextGroup, vertical ? along : across],\n [t.prevNextItem, vertical ? across : along],\n [t.firstLastGroup, [t.home, t.end]],\n [t.jump, ['1–9']],\n ]\n : [\n [t.prevNext, vertical ? along : across],\n [t.firstLast, [t.home, t.end]],\n ]\n const fs = (document as { webkitFullscreenEnabled?: boolean }).webkitFullscreenEnabled\n const tools: Array<[string, string | undefined]> = [\n [t.fullscreen, (document.fullscreenEnabled ?? fs) ? held('fullscreen', 'f') : undefined],\n [t.rail, rail ? 't' : undefined],\n [t.items, held('thumbnails', 'e')],\n [t.grid, held('grid')],\n [t.pins, held('pins', 'p')],\n [t.addPin, held('pins', 'c')],\n [t.download, held('download')],\n [t.help, '?'],\n [t.exit, overlay ? t.esc : undefined],\n ]\n sections = [\n [t.move, move],\n [\n t.zoom,\n zoom\n ? [\n [t.zoomIn, ['+']],\n [t.zoomOut, ['−']],\n [t.fit, ['0']],\n ]\n : [],\n ],\n [t.viewer, tools.flatMap(([label, k]) => (k ? [[label, [cap(k)]] as Row] : []))],\n ]\n } else {\n sections = [\n [\n '',\n [\n [grouped ? t.prevNextGroup : t.prevNext, t.swipe],\n ...(grouped ? [[t.nextItem, t.swipeUp] as Row] : []),\n ...(zoom ? [[t.zoom, t.pinch] as Row, [t.pan, t.drag] as Row] : []),\n ...(overlay ? [[t.exit, t.swipeDown] as Row] : []),\n ],\n ],\n ]\n }\n for (const x of opts.shortcuts ?? []) {\n const what = keys ? x.keys : x.gesture\n if (!what?.length) continue\n const title = keys ? (x.section ?? t.viewer) : ''\n let section = sections.find(([s]) => s === title)\n if (!section) {\n section = [title, []]\n sections.push(section)\n }\n section[1].push([x.label, what])\n }\n return sections.filter(([, list]) => list.length)\n }\n\n const close = (refocus = false) => {\n if (!scrim) return\n scrim.remove()\n scrim = null\n for (const node of inerted) node.removeAttribute('inert')\n inerted = []\n window.removeEventListener('keydown', onKey, true)\n document.removeEventListener('pointerdown', onPress, true)\n if (refocus) (back?.isConnected ? back : root())?.focus({ preventScroll: true })\n back = null\n }\n\n // Every key pressed in the viewer while the list shows stops here: the viewer's keys, the zoom\n // tool's and the page's never see it. Escape and ? close; Tab stays in the dialog.\n const onKey = (e: KeyboardEvent) => {\n const r = root()\n if (!scrim?.isConnected) return close() // the viewer went away with it open\n if (!r || !e.composedPath().includes(r)) return\n e.stopPropagation()\n if (e.key === 'Escape' || (e.key === '?' && !chord(e))) {\n e.preventDefault()\n close(true)\n } else if (e.key === 'Tab') {\n const box = scrim.firstElementChild as HTMLElement\n const list = [...box.querySelectorAll<HTMLElement>(TABBABLE)]\n const active = (r.getRootNode() as Document | ShadowRoot).activeElement\n const first = list[0]\n const last = list[list.length - 1]\n const to = e.shiftKey\n ? active === first || active === box\n ? last\n : null\n : active === last\n ? first\n : null\n if (!first || to) {\n e.preventDefault()\n to?.focus()\n }\n }\n }\n // A press on the page outside the viewer closes it, leaving focus where the press puts it (a\n // press on the scrim closes it too, with focus back where it was: see open()).\n // (A viewer gone with it open is outside too: its listeners go with the next press or key.)\n const onPress = (e: Event) => {\n if (!e.composedPath().includes(root() as EventTarget)) close()\n }\n\n const open = (keys = !coarse()): boolean => {\n const r = root()\n if (!r || !anchor?.isConnected || viewer.getState().phase !== 'open') return false\n close()\n back = (r.getRootNode() as Document | ShadowRoot).activeElement as HTMLElement | null\n const s = el('div', 'gallerist-shortcuts-scrim')\n const box = el('div', 'gallerist-shortcuts')\n box.setAttribute('role', 'dialog')\n box.setAttribute('aria-modal', 'true')\n box.setAttribute('aria-labelledby', id)\n box.tabIndex = -1\n box.setAttribute('data-gallerist-list', keys ? 'keys' : 'gestures')\n const head = el('div', 'gallerist-shortcuts-head')\n if (keys) head.innerHTML = KEYBOARD\n const title = head.appendChild(el('h2', '', keys ? t.title : t.gestures))\n title.id = id\n const x = head.appendChild(el('button', 'gallerist-shortcuts-close'))\n x.type = 'button'\n if (keys) {\n const [before, after] = t.toClose.split('{key}')\n x.append(before as string, el('kbd', '', t.esc), after ?? '')\n } else {\n x.innerHTML = X\n x.setAttribute('aria-label', t.close)\n }\n x.addEventListener('click', () => close(true))\n // the list scrolls when the viewer is short: a stop for the keys, so they can scroll it (no\n // role or name of its own: the dialog's title names it already, once)\n const body = el('div', 'gallerist-shortcuts-body')\n body.tabIndex = 0\n for (const [name, list] of rows(keys)) {\n const section = body.appendChild(el('section'))\n if (name) section.appendChild(el('h3', '', name))\n const dl = section.appendChild(el('dl'))\n for (const [label, what] of list) {\n const row = dl.appendChild(el('div', 'gallerist-shortcuts-row'))\n row.appendChild(el('dt', '', label))\n const dd = row.appendChild(el('dd'))\n if (typeof what === 'string') dd.textContent = what\n else for (const k of what) dd.appendChild(el('kbd', '', k))\n }\n }\n box.append(head, body)\n if (!keys) box.appendChild(el('p', 'gallerist-shortcuts-note', t.keysNote))\n s.appendChild(box)\n // the scrim takes no focus (the viewer's root would, under a press), and a click on it closes\n s.addEventListener('mousedown', (e) => e.target === s && e.preventDefault())\n s.addEventListener('click', (e) => e.target === s && close(true))\n // the rest of the viewer is inert while the dialog shows (what was inert already stays so)\n for (const node of r.children)\n if (!node.hasAttribute('inert')) {\n node.setAttribute('inert', '')\n inerted.push(node)\n }\n r.appendChild(s)\n scrim = s\n window.addEventListener('keydown', onKey, true)\n document.addEventListener('pointerdown', onPress, true)\n box.focus({ preventScroll: true })\n return true\n }\n\n const instance: ShortcutsInstance = {\n name: 'shortcuts',\n get label() {\n return coarse() ? t.gestures : t.title\n },\n get isOpen() {\n return !!scrim\n },\n open,\n close: () => close(true),\n init() {\n live.push(instance)\n unsub = viewer.subscribe((st) => {\n if (st.phase === 'closing' || st.phase === 'closed') close()\n })\n },\n destroy() {\n close()\n unsub?.()\n unsub = anchor = null\n const at = live.indexOf(instance)\n if (at >= 0) live.splice(at, 1)\n },\n keymap: { '?': () => open(true) },\n // a hidden node, to find the viewer's root by (marked off: a bar shows not for it)\n regions: {\n toolbar: () => {\n anchor = el('span')\n anchor.hidden = true\n anchor.setAttribute('data-gallerist-off', '')\n return anchor\n },\n },\n }\n return instance\n }\n return Object.assign(plugin, {\n open: (keys?: boolean) => live.reduceRight((done, i) => done || i.open(keys), false),\n close: () => {\n for (const i of live) i.close()\n },\n })\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { PluginInstance, Plugin } from '@gallerist/core';
|
|
2
|
+
|
|
3
|
+
/** A host's own shortcut, listed with the built-in ones. */
|
|
4
|
+
interface ShortcutEntry {
|
|
5
|
+
/** what it does */
|
|
6
|
+
label: string;
|
|
7
|
+
/** the keys, each drawn as a key cap (`['N']`, `['Shift', 'N']`); without them it's listed only
|
|
8
|
+
* with the gestures */
|
|
9
|
+
keys?: readonly string[];
|
|
10
|
+
/** the gesture that does it on a touch screen (`'Long press'`); without one it's listed only with
|
|
11
|
+
* the keys */
|
|
12
|
+
gesture?: string;
|
|
13
|
+
/** the section it joins: a built-in one by its title (`strings.move`, `zoom`, `viewer`) or a new
|
|
14
|
+
* one after them. Default: the Viewer section. */
|
|
15
|
+
section?: string;
|
|
16
|
+
}
|
|
17
|
+
interface ShortcutsStrings {
|
|
18
|
+
/** the dialog's title with the keys, and the View menu's row */
|
|
19
|
+
title: string;
|
|
20
|
+
/** the dialog's title with the gestures (and the View menu's row on a touch screen) */
|
|
21
|
+
gestures: string;
|
|
22
|
+
/** the close button's text, `{key}` where the Esc key cap goes ("{key} to close") */
|
|
23
|
+
toClose: string;
|
|
24
|
+
close: string;
|
|
25
|
+
/** section titles */
|
|
26
|
+
move: string;
|
|
27
|
+
zoom: string;
|
|
28
|
+
viewer: string;
|
|
29
|
+
/** moving */
|
|
30
|
+
prevNext: string;
|
|
31
|
+
prevNextGroup: string;
|
|
32
|
+
prevNextItem: string;
|
|
33
|
+
nextItem: string;
|
|
34
|
+
firstLast: string;
|
|
35
|
+
firstLastGroup: string;
|
|
36
|
+
jump: string;
|
|
37
|
+
/** zooming */
|
|
38
|
+
zoomIn: string;
|
|
39
|
+
zoomOut: string;
|
|
40
|
+
fit: string;
|
|
41
|
+
pan: string;
|
|
42
|
+
/** the viewer, and the plugins' keys */
|
|
43
|
+
fullscreen: string;
|
|
44
|
+
rail: string;
|
|
45
|
+
items: string;
|
|
46
|
+
grid: string;
|
|
47
|
+
pins: string;
|
|
48
|
+
addPin: string;
|
|
49
|
+
download: string;
|
|
50
|
+
help: string;
|
|
51
|
+
exit: string;
|
|
52
|
+
/** gestures */
|
|
53
|
+
swipe: string;
|
|
54
|
+
swipeUp: string;
|
|
55
|
+
swipeDown: string;
|
|
56
|
+
pinch: string;
|
|
57
|
+
drag: string;
|
|
58
|
+
/** under the gestures */
|
|
59
|
+
keysNote: string;
|
|
60
|
+
/** key names on their caps */
|
|
61
|
+
home: string;
|
|
62
|
+
end: string;
|
|
63
|
+
esc: string;
|
|
64
|
+
}
|
|
65
|
+
declare const DEFAULT_SHORTCUTS_STRINGS: ShortcutsStrings;
|
|
66
|
+
interface ShortcutsOptions {
|
|
67
|
+
/** the host's own shortcuts, listed after the built-in ones */
|
|
68
|
+
shortcuts?: readonly ShortcutEntry[];
|
|
69
|
+
/** UI text overrides, for translation; merged over English. */
|
|
70
|
+
strings?: Partial<ShortcutsStrings>;
|
|
71
|
+
}
|
|
72
|
+
/** The plugin's instance on a viewer (named `shortcuts`); the View menu finds it by that name. */
|
|
73
|
+
interface ShortcutsInstance extends PluginInstance {
|
|
74
|
+
/** what `open()` shows now: the title of the keys, or of the gestures on a coarse pointer */
|
|
75
|
+
readonly label: string;
|
|
76
|
+
readonly isOpen: boolean;
|
|
77
|
+
/** Opens the list on the open viewer: the keys (`true`), the gestures (`false`), or by the
|
|
78
|
+
* pointer (default). False when there is no open viewer to show it in. */
|
|
79
|
+
open(keys?: boolean): boolean;
|
|
80
|
+
close(): void;
|
|
81
|
+
}
|
|
82
|
+
/** `shortcuts()`: a plugin, with `open()` / `close()` for the host (a help button of its own). */
|
|
83
|
+
interface ShortcutsPlugin extends Plugin {
|
|
84
|
+
/** Opens the list on the viewer this plugin was last registered on that is open. */
|
|
85
|
+
open(keys?: boolean): boolean;
|
|
86
|
+
close(): void;
|
|
87
|
+
}
|
|
88
|
+
declare function shortcuts(opts?: ShortcutsOptions): ShortcutsPlugin;
|
|
89
|
+
|
|
90
|
+
export { DEFAULT_SHORTCUTS_STRINGS, type ShortcutEntry, type ShortcutsInstance, type ShortcutsOptions, type ShortcutsPlugin, type ShortcutsStrings, shortcuts };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { PluginInstance, Plugin } from '@gallerist/core';
|
|
2
|
+
|
|
3
|
+
/** A host's own shortcut, listed with the built-in ones. */
|
|
4
|
+
interface ShortcutEntry {
|
|
5
|
+
/** what it does */
|
|
6
|
+
label: string;
|
|
7
|
+
/** the keys, each drawn as a key cap (`['N']`, `['Shift', 'N']`); without them it's listed only
|
|
8
|
+
* with the gestures */
|
|
9
|
+
keys?: readonly string[];
|
|
10
|
+
/** the gesture that does it on a touch screen (`'Long press'`); without one it's listed only with
|
|
11
|
+
* the keys */
|
|
12
|
+
gesture?: string;
|
|
13
|
+
/** the section it joins: a built-in one by its title (`strings.move`, `zoom`, `viewer`) or a new
|
|
14
|
+
* one after them. Default: the Viewer section. */
|
|
15
|
+
section?: string;
|
|
16
|
+
}
|
|
17
|
+
interface ShortcutsStrings {
|
|
18
|
+
/** the dialog's title with the keys, and the View menu's row */
|
|
19
|
+
title: string;
|
|
20
|
+
/** the dialog's title with the gestures (and the View menu's row on a touch screen) */
|
|
21
|
+
gestures: string;
|
|
22
|
+
/** the close button's text, `{key}` where the Esc key cap goes ("{key} to close") */
|
|
23
|
+
toClose: string;
|
|
24
|
+
close: string;
|
|
25
|
+
/** section titles */
|
|
26
|
+
move: string;
|
|
27
|
+
zoom: string;
|
|
28
|
+
viewer: string;
|
|
29
|
+
/** moving */
|
|
30
|
+
prevNext: string;
|
|
31
|
+
prevNextGroup: string;
|
|
32
|
+
prevNextItem: string;
|
|
33
|
+
nextItem: string;
|
|
34
|
+
firstLast: string;
|
|
35
|
+
firstLastGroup: string;
|
|
36
|
+
jump: string;
|
|
37
|
+
/** zooming */
|
|
38
|
+
zoomIn: string;
|
|
39
|
+
zoomOut: string;
|
|
40
|
+
fit: string;
|
|
41
|
+
pan: string;
|
|
42
|
+
/** the viewer, and the plugins' keys */
|
|
43
|
+
fullscreen: string;
|
|
44
|
+
rail: string;
|
|
45
|
+
items: string;
|
|
46
|
+
grid: string;
|
|
47
|
+
pins: string;
|
|
48
|
+
addPin: string;
|
|
49
|
+
download: string;
|
|
50
|
+
help: string;
|
|
51
|
+
exit: string;
|
|
52
|
+
/** gestures */
|
|
53
|
+
swipe: string;
|
|
54
|
+
swipeUp: string;
|
|
55
|
+
swipeDown: string;
|
|
56
|
+
pinch: string;
|
|
57
|
+
drag: string;
|
|
58
|
+
/** under the gestures */
|
|
59
|
+
keysNote: string;
|
|
60
|
+
/** key names on their caps */
|
|
61
|
+
home: string;
|
|
62
|
+
end: string;
|
|
63
|
+
esc: string;
|
|
64
|
+
}
|
|
65
|
+
declare const DEFAULT_SHORTCUTS_STRINGS: ShortcutsStrings;
|
|
66
|
+
interface ShortcutsOptions {
|
|
67
|
+
/** the host's own shortcuts, listed after the built-in ones */
|
|
68
|
+
shortcuts?: readonly ShortcutEntry[];
|
|
69
|
+
/** UI text overrides, for translation; merged over English. */
|
|
70
|
+
strings?: Partial<ShortcutsStrings>;
|
|
71
|
+
}
|
|
72
|
+
/** The plugin's instance on a viewer (named `shortcuts`); the View menu finds it by that name. */
|
|
73
|
+
interface ShortcutsInstance extends PluginInstance {
|
|
74
|
+
/** what `open()` shows now: the title of the keys, or of the gestures on a coarse pointer */
|
|
75
|
+
readonly label: string;
|
|
76
|
+
readonly isOpen: boolean;
|
|
77
|
+
/** Opens the list on the open viewer: the keys (`true`), the gestures (`false`), or by the
|
|
78
|
+
* pointer (default). False when there is no open viewer to show it in. */
|
|
79
|
+
open(keys?: boolean): boolean;
|
|
80
|
+
close(): void;
|
|
81
|
+
}
|
|
82
|
+
/** `shortcuts()`: a plugin, with `open()` / `close()` for the host (a help button of its own). */
|
|
83
|
+
interface ShortcutsPlugin extends Plugin {
|
|
84
|
+
/** Opens the list on the viewer this plugin was last registered on that is open. */
|
|
85
|
+
open(keys?: boolean): boolean;
|
|
86
|
+
close(): void;
|
|
87
|
+
}
|
|
88
|
+
declare function shortcuts(opts?: ShortcutsOptions): ShortcutsPlugin;
|
|
89
|
+
|
|
90
|
+
export { DEFAULT_SHORTCUTS_STRINGS, type ShortcutEntry, type ShortcutsInstance, type ShortcutsOptions, type ShortcutsPlugin, type ShortcutsStrings, shortcuts };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { getInternals } from '@gallerist/core/internal';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
var DEFAULT_SHORTCUTS_STRINGS = {
|
|
5
|
+
title: "Keyboard shortcuts",
|
|
6
|
+
gestures: "Gestures",
|
|
7
|
+
toClose: "{key} to close",
|
|
8
|
+
close: "Close",
|
|
9
|
+
move: "Move",
|
|
10
|
+
zoom: "Zoom",
|
|
11
|
+
viewer: "Viewer",
|
|
12
|
+
prevNext: "Previous / next",
|
|
13
|
+
prevNextGroup: "Previous / next group",
|
|
14
|
+
prevNextItem: "Previous / next in the group",
|
|
15
|
+
nextItem: "Next in the group",
|
|
16
|
+
firstLast: "First / last",
|
|
17
|
+
firstLastGroup: "First / last group",
|
|
18
|
+
jump: "Item in the group",
|
|
19
|
+
zoomIn: "Zoom in",
|
|
20
|
+
zoomOut: "Zoom out",
|
|
21
|
+
fit: "Fit to screen",
|
|
22
|
+
pan: "Move while zoomed",
|
|
23
|
+
fullscreen: "Fullscreen",
|
|
24
|
+
rail: "Thumbnails",
|
|
25
|
+
items: "Item list",
|
|
26
|
+
grid: "Grid",
|
|
27
|
+
pins: "Pins",
|
|
28
|
+
addPin: "Add a comment",
|
|
29
|
+
download: "Download",
|
|
30
|
+
help: "This list",
|
|
31
|
+
exit: "Close",
|
|
32
|
+
swipe: "Swipe left or right",
|
|
33
|
+
swipeUp: "Swipe up",
|
|
34
|
+
swipeDown: "Swipe down",
|
|
35
|
+
pinch: "Pinch, or double-tap",
|
|
36
|
+
drag: "Drag",
|
|
37
|
+
keysNote: "With a keyboard, ? lists its shortcuts.",
|
|
38
|
+
home: "Home",
|
|
39
|
+
end: "End",
|
|
40
|
+
esc: "Esc"
|
|
41
|
+
};
|
|
42
|
+
var KEYBOARD = '<svg class="gallerist-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"/></svg>';
|
|
43
|
+
var X = '<svg class="gallerist-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M18 6 6 18M6 6l12 12"/></svg>';
|
|
44
|
+
var TABBABLE = 'button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])';
|
|
45
|
+
var lists = 0;
|
|
46
|
+
var el = (tag, className = "", text = "") => {
|
|
47
|
+
const node = document.createElement(tag);
|
|
48
|
+
if (className) node.className = className;
|
|
49
|
+
if (text) node.textContent = text;
|
|
50
|
+
return node;
|
|
51
|
+
};
|
|
52
|
+
var cap = (k) => k.length === 1 ? k.toUpperCase() : k;
|
|
53
|
+
var chord = (e) => (e.ctrlKey || e.metaKey || e.altKey) && !e.getModifierState?.("AltGraph");
|
|
54
|
+
function shortcuts(opts = {}) {
|
|
55
|
+
const t = { ...DEFAULT_SHORTCUTS_STRINGS, ...opts.strings };
|
|
56
|
+
const live = [];
|
|
57
|
+
const plugin = (viewer) => {
|
|
58
|
+
let anchor = null;
|
|
59
|
+
let scrim = null;
|
|
60
|
+
let back = null;
|
|
61
|
+
let inerted = [];
|
|
62
|
+
let unsub = null;
|
|
63
|
+
const id = `gallerist-shortcuts-${++lists}`;
|
|
64
|
+
const root = () => anchor?.closest(".gallerist") ?? null;
|
|
65
|
+
const coarse = () => !!window.matchMedia?.("(pointer: coarse)").matches;
|
|
66
|
+
const rows = (keys) => {
|
|
67
|
+
const r = root();
|
|
68
|
+
const plugins = getInternals(viewer).getPlugins();
|
|
69
|
+
const map = {};
|
|
70
|
+
for (const p of plugins) Object.assign(map, p.keymap);
|
|
71
|
+
const named = (name) => plugins.find((p) => p.name === name);
|
|
72
|
+
const held = (name, k) => {
|
|
73
|
+
const own = named(name)?.keymap ?? {};
|
|
74
|
+
const key = k ?? Object.keys(own)[0];
|
|
75
|
+
return key && own[key] && map[key] === own[key] ? key : void 0;
|
|
76
|
+
};
|
|
77
|
+
const linear = r.hasAttribute("data-gallerist-linear");
|
|
78
|
+
const vertical = r.getAttribute("data-gallerist-axis") === "vertical";
|
|
79
|
+
const across = r.getAttribute("data-gallerist-dir") === "rtl" ? ["\u2192", "\u2190"] : ["\u2190", "\u2192"];
|
|
80
|
+
const along = ["\u2191", "\u2193"];
|
|
81
|
+
const grouped = !linear && viewer.getCollection().groups.some((g) => g.items.length > 1);
|
|
82
|
+
const overlay = r.getAttribute("data-gallerist-mode") === "overlay";
|
|
83
|
+
const rail = named("thumbnails")?.display;
|
|
84
|
+
const can = named("zoom")?.zoomable ?? ((i) => i.type === "image");
|
|
85
|
+
const zoom = viewer.getCollection().groups.some((g) => g.items.some((i) => can(i)));
|
|
86
|
+
let sections;
|
|
87
|
+
if (keys) {
|
|
88
|
+
const move = grouped ? [
|
|
89
|
+
[t.prevNextGroup, vertical ? along : across],
|
|
90
|
+
[t.prevNextItem, vertical ? across : along],
|
|
91
|
+
[t.firstLastGroup, [t.home, t.end]],
|
|
92
|
+
[t.jump, ["1\u20139"]]
|
|
93
|
+
] : [
|
|
94
|
+
[t.prevNext, vertical ? along : across],
|
|
95
|
+
[t.firstLast, [t.home, t.end]]
|
|
96
|
+
];
|
|
97
|
+
const fs = document.webkitFullscreenEnabled;
|
|
98
|
+
const tools = [
|
|
99
|
+
[t.fullscreen, document.fullscreenEnabled ?? fs ? held("fullscreen", "f") : void 0],
|
|
100
|
+
[t.rail, rail ? "t" : void 0],
|
|
101
|
+
[t.items, held("thumbnails", "e")],
|
|
102
|
+
[t.grid, held("grid")],
|
|
103
|
+
[t.pins, held("pins", "p")],
|
|
104
|
+
[t.addPin, held("pins", "c")],
|
|
105
|
+
[t.download, held("download")],
|
|
106
|
+
[t.help, "?"],
|
|
107
|
+
[t.exit, overlay ? t.esc : void 0]
|
|
108
|
+
];
|
|
109
|
+
sections = [
|
|
110
|
+
[t.move, move],
|
|
111
|
+
[
|
|
112
|
+
t.zoom,
|
|
113
|
+
zoom ? [
|
|
114
|
+
[t.zoomIn, ["+"]],
|
|
115
|
+
[t.zoomOut, ["\u2212"]],
|
|
116
|
+
[t.fit, ["0"]]
|
|
117
|
+
] : []
|
|
118
|
+
],
|
|
119
|
+
[t.viewer, tools.flatMap(([label, k]) => k ? [[label, [cap(k)]]] : [])]
|
|
120
|
+
];
|
|
121
|
+
} else {
|
|
122
|
+
sections = [
|
|
123
|
+
[
|
|
124
|
+
"",
|
|
125
|
+
[
|
|
126
|
+
[grouped ? t.prevNextGroup : t.prevNext, t.swipe],
|
|
127
|
+
...grouped ? [[t.nextItem, t.swipeUp]] : [],
|
|
128
|
+
...zoom ? [[t.zoom, t.pinch], [t.pan, t.drag]] : [],
|
|
129
|
+
...overlay ? [[t.exit, t.swipeDown]] : []
|
|
130
|
+
]
|
|
131
|
+
]
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
for (const x of opts.shortcuts ?? []) {
|
|
135
|
+
const what = keys ? x.keys : x.gesture;
|
|
136
|
+
if (!what?.length) continue;
|
|
137
|
+
const title = keys ? x.section ?? t.viewer : "";
|
|
138
|
+
let section = sections.find(([s]) => s === title);
|
|
139
|
+
if (!section) {
|
|
140
|
+
section = [title, []];
|
|
141
|
+
sections.push(section);
|
|
142
|
+
}
|
|
143
|
+
section[1].push([x.label, what]);
|
|
144
|
+
}
|
|
145
|
+
return sections.filter(([, list]) => list.length);
|
|
146
|
+
};
|
|
147
|
+
const close = (refocus = false) => {
|
|
148
|
+
if (!scrim) return;
|
|
149
|
+
scrim.remove();
|
|
150
|
+
scrim = null;
|
|
151
|
+
for (const node of inerted) node.removeAttribute("inert");
|
|
152
|
+
inerted = [];
|
|
153
|
+
window.removeEventListener("keydown", onKey, true);
|
|
154
|
+
document.removeEventListener("pointerdown", onPress, true);
|
|
155
|
+
if (refocus) (back?.isConnected ? back : root())?.focus({ preventScroll: true });
|
|
156
|
+
back = null;
|
|
157
|
+
};
|
|
158
|
+
const onKey = (e) => {
|
|
159
|
+
const r = root();
|
|
160
|
+
if (!scrim?.isConnected) return close();
|
|
161
|
+
if (!r || !e.composedPath().includes(r)) return;
|
|
162
|
+
e.stopPropagation();
|
|
163
|
+
if (e.key === "Escape" || e.key === "?" && !chord(e)) {
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
close(true);
|
|
166
|
+
} else if (e.key === "Tab") {
|
|
167
|
+
const box = scrim.firstElementChild;
|
|
168
|
+
const list = [...box.querySelectorAll(TABBABLE)];
|
|
169
|
+
const active = r.getRootNode().activeElement;
|
|
170
|
+
const first = list[0];
|
|
171
|
+
const last = list[list.length - 1];
|
|
172
|
+
const to = e.shiftKey ? active === first || active === box ? last : null : active === last ? first : null;
|
|
173
|
+
if (!first || to) {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
to?.focus();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const onPress = (e) => {
|
|
180
|
+
if (!e.composedPath().includes(root())) close();
|
|
181
|
+
};
|
|
182
|
+
const open = (keys = !coarse()) => {
|
|
183
|
+
const r = root();
|
|
184
|
+
if (!r || !anchor?.isConnected || viewer.getState().phase !== "open") return false;
|
|
185
|
+
close();
|
|
186
|
+
back = r.getRootNode().activeElement;
|
|
187
|
+
const s = el("div", "gallerist-shortcuts-scrim");
|
|
188
|
+
const box = el("div", "gallerist-shortcuts");
|
|
189
|
+
box.setAttribute("role", "dialog");
|
|
190
|
+
box.setAttribute("aria-modal", "true");
|
|
191
|
+
box.setAttribute("aria-labelledby", id);
|
|
192
|
+
box.tabIndex = -1;
|
|
193
|
+
box.setAttribute("data-gallerist-list", keys ? "keys" : "gestures");
|
|
194
|
+
const head = el("div", "gallerist-shortcuts-head");
|
|
195
|
+
if (keys) head.innerHTML = KEYBOARD;
|
|
196
|
+
const title = head.appendChild(el("h2", "", keys ? t.title : t.gestures));
|
|
197
|
+
title.id = id;
|
|
198
|
+
const x = head.appendChild(el("button", "gallerist-shortcuts-close"));
|
|
199
|
+
x.type = "button";
|
|
200
|
+
if (keys) {
|
|
201
|
+
const [before, after] = t.toClose.split("{key}");
|
|
202
|
+
x.append(before, el("kbd", "", t.esc), after ?? "");
|
|
203
|
+
} else {
|
|
204
|
+
x.innerHTML = X;
|
|
205
|
+
x.setAttribute("aria-label", t.close);
|
|
206
|
+
}
|
|
207
|
+
x.addEventListener("click", () => close(true));
|
|
208
|
+
const body = el("div", "gallerist-shortcuts-body");
|
|
209
|
+
body.tabIndex = 0;
|
|
210
|
+
for (const [name, list] of rows(keys)) {
|
|
211
|
+
const section = body.appendChild(el("section"));
|
|
212
|
+
if (name) section.appendChild(el("h3", "", name));
|
|
213
|
+
const dl = section.appendChild(el("dl"));
|
|
214
|
+
for (const [label, what] of list) {
|
|
215
|
+
const row = dl.appendChild(el("div", "gallerist-shortcuts-row"));
|
|
216
|
+
row.appendChild(el("dt", "", label));
|
|
217
|
+
const dd = row.appendChild(el("dd"));
|
|
218
|
+
if (typeof what === "string") dd.textContent = what;
|
|
219
|
+
else for (const k of what) dd.appendChild(el("kbd", "", k));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
box.append(head, body);
|
|
223
|
+
if (!keys) box.appendChild(el("p", "gallerist-shortcuts-note", t.keysNote));
|
|
224
|
+
s.appendChild(box);
|
|
225
|
+
s.addEventListener("mousedown", (e) => e.target === s && e.preventDefault());
|
|
226
|
+
s.addEventListener("click", (e) => e.target === s && close(true));
|
|
227
|
+
for (const node of r.children)
|
|
228
|
+
if (!node.hasAttribute("inert")) {
|
|
229
|
+
node.setAttribute("inert", "");
|
|
230
|
+
inerted.push(node);
|
|
231
|
+
}
|
|
232
|
+
r.appendChild(s);
|
|
233
|
+
scrim = s;
|
|
234
|
+
window.addEventListener("keydown", onKey, true);
|
|
235
|
+
document.addEventListener("pointerdown", onPress, true);
|
|
236
|
+
box.focus({ preventScroll: true });
|
|
237
|
+
return true;
|
|
238
|
+
};
|
|
239
|
+
const instance = {
|
|
240
|
+
name: "shortcuts",
|
|
241
|
+
get label() {
|
|
242
|
+
return coarse() ? t.gestures : t.title;
|
|
243
|
+
},
|
|
244
|
+
get isOpen() {
|
|
245
|
+
return !!scrim;
|
|
246
|
+
},
|
|
247
|
+
open,
|
|
248
|
+
close: () => close(true),
|
|
249
|
+
init() {
|
|
250
|
+
live.push(instance);
|
|
251
|
+
unsub = viewer.subscribe((st) => {
|
|
252
|
+
if (st.phase === "closing" || st.phase === "closed") close();
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
destroy() {
|
|
256
|
+
close();
|
|
257
|
+
unsub?.();
|
|
258
|
+
unsub = anchor = null;
|
|
259
|
+
const at = live.indexOf(instance);
|
|
260
|
+
if (at >= 0) live.splice(at, 1);
|
|
261
|
+
},
|
|
262
|
+
keymap: { "?": () => open(true) },
|
|
263
|
+
// a hidden node, to find the viewer's root by (marked off: a bar shows not for it)
|
|
264
|
+
regions: {
|
|
265
|
+
toolbar: () => {
|
|
266
|
+
anchor = el("span");
|
|
267
|
+
anchor.hidden = true;
|
|
268
|
+
anchor.setAttribute("data-gallerist-off", "");
|
|
269
|
+
return anchor;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
return instance;
|
|
274
|
+
};
|
|
275
|
+
return Object.assign(plugin, {
|
|
276
|
+
open: (keys) => live.reduceRight((done, i) => done || i.open(keys), false),
|
|
277
|
+
close: () => {
|
|
278
|
+
for (const i of live) i.close();
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export { DEFAULT_SHORTCUTS_STRINGS, shortcuts };
|
|
284
|
+
//# sourceMappingURL=index.js.map
|
|
285
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// @gallerist/plugin-shortcuts — the viewer's keyboard shortcuts in a modal dialog, on `?`.\n//\n// The list is built each time it opens, from what the viewer does then: the renderer's own keys as\n// the layout has them (a row rail walks every item with ←/→ and has no ↑/↓; a side rail turns the\n// axes; right to left mirrors ←/→), the official plugins' keys while they are registered and hold\n// them (the aggregated keymap, so a key another plugin took isn't listed), and the host's entries.\n// Opened by a touch (the View menu's row, `open()`, on a coarse pointer) it lists the renderer's\n// gestures instead. `?` (with the viewer focused, not in a text field) opens the keys.\n//\n// The dialog sits in the viewer's root over a scrim that covers the viewer: `aria-modal`, the rest\n// of the root inert while it shows, Tab kept inside, focus back where it was on close. Keys pressed\n// in it never reach the viewer (a window listener in the capture phase takes them first): Escape and\n// `?` close it, and nothing else acts. The root is found from a hidden node in the toolbar region.\n\nimport type { Item, Plugin, PluginInstance, ViewerApi } from '@gallerist/core'\nimport { getInternals } from '@gallerist/core/internal'\n\n/** A host's own shortcut, listed with the built-in ones. */\nexport interface ShortcutEntry {\n /** what it does */\n label: string\n /** the keys, each drawn as a key cap (`['N']`, `['Shift', 'N']`); without them it's listed only\n * with the gestures */\n keys?: readonly string[]\n /** the gesture that does it on a touch screen (`'Long press'`); without one it's listed only with\n * the keys */\n gesture?: string\n /** the section it joins: a built-in one by its title (`strings.move`, `zoom`, `viewer`) or a new\n * one after them. Default: the Viewer section. */\n section?: string\n}\n\nexport interface ShortcutsStrings {\n /** the dialog's title with the keys, and the View menu's row */\n title: string\n /** the dialog's title with the gestures (and the View menu's row on a touch screen) */\n gestures: string\n /** the close button's text, `{key}` where the Esc key cap goes (\"{key} to close\") */\n toClose: string\n close: string\n /** section titles */\n move: string\n zoom: string\n viewer: string\n /** moving */\n prevNext: string\n prevNextGroup: string\n prevNextItem: string\n nextItem: string\n firstLast: string\n firstLastGroup: string\n jump: string\n /** zooming */\n zoomIn: string\n zoomOut: string\n fit: string\n pan: string\n /** the viewer, and the plugins' keys */\n fullscreen: string\n rail: string\n items: string\n grid: string\n pins: string\n addPin: string\n download: string\n help: string\n exit: string\n /** gestures */\n swipe: string\n swipeUp: string\n swipeDown: string\n pinch: string\n drag: string\n /** under the gestures */\n keysNote: string\n /** key names on their caps */\n home: string\n end: string\n esc: string\n}\n\nexport const DEFAULT_SHORTCUTS_STRINGS: ShortcutsStrings = {\n title: 'Keyboard shortcuts',\n gestures: 'Gestures',\n toClose: '{key} to close',\n close: 'Close',\n move: 'Move',\n zoom: 'Zoom',\n viewer: 'Viewer',\n prevNext: 'Previous / next',\n prevNextGroup: 'Previous / next group',\n prevNextItem: 'Previous / next in the group',\n nextItem: 'Next in the group',\n firstLast: 'First / last',\n firstLastGroup: 'First / last group',\n jump: 'Item in the group',\n zoomIn: 'Zoom in',\n zoomOut: 'Zoom out',\n fit: 'Fit to screen',\n pan: 'Move while zoomed',\n fullscreen: 'Fullscreen',\n rail: 'Thumbnails',\n items: 'Item list',\n grid: 'Grid',\n pins: 'Pins',\n addPin: 'Add a comment',\n download: 'Download',\n help: 'This list',\n exit: 'Close',\n swipe: 'Swipe left or right',\n swipeUp: 'Swipe up',\n swipeDown: 'Swipe down',\n pinch: 'Pinch, or double-tap',\n drag: 'Drag',\n keysNote: 'With a keyboard, ? lists its shortcuts.',\n home: 'Home',\n end: 'End',\n esc: 'Esc',\n}\n\nexport interface ShortcutsOptions {\n /** the host's own shortcuts, listed after the built-in ones */\n shortcuts?: readonly ShortcutEntry[]\n /** UI text overrides, for translation; merged over English. */\n strings?: Partial<ShortcutsStrings>\n}\n\n/** The plugin's instance on a viewer (named `shortcuts`); the View menu finds it by that name. */\nexport interface ShortcutsInstance extends PluginInstance {\n /** what `open()` shows now: the title of the keys, or of the gestures on a coarse pointer */\n readonly label: string\n readonly isOpen: boolean\n /** Opens the list on the open viewer: the keys (`true`), the gestures (`false`), or by the\n * pointer (default). False when there is no open viewer to show it in. */\n open(keys?: boolean): boolean\n close(): void\n}\n\n/** `shortcuts()`: a plugin, with `open()` / `close()` for the host (a help button of its own). */\nexport interface ShortcutsPlugin extends Plugin {\n /** Opens the list on the viewer this plugin was last registered on that is open. */\n open(keys?: boolean): boolean\n close(): void\n}\n\ntype Row = [label: string, keys: readonly string[] | string]\n\nconst KEYBOARD =\n '<svg class=\"gallerist-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"/><path d=\"M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10\"/></svg>'\nconst X =\n '<svg class=\"gallerist-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>'\nconst TABBABLE = 'button:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])'\n\nlet lists = 0 // ids, for aria-labelledby\n\nconst el = <K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className = '',\n text = '',\n): HTMLElementTagNameMap[K] => {\n const node = document.createElement(tag)\n if (className) node.className = className\n if (text) node.textContent = text\n return node\n}\nconst cap = (k: string) => (k.length === 1 ? k.toUpperCase() : k)\n// `?` typed with AltGr (Ctrl + Alt on Windows) is still `?`; another chord is the browser's\nconst chord = (e: KeyboardEvent) =>\n (e.ctrlKey || e.metaKey || e.altKey) && !e.getModifierState?.('AltGraph')\n\nexport function shortcuts(opts: ShortcutsOptions = {}): ShortcutsPlugin {\n const t: ShortcutsStrings = { ...DEFAULT_SHORTCUTS_STRINGS, ...opts.strings }\n const live: ShortcutsInstance[] = [] // registered instances, the latest last\n\n const plugin = (viewer: ViewerApi): ShortcutsInstance => {\n let anchor: HTMLElement | null = null\n let scrim: HTMLElement | null = null\n let back: HTMLElement | null = null // focus before it opened\n let inerted: Element[] = []\n let unsub: (() => void) | null = null\n const id = `gallerist-shortcuts-${++lists}`\n const root = () => anchor?.closest<HTMLElement>('.gallerist') ?? null\n const coarse = () => !!window.matchMedia?.('(pointer: coarse)').matches\n\n // What the viewer does now, read from its root and its plugins.\n const rows = (keys: boolean): Array<[string, Row[]]> => {\n const r = root() as HTMLElement\n const plugins = getInternals(viewer).getPlugins()\n const map: Record<string, unknown> = {}\n for (const p of plugins) Object.assign(map, p.keymap)\n const named = (name: string) => plugins.find((p) => p.name === name)\n // the key `k` of the plugin `name`, while it holds it (none: its first key)\n const held = (name: string, k?: string) => {\n const own = named(name)?.keymap ?? {}\n const key = k ?? Object.keys(own)[0]\n return key && own[key] && map[key] === own[key] ? key : undefined\n }\n const linear = r.hasAttribute('data-gallerist-linear')\n const vertical = r.getAttribute('data-gallerist-axis') === 'vertical'\n const across = r.getAttribute('data-gallerist-dir') === 'rtl' ? ['→', '←'] : ['←', '→']\n const along = ['↑', '↓']\n const grouped = !linear && viewer.getCollection().groups.some((g) => g.items.length > 1)\n const overlay = r.getAttribute('data-gallerist-mode') === 'overlay'\n const rail = (named('thumbnails') as { display?: string } | undefined)?.display\n\n // zooming, while an item can zoom: plugin-zoom's `zoomable`, else an image\n const can =\n (named('zoom') as { zoomable?: (item: Item) => boolean } | undefined)?.zoomable ??\n ((i: Item) => i.type === 'image')\n const zoom = viewer.getCollection().groups.some((g) => g.items.some((i) => can(i)))\n let sections: Array<[string, Row[]]>\n if (keys) {\n const move: Row[] = grouped\n ? [\n [t.prevNextGroup, vertical ? along : across],\n [t.prevNextItem, vertical ? across : along],\n [t.firstLastGroup, [t.home, t.end]],\n [t.jump, ['1–9']],\n ]\n : [\n [t.prevNext, vertical ? along : across],\n [t.firstLast, [t.home, t.end]],\n ]\n const fs = (document as { webkitFullscreenEnabled?: boolean }).webkitFullscreenEnabled\n const tools: Array<[string, string | undefined]> = [\n [t.fullscreen, (document.fullscreenEnabled ?? fs) ? held('fullscreen', 'f') : undefined],\n [t.rail, rail ? 't' : undefined],\n [t.items, held('thumbnails', 'e')],\n [t.grid, held('grid')],\n [t.pins, held('pins', 'p')],\n [t.addPin, held('pins', 'c')],\n [t.download, held('download')],\n [t.help, '?'],\n [t.exit, overlay ? t.esc : undefined],\n ]\n sections = [\n [t.move, move],\n [\n t.zoom,\n zoom\n ? [\n [t.zoomIn, ['+']],\n [t.zoomOut, ['−']],\n [t.fit, ['0']],\n ]\n : [],\n ],\n [t.viewer, tools.flatMap(([label, k]) => (k ? [[label, [cap(k)]] as Row] : []))],\n ]\n } else {\n sections = [\n [\n '',\n [\n [grouped ? t.prevNextGroup : t.prevNext, t.swipe],\n ...(grouped ? [[t.nextItem, t.swipeUp] as Row] : []),\n ...(zoom ? [[t.zoom, t.pinch] as Row, [t.pan, t.drag] as Row] : []),\n ...(overlay ? [[t.exit, t.swipeDown] as Row] : []),\n ],\n ],\n ]\n }\n for (const x of opts.shortcuts ?? []) {\n const what = keys ? x.keys : x.gesture\n if (!what?.length) continue\n const title = keys ? (x.section ?? t.viewer) : ''\n let section = sections.find(([s]) => s === title)\n if (!section) {\n section = [title, []]\n sections.push(section)\n }\n section[1].push([x.label, what])\n }\n return sections.filter(([, list]) => list.length)\n }\n\n const close = (refocus = false) => {\n if (!scrim) return\n scrim.remove()\n scrim = null\n for (const node of inerted) node.removeAttribute('inert')\n inerted = []\n window.removeEventListener('keydown', onKey, true)\n document.removeEventListener('pointerdown', onPress, true)\n if (refocus) (back?.isConnected ? back : root())?.focus({ preventScroll: true })\n back = null\n }\n\n // Every key pressed in the viewer while the list shows stops here: the viewer's keys, the zoom\n // tool's and the page's never see it. Escape and ? close; Tab stays in the dialog.\n const onKey = (e: KeyboardEvent) => {\n const r = root()\n if (!scrim?.isConnected) return close() // the viewer went away with it open\n if (!r || !e.composedPath().includes(r)) return\n e.stopPropagation()\n if (e.key === 'Escape' || (e.key === '?' && !chord(e))) {\n e.preventDefault()\n close(true)\n } else if (e.key === 'Tab') {\n const box = scrim.firstElementChild as HTMLElement\n const list = [...box.querySelectorAll<HTMLElement>(TABBABLE)]\n const active = (r.getRootNode() as Document | ShadowRoot).activeElement\n const first = list[0]\n const last = list[list.length - 1]\n const to = e.shiftKey\n ? active === first || active === box\n ? last\n : null\n : active === last\n ? first\n : null\n if (!first || to) {\n e.preventDefault()\n to?.focus()\n }\n }\n }\n // A press on the page outside the viewer closes it, leaving focus where the press puts it (a\n // press on the scrim closes it too, with focus back where it was: see open()).\n // (A viewer gone with it open is outside too: its listeners go with the next press or key.)\n const onPress = (e: Event) => {\n if (!e.composedPath().includes(root() as EventTarget)) close()\n }\n\n const open = (keys = !coarse()): boolean => {\n const r = root()\n if (!r || !anchor?.isConnected || viewer.getState().phase !== 'open') return false\n close()\n back = (r.getRootNode() as Document | ShadowRoot).activeElement as HTMLElement | null\n const s = el('div', 'gallerist-shortcuts-scrim')\n const box = el('div', 'gallerist-shortcuts')\n box.setAttribute('role', 'dialog')\n box.setAttribute('aria-modal', 'true')\n box.setAttribute('aria-labelledby', id)\n box.tabIndex = -1\n box.setAttribute('data-gallerist-list', keys ? 'keys' : 'gestures')\n const head = el('div', 'gallerist-shortcuts-head')\n if (keys) head.innerHTML = KEYBOARD\n const title = head.appendChild(el('h2', '', keys ? t.title : t.gestures))\n title.id = id\n const x = head.appendChild(el('button', 'gallerist-shortcuts-close'))\n x.type = 'button'\n if (keys) {\n const [before, after] = t.toClose.split('{key}')\n x.append(before as string, el('kbd', '', t.esc), after ?? '')\n } else {\n x.innerHTML = X\n x.setAttribute('aria-label', t.close)\n }\n x.addEventListener('click', () => close(true))\n // the list scrolls when the viewer is short: a stop for the keys, so they can scroll it (no\n // role or name of its own: the dialog's title names it already, once)\n const body = el('div', 'gallerist-shortcuts-body')\n body.tabIndex = 0\n for (const [name, list] of rows(keys)) {\n const section = body.appendChild(el('section'))\n if (name) section.appendChild(el('h3', '', name))\n const dl = section.appendChild(el('dl'))\n for (const [label, what] of list) {\n const row = dl.appendChild(el('div', 'gallerist-shortcuts-row'))\n row.appendChild(el('dt', '', label))\n const dd = row.appendChild(el('dd'))\n if (typeof what === 'string') dd.textContent = what\n else for (const k of what) dd.appendChild(el('kbd', '', k))\n }\n }\n box.append(head, body)\n if (!keys) box.appendChild(el('p', 'gallerist-shortcuts-note', t.keysNote))\n s.appendChild(box)\n // the scrim takes no focus (the viewer's root would, under a press), and a click on it closes\n s.addEventListener('mousedown', (e) => e.target === s && e.preventDefault())\n s.addEventListener('click', (e) => e.target === s && close(true))\n // the rest of the viewer is inert while the dialog shows (what was inert already stays so)\n for (const node of r.children)\n if (!node.hasAttribute('inert')) {\n node.setAttribute('inert', '')\n inerted.push(node)\n }\n r.appendChild(s)\n scrim = s\n window.addEventListener('keydown', onKey, true)\n document.addEventListener('pointerdown', onPress, true)\n box.focus({ preventScroll: true })\n return true\n }\n\n const instance: ShortcutsInstance = {\n name: 'shortcuts',\n get label() {\n return coarse() ? t.gestures : t.title\n },\n get isOpen() {\n return !!scrim\n },\n open,\n close: () => close(true),\n init() {\n live.push(instance)\n unsub = viewer.subscribe((st) => {\n if (st.phase === 'closing' || st.phase === 'closed') close()\n })\n },\n destroy() {\n close()\n unsub?.()\n unsub = anchor = null\n const at = live.indexOf(instance)\n if (at >= 0) live.splice(at, 1)\n },\n keymap: { '?': () => open(true) },\n // a hidden node, to find the viewer's root by (marked off: a bar shows not for it)\n regions: {\n toolbar: () => {\n anchor = el('span')\n anchor.hidden = true\n anchor.setAttribute('data-gallerist-off', '')\n return anchor\n },\n },\n }\n return instance\n }\n return Object.assign(plugin, {\n open: (keys?: boolean) => live.reduceRight((done, i) => done || i.open(keys), false),\n close: () => {\n for (const i of live) i.close()\n },\n })\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,68 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gallerist/plugin-shortcuts",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Gallerist plugin — a keyboard shortcut list on ?: the keys that work in the viewer as it is laid out (and the gestures on a touch screen), with the host's own, in a modal dialog.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"LICENSE",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"require": {
|
|
23
|
+
"types": "./dist/index.d.cts",
|
|
24
|
+
"default": "./dist/index.cjs"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@gallerist/core": ">=0.3.0 <1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsup": "^8.3.5",
|
|
33
|
+
"typescript": "^5.7.2",
|
|
34
|
+
"@gallerist/core": "0.4.0"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/shafayet9780/gallerist.git",
|
|
39
|
+
"directory": "packages/plugin-shortcuts"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/shafayet9780/gallerist#readme",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/shafayet9780/gallerist/issues"
|
|
44
|
+
},
|
|
45
|
+
"author": "Shafayet Ahmmed",
|
|
46
|
+
"keywords": [
|
|
47
|
+
"lightbox",
|
|
48
|
+
"gallery",
|
|
49
|
+
"image-viewer",
|
|
50
|
+
"media-viewer",
|
|
51
|
+
"photo-gallery",
|
|
52
|
+
"carousel",
|
|
53
|
+
"keyboard-shortcuts",
|
|
54
|
+
"accessibility",
|
|
55
|
+
"overlays",
|
|
56
|
+
"headless",
|
|
57
|
+
"framework-agnostic",
|
|
58
|
+
"virtualized",
|
|
59
|
+
"nested-gallery"
|
|
60
|
+
],
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "tsup",
|
|
66
|
+
"typecheck": "tsc --noEmit"
|
|
67
|
+
}
|
|
6
68
|
}
|