jaamd 0.1.14 → 0.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jaamd",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Just Another Astro Markdown",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -3,32 +3,18 @@
3
3
  *
4
4
  * Plain ES module — bundled by Vite as part of the Astro build.
5
5
  * No external runtime dependencies.
6
+ *
7
+ * Each feature lives in its own module; this file is the public entry point
8
+ * that wires them all together via `initMarkdownEnhancements`.
6
9
  */
7
10
 
8
- // ─── Utilities ────────────────────────────────────────────────────────────────
9
-
10
- function slugify(text: string): string {
11
- return text
12
- .toLowerCase()
13
- .trim()
14
- .replace(/[^\w\s-]/g, "")
15
- .replace(/[\s_]+/g, "-")
16
- .replace(/^-+|-+$/g, "");
17
- }
18
-
19
- function qs<T extends Element = Element>(
20
- root: ParentNode,
21
- sel: string,
22
- ): T | null {
23
- return root.querySelector<T>(sel);
24
- }
25
-
26
- function qsa<T extends Element = Element>(
27
- root: ParentNode,
28
- sel: string,
29
- ): NodeListOf<T> {
30
- return root.querySelectorAll<T>(sel);
31
- }
11
+ import { addHeadingLinks } from "./modules/heading-links.js";
12
+ import { addCopyButtons } from "./modules/copy-buttons.js";
13
+ import { addImageLightbox } from "./modules/lightbox.js";
14
+ import { wrapTables } from "./modules/tables.js";
15
+ import { initCodeTabs } from "./modules/code-tabs.js";
16
+ import { initSpoilers } from "./modules/spoilers.js";
17
+ import { initDetails } from "./modules/details.js";
32
18
 
33
19
  // ─── Public API ───────────────────────────────────────────────────────────────
34
20
 
