@haiyue/ui 0.1.2 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/LICENSE +27 -0
  3. package/README.md +643 -2
  4. package/dist/adapters/web/controls.d.ts +19 -0
  5. package/dist/adapters/web/controls.js +307 -0
  6. package/dist/adapters/web/dom.d.ts +10 -0
  7. package/dist/adapters/web/dom.js +98 -0
  8. package/dist/adapters/web/index.d.ts +3 -0
  9. package/dist/adapters/web/index.js +3 -0
  10. package/dist/adapters/web/renderer.d.ts +18 -0
  11. package/dist/adapters/web/renderer.js +553 -0
  12. package/dist/adapters/web/styles.d.ts +3 -0
  13. package/dist/adapters/web/styles.js +83 -0
  14. package/dist/adapters/web/types.d.ts +46 -0
  15. package/dist/adapters/web/types.js +2 -0
  16. package/dist/affix.d.ts +35 -0
  17. package/dist/affix.js +137 -0
  18. package/dist/badge.d.ts +45 -0
  19. package/dist/badge.js +168 -0
  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/carousel.d.ts +92 -0
  27. package/dist/carousel.js +509 -0
  28. package/dist/cascader.d.ts +65 -0
  29. package/dist/cascader.js +479 -0
  30. package/dist/collapse.d.ts +68 -0
  31. package/dist/collapse.js +348 -0
  32. package/dist/core/actions.d.ts +32 -0
  33. package/dist/core/actions.js +145 -0
  34. package/dist/core/data.d.ts +4 -0
  35. package/dist/core/data.js +27 -0
  36. package/dist/core/index.d.ts +7 -0
  37. package/dist/core/index.js +6 -0
  38. package/dist/core/platform-view.d.ts +17 -0
  39. package/dist/core/platform-view.js +84 -0
  40. package/dist/core/selection.d.ts +14 -0
  41. package/dist/core/selection.js +93 -0
  42. package/dist/core/text.d.ts +31 -0
  43. package/dist/core/text.js +154 -0
  44. package/dist/core/types.d.ts +269 -0
  45. package/dist/core/types.js +2 -0
  46. package/dist/core/validation.d.ts +24 -0
  47. package/dist/core/validation.js +243 -0
  48. package/dist/date-picker.d.ts +60 -0
  49. package/dist/date-picker.js +355 -0
  50. package/dist/dialog.d.ts +4 -0
  51. package/dist/dialog.js +23 -5
  52. package/dist/divider.d.ts +33 -0
  53. package/dist/divider.js +113 -0
  54. package/dist/draggable.d.ts +78 -0
  55. package/dist/draggable.js +255 -0
  56. package/dist/index.d.ts +53 -1
  57. package/dist/index.js +78 -0
  58. package/dist/input.d.ts +30 -1
  59. package/dist/input.js +166 -54
  60. package/dist/internal/qrcodegen.d.ts +94 -0
  61. package/dist/internal/qrcodegen.js +856 -0
  62. package/dist/masonry.d.ts +36 -0
  63. package/dist/masonry.js +171 -0
  64. package/dist/modal.d.ts +73 -0
  65. package/dist/modal.js +361 -0
  66. package/dist/portable/icons.d.ts +5 -0
  67. package/dist/portable/icons.js +24 -0
  68. package/dist/portable/index.d.ts +7 -0
  69. package/dist/portable/index.js +5 -0
  70. package/dist/portable/page.d.ts +38 -0
  71. package/dist/portable/page.js +361 -0
  72. package/dist/portable/responsive.d.ts +20 -0
  73. package/dist/portable/responsive.js +15 -0
  74. package/dist/portable/theme.d.ts +24 -0
  75. package/dist/portable/theme.js +60 -0
  76. package/dist/portable/tree.d.ts +10 -0
  77. package/dist/portable/tree.js +96 -0
  78. package/dist/qr-code.d.ts +62 -0
  79. package/dist/qr-code.js +224 -0
  80. package/dist/range.d.ts +3 -0
  81. package/dist/range.js +16 -5
  82. package/dist/rate.d.ts +46 -0
  83. package/dist/rate.js +193 -0
  84. package/dist/resizable.d.ts +97 -0
  85. package/dist/resizable.js +426 -0
  86. package/dist/skeleton.d.ts +41 -0
  87. package/dist/skeleton.js +141 -0
  88. package/dist/steps.d.ts +45 -0
  89. package/dist/steps.js +203 -0
  90. package/dist/table.d.ts +106 -0
  91. package/dist/table.js +689 -0
  92. package/dist/tabs.d.ts +3 -0
  93. package/dist/tabs.js +11 -1
  94. package/dist/tag.d.ts +55 -0
  95. package/dist/tag.js +225 -0
  96. package/dist/time-picker.d.ts +62 -0
  97. package/dist/time-picker.js +298 -0
  98. package/dist/timeline.d.ts +48 -0
  99. package/dist/timeline.js +201 -0
  100. package/dist/toast.d.ts +55 -0
  101. package/dist/toast.js +224 -0
  102. package/dist/tour.d.ts +89 -0
  103. package/dist/tour.js +306 -0
  104. package/dist/virtual-keyboard.d.ts +95 -0
  105. package/dist/virtual-keyboard.js +432 -0
  106. package/dist/virtual-mouse.d.ts +82 -0
  107. package/dist/virtual-mouse.js +358 -0
  108. package/dist/watermark.d.ts +44 -0
  109. package/dist/watermark.js +148 -0
  110. package/package.json +132 -4
