@haiyue/ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +1 -1
  3. package/README.md +226 -2
  4. package/dist/border-beam.d.ts +26 -0
  5. package/dist/border-beam.js +174 -0
  6. package/dist/button.d.ts +10 -0
  7. package/dist/button.js +85 -0
  8. package/dist/checkbox.d.ts +2 -2
  9. package/dist/checkbox.js +11 -11
  10. package/dist/context-menu.d.ts +6 -6
  11. package/dist/context-menu.js +11 -9
  12. package/dist/dialog.d.ts +5 -5
  13. package/dist/dialog.js +18 -18
  14. package/dist/drawer.d.ts +49 -0
  15. package/dist/drawer.js +377 -0
  16. package/dist/dropdown.d.ts +9 -9
  17. package/dist/dropdown.js +9 -9
  18. package/dist/expandable.d.ts +32 -0
  19. package/dist/expandable.js +136 -0
  20. package/dist/history-controls.d.ts +40 -0
  21. package/dist/history-controls.js +123 -0
  22. package/dist/index.d.ts +36 -31
  23. package/dist/index.js +35 -85
  24. package/dist/input.d.ts +5 -5
  25. package/dist/input.js +13 -13
  26. package/dist/menu-shared.d.ts +4 -4
  27. package/dist/menu-shared.js +4 -4
  28. package/dist/notification.d.ts +55 -0
  29. package/dist/notification.js +357 -0
  30. package/dist/radio.d.ts +2 -2
  31. package/dist/radio.js +12 -12
  32. package/dist/range.d.ts +74 -0
  33. package/dist/range.js +393 -0
  34. package/dist/select.d.ts +6 -6
  35. package/dist/select.js +20 -20
  36. package/dist/split.d.ts +6 -6
  37. package/dist/split.js +14 -14
  38. package/dist/tabs.d.ts +10 -6
  39. package/dist/tabs.js +94 -30
  40. package/dist/tooltip.d.ts +4 -4
  41. package/dist/tooltip.js +9 -9
  42. package/dist/tree-model.d.ts +15 -7
  43. package/dist/tree-model.js +93 -80
  44. package/dist/tree-node.d.ts +3 -3
  45. package/dist/tree-node.js +3 -3
  46. package/dist/tree-types.d.ts +14 -14
  47. package/dist/tree.d.ts +9 -7
  48. package/dist/tree.js +68 -66
  49. package/dist/virtual-list.d.ts +61 -0
  50. package/dist/virtual-list.js +324 -0
  51. package/package.json +100 -7
  52. package/themes/haiyue-dark.css +46 -0
  53. package/themes/haiyue-light.css +46 -0
  54. package/src/checkbox.ts +0 -215
  55. package/src/context-menu.ts +0 -248
  56. package/src/dialog.ts +0 -195
  57. package/src/dropdown.ts +0 -249
  58. package/src/index.ts +0 -192
  59. package/src/input.ts +0 -150
  60. package/src/menu-shared.ts +0 -65
  61. package/src/radio.ts +0 -216
  62. package/src/select.ts +0 -271
  63. package/src/split.ts +0 -306
  64. package/src/tabs.ts +0 -182
  65. package/src/tooltip.ts +0 -321
  66. package/src/tree-model.ts +0 -305
  67. package/src/tree-node.ts +0 -67
  68. package/src/tree-types.ts +0 -47
  69. package/src/tree.ts +0 -1002
