@haiyue/ui 0.1.2 → 0.2.2

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 (110) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/LICENSE +27 -0
  3. package/README.md +643 -2
  4. package/dist/adapters/web/controls.d.ts +19 -0
  5. package/dist/adapters/web/controls.js +307 -0
  6. package/dist/adapters/web/dom.d.ts +10 -0
  7. package/dist/adapters/web/dom.js +98 -0
  8. package/dist/adapters/web/index.d.ts +3 -0
  9. package/dist/adapters/web/index.js +3 -0
  10. package/dist/adapters/web/renderer.d.ts +18 -0
  11. package/dist/adapters/web/renderer.js +553 -0
  12. package/dist/adapters/web/styles.d.ts +3 -0
  13. package/dist/adapters/web/styles.js +83 -0
  14. package/dist/adapters/web/types.d.ts +46 -0
  15. package/dist/adapters/web/types.js +2 -0
  16. package/dist/affix.d.ts +35 -0
  17. package/dist/affix.js +137 -0
  18. package/dist/badge.d.ts +45 -0
  19. package/dist/badge.js +168 -0
  20. package/dist/breadcrumb.d.ts +37 -0
  21. package/dist/breadcrumb.js +210 -0
  22. package/dist/button-group.d.ts +12 -0
  23. package/dist/button-group.js +72 -0
  24. package/dist/button.d.ts +15 -1
  25. package/dist/button.js +101 -12
  26. package/dist/carousel.d.ts +92 -0
  27. package/dist/carousel.js +509 -0
  28. package/dist/cascader.d.ts +65 -0
  29. package/dist/cascader.js +479 -0
  30. package/dist/collapse.d.ts +68 -0
  31. package/dist/collapse.js +348 -0
  32. package/dist/core/actions.d.ts +32 -0
  33. package/dist/core/actions.js +145 -0
  34. package/dist/core/data.d.ts +4 -0
  35. package/dist/core/data.js +27 -0
  36. package/dist/core/index.d.ts +7 -0
  37. package/dist/core/index.js +6 -0
  38. package/dist/core/platform-view.d.ts +17 -0
  39. package/dist/core/platform-view.js +84 -0
  40. package/dist/core/selection.d.ts +14 -0
  41. package/dist/core/selection.js +93 -0
  42. package/dist/core/text.d.ts +31 -0
  43. package/dist/core/text.js +154 -0
  44. package/dist/core/types.d.ts +269 -0
  45. package/dist/core/types.js +2 -0
  46. package/dist/core/validation.d.ts +24 -0
  47. package/dist/core/validation.js +243 -0
  48. package/dist/date-picker.d.ts +60 -0
  49. package/dist/date-picker.js +355 -0
  50. package/dist/dialog.d.ts +4 -0
  51. package/dist/dialog.js +23 -5
  52. package/dist/divider.d.ts +33 -0
  53. package/dist/divider.js +113 -0
  54. package/dist/draggable.d.ts +78 -0
  55. package/dist/draggable.js +255 -0
  56. package/dist/index.d.ts +53 -1
  57. package/dist/index.js +78 -0
  58. package/dist/input.d.ts +30 -1
  59. package/dist/input.js +166 -54
  60. package/dist/internal/qrcodegen.d.ts +94 -0
  61. package/dist/internal/qrcodegen.js +856 -0
  62. package/dist/masonry.d.ts +36 -0
  63. package/dist/masonry.js +171 -0
  64. package/dist/modal.d.ts +73 -0
  65. package/dist/modal.js +361 -0
  66. package/dist/portable/icons.d.ts +5 -0
  67. package/dist/portable/icons.js +24 -0
  68. package/dist/portable/index.d.ts +7 -0
  69. package/dist/portable/index.js +5 -0
  70. package/dist/portable/page.d.ts +38 -0
  71. package/dist/portable/page.js +361 -0
  72. package/dist/portable/responsive.d.ts +20 -0
  73. package/dist/portable/responsive.js +15 -0
  74. package/dist/portable/theme.d.ts +24 -0
  75. package/dist/portable/theme.js +60 -0
  76. package/dist/portable/tree.d.ts +10 -0
  77. package/dist/portable/tree.js +96 -0
  78. package/dist/qr-code.d.ts +62 -0
  79. package/dist/qr-code.js +224 -0
  80. package/dist/range.d.ts +3 -0
  81. package/dist/range.js +16 -5
  82. package/dist/rate.d.ts +46 -0
  83. package/dist/rate.js +193 -0
  84. package/dist/resizable.d.ts +97 -0
  85. package/dist/resizable.js +426 -0
  86. package/dist/skeleton.d.ts +41 -0
  87. package/dist/skeleton.js +141 -0
  88. package/dist/steps.d.ts +45 -0
  89. package/dist/steps.js +203 -0
  90. package/dist/table.d.ts +106 -0
  91. package/dist/table.js +689 -0
  92. package/dist/tabs.d.ts +3 -0
  93. package/dist/tabs.js +11 -1
  94. package/dist/tag.d.ts +55 -0
  95. package/dist/tag.js +225 -0
  96. package/dist/time-picker.d.ts +62 -0
  97. package/dist/time-picker.js +298 -0
  98. package/dist/timeline.d.ts +48 -0
  99. package/dist/timeline.js +201 -0
  100. package/dist/toast.d.ts +55 -0
  101. package/dist/toast.js +224 -0
  102. package/dist/tour.d.ts +89 -0
  103. package/dist/tour.js +306 -0
  104. package/dist/virtual-keyboard.d.ts +95 -0
  105. package/dist/virtual-keyboard.js +432 -0
  106. package/dist/virtual-mouse.d.ts +82 -0
  107. package/dist/virtual-mouse.js +358 -0
  108. package/dist/watermark.d.ts +44 -0
  109. package/dist/watermark.js +148 -0
  110. package/package.json +132 -4
