@adia-ai/web-components 0.6.36 → 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.
Files changed (115) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/components/badge/badge.a2ui.json +10 -0
  3. package/components/badge/badge.css +70 -0
  4. package/components/badge/badge.yaml +20 -0
  5. package/components/blockquote/blockquote.a2ui.json +121 -0
  6. package/components/blockquote/blockquote.class.js +68 -0
  7. package/components/blockquote/blockquote.css +46 -0
  8. package/components/blockquote/blockquote.d.ts +31 -0
  9. package/components/blockquote/blockquote.js +17 -0
  10. package/components/blockquote/blockquote.yaml +124 -0
  11. package/components/button/button.css +11 -3
  12. package/components/calendar-picker/calendar-picker.a2ui.json +15 -0
  13. package/components/calendar-picker/calendar-picker.class.js +7 -1
  14. package/components/calendar-picker/calendar-picker.yaml +14 -0
  15. package/components/color-input/color-input.a2ui.json +2 -2
  16. package/components/color-input/color-input.class.js +9 -2
  17. package/components/color-input/color-input.yaml +2 -2
  18. package/components/combobox/combobox.class.js +4 -0
  19. package/components/context-menu/context-menu.a2ui.json +159 -0
  20. package/components/context-menu/context-menu.class.js +275 -0
  21. package/components/context-menu/context-menu.css +56 -0
  22. package/components/context-menu/context-menu.d.ts +70 -0
  23. package/components/context-menu/context-menu.js +17 -0
  24. package/components/context-menu/context-menu.yaml +136 -0
  25. package/components/date-range-picker/date-range-picker.a2ui.json +15 -0
  26. package/components/date-range-picker/date-range-picker.class.js +2 -0
  27. package/components/date-range-picker/date-range-picker.yaml +14 -0
  28. package/components/datetime-picker/datetime-picker.a2ui.json +15 -0
  29. package/components/datetime-picker/datetime-picker.class.js +3 -1
  30. package/components/datetime-picker/datetime-picker.d.ts +2 -0
  31. package/components/datetime-picker/datetime-picker.yaml +14 -0
  32. package/components/empty-state/empty-state.class.js +2 -0
  33. package/components/feed/feed.class.js +13 -5
  34. package/components/feed/feed.css +14 -0
  35. package/components/index.js +9 -0
  36. package/components/integration-card/integration-card.class.js +9 -0
  37. package/components/integration-card/integration-card.test.js +4 -3
  38. package/components/nav-group/nav-group.css +7 -1
  39. package/components/number-format/number-format.a2ui.json +180 -0
  40. package/components/number-format/number-format.class.js +96 -0
  41. package/components/number-format/number-format.css +18 -0
  42. package/components/number-format/number-format.d.ts +68 -0
  43. package/components/number-format/number-format.js +17 -0
  44. package/components/number-format/number-format.yaml +204 -0
  45. package/components/pagination/pagination.a2ui.json +19 -2
  46. package/components/pagination/pagination.class.js +90 -37
  47. package/components/pagination/pagination.css +32 -127
  48. package/components/pagination/pagination.d.ts +8 -2
  49. package/components/pagination/pagination.test.js +195 -0
  50. package/components/pagination/pagination.yaml +22 -1
  51. package/components/password-strength/password-strength.a2ui.json +152 -0
  52. package/components/password-strength/password-strength.class.js +157 -0
  53. package/components/password-strength/password-strength.css +80 -0
  54. package/components/password-strength/password-strength.d.ts +59 -0
  55. package/components/password-strength/password-strength.js +17 -0
  56. package/components/password-strength/password-strength.yaml +153 -0
  57. package/components/popover/popover.css +43 -23
  58. package/components/popover/popover.yaml +8 -4
  59. package/components/qr-code/QR-TEST.svg +4 -0
  60. package/components/qr-code/qr-code.a2ui.json +154 -0
  61. package/components/qr-code/qr-code.class.js +129 -0
  62. package/components/qr-code/qr-code.css +41 -0
  63. package/components/qr-code/qr-code.d.ts +83 -0
  64. package/components/qr-code/qr-code.js +17 -0
  65. package/components/qr-code/qr-code.yaml +203 -0
  66. package/components/qr-code/qr-encoder.js +633 -0
  67. package/components/relative-time/relative-time.a2ui.json +120 -0
  68. package/components/relative-time/relative-time.class.js +136 -0
  69. package/components/relative-time/relative-time.css +22 -0
  70. package/components/relative-time/relative-time.d.ts +51 -0
  71. package/components/relative-time/relative-time.js +17 -0
  72. package/components/relative-time/relative-time.yaml +133 -0
  73. package/components/segmented/segmented.class.js +5 -1
  74. package/components/select/select.class.js +4 -0
  75. package/components/skip-nav/skip-nav.a2ui.json +92 -0
  76. package/components/skip-nav/skip-nav.class.js +45 -0
  77. package/components/skip-nav/skip-nav.css +54 -0
  78. package/components/skip-nav/skip-nav.d.ts +27 -0
  79. package/components/skip-nav/skip-nav.js +12 -0
  80. package/components/skip-nav/skip-nav.yaml +68 -0
  81. package/components/slider/slider.a2ui.json +16 -1
  82. package/components/slider/slider.class.js +264 -122
  83. package/components/slider/slider.css +82 -2
  84. package/components/slider/slider.d.ts +19 -3
  85. package/components/slider/slider.test.js +55 -0
  86. package/components/slider/slider.yaml +28 -6
  87. package/components/table/table.class.js +29 -6
  88. package/components/table/table.css +31 -4
  89. package/components/table-toolbar/table-toolbar.class.js +3 -1
  90. package/components/tag/tag.a2ui.json +3 -2
  91. package/components/tag/tag.css +35 -11
  92. package/components/tag/tag.d.ts +14 -0
  93. package/components/tag/tag.test.js +35 -11
  94. package/components/tag/tag.yaml +13 -7
  95. package/components/toast/toast.class.js +12 -4
  96. package/components/toc/toc.a2ui.json +159 -0
  97. package/components/toc/toc.class.js +222 -0
  98. package/components/toc/toc.css +92 -0
  99. package/components/toc/toc.d.ts +61 -0
  100. package/components/toc/toc.js +17 -0
  101. package/components/toc/toc.yaml +180 -0
  102. package/components/toolbar/toolbar.class.js +3 -0
  103. package/components/visually-hidden/visually-hidden.a2ui.json +71 -0
  104. package/components/visually-hidden/visually-hidden.class.js +14 -0
  105. package/components/visually-hidden/visually-hidden.css +25 -0
  106. package/components/visually-hidden/visually-hidden.d.ts +26 -0
  107. package/components/visually-hidden/visually-hidden.js +12 -0
  108. package/components/visually-hidden/visually-hidden.yaml +54 -0
  109. package/core/anchor.js +19 -3
  110. package/dist/web-components.min.css +1 -1
  111. package/dist/web-components.min.js +100 -89
  112. package/package.json +1 -1
  113. package/styles/colors/semantics.css +11 -2
  114. package/styles/components.css +9 -0
  115. package/styles/resets.css +10 -0
