@haiyue/ui 0.1.0 → 0.1.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/LICENSE +1 -1
- package/README.md +166 -2
- package/dist/border-beam.d.ts +26 -0
- package/dist/border-beam.js +172 -0
- package/dist/button.d.ts +10 -0
- package/dist/button.js +85 -0
- package/dist/checkbox.d.ts +2 -2
- package/dist/checkbox.js +11 -11
- package/dist/context-menu.d.ts +6 -6
- package/dist/context-menu.js +11 -9
- package/dist/dialog.d.ts +5 -5
- package/dist/dialog.js +18 -18
- package/dist/drawer.d.ts +49 -0
- package/dist/drawer.js +377 -0
- package/dist/dropdown.d.ts +9 -9
- package/dist/dropdown.js +9 -9
- package/dist/history-controls.d.ts +40 -0
- package/dist/history-controls.js +123 -0
- package/dist/index.d.ts +34 -31
- package/dist/index.js +32 -85
- package/dist/input.d.ts +5 -5
- package/dist/input.js +13 -13
- package/dist/menu-shared.d.ts +4 -4
- package/dist/menu-shared.js +4 -4
- package/dist/notification.d.ts +55 -0
- package/dist/notification.js +357 -0
- package/dist/radio.d.ts +2 -2
- package/dist/radio.js +12 -12
- package/dist/range.d.ts +74 -0
- package/dist/range.js +393 -0
- package/dist/select.d.ts +6 -6
- package/dist/select.js +20 -20
- package/dist/split.d.ts +6 -6
- package/dist/split.js +14 -14
- package/dist/tabs.d.ts +6 -6
- package/dist/tabs.js +14 -14
- package/dist/tooltip.d.ts +4 -4
- package/dist/tooltip.js +9 -9
- package/dist/tree-model.d.ts +15 -7
- package/dist/tree-model.js +93 -80
- package/dist/tree-node.d.ts +3 -3
- package/dist/tree-node.js +3 -3
- package/dist/tree-types.d.ts +14 -14
- package/dist/tree.d.ts +9 -7
- package/dist/tree.js +68 -66
- package/dist/virtual-list.d.ts +61 -0
- package/dist/virtual-list.js +322 -0
- package/package.json +87 -11
- package/themes/haiyue-dark.css +46 -0
- package/themes/haiyue-light.css +46 -0
- package/src/checkbox.ts +0 -215
- package/src/context-menu.ts +0 -248
- package/src/dialog.ts +0 -195
- package/src/dropdown.ts +0 -249
- package/src/index.ts +0 -192
- package/src/input.ts +0 -150
- package/src/menu-shared.ts +0 -65
- package/src/radio.ts +0 -216
- package/src/select.ts +0 -271
- package/src/split.ts +0 -306
- package/src/tabs.ts +0 -182
- package/src/tooltip.ts +0 -321
- package/src/tree-model.ts +0 -305
- package/src/tree-node.ts +0 -67
- package/src/tree-types.ts +0 -47
- package/src/tree.ts +0 -1002
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
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,173 @@
|
|
|
1
1
|
# @haiyue/ui
|
|
2
2
|
|
|
3
|
-
Dependency-free Web Components
|
|
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
|
+
Public classes use the `HY` prefix and custom-element tags use `hy-`:
|
|
7
|
+
|
|
8
|
+
```html
|
|
9
|
+
<hy-button label="Publish"></hy-button>
|
|
10
|
+
<hy-tree allow-drag></hy-tree>
|
|
11
|
+
```
|
|
4
12
|
|
|
5
13
|
```bash
|
|
6
14
|
npm install @haiyue/ui
|
|
7
15
|
```
|
|
8
16
|
|
|
9
|
-
|
|
17
|
+
## Import only what a product uses
|
|
18
|
+
|
|
19
|
+
Each component has an isolated package subpath. Importing a subpath loads only that component and its direct
|
|
20
|
+
helpers, and registration stays explicit:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { defineButtonComponents } from '@haiyue/ui/button';
|
|
24
|
+
import { defineTreeComponents } from '@haiyue/ui/tree';
|
|
25
|
+
import { defineVirtualListComponents } from '@haiyue/ui/virtual-list';
|
|
26
|
+
|
|
27
|
+
defineButtonComponents();
|
|
28
|
+
defineTreeComponents();
|
|
29
|
+
defineVirtualListComponents();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Component JavaScript does not register custom elements during module evaluation, and every component subpath
|
|
33
|
+
publishes matching JavaScript and type declarations. Only `themes/*.css` is marked as a package side effect so
|
|
34
|
+
bundlers retain an imported skin while excluding component JavaScript that is not reachable from selected imports.
|
|
35
|
+
|
|
36
|
+
The root entry remains available when an application intentionally wants every component:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { defineHaiyueUI } from '@haiyue/ui';
|
|
40
|
+
|
|
41
|
+
defineHaiyueUI();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Because `defineHaiyueUI()` references the complete component set, prefer focused subpaths in bundle-sensitive
|
|
45
|
+
product entry points.
|
|
46
|
+
|
|
47
|
+
## Virtual list
|
|
48
|
+
|
|
49
|
+
`<hy-virtual-list>` renders only the visible fixed-height rows plus a configurable overscan buffer. The full
|
|
50
|
+
collection stays in the `items` property, while `renderItem` is called only for rows that currently need DOM:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { defineVirtualListComponents } from '@haiyue/ui/virtual-list';
|
|
54
|
+
|
|
55
|
+
defineVirtualListComponents();
|
|
56
|
+
const list = document.querySelector('hy-virtual-list');
|
|
57
|
+
list.items = records;
|
|
58
|
+
list.itemHeight = 40;
|
|
59
|
+
list.overscan = 3;
|
|
60
|
+
list.renderItem = (record, index) => `${index + 1}. ${record.name}`;
|
|
61
|
+
list.scrollToIndex(50_000, 'center');
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use the `height` property (or `height` attribute) for the internal viewport. `visible-range-change` reports
|
|
65
|
+
exclusive `endIndex` / `visibleEndIndex` values, and `item-click` reports the source item and absolute index.
|
|
66
|
+
The scrollbar can be themed with inherited semantic tokens and `--hy-virtual-list-scrollbar-size`.
|
|
67
|
+
|
|
68
|
+
## Drawer
|
|
69
|
+
|
|
70
|
+
Import and register the drawer independently when a product needs an off-canvas panel:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { defineDrawerComponents } from '@haiyue/ui/drawer';
|
|
74
|
+
|
|
75
|
+
defineDrawerComponents();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`<hy-drawer>` supports `top`, `right`, `bottom`, and `left` placement. The mask defaults to enabled; set
|
|
79
|
+
`mask="false"` or `drawer.mask = false` to keep the underlying page visible and interactive. Escape, the close
|
|
80
|
+
button, and mask clicks close the drawer and emit `drawer-close` with an `action`, `mask`, `escape`, or
|
|
81
|
+
`programmatic` reason. With `destroy-on-hidden`, slotted children are unmounted after the exit transition and
|
|
82
|
+
restored when the drawer opens again, allowing nested custom elements to run their disconnect/connect cleanup.
|
|
83
|
+
|
|
84
|
+
## Notification
|
|
85
|
+
|
|
86
|
+
`<hy-notification>` is an imperative notification center with isolated package entry `@haiyue/ui/notification`:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { defineNotificationComponents } from '@haiyue/ui/notification';
|
|
90
|
+
|
|
91
|
+
defineNotificationComponents();
|
|
92
|
+
const center = document.querySelector('hy-notification');
|
|
93
|
+
center.success({
|
|
94
|
+
message: 'Export complete',
|
|
95
|
+
description: 'The project package is ready.',
|
|
96
|
+
placement: 'topRight',
|
|
97
|
+
duration: 4,
|
|
98
|
+
showProgress: true,
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Types are `success`, `info`, `warning`, and `error`. Placements are `topLeft`, `top`, `topRight`, `bottomLeft`,
|
|
103
|
+
`bottom`, and `bottomRight`. Each notice may override the host defaults for placement, duration in seconds, and
|
|
104
|
+
progress visibility; duration `0` disables automatic closing. Notices at the same placement form a perspective
|
|
105
|
+
stack and expand on hover or keyboard focus. Use `close(key)` for one notice or `destroy()` for all notices.
|
|
106
|
+
|
|
107
|
+
## Border beam
|
|
108
|
+
|
|
109
|
+
`<hy-border-beam>` wraps arbitrary content with an animated SVG beam that stays aligned to the element's live
|
|
110
|
+
rounded border. Import it from `@haiyue/ui/border-beam` and register it explicitly:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { defineBorderBeamComponents } from '@haiyue/ui/border-beam';
|
|
114
|
+
|
|
115
|
+
defineBorderBeamComponents();
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Use `thickness` for stroke width, `color` for any valid CSS color, and `count` for 1–12 evenly phased beams.
|
|
119
|
+
`speed` is a multiplier: `1` completes the default loop in six seconds, while `2` runs twice as fast. The
|
|
120
|
+
component observes its own size and keeps the SVG view box and corner radius synchronized automatically.
|
|
121
|
+
Consumers may override `--hy-border-beam-radius` and `--hy-border-beam-track-color`.
|
|
122
|
+
|
|
123
|
+
## Range
|
|
124
|
+
|
|
125
|
+
Import the slider independently and opt into two-handle interval mode with the `range` attribute:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { defineRangeComponents } from '@haiyue/ui/range';
|
|
129
|
+
|
|
130
|
+
defineRangeComponents();
|
|
131
|
+
const interval = document.querySelector('hy-range[range]');
|
|
132
|
+
interval.values = [20, 80];
|
|
133
|
+
interval.addEventListener('value-change', event => console.log(event.detail.value));
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Single-value mode uses `value`; interval mode exposes `lowerValue`, `upperValue`, and `values`. Both modes honor
|
|
137
|
+
`min`, `max`, and `step`, and support pointer dragging, track clicks, arrows, Page Up/Down, Home, and End. Use
|
|
138
|
+
`--hy-range-handle-*`, `--hy-range-rail-*`, and `--hy-range-track-*` tokens for styling, or target the exported
|
|
139
|
+
`rail`, `track`, `handle`, `handle-lower`, and `handle-upper` shadow parts. `value-input` reports live movement and
|
|
140
|
+
`value-change` reports a committed pointer or keyboard change.
|
|
141
|
+
|
|
142
|
+
## Themes
|
|
143
|
+
|
|
144
|
+
Two optional CSS theme entries are provided. Import only the skin a product needs, or import both when the
|
|
145
|
+
product offers runtime switching:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import '@haiyue/ui/themes/light.css';
|
|
149
|
+
import '@haiyue/ui/themes/dark.css';
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Apply a theme to the document or to a component subtree. Tokens inherit through component shadow roots:
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
<html data-hy-theme="light">…</html>
|
|
156
|
+
<section data-hy-theme="dark">…</section>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- `light` / Haiyue Moonlight: moon white, pale ice blue, cyan and a soft violet highlight.
|
|
160
|
+
- `dark` / Haiyue Nightfall: deep navy, blue violet, cool white and a restrained gold highlight.
|
|
161
|
+
|
|
162
|
+
All theme tokens use the `--hy-` prefix and may be overridden by consuming products.
|
|
163
|
+
|
|
164
|
+
## Component examples
|
|
165
|
+
|
|
166
|
+
Run the interactive component gallery locally:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npm run examples
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Then open <http://localhost:4173/>. The gallery provides a left-side component index and live examples for
|
|
173
|
+
every exported component, including editable parameters and event output.
|
|
@@ -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,172 @@
|
|
|
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
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
77
|
+
this._resizeObserver = new ResizeObserver(() => this.refresh());
|
|
78
|
+
this._resizeObserver.observe(this);
|
|
79
|
+
}
|
|
80
|
+
this.refresh();
|
|
81
|
+
}
|
|
82
|
+
disconnectedCallback() {
|
|
83
|
+
if (!this._connected)
|
|
84
|
+
return;
|
|
85
|
+
this._connected = false;
|
|
86
|
+
this._resizeObserver?.disconnect();
|
|
87
|
+
this._resizeObserver = null;
|
|
88
|
+
}
|
|
89
|
+
attributeChangedCallback(name) {
|
|
90
|
+
if (name === 'count')
|
|
91
|
+
this._renderBeams();
|
|
92
|
+
this._updateAppearance();
|
|
93
|
+
this.refresh();
|
|
94
|
+
}
|
|
95
|
+
get thickness() {
|
|
96
|
+
return Math.min(20, Math.max(0.5, finiteNumber(this.getAttribute('thickness'), DEFAULT_THICKNESS)));
|
|
97
|
+
}
|
|
98
|
+
set thickness(value) {
|
|
99
|
+
this.setAttribute('thickness', String(Math.min(20, Math.max(0.5, finiteNumber(value, DEFAULT_THICKNESS)))));
|
|
100
|
+
}
|
|
101
|
+
/** Animation speed multiplier. 1 is normal speed and 2 is twice as fast. */
|
|
102
|
+
get speed() {
|
|
103
|
+
return Math.min(10, Math.max(0.1, finiteNumber(this.getAttribute('speed'), DEFAULT_SPEED)));
|
|
104
|
+
}
|
|
105
|
+
set speed(value) {
|
|
106
|
+
this.setAttribute('speed', String(Math.min(10, Math.max(0.1, finiteNumber(value, DEFAULT_SPEED)))));
|
|
107
|
+
}
|
|
108
|
+
get color() { return this.getAttribute('color') ?? ''; }
|
|
109
|
+
set color(value) {
|
|
110
|
+
const normalized = String(value).trim();
|
|
111
|
+
if (normalized)
|
|
112
|
+
this.setAttribute('color', normalized);
|
|
113
|
+
else
|
|
114
|
+
this.removeAttribute('color');
|
|
115
|
+
}
|
|
116
|
+
get count() {
|
|
117
|
+
return Math.min(12, Math.max(1, Math.floor(finiteNumber(this.getAttribute('count'), DEFAULT_COUNT))));
|
|
118
|
+
}
|
|
119
|
+
set count(value) {
|
|
120
|
+
this.setAttribute('count', String(Math.min(12, Math.max(1, Math.floor(finiteNumber(value, DEFAULT_COUNT))))));
|
|
121
|
+
}
|
|
122
|
+
refresh() {
|
|
123
|
+
const rect = this.getBoundingClientRect();
|
|
124
|
+
const width = Math.max(1, rect.width);
|
|
125
|
+
const height = Math.max(1, rect.height);
|
|
126
|
+
const inset = this.thickness / 2;
|
|
127
|
+
const radius = Math.min(Math.max(0, Number.parseFloat(getComputedStyle(this).borderTopLeftRadius) || 10), width / 2, height / 2);
|
|
128
|
+
this._svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
|
|
129
|
+
for (const element of [this._track, ...this._beamLayer.querySelectorAll('.beam')]) {
|
|
130
|
+
element.setAttribute('x', String(inset));
|
|
131
|
+
element.setAttribute('y', String(inset));
|
|
132
|
+
element.setAttribute('width', String(Math.max(0, width - this.thickness)));
|
|
133
|
+
element.setAttribute('height', String(Math.max(0, height - this.thickness)));
|
|
134
|
+
element.setAttribute('rx', String(Math.max(0, radius - inset)));
|
|
135
|
+
element.setAttribute('ry', String(Math.max(0, radius - inset)));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
_renderBeams() {
|
|
139
|
+
const beams = [];
|
|
140
|
+
for (let index = 0; index < this.count; index += 1) {
|
|
141
|
+
const beam = document.createElementNS(SVG_NAMESPACE, 'rect');
|
|
142
|
+
beam.classList.add('beam');
|
|
143
|
+
beam.part.add('beam');
|
|
144
|
+
beam.setAttribute('pathLength', '100');
|
|
145
|
+
beams.push(beam);
|
|
146
|
+
}
|
|
147
|
+
this._beamLayer.replaceChildren(...beams);
|
|
148
|
+
this._updateAppearance();
|
|
149
|
+
}
|
|
150
|
+
_updateAppearance() {
|
|
151
|
+
const duration = BASE_DURATION_SECONDS / this.speed;
|
|
152
|
+
const beamLength = Math.min(14, 60 / this.count);
|
|
153
|
+
this.style.setProperty('--_beam-glow', `${Math.max(3, this.thickness * 2.5)}px`);
|
|
154
|
+
if (this.color)
|
|
155
|
+
this.style.setProperty('--_beam-color', this.color);
|
|
156
|
+
else
|
|
157
|
+
this.style.removeProperty('--_beam-color');
|
|
158
|
+
this._track.setAttribute('stroke-width', String(Math.max(1, this.thickness * 0.5)));
|
|
159
|
+
const beams = [...this._beamLayer.querySelectorAll('.beam')];
|
|
160
|
+
beams.forEach((beam, index) => {
|
|
161
|
+
beam.setAttribute('stroke-width', String(this.thickness));
|
|
162
|
+
beam.setAttribute('stroke-dasharray', `${beamLength} ${100 - beamLength}`);
|
|
163
|
+
beam.style.animationDuration = `${duration}s`;
|
|
164
|
+
beam.style.animationDelay = `${-(duration * index) / Math.max(1, beams.length)}s`;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export function defineBorderBeamComponents() {
|
|
169
|
+
if (!customElements.get('hy-border-beam'))
|
|
170
|
+
customElements.define('hy-border-beam', HYBorderBeam);
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=border-beam.js.map
|
package/dist/button.d.ts
ADDED
|
@@ -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
|
package/dist/checkbox.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface HYCheckboxChangeDetail {
|
|
2
2
|
checked: boolean;
|
|
3
3
|
value: string;
|
|
4
4
|
}
|
|
5
|
-
export declare class
|
|
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
|
|
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(--
|
|
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(--
|
|
54
|
+
border: 1px solid var(--hy-checkbox-border-color, var(--hy-border-color, #303746));
|
|
55
55
|
border-radius: 3px;
|
|
56
|
-
background: var(--
|
|
57
|
-
color: var(--
|
|
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(--
|
|
62
|
-
box-shadow: 0 0 0 2px var(--
|
|
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(--
|
|
67
|
-
background: var(--
|
|
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('
|
|
201
|
-
customElements.define('
|
|
200
|
+
if (!customElements.get('hy-checkbox')) {
|
|
201
|
+
customElements.define('hy-checkbox', HYCheckbox);
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
//# sourceMappingURL=checkbox.js.map
|
package/dist/context-menu.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
export interface
|
|
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
|
|
7
|
+
export interface HYContextMenuSelectDetail {
|
|
8
8
|
value: string;
|
|
9
|
-
item:
|
|
9
|
+
item: HYContextMenuItem;
|
|
10
10
|
}
|
|
11
|
-
export declare class
|
|
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():
|
|
24
|
-
set items(value:
|
|
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;
|
package/dist/context-menu.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GE_MENU_ITEM_STYLES, createMenuItemButton, createMenuSeparator } from './menu-shared.js';
|
|
2
|
-
export class
|
|
2
|
+
export class HYContextMenu extends HTMLElement {
|
|
3
3
|
_root;
|
|
4
4
|
_style = document.createElement('style');
|
|
5
5
|
_menu = document.createElement('div');
|
|
@@ -18,7 +18,7 @@ export class GEContextMenu extends HTMLElement {
|
|
|
18
18
|
z-index: 10000;
|
|
19
19
|
display: none;
|
|
20
20
|
min-width: 132px;
|
|
21
|
-
color: var(--
|
|
21
|
+
color: var(--hy-text-color, #d8e2f2);
|
|
22
22
|
font: 12px system-ui, sans-serif;
|
|
23
23
|
}
|
|
24
24
|
:host([open]) {
|
|
@@ -28,10 +28,10 @@ export class GEContextMenu extends HTMLElement {
|
|
|
28
28
|
box-sizing: border-box;
|
|
29
29
|
min-width: 132px;
|
|
30
30
|
padding: 4px;
|
|
31
|
-
border: 1px solid var(--
|
|
31
|
+
border: 1px solid var(--hy-border-color, #303746);
|
|
32
32
|
border-radius: 4px;
|
|
33
|
-
background: var(--
|
|
34
|
-
box-shadow: 0 10px 26px var(--
|
|
33
|
+
background: var(--hy-menu-bg-color, var(--hy-surface-elevated-color, #1b2230));
|
|
34
|
+
box-shadow: 0 10px 26px var(--hy-menu-shadow-color, rgba(0, 0, 0, 0.36));
|
|
35
35
|
}
|
|
36
36
|
${GE_MENU_ITEM_STYLES}
|
|
37
37
|
`;
|
|
@@ -107,7 +107,7 @@ ${GE_MENU_ITEM_STYLES}
|
|
|
107
107
|
this._items = Array.isArray(parsed) ? parsed : [];
|
|
108
108
|
}
|
|
109
109
|
catch (error) {
|
|
110
|
-
console.warn('Invalid
|
|
110
|
+
console.warn('Invalid hy-context-menu items attribute.', error);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
_render() {
|
|
@@ -188,7 +188,9 @@ ${GE_MENU_ITEM_STYLES}
|
|
|
188
188
|
_nextEnabledIndex(direction) {
|
|
189
189
|
if (!this._items.length)
|
|
190
190
|
return -1;
|
|
191
|
-
|
|
191
|
+
let start = this._activeIndex;
|
|
192
|
+
if (start < 0)
|
|
193
|
+
start = direction > 0 ? -1 : this._items.length;
|
|
192
194
|
for (let step = 1; step <= this._items.length; step++) {
|
|
193
195
|
const index = (start + direction * step + this._items.length) % this._items.length;
|
|
194
196
|
if (this._isEnabledItem(index))
|
|
@@ -218,8 +220,8 @@ function getVisualViewportRect() {
|
|
|
218
220
|
: { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight };
|
|
219
221
|
}
|
|
220
222
|
export function defineContextMenuComponents() {
|
|
221
|
-
if (!customElements.get('
|
|
222
|
-
customElements.define('
|
|
223
|
+
if (!customElements.get('hy-context-menu')) {
|
|
224
|
+
customElements.define('hy-context-menu', HYContextMenu);
|
|
223
225
|
}
|
|
224
226
|
}
|
|
225
227
|
//# sourceMappingURL=context-menu.js.map
|
package/dist/dialog.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export type
|
|
2
|
-
export interface
|
|
3
|
-
readonly reason:
|
|
1
|
+
export type HYDialogCloseReason = 'action' | 'backdrop' | 'escape' | 'programmatic';
|
|
2
|
+
export interface HYDialogCloseDetail {
|
|
3
|
+
readonly reason: HYDialogCloseReason | string;
|
|
4
4
|
}
|
|
5
5
|
/** Accessible modal surface with slotted body/footer content and reflected open state. */
|
|
6
|
-
export declare class
|
|
6
|
+
export declare class HYDialog extends HTMLElement {
|
|
7
7
|
private readonly _dialog;
|
|
8
8
|
private readonly _title;
|
|
9
9
|
private readonly _closeButton;
|
|
@@ -20,7 +20,7 @@ export declare class GEDialog extends HTMLElement {
|
|
|
20
20
|
get heading(): string;
|
|
21
21
|
set heading(value: string);
|
|
22
22
|
showModal(): void;
|
|
23
|
-
close(reason?:
|
|
23
|
+
close(reason?: HYDialogCloseDetail['reason']): void;
|
|
24
24
|
private _syncHeading;
|
|
25
25
|
private _syncOpen;
|
|
26
26
|
private _onCancel;
|