package/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.1.2 - 2026-09-03
6
+
7
+ - 修复 `<hy-virtual-list>` 快速滚动时 light DOM slot 异步分发旧行的问题;虚拟窗口现在完全在 Shadow DOM 内同步替换,避免上一范围的条目再次闪现。
8
+ - 新增 `HYExpandable` / `<hy-expandable>`:保持内容挂载的非模态展开容器,提供独立子入口、尺寸与按钮位置配置、自定义图标、事件和主题变量。
9
+ - 修复展开容器位于嵌套 Shadow DOM 时 Escape 无法恢复的问题;内部菜单可优先消费 Escape,嵌套展开容器每次只关闭内层。
10
+ - Tabs 支持横向溢出滚动,并保持标签文字不换行。
11
+ - 补充展开容器示例和浏览器交互回归,覆盖键盘、焦点、事件、内容保留及断开重连。
12
+ - API 基线、包体积与消费者验证由 UI 仓库独立维护,不再由 Engine 发布门禁管理。
13
+
14
+ ## 0.1.1 - 2026-08-22
15
+
16
+ - 新增可按需导入的 `HYRange` / `<hy-range>` 数值滑块,支持单值和双拖拽点区间、步进与键盘操作、实时/提交事件,以及拖拽点和轨道样式定制。
17
+ - 新增可按需导入的 `HYBorderBeam` / `<hy-border-beam>` 响应式边框流光组件,支持粗细、速度倍率、颜色和多流光错相配置。
18
+ - 新增可按需导入的 `HYNotification` / `<hy-notification>` 通知中心,提供 success/info/warning/error 语义配色和图标、六向 placement、自动关闭、Z 轴堆叠及可选倒计时进度条。
19
+ - 新增可按需导入的 `HYDrawer` / `<hy-drawer>` 抽屉组件,支持四向 placement、可选蒙层、Esc/蒙层关闭、焦点管理及 `destroyOnHidden` 内容卸载。
20
+ - 新增可按需导入的 `HYVirtualList` / `<hy-virtual-list>` 固定行高虚拟列表,仅渲染可视区与 overscan 缓冲项,并提供内部滚动条、范围事件、点击事件和 `scrollToIndex()`。
21
+ - 组件品牌前缀从 `GE/ge` 统一迁移为 `HY/hy`,不保留旧标签、类名或 CSS token alias;新增可独立导入的 Moonlight 淡蓝亮色和 Nightfall 深蓝紫暗色皮肤。
22
+ - 独立 UI 仓库移除重复的 `ui/` workspace 层级,并为全部组件新增 `@haiyue/ui/<component>` 独立、无副作用的公开入口;根入口继续提供显式全量注册。
23
+
24
+ ## 0.1.0 - 2026-07-13
25
+
26
+ - 建立海月 3D 优先的稳定包边界、生命周期、编辑器工作流、可观测性、资产管线和脚本 runtime。
27
+ - 用 `simple` / `batched` / `gpu-driven` / `diagnostic` RenderProfile 替换公开渲染布尔开关,并加入显式设备降级报告。
28
+ - 加入原生 glTF metallic-roughness PBR、directional shadow map、environment IBL、material variants 和可扩展材质 shader contract。
29
+ - examples/games 改为 manifest 驱动,统一 build policy、能力覆盖矩阵、真实 WebGPU 像素基线和 release gate。
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 HaiyueStudio Contributors
3
+ Copyright (c) 2026 Haiyue Engine contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,9 +1,233 @@
1
1
  # @haiyue/ui
2
2
 
3
- Dependency-free Web Components used by HaiyueStudio tools and products.
3
+ Dependency-free Web Components for HaiyueStudio products. Components own presentation and local interaction;
4
+ they do not depend on Engine, Editor, Games, or AIStudio state.
5
+
6
+ **Online component gallery:** <https://haiyuestudio.github.io/UI/>
7
+
8
+ Public classes use the `HY` prefix and custom-element tags use `hy-`:
9
+
10
+ ```html
11
+ <hy-button label="Publish"></hy-button>
12
+ <hy-tree allow-drag></hy-tree>
13
+ ```
4
14
 
5
15
  ```bash
6
16
  npm install @haiyue/ui
7
17
  ```
8
18
 