@@ -42,241 +28,4 @@ export function initMarkdownEnhancements(
42
28
  initCodeTabs();
43
29
  initSpoilers(selector);
44
30
  initDetails(selector);
45
- }
46
-
47
- // ─── Heading anchor links ─────────────────────────────────────────────────────
48
-
49
- function addHeadingLinks(selector: string): void {
50
- qsa<HTMLElement>(document, `${selector} h1, ${selector} h2, ${selector} h3`).forEach(
51
- (header) => {
52
- if (!header.id) header.id = slugify(header.textContent ?? "");
53
- if (qs(header, ".jaamd-heading-link")) return;
54
-
55
- const a = document.createElement("a");
56
- a.className = "jaamd-heading-link";
57
- a.setAttribute("aria-label", `Link to section: ${header.textContent}`);
58
- a.setAttribute("href", `#${header.id}`);
59
- a.title = "Copy link";
60
- a.innerHTML =
61
- `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" ` +
62
- `stroke="currentColor" stroke-width="2" stroke-linecap="round" ` +
63
- `stroke-linejoin="round" width="1em" height="1em" aria-hidden="true">` +
64
- `<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>` +
65
- `<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>` +
66
- `</svg>`;
67
-
68
- a.addEventListener("click", async (e) => {
69
- e.preventDefault();
70
- const url = `${location.origin}${location.pathname}#${header.id}`;
71
- try {
72
- await navigator.clipboard.writeText(url);
73
- } catch {
74
- // clipboard not available (e.g. non-secure context) — fail silently
75
- }
76
- });
77
-
78
- header.appendChild(a);
79
- },
80
- );
81
- }
82
-
83
- // ─── Code copy buttons ────────────────────────────────────────────────────────
84
-
85
- function addCopyButtons(selector: string): void {
86
- qsa<HTMLElement>(document, `${selector} pre`).forEach((pre) => {
87
- if (qs(pre, ".jaamd-copy-btn")) return;
88
- const code = qs<HTMLElement>(pre, "code");
89
- if (!code) return;
90
-
91
- const btn = document.createElement("button");
92
- btn.className = "jaamd-copy-btn";
93
- btn.title = "Copy code";
94
- btn.setAttribute("aria-label", "Copy code to clipboard");
95
- btn.innerHTML = iconCopy();
96
-
97
- btn.addEventListener("click", async () => {
98
- try {
99
- await navigator.clipboard.writeText(code.textContent ?? "");
100
- const orig = btn.innerHTML;
101
- btn.innerHTML = iconCheck();
102
- btn.style.color = "var(--jaamd-color-success, #22c55e)";
103
- setTimeout(() => {
104
- btn.innerHTML = orig;
105
- btn.style.color = "";
106
- }, 2000);
107
- } catch {
108
- // fail silently
109
- }
110
- });
111
-
112
- pre.appendChild(btn);
113
- });
114
- }
115
-
116
- function iconCopy(): string {
117
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="1em" height="1em" aria-hidden="true"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`;
118
- }
119
-
120
- function iconCheck(): string {
121
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="1em" height="1em" aria-hidden="true"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
122
- }
123
-
124
- // ─── Image lightbox ───────────────────────────────────────────────────────────
125
-
126
- function addImageLightbox(selector: string): void {
127
- qsa<HTMLImageElement>(document, `${selector} img`).forEach((img) => {
128
- if (img.classList.contains("jaamd-lightbox-enabled")) return;
129
- img.classList.add("jaamd-lightbox-enabled");
130
- img.style.cursor = "pointer";
131
- img.title = img.alt || "Click to enlarge";
132
- img.addEventListener("click", () => openLightbox(img.src, img.alt));
133
- });
134
- }
135
-
136
- function openLightbox(src: string, alt: string): void {
137
- let lb = document.getElementById("jaamd-lightbox");
138
-
139
- if (!lb) {
140
- lb = document.createElement("div");
141
- lb.id = "jaamd-lightbox";
142
- lb.className = "jaamd-lightbox jaamd-lightbox--hidden";
143
- lb.innerHTML =
144
- `<div class="jaamd-lightbox__backdrop"></div>` +
145
- `<div class="jaamd-lightbox__content">` +
146
- `<button class="jaamd-lightbox__close" aria-label="Close lightbox">` +
147
- `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="1em" height="1em" aria-hidden="true">` +
148
- `<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>` +
149
- `</button>` +
150
- `<img class="jaamd-lightbox__img" src="" alt="" />` +
151
- `</div>`;
152
- document.body.appendChild(lb);
153
-
154
- const close = (): void => {
155
- lb!.classList.add("jaamd-lightbox--hidden");
156
- document.body.style.overflow = "";
157
- };
158
-
159
- qs(lb, ".jaamd-lightbox__backdrop")!.addEventListener("click", close);
160
- qs(lb, ".jaamd-lightbox__close")!.addEventListener("click", close);
161
- document.addEventListener("keydown", (e) => {
162
- if (e.key === "Escape" && !lb!.classList.contains("jaamd-lightbox--hidden"))
163
- close();
164
- });
165
- }
166
-
167
- (qs<HTMLImageElement>(lb, ".jaamd-lightbox__img")!).src = src;
168
- (qs<HTMLImageElement>(lb, ".jaamd-lightbox__img")!).alt = alt ?? "";
169
- lb.classList.remove("jaamd-lightbox--hidden");
170
- document.body.style.overflow = "hidden";
171
- }
172
-
173
- // ─── Responsive table wrapper ─────────────────────────────────────────────────
174
-
175
- function wrapTables(selector: string): void {
176
- qsa<HTMLTableElement>(document, `${selector} table`).forEach((table) => {
177
- if (table.parentElement?.classList.contains("jaamd-table-wrapper")) return;
178
- const wrapper = document.createElement("div");
179
- wrapper.className = "jaamd-table-wrapper";
180
- table.parentNode!.insertBefore(wrapper, table);
181
- wrapper.appendChild(table);
182
- });
183
- }
184
-
185
- // ─── Code tabs ────────────────────────────────────────────────────────────────
186
-
187
- function initCodeTabs(): void {
188
- qsa<HTMLElement>(document, ".code-tabs").forEach((group) => {
189
- if (group.dataset.tabsInit) return;
190
- group.dataset.tabsInit = "1";
191
-
192
- qsa<HTMLButtonElement>(group, ".code-tab-btn").forEach((btn) => {
193
- btn.addEventListener("click", () => {
194
- const idx = btn.dataset.tabIndex;
195
- qsa(group, ".code-tab-btn").forEach((b) =>
196
- b.classList.remove("active"),
197
- );
198
- qsa(group, ".code-tab-panel").forEach((p) =>
199
- p.classList.remove("active"),
200
- );
201
- btn.classList.add("active");
202
- qs(group, `.code-tab-panel[data-tab-index="${idx}"]`)?.classList.add(
203
- "active",
204
- );
205
- });
206
- });
207
- });
208
- }
209
-
210
- // ─── Spoilers ─────────────────────────────────────────────────────────────────
211
-
212
- function initSpoilers(selector: string): void {
213
- qsa<HTMLElement>(document, `${selector} .spoiler`).forEach((el) => {
214
- if (el.dataset.spoilerInit) return;
215
- el.dataset.spoilerInit = "1";
216
- el.addEventListener("click", () => el.classList.toggle("revealed"));
217
- });
218
- }
219
-
220
- // ─── Details / Summary animation ─────────────────────────────────────────────
221
-
222
- function initDetails(selector: string): void {
223
- qsa<HTMLDetailsElement>(document, `${selector} details`).forEach(
224
- (details) => {
225
- if (details.dataset.detailsInit) return;
226
- details.dataset.detailsInit = "1";
227
-
228
- const summary = qs<HTMLElement>(details, "summary");
229
- if (!summary) return;
230
-
231
- // Two-level wrap:
232
- // wrapper — animates height only
233
- // inner — holds padding so it's baked into scrollHeight (no jump)
234
- const wrapper = document.createElement("div");
235
- wrapper.className = "jaamd-details-wrapper";
236
- const inner = document.createElement("div");
237
- inner.className = "jaamd-details-inner";
238
-
239
- Array.from(details.childNodes).forEach((node) => {
240
- if (node !== summary) inner.appendChild(node);
241
- });
242
- wrapper.appendChild(inner);
243
- details.appendChild(wrapper);
244
-
245
- if (!details.open) {
246
- wrapper.style.height = "0px";
247
- wrapper.style.overflow = "hidden";
248
- }
249
-
250
- summary.addEventListener("click", (e) => {
251
- e.preventDefault();
252
-
253
- if (details.open) {
254
- // closing
255
- const startH = wrapper.scrollHeight;
256
- wrapper.style.overflow = "hidden";
257
- const anim = wrapper.animate(
258
- [{ height: `${startH}px` }, { height: "0px" }],
259
- { duration: 280, easing: "ease" },
260
- );
261
- anim.onfinish = () => {
262
- wrapper.style.height = "0px";
263
- details.removeAttribute("open");
264
- };
265
- } else {
266
- // opening
267
- details.setAttribute("open", "");
268
- const endH = wrapper.scrollHeight;
269
- wrapper.style.overflow = "hidden";
270
- const anim = wrapper.animate(
271
- [{ height: "0px" }, { height: `${endH}px` }],
272
- { duration: 280, easing: "ease" },
273
- );
274
- anim.onfinish = () => {
275
- wrapper.style.height = "auto";
276
- wrapper.style.overflow = "";
277
- };
278
- }
279
- });
280
- },
281
- );
282
31
  }
@@ -0,0 +1,26 @@
1
+ import { qs, qsa } from "../utils.js";
2
+
3
+ // ─── Code tabs ────────────────────────────────────────────────────────────────
4
+
5
+ export function initCodeTabs(): void {
6
+ qsa<HTMLElement>(document, ".code-tabs").forEach((group) => {
7
+ if (group.dataset.tabsInit) return;
8
+ group.dataset.tabsInit = "1";
9
+
10
+ qsa<HTMLButtonElement>(group, ".code-tab-btn").forEach((btn) => {
11
+ btn.addEventListener("click", () => {
12
+ const idx = btn.dataset.tabIndex;
13
+ qsa(group, ".code-tab-btn").forEach((b) =>
14
+ b.classList.remove("active"),
15
+ );
16
+ qsa(group, ".code-tab-panel").forEach((p) =>
17
+ p.classList.remove("active"),
18
+ );
19
+ btn.classList.add("active");
20
+ qs(group, `.code-tab-panel[data-tab-index="${idx}"]`)?.classList.add(
21
+ "active",
22
+ );
23
+ });
24
+ });
25
+ });
26
+ }
@@ -0,0 +1,42 @@
1
+ import { qs, qsa } from "../utils.js";
2
+
3
+ // ─── Code copy buttons ────────────────────────────────────────────────────────
4
+
5
+ export function addCopyButtons(selector: string): void {
6
+ qsa<HTMLElement>(document, `${selector} pre`).forEach((pre) => {
7
+ if (qs(pre, ".jaamd-copy-btn")) return;
8
+ const code = qs<HTMLElement>(pre, "code");
9
+ if (!code) return;
10
+
11
+ const btn = document.createElement("button");
12
+ btn.className = "jaamd-copy-btn";
13
+ btn.title = "Copy code";
14
+ btn.setAttribute("aria-label", "Copy code to clipboard");
15
+ btn.innerHTML = iconCopy();
16
+
17
+ btn.addEventListener("click", async () => {
18
+ try {
19
+ await navigator.clipboard.writeText(code.textContent ?? "");
20
+ const orig = btn.innerHTML;
21
+ btn.innerHTML = iconCheck();
22
+ btn.style.color = "var(--jaamd-color-success, #22c55e)";
23
+ setTimeout(() => {
24
+ btn.innerHTML = orig;
25
+ btn.style.color = "";
26
+ }, 2000);
27
+ } catch {
28
+ // fail silently
29
+ }
30
+ });
31
+
32
+ pre.appendChild(btn);
33
+ });
34
+ }
35
+
36
+ function iconCopy(): string {
37
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="1em" height="1em" aria-hidden="true"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`;
38
+ }
39
+
40
+ function iconCheck(): string {
41
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="1em" height="1em" aria-hidden="true"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
42
+ }
@@ -0,0 +1,65 @@
1
+ import { qs, qsa } from "../utils.js";
2
+
3
+ // ─── Details / Summary animation ─────────────────────────────────────────────
4
+
5
+ export function initDetails(selector: string): void {
6
+ qsa<HTMLDetailsElement>(document, `${selector} details`).forEach(
7
+ (details) => {
8
+ if (details.dataset.detailsInit) return;
9
+ details.dataset.detailsInit = "1";
10
+
11
+ const summary = qs<HTMLElement>(details, "summary");
12
+ if (!summary) return;
13
+
14
+ // Two-level wrap:
15
+ // wrapper — animates height only
16
+ // inner — holds padding so it's baked into scrollHeight (no jump)
17
+ const wrapper = document.createElement("div");
18
+ wrapper.className = "jaamd-details-wrapper";
19
+ const inner = document.createElement("div");
20
+ inner.className = "jaamd-details-inner";
21
+
22
+ Array.from(details.childNodes).forEach((node) => {
23
+ if (node !== summary) inner.appendChild(node);
24
+ });
25
+ wrapper.appendChild(inner);
26
+ details.appendChild(wrapper);
27
+
28
+ if (!details.open) {
29
+ wrapper.style.height = "0px";
30
+ wrapper.style.overflow = "hidden";
31
+ }
32
+
33
+ summary.addEventListener("click", (e) => {
34
+ e.preventDefault();
35
+
36
+ if (details.open) {
37
+ // closing
38
+ const startH = wrapper.scrollHeight;
39
+ wrapper.style.overflow = "hidden";
40
+ const anim = wrapper.animate(
41
+ [{ height: `${startH}px` }, { height: "0px" }],
42
+ { duration: 280, easing: "ease" },
43
+ );
44
+ anim.onfinish = () => {
45
+ wrapper.style.height = "0px";
46
+ details.removeAttribute("open");
47
+ };
48
+ } else {
49
+ // opening
50
+ details.setAttribute("open", "");
51
+ const endH = wrapper.scrollHeight;
52
+ wrapper.style.overflow = "hidden";
53
+ const anim = wrapper.animate(
54
+ [{ height: "0px" }, { height: `${endH}px` }],
55
+ { duration: 280, easing: "ease" },
56
+ );
57
+ anim.onfinish = () => {
58
+ wrapper.style.height = "auto";
59
+ wrapper.style.overflow = "";
60
+ };
61
+ }
62
+ });
63
+ },
64
+ );
65
+ }
@@ -0,0 +1,37 @@
1
+ import { qs, qsa, slugify } from "../utils.js";
2
+
3
+ // ─── Heading anchor links ─────────────────────────────────────────────────────
4
+
5
+ export function addHeadingLinks(selector: string): void {
6
+ qsa<HTMLElement>(document, `${selector} h1, ${selector} h2, ${selector} h3`).forEach(
7
+ (header) => {
8
+ if (!header.id) header.id = slugify(header.textContent ?? "");
9
+ if (qs(header, ".jaamd-heading-link")) return;
10
+
11
+ const a = document.createElement("a");
12
+ a.className = "jaamd-heading-link";
13
+ a.setAttribute("aria-label", `Link to section: ${header.textContent}`);
14
+ a.setAttribute("href", `#${header.id}`);
15
+ a.title = "Copy link";
16
+ a.innerHTML =
17
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" ` +
18
+ `stroke="currentColor" stroke-width="2" stroke-linecap="round" ` +
19
+ `stroke-linejoin="round" width="1em" height="1em" aria-hidden="true">` +
20
+ `<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>` +
21
+ `<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>` +
22
+ `</svg>`;
23
+
24
+ a.addEventListener("click", async (e) => {
25
+ e.preventDefault();
26
+ const url = `${location.origin}${location.pathname}#${header.id}`;
27
+ try {
28
+ await navigator.clipboard.writeText(url);
29
+ } catch {
30
+ // clipboard not available (e.g. non-secure context) — fail silently
31
+ }
32
+ });
33
+
34
+ header.appendChild(a);
35
+ },
36
+ );
37
+ }
@@ -0,0 +1,50 @@
1
+ import { qs, qsa } from "../utils.js";
2
+
3
+ // ─── Image lightbox ───────────────────────────────────────────────────────────
4
+
5
+ export function addImageLightbox(selector: string): void {
6
+ qsa<HTMLImageElement>(document, `${selector} img`).forEach((img) => {
7
+ if (img.classList.contains("jaamd-lightbox-enabled")) return;
8
+ img.classList.add("jaamd-lightbox-enabled");
9
+ img.style.cursor = "pointer";
10
+ img.title = img.alt || "Click to enlarge";
11
+ img.addEventListener("click", () => openLightbox(img.src, img.alt));
12
+ });
13
+ }
14
+
15
+ function openLightbox(src: string, alt: string): void {
16
+ let lb = document.getElementById("jaamd-lightbox");
17
+
18
+ if (!lb) {
19
+ lb = document.createElement("div");
20
+ lb.id = "jaamd-lightbox";
21
+ lb.className = "jaamd-lightbox jaamd-lightbox--hidden";
22
+ lb.innerHTML =
23
+ `<div class="jaamd-lightbox__backdrop"></div>` +
24
+ `<div class="jaamd-lightbox__content">` +
25
+ `<button class="jaamd-lightbox__close" aria-label="Close lightbox">` +
26
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="1em" height="1em" aria-hidden="true">` +
27
+ `<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>` +
28
+ `</button>` +
29
+ `<img class="jaamd-lightbox__img" src="" alt="" />` +
30
+ `</div>`;
31
+ document.body.appendChild(lb);
32
+
33
+ const close = (): void => {
34
+ lb!.classList.add("jaamd-lightbox--hidden");
35
+ document.body.style.overflow = "";
36
+ };
37
+
38
+ qs(lb, ".jaamd-lightbox__backdrop")!.addEventListener("click", close);
39
+ qs(lb, ".jaamd-lightbox__close")!.addEventListener("click", close);
40
+ document.addEventListener("keydown", (e) => {
41
+ if (e.key === "Escape" && !lb!.classList.contains("jaamd-lightbox--hidden"))
42
+ close();
43
+ });
44
+ }
45
+
46
+ (qs<HTMLImageElement>(lb, ".jaamd-lightbox__img")!).src = src;
47
+ (qs<HTMLImageElement>(lb, ".jaamd-lightbox__img")!).alt = alt ?? "";
48
+ lb.classList.remove("jaamd-lightbox--hidden");
49
+ document.body.style.overflow = "hidden";
50
+ }
@@ -0,0 +1,11 @@
1
+ import { qsa } from "../utils.js";
2
+
3
+ // ─── Spoilers ─────────────────────────────────────────────────────────────────
4
+
5
+ export function initSpoilers(selector: string): void {
6
+ qsa<HTMLElement>(document, `${selector} .spoiler`).forEach((el) => {
7
+ if (el.dataset.spoilerInit) return;
8
+ el.dataset.spoilerInit = "1";
9
+ el.addEventListener("click", () => el.classList.toggle("revealed"));
10
+ });
11
+ }
@@ -0,0 +1,13 @@
1
+ import { qsa } from "../utils.js";
2
+
3
+ // ─── Responsive table wrapper ─────────────────────────────────────────────────
4
+
5
+ export function wrapTables(selector: string): void {
6
+ qsa<HTMLTableElement>(document, `${selector} table`).forEach((table) => {
7
+ if (table.parentElement?.classList.contains("jaamd-table-wrapper")) return;
8
+ const wrapper = document.createElement("div");
9
+ wrapper.className = "jaamd-table-wrapper";
10
+ table.parentNode!.insertBefore(wrapper, table);
11
+ wrapper.appendChild(table);
12
+ });
13
+ }
@@ -0,0 +1,24 @@
1
+ // ─── Shared DOM utilities ─────────────────────────────────────────────────────
2
+
3
+ export function slugify(text: string): string {
4
+ return text
5
+ .toLowerCase()
6
+ .trim()
7
+ .replace(/[^\w\s-]/g, "")
8
+ .replace(/[\s_]+/g, "-")
9
+ .replace(/^-+|-+$/g, "");
10
+ }
11
+
12
+ export function qs<T extends Element = Element>(
13
+ root: ParentNode,
14
+ sel: string,
15
+ ): T | null {
16
+ return root.querySelector<T>(sel);
17
+ }
18
+
19
+ export function qsa<T extends Element = Element>(
20
+ root: ParentNode,
21
+ sel: string,
22
+ ): NodeListOf<T> {
23
+ return root.querySelectorAll<T>(sel);
24
+ }
@@ -361,11 +361,11 @@
361
361
  animation: jaamd-fade-in 0.2s ease-in-out;
362
362
  }
363
363
 
364
- .jaamd-lightbox.hidden {
364
+ .jaamd-lightbox--hidden {
365
365
  display: none;
366
366
  }
367
367
 
368
- .jaamd-lightbox-backdrop {
368
+ .jaamd-lightbox__backdrop {
369
369
  position: absolute;
370
370
  inset: 0;
371
371
  background-color: rgba(0, 0, 0, 0.75);
@@ -373,7 +373,7 @@
373
373
  cursor: pointer;
374
374
  }
375
375
 
376
- .jaamd-lightbox-content {
376
+ .jaamd-lightbox__content {
377
377
  position: relative;
378
378
  max-width: 95vw;
379
379
  max-height: 95vh;
@@ -383,7 +383,7 @@
383
383
  z-index: 1;
384
384
  }
385
385
 
386
- .jaamd-lightbox-img {
386
+ .jaamd-lightbox__img {
387
387
  max-width: 100%;
388
388
  max-height: 95vh;
389
389
  width: auto;
@@ -393,7 +393,7 @@
393
393
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
394
394
  }
395
395
 
396
- .jaamd-lightbox-close {
396
+ .jaamd-lightbox__close {
397
397
  position: fixed;
398
398
  top: 1rem;
399
399
  right: 1rem;
@@ -413,13 +413,13 @@
413
413
  z-index: 10;
414
414
  }
415
415
 
416
- .jaamd-lightbox-close:hover {
416
+ .jaamd-lightbox__close:hover {
417
417
  background: rgba(255, 255, 255, 0.2);
418
418
  border-color: rgba(255, 255, 255, 0.3);
419
419
  transform: scale(1.1);
420
420
  }
421
421
 
422
- .jaamd-lightbox-close:active {
422
+ .jaamd-lightbox__close:active {
423
423
  transform: scale(0.95);
424
424
  }
425
425