@haiyue/ui 0.1.2 → 0.2.1
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 +28 -1
- package/README.md +444 -1
- package/dist/adapters/web/controls.d.ts +19 -0
- package/dist/adapters/web/controls.js +284 -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 +545 -0
- package/dist/adapters/web/styles.d.ts +3 -0
- package/dist/adapters/web/styles.js +62 -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/cascader.d.ts +65 -0
- package/dist/cascader.js +479 -0
- package/dist/collapse.d.ts +55 -0
- package/dist/collapse.js +261 -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 +250 -0
- package/dist/core/types.js +2 -0
- package/dist/core/validation.d.ts +24 -0
- package/dist/core/validation.js +238 -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 +45 -1
- package/dist/index.js +66 -0
- package/dist/input.d.ts +30 -1
- package/dist/input.js +166 -54
- package/dist/portable/index.d.ts +4 -0
- package/dist/portable/index.js +3 -0
- package/dist/portable/page.d.ts +38 -0
- package/dist/portable/page.js +361 -0
- package/dist/portable/theme.d.ts +19 -0
- package/dist/portable/theme.js +40 -0
- package/dist/portable/tree.d.ts +10 -0
- package/dist/portable/tree.js +96 -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 +115 -4
package/dist/affix.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface HYAffixChangeDetail {
|
|
2
|
+
readonly affixed: boolean;
|
|
3
|
+
}
|
|
4
|
+
/** Keeps slotted content pinned to a viewport or scrolling target without collapsing layout. */
|
|
5
|
+
export declare class HYAffix extends HTMLElement {
|
|
6
|
+
private readonly _placeholder;
|
|
7
|
+
private readonly _content;
|
|
8
|
+
private _target;
|
|
9
|
+
private _affixed;
|
|
10
|
+
private _listeners;
|
|
11
|
+
private _resizeObserver;
|
|
12
|
+
private _frame;
|
|
13
|
+
static get observedAttributes(): string[];
|
|
14
|
+
constructor();
|
|
15
|
+
connectedCallback(): void;
|
|
16
|
+
disconnectedCallback(): void;
|
|
17
|
+
attributeChangedCallback(): void;
|
|
18
|
+
get topOffset(): number | null;
|
|
19
|
+
set topOffset(value: number | null);
|
|
20
|
+
get bottomOffset(): number | null;
|
|
21
|
+
set bottomOffset(value: number | null);
|
|
22
|
+
get disabled(): boolean;
|
|
23
|
+
set disabled(value: boolean);
|
|
24
|
+
get zIndex(): number;
|
|
25
|
+
set zIndex(value: number);
|
|
26
|
+
get affixed(): boolean;
|
|
27
|
+
get target(): HTMLElement | Window | null;
|
|
28
|
+
set target(value: HTMLElement | Window | null);
|
|
29
|
+
refresh(): void;
|
|
30
|
+
private _connect;
|
|
31
|
+
private _disconnect;
|
|
32
|
+
private _measure;
|
|
33
|
+
private _setAffixed;
|
|
34
|
+
}
|
|
35
|
+
export declare function defineAffixComponents(): void;
|
package/dist/affix.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
function finiteNumber(value, fallback, min, max) {
|
|
2
|
+
if (value == null || value === '')
|
|
3
|
+
return fallback;
|
|
4
|
+
const number = Number(value);
|
|
5
|
+
return Number.isFinite(number) ? Math.min(max, Math.max(min, number)) : fallback;
|
|
6
|
+
}
|
|
7
|
+
/** Keeps slotted content pinned to a viewport or scrolling target without collapsing layout. */
|
|
8
|
+
export class HYAffix extends HTMLElement {
|
|
9
|
+
_placeholder = document.createElement('div');
|
|
10
|
+
_content = document.createElement('div');
|
|
11
|
+
_target = null;
|
|
12
|
+
_affixed = false;
|
|
13
|
+
_listeners = null;
|
|
14
|
+
_resizeObserver = null;
|
|
15
|
+
_frame = 0;
|
|
16
|
+
static get observedAttributes() { return ['offset-top', 'offset-bottom', 'disabled', 'z-index']; }
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
const root = this.attachShadow({ mode: 'open' });
|
|
20
|
+
const style = document.createElement('style');
|
|
21
|
+
const slot = document.createElement('slot');
|
|
22
|
+
style.textContent = `
|
|
23
|
+
:host { display: block; min-width: 0; }
|
|
24
|
+
.placeholder { min-width: 0; }
|
|
25
|
+
.content { min-width: 0; }
|
|
26
|
+
.content.fixed { position: fixed; z-index: var(--_hy-affix-z-index, 100); box-sizing: border-box; }
|
|
27
|
+
`;
|
|
28
|
+
this._placeholder.className = 'placeholder';
|
|
29
|
+
this._placeholder.part.add('placeholder');
|
|
30
|
+
this._content.className = 'content';
|
|
31
|
+
this._content.part.add('content');
|
|
32
|
+
this._content.append(slot);
|
|
33
|
+
this._placeholder.append(this._content);
|
|
34
|
+
root.append(style, this._placeholder);
|
|
35
|
+
}
|
|
36
|
+
connectedCallback() { this._connect(); queueMicrotask(() => this.refresh()); }
|
|
37
|
+
disconnectedCallback() { this._disconnect(); this._setAffixed(false); }
|
|
38
|
+
attributeChangedCallback() { if (this.isConnected) {
|
|
39
|
+
this._connect();
|
|
40
|
+
this.refresh();
|
|
41
|
+
} }
|
|
42
|
+
get topOffset() { return this.hasAttribute('offset-top') ? finiteNumber(this.getAttribute('offset-top'), 0, 0, 10000) : null; }
|
|
43
|
+
set topOffset(value) { if (value == null)
|
|
44
|
+
this.removeAttribute('offset-top');
|
|
45
|
+
else
|
|
46
|
+
this.setAttribute('offset-top', String(finiteNumber(value, 0, 0, 10000))); }
|
|
47
|
+
get bottomOffset() { return this.hasAttribute('offset-bottom') ? finiteNumber(this.getAttribute('offset-bottom'), 0, 0, 10000) : null; }
|
|
48
|
+
set bottomOffset(value) { if (value == null)
|
|
49
|
+
this.removeAttribute('offset-bottom');
|
|
50
|
+
else
|
|
51
|
+
this.setAttribute('offset-bottom', String(finiteNumber(value, 0, 0, 10000))); }
|
|
52
|
+
get disabled() { return this.hasAttribute('disabled'); }
|
|
53
|
+
set disabled(value) { this.toggleAttribute('disabled', Boolean(value)); }
|
|
54
|
+
get zIndex() { return Math.round(finiteNumber(this.getAttribute('z-index'), 100, -1000, 100000)); }
|
|
55
|
+
set zIndex(value) { this.setAttribute('z-index', String(Math.round(finiteNumber(value, 100, -1000, 100000)))); }
|
|
56
|
+
get affixed() { return this._affixed; }
|
|
57
|
+
get target() { return this._target; }
|
|
58
|
+
set target(value) { this._target = value instanceof HTMLElement || value === window ? value : null; if (this.isConnected) {
|
|
59
|
+
this._connect();
|
|
60
|
+
this.refresh();
|
|
61
|
+
} }
|
|
62
|
+
refresh() {
|
|
63
|
+
cancelAnimationFrame(this._frame);
|
|
64
|
+
this._frame = requestAnimationFrame(() => this._measure());
|
|
65
|
+
}
|
|
66
|
+
_connect() {
|
|
67
|
+
this._disconnect();
|
|
68
|
+
const target = this._target ?? window;
|
|
69
|
+
const abort = new AbortController();
|
|
70
|
+
this._listeners = abort;
|
|
71
|
+
target.addEventListener('scroll', () => this.refresh(), { passive: true, signal: abort.signal });
|
|
72
|
+
if (target !== window)
|
|
73
|
+
window.addEventListener('scroll', () => this.refresh(), { passive: true, signal: abort.signal });
|
|
74
|
+
window.addEventListener('resize', () => this.refresh(), { passive: true, signal: abort.signal });
|
|
75
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
76
|
+
this._resizeObserver = new ResizeObserver(() => this.refresh());
|
|
77
|
+
this._resizeObserver.observe(this);
|
|
78
|
+
this._resizeObserver.observe(this._content);
|
|
79
|
+
if (target instanceof HTMLElement)
|
|
80
|
+
this._resizeObserver.observe(target);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
_disconnect() { this._listeners?.abort(); this._listeners = null; this._resizeObserver?.disconnect(); this._resizeObserver = null; cancelAnimationFrame(this._frame); this._frame = 0; }
|
|
84
|
+
_measure() {
|
|
85
|
+
if (!this.isConnected || this.disabled) {
|
|
86
|
+
this._setAffixed(false);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const target = this._target ?? window;
|
|
90
|
+
const targetRect = target instanceof HTMLElement ? target.getBoundingClientRect() : { top: 0, bottom: window.innerHeight };
|
|
91
|
+
const placeholderRect = this._placeholder.getBoundingClientRect();
|
|
92
|
+
const contentRect = this._affixed ? placeholderRect : this._content.getBoundingClientRect();
|
|
93
|
+
const useBottom = this.topOffset == null && this.bottomOffset != null;
|
|
94
|
+
const shouldAffix = useBottom ? contentRect.bottom >= targetRect.bottom - (this.bottomOffset ?? 0) : contentRect.top <= targetRect.top + (this.topOffset ?? 0);
|
|
95
|
+
if (!shouldAffix) {
|
|
96
|
+
this._setAffixed(false);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this._placeholder.style.width = `${contentRect.width}px`;
|
|
100
|
+
this._placeholder.style.height = `${contentRect.height}px`;
|
|
101
|
+
this._content.style.left = `${placeholderRect.left}px`;
|
|
102
|
+
this._content.style.width = `${placeholderRect.width}px`;
|
|
103
|
+
this._content.style.setProperty('--_hy-affix-z-index', String(this.zIndex));
|
|
104
|
+
if (useBottom) {
|
|
105
|
+
this._content.style.removeProperty('top');
|
|
106
|
+
this._content.style.bottom = `${Math.max(0, window.innerHeight - targetRect.bottom) + (this.bottomOffset ?? 0)}px`;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
this._content.style.removeProperty('bottom');
|
|
110
|
+
this._content.style.top = `${Math.max(0, targetRect.top) + (this.topOffset ?? 0)}px`;
|
|
111
|
+
}
|
|
112
|
+
this._setAffixed(true);
|
|
113
|
+
}
|
|
114
|
+
_setAffixed(value) {
|
|
115
|
+
if (!value) {
|
|
116
|
+
this._content.classList.remove('fixed');
|
|
117
|
+
this._placeholder.style.removeProperty('width');
|
|
118
|
+
this._placeholder.style.removeProperty('height');
|
|
119
|
+
this._content.style.removeProperty('left');
|
|
120
|
+
this._content.style.removeProperty('width');
|
|
121
|
+
this._content.style.removeProperty('top');
|
|
122
|
+
this._content.style.removeProperty('bottom');
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
this._content.classList.add('fixed');
|
|
126
|
+
if (value === this._affixed)
|
|
127
|
+
return;
|
|
128
|
+
this._affixed = value;
|
|
129
|
+
this.toggleAttribute('affixed', value);
|
|
130
|
+
this.dispatchEvent(new CustomEvent('affix-change', { bubbles: true, composed: true, detail: { affixed: value } }));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function defineAffixComponents() {
|
|
134
|
+
if (!customElements.get('hy-affix'))
|
|
135
|
+
customElements.define('hy-affix', HYAffix);
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=affix.js.map
|
package/dist/badge.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type HYBadgeStatus = 'default' | 'success' | 'processing' | 'warning' | 'error';
|
|
2
|
+
export type HYBadgeSize = 'default' | 'small';
|
|
3
|
+
export type HYBadgeCount = number | string | null;
|
|
4
|
+
/** Count, dot, or semantic status badge that can decorate slotted content. */
|
|
5
|
+
export declare class HYBadge extends HTMLElement {
|
|
6
|
+
private readonly _wrapper;
|
|
7
|
+
private readonly _slot;
|
|
8
|
+
private readonly _badge;
|
|
9
|
+
private readonly _badgeContent;
|
|
10
|
+
private readonly _status;
|
|
11
|
+
private readonly _statusDot;
|
|
12
|
+
private readonly _statusText;
|
|
13
|
+
static get observedAttributes(): string[];
|
|
14
|
+
constructor();
|
|
15
|
+
connectedCallback(): void;
|
|
16
|
+
attributeChangedCallback(): void;
|
|
17
|
+
/** Opt-in literal label; preserve zeros and bypass numeric overflow formatting. */
|
|
18
|
+
get literal(): boolean;
|
|
19
|
+
set literal(value: boolean);
|
|
20
|
+
get count(): HYBadgeCount;
|
|
21
|
+
set count(value: HYBadgeCount);
|
|
22
|
+
get overflowCount(): number;
|
|
23
|
+
set overflowCount(value: number);
|
|
24
|
+
get showZero(): boolean;
|
|
25
|
+
set showZero(value: boolean);
|
|
26
|
+
get dot(): boolean;
|
|
27
|
+
set dot(value: boolean);
|
|
28
|
+
get status(): HYBadgeStatus | null;
|
|
29
|
+
set status(value: HYBadgeStatus | null);
|
|
30
|
+
get color(): string;
|
|
31
|
+
set color(value: string);
|
|
32
|
+
get text(): string;
|
|
33
|
+
set text(value: string);
|
|
34
|
+
get offset(): readonly [number, number];
|
|
35
|
+
set offset(value: readonly [number, number]);
|
|
36
|
+
get offsetX(): number;
|
|
37
|
+
set offsetX(value: number);
|
|
38
|
+
get offsetY(): number;
|
|
39
|
+
set offsetY(value: number);
|
|
40
|
+
get size(): HYBadgeSize;
|
|
41
|
+
set size(value: HYBadgeSize);
|
|
42
|
+
private _render;
|
|
43
|
+
private _statusColor;
|
|
44
|
+
}
|
|
45
|
+
export declare function defineBadgeComponents(): void;
|
package/dist/badge.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
function boundedInteger(value, fallback, min, max) {
|
|
2
|
+
if (value == null || value === '')
|
|
3
|
+
return fallback;
|
|
4
|
+
const number = Number(value);
|
|
5
|
+
return Number.isFinite(number) ? Math.min(max, Math.max(min, Math.floor(number))) : fallback;
|
|
6
|
+
}
|
|
7
|
+
/** Count, dot, or semantic status badge that can decorate slotted content. */
|
|
8
|
+
export class HYBadge extends HTMLElement {
|
|
9
|
+
_wrapper = document.createElement('span');
|
|
10
|
+
_slot = document.createElement('slot');
|
|
11
|
+
_badge = document.createElement('sup');
|
|
12
|
+
_badgeContent = document.createElement('span');
|
|
13
|
+
_status = document.createElement('span');
|
|
14
|
+
_statusDot = document.createElement('span');
|
|
15
|
+
_statusText = document.createElement('span');
|
|
16
|
+
static get observedAttributes() {
|
|
17
|
+
return ['literal', 'count', 'overflow-count', 'show-zero', 'dot', 'status', 'color', 'text', 'offset-x', 'offset-y', 'size', 'title', 'aria-label'];
|
|
18
|
+
}
|
|
19
|
+
constructor() {
|
|
20
|
+
super();
|
|
21
|
+
const root = this.attachShadow({ mode: 'open' });
|
|
22
|
+
const style = document.createElement('style');
|
|
23
|
+
style.textContent = `
|
|
24
|
+
:host { display: inline-block; max-width: 100%; color: var(--hy-text-color, #eef3ff); font: 12px/1.4 system-ui, sans-serif; vertical-align: middle; }
|
|
25
|
+
.wrapper { position: relative; display: inline-flex; max-width: 100%; align-items: center; }
|
|
26
|
+
.badge { display: inline-flex; min-width: var(--hy-badge-height, 20px); height: var(--hy-badge-height, 20px); align-items: center; justify-content: center; box-sizing: border-box; margin: 0; padding: 0 var(--hy-badge-padding, 6px); border: var(--hy-badge-border-width, 2px) solid var(--hy-surface-color, #111722); border-radius: 999px; color: var(--hy-badge-text-color, #fff); background: var(--_badge-color, var(--hy-error-color, #ef5b72)); box-shadow: 0 0 0 1px color-mix(in srgb, var(--_badge-color, var(--hy-error-color, #ef5b72)) 12%, transparent); font: 650 var(--hy-badge-font-size, 11px)/1 system-ui, sans-serif; white-space: nowrap; }
|
|
27
|
+
.wrapper.has-content .badge { position: absolute; z-index: 1; inset-block-start: 0; inset-inline-end: 0; transform: translate(calc(50% + var(--_offset-x, 0px)), calc(-50% + var(--_offset-y, 0px))); transform-origin: 100% 0; }
|
|
28
|
+
.badge.dot { min-width: var(--hy-badge-dot-size, 8px); width: var(--hy-badge-dot-size, 8px); height: var(--hy-badge-dot-size, 8px); padding: 0; border-width: var(--hy-badge-dot-border-width, 1px); }
|
|
29
|
+
.badge[hidden], .status[hidden] { display: none; }
|
|
30
|
+
.status { display: inline-flex; align-items: center; gap: var(--hy-badge-status-gap, 7px); color: var(--hy-secondary-text-color, #aebbd0); }
|
|
31
|
+
.wrapper.has-content .status { margin-inline-start: 10px; }
|
|
32
|
+
.status-dot { position: relative; width: var(--hy-badge-status-size, 8px); height: var(--hy-badge-status-size, 8px); flex: 0 0 auto; border-radius: 50%; background: var(--_badge-color, var(--hy-muted-color, #788697)); }
|
|
33
|
+
.status.processing .status-dot::after { content: ''; position: absolute; inset: -3px; border: 1px solid var(--_badge-color, var(--hy-accent-color, #79a8ff)); border-radius: inherit; animation: hy-badge-pulse 1.5s ease-out infinite; }
|
|
34
|
+
:host([size="small"]) .badge { --hy-badge-height: 16px; --hy-badge-padding: 4px; --hy-badge-font-size: 9px; }
|
|
35
|
+
@keyframes hy-badge-pulse { 0% { opacity: .75; transform: scale(.55); } 100% { opacity: 0; transform: scale(1.35); } }
|
|
36
|
+
@media (prefers-reduced-motion: reduce) { .status.processing .status-dot::after { animation: none; } }
|
|
37
|
+
`;
|
|
38
|
+
this._wrapper.className = 'wrapper';
|
|
39
|
+
this._wrapper.part.add('wrapper');
|
|
40
|
+
this._badge.className = 'badge';
|
|
41
|
+
this._badge.part.add('badge');
|
|
42
|
+
this._badge.setAttribute('role', 'status');
|
|
43
|
+
this._badge.setAttribute('aria-live', 'polite');
|
|
44
|
+
this._badgeContent.part.add('badge-content');
|
|
45
|
+
this._badge.append(this._badgeContent);
|
|
46
|
+
this._status.className = 'status';
|
|
47
|
+
this._status.part.add('status');
|
|
48
|
+
this._status.setAttribute('role', 'status');
|
|
49
|
+
this._statusDot.className = 'status-dot';
|
|
50
|
+
this._statusDot.part.add('status-dot');
|
|
51
|
+
this._statusText.className = 'status-text';
|
|
52
|
+
this._statusText.part.add('status-text');
|
|
53
|
+
this._status.append(this._statusDot, this._statusText);
|
|
54
|
+
this._wrapper.append(this._slot, this._badge, this._status);
|
|
55
|
+
root.append(style, this._wrapper);
|
|
56
|
+
this._slot.addEventListener('slotchange', () => this._render());
|
|
57
|
+
}
|
|
58
|
+
connectedCallback() { this._render(); }
|
|
59
|
+
attributeChangedCallback() { this._render(); }
|
|
60
|
+
/** Opt-in literal label; preserve zeros and bypass numeric overflow formatting. */
|
|
61
|
+
get literal() { return this.hasAttribute('literal'); }
|
|
62
|
+
set literal(value) { this.toggleAttribute('literal', Boolean(value)); }
|
|
63
|
+
get count() {
|
|
64
|
+
const value = this.getAttribute('count');
|
|
65
|
+
if (value == null)
|
|
66
|
+
return null;
|
|
67
|
+
if (this.literal)
|
|
68
|
+
return value;
|
|
69
|
+
if (/^-?\d+(?:\.\d+)?$/u.test(value.trim()))
|
|
70
|
+
return Number(value);
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
set count(value) { if (value == null)
|
|
74
|
+
this.removeAttribute('count');
|
|
75
|
+
else
|
|
76
|
+
this.setAttribute('count', String(value)); }
|
|
77
|
+
get overflowCount() { return boundedInteger(this.getAttribute('overflow-count'), 99, 0, 999999); }
|
|
78
|
+
set overflowCount(value) { this.setAttribute('overflow-count', String(boundedInteger(value, 99, 0, 999999))); }
|
|
79
|
+
get showZero() { return this.hasAttribute('show-zero'); }
|
|
80
|
+
set showZero(value) { this.toggleAttribute('show-zero', Boolean(value)); }
|
|
81
|
+
get dot() { return this.hasAttribute('dot'); }
|
|
82
|
+
set dot(value) { this.toggleAttribute('dot', Boolean(value)); }
|
|
83
|
+
get status() {
|
|
84
|
+
const value = this.getAttribute('status');
|
|
85
|
+
return value === 'default' || value === 'success' || value === 'processing' || value === 'warning' || value === 'error' ? value : null;
|
|
86
|
+
}
|
|
87
|
+
set status(value) { if (value == null)
|
|
88
|
+
this.removeAttribute('status');
|
|
89
|
+
else
|
|
90
|
+
this.setAttribute('status', value); }
|
|
91
|
+
get color() { return this.getAttribute('color')?.trim() ?? ''; }
|
|
92
|
+
set color(value) { const normalized = String(value).trim(); if (normalized)
|
|
93
|
+
this.setAttribute('color', normalized);
|
|
94
|
+
else
|
|
95
|
+
this.removeAttribute('color'); }
|
|
96
|
+
get text() { return this.getAttribute('text') ?? ''; }
|
|
97
|
+
set text(value) { const normalized = String(value); if (normalized)
|
|
98
|
+
this.setAttribute('text', normalized);
|
|
99
|
+
else
|
|
100
|
+
this.removeAttribute('text'); }
|
|
101
|
+
get offset() { return [this.offsetX, this.offsetY]; }
|
|
102
|
+
set offset(value) { this.offsetX = value?.[0] ?? 0; this.offsetY = value?.[1] ?? 0; }
|
|
103
|
+
get offsetX() { return boundedInteger(this.getAttribute('offset-x'), 0, -1000, 1000); }
|
|
104
|
+
set offsetX(value) { this.setAttribute('offset-x', String(boundedInteger(value, 0, -1000, 1000))); }
|
|
105
|
+
get offsetY() { return boundedInteger(this.getAttribute('offset-y'), 0, -1000, 1000); }
|
|
106
|
+
set offsetY(value) { this.setAttribute('offset-y', String(boundedInteger(value, 0, -1000, 1000))); }
|
|
107
|
+
get size() { return this.getAttribute('size') === 'small' ? 'small' : 'default'; }
|
|
108
|
+
set size(value) { if (value === 'small')
|
|
109
|
+
this.setAttribute('size', 'small');
|
|
110
|
+
else
|
|
111
|
+
this.removeAttribute('size'); }
|
|
112
|
+
_render() {
|
|
113
|
+
if (!this.isConnected)
|
|
114
|
+
return;
|
|
115
|
+
const hasContent = this._slot.assignedNodes().some(node => node.nodeType === Node.ELEMENT_NODE || Boolean(node.textContent?.trim()));
|
|
116
|
+
this._wrapper.classList.toggle('has-content', hasContent);
|
|
117
|
+
this.style.setProperty('--_offset-x', `${this.offsetX}px`);
|
|
118
|
+
this.style.setProperty('--_offset-y', `${this.offsetY}px`);
|
|
119
|
+
const status = this.status;
|
|
120
|
+
const color = this.color || this._statusColor(status);
|
|
121
|
+
if (color)
|
|
122
|
+
this.style.setProperty('--_badge-color', color);
|
|
123
|
+
else
|
|
124
|
+
this.style.removeProperty('--_badge-color');
|
|
125
|
+
const showStatus = status != null;
|
|
126
|
+
this._status.hidden = !showStatus;
|
|
127
|
+
this._status.className = `status${status ? ` ${status}` : ''}`;
|
|
128
|
+
this._statusText.textContent = this.text;
|
|
129
|
+
const statusLabel = this.getAttribute('aria-label') || this.text || status || '';
|
|
130
|
+
if (statusLabel)
|
|
131
|
+
this._status.setAttribute('aria-label', statusLabel);
|
|
132
|
+
else
|
|
133
|
+
this._status.removeAttribute('aria-label');
|
|
134
|
+
const value = this.count;
|
|
135
|
+
const numeric = typeof value === 'number';
|
|
136
|
+
const hiddenByZero = numeric && value === 0 && !this.showZero;
|
|
137
|
+
const showBadge = !showStatus && (this.dot || (value != null && value !== '')) && !hiddenByZero;
|
|
138
|
+
const display = this.dot ? '' : numeric && value > this.overflowCount ? `${this.overflowCount}+` : String(value ?? '');
|
|
139
|
+
this._badge.hidden = !showBadge;
|
|
140
|
+
this._badge.classList.toggle('dot', this.dot);
|
|
141
|
+
this._badgeContent.textContent = display;
|
|
142
|
+
const badgeLabel = this.getAttribute('aria-label') || (this.dot ? 'Notification' : display);
|
|
143
|
+
if (badgeLabel) {
|
|
144
|
+
this._badge.setAttribute('aria-label', badgeLabel);
|
|
145
|
+
this._badge.title = this.getAttribute('title') || (this.dot ? '' : String(value ?? ''));
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this._badge.removeAttribute('aria-label');
|
|
149
|
+
this._badge.removeAttribute('title');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
_statusColor(status) {
|
|
153
|
+
if (status === 'success')
|
|
154
|
+
return 'var(--hy-success-color, #45c795)';
|
|
155
|
+
if (status === 'processing')
|
|
156
|
+
return 'var(--hy-accent-color, #79a8ff)';
|
|
157
|
+
if (status === 'warning')
|
|
158
|
+
return 'var(--hy-warning-color, #f2b84b)';
|
|
159
|
+
if (status === 'error')
|
|
160
|
+
return 'var(--hy-error-color, #ef5b72)';
|
|
161
|
+
return status === 'default' ? 'var(--hy-muted-color, #788697)' : '';
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export function defineBadgeComponents() {
|
|
165
|
+
if (!customElements.get('hy-badge'))
|
|
166
|
+
customElements.define('hy-badge', HYBadge);
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=badge.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface HYBreadcrumbItem {
|
|
2
|
+
readonly key?: string;
|
|
3
|
+
/** Ant Design-compatible display text. */
|
|
4
|
+
readonly title?: string;
|
|
5
|
+
/** Alias for title, retained for consistency with the other Haiyue UI data components. */
|
|
6
|
+
readonly label?: string;
|
|
7
|
+
readonly href?: string;
|
|
8
|
+
readonly target?: string;
|
|
9
|
+
readonly disabled?: boolean;
|
|
10
|
+
readonly ariaLabel?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface HYBreadcrumbClickDetail {
|
|
13
|
+
readonly item: HYBreadcrumbItem;
|
|
14
|
+
readonly index: number;
|
|
15
|
+
}
|
|
16
|
+
export type HYBreadcrumbItemRenderer = (item: HYBreadcrumbItem, index: number, current: boolean) => Node | string | number | null | undefined;
|
|
17
|
+
/** Accessible breadcrumb navigation with links, actions, and a configurable separator. */
|
|
18
|
+
export declare class HYBreadcrumb extends HTMLElement {
|
|
19
|
+
private readonly _nav;
|
|
20
|
+
private readonly _list;
|
|
21
|
+
private _items;
|
|
22
|
+
private _renderItem;
|
|
23
|
+
static get observedAttributes(): string[];
|
|
24
|
+
constructor();
|
|
25
|
+
connectedCallback(): void;
|
|
26
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
27
|
+
get items(): readonly HYBreadcrumbItem[];
|
|
28
|
+
set items(value: readonly HYBreadcrumbItem[]);
|
|
29
|
+
get separator(): string;
|
|
30
|
+
set separator(value: string);
|
|
31
|
+
get renderItem(): HYBreadcrumbItemRenderer | null;
|
|
32
|
+
set renderItem(value: HYBreadcrumbItemRenderer | null);
|
|
33
|
+
private _readItemsAttribute;
|
|
34
|
+
private _render;
|
|
35
|
+
private _dispatchItemClick;
|
|
36
|
+
}
|
|
37
|
+
export declare function defineBreadcrumbComponents(): void;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
function itemTitle(item) {
|
|
2
|
+
return String(item.title ?? item.label ?? '');
|
|
3
|
+
}
|
|
4
|
+
/** Accessible breadcrumb navigation with links, actions, and a configurable separator. */
|
|
5
|
+
export class HYBreadcrumb extends HTMLElement {
|
|
6
|
+
_nav = document.createElement('nav');
|
|
7
|
+
_list = document.createElement('ol');
|
|
8
|
+
_items = [];
|
|
9
|
+
_renderItem = null;
|
|
10
|
+
static get observedAttributes() {
|
|
11
|
+
return ['items', 'separator', 'aria-label'];
|
|
12
|
+
}
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
const root = this.attachShadow({ mode: 'open' });
|
|
16
|
+
const style = document.createElement('style');
|
|
17
|
+
style.textContent = `
|
|
18
|
+
:host {
|
|
19
|
+
display: block;
|
|
20
|
+
min-width: 0;
|
|
21
|
+
color: var(--hy-secondary-text-color, #aebbd0);
|
|
22
|
+
font: 12px/1.4 system-ui, sans-serif;
|
|
23
|
+
}
|
|
24
|
+
nav { min-width: 0; }
|
|
25
|
+
ol {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
min-width: 0;
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: 0;
|
|
31
|
+
overflow-x: auto;
|
|
32
|
+
overflow-y: hidden;
|
|
33
|
+
list-style: none;
|
|
34
|
+
scrollbar-width: none;
|
|
35
|
+
}
|
|
36
|
+
ol::-webkit-scrollbar { display: none; }
|
|
37
|
+
li {
|
|
38
|
+
display: inline-flex;
|
|
39
|
+
flex: 0 0 auto;
|
|
40
|
+
align-items: center;
|
|
41
|
+
min-width: 0;
|
|
42
|
+
}
|
|
43
|
+
a, button, .current, .disabled {
|
|
44
|
+
display: inline-flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
min-width: 0;
|
|
47
|
+
max-width: var(--hy-breadcrumb-item-max-width, 240px);
|
|
48
|
+
box-sizing: border-box;
|
|
49
|
+
padding: var(--hy-breadcrumb-item-padding, 3px 5px);
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
border: 0;
|
|
52
|
+
border-radius: var(--hy-breadcrumb-radius, 4px);
|
|
53
|
+
color: inherit;
|
|
54
|
+
background: transparent;
|
|
55
|
+
font: inherit;
|
|
56
|
+
text-decoration: none;
|
|
57
|
+
text-overflow: ellipsis;
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
}
|
|
60
|
+
a, button { cursor: pointer; }
|
|
61
|
+
a:hover, button:hover {
|
|
62
|
+
color: var(--hy-text-color, #eef3ff);
|
|
63
|
+
background: var(--hy-hover-bg-color, #273241);
|
|
64
|
+
}
|
|
65
|
+
a:focus-visible, button:focus-visible {
|
|
66
|
+
outline: 2px solid var(--hy-focus-border-color, #79a8ff);
|
|
67
|
+
outline-offset: 1px;
|
|
68
|
+
}
|
|
69
|
+
.current {
|
|
70
|
+
color: var(--hy-text-color, #eef3ff);
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
}
|
|
73
|
+
.disabled { color: var(--hy-disabled-text-color, #657185); cursor: not-allowed; }
|
|
74
|
+
.separator {
|
|
75
|
+
display: inline-flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
min-width: 14px;
|
|
79
|
+
padding: 0 var(--hy-breadcrumb-separator-gap, 3px);
|
|
80
|
+
color: var(--hy-muted-color, #657185);
|
|
81
|
+
user-select: none;
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
this._nav.part.add('nav');
|
|
85
|
+
this._list.part.add('list');
|
|
86
|
+
this._nav.append(this._list);
|
|
87
|
+
root.append(style, this._nav);
|
|
88
|
+
}
|
|
89
|
+
connectedCallback() {
|
|
90
|
+
if (this.hasAttribute('items'))
|
|
91
|
+
this._readItemsAttribute();
|
|
92
|
+
this._render();
|
|
93
|
+
}
|
|
94
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
95
|
+
if (oldValue === newValue)
|
|
96
|
+
return;
|
|
97
|
+
if (name === 'items')
|
|
98
|
+
this._readItemsAttribute();
|
|
99
|
+
this._render();
|
|
100
|
+
}
|
|
101
|
+
get items() { return this._items; }
|
|
102
|
+
set items(value) {
|
|
103
|
+
this._items = Array.isArray(value) ? value.map(item => ({ ...item, title: itemTitle(item) })) : [];
|
|
104
|
+
this._render();
|
|
105
|
+
}
|
|
106
|
+
get separator() { return this.getAttribute('separator') ?? '/'; }
|
|
107
|
+
set separator(value) { this.setAttribute('separator', String(value)); }
|
|
108
|
+
get renderItem() { return this._renderItem; }
|
|
109
|
+
set renderItem(value) {
|
|
110
|
+
this._renderItem = typeof value === 'function' ? value : null;
|
|
111
|
+
this._render();
|
|
112
|
+
}
|
|
113
|
+
_readItemsAttribute() {
|
|
114
|
+
const raw = this.getAttribute('items');
|
|
115
|
+
if (!raw) {
|
|
116
|
+
this._items = [];
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const parsed = JSON.parse(raw);
|
|
121
|
+
this._items = Array.isArray(parsed) ? parsed.map((item) => {
|
|
122
|
+
const record = item && typeof item === 'object' ? item : {};
|
|
123
|
+
return {
|
|
124
|
+
...(record.key == null ? {} : { key: String(record.key) }),
|
|
125
|
+
title: String(record.title ?? record.label ?? ''),
|
|
126
|
+
...(record.label == null ? {} : { label: String(record.label) }),
|
|
127
|
+
...(record.href == null ? {} : { href: String(record.href) }),
|
|
128
|
+
...(record.target == null ? {} : { target: String(record.target) }),
|
|
129
|
+
disabled: Boolean(record.disabled),
|
|
130
|
+
...(record.ariaLabel == null ? {} : { ariaLabel: String(record.ariaLabel) }),
|
|
131
|
+
};
|
|
132
|
+
}) : [];
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
console.warn('Invalid hy-breadcrumb items attribute.', error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
_render() {
|
|
139
|
+
this._nav.setAttribute('aria-label', this.getAttribute('aria-label') ?? 'Breadcrumb');
|
|
140
|
+
const lastIndex = this._items.length - 1;
|
|
141
|
+
const rows = this._items.map((item, index) => {
|
|
142
|
+
const row = document.createElement('li');
|
|
143
|
+
row.part.add('item');
|
|
144
|
+
if (index > 0) {
|
|
145
|
+
const separator = document.createElement('span');
|
|
146
|
+
separator.className = 'separator';
|
|
147
|
+
separator.part.add('separator');
|
|
148
|
+
separator.setAttribute('aria-hidden', 'true');
|
|
149
|
+
separator.textContent = this.separator;
|
|
150
|
+
row.append(separator);
|
|
151
|
+
}
|
|
152
|
+
const current = index === lastIndex;
|
|
153
|
+
const content = this._renderItem?.(item, index, current) ?? itemTitle(item);
|
|
154
|
+
let element;
|
|
155
|
+
if (current) {
|
|
156
|
+
element = document.createElement('span');
|
|
157
|
+
element.className = 'current';
|
|
158
|
+
element.part.add('current');
|
|
159
|
+
element.setAttribute('aria-current', 'page');
|
|
160
|
+
}
|
|
161
|
+
else if (item.disabled) {
|
|
162
|
+
element = document.createElement('span');
|
|
163
|
+
element.className = 'disabled';
|
|
164
|
+
element.part.add('disabled');
|
|
165
|
+
element.setAttribute('aria-disabled', 'true');
|
|
166
|
+
}
|
|
167
|
+
else if (item.href) {
|
|
168
|
+
const anchor = document.createElement('a');
|
|
169
|
+
anchor.href = item.href;
|
|
170
|
+
if (item.target)
|
|
171
|
+
anchor.target = item.target;
|
|
172
|
+
anchor.part.add('link');
|
|
173
|
+
anchor.addEventListener('click', event => {
|
|
174
|
+
if (!this._dispatchItemClick(item, index))
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
});
|
|
177
|
+
element = anchor;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const button = document.createElement('button');
|
|
181
|
+
button.type = 'button';
|
|
182
|
+
button.part.add('button');
|
|
183
|
+
button.addEventListener('click', () => this._dispatchItemClick(item, index));
|
|
184
|
+
element = button;
|
|
185
|
+
}
|
|
186
|
+
if (item.ariaLabel)
|
|
187
|
+
element.setAttribute('aria-label', item.ariaLabel);
|
|
188
|
+
if (content instanceof Node)
|
|
189
|
+
element.append(content);
|
|
190
|
+
else
|
|
191
|
+
element.textContent = content == null ? '' : String(content);
|
|
192
|
+
row.append(element);
|
|
193
|
+
return row;
|
|
194
|
+
});
|
|
195
|
+
this._list.replaceChildren(...rows);
|
|
196
|
+
}
|
|
197
|
+
_dispatchItemClick(item, index) {
|
|
198
|
+
return this.dispatchEvent(new CustomEvent('breadcrumb-click', {
|
|
199
|
+
bubbles: true,
|
|
200
|
+
cancelable: true,
|
|
201
|
+
composed: true,
|
|
202
|
+
detail: { item, index },
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
export function defineBreadcrumbComponents() {
|
|
207
|
+
if (!customElements.get('hy-breadcrumb'))
|
|
208
|
+
customElements.define('hy-breadcrumb', HYBreadcrumb);
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=breadcrumb.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type HYButtonGroupOrientation = 'horizontal' | 'vertical';
|
|
2
|
+
export declare class HYButtonGroup extends HTMLElement {
|
|
3
|
+
private readonly _group;
|
|
4
|
+
static get observedAttributes(): string[];
|
|
5
|
+
constructor();
|
|
6
|
+
connectedCallback(): void;
|
|
7
|
+
attributeChangedCallback(): void;
|
|
8
|
+
get orientation(): HYButtonGroupOrientation;
|
|
9
|
+
set orientation(value: HYButtonGroupOrientation);
|
|
10
|
+
private _sync;
|
|
11
|
+
}
|
|
12
|
+
export declare function defineButtonGroupComponents(): void;
|