@pigmilcom/a11y 1.0.8 → 1.1.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/dist/index.mjs CHANGED
@@ -3,6 +3,61 @@
3
3
  // src/widget.jsx
4
4
  import { useEffect, useRef, useState } from "react";
5
5
  import { createPortal } from "react-dom";
6
+
7
+ // src/license.js
8
+ var _LRE = /^(localhost|127\.\d+\.\d+\.\d+|::1|\[::1\]|0\.0\.0\.0)$/i;
9
+ function _isLocalDev() {
10
+ try {
11
+ const { hostname: h, port: p } = location;
12
+ if (_LRE.test(h)) return true;
13
+ if (p && p !== "80" && p !== "443") return true;
14
+ return false;
15
+ } catch {
16
+ return true;
17
+ }
18
+ }
19
+ var _CK = "_pgm_lc";
20
+ function _readCache() {
21
+ try {
22
+ const v = sessionStorage.getItem(_CK);
23
+ if (!v) return null;
24
+ const p = JSON.parse(v);
25
+ return Date.now() - (p._ts || 0) < 18e5 ? p : null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ function _writeCache(result) {
31
+ try {
32
+ sessionStorage.setItem(_CK, JSON.stringify({ ...result, _ts: Date.now() }));
33
+ } catch {
34
+ }
35
+ }
36
+ var _mock = () => Promise.resolve({ ok: true, plan: "free", blocked: false });
37
+ async function validateLicense() {
38
+ const cached = _readCache();
39
+ if (cached) return { status: cached.status, plan: cached.plan };
40
+ if (_isLocalDev()) {
41
+ const result2 = { status: "dev", plan: "free" };
42
+ _writeCache(result2);
43
+ return result2;
44
+ }
45
+ const domain = typeof location !== "undefined" ? location.hostname.toLowerCase().replace(/^www\./, "") : "";
46
+ if (!domain) return { status: "error", plan: "free" };
47
+ let result;
48
+ try {
49
+ const data = await _mock();
50
+ if (!(data == null ? void 0 : data.ok)) result = { status: "error", plan: "free" };
51
+ else if (data.blocked) result = { status: "blocked", plan: data.plan || "free" };
52
+ else result = { status: "valid", plan: data.plan || "free" };
53
+ } catch {
54
+ result = { status: "error", plan: "free" };
55
+ }
56
+ _writeCache(result);
57
+ return result;
58
+ }
59
+
60
+ // src/widget.jsx
6
61
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
62
  var STORAGE_KEY = "pgm-a11y";
8
63
  var DEFAULTS = {
@@ -127,14 +182,19 @@ var TOGGLES = [
127
182
  ] })
128
183
  }
129
184
  ];
