@oksigenia/access-panel 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +62 -3
- package/dist/index.d.ts +31 -1
- package/dist/index.js +139 -23
- package/dist/index.js.map +1 -1
- package/dist/web-component.d.ts +5 -0
- package/dist/web-component.js +172 -29
- package/dist/web-component.js.map +1 -1
- package/package.json +1 -1
package/dist/web-component.js
CHANGED
|
@@ -60,6 +60,18 @@ function resolveEnabledControls(controlsAttr, excludeAttr) {
|
|
|
60
60
|
}
|
|
61
61
|
return enabled;
|
|
62
62
|
}
|
|
63
|
+
var NON_SCOPABLE_CONTROLS = [
|
|
64
|
+
"grayscale",
|
|
65
|
+
"reading-guide",
|
|
66
|
+
"reading-mask",
|
|
67
|
+
"big-cursor",
|
|
68
|
+
"colorblind"
|
|
69
|
+
];
|
|
70
|
+
function scopedControls(enabled) {
|
|
71
|
+
const out = new Set(enabled);
|
|
72
|
+
for (const id of NON_SCOPABLE_CONTROLS) out.delete(id);
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
63
75
|
function filterPresetForEnabled(id, enabled) {
|
|
64
76
|
const out = {};
|
|
65
77
|
for (const [k, v] of Object.entries(PRESETS[id])) {
|
|
@@ -360,30 +372,36 @@ function bindPanelBehavior(root, opts = {}) {
|
|
|
360
372
|
}
|
|
361
373
|
let state = loadState(storageKey);
|
|
362
374
|
function applyState() {
|
|
363
|
-
const
|
|
375
|
+
const scoped = opts.scopeEl !== void 0;
|
|
376
|
+
const fxRoot = scoped ? opts.scopeEl ?? null : document.body;
|
|
364
377
|
const rootEl = document.documentElement;
|
|
365
|
-
|
|
366
|
-
|
|
378
|
+
if (fxRoot) {
|
|
379
|
+
for (const cls of Array.from(fxRoot.classList)) {
|
|
380
|
+
if (cls.startsWith("oks-")) fxRoot.classList.remove(cls);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (!scoped) [1, 2, 3].forEach((l) => rootEl.classList.remove(`oks-colorblind-${l}`));
|
|
384
|
+
if (fxRoot) {
|
|
385
|
+
if (state.zoom > 0) fxRoot.classList.add(`oks-zoom-${state.zoom}`);
|
|
386
|
+
if (state.lh > 0) fxRoot.classList.add(`oks-lh-${state.lh}`);
|
|
387
|
+
if (state.align > 0) fxRoot.classList.add(`oks-align-${state.align}`);
|
|
388
|
+
if (state.ls > 0) fxRoot.classList.add(`oks-ls-${state.ls}`);
|
|
389
|
+
if (state.font) fxRoot.classList.add("oks-a11y-font");
|
|
390
|
+
if (state.dyslexia) fxRoot.classList.add("oks-dyslexia");
|
|
391
|
+
if (state.contrast) fxRoot.classList.add("oks-a11y-contrast");
|
|
392
|
+
if (state.hideImages) fxRoot.classList.add("oks-a11y-hide");
|
|
393
|
+
if (state.highlightLinks) fxRoot.classList.add("oks-a11y-links");
|
|
394
|
+
if (state.pauseAnim) fxRoot.classList.add("oks-a11y-pause");
|
|
395
|
+
if (state.focusOutline) fxRoot.classList.add("oks-a11y-focus");
|
|
396
|
+
if (state.bigTargets) fxRoot.classList.add("oks-a11y-bigtargets");
|
|
397
|
+
}
|
|
398
|
+
if (!scoped) {
|
|
399
|
+
if (state.colorblind > 0) rootEl.classList.add(`oks-colorblind-${state.colorblind}`);
|
|
400
|
+
if (state.bigCursor) document.body.classList.add("oks-big-cursor");
|
|
401
|
+
if (state.readingGuide) document.body.classList.add("oks-a11y-guide");
|
|
402
|
+
if (state.readingMask) document.body.classList.add("oks-a11y-mask");
|
|
403
|
+
ensureOverlay().classList.toggle("is-active", state.grayOverlay);
|
|
367
404
|
}
|
|
368
|
-
[1, 2, 3].forEach((l) => rootEl.classList.remove(`oks-colorblind-${l}`));
|
|
369
|
-
if (state.zoom > 0) body.classList.add(`oks-zoom-${state.zoom}`);
|
|
370
|
-
if (state.lh > 0) body.classList.add(`oks-lh-${state.lh}`);
|
|
371
|
-
if (state.align > 0) body.classList.add(`oks-align-${state.align}`);
|
|
372
|
-
if (state.ls > 0) body.classList.add(`oks-ls-${state.ls}`);
|
|
373
|
-
if (state.colorblind > 0) rootEl.classList.add(`oks-colorblind-${state.colorblind}`);
|
|
374
|
-
if (state.font) body.classList.add("oks-a11y-font");
|
|
375
|
-
if (state.dyslexia) body.classList.add("oks-dyslexia");
|
|
376
|
-
if (state.contrast) body.classList.add("oks-a11y-contrast");
|
|
377
|
-
if (state.hideImages) body.classList.add("oks-a11y-hide");
|
|
378
|
-
if (state.highlightLinks) body.classList.add("oks-a11y-links");
|
|
379
|
-
if (state.bigCursor) body.classList.add("oks-big-cursor");
|
|
380
|
-
if (state.pauseAnim) body.classList.add("oks-a11y-pause");
|
|
381
|
-
if (state.focusOutline) body.classList.add("oks-a11y-focus");
|
|
382
|
-
if (state.readingGuide) body.classList.add("oks-a11y-guide");
|
|
383
|
-
if (state.readingMask) body.classList.add("oks-a11y-mask");
|
|
384
|
-
if (state.bigTargets) body.classList.add("oks-a11y-bigtargets");
|
|
385
|
-
const overlay = ensureOverlay();
|
|
386
|
-
overlay.classList.toggle("is-active", state.grayOverlay);
|
|
387
405
|
syncButtonsFromState();
|
|
388
406
|
wrapper?.classList.toggle("has-active", !isStateEmpty(state));
|
|
389
407
|
}
|
|
@@ -462,11 +480,24 @@ function bindPanelBehavior(root, opts = {}) {
|
|
|
462
480
|
};
|
|
463
481
|
let opener = null;
|
|
464
482
|
let ignoreDocClose = false;
|
|
483
|
+
const positionScopedDialog = () => {
|
|
484
|
+
const el = opts.scopeEl;
|
|
485
|
+
if (!el) return;
|
|
486
|
+
const r = el.getBoundingClientRect();
|
|
487
|
+
if (r.width === 0 && r.height === 0) return;
|
|
488
|
+
const pad = 8;
|
|
489
|
+
panel.style.top = `${Math.max(pad, r.top + pad)}px`;
|
|
490
|
+
panel.style.bottom = "auto";
|
|
491
|
+
panel.style.left = `${r.left + r.width / 2}px`;
|
|
492
|
+
panel.style.right = "auto";
|
|
493
|
+
panel.style.transform = "translateX(-50%)";
|
|
494
|
+
};
|
|
465
495
|
const openPanel = () => {
|
|
466
496
|
if (panel.classList.contains("is-open")) return;
|
|
467
497
|
opener = deepActiveElement();
|
|
468
498
|
panel.classList.add("is-open");
|
|
469
499
|
panel.removeAttribute("inert");
|
|
500
|
+
if (opts.scopeEl) positionScopedDialog();
|
|
470
501
|
trigger?.setAttribute("aria-expanded", "true");
|
|
471
502
|
ignoreDocClose = true;
|
|
472
503
|
setTimeout(() => {
|
|
@@ -633,6 +664,13 @@ function bindPanelBehavior(root, opts = {}) {
|
|
|
633
664
|
document.addEventListener("keydown", onKeyDown);
|
|
634
665
|
document.addEventListener("mousemove", onMove);
|
|
635
666
|
document.addEventListener("touchmove", onMove, { passive: true });
|
|
667
|
+
const onReposition = () => {
|
|
668
|
+
if (opts.scopeEl && panel.classList.contains("is-open")) positionScopedDialog();
|
|
669
|
+
};
|
|
670
|
+
if (opts.scopeEl !== void 0) {
|
|
671
|
+
window.addEventListener("scroll", onReposition, true);
|
|
672
|
+
window.addEventListener("resize", onReposition);
|
|
673
|
+
}
|
|
636
674
|
if (nudgeMax > 0 && trigger) {
|
|
637
675
|
loadNudge();
|
|
638
676
|
applyNudge();
|
|
@@ -653,6 +691,10 @@ function bindPanelBehavior(root, opts = {}) {
|
|
|
653
691
|
document.removeEventListener("keydown", onKeyDown);
|
|
654
692
|
document.removeEventListener("mousemove", onMove);
|
|
655
693
|
document.removeEventListener("touchmove", onMove);
|
|
694
|
+
if (opts.scopeEl !== void 0) {
|
|
695
|
+
window.removeEventListener("scroll", onReposition, true);
|
|
696
|
+
window.removeEventListener("resize", onReposition);
|
|
697
|
+
}
|
|
656
698
|
trigger?.removeEventListener("pointerdown", onPointerDown);
|
|
657
699
|
document.removeEventListener("pointermove", onPointerMove);
|
|
658
700
|
document.removeEventListener("pointerup", onPointerUp);
|
|
@@ -1069,6 +1111,80 @@ body.oks-a11y-bigtargets summary:not(oksigenia-access-panel):not(oksigenia-acces
|
|
|
1069
1111
|
box-sizing: border-box !important;
|
|
1070
1112
|
}
|
|
1071
1113
|
`;
|
|
1114
|
+
function scopedEffectCss(s) {
|
|
1115
|
+
return `
|
|
1116
|
+
${s}.oks-zoom-1 { font-size: 110% !important; }
|
|
1117
|
+
${s}.oks-zoom-2 { font-size: 120% !important; }
|
|
1118
|
+
${s}.oks-zoom-3 { font-size: 135% !important; }
|
|
1119
|
+
${s}.oks-zoom-4 { font-size: 150% !important; }
|
|
1120
|
+
|
|
1121
|
+
${s}.oks-lh-1 * { line-height: 1.6 !important; }
|
|
1122
|
+
${s}.oks-lh-2 * { line-height: 1.9 !important; }
|
|
1123
|
+
${s}.oks-lh-3 * { line-height: 2.2 !important; }
|
|
1124
|
+
|
|
1125
|
+
${s}.oks-a11y-font, ${s}.oks-a11y-font * { font-family: Arial, sans-serif !important; }
|
|
1126
|
+
|
|
1127
|
+
${s}.oks-dyslexia * {
|
|
1128
|
+
font-family: 'Comic Sans MS', 'Verdana', sans-serif !important;
|
|
1129
|
+
letter-spacing: 0.05em !important;
|
|
1130
|
+
word-spacing: 0.1em !important;
|
|
1131
|
+
line-height: 1.6 !important;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
${s}.oks-a11y-hide img { opacity: 0 !important; visibility: hidden !important; }
|
|
1135
|
+
${s}.oks-a11y-links a { text-decoration: underline !important; background: #ff0 !important; color: #000 !important; }
|
|
1136
|
+
|
|
1137
|
+
${s}.oks-align-1 * { text-align: left !important; }
|
|
1138
|
+
${s}.oks-align-2 * { text-align: center !important; }
|
|
1139
|
+
${s}.oks-align-3 * { text-align: right !important; }
|
|
1140
|
+
|
|
1141
|
+
${s}.oks-a11y-pause * { animation: none !important; transition: none !important; }
|
|
1142
|
+
|
|
1143
|
+
${s}.oks-ls-1 * { letter-spacing: 0.05em !important; }
|
|
1144
|
+
${s}.oks-ls-2 * { letter-spacing: 0.10em !important; }
|
|
1145
|
+
${s}.oks-ls-3 * { letter-spacing: 0.16em !important; }
|
|
1146
|
+
|
|
1147
|
+
${s}.oks-a11y-focus a,
|
|
1148
|
+
${s}.oks-a11y-focus button,
|
|
1149
|
+
${s}.oks-a11y-focus input,
|
|
1150
|
+
${s}.oks-a11y-focus select,
|
|
1151
|
+
${s}.oks-a11y-focus textarea {
|
|
1152
|
+
outline: 2px dashed rgba(0, 95, 204, 0.45) !important;
|
|
1153
|
+
outline-offset: 2px !important;
|
|
1154
|
+
}
|
|
1155
|
+
${s}.oks-a11y-focus *:focus-visible {
|
|
1156
|
+
outline: 3px solid #005fcc !important;
|
|
1157
|
+
outline-offset: 3px !important;
|
|
1158
|
+
box-shadow: 0 0 0 6px rgba(0, 95, 204, 0.25) !important;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
${s}.oks-a11y-contrast, ${s}.oks-a11y-contrast * {
|
|
1162
|
+
background-color: #000 !important;
|
|
1163
|
+
color: #ff0 !important;
|
|
1164
|
+
border-color: #ff0 !important;
|
|
1165
|
+
text-shadow: none !important;
|
|
1166
|
+
box-shadow: none !important;
|
|
1167
|
+
}
|
|
1168
|
+
${s}.oks-a11y-contrast img { filter: grayscale(100%) contrast(120%) !important; }
|
|
1169
|
+
${s}.oks-a11y-contrast a { color: #0ff !important; text-decoration: underline !important; }
|
|
1170
|
+
${s}.oks-a11y-contrast.oks-a11y-focus *:focus-visible {
|
|
1171
|
+
outline-color: #0ff !important;
|
|
1172
|
+
box-shadow: 0 0 0 6px rgba(0, 255, 255, 0.3) !important;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
${s}.oks-a11y-bigtargets a,
|
|
1176
|
+
${s}.oks-a11y-bigtargets button,
|
|
1177
|
+
${s}.oks-a11y-bigtargets [role="button"],
|
|
1178
|
+
${s}.oks-a11y-bigtargets input[type="checkbox"],
|
|
1179
|
+
${s}.oks-a11y-bigtargets input[type="radio"],
|
|
1180
|
+
${s}.oks-a11y-bigtargets summary {
|
|
1181
|
+
min-height: 44px !important;
|
|
1182
|
+
min-width: 44px !important;
|
|
1183
|
+
padding: 8px 12px !important;
|
|
1184
|
+
box-sizing: border-box !important;
|
|
1185
|
+
}
|
|
1186
|
+
`;
|
|
1187
|
+
}
|
|
1072
1188
|
|
|
1073
1189
|
// src/web-component.ts
|
|
1074
1190
|
var OBSERVED = [
|
|
@@ -1082,7 +1198,8 @@ var OBSERVED = [
|
|
|
1082
1198
|
"trigger",
|
|
1083
1199
|
"effects-exclude",
|
|
1084
1200
|
"nudge",
|
|
1085
|
-
"presets"
|
|
1201
|
+
"presets",
|
|
1202
|
+
"scope"
|
|
1086
1203
|
];
|
|
1087
1204
|
var STYLE_ID = "oksigenia-access-effects";
|
|
1088
1205
|
var FILTERS_ID = "oksigenia-access-filters";
|
|
@@ -1135,6 +1252,7 @@ var OksigeniaAccessPanelElement = class extends HTMLElement {
|
|
|
1135
1252
|
}
|
|
1136
1253
|
_controller = null;
|
|
1137
1254
|
_fxId = `oks-access-fx-${++fxSeq}`;
|
|
1255
|
+
_scopeId = `oks-access-scope-${this._fxId.split("-").pop()}`;
|
|
1138
1256
|
constructor() {
|
|
1139
1257
|
super();
|
|
1140
1258
|
this.attachShadow({ mode: "open" });
|
|
@@ -1146,7 +1264,10 @@ var OksigeniaAccessPanelElement = class extends HTMLElement {
|
|
|
1146
1264
|
disconnectedCallback() {
|
|
1147
1265
|
this._controller?.();
|
|
1148
1266
|
this._controller = null;
|
|
1149
|
-
if (typeof document !== "undefined")
|
|
1267
|
+
if (typeof document !== "undefined") {
|
|
1268
|
+
document.getElementById(this._fxId)?.remove();
|
|
1269
|
+
document.getElementById(this._scopeId)?.remove();
|
|
1270
|
+
}
|
|
1150
1271
|
}
|
|
1151
1272
|
attributeChangedCallback() {
|
|
1152
1273
|
if (this.isConnected) this.render();
|
|
@@ -1189,7 +1310,7 @@ var OksigeniaAccessPanelElement = class extends HTMLElement {
|
|
|
1189
1310
|
* drop the destructive filter from selectors the host marks as essential
|
|
1190
1311
|
* media (video, canvas, scientific imagery). Grayscale is handled by not
|
|
1191
1312
|
* offering the control (curation), not here. */
|
|
1192
|
-
updateEffectsExclude() {
|
|
1313
|
+
updateEffectsExclude(base) {
|
|
1193
1314
|
if (typeof document === "undefined") return;
|
|
1194
1315
|
const sel = (this.getAttribute("effects-exclude") ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
1195
1316
|
const existing = document.getElementById(this._fxId);
|
|
@@ -1203,7 +1324,24 @@ var OksigeniaAccessPanelElement = class extends HTMLElement {
|
|
|
1203
1324
|
document.head.appendChild(style);
|
|
1204
1325
|
}
|
|
1205
1326
|
const list = sel.join(", ");
|
|
1206
|
-
style.textContent =
|
|
1327
|
+
style.textContent = `${base}.oks-a11y-contrast :is(${list}), ${base}.oks-a11y-contrast :is(${list}) * { filter: none !important; }`;
|
|
1328
|
+
}
|
|
1329
|
+
/** Inject the per-instance scoped effect CSS (`scope=`): the scopable effects
|
|
1330
|
+
* anchored to the host's container instead of `body`, so several panels
|
|
1331
|
+
* regionalise their own zone without clobbering each other. */
|
|
1332
|
+
updateScopeStyle(scope) {
|
|
1333
|
+
if (typeof document === "undefined") return;
|
|
1334
|
+
const existing = document.getElementById(this._scopeId);
|
|
1335
|
+
if (!scope) {
|
|
1336
|
+
existing?.remove();
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
const style = existing ?? document.createElement("style");
|
|
1340
|
+
if (!existing) {
|
|
1341
|
+
style.id = this._scopeId;
|
|
1342
|
+
document.head.appendChild(style);
|
|
1343
|
+
}
|
|
1344
|
+
style.textContent = scopedEffectCss(scope);
|
|
1207
1345
|
}
|
|
1208
1346
|
render() {
|
|
1209
1347
|
const shadow = this.shadowRoot;
|
|
@@ -1211,7 +1349,9 @@ var OksigeniaAccessPanelElement = class extends HTMLElement {
|
|
|
1211
1349
|
this._controller?.();
|
|
1212
1350
|
const position = this.getPosition();
|
|
1213
1351
|
const positionMobile = this.getPositionMobile();
|
|
1214
|
-
const
|
|
1352
|
+
const scope = this.getAttribute("scope")?.trim() || null;
|
|
1353
|
+
let enabled = resolveEnabledControls(this.getAttribute("controls"), this.getAttribute("exclude"));
|
|
1354
|
+
if (scope) enabled = scopedControls(enabled);
|
|
1215
1355
|
const showTrigger = this.getAttribute("trigger") !== "none";
|
|
1216
1356
|
const showPresets = (this.getAttribute("presets") ?? "").trim().toLowerCase() !== "none";
|
|
1217
1357
|
const html = buildPanelHtml({
|
|
@@ -1223,13 +1363,16 @@ var OksigeniaAccessPanelElement = class extends HTMLElement {
|
|
|
1223
1363
|
showPresets
|
|
1224
1364
|
});
|
|
1225
1365
|
shadow.innerHTML = `<style>${PANEL_CSS}${positionCss(position, positionMobile)}</style>${html}`;
|
|
1366
|
+
const scopeEl = scope ? document.querySelector(scope) : void 0;
|
|
1226
1367
|
this._controller = bindPanelBehavior(shadow, {
|
|
1227
1368
|
storageKey: this.getAttribute("storage-key") ?? void 0,
|
|
1228
1369
|
locale: this.getLocale(),
|
|
1229
1370
|
enabled,
|
|
1230
|
-
nudgeMax: this.getNudgeMax()
|
|
1371
|
+
nudgeMax: this.getNudgeMax(),
|
|
1372
|
+
scopeEl
|
|
1231
1373
|
});
|
|
1232
|
-
this.
|
|
1374
|
+
this.updateScopeStyle(scope);
|
|
1375
|
+
this.updateEffectsExclude(scope ?? "body");
|
|
1233
1376
|
}
|
|
1234
1377
|
};
|
|
1235
1378
|
if (typeof customElements !== "undefined" && !customElements.get("oksigenia-access-panel")) {
|