@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.
- package/CHANGELOG.md +43 -0
- package/LICENSE +27 -0
- package/README.md +643 -2
- package/dist/adapters/web/controls.d.ts +19 -0
- package/dist/adapters/web/controls.js +307 -0
- package/dist/adapters/web/dom.d.ts +10 -0
- package/dist/adapters/web/dom.js +98 -0
- package/dist/adapters/web/index.d.ts +3 -0
- package/dist/adapters/web/index.js +3 -0
- package/dist/adapters/web/renderer.d.ts +18 -0
- package/dist/adapters/web/renderer.js +553 -0
- package/dist/adapters/web/styles.d.ts +3 -0
- package/dist/adapters/web/styles.js +83 -0
- package/dist/adapters/web/types.d.ts +46 -0
- package/dist/adapters/web/types.js +2 -0
- package/dist/affix.d.ts +35 -0
- package/dist/affix.js +137 -0
- package/dist/badge.d.ts +45 -0
- package/dist/badge.js +168 -0
- package/dist/breadcrumb.d.ts +37 -0
- package/dist/breadcrumb.js +210 -0
- package/dist/button-group.d.ts +12 -0
- package/dist/button-group.js +72 -0
- package/dist/button.d.ts +15 -1
- package/dist/button.js +101 -12
- package/dist/carousel.d.ts +92 -0
- package/dist/carousel.js +509 -0
- package/dist/cascader.d.ts +65 -0
- package/dist/cascader.js +479 -0
- package/dist/collapse.d.ts +68 -0
- package/dist/collapse.js +348 -0
- package/dist/core/actions.d.ts +32 -0
- package/dist/core/actions.js +145 -0
- package/dist/core/data.d.ts +4 -0
- package/dist/core/data.js +27 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +6 -0
- package/dist/core/platform-view.d.ts +17 -0
- package/dist/core/platform-view.js +84 -0
- package/dist/core/selection.d.ts +14 -0
- package/dist/core/selection.js +93 -0
- package/dist/core/text.d.ts +31 -0
- package/dist/core/text.js +154 -0
- package/dist/core/types.d.ts +269 -0
- package/dist/core/types.js +2 -0
- package/dist/core/validation.d.ts +24 -0
- package/dist/core/validation.js +243 -0
- package/dist/date-picker.d.ts +60 -0
- package/dist/date-picker.js +355 -0
- package/dist/dialog.d.ts +4 -0
- package/dist/dialog.js +23 -5
- package/dist/divider.d.ts +33 -0
- package/dist/divider.js +113 -0
- package/dist/draggable.d.ts +78 -0
- package/dist/draggable.js +255 -0
- package/dist/index.d.ts +53 -1
- package/dist/index.js +78 -0
- package/dist/input.d.ts +30 -1
- package/dist/input.js +166 -54
- package/dist/internal/qrcodegen.d.ts +94 -0
- package/dist/internal/qrcodegen.js +856 -0
- package/dist/masonry.d.ts +36 -0
- package/dist/masonry.js +171 -0
- package/dist/modal.d.ts +73 -0
- package/dist/modal.js +361 -0
- package/dist/portable/icons.d.ts +5 -0
- package/dist/portable/icons.js +24 -0
- package/dist/portable/index.d.ts +7 -0
- package/dist/portable/index.js +5 -0
- package/dist/portable/page.d.ts +38 -0
- package/dist/portable/page.js +361 -0
- package/dist/portable/responsive.d.ts +20 -0
- package/dist/portable/responsive.js +15 -0
- package/dist/portable/theme.d.ts +24 -0
- package/dist/portable/theme.js +60 -0
- package/dist/portable/tree.d.ts +10 -0
- package/dist/portable/tree.js +96 -0
- package/dist/qr-code.d.ts +62 -0
- package/dist/qr-code.js +224 -0
- package/dist/range.d.ts +3 -0
- package/dist/range.js +16 -5
- package/dist/rate.d.ts +46 -0
- package/dist/rate.js +193 -0
- package/dist/resizable.d.ts +97 -0
- package/dist/resizable.js +426 -0
- package/dist/skeleton.d.ts +41 -0
- package/dist/skeleton.js +141 -0
- package/dist/steps.d.ts +45 -0
- package/dist/steps.js +203 -0
- package/dist/table.d.ts +106 -0
- package/dist/table.js +689 -0
- package/dist/tabs.d.ts +3 -0
- package/dist/tabs.js +11 -1
- package/dist/tag.d.ts +55 -0
- package/dist/tag.js +225 -0
- package/dist/time-picker.d.ts +62 -0
- package/dist/time-picker.js +298 -0
- package/dist/timeline.d.ts +48 -0
- package/dist/timeline.js +201 -0
- package/dist/toast.d.ts +55 -0
- package/dist/toast.js +224 -0
- package/dist/tour.d.ts +89 -0
- package/dist/tour.js +306 -0
- package/dist/virtual-keyboard.d.ts +95 -0
- package/dist/virtual-keyboard.js +432 -0
- package/dist/virtual-mouse.d.ts +82 -0
- package/dist/virtual-mouse.js +358 -0
- package/dist/watermark.d.ts +44 -0
- package/dist/watermark.js +148 -0
- 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;
|
package/dist/qr-code.js
ADDED
|
@@ -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.
|
|
165
|
-
set value(value) { this.setAttribute('value', String(this.
|
|
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;
|