@@ -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 };
@@ -0,0 +1,203 @@
1
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
2
+ name: UIQRCode
3
+ tag: qr-code-ui
4
+ status: stable
5
+ component: QRCode
6
+ category: display
7
+ version: 1
8
+ description: |
9
+ Inline QR code generator — renders a scannable code as inline SVG.
10
+ Two modes:
11
+
12
+ **A. Auto-encode (default).** Set `[value]` to a string (URL, plain
13
+ text, share link, 2FA secret). The component encodes the data with
14
+ the built-in zero-deps QR encoder (byte mode, ECC level configurable,
15
+ versions 1–10 up to ~150 byte chars) and renders an SVG.
16
+
17
+ **B. Bring-your-own matrix.** Set `[matrix]` to a JSON string of a
18
+ 2-D 0/1 array (`[[1,0,1,...],[...],...]`). The component renders the
19
+ supplied matrix directly without invoking its own encoder. Use this
20
+ when you need versions 11–40, kanji/alphanumeric mode, or any
21
+ encoder feature beyond the built-in. When both `[value]` and
22
+ `[matrix]` are set, the matrix wins (BYO override).
23
+
24
+ Output is a single `<svg>` with `viewBox="0 0 N N"`. Crisp-edges
25
+ rendering preserves the scannable cell boundaries at any size.
26
+ Sizing is controlled by the `[size]` prop (host CSS-pixel dimension);
27
+ internal cell size is computed from `size / (matrix-size + 2 ·
28
+ margin)`. ARIA: `role="img"` with `aria-label` (default "QR code"
29
+ or the `[label]` prop).
30
+ props:
31
+ value:
32
+ description: |
33
+ The string to encode (URL / text / share link / 2FA seed). When
34
+ [matrix] is also set, the matrix wins. Empty value + empty matrix
35
+ renders nothing.
36
+ type: string
37
+ default: ""
38
+ reflect: true
39
+ matrix:
40
+ description: |
41
+ BYO precomputed bit matrix as JSON — a 2-D array of 0/1
42
+ (`[[1,0,...],[0,1,...],...]`). When set, the built-in encoder is
43
+ bypassed and this matrix renders directly. Useful for QR variants
44
+ the built-in doesn't support (versions 11–40, alphanumeric mode,
45
+ kanji, etc.) — bring your own encoder, pass the matrix here.
46
+ type: string
47
+ default: ""
48
+ reflect: true
49
+ size:
50
+ description: |
51
+ Host display size in CSS pixels (width and height; the SVG is
52
+ always square). Default 200. Internal cell size is computed from
53
+ this divided by `matrix-size + 2·margin`.
54
+ type: number
55
+ default: 200
56
+ reflect: true
57
+ errorCorrection:
58
+ description: |
59
+ Error-correction level (auto-encode mode only). `L` (7% recovery),
60
+ `M` (15%, default), `Q` (25%), `H` (30%). Higher levels recover
61
+ from more damage at the cost of QR-version size growth.
62
+ type: string
63
+ default: M
64
+ enum: [L, M, Q, H]
65
+ reflect: true
66
+ attribute: error-correction
67
+ margin:
68
+ description: |
69
+ Quiet zone in cells around the QR pattern. QR spec requires a
70
+ minimum 4-cell margin for reliable scanning; smaller values may
71
+ not scan on some devices. Default `4`.
72
+ type: number
73
+ default: 4
74
+ reflect: true
75
+ color:
76
+ description: |
77
+ Foreground (dark-cell) color. Any CSS color string (hex, rgb,
78
+ token reference). **Empty defaults to `#000000` — hardcoded black
79
+ for guaranteed scanability** (theme-aware `currentColor` would
80
+ produce light-on-dark in dark mode, which most phone scanners
81
+ refuse to decode). Override for branded QRs only when you've
82
+ verified the chosen color/background pair scans on the target
83
+ devices.
84
+ type: string
85
+ default: ""
86
+ reflect: true
87
+ background:
88
+ description: |
89
+ Background (light-cell) color. **Empty defaults to `#ffffff` —
90
+ hardcoded white** alongside the black foreground, for guaranteed
91
+ scanability. Override only when paired with an explicit
92
+ [color] (the contrast pair must scan).
93
+ type: string
94
+ default: ""
95
+ reflect: true
96
+ label:
97
+ description: |
98
+ `aria-label` for the SVG. Defaults to "QR code". Customize for
99
+ better AT context (e.g. "Share link QR code", "2FA setup QR").
100
+ type: string
101
+ default: "QR code"
102
+ reflect: true
103
+ events: {}
104
+ slots: {}
105
+ states:
106
+ - name: idle
107
+ description: Default — QR rendered (or empty if no value/matrix).
108
+ - name: error
109
+ 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.
110
+ attribute: data-error
111
+ traits: []
112
+ tokens:
113
+ --qr-code-fg:
114
+ description: Foreground (dark) color when [color] is empty.
115
+ default: currentColor
116
+ --qr-code-bg:
117
+ description: Background (light) color when [background] is empty.
118
+ default: transparent
119
+ requiredIcons: []
120
+ a2ui:
121
+ rules:
122
+ - rule: "Set [value] for URLs / share links / plain text. The built-in encoder covers byte-mode UTF-8 up to QR version 10 (~150 chars at ECC-M, more at lower ECC levels)."
123
+ reason: "Default mode for the 95% case."
124
+ - rule: "For data that exceeds version 10, switch to a higher ECC level downgrade ([error-correction=L]) OR use a BYO encoder and pass the precomputed [matrix]."
125
+ reason: "Encoder capacity contract."
126
+ - rule: "When QR sits over a non-white surface, set [background] to a solid color. White-on-image QR backgrounds are unreliable for scanners — quiet-zone contrast against the dark cells is the load-bearing visual."
127
+ reason: "Scannability constraint."
128
+ - rule: "The [margin] (quiet zone) defaults to 4 cells per QR spec. Smaller margins may scan on some devices but not others; do not go below 2."
129
+ reason: "Quiet-zone requirement."
130
+ anti_patterns:
131
+ - wrong: |
132
+ <qr-code-ui value="https://very.long.url.that.exceeds.150.bytes.of.utf8.encoded.length..."></qr-code-ui>
133
+ why: |
134
+ Built-in encoder covers versions 1–10. Data beyond v10 capacity
135
+ throws + renders empty + sets [data-error]. Console warns with
136
+ the byte length so consumers can diagnose.
137
+ fix: |
138
+ Drop the ECC level (lower L gives more capacity for the same
139
+ version), OR shorten the URL (use a URL shortener), OR encode
140
+ with a BYO encoder library + pass the [matrix]:
141
+ `<qr-code-ui matrix="[[1,0,1,...],...]"></qr-code-ui>`
142
+ - wrong: |
143
+ <qr-code-ui value="..." margin="0"></qr-code-ui>
144
+ why: |
145
+ Zero margin breaks scanning. QR spec requires a 4-cell quiet
146
+ zone for reliable detection.
147
+ fix: |
148
+ <qr-code-ui value="..." margin="4"></qr-code-ui>
149
+ examples:
150
+ - name: default
151
+ description: Plain URL encoding at default size + ECC-M.
152
+ a2ui: |
153
+ [
154
+ {
155
+ "id": "q",
156
+ "component": "QRCode",
157
+ "value": "https://example.com"
158
+ }
159
+ ]
160
+ - name: branded
161
+ description: Custom colors matching brand chrome.
162
+ a2ui: |
163
+ [
164
+ {
165
+ "id": "q",
166
+ "component": "QRCode",
167
+ "value": "https://adia.health/share/abc123",
168
+ "size": 280,
169
+ "color": "#1e293b",
170
+ "background": "#ffffff",
171
+ "errorCorrection": "Q"
172
+ }
173
+ ]
174
+ - name: byo-matrix
175
+ description: BYO precomputed matrix from an external encoder.
176
+ a2ui: |
177
+ [
178
+ {
179
+ "id": "q",
180
+ "component": "QRCode",
181
+ "matrix": "[[1,1,1,1,1,1,1,0,1,...]...]",
182
+ "size": 200,
183
+ "label": "Boarding pass QR"
184
+ }
185
+ ]
186
+ keywords:
187
+ - qr-code
188
+ - qr
189
+ - barcode
190
+ - scan
191
+ - share
192
+ - 2fa
193
+ - share-link
194
+ synonyms:
195
+ qr:
196
+ - qr-code
197
+ - barcode
198
+ scan:
199
+ - qr-code
200
+ - barcode
201
+ related:
202
+ - link
203
+ - share