9
- The package owns presentation and local interaction only. It does not depend on Engine, Editor, or AIStudio.
19
+ ## Import only what a product uses
20
+
21
+ Each component has an isolated package subpath. Importing a subpath loads only that component and its direct
22
+ helpers, and registration stays explicit:
23
+
24
+ ```ts
25
+ import { defineButtonComponents } from '@haiyue/ui/button';
26
+ import { defineTreeComponents } from '@haiyue/ui/tree';
27
+ import { defineVirtualListComponents } from '@haiyue/ui/virtual-list';
28
+
29
+ defineButtonComponents();
30
+ defineTreeComponents();
31
+ defineVirtualListComponents();
32
+ ```
33
+
34
+ Component JavaScript does not register custom elements during module evaluation, and every component subpath
35
+ publishes matching JavaScript and type declarations. Only `themes/*.css` is marked as a package side effect so
36
+ bundlers retain an imported skin while excluding component JavaScript that is not reachable from selected imports.
37
+
38
+ The root entry remains available when an application intentionally wants every component:
39
+
40
+ ```ts
41
+ import { defineHaiyueUI } from '@haiyue/ui';
42
+
43
+ defineHaiyueUI();
44
+ ```
45
+
46
+ Because `defineHaiyueUI()` references the complete component set, prefer focused subpaths in bundle-sensitive
47
+ product entry points.
48
+
49
+ ## Tabs
50
+
51
+ `@haiyue/ui/tabs` retains its existing `HYTabs`, `HYTabOption`, `HYTabChangeDetail`, and
52
+ `defineTabsComponents` exports; the 0.1.3 fix adds no new public exports. Assign `options`
53
+ and `value`, provide children with matching `slot` names, and listen for `tab-change`.
54
+ Property-assigned options and the selected value survive reconnection. Set `aria-label`
55
+ to name the tablist. Arrow keys, Home and End both move focus and activate the next
56
+ enabled tab with roving focus; the active tab labels its panel within the component's
57
+ shadow tree. The generated `aria-controls` and tabpanel ids are per-instance opaque
58
+ values and should not be treated as stable application selectors.
59
+
60
+ The existing `@haiyue/ui/border-beam` exports are also unchanged. Its constructor only
61
+ builds shadow content; host appearance is applied after creation, avoiding browser
62
+ custom-element construction errors during repeated `document.createElement` calls.
63
+
64
+ ## Virtual list
65
+
66
+ `<hy-virtual-list>` renders only the visible fixed-height rows plus a configurable overscan buffer. The full
67
+ collection stays in the `items` property, while `renderItem` is called only for rows that currently need DOM:
68
+
69
+ ```ts
70
+ import { defineVirtualListComponents } from '@haiyue/ui/virtual-list';
71
+
72
+ defineVirtualListComponents();
73
+ const list = document.querySelector('hy-virtual-list');
74
+ list.items = records;
75
+ list.itemHeight = 40;
76
+ list.overscan = 3;
77
+ list.renderItem = (record, index) => `${index + 1}. ${record.name}`;
78
+ list.scrollToIndex(50_000, 'center');
79
+ ```
80
+
81
+ Use the `height` property (or `height` attribute) for the internal viewport. `visible-range-change` reports
82
+ exclusive `endIndex` / `visibleEndIndex` values, and `item-click` reports the source item and absolute index.
83
+ The scrollbar can be themed with inherited semantic tokens and `--hy-virtual-list-scrollbar-size`.
84
+
85
+ ## Drawer
86
+
87
+ Import and register the drawer independently when a product needs an off-canvas panel:
88
+
89
+ ```ts
90
+ import { defineDrawerComponents } from '@haiyue/ui/drawer';
91
+
92
+ defineDrawerComponents();
93
+ ```
94
+
95
+ `<hy-drawer>` supports `top`, `right`, `bottom`, and `left` placement. The mask defaults to enabled; set
96
+ `mask="false"` or `drawer.mask = false` to keep the underlying page visible and interactive. Escape, the close
97
+ button, and mask clicks close the drawer and emit `drawer-close` with an `action`, `mask`, `escape`, or
98
+ `programmatic` reason. With `destroy-on-hidden`, slotted children are unmounted after the exit transition and
99
+ restored when the drawer opens again, allowing nested custom elements to run their disconnect/connect cleanup.
100
+
101
+ ## Notification
102
+
103
+ `<hy-notification>` is an imperative notification center with isolated package entry `@haiyue/ui/notification`:
104
+
105
+ ```ts
106
+ import { defineNotificationComponents } from '@haiyue/ui/notification';
107
+
108
+ defineNotificationComponents();
109
+ const center = document.querySelector('hy-notification');
110
+ center.success({
111
+ message: 'Export complete',
112
+ description: 'The project package is ready.',
113
+ placement: 'topRight',
114
+ duration: 4,
115
+ showProgress: true,
116
+ });
117
+ ```
118
+
119
+ Types are `success`, `info`, `warning`, and `error`. Placements are `topLeft`, `top`, `topRight`, `bottomLeft`,
120
+ `bottom`, and `bottomRight`. Each notice may override the host defaults for placement, duration in seconds, and
121
+ progress visibility; duration `0` disables automatic closing. Notices at the same placement form a perspective
122
+ stack and expand on hover or keyboard focus. Use `close(key)` for one notice or `destroy()` for all notices.
123
+
124
+ ## Border beam
125
+
126
+ `<hy-border-beam>` wraps arbitrary content with an animated SVG beam that stays aligned to the element's live
127
+ rounded border. Import it from `@haiyue/ui/border-beam` and register it explicitly:
128
+
129
+ ```ts
130
+ import { defineBorderBeamComponents } from '@haiyue/ui/border-beam';
131
+
132
+ defineBorderBeamComponents();
133
+ ```
134
+
135
+ Use `thickness` for stroke width, `color` for any valid CSS color, and `count` for 1–12 evenly phased beams.
136
+ `speed` is a multiplier: `1` completes the default loop in six seconds, while `2` runs twice as fast. The
137
+ component observes its own size and keeps the SVG view box and corner radius synchronized automatically.
138
+ Consumers may override `--hy-border-beam-radius` and `--hy-border-beam-track-color`.
139
+
140
+ ## Range
141
+
142
+ Import the slider independently and opt into two-handle interval mode with the `range` attribute:
143
+
144
+ ```ts
145
+ import { defineRangeComponents } from '@haiyue/ui/range';
146
+
147
+ defineRangeComponents();
148
+ const interval = document.querySelector('hy-range[range]');
149
+ interval.values = [20, 80];
150
+ interval.addEventListener('value-change', event => console.log(event.detail.value));
151
+ ```
152
+
153
+ Single-value mode uses `value`; interval mode exposes `lowerValue`, `upperValue`, and `values`. Both modes honor
154
+ `min`, `max`, and `step`, and support pointer dragging, track clicks, arrows, Page Up/Down, Home, and End. Use
155
+ `--hy-range-handle-*`, `--hy-range-rail-*`, and `--hy-range-track-*` tokens for styling, or target the exported
156
+ `rail`, `track`, `handle`, `handle-lower`, and `handle-upper` shadow parts. `value-input` reports live movement and
157
+ `value-change` reports a committed pointer or keyboard change.
158
+
159
+ ## Themes
160
+
161
+ Two optional CSS theme entries are provided. Import only the skin a product needs, or import both when the
162
+ product offers runtime switching:
163
+
164
+ ```ts
165
+ import '@haiyue/ui/themes/light.css';
166
+ import '@haiyue/ui/themes/dark.css';
167
+ ```
168
+
169
+ Apply a theme to the document or to a component subtree. Tokens inherit through component shadow roots:
170
+
171
+ ```html
172
+ <html data-hy-theme="light">…</html>
173
+ <section data-hy-theme="dark">…</section>
174
+ ```
175
+
176
+ - `light` / Haiyue Moonlight: moon white, pale ice blue, cyan and a soft violet highlight.
177
+ - `dark` / Haiyue Nightfall: deep navy, blue violet, cool white and a restrained gold highlight.
178
+
179
+ All theme tokens use the `--hy-` prefix and may be overridden by consuming products.
180
+
181
+ ## Component examples
182
+
183
+ Browse the published gallery at <https://haiyuestudio.github.io/UI/>.
184
+
185
+ Run the interactive component gallery locally:
186
+
187
+ ```bash
188
+ npm run examples
189
+ ```
190
+
191
+ Then open <http://localhost:4173/>. The gallery provides a left-side component index and live examples for
192
+ every exported component, including editable parameters and event output.
193
+
194
+ ## Expandable container
195
+
196
+ Explicit export: `@haiyue/ui/expandable` → `HYExpandable`, `defineExpandableComponents`,
197
+ `HYExpandableButtonPosition`, `HYExpandableChangeDetail`. Also included in `defineHaiyueUI()`.
198
+
199
+ ```html
200
+ <hy-expandable expanded-width="1200px" expanded-height="90dvh"
201
+ button-position="top-right" expand-label="放大" restore-label="还原">
202
+ <div>Your graph, editor, or other stateful content</div>
203
+ <!-- Optional SVG elements: slot="expand-icon" / slot="restore-icon" -->
204
+ </hy-expandable>
205
+ ```
206
+
207
+ `expanded` is a reflected boolean attribute/property; `toggle()` switches it.
208
+ `expandedWidth` / `expandedHeight` accept CSS lengths (defaults `90vw` / `90dvh`).
209
+ Sizes are clamped to the viewport with a margin; the normal size belongs to the host's layout/CSS.
210
+ `buttonPosition` supports `top-right`, `top-left`, `bottom-right`, `bottom-left`.
211
+ `expanded-change` bubbles with `{ expanded: boolean }`. `expand-icon` / `restore-icon` slots
212
+ replace the built-in SVG icons without accepting HTML strings. Labels also become tooltips.
213
+ Style through inherited `--hy-surface-color`, `--hy-border-color`, `--hy-text-color`,
214
+ `--hy-accent-color` and `--hy-expandable-{margin,padding,radius,shadow,button-size,button-offset}`;
215
+ `panel` and `toggle` are CSS parts.
216
+
217
+ Requires a browser supporting the Popover API. Expansion is non-modal and uses a fixed top-layer
218
+ panel so transformed/overflow ancestors cannot clip it. The same slotted DOM stays mounted;
219
+ normal-flow height is preserved while expanded. Keyboard activation uses the native button;
220
+ Escape restores the focused container. Disconnect closes the panel and releases event listeners.
221
+
222
+ ## Independent release checks
223
+
224
+ UI releases independently of Engine.
225
+
226
+ ```bash
227
+ npm ci
228
+ npx playwright install chromium
229
+ npm run release:check
230
+ ```
231
+
232
+ Candidates: `.artifacts/packages/`. Review API changes before `npm run api:update`.
233
+ See [release review](review/release-0.1.2.md).
@@ -0,0 +1,26 @@
1
+ /** Decorative animated border that follows the host's live rounded rectangle. */
2
+ export declare class HYBorderBeam extends HTMLElement {
3
+ private readonly _svg;
4
+ private readonly _track;
5
+ private readonly _beamLayer;
6
+ private _resizeObserver;
7
+ private _connected;
8
+ static get observedAttributes(): string[];
9
+ constructor();
10
+ connectedCallback(): void;
11
+ disconnectedCallback(): void;
12
+ attributeChangedCallback(name: string): void;
13
+ get thickness(): number;
14
+ set thickness(value: number);
15
+ /** Animation speed multiplier. 1 is normal speed and 2 is twice as fast. */
16
+ get speed(): number;
17
+ set speed(value: number);
18
+ get color(): string;
19
+ set color(value: string);
20
+ get count(): number;
21
+ set count(value: number);
22
+ refresh(): void;
23
+ private _renderBeams;
24
+ private _updateAppearance;
25
+ }
26
+ export declare function defineBorderBeamComponents(): void;
@@ -0,0 +1,174 @@
1
+ const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
2
+ const DEFAULT_THICKNESS = 2;
3
+ const DEFAULT_SPEED = 1;
4
+ const DEFAULT_COUNT = 1;
5
+ const BASE_DURATION_SECONDS = 6;
6
+ function finiteNumber(value, fallback) {
7
+ const number = Number(value);
8
+ return Number.isFinite(number) ? number : fallback;
9
+ }
10
+ /** Decorative animated border that follows the host's live rounded rectangle. */
11
+ export class HYBorderBeam extends HTMLElement {
12
+ _svg = document.createElementNS(SVG_NAMESPACE, 'svg');
13
+ _track = document.createElementNS(SVG_NAMESPACE, 'rect');
14
+ _beamLayer = document.createElementNS(SVG_NAMESPACE, 'g');
15
+ _resizeObserver = null;
16
+ _connected = false;
17
+ static get observedAttributes() {
18
+ return ['thickness', 'speed', 'color', 'count'];
19
+ }
20
+ constructor() {
21
+ super();
22
+ const root = this.attachShadow({ mode: 'open' });
23
+ const style = document.createElement('style');
24
+ style.textContent = `
25
+ :host {
26
+ position: relative;
27
+ display: block;
28
+ min-width: 0;
29
+ border-radius: var(--hy-border-beam-radius, 10px);
30
+ isolation: isolate;
31
+ --_beam-color: var(--hy-accent-color, #7dd3fc);
32
+ }
33
+ svg {
34
+ position: absolute;
35
+ z-index: 1;
36
+ inset: 0;
37
+ width: 100%;
38
+ height: 100%;
39
+ overflow: visible;
40
+ pointer-events: none;
41
+ }
42
+ .track {
43
+ fill: none;
44
+ stroke: var(--hy-border-beam-track-color, var(--hy-border-color, rgba(125, 211, 252, 0.2)));
45
+ vector-effect: non-scaling-stroke;
46
+ }
47
+ .beam {
48
+ fill: none;
49
+ stroke: var(--_beam-color);
50
+ stroke-linecap: round;
51
+ vector-effect: non-scaling-stroke;
52
+ filter: drop-shadow(0 0 var(--_beam-glow, 5px) var(--_beam-color));
53
+ animation: hy-border-beam-flow linear infinite;
54
+ }
55
+ @keyframes hy-border-beam-flow {
56
+ to { stroke-dashoffset: -100; }
57
+ }
58
+ @media (prefers-reduced-motion: reduce) {
59
+ .beam { animation-play-state: paused; }
60
+ }
61
+ `;
62
+ const slot = document.createElement('slot');
63
+ this._svg.setAttribute('aria-hidden', 'true');
64
+ this._svg.setAttribute('preserveAspectRatio', 'none');
65
+ this._track.classList.add('track');
66
+ this._track.part.add('track');
67
+ this._track.setAttribute('pathLength', '100');
68
+ this._svg.append(this._track, this._beamLayer);
69
+ root.append(style, slot, this._svg);
70
+ this._renderBeams();
71
+ }
72
+ connectedCallback() {
73
+ if (this._connected)
74
+ return;
75
+ this._connected = true;
76
+ this._updateAppearance();
77
+ if (typeof ResizeObserver !== 'undefined') {
78
+ this._resizeObserver = new ResizeObserver(() => this.refresh());
79
+ this._resizeObserver.observe(this);
80
+ }
81
+ this.refresh();
82
+ }
83
+ disconnectedCallback() {
84
+ if (!this._connected)
85
+ return;
86
+ this._connected = false;
87
+ this._resizeObserver?.disconnect();
88
+ this._resizeObserver = null;
89
+ }
90
+ attributeChangedCallback(name) {
91
+ if (name === 'count')
92
+ this._renderBeams();
93
+ this._updateAppearance();
94
+ this.refresh();
95
+ }
96
+ get thickness() {
97
+ return Math.min(20, Math.max(0.5, finiteNumber(this.getAttribute('thickness'), DEFAULT_THICKNESS)));
98
+ }
99
+ set thickness(value) {
100
+ this.setAttribute('thickness', String(Math.min(20, Math.max(0.5, finiteNumber(value, DEFAULT_THICKNESS)))));
101
+ }
102
+ /** Animation speed multiplier. 1 is normal speed and 2 is twice as fast. */
103
+ get speed() {
104
+ return Math.min(10, Math.max(0.1, finiteNumber(this.getAttribute('speed'), DEFAULT_SPEED)));
105
+ }
106
+ set speed(value) {
107
+ this.setAttribute('speed', String(Math.min(10, Math.max(0.1, finiteNumber(value, DEFAULT_SPEED)))));
108
+ }
109
+ get color() { return this.getAttribute('color') ?? ''; }
110
+ set color(value) {
111
+ const normalized = String(value).trim();
112
+ if (normalized)
113
+ this.setAttribute('color', normalized);
114
+ else
115
+ this.removeAttribute('color');
116
+ }
117
+ get count() {
118
+ return Math.min(12, Math.max(1, Math.floor(finiteNumber(this.getAttribute('count'), DEFAULT_COUNT))));
119
+ }
120
+ set count(value) {
121
+ this.setAttribute('count', String(Math.min(12, Math.max(1, Math.floor(finiteNumber(value, DEFAULT_COUNT))))));
122
+ }
123
+ refresh() {
124
+ const rect = this.getBoundingClientRect();
125
+ const width = Math.max(1, rect.width);
126
+ const height = Math.max(1, rect.height);
127
+ const inset = this.thickness / 2;
128
+ const radius = Math.min(Math.max(0, Number.parseFloat(getComputedStyle(this).borderTopLeftRadius) || 10), width / 2, height / 2);
129
+ this._svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
130
+ for (const element of [this._track, ...this._beamLayer.querySelectorAll('.beam')]) {
131
+ element.setAttribute('x', String(inset));
132
+ element.setAttribute('y', String(inset));
133
+ element.setAttribute('width', String(Math.max(0, width - this.thickness)));
134
+ element.setAttribute('height', String(Math.max(0, height - this.thickness)));
135
+ element.setAttribute('rx', String(Math.max(0, radius - inset)));
136
+ element.setAttribute('ry', String(Math.max(0, radius - inset)));
137
+ }
138
+ }
139
+ _renderBeams() {
140
+ const beams = [];
141
+ for (let index = 0; index < this.count; index += 1) {
142
+ const beam = document.createElementNS(SVG_NAMESPACE, 'rect');
143
+ beam.classList.add('beam');
144
+ beam.part.add('beam');
145
+ beam.setAttribute('pathLength', '100');
146
+ beams.push(beam);
147
+ }
148
+ this._beamLayer.replaceChildren(...beams);
149
+ // Construction may populate the shadow tree, but must not add host attributes.
150
+ // Host styles are applied by the connection/attribute lifecycle instead.
151
+ }
152
+ _updateAppearance() {
153
+ const duration = BASE_DURATION_SECONDS / this.speed;
154
+ const beamLength = Math.min(14, 60 / this.count);
155
+ this.style.setProperty('--_beam-glow', `${Math.max(3, this.thickness * 2.5)}px`);
156
+ if (this.color)
157
+ this.style.setProperty('--_beam-color', this.color);
158
+ else
159
+ this.style.removeProperty('--_beam-color');
160
+ this._track.setAttribute('stroke-width', String(Math.max(1, this.thickness * 0.5)));
161
+ const beams = [...this._beamLayer.querySelectorAll('.beam')];
162
+ beams.forEach((beam, index) => {
163
+ beam.setAttribute('stroke-width', String(this.thickness));
164
+ beam.setAttribute('stroke-dasharray', `${beamLength} ${100 - beamLength}`);
165
+ beam.style.animationDuration = `${duration}s`;
166
+ beam.style.animationDelay = `${-(duration * index) / Math.max(1, beams.length)}s`;
167
+ });
168
+ }
169
+ }
170
+ export function defineBorderBeamComponents() {
171
+ if (!customElements.get('hy-border-beam'))
172
+ customElements.define('hy-border-beam', HYBorderBeam);
173
+ }
174
+ //# sourceMappingURL=border-beam.js.map
@@ -0,0 +1,10 @@
1
+ export declare class HYButton extends HTMLElement {
2
+ private readonly _button;
3
+ private readonly _slot;
4
+ static get observedAttributes(): string[];
5
+ constructor();
6
+ connectedCallback(): void;
7
+ attributeChangedCallback(): void;
8
+ private _sync;
9
+ }
10
+ export declare function defineButtonComponents(): void;
package/dist/button.js ADDED
@@ -0,0 +1,85 @@
1
+ export class HYButton extends HTMLElement {
2
+ _button = document.createElement('button');
3
+ _slot = document.createElement('slot');
4
+ static get observedAttributes() {
5
+ return ['label', 'disabled', 'aria-label', 'title', 'icon-only'];
6
+ }
7
+ constructor() {
8
+ super();
9
+ const root = this.attachShadow({ mode: 'open' });
10
+ const style = document.createElement('style');
11
+ style.textContent = `
12
+ :host { display: inline-block; }
13
+ button {
14
+ min-height: 28px;
15
+ padding: 0 10px;
16
+ border: 1px solid var(--hy-border-color, #3d4654);
17
+ border-radius: 4px;
18
+ background: var(--hy-surface-elevated-color, #202733);
19
+ color: var(--hy-text-color, #eef3ff);
20
+ font: 12px system-ui, sans-serif;
21
+ cursor: pointer;
22
+ }
23
+ button:hover:not(:disabled) {
24
+ border-color: var(--hy-hover-border-color, #536477);
25
+ background: var(--hy-hover-bg-color, #273241);
26
+ }
27
+ button:focus-visible {
28
+ outline: 2px solid var(--hy-focus-border-color, #79a8ff);
29
+ outline-offset: 1px;
30
+ }
31
+ button.icon-only {
32
+ width: 28px;
33
+ padding: 0;
34
+ display: inline-grid;
35
+ place-items: center;
36
+ }
37
+ ::slotted(svg) {
38
+ width: 16px;
39
+ height: 16px;
40
+ display: block;
41
+ fill: none;
42
+ stroke: currentColor;
43
+ stroke-width: 2;
44
+ stroke-linecap: round;
45
+ stroke-linejoin: round;
46
+ }
47
+ button:disabled {
48
+ opacity: 0.5;
49
+ cursor: default;
50
+ }
51
+ `;
52
+ root.append(style, this._button);
53
+ }
54
+ connectedCallback() {
55
+ this._sync();
56
+ }
57
+ attributeChangedCallback() {
58
+ this._sync();
59
+ }
60
+ _sync() {
61
+ const label = this.getAttribute('label');
62
+ if (label !== null) {
63
+ this._button.textContent = label;
64
+ this._button.classList.remove('icon-only');
65
+ this._button.removeAttribute('aria-label');
66
+ }
67
+ else {
68
+ if (this._button.firstChild !== this._slot)
69
+ this._button.replaceChildren(this._slot);
70
+ this._button.classList.toggle('icon-only', this.hasAttribute('icon-only'));
71
+ const accessibleLabel = this.getAttribute('aria-label') ?? this.getAttribute('title') ?? '';
72
+ if (accessibleLabel)
73
+ this._button.setAttribute('aria-label', accessibleLabel);
74
+ else
75
+ this._button.removeAttribute('aria-label');
76
+ }
77
+ this._button.disabled = this.hasAttribute('disabled');
78
+ }
79
+ }
80
+ export function defineButtonComponents() {
81
+ if (!customElements.get('hy-button')) {
82
+ customElements.define('hy-button', HYButton);
83
+ }
84
+ }
85
+ //# sourceMappingURL=button.js.map
@@ -1,8 +1,8 @@
1
- export interface GECheckboxChangeDetail {
1
+ export interface HYCheckboxChangeDetail {
2
2
  checked: boolean;
3
3
  value: string;
4
4
  }
5
- export declare class GECheckbox extends HTMLElement {
5
+ export declare class HYCheckbox extends HTMLElement {
6
6
  private readonly _input;
7
7
  private readonly _box;
8
8
  private readonly _label;
package/dist/checkbox.js CHANGED
@@ -1,4 +1,4 @@
1
- export class GECheckbox extends HTMLElement {
1
+ export class HYCheckbox extends HTMLElement {
2
2
  _input = document.createElement('input');
3
3
  _box = document.createElement('span');
4
4
  _label = document.createElement('span');
@@ -15,7 +15,7 @@ export class GECheckbox extends HTMLElement {
15
15
  display: inline-block;
16
16
  width: max-content;
17
17
  min-width: 0;
18
- color: var(--ge-checkbox-text-color, var(--ge-text-color, #d8e2f2));
18
+ color: var(--hy-checkbox-text-color, var(--hy-text-color, #d8e2f2));
19
19
  font: 12px system-ui, sans-serif;
20
20
  }
21
21
  :host([disabled]) {
@@ -51,20 +51,20 @@ export class GECheckbox extends HTMLElement {
51
51
  flex: 0 0 14px;
52
52
  display: inline-grid;
53
53
  place-items: center;
54
- border: 1px solid var(--ge-checkbox-border-color, var(--ge-border-color, #303746));
54
+ border: 1px solid var(--hy-checkbox-border-color, var(--hy-border-color, #303746));
55
55
  border-radius: 3px;
56
- background: var(--ge-checkbox-bg-color, var(--ge-input-bg-color, #121822));
57
- color: var(--ge-checkbox-mark-color, var(--ge-selected-text-color, #ffffff));
56
+ background: var(--hy-checkbox-bg-color, var(--hy-input-bg-color, #121822));
57
+ color: var(--hy-checkbox-mark-color, var(--hy-selected-text-color, #ffffff));
58
58
  transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
59
59
  }
60
60
  input:focus-visible + .box {
61
- border-color: var(--ge-focus-border-color, #3d6fa8);
62
- box-shadow: 0 0 0 2px var(--ge-focus-ring-color, rgba(61, 111, 168, 0.28));
61
+ border-color: var(--hy-focus-border-color, #3d6fa8);
62
+ box-shadow: 0 0 0 2px var(--hy-focus-ring-color, rgba(61, 111, 168, 0.28));
63
63
  }
64
64
  input:checked + .box,
65
65
  input:indeterminate + .box {
66
- border-color: var(--ge-checkbox-checked-border-color, var(--ge-accent-color, #3d6fa8));
67
- background: var(--ge-checkbox-checked-bg-color, var(--ge-accent-strong-color, #255a91));
66
+ border-color: var(--hy-checkbox-checked-border-color, var(--hy-accent-color, #3d6fa8));
67
+ background: var(--hy-checkbox-checked-bg-color, var(--hy-accent-strong-color, #255a91));
68
68
  }
69
69
  input:disabled + .box {
70
70
  cursor: default;
@@ -197,8 +197,8 @@ export class GECheckbox extends HTMLElement {
197
197
  };
198
198
  }
199
199
  export function defineCheckboxComponents() {
200
- if (!customElements.get('ge-checkbox')) {
201
- customElements.define('ge-checkbox', GECheckbox);
200
+ if (!customElements.get('hy-checkbox')) {
201
+ customElements.define('hy-checkbox', HYCheckbox);
202
202
  }
203
203
  }
204
204
  //# sourceMappingURL=checkbox.js.map
@@ -1,14 +1,14 @@
1
- export interface GEContextMenuItem {
1
+ export interface HYContextMenuItem {
2
2
  label?: string;
3
3
  value?: string;
4
4
  disabled?: boolean;
5
5
  separator?: boolean;
6
6
  }
7
- export interface GEContextMenuSelectDetail {
7
+ export interface HYContextMenuSelectDetail {
8
8
  value: string;
9
- item: GEContextMenuItem;
9
+ item: HYContextMenuItem;
10
10
  }
11
- export declare class GEContextMenu extends HTMLElement {
11
+ export declare class HYContextMenu extends HTMLElement {
12
12
  private readonly _root;
13
13
  private readonly _style;
14
14
  private readonly _menu;
@@ -20,8 +20,8 @@ export declare class GEContextMenu extends HTMLElement {
20
20
  connectedCallback(): void;
21
21
  disconnectedCallback(): void;
22
22
  attributeChangedCallback(name: string): void;
23
- get items(): GEContextMenuItem[];
24
- set items(value: GEContextMenuItem[]);
23
+ get items(): HYContextMenuItem[];
24
+ set items(value: HYContextMenuItem[]);
25
25
  openAt(x: number, y: number): void;
26
26
  close(): void;
27
27
  private _syncDocumentListeners;