@@ -0,0 +1,96 @@
1
+ import { hyKey } from '../core/validation.js';
2
+ import { equalData } from '../core/data.js';
3
+ export function flatten(root) {
4
+ const entries = new Map();
5
+ const walk = (node, path, parent, slot) => {
6
+ const nextPath = [...path, node.key], key = hyKey(...nextPath);
7
+ const { children, slots, ...shallow } = node;
8
+ entries.set(key, { node: Object.freeze({ ...shallow, key }), parent, ...(slot === undefined ? {} : { slot }) });
9
+ for (const child of children ?? [])
10
+ walk(child, nextPath, key);
11
+ for (const [name, list] of Object.entries(slots ?? {}))
12
+ for (const child of list)
13
+ walk(child, nextPath, key, name);
14
+ };
15
+ walk(root, [], null);
16
+ return entries;
17
+ }
18
+ function group(entry) { return JSON.stringify([entry.parent, entry.slot ?? null]); }
19
+ function groups(tree) {
20
+ const result = new Map();
21
+ for (const [key, entry] of tree) {
22
+ const id = group(entry);
23
+ if (!result.has(id))
24
+ result.set(id, []);
25
+ result.get(id).push(key);
26
+ }
27
+ return result;
28
+ }
29
+ /** Returns a deterministic batch. Removing an ancestor releases descendants exactly once. */
30
+ export function diff(previous, next) {
31
+ // Bound props preserve topology. Avoid building/serializing sibling groups
32
+ // for these updates; keep the general reconciler for insert/move/remove.
33
+ if (previous.size === next.size) {
34
+ const entries = previous.entries(), updates = [];
35
+ let sameStructure = true;
36
+ for (const [key, entry] of next) {
37
+ const [oldKey, old] = entries.next().value;
38
+ if (oldKey !== key || old.parent !== entry.parent || old.slot !== entry.slot || old.node.kind !== entry.node.kind) {
39
+ sameStructure = false;
40
+ break;
41
+ }
42
+ if (old !== entry && !equalData(old.node.props, entry.node.props))
43
+ updates.push(Object.freeze({ type: 'update', node: entry.node }));
44
+ }
45
+ if (sameStructure)
46
+ return Object.freeze(updates);
47
+ }
48
+ const mutations = [];
49
+ const removed = new Set();
50
+ for (const [key, old] of previous) {
51
+ if ((old.parent !== null && removed.has(old.parent)) || !next.has(key) || next.get(key).node.kind !== old.node.kind)
52
+ removed.add(key);
53
+ }
54
+ for (const [key, old] of previous)
55
+ if (removed.has(key) && (old.parent === null || !removed.has(old.parent)))
56
+ mutations.push({ type: 'remove', key });
57
+ const created = new Set();
58
+ for (const [key, entry] of next) {
59
+ const old = previous.get(key);
60
+ if (!old || removed.has(key)) {
61
+ created.add(key);
62
+ mutations.push({ type: 'create', node: entry.node });
63
+ }
64
+ else if (!equalData(old.node.props, entry.node.props))
65
+ mutations.push({ type: 'update', node: entry.node });
66
+ }
67
+ const oldGroups = groups(new Map([...previous].filter(([key]) => !removed.has(key))));
68
+ // Drop cross-slot entries from their old location before placing them in their new group.
69
+ for (const list of oldGroups.values())
70
+ for (let i = list.length - 1; i >= 0; i--) {
71
+ const key = list[i];
72
+ if (next.has(key) && group(previous.get(key)) !== group(next.get(key)))
73
+ list.splice(i, 1);
74
+ }
75
+ for (const [id, ordered] of groups(next)) {
76
+ const current = oldGroups.get(id) ?? [];
77
+ // The common props-only path never searches sibling positions.
78
+ if (current.length === ordered.length && current.every((key, index) => key === ordered[index]))
79
+ continue;
80
+ let before = null;
81
+ for (let i = ordered.length - 1; i >= 0; i--) {
82
+ const key = ordered[i], entry = next.get(key);
83
+ const at = current.indexOf(key), successor = at < 0 ? undefined : (current[at + 1] ?? null);
84
+ if (at < 0 || successor !== before) {
85
+ mutations.push({ type: created.has(key) ? 'insert' : 'move', key, parent: entry.parent, before,
86
+ ...(entry.slot === undefined ? {} : { slot: entry.slot }) });
87
+ if (at >= 0)
88
+ current.splice(at, 1);
89
+ current.splice(before === null ? current.length : current.indexOf(before), 0, key);
90
+ }
91
+ before = key;
92
+ }
93
+ }
94
+ return Object.freeze(mutations.map(mutation => Object.freeze(mutation)));
95
+ }
96
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1,62 @@
1
+ export type HYQRCodeErrorCorrectionLevel = 'L' | 'M' | 'Q' | 'H';
2
+ export type HYQRCodeStatus = 'empty' | 'ready' | 'error';
3
+ export interface HYQRCodeRenderDetail {
4
+ readonly value: string;
5
+ readonly version: number;
6
+ readonly moduleCount: number;
7
+ readonly errorCorrectionLevel: HYQRCodeErrorCorrectionLevel;
8
+ }
9
+ export interface HYQRCodeErrorDetail {
10
+ readonly message: string;
11
+ }
12
+ export interface HYQRCodeIconErrorDetail {
13
+ readonly src: string;
14
+ }
15
+ /** Local SVG QR generation with a bounded decorative image or named icon slot. */
16
+ export declare class HYQRCode extends HTMLElement {
17
+ private readonly _frame;
18
+ private readonly _svg;
19
+ private readonly _background;
20
+ private readonly _modules;
21
+ private readonly _iconFrame;
22
+ private readonly _image;
23
+ private readonly _slot;
24
+ private readonly _message;
25
+ private _code;
26
+ private _key;
27
+ private _pathKey;
28
+ private _lastIcon;
29
+ private _iconReady;
30
+ private _state;
31
+ private _error;
32
+ static get observedAttributes(): string[];
33
+ constructor();
34
+ connectedCallback(): void;
35
+ disconnectedCallback(): void;
36
+ attributeChangedCallback(): void;
37
+ get value(): string;
38
+ set value(value: string);
39
+ get size(): number;
40
+ set size(value: number);
41
+ get color(): string;
42
+ set color(value: string);
43
+ get backgroundColor(): string;
44
+ set backgroundColor(value: string);
45
+ /** Quiet zone in modules. At least four modules are retained. */
46
+ get margin(): number;
47
+ set margin(value: number);
48
+ /** Defaults to H with an icon and M otherwise. An explicit level takes precedence. */
49
+ get errorCorrectionLevel(): HYQRCodeErrorCorrectionLevel;
50
+ set errorCorrectionLevel(value: HYQRCodeErrorCorrectionLevel);
51
+ get icon(): string;
52
+ set icon(value: string);
53
+ /** Requested image/slot size in CSS pixels. Actual coverage is bounded for readability. */
54
+ get iconSize(): number;
55
+ set iconSize(value: number);
56
+ get iconMargin(): number;
57
+ set iconMargin(value: number);
58
+ get status(): HYQRCodeStatus;
59
+ private _hasSlotIcon;
60
+ private _render;
61
+ }
62
+ export declare function defineQRCodeComponents(): void;
@@ -0,0 +1,224 @@
1
+ import { qrcodegen } from './internal/qrcodegen.js';
2
+ const levels = { L: qrcodegen.QrCode.Ecc.LOW, M: qrcodegen.QrCode.Ecc.MEDIUM, Q: qrcodegen.QrCode.Ecc.QUARTILE, H: qrcodegen.QrCode.Ecc.HIGH };
3
+ function integer(value, fallback, min, max) {
4
+ if (value == null || value === '')
5
+ return fallback;
6
+ const number = Number(value);
7
+ return Number.isFinite(number) ? Math.min(max, Math.max(min, Math.floor(number))) : fallback;
8
+ }
9
+ /** Local SVG QR generation with a bounded decorative image or named icon slot. */
10
+ export class HYQRCode extends HTMLElement {
11
+ _frame = document.createElement('div');
12
+ _svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
13
+ _background = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
14
+ _modules = document.createElementNS('http://www.w3.org/2000/svg', 'path');
15
+ _iconFrame = document.createElement('div');
16
+ _image = document.createElement('img');
17
+ _slot = document.createElement('slot');
18
+ _message = document.createElement('span');
19
+ _code = null;
20
+ _key = '';
21
+ _pathKey = '';
22
+ _lastIcon = '';
23
+ _iconReady = false;
24
+ _state = 'empty';
25
+ _error = '';
26
+ static get observedAttributes() {
27
+ return ['value', 'size', 'color', 'background-color', 'margin', 'error-correction-level', 'icon', 'icon-size', 'icon-margin', 'aria-label'];
28
+ }
29
+ constructor() {
30
+ super();
31
+ const root = this.attachShadow({ mode: 'open' });
32
+ const style = document.createElement('style');
33
+ style.textContent = `
34
+ :host { display: inline-block; max-width: 100%; vertical-align: middle; color: var(--hy-text-color, #eef0ff); font: 12px/1.5 system-ui, sans-serif; }
35
+ :host([hidden]), [hidden] { display: none !important; }
36
+ .frame { position: relative; box-sizing: border-box; max-width: 100%; aspect-ratio: 1; }
37
+ svg { display: block; width: 100%; height: 100%; }
38
+ .icon { position: absolute; box-sizing: border-box; inset: 50% auto auto 50%; transform: translate(-50%, -50%); display: grid; place-items: center; background: var(--_qr-background); border-radius: var(--hy-qr-code-icon-radius, 4px); pointer-events: none; overflow: hidden; }
39
+ img { display: block; width: 100%; height: 100%; min-width: 0; min-height: 0; object-fit: contain; }
40
+ slot { display: contents; }
41
+ ::slotted([slot="icon"]) { display: block; box-sizing: border-box; width: 100%; height: 100%; min-width: 0; min-height: 0; object-fit: contain; overflow: hidden; }
42
+ .message { position: absolute; inset: 0; display: grid; place-items: center; box-sizing: border-box; padding: 12px; text-align: center; color: var(--hy-secondary-text-color, #a9add4); background: var(--hy-surface-color, #10132f); border: 1px solid var(--hy-border-color, #343765); border-radius: var(--hy-qr-code-radius, 8px); }
43
+ `;
44
+ this._frame.className = 'frame';
45
+ this._frame.part.add('frame');
46
+ this._svg.part.add('code');
47
+ this._svg.setAttribute('aria-hidden', 'true');
48
+ this._svg.setAttribute('shape-rendering', 'crispEdges');
49
+ this._background.part.add('background');
50
+ this._modules.part.add('modules');
51
+ this._svg.append(this._background, this._modules);
52
+ this._iconFrame.className = 'icon';
53
+ this._iconFrame.part.add('icon');
54
+ this._iconFrame.setAttribute('aria-hidden', 'true');
55
+ this._iconFrame.inert = true;
56
+ this._image.alt = '';
57
+ this._image.draggable = false;
58
+ this._image.part.add('icon-image');
59
+ this._slot.name = 'icon';
60
+ this._iconFrame.append(this._slot, this._image);
61
+ this._message.className = 'message';
62
+ this._message.part.add('message');
63
+ this._frame.append(this._svg, this._iconFrame, this._message);
64
+ root.append(style, this._frame);
65
+ this._image.addEventListener('load', () => {
66
+ if (!this.isConnected || !this._image.naturalWidth)
67
+ return;
68
+ this._iconReady = true;
69
+ this._render();
70
+ });
71
+ this._image.addEventListener('error', () => {
72
+ if (!this.isConnected || !this._lastIcon)
73
+ return;
74
+ this._iconReady = false;
75
+ this._render();
76
+ this.dispatchEvent(new CustomEvent('qr-icon-error', { detail: { src: this._lastIcon }, bubbles: true, composed: true }));
77
+ });
78
+ }
79
+ connectedCallback() { this._slot.addEventListener('slotchange', this._render); this._render(); }
80
+ disconnectedCallback() {
81
+ this._slot.removeEventListener('slotchange', this._render);
82
+ this._lastIcon = '';
83
+ this._iconReady = false;
84
+ this._image.removeAttribute('src');
85
+ }
86
+ attributeChangedCallback() { this._render(); }
87
+ get value() { return this.getAttribute('value') ?? ''; }
88
+ set value(value) { this.setAttribute('value', String(value ?? '')); }
89
+ get size() { return integer(this.getAttribute('size'), 200, 64, 2048); }
90
+ set size(value) { this.setAttribute('size', String(integer(value, 200, 64, 2048))); }
91
+ get color() { return this.getAttribute('color') ?? ''; }
92
+ set color(value) { this.setAttribute('color', value); }
93
+ get backgroundColor() { return this.getAttribute('background-color') ?? ''; }
94
+ set backgroundColor(value) { this.setAttribute('background-color', value); }
95
+ /** Quiet zone in modules. At least four modules are retained. */
96
+ get margin() { return integer(this.getAttribute('margin'), 4, 4, 16); }
97
+ set margin(value) { this.setAttribute('margin', String(integer(value, 4, 4, 16))); }
98
+ /** Defaults to H with an icon and M otherwise. An explicit level takes precedence. */
99
+ get errorCorrectionLevel() {
100
+ const value = this.getAttribute('error-correction-level')?.toUpperCase();
101
+ if (value === 'L' || value === 'M' || value === 'Q' || value === 'H')
102
+ return value;
103
+ return this.icon || this._hasSlotIcon() ? 'H' : 'M';
104
+ }
105
+ set errorCorrectionLevel(value) { this.setAttribute('error-correction-level', value); }
106
+ get icon() { return this.getAttribute('icon') ?? ''; }
107
+ set icon(value) { this.setAttribute('icon', value ?? ''); }
108
+ /** Requested image/slot size in CSS pixels. Actual coverage is bounded for readability. */
109
+ get iconSize() { return integer(this.getAttribute('icon-size'), 32, 4, 512); }
110
+ set iconSize(value) { this.setAttribute('icon-size', String(integer(value, 32, 4, 512))); }
111
+ get iconMargin() { return integer(this.getAttribute('icon-margin'), 2, 0, 64); }
112
+ set iconMargin(value) { this.setAttribute('icon-margin', String(integer(value, 2, 0, 64))); }
113
+ get status() { return this._state; }
114
+ _hasSlotIcon() {
115
+ return this._slot.assignedNodes().some(node => node.nodeType === Node.ELEMENT_NODE || Boolean(node.textContent?.trim()));
116
+ }
117
+ _render = () => {
118
+ if (!this.isConnected)
119
+ return;
120
+ const value = this.value;
121
+ const level = this.errorCorrectionLevel;
122
+ const key = JSON.stringify([value, level]);
123
+ let generated = false;
124
+ let failed = false;
125
+ if (key !== this._key) {
126
+ this._key = key;
127
+ this._code = null;
128
+ this._error = '';
129
+ this._state = value ? 'ready' : 'empty';
130
+ if (value) {
131
+ try {
132
+ const bytes = new TextEncoder().encode(value);
133
+ if (bytes.length > 7089)
134
+ throw new RangeError('Value too long');
135
+ const segments = /^[\x00-\x7f]*$/.test(value) ? qrcodegen.QrSegment.makeSegments(value) : [qrcodegen.QrSegment.makeEci(26), qrcodegen.QrSegment.makeBytes([...bytes])];
136
+ this._code = qrcodegen.QrCode.encodeSegments(segments, levels[level], 1, 40, -1, false);
137
+ generated = true;
138
+ }
139
+ catch {
140
+ this._state = 'error';
141
+ this._error = 'This value is too long for the selected error correction level.';
142
+ failed = true;
143
+ }
144
+ }
145
+ }
146
+ const background = CSS.supports('color', this.backgroundColor) ? this.backgroundColor : 'var(--hy-qr-code-background-color, #ffffff)';
147
+ const color = CSS.supports('color', this.color) ? this.color : 'var(--hy-qr-code-color, #111827)';
148
+ this._frame.style.width = `${this.size}px`;
149
+ this._frame.style.setProperty('--_qr-background', background);
150
+ this._frame.dataset.state = this._state;
151
+ this._svg.toggleAttribute('hidden', this._state !== 'ready');
152
+ this._message.hidden = this._state === 'ready';
153
+ this._message.textContent = this._state === 'error' ? this._error : 'Enter a value to generate a QR code.';
154
+ this._message.setAttribute('role', this._state === 'error' ? 'alert' : 'status');
155
+ if (this._state === 'ready') {
156
+ this._frame.setAttribute('role', 'img');
157
+ this._frame.setAttribute('aria-label', this.getAttribute('aria-label') ?? 'QR code');
158
+ }
159
+ else {
160
+ this._frame.removeAttribute('role');
161
+ this._frame.removeAttribute('aria-label');
162
+ }
163
+ this._background.setAttribute('fill', background);
164
+ this._modules.setAttribute('fill', color);
165
+ const code = this._code;
166
+ const pathKey = `${key}:${this.margin}`;
167
+ if (!code) {
168
+ this._modules.removeAttribute('d');
169
+ this._pathKey = '';
170
+ }
171
+ else if (pathKey !== this._pathKey) {
172
+ this._pathKey = pathKey;
173
+ const extent = code.size + this.margin * 2;
174
+ this._svg.setAttribute('viewBox', `0 0 ${extent} ${extent}`);
175
+ this._background.setAttribute('width', String(extent));
176
+ this._background.setAttribute('height', String(extent));
177
+ const path = [];
178
+ for (let y = 0; y < code.size; y++) {
179
+ for (let x = 0; x < code.size;) {
180
+ if (!code.getModule(x, y)) {
181
+ x++;
182
+ continue;
183
+ }
184
+ const start = x;
185
+ while (x < code.size && code.getModule(x, y))
186
+ x++;
187
+ path.push(`M${start + this.margin},${y + this.margin}h${x - start}v1h-${x - start}z`);
188
+ }
189
+ }
190
+ this._modules.setAttribute('d', path.join(''));
191
+ }
192
+ const slotted = this._hasSlotIcon();
193
+ const src = slotted || !code ? '' : this.icon;
194
+ if (src !== this._lastIcon) {
195
+ this._lastIcon = src;
196
+ this._iconReady = false;
197
+ if (src)
198
+ this._image.src = src;
199
+ else
200
+ this._image.removeAttribute('src');
201
+ }
202
+ this._image.hidden = slotted || !src;
203
+ this._iconFrame.hidden = !code || (!slotted && !this._iconReady);
204
+ if (code) {
205
+ // Include the backing margin in the coverage limit; percentages also
206
+ // preserve that limit when max-width shrinks the rendered code.
207
+ const coverage = Math.min(this.iconSize + this.iconMargin * 2, this.size * code.size / (code.size + this.margin * 2) * .2);
208
+ const padding = Math.min(this.iconMargin, coverage / 4);
209
+ this._iconFrame.style.width = this._iconFrame.style.height = `${coverage / this.size * 100}%`;
210
+ this._iconFrame.style.padding = `${padding / this.size * 100}%`;
211
+ }
212
+ if (generated && code)
213
+ this.dispatchEvent(new CustomEvent('qr-render', {
214
+ detail: { value, version: code.version, moduleCount: code.size, errorCorrectionLevel: level }, bubbles: true, composed: true,
215
+ }));
216
+ if (failed)
217
+ this.dispatchEvent(new CustomEvent('qr-error', { detail: { message: this._error }, bubbles: true, composed: true }));
218
+ };
219
+ }
220
+ export function defineQRCodeComponents() {
221
+ if (!customElements.get('hy-qr-code'))
222
+ customElements.define('hy-qr-code', HYQRCode);
223
+ }
224
+ //# sourceMappingURL=qr-code.js.map
package/dist/range.d.ts CHANGED
@@ -32,6 +32,9 @@ export declare class HYRange extends HTMLElement {
32
32
  set step(value: number);
33
33
  get value(): number;
34
34
  set value(value: number);
35
+ get controlled(): boolean;
36
+ set controlled(value: boolean);
37
+ private _acceptedValue;
35
38
  get range(): boolean;
36
39
  set range(value: boolean);
37
40
  get lowerValue(): number;
package/dist/range.js CHANGED
@@ -17,7 +17,7 @@ export class HYRange extends HTMLElement {
17
17
  static get observedAttributes() {
18
18
  return [
19
19
  'min', 'max', 'step', 'value', 'range', 'lower-value', 'upper-value',
20
- 'disabled', 'show-value', 'aria-label',
20
+ 'disabled', 'show-value', 'aria-label', 'controlled',
21
21
  ];
22
22
  }
23
23
  constructor() {
@@ -147,11 +147,11 @@ export class HYRange extends HTMLElement {
147
147
  set min(value) { this.setAttribute('min', String(finiteOr(value, DEFAULT_MIN))); }
148
148
  get max() {
149
149
  const value = Number(this.getAttribute('max'));
150
- return Number.isFinite(value) && value > this.min ? value : Math.max(DEFAULT_MAX, this.min + DEFAULT_MAX);
150
+ return Number.isFinite(value) && (value > this.min || (this.controlled && value === this.min)) ? value : Math.max(DEFAULT_MAX, this.min + DEFAULT_MAX);
151
151
  }
152
152
  set max(value) {
153
153
  const next = finiteOr(value, DEFAULT_MAX);
154
- this.setAttribute('max', String(next > this.min ? next : this.min + DEFAULT_MAX));
154
+ this.setAttribute('max', String(next > this.min || (this.controlled && next === this.min) ? next : this.min + DEFAULT_MAX));
155
155
  }
156
156
  get step() {
157
157
  const value = Number(this.getAttribute('step'));
@@ -161,8 +161,13 @@ export class HYRange extends HTMLElement {
161
161
  const next = finiteOr(value, DEFAULT_STEP);
162
162
  this.setAttribute('step', String(next > 0 ? next : DEFAULT_STEP));
163
163
  }
164
- get value() { return this._normalize(Number(this.getAttribute('value') ?? this.min)); }
165
- set value(value) { this.setAttribute('value', String(this._normalize(value))); }
164
+ get value() { return this._acceptedValue(Number(this.getAttribute('value') ?? this.min)); }
165
+ set value(value) { this.setAttribute('value', String(this._acceptedValue(value))); }
166
+ get controlled() { return this.hasAttribute('controlled'); }
167
+ set controlled(value) { this.toggleAttribute('controlled', value); }
168
+ _acceptedValue(value) {
169
+ return this.controlled ? Math.max(this.min, Math.min(this.max, finiteOr(value, this.min))) : this._normalize(value);
170
+ }
166
171
  get range() { return this.hasAttribute('range'); }
167
172
  set range(value) { this.toggleAttribute('range', Boolean(value)); }
168
173
  get lowerValue() {
@@ -262,6 +267,10 @@ export class HYRange extends HTMLElement {
262
267
  const changed = this._dragStartValue !== this._serializedValue();
263
268
  const handle = this._activeHandle;
264
269
  this._stopDrag();
270
+ if (this.controlled && event.type === 'pointercancel') {
271
+ this._dispatchValueEvent('value-cancel', handle);
272
+ return;
273
+ }
265
274
  if (changed)
266
275
  this._dispatchValueEvent('value-change', handle);
267
276
  };
@@ -359,6 +368,8 @@ export class HYRange extends HTMLElement {
359
368
  return this._normalize(Number(this.getAttribute('lower-value') ?? this.min));
360
369
  }
361
370
  _toPercent(value) {
371
+ if (this.max === this.min)
372
+ return 0;
362
373
  return (value - this.min) / (this.max - this.min) * 100;
363
374
  }
364
375
  _formatValue(value) {
package/dist/rate.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ export interface HYRateChangeDetail {
2
+ readonly value: number;
3
+ }
4
+ export interface HYRateHoverChangeDetail {
5
+ readonly value: number | null;
6
+ }
7
+ /** Accessible star rating with optional half-step pointer and keyboard selection. */
8
+ export declare class HYRate extends HTMLElement {
9
+ private readonly _group;
10
+ private _value;
11
+ private _hoverValue;
12
+ private _tooltips;
13
+ private _reflecting;
14
+ static get observedAttributes(): string[];
15
+ constructor();
16
+ connectedCallback(): void;
17
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
18
+ get value(): number;
19
+ set value(value: number);
20
+ get count(): number;
21
+ set count(value: number);
22
+ get allowHalf(): boolean;
23
+ set allowHalf(value: boolean);
24
+ get allowClear(): boolean;
25
+ set allowClear(value: boolean);
26
+ get disabled(): boolean;
27
+ set disabled(value: boolean);
28
+ get readOnly(): boolean;
29
+ set readOnly(value: boolean);
30
+ get character(): string;
31
+ set character(value: string);
32
+ get tooltips(): readonly string[];
33
+ set tooltips(value: readonly string[]);
34
+ private _normalize;
35
+ private _reflectValue;
36
+ private _render;
37
+ private _updateVisuals;
38
+ private _pointerValue;
39
+ private _handlePointerMove;
40
+ private _handleClick;
41
+ private _handleKeydown;
42
+ private _setHover;
43
+ private _commit;
44
+ private _valueText;
45
+ }
46
+ export declare function defineRateComponents(): void;