package/dist/dialog.d.ts CHANGED
@@ -17,6 +17,9 @@ export declare class HYDialog extends HTMLElement {
17
17
  set open(value: boolean);
18
18
  get dismissible(): boolean;
19
19
  set dismissible(value: boolean);
20
+ /** Opt-in: user dismissal requests leave open controlled by the application. */
21
+ get controlled(): boolean;
22
+ set controlled(value: boolean);
20
23
  get heading(): string;
21
24
  set heading(value: string);
22
25
  showModal(): void;
@@ -25,6 +28,7 @@ export declare class HYDialog extends HTMLElement {
25
28
  private _syncOpen;
26
29
  private _onCancel;
27
30
  private _onDialogClick;
31
+ private _requestClose;
28
32
  private _onNativeClose;
29
33
  }
30
34
  export declare function defineDialogComponents(): void;
package/dist/dialog.js CHANGED
@@ -6,7 +6,7 @@ export class HYDialog extends HTMLElement {
6
6
  _returnFocus = null;
7
7
  _closeReason = 'programmatic';
8
8
  static get observedAttributes() {
9
- return ['open', 'heading', 'dismissible'];
9
+ return ['open', 'heading', 'dismissible', 'controlled'];
10
10
  }
11
11
  constructor() {
12
12
  super();
@@ -100,7 +100,7 @@ export class HYDialog extends HTMLElement {
100
100
  this._dialog.setAttribute('aria-labelledby', this._title.id);
101
101
  this._dialog.append(header, body, footer);
102
102
  root.append(style, this._dialog);
103
- this._closeButton.addEventListener('click', () => this.close('action'));
103
+ this._closeButton.addEventListener('click', () => this.controlled ? this._requestClose('action') : this.close('action'));
104
104
  this._dialog.addEventListener('cancel', this._onCancel);
105
105
  this._dialog.addEventListener('click', this._onDialogClick);
106
106
  this._dialog.addEventListener('close', this._onNativeClose);
@@ -119,6 +119,9 @@ export class HYDialog extends HTMLElement {
119
119
  set open(value) { this.toggleAttribute('open', value); }
120
120
  get dismissible() { return this.hasAttribute('dismissible'); }
121
121
  set dismissible(value) { this.toggleAttribute('dismissible', value); }
122
+ /** Opt-in: user dismissal requests leave open controlled by the application. */
123
+ get controlled() { return this.hasAttribute('controlled'); }
124
+ set controlled(value) { this.toggleAttribute('controlled', value); }
122
125
  get heading() { return this.getAttribute('heading') ?? ''; }
123
126
  set heading(value) { this.setAttribute('heading', value); }
124
127
  showModal() {
@@ -150,6 +153,10 @@ export class HYDialog extends HTMLElement {
150
153
  event.preventDefault();
151
154
  if (!this.dismissible)
152
155
  return;
156
+ if (this.controlled) {
157
+ this._requestClose('escape');
158
+ return;
159
+ }
153
160
  const forwarded = new CustomEvent('dialog-cancel', { bubbles: true, composed: true, cancelable: true });
154
161
  if (this.dispatchEvent(forwarded))
155
162
  this.close('escape');
@@ -160,10 +167,20 @@ export class HYDialog extends HTMLElement {
160
167
  const rect = this._dialog.getBoundingClientRect();
161
168
  const inside = event.clientX >= rect.left && event.clientX <= rect.right
162
169
  && event.clientY >= rect.top && event.clientY <= rect.bottom;
163
- if (!inside)
164
- this.close('backdrop');
170
+ if (!inside) {
171
+ if (this.controlled)
172
+ this._requestClose('backdrop');
173
+ else
174
+ this.close('backdrop');
175
+ }
165
176
  };
177
+ _requestClose(reason) {
178
+ this.dispatchEvent(new CustomEvent('dialog-cancel', { detail: { reason }, bubbles: true, composed: true, cancelable: true }));
179
+ }
166
180
  _onNativeClose = () => {
181
+ // Native close is queued. A subsequent showModal must not be undone by that old event.
182
+ if (this._dialog.open)
183
+ return;
167
184
  this.removeAttribute('open');
168
185
  const reason = this._closeReason;
169
186
  this._closeReason = 'programmatic';
@@ -172,7 +189,8 @@ export class HYDialog extends HTMLElement {
172
189
  bubbles: true,
173
190
  composed: true,
174
191
  }));
175
- this._returnFocus?.focus();
192
+ if (!this.controlled)
193
+ this._returnFocus?.focus();
176
194
  this._returnFocus = null;
177
195
  };
178
196
  }
@@ -0,0 +1,33 @@
1
+ export type HYDividerOrientation = 'horizontal' | 'vertical';
2
+ export type HYDividerLineStyle = 'solid' | 'dashed' | 'dotted' | 'double';
3
+ export type HYDividerTextPlacement = 'start' | 'center' | 'end';
4
+ /** Theme-aware horizontal or vertical separator with optional slotted text. */
5
+ export declare class HYDivider extends HTMLElement {
6
+ private readonly _divider;
7
+ private readonly _before;
8
+ private readonly _label;
9
+ private readonly _slot;
10
+ private readonly _fallback;
11
+ private readonly _after;
12
+ static get observedAttributes(): string[];
13
+ constructor();
14
+ connectedCallback(): void;
15
+ attributeChangedCallback(): void;
16
+ get orientation(): HYDividerOrientation;
17
+ set orientation(value: HYDividerOrientation);
18
+ get lineStyle(): HYDividerLineStyle;
19
+ set lineStyle(value: HYDividerLineStyle);
20
+ get textPlacement(): HYDividerTextPlacement;
21
+ set textPlacement(value: HYDividerTextPlacement);
22
+ get label(): string;
23
+ set label(value: string);
24
+ get thickness(): number;
25
+ set thickness(value: number);
26
+ get color(): string;
27
+ set color(value: string);
28
+ get spacing(): number;
29
+ set spacing(value: number);
30
+ private _render;
31
+ private _syncLabel;
32
+ }
33
+ export declare function defineDividerComponents(): void;
@@ -0,0 +1,113 @@
1
+ function boundedNumber(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
+ /** Theme-aware horizontal or vertical separator with optional slotted text. */
8
+ export class HYDivider extends HTMLElement {
9
+ _divider = document.createElement('div');
10
+ _before = document.createElement('span');
11
+ _label = document.createElement('span');
12
+ _slot = document.createElement('slot');
13
+ _fallback = document.createElement('span');
14
+ _after = document.createElement('span');
15
+ static get observedAttributes() {
16
+ return ['orientation', 'line-style', 'text-placement', 'label', 'thickness', 'color', 'spacing', 'aria-label'];
17
+ }
18
+ constructor() {
19
+ super();
20
+ const root = this.attachShadow({ mode: 'open' });
21
+ const style = document.createElement('style');
22
+ style.textContent = `
23
+ :host { display: block; min-width: 0; color: var(--hy-secondary-text-color, #aebbd0); font: 12px/1.4 system-ui, sans-serif; }
24
+ .divider { display: flex; width: 100%; align-items: center; gap: var(--_spacing, var(--hy-divider-text-gap, 12px)); box-sizing: border-box; }
25
+ .line { flex: 1 1 auto; min-width: 0; height: 0; border-top: var(--_thickness, 1px) var(--_line-style, solid) var(--_color, var(--hy-divider-color, var(--hy-border-color, #3d4654))); }
26
+ .label { flex: 0 0 auto; max-width: min(70%, 64ch); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
27
+ .label[hidden] { display: none; }
28
+ :host([text-placement="start"]) .before { flex: 0 0 var(--hy-divider-edge-length, 24px); }
29
+ :host([text-placement="end"]) .after { flex: 0 0 var(--hy-divider-edge-length, 24px); }
30
+ :host([orientation="vertical"]) { display: inline-block; width: max-content; height: var(--hy-divider-vertical-height, 1.4em); vertical-align: middle; }
31
+ :host([orientation="vertical"]) .divider { width: auto; height: 100%; flex-direction: column; }
32
+ :host([orientation="vertical"]) .line { width: 0; height: auto; min-height: 0; border-top: 0; border-left: var(--_thickness, 1px) var(--_line-style, solid) var(--_color, var(--hy-divider-color, var(--hy-border-color, #3d4654))); }
33
+ :host([orientation="vertical"]) .label { max-width: none; max-height: 70%; writing-mode: vertical-rl; }
34
+ :host([orientation="vertical"][text-placement="start"]) .before { flex-basis: var(--hy-divider-edge-length, 10px); }
35
+ :host([orientation="vertical"][text-placement="end"]) .after { flex-basis: var(--hy-divider-edge-length, 10px); }
36
+ `;
37
+ this._divider.className = 'divider';
38
+ this._divider.part.add('divider');
39
+ this._divider.setAttribute('role', 'separator');
40
+ this._before.className = 'line before';
41
+ this._before.part.add('line', 'line-before');
42
+ this._before.setAttribute('aria-hidden', 'true');
43
+ this._label.className = 'label';
44
+ this._label.part.add('text');
45
+ this._fallback.className = 'fallback';
46
+ this._fallback.part.add('fallback');
47
+ this._slot.append(this._fallback);
48
+ this._label.append(this._slot);
49
+ this._after.className = 'line after';
50
+ this._after.part.add('line', 'line-after');
51
+ this._after.setAttribute('aria-hidden', 'true');
52
+ this._divider.append(this._before, this._label, this._after);
53
+ root.append(style, this._divider);
54
+ this._slot.addEventListener('slotchange', () => this._syncLabel());
55
+ }
56
+ connectedCallback() { this._render(); }
57
+ attributeChangedCallback() { this._render(); }
58
+ get orientation() { return this.getAttribute('orientation') === 'vertical' ? 'vertical' : 'horizontal'; }
59
+ set orientation(value) { this.setAttribute('orientation', value === 'vertical' ? 'vertical' : 'horizontal'); }
60
+ get lineStyle() {
61
+ const value = this.getAttribute('line-style');
62
+ return value === 'dashed' || value === 'dotted' || value === 'double' ? value : 'solid';
63
+ }
64
+ set lineStyle(value) { this.setAttribute('line-style', value === 'dashed' || value === 'dotted' || value === 'double' ? value : 'solid'); }
65
+ get textPlacement() {
66
+ const value = this.getAttribute('text-placement');
67
+ return value === 'start' || value === 'end' ? value : 'center';
68
+ }
69
+ set textPlacement(value) { this.setAttribute('text-placement', value === 'start' || value === 'end' ? value : 'center'); }
70
+ get label() { return this.getAttribute('label') ?? ''; }
71
+ set label(value) { const normalized = String(value); if (normalized)
72
+ this.setAttribute('label', normalized);
73
+ else
74
+ this.removeAttribute('label'); }
75
+ get thickness() { return boundedNumber(this.getAttribute('thickness'), 1, 1, 12); }
76
+ set thickness(value) { this.setAttribute('thickness', String(boundedNumber(value, 1, 1, 12))); }
77
+ get color() { return this.getAttribute('color') ?? ''; }
78
+ set color(value) { const normalized = String(value).trim(); if (normalized)
79
+ this.setAttribute('color', normalized);
80
+ else
81
+ this.removeAttribute('color'); }
82
+ get spacing() { return boundedNumber(this.getAttribute('spacing'), 12, 0, 80); }
83
+ set spacing(value) { this.setAttribute('spacing', String(boundedNumber(value, 12, 0, 80))); }
84
+ _render() {
85
+ if (!this.isConnected)
86
+ return;
87
+ this._divider.setAttribute('aria-orientation', this.orientation);
88
+ const name = this.getAttribute('aria-label');
89
+ if (name)
90
+ this._divider.setAttribute('aria-label', name);
91
+ else
92
+ this._divider.removeAttribute('aria-label');
93
+ const effectiveThickness = this.lineStyle === 'double' ? Math.max(3, this.thickness) : this.thickness;
94
+ this.style.setProperty('--_line-style', this.lineStyle);
95
+ this.style.setProperty('--_thickness', `${effectiveThickness}px`);
96
+ this.style.setProperty('--_spacing', `${this.spacing}px`);
97
+ if (this.color)
98
+ this.style.setProperty('--_color', this.color);
99
+ else
100
+ this.style.removeProperty('--_color');
101
+ this._fallback.textContent = this.label;
102
+ this._syncLabel();
103
+ }
104
+ _syncLabel() {
105
+ const hasSlottedText = this._slot.assignedNodes().some(node => node.nodeType === Node.ELEMENT_NODE || Boolean(node.textContent?.trim()));
106
+ this._label.hidden = !hasSlottedText && !this.label;
107
+ }
108
+ }
109
+ export function defineDividerComponents() {
110
+ if (!customElements.get('hy-divider'))
111
+ customElements.define('hy-divider', HYDivider);
112
+ }
113
+ //# sourceMappingURL=divider.js.map
@@ -0,0 +1,78 @@
1
+ export type HYDraggableAxis = 'both' | 'x' | 'y';
2
+ export type HYDraggableBoundsMode = 'none' | 'parent' | 'viewport';
3
+ export type HYDraggableBounds = HYDraggableBoundsMode | HTMLElement;
4
+ export type HYDraggableMoveSource = 'pointer' | 'keyboard' | 'api';
5
+ export interface HYDraggablePosition {
6
+ readonly x: number;
7
+ readonly y: number;
8
+ }
9
+ export interface HYDraggableMoveDetail extends HYDraggablePosition {
10
+ readonly deltaX: number;
11
+ readonly deltaY: number;
12
+ readonly source: HYDraggableMoveSource;
13
+ }
14
+ export interface HYDraggableStartDetail extends HYDraggablePosition {
15
+ readonly pointerId: number;
16
+ }
17
+ export interface HYDraggableEndDetail extends HYDraggableMoveDetail {
18
+ readonly cancelled: boolean;
19
+ }
20
+ /** Transform-based draggable container with optional handles, constraints, snapping, and keyboard movement. */
21
+ export declare class HYDraggable extends HTMLElement {
22
+ private readonly _surface;
23
+ private _x;
24
+ private _y;
25
+ private _reflecting;
26
+ private _boundsTarget;
27
+ private _dragAbort;
28
+ private _pointerId;
29
+ private _originX;
30
+ private _originY;
31
+ private _pointerX;
32
+ private _pointerY;
33
+ static get observedAttributes(): string[];
34
+ constructor();
35
+ connectedCallback(): void;
36
+ disconnectedCallback(): void;
37
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
38
+ get x(): number;
39
+ set x(value: number);
40
+ get y(): number;
41
+ set y(value: number);
42
+ get position(): HYDraggablePosition;
43
+ set position(value: HYDraggablePosition);
44
+ get axis(): HYDraggableAxis;
45
+ set axis(value: HYDraggableAxis);
46
+ get bounds(): HYDraggableBounds;
47
+ set bounds(value: HYDraggableBounds);
48
+ get boundsMode(): HYDraggableBoundsMode;
49
+ get grid(): number;
50
+ set grid(value: number);
51
+ get handle(): string;
52
+ set handle(value: string);
53
+ get cancel(): string;
54
+ set cancel(value: string);
55
+ get disabled(): boolean;
56
+ set disabled(value: boolean);
57
+ get keyboard(): boolean;
58
+ set keyboard(value: boolean);
59
+ get dragging(): boolean;
60
+ moveTo(x: number, y: number): HYDraggablePosition;
61
+ moveBy(deltaX: number, deltaY: number): HYDraggablePosition;
62
+ reset(): HYDraggablePosition;
63
+ refresh(): HYDraggablePosition;
64
+ private _startPointerDrag;
65
+ private _handlePointerMove;
66
+ private _finishPointerDrag;
67
+ private _completePointerDrag;
68
+ private _cancelDrag;
69
+ private _handleKeydown;
70
+ private _allowsTarget;
71
+ private _moveTo;
72
+ private _constrain;
73
+ private _setPosition;
74
+ private _syncPosition;
75
+ private _reflectPosition;
76
+ private _syncAccessibility;
77
+ }
78
+ export declare function defineDraggableComponents(): void;
@@ -0,0 +1,255 @@
1
+ const DEFAULT_CANCEL = 'input, textarea, select, button, a, [contenteditable="true"], [data-no-drag]';
2
+ function finiteNumber(value, fallback = 0) {
3
+ const number = Number(value);
4
+ return Number.isFinite(number) ? number : fallback;
5
+ }
6
+ function safeMatches(element, selector) {
7
+ try {
8
+ return element.matches(selector);
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ /** Transform-based draggable container with optional handles, constraints, snapping, and keyboard movement. */
15
+ export class HYDraggable extends HTMLElement {
16
+ _surface = document.createElement('div');
17
+ _x = 0;
18
+ _y = 0;
19
+ _reflecting = false;
20
+ _boundsTarget = null;
21
+ _dragAbort = null;
22
+ _pointerId = null;
23
+ _originX = 0;
24
+ _originY = 0;
25
+ _pointerX = 0;
26
+ _pointerY = 0;
27
+ static get observedAttributes() {
28
+ return ['x', 'y', 'axis', 'bounds', 'grid', 'handle', 'cancel', 'disabled', 'keyboard', 'aria-label'];
29
+ }
30
+ constructor() {
31
+ super();
32
+ const root = this.attachShadow({ mode: 'open' });
33
+ const style = document.createElement('style');
34
+ const slot = document.createElement('slot');
35
+ style.textContent = `
36
+ :host { display: inline-block; max-width: 100%; min-width: 0; translate: var(--_hy-draggable-x, 0px) var(--_hy-draggable-y, 0px); will-change: translate; }
37
+ .surface { min-width: 0; outline: none; cursor: var(--hy-draggable-cursor, grab); touch-action: var(--hy-draggable-touch-action, none); user-select: none; }
38
+ :host([dragging]) { z-index: var(--hy-draggable-active-z-index, 10); }
39
+ :host([dragging]) .surface { cursor: var(--hy-draggable-active-cursor, grabbing); }
40
+ :host([disabled]) .surface { cursor: default; }
41
+ .surface:focus-visible { outline: 2px solid var(--hy-focus-border-color, #79a8ff); outline-offset: var(--hy-draggable-focus-offset, 2px); border-radius: var(--hy-draggable-focus-radius, 6px); }
42
+ ::slotted([data-drag-handle]) { cursor: var(--hy-draggable-cursor, grab); }
43
+ @media (prefers-reduced-motion: reduce) { :host { scroll-behavior: auto; } }
44
+ `;
45
+ this._surface.className = 'surface';
46
+ this._surface.part.add('surface');
47
+ this._surface.setAttribute('role', 'group');
48
+ this._surface.append(slot);
49
+ root.append(style, this._surface);
50
+ this._surface.addEventListener('pointerdown', event => this._startPointerDrag(event));
51
+ this._surface.addEventListener('keydown', event => this._handleKeydown(event));
52
+ }
53
+ connectedCallback() {
54
+ this._x = finiteNumber(this.getAttribute('x'), this._x);
55
+ this._y = finiteNumber(this.getAttribute('y'), this._y);
56
+ this._syncPosition();
57
+ this._syncAccessibility();
58
+ }
59
+ disconnectedCallback() { this._cancelDrag(true); }
60
+ attributeChangedCallback(name, oldValue, newValue) {
61
+ if (oldValue === newValue)
62
+ return;
63
+ if ((name === 'x' || name === 'y') && !this._reflecting) {
64
+ this._x = finiteNumber(this.getAttribute('x'), this._x);
65
+ this._y = finiteNumber(this.getAttribute('y'), this._y);
66
+ this._syncPosition();
67
+ }
68
+ else if (name === 'disabled' && this.disabled)
69
+ this._cancelDrag(true);
70
+ if (name === 'keyboard' || name === 'disabled' || name === 'aria-label')
71
+ this._syncAccessibility();
72
+ }
73
+ get x() { return this._x; }
74
+ set x(value) { this._setPosition(finiteNumber(value, this._x), this._y); }
75
+ get y() { return this._y; }
76
+ set y(value) { this._setPosition(this._x, finiteNumber(value, this._y)); }
77
+ get position() { return { x: this._x, y: this._y }; }
78
+ set position(value) { this._setPosition(finiteNumber(value?.x, this._x), finiteNumber(value?.y, this._y)); }
79
+ get axis() { const value = this.getAttribute('axis'); return value === 'x' || value === 'y' ? value : 'both'; }
80
+ set axis(value) { this.setAttribute('axis', value === 'x' || value === 'y' ? value : 'both'); }
81
+ get bounds() { return this._boundsTarget ?? this.boundsMode; }
82
+ set bounds(value) {
83
+ if (value instanceof HTMLElement) {
84
+ this._boundsTarget = value;
85
+ this.setAttribute('bounds', 'parent');
86
+ }
87
+ else {
88
+ this._boundsTarget = null;
89
+ this.setAttribute('bounds', value === 'parent' || value === 'viewport' ? value : 'none');
90
+ }
91
+ this.refresh();
92
+ }
93
+ get boundsMode() { const value = this.getAttribute('bounds'); return value === 'parent' || value === 'viewport' ? value : 'none'; }
94
+ get grid() { return Math.max(1, Math.min(1000, finiteNumber(this.getAttribute('grid'), 1))); }
95
+ set grid(value) { this.setAttribute('grid', String(Math.max(1, Math.min(1000, finiteNumber(value, 1))))); }
96
+ get handle() { return this.getAttribute('handle')?.trim() ?? ''; }
97
+ set handle(value) { const normalized = String(value).trim(); if (normalized)
98
+ this.setAttribute('handle', normalized);
99
+ else
100
+ this.removeAttribute('handle'); }
101
+ get cancel() { return this.getAttribute('cancel')?.trim() || DEFAULT_CANCEL; }
102
+ set cancel(value) { const normalized = String(value).trim(); if (normalized)
103
+ this.setAttribute('cancel', normalized);
104
+ else
105
+ this.removeAttribute('cancel'); }
106
+ get disabled() { return this.hasAttribute('disabled'); }
107
+ set disabled(value) { this.toggleAttribute('disabled', Boolean(value)); }
108
+ get keyboard() { return this.getAttribute('keyboard') !== 'false'; }
109
+ set keyboard(value) { this.setAttribute('keyboard', String(Boolean(value))); }
110
+ get dragging() { return this._pointerId != null; }
111
+ moveTo(x, y) { return this._moveTo(x, y, 'api'); }
112
+ moveBy(deltaX, deltaY) { return this._moveTo(this._x + finiteNumber(deltaX), this._y + finiteNumber(deltaY), 'api'); }
113
+ reset() { return this._moveTo(0, 0, 'api'); }
114
+ refresh() { const constrained = this._constrain(this._x, this._y); this._setPosition(constrained.x, constrained.y); return this.position; }
115
+ _startPointerDrag(event) {
116
+ if (this.disabled || event.button !== 0 || this._pointerId != null || !this._allowsTarget(event))
117
+ return;
118
+ const start = new CustomEvent('drag-start', { bubbles: true, composed: true, cancelable: true, detail: { x: this._x, y: this._y, pointerId: event.pointerId } });
119
+ if (!this.dispatchEvent(start))
120
+ return;
121
+ event.preventDefault();
122
+ this._surface.focus({ preventScroll: true });
123
+ this._surface.setPointerCapture(event.pointerId);
124
+ this._pointerId = event.pointerId;
125
+ this._originX = this._x;
126
+ this._originY = this._y;
127
+ this._pointerX = event.clientX;
128
+ this._pointerY = event.clientY;
129
+ this.toggleAttribute('dragging', true);
130
+ const abort = new AbortController();
131
+ this._dragAbort = abort;
132
+ this._surface.addEventListener('pointermove', move => this._handlePointerMove(move), { signal: abort.signal });
133
+ this._surface.addEventListener('pointerup', up => this._finishPointerDrag(up, false), { signal: abort.signal });
134
+ this._surface.addEventListener('pointercancel', cancel => this._finishPointerDrag(cancel, true), { signal: abort.signal });
135
+ this._surface.addEventListener('lostpointercapture', () => this._cancelDrag(true), { signal: abort.signal });
136
+ }
137
+ _handlePointerMove(event) {
138
+ if (event.pointerId !== this._pointerId)
139
+ return;
140
+ event.preventDefault();
141
+ const x = this.axis === 'y' ? this._originX : this._originX + event.clientX - this._pointerX;
142
+ const y = this.axis === 'x' ? this._originY : this._originY + event.clientY - this._pointerY;
143
+ this._moveTo(x, y, 'pointer');
144
+ }
145
+ _finishPointerDrag(event, cancelled) {
146
+ this._completePointerDrag(event.pointerId, cancelled);
147
+ }
148
+ _completePointerDrag(pointerId, cancelled) {
149
+ if (pointerId !== this._pointerId)
150
+ return;
151
+ const originX = this._originX;
152
+ const originY = this._originY;
153
+ this._dragAbort?.abort();
154
+ this._dragAbort = null;
155
+ this._pointerId = null;
156
+ this.toggleAttribute('dragging', false);
157
+ if (this._surface.hasPointerCapture(pointerId))
158
+ this._surface.releasePointerCapture(pointerId);
159
+ this.dispatchEvent(new CustomEvent('drag-end', { bubbles: true, composed: true, detail: { x: this._x, y: this._y, deltaX: this._x - originX, deltaY: this._y - originY, source: 'pointer', cancelled } }));
160
+ }
161
+ _cancelDrag(cancelled) {
162
+ if (this._pointerId == null)
163
+ return;
164
+ this._completePointerDrag(this._pointerId, cancelled);
165
+ }
166
+ _handleKeydown(event) {
167
+ if (this.disabled || !this.keyboard)
168
+ return;
169
+ const multiplier = event.shiftKey ? 10 : 1;
170
+ const step = this.grid * multiplier;
171
+ let x = this._x;
172
+ let y = this._y;
173
+ if (event.key === 'ArrowLeft' && this.axis !== 'y')
174
+ x -= step;
175
+ else if (event.key === 'ArrowRight' && this.axis !== 'y')
176
+ x += step;
177
+ else if (event.key === 'ArrowUp' && this.axis !== 'x')
178
+ y -= step;
179
+ else if (event.key === 'ArrowDown' && this.axis !== 'x')
180
+ y += step;
181
+ else if (event.key === 'Home') {
182
+ x = 0;
183
+ y = 0;
184
+ }
185
+ else
186
+ return;
187
+ event.preventDefault();
188
+ this._moveTo(x, y, 'keyboard');
189
+ }
190
+ _allowsTarget(event) {
191
+ const elements = event.composedPath().filter((node) => node instanceof Element && node !== this._surface && node !== this);
192
+ if (elements.some(element => safeMatches(element, this.cancel)))
193
+ return false;
194
+ if (!this.handle)
195
+ return true;
196
+ return elements.some(element => safeMatches(element, this.handle));
197
+ }
198
+ _moveTo(x, y, source) {
199
+ const oldX = this._x;
200
+ const oldY = this._y;
201
+ const restrictAxis = source !== 'api';
202
+ const snappedX = restrictAxis && this.axis === 'y' ? oldX : Math.round(finiteNumber(x, oldX) / this.grid) * this.grid;
203
+ const snappedY = restrictAxis && this.axis === 'x' ? oldY : Math.round(finiteNumber(y, oldY) / this.grid) * this.grid;
204
+ const next = this._constrain(snappedX, snappedY);
205
+ if (next.x === oldX && next.y === oldY)
206
+ return this.position;
207
+ this._setPosition(next.x, next.y);
208
+ this.dispatchEvent(new CustomEvent('drag-move', { bubbles: true, composed: true, detail: { x: next.x, y: next.y, deltaX: next.x - oldX, deltaY: next.y - oldY, source } }));
209
+ return this.position;
210
+ }
211
+ _constrain(x, y) {
212
+ const target = this._boundsTarget ?? (this.boundsMode === 'parent' ? this.parentElement : null);
213
+ const targetRect = target?.getBoundingClientRect();
214
+ const bounds = target && targetRect ? { left: targetRect.left + target.clientLeft, top: targetRect.top + target.clientTop, right: targetRect.left + target.clientLeft + target.clientWidth, bottom: targetRect.top + target.clientTop + target.clientHeight } : this.boundsMode === 'viewport' ? { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight } : null;
215
+ if (!bounds || !this.isConnected)
216
+ return { x, y };
217
+ const rect = this.getBoundingClientRect();
218
+ const baseLeft = rect.left - this._x;
219
+ const baseTop = rect.top - this._y;
220
+ const width = rect.width;
221
+ const height = rect.height;
222
+ const minX = bounds.left - baseLeft;
223
+ const maxX = bounds.right - baseLeft - width;
224
+ const minY = bounds.top - baseTop;
225
+ const maxY = bounds.bottom - baseTop - height;
226
+ return { x: minX <= maxX ? Math.min(maxX, Math.max(minX, x)) : minX, y: minY <= maxY ? Math.min(maxY, Math.max(minY, y)) : minY };
227
+ }
228
+ _setPosition(x, y) {
229
+ this._x = x;
230
+ this._y = y;
231
+ this._syncPosition();
232
+ this._reflectPosition();
233
+ }
234
+ _syncPosition() { this.style.setProperty('--_hy-draggable-x', `${this._x}px`); this.style.setProperty('--_hy-draggable-y', `${this._y}px`); }
235
+ _reflectPosition() {
236
+ const x = String(Math.round(this._x * 1000) / 1000);
237
+ const y = String(Math.round(this._y * 1000) / 1000);
238
+ this._reflecting = true;
239
+ if (this.getAttribute('x') !== x)
240
+ this.setAttribute('x', x);
241
+ if (this.getAttribute('y') !== y)
242
+ this.setAttribute('y', y);
243
+ this._reflecting = false;
244
+ }
245
+ _syncAccessibility() {
246
+ this._surface.tabIndex = this.keyboard && !this.disabled ? 0 : -1;
247
+ this._surface.setAttribute('aria-disabled', String(this.disabled));
248
+ this._surface.setAttribute('aria-label', this.getAttribute('aria-label') || 'Draggable container');
249
+ }
250
+ }
251
+ export function defineDraggableComponents() {
252
+ if (!customElements.get('hy-draggable'))
253
+ customElements.define('hy-draggable', HYDraggable);
254
+ }
255
+ //# sourceMappingURL=draggable.js.map
package/dist/index.d.ts CHANGED
@@ -1,6 +1,17 @@
1
1
  export { defineExpandableComponents, HYExpandable } from './expandable.js';
2
2
  export type { HYExpandableButtonPosition, HYExpandableChangeDetail } from './expandable.js';
3
+ export { defineModalComponents, HYModal } from './modal.js';
4
+ export type { HYModalCloseReason, HYModalActionDetail, HYModalCloseDetail } from './modal.js';
5
+ export { defineQRCodeComponents, HYQRCode } from './qr-code.js';
6
+ export type { HYQRCodeErrorCorrectionLevel, HYQRCodeStatus, HYQRCodeRenderDetail, HYQRCodeErrorDetail, HYQRCodeIconErrorDetail } from './qr-code.js';
7
+ export { defineCarouselComponents, HYCarousel } from './carousel.js';
8
+ export type { HYCarouselDirection, HYCarouselIndicatorPosition, HYCarouselIndicatorStyle, HYCarouselChangeSource, HYCarouselChangeDetail, HYCarouselIndicatorRenderer } from './carousel.js';
9
+ export { defineMasonryComponents, HYMasonry } from './masonry.js';
10
+ export type { HYMasonryLayoutChangeDetail } from './masonry.js';
3
11
  export { defineButtonComponents, HYButton, } from './button.js';
12
+ export type { HYButtonIconPosition } from './button.js';
13
+ export { defineButtonGroupComponents, HYButtonGroup, } from './button-group.js';
14
+ export type { HYButtonGroupOrientation } from './button-group.js';
4
15
  export { defineBorderBeamComponents, HYBorderBeam, } from './border-beam.js';
5
16
  export { defineTreeComponents, HYTree, HYTreeNode, } from './tree.js';
6
17
  export { defineSplitComponents, HYSplit, } from './split.js';
@@ -12,14 +23,52 @@ export { defineTooltipComponents, HYTooltip, } from './tooltip.js';
12
23
  export { defineCheckboxComponents, HYCheckbox, } from './checkbox.js';
13
24
  export { defineRadioComponents, HYRadio, } from './radio.js';
14
25
  export { defineRangeComponents, HYRange, } from './range.js';
26
+ export { defineResizableComponents, HYResizable, } from './resizable.js';
15
27
  export { defineDialogComponents, HYDialog, } from './dialog.js';
16
28
  export { defineDrawerComponents, HYDrawer, } from './drawer.js';
29
+ export { defineDraggableComponents, HYDraggable, } from './draggable.js';
17
30
  export { defineInputComponents, HYInput, } from './input.js';
18
31
  export { defineNotificationComponents, HYNotification, } from './notification.js';
32
+ export { defineToastComponents, HYToast, } from './toast.js';
19
33
  export { defineHistoryControlsComponents, HYHistoryControls, } from './history-controls.js';
20
34
  export type { HYHistoryEntry, } from './history-controls.js';
21
35
  export { calculateVirtualListRange, defineVirtualListComponents, HYVirtualList, } from './virtual-list.js';
22
36
  export type { HYVirtualListAlign, HYVirtualListItemClickDetail, HYVirtualListItemRenderer, HYVirtualListRange, HYVirtualListRangeChangeDetail, } from './virtual-list.js';
37
+ export { defineVirtualMouseComponents, HYVirtualMouse, } from './virtual-mouse.js';
38
+ export { defineVirtualKeyboardComponents, HYVirtualKeyboard, HY_VIRTUAL_KEYBOARD_NUMERIC_LAYOUT, HY_VIRTUAL_KEYBOARD_QWERTY_LAYOUT, } from './virtual-keyboard.js';
39
+ export { defineTableComponents, HYTable, } from './table.js';
40
+ export { defineBreadcrumbComponents, HYBreadcrumb, } from './breadcrumb.js';
41
+ export { defineCascaderComponents, HYCascader, } from './cascader.js';
42
+ export type { HYCascaderChangeDetail, HYCascaderOpenChangeDetail, HYCascaderOption, HYCascaderValue, } from './cascader.js';
43
+ export { defineDatePickerComponents, HYDatePicker, } from './date-picker.js';
44
+ export type { HYDatePickerChangeDetail, HYDatePickerOpenChangeDetail, } from './date-picker.js';
45
+ export { defineTimePickerComponents, HYTimePicker, } from './time-picker.js';
46
+ export type { HYTimePickerChangeDetail, HYTimePickerOpenChangeDetail, } from './time-picker.js';
47
+ export { defineSkeletonComponents, HYSkeleton, } from './skeleton.js';
48
+ export type { HYSkeletonAvatarShape, HYSkeletonSize, } from './skeleton.js';
49
+ export { defineWatermarkComponents, HYWatermark, } from './watermark.js';
50
+ export { defineTimelineComponents, HYTimeline, } from './timeline.js';
51
+ export { defineRateComponents, HYRate, } from './rate.js';
52
+ export type { HYRateChangeDetail, HYRateHoverChangeDetail, } from './rate.js';
53
+ export { defineDividerComponents, HYDivider, } from './divider.js';
54
+ export type { HYDividerLineStyle, HYDividerOrientation, HYDividerTextPlacement, } from './divider.js';
55
+ export { defineStepsComponents, HYSteps, } from './steps.js';
56
+ export { defineAffixComponents, HYAffix, } from './affix.js';
57
+ export type { HYAffixChangeDetail } from './affix.js';
58
+ export { defineTourComponents, HYTour, } from './tour.js';
59
+ export { defineBadgeComponents, HYBadge, } from './badge.js';
60
+ export type { HYBadgeCount, HYBadgeSize, HYBadgeStatus, } from './badge.js';
61
+ export { defineTagComponents, HYTag, } from './tag.js';
62
+ export type { HYTagCheckedChangeDetail, HYTagCloseDetail, HYTagSize, HYTagVariant, } from './tag.js';
63
+ export { defineCollapseComponents, HYCollapse, } from './collapse.js';
64
+ export type { HYCollapseChangeDetail, HYCollapseContent, HYCollapseIconPosition, HYCollapseItem, HYCollapseKey, HYCollapseTrigger, } from './collapse.js';
65
+ export type { HYTourChangeDetail, HYTourCloseDetail, HYTourCloseReason, HYTourFinishDetail, HYTourOpenChangeDetail, HYTourPlacement, HYTourStep, HYTourTarget, } from './tour.js';
66
+ export type { HYStepItem, HYStepsChangeDetail, HYStepsDirection, HYStepsSize, HYStepStatus, } from './steps.js';
67
+ export type { HYTimelineDotRenderer, HYTimelineItem, HYTimelineItemPosition, HYTimelineItemRenderer, HYTimelineMode, HYTimelineStatus, } from './timeline.js';
68
+ export type { HYBreadcrumbClickDetail, HYBreadcrumbItem, HYBreadcrumbItemRenderer, } from './breadcrumb.js';
69
+ export type { HYTableAlign, HYTableChangeDetail, HYTableChangeReason, HYTableColumn, HYTableFilterOption, HYTableFilterState, HYTableRowClickDetail, HYTableSortDirection, HYTableSortState, } from './table.js';
70
+ export type { HYVirtualMouseButton, HYVirtualMouseButtonDetail, HYVirtualMouseClickOptions, HYVirtualMouseImages, HYVirtualMouseMoveDetail, HYVirtualMouseMoveOptions, HYVirtualMousePosition, HYVirtualMouseState, } from './virtual-mouse.js';
71
+ export type { HYVirtualKeyboardAction, HYVirtualKeyboardInputDetail, HYVirtualKeyboardKey, HYVirtualKeyboardKeyDetail, HYVirtualKeyboardLayout, HYVirtualKeyboardMode, HYVirtualKeyboardSource, HYVirtualKeyboardTapOptions, HYVirtualKeyboardTypeOptions, } from './virtual-keyboard.js';
23
72
  export type { HYSplitDirection, HYSplitRatioChangeDetail, } from './split.js';
24
73
  export type { HYSelectChangeDetail, HYSelectOption, } from './select.js';
25
74
  export type { HYTabChangeDetail, HYTabOption, } from './tabs.js';
@@ -29,9 +78,12 @@ export type { HYTooltipPlacement, } from './tooltip.js';
29
78
  export type { HYCheckboxChangeDetail, } from './checkbox.js';
30
79
  export type { HYRadioChangeDetail, } from './radio.js';
31
80
  export type { HYRangeChangeDetail, HYRangeHandle, HYRangeValue, } from './range.js';
81
+ export type { HYResizableBounds, HYResizableBoundsMode, HYResizableChangeDetail, HYResizableEndDetail, HYResizableHandle, HYResizableRect, HYResizableSource, } from './resizable.js';
32
82
  export type { HYDialogCloseDetail, HYDialogCloseReason, } from './dialog.js';
33
83
  export type { HYDrawerCloseDetail, HYDrawerCloseReason, HYDrawerPlacement, } from './drawer.js';
34
- export type { HYInputChangeDetail, HYInputType, } from './input.js';
84
+ export type { HYDraggableAxis, HYDraggableBounds, HYDraggableBoundsMode, HYDraggableEndDetail, HYDraggableMoveDetail, HYDraggableMoveSource, HYDraggablePosition, HYDraggableStartDetail, } from './draggable.js';
85
+ export type { HYInputChangeDetail, HYInputSearchDetail, HYInputSearchSource, HYInputType, } from './input.js';
35
86
  export type { HYNotificationCloseDetail, HYNotificationCloseReason, HYNotificationMethodOptions, HYNotificationOpenDetail, HYNotificationOptions, HYNotificationPlacement, HYNotificationType, } from './notification.js';
87
+ export type { HYToastCloseDetail, HYToastCloseReason, HYToastMethodOptions, HYToastOpenDetail, HYToastOptions, HYToastPlacement, HYToastType, } from './toast.js';
36
88
  export type { HYTreeDataChangeDetail, HYTreeDropPosition, HYTreeNodeContextMenuDetail, HYTreeNodeData, HYTreeSelectionChangeDetail, } from './tree.js';
37
89
  export declare function defineHaiyueUI(): void;