@haiyue/ui 0.1.1 → 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.
Files changed (99) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +504 -1
  3. package/dist/adapters/web/controls.d.ts +19 -0
  4. package/dist/adapters/web/controls.js +284 -0
  5. package/dist/adapters/web/dom.d.ts +10 -0
  6. package/dist/adapters/web/dom.js +98 -0
  7. package/dist/adapters/web/index.d.ts +3 -0
  8. package/dist/adapters/web/index.js +3 -0
  9. package/dist/adapters/web/renderer.d.ts +18 -0
  10. package/dist/adapters/web/renderer.js +545 -0
  11. package/dist/adapters/web/styles.d.ts +3 -0
  12. package/dist/adapters/web/styles.js +62 -0
  13. package/dist/adapters/web/types.d.ts +46 -0
  14. package/dist/adapters/web/types.js +2 -0
  15. package/dist/affix.d.ts +35 -0
  16. package/dist/affix.js +137 -0
  17. package/dist/badge.d.ts +45 -0
  18. package/dist/badge.js +168 -0
  19. package/dist/border-beam.js +3 -1
  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/cascader.d.ts +65 -0
  27. package/dist/cascader.js +479 -0
  28. package/dist/collapse.d.ts +55 -0
  29. package/dist/collapse.js +261 -0
  30. package/dist/core/actions.d.ts +32 -0
  31. package/dist/core/actions.js +145 -0
  32. package/dist/core/data.d.ts +4 -0
  33. package/dist/core/data.js +27 -0
  34. package/dist/core/index.d.ts +7 -0
  35. package/dist/core/index.js +6 -0
  36. package/dist/core/platform-view.d.ts +17 -0
  37. package/dist/core/platform-view.js +84 -0
  38. package/dist/core/selection.d.ts +14 -0
  39. package/dist/core/selection.js +93 -0
  40. package/dist/core/text.d.ts +31 -0
  41. package/dist/core/text.js +154 -0
  42. package/dist/core/types.d.ts +250 -0
  43. package/dist/core/types.js +2 -0
  44. package/dist/core/validation.d.ts +24 -0
  45. package/dist/core/validation.js +238 -0
  46. package/dist/date-picker.d.ts +60 -0
  47. package/dist/date-picker.js +355 -0
  48. package/dist/dialog.d.ts +4 -0
  49. package/dist/dialog.js +23 -5
  50. package/dist/divider.d.ts +33 -0
  51. package/dist/divider.js +113 -0
  52. package/dist/draggable.d.ts +78 -0
  53. package/dist/draggable.js +255 -0
  54. package/dist/expandable.d.ts +32 -0
  55. package/dist/expandable.js +136 -0
  56. package/dist/index.d.ts +47 -1
  57. package/dist/index.js +69 -0
  58. package/dist/input.d.ts +30 -1
  59. package/dist/input.js +166 -54
  60. package/dist/portable/index.d.ts +4 -0
  61. package/dist/portable/index.js +3 -0
  62. package/dist/portable/page.d.ts +38 -0
  63. package/dist/portable/page.js +361 -0
  64. package/dist/portable/theme.d.ts +19 -0
  65. package/dist/portable/theme.js +40 -0
  66. package/dist/portable/tree.d.ts +10 -0
  67. package/dist/portable/tree.js +96 -0
  68. package/dist/range.d.ts +3 -0
  69. package/dist/range.js +16 -5
  70. package/dist/rate.d.ts +46 -0
  71. package/dist/rate.js +193 -0
  72. package/dist/resizable.d.ts +97 -0
  73. package/dist/resizable.js +426 -0
  74. package/dist/skeleton.d.ts +41 -0
  75. package/dist/skeleton.js +141 -0
  76. package/dist/steps.d.ts +45 -0
  77. package/dist/steps.js +203 -0
  78. package/dist/table.d.ts +106 -0
  79. package/dist/table.js +689 -0
  80. package/dist/tabs.d.ts +7 -0
  81. package/dist/tabs.js +90 -16
  82. package/dist/tag.d.ts +55 -0
  83. package/dist/tag.js +225 -0
  84. package/dist/time-picker.d.ts +62 -0
  85. package/dist/time-picker.js +298 -0
  86. package/dist/timeline.d.ts +48 -0
  87. package/dist/timeline.js +201 -0
  88. package/dist/toast.d.ts +55 -0
  89. package/dist/toast.js +224 -0
  90. package/dist/tour.d.ts +89 -0
  91. package/dist/tour.js +306 -0
  92. package/dist/virtual-keyboard.d.ts +95 -0
  93. package/dist/virtual-keyboard.js +432 -0
  94. package/dist/virtual-list.js +12 -10
  95. package/dist/virtual-mouse.d.ts +82 -0
  96. package/dist/virtual-mouse.js +358 -0
  97. package/dist/watermark.d.ts +44 -0
  98. package/dist/watermark.js +148 -0
  99. package/package.json +135 -7
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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
@@ -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
@@ -73,6 +73,7 @@ export class HYBorderBeam extends HTMLElement {
73
73
  if (this._connected)
74
74
  return;
75
75
  this._connected = true;
76
+ this._updateAppearance();
76
77
  if (typeof ResizeObserver !== 'undefined') {
77
78
  this._resizeObserver = new ResizeObserver(() => this.refresh());
78
79
  this._resizeObserver.observe(this);
@@ -145,7 +146,8 @@ export class HYBorderBeam extends HTMLElement {
145
146
  beams.push(beam);
146
147
  }
147
148
  this._beamLayer.replaceChildren(...beams);
148
- this._updateAppearance();
149
+ // Construction may populate the shadow tree, but must not add host attributes.
150
+ // Host styles are applied by the connection/attribute lifecycle instead.
149
151
  }
150
152
  _updateAppearance() {
151
153
  const duration = BASE_DURATION_SECONDS / this.speed;
@@ -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;