130
- function A11yWidget({ className }) {
185
+ function A11y({ className }) {
131
186
  const [open, setOpen] = useState(false);
132
187
  const [prefs, setPrefs] = useState(DEFAULTS);
133
188
  const [mounted, setMounted] = useState(false);
189
+ const [notice, setNotice] = useState(false);
190
+ const [license, setLicense] = useState({ status: "checking", plan: "free" });
134
191
  const triggerRef = useRef(null);
135
192
  useEffect(() => {
136
193
  setMounted(true);
137
194
  }, []);
195
+ useEffect(() => {
196
+ validateLicense().then(setLicense);
197
+ }, []);
138
198
  useEffect(() => {
139
199
  const saved = load();
140
200
  setPrefs(saved);
@@ -148,6 +208,10 @@ function A11yWidget({ className }) {
148
208
  document.addEventListener("keydown", onKey);
149
209
  return () => document.removeEventListener("keydown", onKey);
150
210
  }, [open]);
211
+ if (license.status === "checking" || license.status === "error" || license.status === "blocked") {
212
+ return null;
213
+ }
214
+ const isPro = license.plan === "pro";
151
215
  const update = (patch) => {
152
216
  setPrefs((prev) => {
153
217
  const next = { ...prev, ...patch };
@@ -172,7 +236,7 @@ function A11yWidget({ className }) {
172
236
  "aria-expanded": open,
173
237
  "aria-haspopup": "dialog",
174
238
  onClick: () => setOpen((v) => !v),
175
- className: `pgm-btn${className ? ` ${className}` : ""}`,
239
+ className: `pgm-btn a11y-widget-btn${className ? ` ${className}` : ""}`,
176
240
  children: [
177
241
  /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", className: "pgm-icon-lg", fill: "none", stroke: "currentColor", strokeWidth: 1.8, "aria-hidden": "true", children: [
178
242
  /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.5", fill: "currentColor", stroke: "none" }),
@@ -198,7 +262,7 @@ function A11yWidget({ className }) {
198
262
  role: "dialog",
199
263
  "aria-modal": "true",
200
264
  "aria-label": "Accessibility settings",
201
- className: "pgm-dialog",
265
+ className: "pgm-dialog a11y-widget-dialog",
202
266
  children: [
203
267
  /* @__PURE__ */ jsxs("div", { className: "pgm-header", children: [
204
268
  /* @__PURE__ */ jsxs("div", { children: [
@@ -242,7 +306,7 @@ function A11yWidget({ className }) {
242
306
  role: "switch",
243
307
  "aria-checked": on,
244
308
  onClick: () => update({ [key]: !on }),
245
- className: `pgm-toggle-btn${on ? " pgm-toggle-btn--on" : ""}`,
309
+ className: `pgm-toggle-btn a11y-widget-toggle-btn${on ? " pgm-toggle-btn--on" : ""}`,
246
310
  children: [
247
311
  /* @__PURE__ */ jsx("span", { className: `pgm-toggle-icon${on ? " pgm-toggle-icon--on" : ""}`, children: icon }),
248
312
  /* @__PURE__ */ jsxs("span", { className: "pgm-toggle-text", children: [
@@ -268,7 +332,28 @@ function A11yWidget({ className }) {
268
332
  );
269
333
  }) }),
270
334
  /* @__PURE__ */ jsxs("div", { className: "pgm-footer", children: [
271
- /* @__PURE__ */ jsx("span", { className: "pgm-footer-note", children: "Changes apply instantly & persist" }),
335
+ !isPro && /* @__PURE__ */ jsxs(
336
+ "button",
337
+ {
338
+ type: "button",
339
+ "aria-expanded": notice,
340
+ "aria-controls": "pgm-notice",
341
+ onClick: () => setNotice((v) => !v),
342
+ className: "pgm-notice-btn",
343
+ children: [
344
+ /* @__PURE__ */ jsx(
345
+ "img",
346
+ {
347
+ src: "https://cdn.pigmil.com/shared/icon.png",
348
+ alt: "Pigmil",
349
+ className: "pgm-notice-logo"
350
+ }
351
+ ),
352
+ /* @__PURE__ */ jsx("span", { children: "About this widget" }),
353
+ /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "pgm-notice-chevron", fill: "none", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", style: { transform: notice ? "rotate(180deg)" : "rotate(0deg)" }, children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6", strokeLinecap: "round", strokeLinejoin: "round" }) })
354
+ ]
355
+ }
356
+ ),
272
357
  /* @__PURE__ */ jsx(
273
358
  "button",
274
359
  {
@@ -279,6 +364,37 @@ function A11yWidget({ className }) {
279
364
  children: "Reset all"
280
365
  }
281
366
  )
367
+ ] }),
368
+ !isPro && notice && /* @__PURE__ */ jsxs("div", { id: "pgm-notice", className: "pgm-notice", children: [
369
+ /* @__PURE__ */ jsxs("p", { className: "pgm-notice-text", children: [
370
+ "This accessibility widget is provided free of charge by",
371
+ " ",
372
+ /* @__PURE__ */ jsx(
373
+ "a",
374
+ {
375
+ href: "https://github.com/pigmilcom/a11y",
376
+ target: "_blank",
377
+ rel: "noopener noreferrer",
378
+ className: "pgm-notice-link",
379
+ children: "PIGMIL"
380
+ }
381
+ ),
382
+ " ",
383
+ "as an open-source tool to help make the web more accessible. It stores your preferences locally in your browser and sends no data to any server."
384
+ ] }),
385
+ /* @__PURE__ */ jsxs(
386
+ "a",
387
+ {
388
+ href: "https://github.com/pigmilcom/a11y",
389
+ target: "_blank",
390
+ rel: "noopener noreferrer",
391
+ className: "pgm-notice-gh",
392
+ children: [
393
+ /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "pgm-icon-sm", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0 1 12 6.844a9.59 9.59 0 0 1 2.504.337c1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.02 10.02 0 0 0 22 12.017C22 6.484 17.522 2 12 2z" }) }),
394
+ "View on GitHub"
395
+ ]
396
+ }
397
+ )
282
398
  ] })
283
399
  ]
284
400
  }
@@ -289,6 +405,7 @@ function A11yWidget({ className }) {
289
405
  ] });
290
406
  }
291
407
  export {
292
- A11yWidget,
293
- A11yWidget as default
408
+ A11y as A11yWidget,
409
+ A11y as a11y,
410
+ A11y as default
294
411
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pigmilcom/a11y",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "WCAG 2.1 accessibility widget for React — text size, contrast, dyslexia font, motion reduction, and more.",
5
5
  "keywords": [
6
6
  "accessibility",
package/src/cdn.jsx CHANGED
@@ -15,7 +15,7 @@
15
15
  */
16
16
 
17
17
  import { createRoot } from 'react-dom/client';
18
- import A11yWidget from './widget.jsx';
18
+ import A11y from './widget.jsx';
19
19
 
20
20
  // Inline the pre-built CSS (widget UI + a11y modifier rules).
21
21
  // Imported as a JS module (generated by scripts/build-css.js) so esbuild bundles
@@ -60,7 +60,7 @@ function mount() {
60
60
  container.setAttribute('style', `position:fixed;z-index:9998;line-height:0;font-size:0;${posCSS}`);
61
61
  document.body.appendChild(container);
62
62
  _root = createRoot(container);
63
- _root.render(<A11yWidget />);
63
+ _root.render(<A11y />);
64
64
  }
65
65
 
66
66
  function unmount() {