@riebeckite/plugin-lightbox 0.0.13 → 0.0.15
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/README.md +9 -2
- package/README_ja.md +5 -1
- package/dist/client.d.ts +2 -0
- package/dist/client.js +52 -9
- package/dist/index.d.ts +2 -1
- package/dist/index.js +63 -10
- package/dist/src/dialog.d.ts +4 -1
- package/dist/src/types.d.ts +4 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -36,6 +36,8 @@ the app calls on page initialization.
|
|
|
36
36
|
- Adds `rr-lightbox-image` to the image
|
|
37
37
|
- Skips images that have `data-lightbox-ignore="true"`, and images already
|
|
38
38
|
inside an `<a>`, `<button>`, existing trigger, or the dialog
|
|
39
|
+
- Uses `expandLabel` (default `"Expand image"`) for the trigger and dialog
|
|
40
|
+
accessible labels
|
|
39
41
|
|
|
40
42
|
### Client (`initLightbox`)
|
|
41
43
|
|
|
@@ -54,9 +56,11 @@ the app calls on page initialization.
|
|
|
54
56
|
| Option | Type | Default | Description |
|
|
55
57
|
| ------ | ---- | ------- | ----------- |
|
|
56
58
|
| `selectorClass` | `string` | `"rr-lightbox-trigger"` | Trigger class (build and client) |
|
|
59
|
+
| `expandLabel` | `string` | `"Expand image"` | Accessible label for triggers and the dialog |
|
|
60
|
+
| `closeLabel` | `string` | `"Close"` | Accessible label for the dialog close button |
|
|
57
61
|
| `autoWrapImages` | `boolean` | `true` | Client-only: wrap unhandled images on init |
|
|
58
62
|
|
|
59
|
-
`LightboxOptions` = `{ selectorClass? }` (build),
|
|
63
|
+
`LightboxOptions` = `{ selectorClass?, expandLabel?, closeLabel? }` (build),
|
|
60
64
|
`LightboxInitOptions` = `LightboxOptions & { autoWrapImages? }` (client).
|
|
61
65
|
|
|
62
66
|
## Exports
|
|
@@ -65,8 +69,11 @@ the app calls on page initialization.
|
|
|
65
69
|
- `rehypeLightbox(options?)` — rehype transform
|
|
66
70
|
- `initLightbox(root?, options?)` — client initializer, returns a cleanup
|
|
67
71
|
function
|
|
72
|
+
- `initLightboxFromOptions(options?)` — option-first browser entry the plugin's
|
|
73
|
+
client script calls; wraps `initLightbox`
|
|
68
74
|
- Types: `LightboxOptions`, `LightboxInitOptions`
|
|
69
75
|
|
|
70
76
|
## See also
|
|
71
77
|
|
|
72
|
-
- [Plugin guide](../../../docs/en/reference/plugin-api.md)
|
|
78
|
+
- [Plugin guide](../../../docs/en/docs/reference/plugin-api.md)
|
|
79
|
+
|
package/README_ja.md
CHANGED
|
@@ -30,6 +30,8 @@ export default defineConfig({
|
|
|
30
30
|
| オプション | 型 | 既定値 | 内容 |
|
|
31
31
|
| --- | --- | --- | --- |
|
|
32
32
|
| `selectorClass` | `string` | `"rr-lightbox-trigger"` | トリガーに使う CSS クラス |
|
|
33
|
+
| `expandLabel` | `string` | `"Expand image"` | トリガーとダイアログのアクセシブルラベル |
|
|
34
|
+
| `closeLabel` | `string` | `"Close"` | 閉じるボタンのアクセシブルラベル |
|
|
33
35
|
| `autoWrapImages` | `boolean` | `true` | 初期化時に未処理の画像も囲むか。クライアント側だけの設定 |
|
|
34
36
|
|
|
35
37
|
`initLightbox()` はイベントリスナー、ダイアログ、追加したトリガーを解除する cleanup 関数を返します。
|
|
@@ -39,8 +41,10 @@ export default defineConfig({
|
|
|
39
41
|
- `lightboxPlugin(options?)` — プラグインファクトリ
|
|
40
42
|
- `rehypeLightbox(options?)` — Rehype 変換
|
|
41
43
|
- `initLightbox(root?, options?)` — ブラウザ初期化関数
|
|
44
|
+
- `initLightboxFromOptions(options?)` — プラグインのクライアントスクリプトが呼ぶ、オプションを先に取る初期化関数
|
|
42
45
|
- `LightboxOptions`、`LightboxInitOptions` — 型
|
|
43
46
|
|
|
44
47
|
## 関連資料
|
|
45
48
|
|
|
46
|
-
- [プラグインシステム](../../../docs/ja/reference/plugin-api.md)
|
|
49
|
+
- [プラグインシステム](../../../docs/ja/docs/reference/plugin-api.md)
|
|
50
|
+
|
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// src/dialog.ts
|
|
2
|
-
function createDialog() {
|
|
2
|
+
function createDialog(options) {
|
|
3
3
|
const element = document.createElement("div");
|
|
4
4
|
element.className = "rr-lightbox";
|
|
5
5
|
element.setAttribute("role", "dialog");
|
|
6
6
|
element.setAttribute("aria-modal", "true");
|
|
7
|
-
element.setAttribute("aria-label",
|
|
7
|
+
element.setAttribute("aria-label", options.expandLabel);
|
|
8
|
+
element.tabIndex = -1;
|
|
8
9
|
element.setAttribute("hidden", "");
|
|
9
10
|
const frame = document.createElement("figure");
|
|
10
11
|
frame.className = "rr-lightbox__frame";
|
|
@@ -16,7 +17,7 @@ function createDialog() {
|
|
|
16
17
|
const closeButton = document.createElement("button");
|
|
17
18
|
closeButton.className = "rr-lightbox__close";
|
|
18
19
|
closeButton.type = "button";
|
|
19
|
-
closeButton.setAttribute("aria-label",
|
|
20
|
+
closeButton.setAttribute("aria-label", options.closeLabel);
|
|
20
21
|
closeButton.textContent = "×";
|
|
21
22
|
frame.appendChild(image);
|
|
22
23
|
frame.appendChild(caption);
|
|
@@ -27,11 +28,15 @@ function createDialog() {
|
|
|
27
28
|
|
|
28
29
|
// src/types.ts
|
|
29
30
|
var DEFAULT_TRIGGER_CLASS = "rr-lightbox-trigger";
|
|
31
|
+
var DEFAULT_EXPAND_LABEL = "Expand image";
|
|
32
|
+
var DEFAULT_CLOSE_LABEL = "Close";
|
|
30
33
|
|
|
31
34
|
// src/init.ts
|
|
32
35
|
function initLightbox(root = document, options = {}) {
|
|
33
36
|
const triggerClass = options.selectorClass ?? DEFAULT_TRIGGER_CLASS;
|
|
34
|
-
const
|
|
37
|
+
const expandLabel = options.expandLabel ?? DEFAULT_EXPAND_LABEL;
|
|
38
|
+
const closeLabel = options.closeLabel ?? DEFAULT_CLOSE_LABEL;
|
|
39
|
+
const wrappedTriggers = options.autoWrapImages === false ? [] : wrapImages(root, triggerClass, expandLabel);
|
|
35
40
|
const triggers = Array.from(
|
|
36
41
|
root.querySelectorAll(`.${triggerClass}`)
|
|
37
42
|
);
|
|
@@ -41,7 +46,7 @@ function initLightbox(root = document, options = {}) {
|
|
|
41
46
|
};
|
|
42
47
|
}
|
|
43
48
|
let previouslyFocused = null;
|
|
44
|
-
const dialog = createDialog();
|
|
49
|
+
const dialog = createDialog({ expandLabel, closeLabel });
|
|
45
50
|
document.body.appendChild(dialog.element);
|
|
46
51
|
const open = (trigger) => {
|
|
47
52
|
const src = trigger.dataset.lightboxSrc ?? trigger.href;
|
|
@@ -73,7 +78,12 @@ function initLightbox(root = document, options = {}) {
|
|
|
73
78
|
}
|
|
74
79
|
};
|
|
75
80
|
const handleKeyDown = (event) => {
|
|
76
|
-
if (dialog.element.hidden
|
|
81
|
+
if (dialog.element.hidden) return;
|
|
82
|
+
if (event.key === "Tab") {
|
|
83
|
+
trapFocus(event, dialog.element);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (event.key !== "Escape") return;
|
|
77
87
|
event.preventDefault();
|
|
78
88
|
close();
|
|
79
89
|
};
|
|
@@ -93,7 +103,34 @@ function initLightbox(root = document, options = {}) {
|
|
|
93
103
|
for (const trigger of wrappedTriggers) unwrapImage(trigger);
|
|
94
104
|
};
|
|
95
105
|
}
|
|
96
|
-
function
|
|
106
|
+
function trapFocus(event, container) {
|
|
107
|
+
const focusableElements = getFocusableElements(container);
|
|
108
|
+
if (focusableElements.length === 0) {
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
container.focus();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const first = focusableElements[0];
|
|
114
|
+
const last = focusableElements[focusableElements.length - 1];
|
|
115
|
+
const active = document.activeElement;
|
|
116
|
+
if (event.shiftKey && active === first) {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
last.focus();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (!event.shiftKey && active === last) {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
first.focus();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function getFocusableElements(container) {
|
|
127
|
+
return Array.from(
|
|
128
|
+
container.querySelectorAll(
|
|
129
|
+
'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
130
|
+
)
|
|
131
|
+
).filter((element) => !element.hasAttribute("disabled"));
|
|
132
|
+
}
|
|
133
|
+
function wrapImages(root, triggerClass, expandLabel) {
|
|
97
134
|
const images = Array.from(
|
|
98
135
|
root.querySelectorAll("img[src]")
|
|
99
136
|
);
|
|
@@ -112,7 +149,7 @@ function wrapImages(root, triggerClass) {
|
|
|
112
149
|
trigger.dataset.lightboxAlt = image.alt;
|
|
113
150
|
trigger.setAttribute(
|
|
114
151
|
"aria-label",
|
|
115
|
-
image.alt ?
|
|
152
|
+
image.alt ? `${expandLabel}: ${image.alt}` : expandLabel
|
|
116
153
|
);
|
|
117
154
|
image.parentNode?.insertBefore(trigger, image);
|
|
118
155
|
trigger.appendChild(image);
|
|
@@ -127,6 +164,12 @@ function unwrapImage(trigger) {
|
|
|
127
164
|
trigger.parentNode.insertBefore(image, trigger);
|
|
128
165
|
trigger.remove();
|
|
129
166
|
}
|
|
167
|
+
|
|
168
|
+
// client.ts
|
|
169
|
+
function initLightboxFromOptions(options = {}) {
|
|
170
|
+
return initLightbox(document, options);
|
|
171
|
+
}
|
|
130
172
|
export {
|
|
131
|
-
initLightbox
|
|
173
|
+
initLightbox,
|
|
174
|
+
initLightboxFromOptions
|
|
132
175
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type LightboxOptions } from "./src/types.js";
|
|
2
|
+
export { initLightboxFromOptions } from "./client.js";
|
|
2
3
|
export { initLightbox } from "./src/init.js";
|
|
3
4
|
export { rehypeLightbox } from "./src/rehype.js";
|
|
4
5
|
export type { LightboxInitOptions, LightboxOptions } from "./src/types.js";
|
package/dist/index.js
CHANGED
|
@@ -38,10 +38,13 @@ function getChildren(node) {
|
|
|
38
38
|
|
|
39
39
|
// src/types.ts
|
|
40
40
|
var DEFAULT_TRIGGER_CLASS = "rr-lightbox-trigger";
|
|
41
|
+
var DEFAULT_EXPAND_LABEL = "Expand image";
|
|
42
|
+
var DEFAULT_CLOSE_LABEL = "Close";
|
|
41
43
|
|
|
42
44
|
// src/rehype.ts
|
|
43
45
|
function rehypeLightbox(options = {}) {
|
|
44
46
|
const triggerClass = options.selectorClass ?? DEFAULT_TRIGGER_CLASS;
|
|
47
|
+
const expandLabel = options.expandLabel ?? DEFAULT_EXPAND_LABEL;
|
|
45
48
|
return (tree) => {
|
|
46
49
|
visitElements(tree, (node, parent, index) => {
|
|
47
50
|
if (!parent || index === void 0 || node.tagName !== "img") return;
|
|
@@ -63,7 +66,7 @@ function rehypeLightbox(options = {}) {
|
|
|
63
66
|
href: src,
|
|
64
67
|
dataLightboxSrc: src,
|
|
65
68
|
dataLightboxAlt: alt,
|
|
66
|
-
ariaLabel: alt ?
|
|
69
|
+
ariaLabel: alt ? `${expandLabel}: ${alt}` : expandLabel
|
|
67
70
|
},
|
|
68
71
|
children: [
|
|
69
72
|
{
|
|
@@ -80,12 +83,13 @@ function rehypeLightbox(options = {}) {
|
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
// src/dialog.ts
|
|
83
|
-
function createDialog() {
|
|
86
|
+
function createDialog(options) {
|
|
84
87
|
const element = document.createElement("div");
|
|
85
88
|
element.className = "rr-lightbox";
|
|
86
89
|
element.setAttribute("role", "dialog");
|
|
87
90
|
element.setAttribute("aria-modal", "true");
|
|
88
|
-
element.setAttribute("aria-label",
|
|
91
|
+
element.setAttribute("aria-label", options.expandLabel);
|
|
92
|
+
element.tabIndex = -1;
|
|
89
93
|
element.setAttribute("hidden", "");
|
|
90
94
|
const frame = document.createElement("figure");
|
|
91
95
|
frame.className = "rr-lightbox__frame";
|
|
@@ -97,7 +101,7 @@ function createDialog() {
|
|
|
97
101
|
const closeButton = document.createElement("button");
|
|
98
102
|
closeButton.className = "rr-lightbox__close";
|
|
99
103
|
closeButton.type = "button";
|
|
100
|
-
closeButton.setAttribute("aria-label",
|
|
104
|
+
closeButton.setAttribute("aria-label", options.closeLabel);
|
|
101
105
|
closeButton.textContent = "×";
|
|
102
106
|
frame.appendChild(image);
|
|
103
107
|
frame.appendChild(caption);
|
|
@@ -109,7 +113,9 @@ function createDialog() {
|
|
|
109
113
|
// src/init.ts
|
|
110
114
|
function initLightbox(root = document, options = {}) {
|
|
111
115
|
const triggerClass = options.selectorClass ?? DEFAULT_TRIGGER_CLASS;
|
|
112
|
-
const
|
|
116
|
+
const expandLabel = options.expandLabel ?? DEFAULT_EXPAND_LABEL;
|
|
117
|
+
const closeLabel = options.closeLabel ?? DEFAULT_CLOSE_LABEL;
|
|
118
|
+
const wrappedTriggers = options.autoWrapImages === false ? [] : wrapImages(root, triggerClass, expandLabel);
|
|
113
119
|
const triggers = Array.from(
|
|
114
120
|
root.querySelectorAll(`.${triggerClass}`)
|
|
115
121
|
);
|
|
@@ -119,7 +125,7 @@ function initLightbox(root = document, options = {}) {
|
|
|
119
125
|
};
|
|
120
126
|
}
|
|
121
127
|
let previouslyFocused = null;
|
|
122
|
-
const dialog = createDialog();
|
|
128
|
+
const dialog = createDialog({ expandLabel, closeLabel });
|
|
123
129
|
document.body.appendChild(dialog.element);
|
|
124
130
|
const open = (trigger) => {
|
|
125
131
|
const src = trigger.dataset.lightboxSrc ?? trigger.href;
|
|
@@ -151,7 +157,12 @@ function initLightbox(root = document, options = {}) {
|
|
|
151
157
|
}
|
|
152
158
|
};
|
|
153
159
|
const handleKeyDown = (event) => {
|
|
154
|
-
if (dialog.element.hidden
|
|
160
|
+
if (dialog.element.hidden) return;
|
|
161
|
+
if (event.key === "Tab") {
|
|
162
|
+
trapFocus(event, dialog.element);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (event.key !== "Escape") return;
|
|
155
166
|
event.preventDefault();
|
|
156
167
|
close();
|
|
157
168
|
};
|
|
@@ -171,7 +182,34 @@ function initLightbox(root = document, options = {}) {
|
|
|
171
182
|
for (const trigger of wrappedTriggers) unwrapImage(trigger);
|
|
172
183
|
};
|
|
173
184
|
}
|
|
174
|
-
function
|
|
185
|
+
function trapFocus(event, container) {
|
|
186
|
+
const focusableElements = getFocusableElements(container);
|
|
187
|
+
if (focusableElements.length === 0) {
|
|
188
|
+
event.preventDefault();
|
|
189
|
+
container.focus();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const first = focusableElements[0];
|
|
193
|
+
const last = focusableElements[focusableElements.length - 1];
|
|
194
|
+
const active = document.activeElement;
|
|
195
|
+
if (event.shiftKey && active === first) {
|
|
196
|
+
event.preventDefault();
|
|
197
|
+
last.focus();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (!event.shiftKey && active === last) {
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
first.focus();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function getFocusableElements(container) {
|
|
206
|
+
return Array.from(
|
|
207
|
+
container.querySelectorAll(
|
|
208
|
+
'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
209
|
+
)
|
|
210
|
+
).filter((element) => !element.hasAttribute("disabled"));
|
|
211
|
+
}
|
|
212
|
+
function wrapImages(root, triggerClass, expandLabel) {
|
|
175
213
|
const images = Array.from(
|
|
176
214
|
root.querySelectorAll("img[src]")
|
|
177
215
|
);
|
|
@@ -190,7 +228,7 @@ function wrapImages(root, triggerClass) {
|
|
|
190
228
|
trigger.dataset.lightboxAlt = image.alt;
|
|
191
229
|
trigger.setAttribute(
|
|
192
230
|
"aria-label",
|
|
193
|
-
image.alt ?
|
|
231
|
+
image.alt ? `${expandLabel}: ${image.alt}` : expandLabel
|
|
194
232
|
);
|
|
195
233
|
image.parentNode?.insertBefore(trigger, image);
|
|
196
234
|
trigger.appendChild(image);
|
|
@@ -206,20 +244,35 @@ function unwrapImage(trigger) {
|
|
|
206
244
|
trigger.remove();
|
|
207
245
|
}
|
|
208
246
|
|
|
247
|
+
// client.ts
|
|
248
|
+
function initLightboxFromOptions(options = {}) {
|
|
249
|
+
return initLightbox(document, options);
|
|
250
|
+
}
|
|
251
|
+
|
|
209
252
|
// index.ts
|
|
210
253
|
function lightboxPlugin(options = {}) {
|
|
211
254
|
return definePlugin({
|
|
212
255
|
name: "lightbox",
|
|
256
|
+
processedContentCache: {
|
|
257
|
+
version: "lightbox-v2",
|
|
258
|
+
dependencyMode: "none"
|
|
259
|
+
},
|
|
213
260
|
options,
|
|
214
261
|
extendHtmlPipeline: (pipeline) => {
|
|
215
262
|
pipeline.use(rehypeLightbox, options);
|
|
216
263
|
},
|
|
217
264
|
assets: [createStyleAsset("lightbox")],
|
|
218
|
-
clientEntries: [
|
|
265
|
+
clientEntries: [
|
|
266
|
+
createClientEntry("lightbox", "initLightboxFromOptions", {
|
|
267
|
+
expandLabel: options.expandLabel ?? DEFAULT_EXPAND_LABEL,
|
|
268
|
+
closeLabel: options.closeLabel ?? DEFAULT_CLOSE_LABEL
|
|
269
|
+
})
|
|
270
|
+
]
|
|
219
271
|
});
|
|
220
272
|
}
|
|
221
273
|
export {
|
|
222
274
|
initLightbox,
|
|
275
|
+
initLightboxFromOptions,
|
|
223
276
|
lightboxPlugin,
|
|
224
277
|
rehypeLightbox
|
|
225
278
|
};
|
package/dist/src/dialog.d.ts
CHANGED
package/dist/src/types.d.ts
CHANGED
|
@@ -10,8 +10,12 @@ export type HastNode = ElementNode | {
|
|
|
10
10
|
};
|
|
11
11
|
export type LightboxOptions = {
|
|
12
12
|
selectorClass?: string;
|
|
13
|
+
expandLabel?: string;
|
|
14
|
+
closeLabel?: string;
|
|
13
15
|
};
|
|
14
16
|
export type LightboxInitOptions = LightboxOptions & {
|
|
15
17
|
autoWrapImages?: boolean;
|
|
16
18
|
};
|
|
17
19
|
export declare const DEFAULT_TRIGGER_CLASS = "rr-lightbox-trigger";
|
|
20
|
+
export declare const DEFAULT_EXPAND_LABEL = "Expand image";
|
|
21
|
+
export declare const DEFAULT_CLOSE_LABEL = "Close";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-lightbox",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"./style.css": "./style.css"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@riebeckite/core": "0.0.
|
|
24
|
+
"@riebeckite/core": "0.0.15"
|
|
25
25
|
},
|
|
26
26
|
"publishConfig": {
|
|
27
27
|
"access": "public"
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"./style.css"
|
|
57
57
|
],
|
|
58
58
|
"scripts": {
|
|
59
|
-
"build": "node ../../../scripts/build_package.mjs"
|
|
59
|
+
"build": "node ../../../scripts/build_package.mjs",
|
|
60
|
+
"test": "node --import tsx --test \"test/*.test.ts\""
|
|
60
61
|
}
|
|
61
62
|
}
|