@adia-ai/web-components 0.6.35 → 0.6.37
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 +56 -0
- package/components/badge/badge.a2ui.json +10 -0
- package/components/badge/badge.css +70 -0
- package/components/badge/badge.yaml +20 -0
- package/components/blockquote/blockquote.a2ui.json +121 -0
- package/components/blockquote/blockquote.class.js +68 -0
- package/components/blockquote/blockquote.css +46 -0
- package/components/blockquote/blockquote.d.ts +31 -0
- package/components/blockquote/blockquote.js +17 -0
- package/components/blockquote/blockquote.yaml +124 -0
- package/components/button/button.css +11 -3
- package/components/calendar-picker/calendar-picker.a2ui.json +15 -0
- package/components/calendar-picker/calendar-picker.class.js +7 -1
- package/components/calendar-picker/calendar-picker.yaml +14 -0
- package/components/color-input/color-input.a2ui.json +2 -2
- package/components/color-input/color-input.class.js +9 -2
- package/components/color-input/color-input.yaml +2 -2
- package/components/combobox/combobox.class.js +4 -0
- package/components/combobox/combobox.css +12 -0
- package/components/context-menu/context-menu.a2ui.json +159 -0
- package/components/context-menu/context-menu.class.js +275 -0
- package/components/context-menu/context-menu.css +56 -0
- package/components/context-menu/context-menu.d.ts +70 -0
- package/components/context-menu/context-menu.js +17 -0
- package/components/context-menu/context-menu.yaml +136 -0
- package/components/date-range-picker/date-range-picker.a2ui.json +15 -0
- package/components/date-range-picker/date-range-picker.class.js +3 -1
- package/components/date-range-picker/date-range-picker.css +4 -1
- package/components/date-range-picker/date-range-picker.yaml +14 -0
- package/components/datetime-picker/datetime-picker.a2ui.json +15 -0
- package/components/datetime-picker/datetime-picker.class.js +3 -1
- package/components/datetime-picker/datetime-picker.css +7 -1
- package/components/datetime-picker/datetime-picker.d.ts +2 -0
- package/components/datetime-picker/datetime-picker.yaml +14 -0
- package/components/empty-state/empty-state.class.js +2 -0
- package/components/feed/feed.class.js +13 -5
- package/components/feed/feed.css +14 -0
- package/components/index.js +9 -0
- package/components/input/input.css +15 -1
- package/components/input/input.test.js +40 -0
- package/components/integration-card/integration-card.class.js +9 -0
- package/components/integration-card/integration-card.test.js +4 -3
- package/components/nav-group/nav-group.css +7 -1
- package/components/number-format/number-format.a2ui.json +180 -0
- package/components/number-format/number-format.class.js +96 -0
- package/components/number-format/number-format.css +18 -0
- package/components/number-format/number-format.d.ts +68 -0
- package/components/number-format/number-format.js +17 -0
- package/components/number-format/number-format.yaml +204 -0
- package/components/pagination/pagination.a2ui.json +19 -2
- package/components/pagination/pagination.class.js +90 -37
- package/components/pagination/pagination.css +32 -127
- package/components/pagination/pagination.d.ts +8 -2
- package/components/pagination/pagination.test.js +195 -0
- package/components/pagination/pagination.yaml +22 -1
- package/components/password-strength/password-strength.a2ui.json +152 -0
- package/components/password-strength/password-strength.class.js +157 -0
- package/components/password-strength/password-strength.css +80 -0
- package/components/password-strength/password-strength.d.ts +59 -0
- package/components/password-strength/password-strength.js +17 -0
- package/components/password-strength/password-strength.yaml +153 -0
- package/components/popover/popover.css +43 -23
- package/components/popover/popover.yaml +8 -4
- package/components/qr-code/QR-TEST.svg +4 -0
- package/components/qr-code/qr-code.a2ui.json +154 -0
- package/components/qr-code/qr-code.class.js +129 -0
- package/components/qr-code/qr-code.css +41 -0
- package/components/qr-code/qr-code.d.ts +83 -0
- package/components/qr-code/qr-code.js +17 -0
- package/components/qr-code/qr-code.yaml +203 -0
- package/components/qr-code/qr-encoder.js +633 -0
- package/components/relative-time/relative-time.a2ui.json +120 -0
- package/components/relative-time/relative-time.class.js +136 -0
- package/components/relative-time/relative-time.css +22 -0
- package/components/relative-time/relative-time.d.ts +51 -0
- package/components/relative-time/relative-time.js +17 -0
- package/components/relative-time/relative-time.yaml +133 -0
- package/components/search/search.class.js +2 -0
- package/components/segmented/segmented.class.js +5 -1
- package/components/select/select.class.js +4 -0
- package/components/skip-nav/skip-nav.a2ui.json +92 -0
- package/components/skip-nav/skip-nav.class.js +45 -0
- package/components/skip-nav/skip-nav.css +54 -0
- package/components/skip-nav/skip-nav.d.ts +27 -0
- package/components/skip-nav/skip-nav.js +12 -0
- package/components/skip-nav/skip-nav.yaml +68 -0
- package/components/slider/slider.a2ui.json +16 -1
- package/components/slider/slider.class.js +264 -122
- package/components/slider/slider.css +82 -2
- package/components/slider/slider.d.ts +19 -3
- package/components/slider/slider.test.js +55 -0
- package/components/slider/slider.yaml +28 -6
- package/components/table/table.class.js +29 -6
- package/components/table/table.css +31 -4
- package/components/table-toolbar/table-toolbar.class.js +4 -1
- package/components/tag/tag.a2ui.json +10 -0
- package/components/tag/tag.class.js +8 -1
- package/components/tag/tag.css +108 -20
- package/components/tag/tag.d.ts +14 -0
- package/components/tag/tag.test.js +99 -1
- package/components/tag/tag.yaml +20 -0
- package/components/tags-input/tags-input.class.js +10 -3
- package/components/tags-input/tags-input.css +12 -3
- package/components/textarea/textarea.css +10 -1
- package/components/toast/toast.class.js +12 -4
- package/components/toc/toc.a2ui.json +159 -0
- package/components/toc/toc.class.js +222 -0
- package/components/toc/toc.css +92 -0
- package/components/toc/toc.d.ts +61 -0
- package/components/toc/toc.js +17 -0
- package/components/toc/toc.yaml +180 -0
- package/components/toolbar/toolbar.class.js +3 -0
- package/components/visually-hidden/visually-hidden.a2ui.json +71 -0
- package/components/visually-hidden/visually-hidden.class.js +14 -0
- package/components/visually-hidden/visually-hidden.css +25 -0
- package/components/visually-hidden/visually-hidden.d.ts +26 -0
- package/components/visually-hidden/visually-hidden.js +12 -0
- package/components/visually-hidden/visually-hidden.yaml +54 -0
- package/core/anchor.js +19 -3
- package/core/provider.js +19 -2
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +101 -89
- package/package.json +1 -1
- package/styles/colors/semantics.css +11 -2
- package/styles/components.css +9 -0
- package/styles/resets.css +10 -0
|
@@ -5,15 +5,11 @@
|
|
|
5
5
|
--popover-py-default: var(--a-space-2);
|
|
6
6
|
--popover-radius-default: var(--a-radius-lg);
|
|
7
7
|
|
|
8
|
-
/* ── Colors
|
|
8
|
+
/* ── Colors (default panel chrome — opt out per `:has(>card-ui)` rule
|
|
9
|
+
below or by setting tokens to transparent / 0) ── */
|
|
9
10
|
--popover-bg-default: var(--a-bg-subtle);
|
|
10
11
|
--popover-border-default: var(--a-border-subtle);
|
|
11
12
|
--popover-shadow-default: var(--a-shadow-lg);
|
|
12
|
-
--popover-fg-default: var(--a-fg);
|
|
13
|
-
|
|
14
|
-
/* ── Typography ── */
|
|
15
|
-
--popover-font-default: var(--a-font-family);
|
|
16
|
-
--popover-font-size-default: var(--a-ui-size);
|
|
17
13
|
}
|
|
18
14
|
|
|
19
15
|
:scope {
|
|
@@ -34,25 +30,55 @@
|
|
|
34
30
|
display: none !important;
|
|
35
31
|
}
|
|
36
32
|
|
|
33
|
+
/* ── popover-ui's CSS opinions, narrowly scoped ──
|
|
34
|
+
*
|
|
35
|
+
* What's HERE and why:
|
|
36
|
+
* • [slot="content"]:not(:popover-open) display:none — required Popover
|
|
37
|
+
* API mechanics (closed-state visibility for slotted children).
|
|
38
|
+
* • [slot="content"] margin: 0 — reset UA stylesheet's `margin: auto`
|
|
39
|
+
* on popover elements (otherwise popover centers in viewport).
|
|
40
|
+
* • [slot="content"] box-sizing — universal sanity.
|
|
41
|
+
* • Default panel chrome — defensive for the bare-HTML case
|
|
42
|
+
* (<div slot="content">Hello</div> needs to be visible against the
|
|
43
|
+
* page). Opt out by putting a surface primitive (card-ui / drawer-ui
|
|
44
|
+
* / menu-ui) as the only child — they own their own chrome.
|
|
45
|
+
* • Light enter/exit animation (opacity + 4px translate) tied to the
|
|
46
|
+
* :popover-open state; honors prefers-reduced-motion.
|
|
47
|
+
*
|
|
48
|
+
* What's deliberately NOT here:
|
|
49
|
+
* • width / max-width / max-height / overflow-y — set by core/anchor.js
|
|
50
|
+
* inline during anchoring; those are the anchor module's contract,
|
|
51
|
+
* not the shell's.
|
|
52
|
+
* • font-family / font-size / color — inherit from the page naturally.
|
|
53
|
+
* • Prose first/last-child margin resets — touched consumer DOM.
|
|
54
|
+
*/
|
|
37
55
|
[slot="content"] {
|
|
38
56
|
box-sizing: border-box;
|
|
57
|
+
/* Reset UA stylesheet's `[popover]` defaults — it sets `margin: auto`
|
|
58
|
+
(centers in viewport) AND `padding: 0.25em` (~3.75 px at default
|
|
59
|
+
font-size, leaks into card-ui / drawer-ui consumers and looks like
|
|
60
|
+
phantom inset between popover edge and inner surface). Both must
|
|
61
|
+
be zeroed; the chrome rule below re-applies padding only when no
|
|
62
|
+
surface primitive is in the slot. */
|
|
39
63
|
margin: 0;
|
|
64
|
+
padding: 0;
|
|
65
|
+
opacity: 1;
|
|
66
|
+
translate: 0 0;
|
|
67
|
+
transition: opacity var(--a-duration-fast) var(--a-easing-out),
|
|
68
|
+
translate var(--a-duration-fast) var(--a-easing-out);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Skip chrome when the slot IS a surface primitive itself
|
|
72
|
+
(`<card-ui slot="content">…`) AND when it CONTAINS one as the only
|
|
73
|
+
child (`<div slot="content"><card-ui>…</card-ui></div>`). Both
|
|
74
|
+
shapes appear in consumer code; both should let the inner surface
|
|
75
|
+
own padding + bg + border + radius + shadow. */
|
|
76
|
+
[slot="content"]:not(card-ui):not(drawer-ui):not(menu-ui):not(:has(> card-ui:only-child)):not(:has(> drawer-ui:only-child)):not(:has(> menu-ui:only-child)) {
|
|
40
77
|
padding: var(--popover-py, var(--popover-py-default)) var(--popover-px, var(--popover-px-default));
|
|
41
78
|
background: var(--popover-bg, var(--popover-bg-default));
|
|
42
79
|
border: 1px solid var(--popover-border, var(--popover-border-default));
|
|
43
80
|
border-radius: var(--popover-radius, var(--popover-radius-default));
|
|
44
81
|
box-shadow: var(--popover-shadow, var(--popover-shadow-default));
|
|
45
|
-
font-family: var(--popover-font, var(--popover-font-default));
|
|
46
|
-
font-size: var(--popover-font-size, var(--popover-font-size-default));
|
|
47
|
-
color: var(--popover-fg, var(--popover-fg-default));
|
|
48
|
-
max-height: calc(100vh - 3rem);
|
|
49
|
-
overflow-y: auto;
|
|
50
|
-
/* Fade + lift in on first paint. @starting-style is the initial frame
|
|
51
|
-
browsers paint before the popover transitions to its open state. */
|
|
52
|
-
opacity: 1;
|
|
53
|
-
translate: 0 0;
|
|
54
|
-
transition: opacity var(--a-duration-fast) var(--a-easing-out),
|
|
55
|
-
translate var(--a-duration-fast) var(--a-easing-out);
|
|
56
82
|
}
|
|
57
83
|
|
|
58
84
|
[slot="content"]:popover-open {
|
|
@@ -65,10 +91,4 @@
|
|
|
65
91
|
@media (prefers-reduced-motion: reduce) {
|
|
66
92
|
[slot="content"] { transition: none; }
|
|
67
93
|
}
|
|
68
|
-
|
|
69
|
-
/* Collapse default margins on the first/last block child so the
|
|
70
|
-
content sits snug inside --popover-py (no phantom extra space
|
|
71
|
-
below <p>, <h*>, etc.). */
|
|
72
|
-
[slot="content"] > :first-child { margin-block-start: 0; }
|
|
73
|
-
[slot="content"] > :last-child { margin-block-end: 0; }
|
|
74
94
|
}
|
|
@@ -76,10 +76,14 @@ a2ui:
|
|
|
76
76
|
everything else (inline forms, color pickers, theme panels,
|
|
77
77
|
export menus with non-action content).
|
|
78
78
|
- >-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
(
|
|
79
|
+
Placement convention (ADR-0034): default `bottom` centers under
|
|
80
|
+
the trigger — correct for wide pickers (calendar, color,
|
|
81
|
+
date-range, filter forms). Use `bottom-start` for trigger-width
|
|
82
|
+
menus (action lists, listboxes, breadcrumb overflow — popover
|
|
83
|
+
width ≈ trigger width). Use `bottom-end` only when the trigger
|
|
84
|
+
sits at the right edge of a container by construction (toolbar
|
|
85
|
+
spillover). Use `top-*` when the trigger sits low in the
|
|
86
|
+
viewport (statusbar). [gap] (default 4px) sets offset from anchor.
|
|
83
87
|
- >-
|
|
84
88
|
[trigger="hover"] is for non-essential disclosure only — never
|
|
85
89
|
use it for popovers containing inputs, destructive actions, or
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 330 330" width="330" height="330" shape-rendering="crispEdges">
|
|
2
|
+
<rect width="100%" height="100%" fill="#fff"/>
|
|
3
|
+
<g fill="#000"><rect x="40" y="40" width="10" height="10"/><rect x="50" y="40" width="10" height="10"/><rect x="60" y="40" width="10" height="10"/><rect x="70" y="40" width="10" height="10"/><rect x="80" y="40" width="10" height="10"/><rect x="90" y="40" width="10" height="10"/><rect x="100" y="40" width="10" height="10"/><rect x="120" y="40" width="10" height="10"/><rect x="150" y="40" width="10" height="10"/><rect x="170" y="40" width="10" height="10"/><rect x="180" y="40" width="10" height="10"/><rect x="190" y="40" width="10" height="10"/><rect x="220" y="40" width="10" height="10"/><rect x="230" y="40" width="10" height="10"/><rect x="240" y="40" width="10" height="10"/><rect x="250" y="40" width="10" height="10"/><rect x="260" y="40" width="10" height="10"/><rect x="270" y="40" width="10" height="10"/><rect x="280" y="40" width="10" height="10"/><rect x="40" y="50" width="10" height="10"/><rect x="100" y="50" width="10" height="10"/><rect x="190" y="50" width="10" height="10"/><rect x="220" y="50" width="10" height="10"/><rect x="280" y="50" width="10" height="10"/><rect x="40" y="60" width="10" height="10"/><rect x="60" y="60" width="10" height="10"/><rect x="70" y="60" width="10" height="10"/><rect x="80" y="60" width="10" height="10"/><rect x="100" y="60" width="10" height="10"/><rect x="120" y="60" width="10" height="10"/><rect x="140" y="60" width="10" height="10"/><rect x="160" y="60" width="10" height="10"/><rect x="170" y="60" width="10" height="10"/><rect x="220" y="60" width="10" height="10"/><rect x="240" y="60" width="10" height="10"/><rect x="250" y="60" width="10" height="10"/><rect x="260" y="60" width="10" height="10"/><rect x="280" y="60" width="10" height="10"/><rect x="40" y="70" width="10" height="10"/><rect x="60" y="70" width="10" height="10"/><rect x="70" y="70" width="10" height="10"/><rect x="80" y="70" width="10" height="10"/><rect x="100" y="70" width="10" height="10"/><rect x="130" y="70" width="10" height="10"/><rect x="150" y="70" width="10" height="10"/><rect x="160" y="70" width="10" height="10"/><rect x="190" y="70" width="10" height="10"/><rect x="220" y="70" width="10" height="10"/><rect x="240" y="70" width="10" height="10"/><rect x="250" y="70" width="10" height="10"/><rect x="260" y="70" width="10" height="10"/><rect x="280" y="70" width="10" height="10"/><rect x="40" y="80" width="10" height="10"/><rect x="60" y="80" width="10" height="10"/><rect x="70" y="80" width="10" height="10"/><rect x="80" y="80" width="10" height="10"/><rect x="100" y="80" width="10" height="10"/><rect x="130" y="80" width="10" height="10"/><rect x="190" y="80" width="10" height="10"/><rect x="200" y="80" width="10" height="10"/><rect x="220" y="80" width="10" height="10"/><rect x="240" y="80" width="10" height="10"/><rect x="250" y="80" width="10" height="10"/><rect x="260" y="80" width="10" height="10"/><rect x="280" y="80" width="10" height="10"/><rect x="40" y="90" width="10" height="10"/><rect x="100" y="90" width="10" height="10"/><rect x="120" y="90" width="10" height="10"/><rect x="160" y="90" width="10" height="10"/><rect x="170" y="90" width="10" height="10"/><rect x="180" y="90" width="10" height="10"/><rect x="220" y="90" width="10" height="10"/><rect x="280" y="90" width="10" height="10"/><rect x="40" y="100" width="10" height="10"/><rect x="50" y="100" width="10" height="10"/><rect x="60" y="100" width="10" height="10"/><rect x="70" y="100" width="10" height="10"/><rect x="80" y="100" width="10" height="10"/><rect x="90" y="100" width="10" height="10"/><rect x="100" y="100" width="10" height="10"/><rect x="120" y="100" width="10" height="10"/><rect x="140" y="100" width="10" height="10"/><rect x="160" y="100" width="10" height="10"/><rect x="180" y="100" width="10" height="10"/><rect x="200" y="100" width="10" height="10"/><rect x="220" y="100" width="10" height="10"/><rect x="230" y="100" width="10" height="10"/><rect x="240" y="100" width="10" height="10"/><rect x="250" y="100" width="10" height="10"/><rect x="260" y="100" width="10" height="10"/><rect x="270" y="100" width="10" height="10"/><rect x="280" y="100" width="10" height="10"/><rect x="160" y="110" width="10" height="10"/><rect x="170" y="110" width="10" height="10"/><rect x="190" y="110" width="10" height="10"/><rect x="200" y="110" width="10" height="10"/><rect x="40" y="120" width="10" height="10"/><rect x="60" y="120" width="10" height="10"/><rect x="100" y="120" width="10" height="10"/><rect x="110" y="120" width="10" height="10"/><rect x="130" y="120" width="10" height="10"/><rect x="140" y="120" width="10" height="10"/><rect x="150" y="120" width="10" height="10"/><rect x="160" y="120" width="10" height="10"/><rect x="170" y="120" width="10" height="10"/><rect x="190" y="120" width="10" height="10"/><rect x="230" y="120" width="10" height="10"/><rect x="260" y="120" width="10" height="10"/><rect x="280" y="120" width="10" height="10"/><rect x="40" y="130" width="10" height="10"/><rect x="50" y="130" width="10" height="10"/><rect x="60" y="130" width="10" height="10"/><rect x="70" y="130" width="10" height="10"/><rect x="130" y="130" width="10" height="10"/><rect x="150" y="130" width="10" height="10"/><rect x="160" y="130" width="10" height="10"/><rect x="190" y="130" width="10" height="10"/><rect x="200" y="130" width="10" height="10"/><rect x="220" y="130" width="10" height="10"/><rect x="230" y="130" width="10" height="10"/><rect x="250" y="130" width="10" height="10"/><rect x="270" y="130" width="10" height="10"/><rect x="280" y="130" width="10" height="10"/><rect x="60" y="140" width="10" height="10"/><rect x="70" y="140" width="10" height="10"/><rect x="80" y="140" width="10" height="10"/><rect x="100" y="140" width="10" height="10"/><rect x="110" y="140" width="10" height="10"/><rect x="130" y="140" width="10" height="10"/><rect x="140" y="140" width="10" height="10"/><rect x="170" y="140" width="10" height="10"/><rect x="180" y="140" width="10" height="10"/><rect x="190" y="140" width="10" height="10"/><rect x="220" y="140" width="10" height="10"/><rect x="240" y="140" width="10" height="10"/><rect x="250" y="140" width="10" height="10"/><rect x="260" y="140" width="10" height="10"/><rect x="280" y="140" width="10" height="10"/><rect x="70" y="150" width="10" height="10"/><rect x="80" y="150" width="10" height="10"/><rect x="110" y="150" width="10" height="10"/><rect x="160" y="150" width="10" height="10"/><rect x="180" y="150" width="10" height="10"/><rect x="200" y="150" width="10" height="10"/><rect x="250" y="150" width="10" height="10"/><rect x="40" y="160" width="10" height="10"/><rect x="70" y="160" width="10" height="10"/><rect x="90" y="160" width="10" height="10"/><rect x="100" y="160" width="10" height="10"/><rect x="130" y="160" width="10" height="10"/><rect x="150" y="160" width="10" height="10"/><rect x="170" y="160" width="10" height="10"/><rect x="180" y="160" width="10" height="10"/><rect x="190" y="160" width="10" height="10"/><rect x="200" y="160" width="10" height="10"/><rect x="220" y="160" width="10" height="10"/><rect x="230" y="160" width="10" height="10"/><rect x="280" y="160" width="10" height="10"/><rect x="70" y="170" width="10" height="10"/><rect x="80" y="170" width="10" height="10"/><rect x="90" y="170" width="10" height="10"/><rect x="110" y="170" width="10" height="10"/><rect x="120" y="170" width="10" height="10"/><rect x="170" y="170" width="10" height="10"/><rect x="180" y="170" width="10" height="10"/><rect x="190" y="170" width="10" height="10"/><rect x="200" y="170" width="10" height="10"/><rect x="220" y="170" width="10" height="10"/><rect x="230" y="170" width="10" height="10"/><rect x="270" y="170" width="10" height="10"/><rect x="280" y="170" width="10" height="10"/><rect x="40" y="180" width="10" height="10"/><rect x="50" y="180" width="10" height="10"/><rect x="60" y="180" width="10" height="10"/><rect x="90" y="180" width="10" height="10"/><rect x="100" y="180" width="10" height="10"/><rect x="110" y="180" width="10" height="10"/><rect x="130" y="180" width="10" height="10"/><rect x="150" y="180" width="10" height="10"/><rect x="190" y="180" width="10" height="10"/><rect x="200" y="180" width="10" height="10"/><rect x="210" y="180" width="10" height="10"/><rect x="250" y="180" width="10" height="10"/><rect x="260" y="180" width="10" height="10"/><rect x="280" y="180" width="10" height="10"/><rect x="60" y="190" width="10" height="10"/><rect x="80" y="190" width="10" height="10"/><rect x="90" y="190" width="10" height="10"/><rect x="120" y="190" width="10" height="10"/><rect x="130" y="190" width="10" height="10"/><rect x="140" y="190" width="10" height="10"/><rect x="170" y="190" width="10" height="10"/><rect x="200" y="190" width="10" height="10"/><rect x="210" y="190" width="10" height="10"/><rect x="230" y="190" width="10" height="10"/><rect x="240" y="190" width="10" height="10"/><rect x="250" y="190" width="10" height="10"/><rect x="40" y="200" width="10" height="10"/><rect x="50" y="200" width="10" height="10"/><rect x="70" y="200" width="10" height="10"/><rect x="90" y="200" width="10" height="10"/><rect x="100" y="200" width="10" height="10"/><rect x="110" y="200" width="10" height="10"/><rect x="120" y="200" width="10" height="10"/><rect x="150" y="200" width="10" height="10"/><rect x="160" y="200" width="10" height="10"/><rect x="170" y="200" width="10" height="10"/><rect x="190" y="200" width="10" height="10"/><rect x="200" y="200" width="10" height="10"/><rect x="210" y="200" width="10" height="10"/><rect x="220" y="200" width="10" height="10"/><rect x="230" y="200" width="10" height="10"/><rect x="240" y="200" width="10" height="10"/><rect x="270" y="200" width="10" height="10"/><rect x="120" y="210" width="10" height="10"/><rect x="130" y="210" width="10" height="10"/><rect x="140" y="210" width="10" height="10"/><rect x="160" y="210" width="10" height="10"/><rect x="200" y="210" width="10" height="10"/><rect x="240" y="210" width="10" height="10"/><rect x="280" y="210" width="10" height="10"/><rect x="40" y="220" width="10" height="10"/><rect x="50" y="220" width="10" height="10"/><rect x="60" y="220" width="10" height="10"/><rect x="70" y="220" width="10" height="10"/><rect x="80" y="220" width="10" height="10"/><rect x="90" y="220" width="10" height="10"/><rect x="100" y="220" width="10" height="10"/><rect x="120" y="220" width="10" height="10"/><rect x="160" y="220" width="10" height="10"/><rect x="170" y="220" width="10" height="10"/><rect x="180" y="220" width="10" height="10"/><rect x="200" y="220" width="10" height="10"/><rect x="220" y="220" width="10" height="10"/><rect x="240" y="220" width="10" height="10"/><rect x="280" y="220" width="10" height="10"/><rect x="40" y="230" width="10" height="10"/><rect x="100" y="230" width="10" height="10"/><rect x="130" y="230" width="10" height="10"/><rect x="140" y="230" width="10" height="10"/><rect x="150" y="230" width="10" height="10"/><rect x="160" y="230" width="10" height="10"/><rect x="180" y="230" width="10" height="10"/><rect x="190" y="230" width="10" height="10"/><rect x="200" y="230" width="10" height="10"/><rect x="240" y="230" width="10" height="10"/><rect x="40" y="240" width="10" height="10"/><rect x="60" y="240" width="10" height="10"/><rect x="70" y="240" width="10" height="10"/><rect x="80" y="240" width="10" height="10"/><rect x="100" y="240" width="10" height="10"/><rect x="130" y="240" width="10" height="10"/><rect x="170" y="240" width="10" height="10"/><rect x="180" y="240" width="10" height="10"/><rect x="190" y="240" width="10" height="10"/><rect x="200" y="240" width="10" height="10"/><rect x="210" y="240" width="10" height="10"/><rect x="220" y="240" width="10" height="10"/><rect x="230" y="240" width="10" height="10"/><rect x="240" y="240" width="10" height="10"/><rect x="270" y="240" width="10" height="10"/><rect x="280" y="240" width="10" height="10"/><rect x="40" y="250" width="10" height="10"/><rect x="60" y="250" width="10" height="10"/><rect x="70" y="250" width="10" height="10"/><rect x="80" y="250" width="10" height="10"/><rect x="100" y="250" width="10" height="10"/><rect x="140" y="250" width="10" height="10"/><rect x="170" y="250" width="10" height="10"/><rect x="180" y="250" width="10" height="10"/><rect x="200" y="250" width="10" height="10"/><rect x="210" y="250" width="10" height="10"/><rect x="240" y="250" width="10" height="10"/><rect x="260" y="250" width="10" height="10"/><rect x="270" y="250" width="10" height="10"/><rect x="40" y="260" width="10" height="10"/><rect x="60" y="260" width="10" height="10"/><rect x="70" y="260" width="10" height="10"/><rect x="80" y="260" width="10" height="10"/><rect x="100" y="260" width="10" height="10"/><rect x="120" y="260" width="10" height="10"/><rect x="150" y="260" width="10" height="10"/><rect x="210" y="260" width="10" height="10"/><rect x="230" y="260" width="10" height="10"/><rect x="240" y="260" width="10" height="10"/><rect x="250" y="260" width="10" height="10"/><rect x="270" y="260" width="10" height="10"/><rect x="280" y="260" width="10" height="10"/><rect x="40" y="270" width="10" height="10"/><rect x="100" y="270" width="10" height="10"/><rect x="130" y="270" width="10" height="10"/><rect x="170" y="270" width="10" height="10"/><rect x="200" y="270" width="10" height="10"/><rect x="230" y="270" width="10" height="10"/><rect x="240" y="270" width="10" height="10"/><rect x="40" y="280" width="10" height="10"/><rect x="50" y="280" width="10" height="10"/><rect x="60" y="280" width="10" height="10"/><rect x="70" y="280" width="10" height="10"/><rect x="80" y="280" width="10" height="10"/><rect x="90" y="280" width="10" height="10"/><rect x="100" y="280" width="10" height="10"/><rect x="120" y="280" width="10" height="10"/><rect x="130" y="280" width="10" height="10"/><rect x="150" y="280" width="10" height="10"/><rect x="160" y="280" width="10" height="10"/><rect x="170" y="280" width="10" height="10"/><rect x="190" y="280" width="10" height="10"/><rect x="200" y="280" width="10" height="10"/><rect x="220" y="280" width="10" height="10"/><rect x="250" y="280" width="10" height="10"/><rect x="280" y="280" width="10" height="10"/></g>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/QRCode.json",
|
|
4
|
+
"title": "QRCode",
|
|
5
|
+
"description": "Inline QR code generator — renders a scannable code as inline SVG.\nTwo modes:\n\n**A. Auto-encode (default).** Set `[value]` to a string (URL, plain\ntext, share link, 2FA secret). The component encodes the data with\nthe built-in zero-deps QR encoder (byte mode, ECC level configurable,\nversions 1–10 up to ~150 byte chars) and renders an SVG.\n\n**B. Bring-your-own matrix.** Set `[matrix]` to a JSON string of a\n2-D 0/1 array (`[[1,0,1,...],[...],...]`). The component renders the\nsupplied matrix directly without invoking its own encoder. Use this\nwhen you need versions 11–40, kanji/alphanumeric mode, or any\nencoder feature beyond the built-in. When both `[value]` and\n`[matrix]` are set, the matrix wins (BYO override).\n\nOutput is a single `<svg>` with `viewBox=\"0 0 N N\"`. Crisp-edges\nrendering preserves the scannable cell boundaries at any size.\nSizing is controlled by the `[size]` prop (host CSS-pixel dimension);\ninternal cell size is computed from `size / (matrix-size + 2 ·\nmargin)`. ARIA: `role=\"img\"` with `aria-label` (default \"QR code\"\nor the `[label]` prop).\n",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"allOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "common_types.json#/$defs/ComponentCommon"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "common_types.json#/$defs/CatalogComponentCommon"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"background": {
|
|
17
|
+
"description": "Background (light-cell) color. **Empty defaults to `#ffffff` —\nhardcoded white** alongside the black foreground, for guaranteed\nscanability. Override only when paired with an explicit\n[color] (the contrast pair must scan).\n",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"default": ""
|
|
20
|
+
},
|
|
21
|
+
"color": {
|
|
22
|
+
"description": "Foreground (dark-cell) color. Any CSS color string (hex, rgb,\ntoken reference). **Empty defaults to `#000000` — hardcoded black\nfor guaranteed scanability** (theme-aware `currentColor` would\nproduce light-on-dark in dark mode, which most phone scanners\nrefuse to decode). Override for branded QRs only when you've\nverified the chosen color/background pair scans on the target\ndevices.\n",
|
|
23
|
+
"type": "string",
|
|
24
|
+
"default": ""
|
|
25
|
+
},
|
|
26
|
+
"component": {
|
|
27
|
+
"const": "QRCode"
|
|
28
|
+
},
|
|
29
|
+
"errorCorrection": {
|
|
30
|
+
"description": "Error-correction level (auto-encode mode only). `L` (7% recovery),\n`M` (15%, default), `Q` (25%), `H` (30%). Higher levels recover\nfrom more damage at the cost of QR-version size growth.\n",
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": [
|
|
33
|
+
"L",
|
|
34
|
+
"M",
|
|
35
|
+
"Q",
|
|
36
|
+
"H"
|
|
37
|
+
],
|
|
38
|
+
"default": "M"
|
|
39
|
+
},
|
|
40
|
+
"label": {
|
|
41
|
+
"description": "`aria-label` for the SVG. Defaults to \"QR code\". Customize for\nbetter AT context (e.g. \"Share link QR code\", \"2FA setup QR\").\n",
|
|
42
|
+
"type": "string",
|
|
43
|
+
"default": "QR code"
|
|
44
|
+
},
|
|
45
|
+
"margin": {
|
|
46
|
+
"description": "Quiet zone in cells around the QR pattern. QR spec requires a\nminimum 4-cell margin for reliable scanning; smaller values may\nnot scan on some devices. Default `4`.\n",
|
|
47
|
+
"type": "number",
|
|
48
|
+
"default": 4
|
|
49
|
+
},
|
|
50
|
+
"matrix": {
|
|
51
|
+
"description": "BYO precomputed bit matrix as JSON — a 2-D array of 0/1\n(`[[1,0,...],[0,1,...],...]`). When set, the built-in encoder is\nbypassed and this matrix renders directly. Useful for QR variants\nthe built-in doesn't support (versions 11–40, alphanumeric mode,\nkanji, etc.) — bring your own encoder, pass the matrix here.\n",
|
|
52
|
+
"type": "string",
|
|
53
|
+
"default": ""
|
|
54
|
+
},
|
|
55
|
+
"size": {
|
|
56
|
+
"description": "Host display size in CSS pixels (width and height; the SVG is\nalways square). Default 200. Internal cell size is computed from\nthis divided by `matrix-size + 2·margin`.\n",
|
|
57
|
+
"type": "number",
|
|
58
|
+
"default": 200
|
|
59
|
+
},
|
|
60
|
+
"value": {
|
|
61
|
+
"description": "The string to encode (URL / text / share link / 2FA seed). When\n[matrix] is also set, the matrix wins. Empty value + empty matrix\nrenders nothing.\n",
|
|
62
|
+
"type": "string",
|
|
63
|
+
"default": ""
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"required": [
|
|
67
|
+
"component"
|
|
68
|
+
],
|
|
69
|
+
"unevaluatedProperties": false,
|
|
70
|
+
"x-adiaui": {
|
|
71
|
+
"anti_patterns": [
|
|
72
|
+
{
|
|
73
|
+
"fix": "Drop the ECC level (lower L gives more capacity for the same\nversion), OR shorten the URL (use a URL shortener), OR encode\nwith a BYO encoder library + pass the [matrix]:\n`<qr-code-ui matrix=\"[[1,0,1,...],...]\"></qr-code-ui>`\n",
|
|
74
|
+
"why": "Built-in encoder covers versions 1–10. Data beyond v10 capacity\nthrows + renders empty + sets [data-error]. Console warns with\nthe byte length so consumers can diagnose.\n",
|
|
75
|
+
"wrong": "<qr-code-ui value=\"https://very.long.url.that.exceeds.150.bytes.of.utf8.encoded.length...\"></qr-code-ui>\n"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"fix": "<qr-code-ui value=\"...\" margin=\"4\"></qr-code-ui>\n",
|
|
79
|
+
"why": "Zero margin breaks scanning. QR spec requires a 4-cell quiet\nzone for reliable detection.\n",
|
|
80
|
+
"wrong": "<qr-code-ui value=\"...\" margin=\"0\"></qr-code-ui>\n"
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"category": "display",
|
|
84
|
+
"composes": [],
|
|
85
|
+
"events": {},
|
|
86
|
+
"examples": [
|
|
87
|
+
{
|
|
88
|
+
"description": "Plain URL encoding at default size + ECC-M.",
|
|
89
|
+
"a2ui": "[\n {\n \"id\": \"q\",\n \"component\": \"QRCode\",\n \"value\": \"https://example.com\"\n }\n]\n",
|
|
90
|
+
"name": "default"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"description": "Custom colors matching brand chrome.",
|
|
94
|
+
"a2ui": "[\n {\n \"id\": \"q\",\n \"component\": \"QRCode\",\n \"value\": \"https://adia.health/share/abc123\",\n \"size\": 280,\n \"color\": \"#1e293b\",\n \"background\": \"#ffffff\",\n \"errorCorrection\": \"Q\"\n }\n]\n",
|
|
95
|
+
"name": "branded"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"description": "BYO precomputed matrix from an external encoder.",
|
|
99
|
+
"a2ui": "[\n {\n \"id\": \"q\",\n \"component\": \"QRCode\",\n \"matrix\": \"[[1,1,1,1,1,1,1,0,1,...]...]\",\n \"size\": 200,\n \"label\": \"Boarding pass QR\"\n }\n]\n",
|
|
100
|
+
"name": "byo-matrix"
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"keywords": [
|
|
104
|
+
"qr-code",
|
|
105
|
+
"qr",
|
|
106
|
+
"barcode",
|
|
107
|
+
"scan",
|
|
108
|
+
"share",
|
|
109
|
+
"2fa",
|
|
110
|
+
"share-link"
|
|
111
|
+
],
|
|
112
|
+
"name": "UIQRCode",
|
|
113
|
+
"related": [
|
|
114
|
+
"link",
|
|
115
|
+
"share"
|
|
116
|
+
],
|
|
117
|
+
"slots": {},
|
|
118
|
+
"states": [
|
|
119
|
+
{
|
|
120
|
+
"description": "Default — QR rendered (or empty if no value/matrix).",
|
|
121
|
+
"name": "idle"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"description": "Auto-encode failed (data too long for v1–10 at requested ECC). Renders an empty placeholder; logs a console warning. Switch to a BYO matrix from an external encoder, or downgrade the ECC level.",
|
|
125
|
+
"attribute": "data-error",
|
|
126
|
+
"name": "error"
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"status": "stable",
|
|
130
|
+
"synonyms": {
|
|
131
|
+
"qr": [
|
|
132
|
+
"qr-code",
|
|
133
|
+
"barcode"
|
|
134
|
+
],
|
|
135
|
+
"scan": [
|
|
136
|
+
"qr-code",
|
|
137
|
+
"barcode"
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
"tag": "qr-code-ui",
|
|
141
|
+
"tokens": {
|
|
142
|
+
"--qr-code-bg": {
|
|
143
|
+
"description": "Background (light) color when [background] is empty.",
|
|
144
|
+
"default": "transparent"
|
|
145
|
+
},
|
|
146
|
+
"--qr-code-fg": {
|
|
147
|
+
"description": "Foreground (dark) color when [color] is empty.",
|
|
148
|
+
"default": "currentColor"
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"traits": [],
|
|
152
|
+
"version": 1
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-side-effect class export for `<qr-code-ui>`.
|
|
3
|
+
*
|
|
4
|
+
* Importing this file gives you the class without auto-registering the
|
|
5
|
+
* tag. Useful for test isolation, subclassing with tag-name override,
|
|
6
|
+
* or selective composition.
|
|
7
|
+
*
|
|
8
|
+
* The auto-register path stays at `@adia-ai/web-components/components/qr-code`
|
|
9
|
+
* (which imports this file + calls `defineIfFree()`).
|
|
10
|
+
*
|
|
11
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* <qr-code-ui value="https://example.com" size="200"></qr-code-ui>
|
|
16
|
+
*
|
|
17
|
+
* Two modes:
|
|
18
|
+
* A. Auto-encode (default) — set `[value]`, built-in encoder runs.
|
|
19
|
+
* B. BYO matrix — set `[matrix]` (JSON 2-D 0/1 array), bypass the encoder.
|
|
20
|
+
*
|
|
21
|
+
* Matrix wins when both are set. Empty value + empty matrix renders
|
|
22
|
+
* nothing.
|
|
23
|
+
*
|
|
24
|
+
* @see ./qr-encoder.js for the encoder implementation.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { UIElement } from '../../core/element.js';
|
|
28
|
+
import { encodeQR, matrixToSVG } from './qr-encoder.js';
|
|
29
|
+
|
|
30
|
+
export class UIQRCode extends UIElement {
|
|
31
|
+
static properties = {
|
|
32
|
+
value: { type: String, default: '', reflect: true },
|
|
33
|
+
matrix: { type: String, default: '', reflect: true },
|
|
34
|
+
size: { type: Number, default: 200, reflect: true },
|
|
35
|
+
errorCorrection: { type: String, default: 'M', reflect: true, attribute: 'error-correction' },
|
|
36
|
+
margin: { type: Number, default: 4, reflect: true },
|
|
37
|
+
color: { type: String, default: '', reflect: true },
|
|
38
|
+
background: { type: String, default: '', reflect: true },
|
|
39
|
+
label: { type: String, default: 'QR code', reflect: true },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
static template = () => null;
|
|
43
|
+
|
|
44
|
+
#lastSig = '';
|
|
45
|
+
|
|
46
|
+
#parseMatrix(raw) {
|
|
47
|
+
if (!raw) return null;
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(raw);
|
|
50
|
+
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
51
|
+
if (!Array.isArray(parsed[0])) return null;
|
|
52
|
+
// Coerce to 0/1
|
|
53
|
+
return parsed.map((row) => row.map((v) => (v ? 1 : 0)));
|
|
54
|
+
} catch (e) {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.warn('[qr-code-ui] invalid [matrix] JSON:', e.message);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#resolveMatrix() {
|
|
62
|
+
// BYO matrix wins
|
|
63
|
+
const byo = this.#parseMatrix(this.matrix);
|
|
64
|
+
if (byo) {
|
|
65
|
+
this.removeAttribute('data-error');
|
|
66
|
+
return byo;
|
|
67
|
+
}
|
|
68
|
+
if (!this.value) {
|
|
69
|
+
this.removeAttribute('data-error');
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const result = encodeQR(this.value, { errorCorrection: this.errorCorrection || 'M' });
|
|
74
|
+
this.removeAttribute('data-error');
|
|
75
|
+
return result.matrix;
|
|
76
|
+
} catch (e) {
|
|
77
|
+
this.setAttribute('data-error', '');
|
|
78
|
+
// eslint-disable-next-line no-console
|
|
79
|
+
console.warn(`[qr-code-ui] encode failed for value (${this.value?.length || 0} chars at ECC=${this.errorCorrection}): ${e.message}. Lower ECC, shorten input, or supply a precomputed [matrix].`);
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
connected() {
|
|
85
|
+
super.connected();
|
|
86
|
+
this.setAttribute('role', 'img');
|
|
87
|
+
if (!this.hasAttribute('aria-label')) this.setAttribute('aria-label', this.label || 'QR code');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
render() {
|
|
91
|
+
// Cheap idempotence: compute a signature of inputs, skip re-render
|
|
92
|
+
// if nothing changed (encoding is non-trivial — version selection +
|
|
93
|
+
// RS ECC + 8 mask evaluations).
|
|
94
|
+
const sig = `${this.value}|${this.matrix}|${this.size}|${this.errorCorrection}|${this.margin}|${this.color}|${this.background}`;
|
|
95
|
+
if (sig === this.#lastSig && this.querySelector('svg')) return;
|
|
96
|
+
this.#lastSig = sig;
|
|
97
|
+
|
|
98
|
+
// Keep aria-label fresh from prop
|
|
99
|
+
if (this.label) this.setAttribute('aria-label', this.label);
|
|
100
|
+
|
|
101
|
+
const matrix = this.#resolveMatrix();
|
|
102
|
+
if (!matrix) {
|
|
103
|
+
this.innerHTML = '';
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const svg = matrixToSVG(matrix, {
|
|
108
|
+
cellSize: 8, // arbitrary internal unit; host CSS controls actual size via [size]
|
|
109
|
+
margin: Number.isFinite(this.margin) ? this.margin : 4,
|
|
110
|
+
// Hardcoded fallback to black-on-white — the scannable convention.
|
|
111
|
+
// Theme-aware `currentColor` produces light-on-dark in dark mode,
|
|
112
|
+
// which most phone cameras (iPhone Camera in particular) refuse to
|
|
113
|
+
// decode. Consumers wanting brand chrome must set [color] +
|
|
114
|
+
// [background] explicitly; the bare default ALWAYS scans.
|
|
115
|
+
color: this.color || '#000000',
|
|
116
|
+
background: this.background || '#ffffff',
|
|
117
|
+
});
|
|
118
|
+
this.innerHTML = svg;
|
|
119
|
+
|
|
120
|
+
// Lock the visual size on the SVG root (CSS host dims would also
|
|
121
|
+
// work, but explicit width/height on the SVG keeps the box clear
|
|
122
|
+
// when the host is laid out as inline content).
|
|
123
|
+
const svgEl = this.querySelector('svg');
|
|
124
|
+
if (svgEl && Number.isFinite(this.size)) {
|
|
125
|
+
svgEl.setAttribute('width', String(this.size));
|
|
126
|
+
svgEl.setAttribute('height', String(this.size));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
2
|
+
QR-CODE-UI — Inline scannable QR code.
|
|
3
|
+
═══════════════════════════════════════════════════════════════ */
|
|
4
|
+
|
|
5
|
+
@scope (qr-code-ui) {
|
|
6
|
+
:where(:scope) {
|
|
7
|
+
--qr-code-fg-default: currentColor;
|
|
8
|
+
--qr-code-bg-default: transparent;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
:scope {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
display: inline-block;
|
|
14
|
+
/* The SVG itself carries explicit width/height; this is a fallback
|
|
15
|
+
for cases where the SVG hasn't stamped yet (empty value/matrix). */
|
|
16
|
+
line-height: 0;
|
|
17
|
+
color: var(--qr-code-fg, var(--qr-code-fg-default));
|
|
18
|
+
background: var(--qr-code-bg, var(--qr-code-bg-default));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
:scope svg {
|
|
22
|
+
display: block;
|
|
23
|
+
/* Width / height come from the explicit SVG attributes set by JS
|
|
24
|
+
per the [size] prop. CSS `width: 100%` would stretch to fill the
|
|
25
|
+
host, but the host is inline-block sized-to-content (the SVG
|
|
26
|
+
itself), which produces a circular sizing dependency in some
|
|
27
|
+
browsers + zero-height in others. Trust the SVG attributes. */
|
|
28
|
+
shape-rendering: crispEdges;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Encode-error state — caller passed data too long for v1-10 at
|
|
32
|
+
the configured ECC. Render an empty placeholder; the warning
|
|
33
|
+
went to console. Visually: a dashed outline at the requested
|
|
34
|
+
size so the layout doesn't collapse silently. */
|
|
35
|
+
:scope[data-error] {
|
|
36
|
+
border: 1px dashed var(--a-border-strong);
|
|
37
|
+
border-radius: var(--a-radius-sm);
|
|
38
|
+
min-width: 4em;
|
|
39
|
+
min-height: 4em;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<qr-code-ui>` — Inline QR code generator — renders a scannable code as inline SVG.
|
|
3
|
+
Two modes:
|
|
4
|
+
|
|
5
|
+
**A. Auto-encode (default).** Set `[value]` to a string (URL, plain
|
|
6
|
+
text, share link, 2FA secret). The component encodes the data with
|
|
7
|
+
the built-in zero-deps QR encoder (byte mode, ECC level configurable,
|
|
8
|
+
versions 1–10 up to ~150 byte chars) and renders an SVG.
|
|
9
|
+
|
|
10
|
+
**B. Bring-your-own matrix.** Set `[matrix]` to a JSON string of a
|
|
11
|
+
2-D 0/1 array (`[[1,0,1,...],[...],...]`). The component renders the
|
|
12
|
+
supplied matrix directly without invoking its own encoder. Use this
|
|
13
|
+
when you need versions 11–40, kanji/alphanumeric mode, or any
|
|
14
|
+
encoder feature beyond the built-in. When both `[value]` and
|
|
15
|
+
`[matrix]` are set, the matrix wins (BYO override).
|
|
16
|
+
|
|
17
|
+
Output is a single `<svg>` with `viewBox="0 0 N N"`. Crisp-edges
|
|
18
|
+
rendering preserves the scannable cell boundaries at any size.
|
|
19
|
+
Sizing is controlled by the `[size]` prop (host CSS-pixel dimension);
|
|
20
|
+
internal cell size is computed from `size / (matrix-size + 2 ·
|
|
21
|
+
margin)`. ARIA: `role="img"` with `aria-label` (default "QR code"
|
|
22
|
+
or the `[label]` prop).
|
|
23
|
+
|
|
24
|
+
*
|
|
25
|
+
* @see https://ui-kit.exe.xyz/site/components/qr-code
|
|
26
|
+
*
|
|
27
|
+
* Type declarations generated by scripts/build/dts-codegen.mjs from
|
|
28
|
+
* the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
|
|
29
|
+
* run `npm run build:components`, then `npm run codegen:dts` to
|
|
30
|
+
* regenerate; or hand-author this file fully if rich event types are
|
|
31
|
+
* needed beyond what the yaml `events:` block can express.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { UIElement } from '../../core/element.js';
|
|
35
|
+
|
|
36
|
+
export class UIQRCode extends UIElement {
|
|
37
|
+
/** Background (light-cell) color. **Empty defaults to `#ffffff` —
|
|
38
|
+
hardcoded white** alongside the black foreground, for guaranteed
|
|
39
|
+
scanability. Override only when paired with an explicit
|
|
40
|
+
[color] (the contrast pair must scan).
|
|
41
|
+
*/
|
|
42
|
+
background: string;
|
|
43
|
+
/** Foreground (dark-cell) color. Any CSS color string (hex, rgb,
|
|
44
|
+
token reference). **Empty defaults to `#000000` — hardcoded black
|
|
45
|
+
for guaranteed scanability** (theme-aware `currentColor` would
|
|
46
|
+
produce light-on-dark in dark mode, which most phone scanners
|
|
47
|
+
refuse to decode). Override for branded QRs only when you've
|
|
48
|
+
verified the chosen color/background pair scans on the target
|
|
49
|
+
devices.
|
|
50
|
+
*/
|
|
51
|
+
color: string;
|
|
52
|
+
/** Error-correction level (auto-encode mode only). `L` (7% recovery),
|
|
53
|
+
`M` (15%, default), `Q` (25%), `H` (30%). Higher levels recover
|
|
54
|
+
from more damage at the cost of QR-version size growth.
|
|
55
|
+
*/
|
|
56
|
+
errorCorrection: 'L' | 'M' | 'Q' | 'H';
|
|
57
|
+
/** `aria-label` for the SVG. Defaults to "QR code". Customize for
|
|
58
|
+
better AT context (e.g. "Share link QR code", "2FA setup QR").
|
|
59
|
+
*/
|
|
60
|
+
label: string;
|
|
61
|
+
/** Quiet zone in cells around the QR pattern. QR spec requires a
|
|
62
|
+
minimum 4-cell margin for reliable scanning; smaller values may
|
|
63
|
+
not scan on some devices. Default `4`.
|
|
64
|
+
*/
|
|
65
|
+
margin: number;
|
|
66
|
+
/** BYO precomputed bit matrix as JSON — a 2-D array of 0/1
|
|
67
|
+
(`[[1,0,...],[0,1,...],...]`). When set, the built-in encoder is
|
|
68
|
+
bypassed and this matrix renders directly. Useful for QR variants
|
|
69
|
+
the built-in doesn't support (versions 11–40, alphanumeric mode,
|
|
70
|
+
kanji, etc.) — bring your own encoder, pass the matrix here.
|
|
71
|
+
*/
|
|
72
|
+
matrix: string;
|
|
73
|
+
/** Host display size in CSS pixels (width and height; the SVG is
|
|
74
|
+
always square). Default 200. Internal cell size is computed from
|
|
75
|
+
this divided by `matrix-size + 2·margin`.
|
|
76
|
+
*/
|
|
77
|
+
size: number;
|
|
78
|
+
/** The string to encode (URL / text / share link / 2FA seed). When
|
|
79
|
+
[matrix] is also set, the matrix wins. Empty value + empty matrix
|
|
80
|
+
renders nothing.
|
|
81
|
+
*/
|
|
82
|
+
value: string;
|
|
83
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<qr-code-ui>` — auto-registers the tag on import.
|
|
3
|
+
*
|
|
4
|
+
* For non-side-effect class import (test isolation, tag override), use
|
|
5
|
+
* the `class` subpath:
|
|
6
|
+
*
|
|
7
|
+
* import { UIQRCode } from '@adia-ai/web-components/components/qr-code/class';
|
|
8
|
+
*
|
|
9
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { defineIfFree } from '../../core/register.js';
|
|
13
|
+
import { UIQRCode } from './qr-code.class.js';
|
|
14
|
+
|
|
15
|
+
defineIfFree('qr-code-ui', UIQRCode);
|
|
16
|
+
|
|
17
|
+
export { UIQRCode };
|