@haiyue/ui 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/README.md +444 -1
  3. package/dist/adapters/web/controls.d.ts +19 -0
  4. package/dist/adapters/web/controls.js +284 -0
  5. package/dist/adapters/web/dom.d.ts +10 -0
  6. package/dist/adapters/web/dom.js +98 -0
  7. package/dist/adapters/web/index.d.ts +3 -0
  8. package/dist/adapters/web/index.js +3 -0
  9. package/dist/adapters/web/renderer.d.ts +18 -0
  10. package/dist/adapters/web/renderer.js +545 -0
  11. package/dist/adapters/web/styles.d.ts +3 -0
  12. package/dist/adapters/web/styles.js +62 -0
  13. package/dist/adapters/web/types.d.ts +46 -0
  14. package/dist/adapters/web/types.js +2 -0
  15. package/dist/affix.d.ts +35 -0
  16. package/dist/affix.js +137 -0
  17. package/dist/badge.d.ts +45 -0
  18. package/dist/badge.js +168 -0
  19. package/dist/breadcrumb.d.ts +37 -0
  20. package/dist/breadcrumb.js +210 -0
  21. package/dist/button-group.d.ts +12 -0
  22. package/dist/button-group.js +72 -0
  23. package/dist/button.d.ts +15 -1
  24. package/dist/button.js +101 -12
  25. package/dist/cascader.d.ts +65 -0
  26. package/dist/cascader.js +479 -0
  27. package/dist/collapse.d.ts +55 -0
  28. package/dist/collapse.js +261 -0
  29. package/dist/core/actions.d.ts +32 -0
  30. package/dist/core/actions.js +145 -0
  31. package/dist/core/data.d.ts +4 -0
  32. package/dist/core/data.js +27 -0
  33. package/dist/core/index.d.ts +7 -0
  34. package/dist/core/index.js +6 -0
  35. package/dist/core/platform-view.d.ts +17 -0
  36. package/dist/core/platform-view.js +84 -0
  37. package/dist/core/selection.d.ts +14 -0
  38. package/dist/core/selection.js +93 -0
  39. package/dist/core/text.d.ts +31 -0
  40. package/dist/core/text.js +154 -0
  41. package/dist/core/types.d.ts +250 -0
  42. package/dist/core/types.js +2 -0
  43. package/dist/core/validation.d.ts +24 -0
  44. package/dist/core/validation.js +238 -0
  45. package/dist/date-picker.d.ts +60 -0
  46. package/dist/date-picker.js +355 -0
  47. package/dist/dialog.d.ts +4 -0
  48. package/dist/dialog.js +23 -5
  49. package/dist/divider.d.ts +33 -0
  50. package/dist/divider.js +113 -0
  51. package/dist/draggable.d.ts +78 -0
  52. package/dist/draggable.js +255 -0
  53. package/dist/index.d.ts +45 -1
  54. package/dist/index.js +66 -0
  55. package/dist/input.d.ts +30 -1
  56. package/dist/input.js +166 -54
  57. package/dist/portable/index.d.ts +4 -0
  58. package/dist/portable/index.js +3 -0
  59. package/dist/portable/page.d.ts +38 -0
  60. package/dist/portable/page.js +361 -0
  61. package/dist/portable/theme.d.ts +19 -0
  62. package/dist/portable/theme.js +40 -0
  63. package/dist/portable/tree.d.ts +10 -0
  64. package/dist/portable/tree.js +96 -0
  65. package/dist/range.d.ts +3 -0
  66. package/dist/range.js +16 -5
  67. package/dist/rate.d.ts +46 -0
  68. package/dist/rate.js +193 -0
  69. package/dist/resizable.d.ts +97 -0
  70. package/dist/resizable.js +426 -0
  71. package/dist/skeleton.d.ts +41 -0
  72. package/dist/skeleton.js +141 -0
  73. package/dist/steps.d.ts +45 -0
  74. package/dist/steps.js +203 -0
  75. package/dist/table.d.ts +106 -0
  76. package/dist/table.js +689 -0
  77. package/dist/tabs.d.ts +3 -0
  78. package/dist/tabs.js +11 -1
  79. package/dist/tag.d.ts +55 -0
  80. package/dist/tag.js +225 -0
  81. package/dist/time-picker.d.ts +62 -0
  82. package/dist/time-picker.js +298 -0
  83. package/dist/timeline.d.ts +48 -0
  84. package/dist/timeline.js +201 -0
  85. package/dist/toast.d.ts +55 -0
  86. package/dist/toast.js +224 -0
  87. package/dist/tour.d.ts +89 -0
  88. package/dist/tour.js +306 -0
  89. package/dist/virtual-keyboard.d.ts +95 -0
  90. package/dist/virtual-keyboard.js +432 -0
  91. package/dist/virtual-mouse.d.ts +82 -0
  92. package/dist/virtual-mouse.js +358 -0
  93. package/dist/watermark.d.ts +44 -0
  94. package/dist/watermark.js +148 -0
  95. package/package.json +115 -4
@@ -0,0 +1,358 @@
1
+ const DEFAULT_DURATION = 320;
2
+ const DEFAULT_EASING = 'cubic-bezier(0.22, 1, 0.36, 1)';
3
+ const BUTTONS = ['left', 'middle', 'right'];
4
+ const STATES = ['idle', 'left-down', 'middle-down', 'right-down'];
5
+ function finiteNumber(value, fallback) {
6
+ const number = Number(value);
7
+ return Number.isFinite(number) ? number : fallback;
8
+ }
9
+ function normalizeButton(button) {
10
+ if (!BUTTONS.includes(button))
11
+ throw new RangeError(`Unsupported virtual mouse button: ${String(button)}`);
12
+ return button;
13
+ }
14
+ /** Fixed, non-interactive cursor visual for tutorials, playback, and remote-operation feedback. */
15
+ export class HYVirtualMouse extends HTMLElement {
16
+ _cursor = document.createElement('span');
17
+ _shape = document.createElement('span');
18
+ _image = document.createElement('img');
19
+ _indicator = document.createElement('span');
20
+ _pressed = new Set();
21
+ _images = {};
22
+ _x = 0;
23
+ _y = 0;
24
+ _reflectingPosition = false;
25
+ _moveTimer = null;
26
+ _resolveMove = null;
27
+ _delays = new Map();
28
+ static get observedAttributes() {
29
+ return [
30
+ 'x', 'y', 'animated', 'duration', 'easing',
31
+ 'idle-image', 'left-down-image', 'middle-down-image', 'right-down-image',
32
+ ];
33
+ }
34
+ constructor() {
35
+ super();
36
+ const root = this.attachShadow({ mode: 'open' });
37
+ const style = document.createElement('style');
38
+ style.textContent = `
39
+ :host {
40
+ position: fixed !important;
41
+ z-index: var(--hy-virtual-mouse-z-index, 2147483000);
42
+ inset: 0 auto auto 0;
43
+ display: block;
44
+ width: 0;
45
+ height: 0;
46
+ overflow: visible;
47
+ pointer-events: none !important;
48
+ transform: translate3d(var(--_hy-virtual-mouse-x, 0px), var(--_hy-virtual-mouse-y, 0px), 0);
49
+ transition-property: transform;
50
+ transition-duration: 0ms;
51
+ transition-timing-function: var(--_hy-virtual-mouse-easing, cubic-bezier(0.22, 1, 0.36, 1));
52
+ will-change: transform;
53
+ contain: layout style;
54
+ }
55
+ :host([hidden]) { display: none !important; }
56
+ .cursor {
57
+ position: absolute;
58
+ width: var(--hy-virtual-mouse-width, 24px);
59
+ height: var(--hy-virtual-mouse-height, 30px);
60
+ transform: translate(
61
+ calc(-1 * var(--hy-virtual-mouse-hotspot-x, 2px)),
62
+ calc(-1 * var(--hy-virtual-mouse-hotspot-y, 2px))
63
+ );
64
+ transform-origin: var(--hy-virtual-mouse-hotspot-x, 2px) var(--hy-virtual-mouse-hotspot-y, 2px);
65
+ transition: scale 90ms ease;
66
+ }
67
+ :host([data-state$="-down"]) .cursor { scale: var(--hy-virtual-mouse-pressed-scale, 0.9); }
68
+ .shape {
69
+ position: absolute;
70
+ inset: 0;
71
+ display: block;
72
+ box-sizing: border-box;
73
+ border: var(--hy-virtual-mouse-border-width, 1.5px) solid var(--hy-virtual-mouse-border-color, #f5fbff);
74
+ clip-path: polygon(5% 0, 88% 70%, 55% 74%, 72% 100%, 51% 100%, 36% 76%, 10% 96%);
75
+ background: var(--hy-virtual-mouse-idle-background, linear-gradient(145deg, #78d7ff, #5265e8));
76
+ filter: drop-shadow(var(--hy-virtual-mouse-shadow, 0 3px 5px rgba(4, 9, 28, 0.5)));
77
+ }
78
+ :host([data-state="left-down"]) .shape {
79
+ background: var(--hy-virtual-mouse-left-down-background, linear-gradient(145deg, #b5f1ff, #6d74ff));
80
+ }
81
+ :host([data-state="middle-down"]) .shape {
82
+ background: var(--hy-virtual-mouse-middle-down-background, linear-gradient(145deg, #d6c4ff, #7654e8));
83
+ }
84
+ :host([data-state="right-down"]) .shape {
85
+ background: var(--hy-virtual-mouse-right-down-background, linear-gradient(145deg, #f4c8ff, #9156dc));
86
+ }
87
+ .image {
88
+ position: absolute;
89
+ inset: 0;
90
+ display: none;
91
+ width: 100%;
92
+ height: 100%;
93
+ object-fit: contain;
94
+ object-position: left top;
95
+ filter: drop-shadow(var(--hy-virtual-mouse-shadow, 0 3px 5px rgba(4, 9, 28, 0.5)));
96
+ user-select: none;
97
+ }
98
+ .cursor[data-image-ready] .shape { display: none; }
99
+ .cursor[data-image-ready] .image { display: block; }
100
+ .indicator {
101
+ position: absolute;
102
+ top: var(--hy-virtual-mouse-hotspot-y, 2px);
103
+ left: var(--hy-virtual-mouse-hotspot-x, 2px);
104
+ width: var(--hy-virtual-mouse-click-size, 28px);
105
+ height: var(--hy-virtual-mouse-click-size, 28px);
106
+ border: 2px solid var(--hy-virtual-mouse-click-color, #83dcff);
107
+ border-radius: 50%;
108
+ opacity: 0;
109
+ translate: -50% -50%;
110
+ }
111
+ .indicator.pulse { animation: hy-virtual-mouse-click 360ms ease-out; }
112
+ @keyframes hy-virtual-mouse-click {
113
+ 0% { opacity: 0.9; scale: 0.3; }
114
+ 100% { opacity: 0; scale: 1.35; }
115
+ }
116
+ @media (prefers-reduced-motion: reduce) {
117
+ :host { transition-duration: 0ms !important; }
118
+ .cursor { transition-duration: 0ms; }
119
+ .indicator.pulse { animation-duration: 1ms; }
120
+ }
121
+ `;
122
+ this._cursor.className = 'cursor';
123
+ this._cursor.part.add('cursor');
124
+ this._shape.className = 'shape';
125
+ this._shape.part.add('shape');
126
+ this._image.className = 'image';
127
+ this._image.part.add('image');
128
+ this._image.alt = '';
129
+ this._image.draggable = false;
130
+ this._indicator.className = 'indicator';
131
+ this._indicator.part.add('indicator');
132
+ this._image.addEventListener('load', () => { this._cursor.dataset.imageReady = ''; });
133
+ this._image.addEventListener('error', () => { delete this._cursor.dataset.imageReady; });
134
+ this._indicator.addEventListener('animationend', () => this._indicator.classList.remove('pulse'));
135
+ this._cursor.append(this._shape, this._image, this._indicator);
136
+ root.append(style, this._cursor);
137
+ }
138
+ connectedCallback() {
139
+ this.setAttribute('aria-hidden', 'true');
140
+ this._x = finiteNumber(this.getAttribute('x'), this._x);
141
+ this._y = finiteNumber(this.getAttribute('y'), this._y);
142
+ this._applyPosition();
143
+ this._syncState();
144
+ }
145
+ disconnectedCallback() {
146
+ this._cancelMove();
147
+ for (const cancel of this._delays.values())
148
+ cancel();
149
+ this._delays.clear();
150
+ this._pressed.clear();
151
+ }
152
+ attributeChangedCallback(name) {
153
+ if ((name === 'x' || name === 'y') && !this._reflectingPosition) {
154
+ this._x = finiteNumber(this.getAttribute('x'), this._x);
155
+ this._y = finiteNumber(this.getAttribute('y'), this._y);
156
+ this._applyPosition();
157
+ }
158
+ else if (name.endsWith('-image')) {
159
+ this._syncImage();
160
+ }
161
+ }
162
+ get x() { return this._x; }
163
+ set x(value) { this._setPosition(finiteNumber(value, this._x), this._y); }
164
+ get y() { return this._y; }
165
+ set y(value) { this._setPosition(this._x, finiteNumber(value, this._y)); }
166
+ get animated() { return this.hasAttribute('animated'); }
167
+ set animated(value) { this.toggleAttribute('animated', Boolean(value)); }
168
+ get duration() {
169
+ return Math.min(60_000, Math.max(0, finiteNumber(this.getAttribute('duration'), DEFAULT_DURATION)));
170
+ }
171
+ set duration(value) {
172
+ this.setAttribute('duration', String(Math.min(60_000, Math.max(0, finiteNumber(value, DEFAULT_DURATION)))));
173
+ }
174
+ get easing() { return this.getAttribute('easing')?.trim() || DEFAULT_EASING; }
175
+ set easing(value) {
176
+ const normalized = String(value).trim();
177
+ if (normalized)
178
+ this.setAttribute('easing', normalized);
179
+ else
180
+ this.removeAttribute('easing');
181
+ }
182
+ get state() { return this._currentState(); }
183
+ get pressedButtons() { return BUTTONS.filter(button => this._pressed.has(button)); }
184
+ get images() { return { ...this._images }; }
185
+ set images(value) {
186
+ this._images = Object.fromEntries(STATES.flatMap(state => {
187
+ const source = value?.[state]?.trim();
188
+ return source ? [[state, source]] : [];
189
+ }));
190
+ this._syncImage();
191
+ }
192
+ setStateImage(state, source) {
193
+ if (!STATES.includes(state))
194
+ throw new RangeError(`Unsupported virtual mouse state: ${String(state)}`);
195
+ const normalized = source?.trim();
196
+ if (normalized)
197
+ this._images[state] = normalized;
198
+ else
199
+ delete this._images[state];
200
+ this._syncImage();
201
+ }
202
+ show() { this.hidden = false; }
203
+ hide() { this.hidden = true; }
204
+ moveBy(deltaX, deltaY, options = {}) {
205
+ return this.moveTo(this._x + finiteNumber(deltaX, 0), this._y + finiteNumber(deltaY, 0), options);
206
+ }
207
+ moveTo(x, y, options = {}) {
208
+ const from = { x: this._x, y: this._y };
209
+ const to = { x: finiteNumber(x, this._x), y: finiteNumber(y, this._y) };
210
+ const requestedDuration = Math.min(60_000, Math.max(0, finiteNumber(options.duration, this.duration)));
211
+ const reducedMotion = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
212
+ const animated = (options.animate ?? this.animated) && requestedDuration > 0 && !reducedMotion;
213
+ const duration = animated ? requestedDuration : 0;
214
+ const detail = { from, to, animated, duration };
215
+ this._cancelMove();
216
+ this.dispatchEvent(new CustomEvent('virtual-mouse-move-start', {
217
+ bubbles: true, composed: true, detail,
218
+ }));
219
+ this.style.transitionDuration = `${duration}ms`;
220
+ this.style.setProperty('--_hy-virtual-mouse-easing', options.easing?.trim() || this.easing);
221
+ this._setPosition(to.x, to.y);
222
+ this.dispatchEvent(new CustomEvent('virtual-mouse-move', {
223
+ bubbles: true, composed: true, detail,
224
+ }));
225
+ if (!animated) {
226
+ this.dispatchEvent(new CustomEvent('virtual-mouse-move-end', {
227
+ bubbles: true, composed: true, detail,
228
+ }));
229
+ return Promise.resolve();
230
+ }
231
+ return new Promise(resolve => {
232
+ this._resolveMove = resolve;
233
+ this._moveTimer = window.setTimeout(() => {
234
+ this._moveTimer = null;
235
+ this._resolveMove = null;
236
+ this.style.transitionDuration = '0ms';
237
+ resolve();
238
+ this.dispatchEvent(new CustomEvent('virtual-mouse-move-end', {
239
+ bubbles: true, composed: true, detail,
240
+ }));
241
+ }, duration);
242
+ });
243
+ }
244
+ press(button = 'left') {
245
+ const normalized = normalizeButton(button);
246
+ if (this._pressed.has(normalized))
247
+ return;
248
+ this._pressed.add(normalized);
249
+ this._syncState();
250
+ this._dispatchButtonEvent('virtual-mouse-down', normalized);
251
+ }
252
+ release(button = 'left') {
253
+ const normalized = normalizeButton(button);
254
+ if (!this._pressed.delete(normalized))
255
+ return;
256
+ this._syncState();
257
+ this._dispatchButtonEvent('virtual-mouse-up', normalized);
258
+ }
259
+ releaseAll() {
260
+ for (const button of [...this.pressedButtons])
261
+ this.release(button);
262
+ }
263
+ async click(button = 'left', options = {}) {
264
+ const normalized = normalizeButton(button);
265
+ this.press(normalized);
266
+ await this._wait(Math.min(60_000, Math.max(0, finiteNumber(options.holdDuration, 90))));
267
+ this.release(normalized);
268
+ this._pulse();
269
+ this._dispatchButtonEvent('virtual-mouse-click', normalized);
270
+ }
271
+ leftClick(options = {}) { return this.click('left', options); }
272
+ middleClick(options = {}) { return this.click('middle', options); }
273
+ rightClick(options = {}) { return this.click('right', options); }
274
+ _setPosition(x, y) {
275
+ this._x = x;
276
+ this._y = y;
277
+ this._reflectingPosition = true;
278
+ this.setAttribute('x', String(x));
279
+ this.setAttribute('y', String(y));
280
+ this._reflectingPosition = false;
281
+ this._applyPosition();
282
+ }
283
+ _applyPosition() {
284
+ this.style.setProperty('--_hy-virtual-mouse-x', `${this._x}px`);
285
+ this.style.setProperty('--_hy-virtual-mouse-y', `${this._y}px`);
286
+ }
287
+ _currentState() {
288
+ if (this._pressed.has('right'))
289
+ return 'right-down';
290
+ if (this._pressed.has('middle'))
291
+ return 'middle-down';
292
+ if (this._pressed.has('left'))
293
+ return 'left-down';
294
+ return 'idle';
295
+ }
296
+ _syncState() {
297
+ this.dataset.state = this._currentState();
298
+ this._syncImage();
299
+ }
300
+ _syncImage() {
301
+ const state = this._currentState();
302
+ const source = this._images[state]
303
+ ?? this.getAttribute(`${state}-image`)?.trim()
304
+ ?? this._images.idle
305
+ ?? this.getAttribute('idle-image')?.trim()
306
+ ?? '';
307
+ delete this._cursor.dataset.imageReady;
308
+ if (source) {
309
+ if (this._image.getAttribute('src') !== source)
310
+ this._image.src = source;
311
+ else if (this._image.complete && this._image.naturalWidth > 0)
312
+ this._cursor.dataset.imageReady = '';
313
+ }
314
+ else {
315
+ this._image.removeAttribute('src');
316
+ }
317
+ }
318
+ _dispatchButtonEvent(name, button) {
319
+ this.dispatchEvent(new CustomEvent(name, {
320
+ bubbles: true,
321
+ composed: true,
322
+ detail: { button, buttons: this.pressedButtons, state: this.state, x: this._x, y: this._y },
323
+ }));
324
+ }
325
+ _pulse() {
326
+ this._indicator.classList.remove('pulse');
327
+ void this._indicator.offsetWidth;
328
+ this._indicator.classList.add('pulse');
329
+ }
330
+ _cancelMove() {
331
+ if (this._moveTimer !== null)
332
+ window.clearTimeout(this._moveTimer);
333
+ this._moveTimer = null;
334
+ const resolve = this._resolveMove;
335
+ this._resolveMove = null;
336
+ resolve?.();
337
+ this.style.transitionDuration = '0ms';
338
+ }
339
+ _wait(duration) {
340
+ if (duration === 0)
341
+ return Promise.resolve();
342
+ return new Promise(resolve => {
343
+ const timer = window.setTimeout(() => {
344
+ this._delays.delete(timer);
345
+ resolve();
346
+ }, duration);
347
+ this._delays.set(timer, () => {
348
+ window.clearTimeout(timer);
349
+ resolve();
350
+ });
351
+ });
352
+ }
353
+ }
354
+ export function defineVirtualMouseComponents() {
355
+ if (!customElements.get('hy-virtual-mouse'))
356
+ customElements.define('hy-virtual-mouse', HYVirtualMouse);
357
+ }
358
+ //# sourceMappingURL=virtual-mouse.js.map
@@ -0,0 +1,44 @@
1
+ /** Repeating, non-interactive text or image watermark over slotted content. */
2
+ export declare class HYWatermark extends HTMLElement {
3
+ private readonly _svg;
4
+ private readonly _pattern;
5
+ private readonly _patternId;
6
+ static get observedAttributes(): string[];
7
+ constructor();
8
+ connectedCallback(): void;
9
+ attributeChangedCallback(): void;
10
+ get content(): string;
11
+ set content(value: string | readonly string[]);
12
+ get image(): string;
13
+ set image(value: string);
14
+ get rotate(): number;
15
+ set rotate(value: number);
16
+ get gapX(): number;
17
+ set gapX(value: number);
18
+ get gapY(): number;
19
+ set gapY(value: number);
20
+ get offsetX(): number;
21
+ set offsetX(value: number);
22
+ get offsetY(): number;
23
+ set offsetY(value: number);
24
+ get markWidth(): number;
25
+ set markWidth(value: number);
26
+ get markHeight(): number;
27
+ set markHeight(value: number);
28
+ get fontSize(): number;
29
+ set fontSize(value: number);
30
+ get fontFamily(): string;
31
+ set fontFamily(value: string);
32
+ get fontWeight(): string;
33
+ set fontWeight(value: string | number);
34
+ get color(): string;
35
+ set color(value: string);
36
+ get opacity(): number;
37
+ set opacity(value: number);
38
+ get zIndex(): number;
39
+ set zIndex(value: number);
40
+ private _render;
41
+ private _renderImage;
42
+ private _renderText;
43
+ }
44
+ export declare function defineWatermarkComponents(): void;
@@ -0,0 +1,148 @@
1
+ const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
2
+ function finiteNumber(value, fallback, min, max) {
3
+ const number = Number(value);
4
+ return Number.isFinite(number) ? Math.min(max, Math.max(min, number)) : fallback;
5
+ }
6
+ function svgElement(name) {
7
+ return document.createElementNS(SVG_NAMESPACE, name);
8
+ }
9
+ /** Repeating, non-interactive text or image watermark over slotted content. */
10
+ export class HYWatermark extends HTMLElement {
11
+ _svg = svgElement('svg');
12
+ _pattern = svgElement('pattern');
13
+ _patternId = `hy-watermark-${globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2)}`;
14
+ static get observedAttributes() {
15
+ return ['content', 'image', 'rotate', 'gap-x', 'gap-y', 'offset-x', 'offset-y', 'mark-width', 'mark-height', 'font-size', 'font-family', 'font-weight', 'color', 'opacity', 'z-index'];
16
+ }
17
+ constructor() {
18
+ super();
19
+ const root = this.attachShadow({ mode: 'open' });
20
+ const style = document.createElement('style');
21
+ const content = document.createElement('div');
22
+ const slot = document.createElement('slot');
23
+ style.textContent = `
24
+ :host { position: relative; display: block; min-width: 0; isolation: isolate; }
25
+ .content { position: relative; z-index: 0; min-width: 0; }
26
+ svg { position: absolute; z-index: var(--_hy-watermark-z-index, 9); inset: 0; width: 100%; height: 100%; overflow: hidden; pointer-events: none; user-select: none; }
27
+ svg[hidden] { display: none; }
28
+ .mark-text { fill: var(--_hy-watermark-color, var(--hy-watermark-color, currentColor)); }
29
+ `;
30
+ content.className = 'content';
31
+ content.part.add('content');
32
+ content.append(slot);
33
+ this._svg.classList.add('overlay');
34
+ this._svg.part.add('overlay');
35
+ this._svg.setAttribute('aria-hidden', 'true');
36
+ this._svg.setAttribute('focusable', 'false');
37
+ const definitions = svgElement('defs');
38
+ this._pattern.id = this._patternId;
39
+ this._pattern.setAttribute('patternUnits', 'userSpaceOnUse');
40
+ definitions.append(this._pattern);
41
+ const fill = svgElement('rect');
42
+ fill.setAttribute('width', '100%');
43
+ fill.setAttribute('height', '100%');
44
+ fill.setAttribute('fill', `url(#${this._patternId})`);
45
+ this._svg.append(definitions, fill);
46
+ root.append(style, content, this._svg);
47
+ }
48
+ connectedCallback() { this._render(); }
49
+ attributeChangedCallback() { this._render(); }
50
+ get content() { return this.getAttribute('content') ?? ''; }
51
+ set content(value) { this.setAttribute('content', Array.isArray(value) ? value.map(String).join('\n') : String(value)); }
52
+ get image() { return this.getAttribute('image') ?? ''; }
53
+ set image(value) { const normalized = String(value).trim(); if (normalized)
54
+ this.setAttribute('image', normalized);
55
+ else
56
+ this.removeAttribute('image'); }
57
+ get rotate() { return finiteNumber(this.getAttribute('rotate'), -22, -180, 180); }
58
+ set rotate(value) { this.setAttribute('rotate', String(finiteNumber(value, -22, -180, 180))); }
59
+ get gapX() { return finiteNumber(this.getAttribute('gap-x'), 100, 0, 1000); }
60
+ set gapX(value) { this.setAttribute('gap-x', String(finiteNumber(value, 100, 0, 1000))); }
61
+ get gapY() { return finiteNumber(this.getAttribute('gap-y'), 100, 0, 1000); }
62
+ set gapY(value) { this.setAttribute('gap-y', String(finiteNumber(value, 100, 0, 1000))); }
63
+ get offsetX() { return finiteNumber(this.getAttribute('offset-x'), 0, -2000, 2000); }
64
+ set offsetX(value) { this.setAttribute('offset-x', String(finiteNumber(value, 0, -2000, 2000))); }
65
+ get offsetY() { return finiteNumber(this.getAttribute('offset-y'), 0, -2000, 2000); }
66
+ set offsetY(value) { this.setAttribute('offset-y', String(finiteNumber(value, 0, -2000, 2000))); }
67
+ get markWidth() { return finiteNumber(this.getAttribute('mark-width'), 120, 16, 1000); }
68
+ set markWidth(value) { this.setAttribute('mark-width', String(finiteNumber(value, 120, 16, 1000))); }
69
+ get markHeight() { return finiteNumber(this.getAttribute('mark-height'), 64, 16, 1000); }
70
+ set markHeight(value) { this.setAttribute('mark-height', String(finiteNumber(value, 64, 16, 1000))); }
71
+ get fontSize() { return finiteNumber(this.getAttribute('font-size'), 14, 6, 96); }
72
+ set fontSize(value) { this.setAttribute('font-size', String(finiteNumber(value, 14, 6, 96))); }
73
+ get fontFamily() { return this.getAttribute('font-family') || 'system-ui, sans-serif'; }
74
+ set fontFamily(value) { this.setAttribute('font-family', String(value)); }
75
+ get fontWeight() { return this.getAttribute('font-weight') || '500'; }
76
+ set fontWeight(value) { this.setAttribute('font-weight', String(value)); }
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 opacity() { return finiteNumber(this.getAttribute('opacity'), 0.16, 0, 1); }
83
+ set opacity(value) { this.setAttribute('opacity', String(finiteNumber(value, 0.16, 0, 1))); }
84
+ get zIndex() { return Math.round(finiteNumber(this.getAttribute('z-index'), 9, -1000, 100000)); }
85
+ set zIndex(value) { this.setAttribute('z-index', String(Math.round(finiteNumber(value, 9, -1000, 100000)))); }
86
+ _render() {
87
+ if (!this.isConnected)
88
+ return;
89
+ const hasMark = Boolean(this.image || this.content);
90
+ this._svg.toggleAttribute('hidden', !hasMark);
91
+ this._pattern.replaceChildren();
92
+ this.style.setProperty('--_hy-watermark-z-index', String(this.zIndex));
93
+ if (this.color)
94
+ this.style.setProperty('--_hy-watermark-color', this.color);
95
+ else
96
+ this.style.removeProperty('--_hy-watermark-color');
97
+ if (!hasMark)
98
+ return;
99
+ const tileWidth = this.markWidth + this.gapX;
100
+ const tileHeight = this.markHeight + this.gapY;
101
+ this._pattern.setAttribute('x', String(this.offsetX));
102
+ this._pattern.setAttribute('y', String(this.offsetY));
103
+ this._pattern.setAttribute('width', String(tileWidth));
104
+ this._pattern.setAttribute('height', String(tileHeight));
105
+ const group = svgElement('g');
106
+ group.setAttribute('opacity', String(this.opacity));
107
+ group.setAttribute('transform', `rotate(${this.rotate} ${this.markWidth / 2} ${this.markHeight / 2})`);
108
+ if (this.image)
109
+ this._renderImage(group);
110
+ else
111
+ this._renderText(group);
112
+ this._pattern.append(group);
113
+ }
114
+ _renderImage(group) {
115
+ const image = svgElement('image');
116
+ image.part.add('mark', 'image');
117
+ image.setAttribute('href', this.image);
118
+ image.setAttribute('x', '0');
119
+ image.setAttribute('y', '0');
120
+ image.setAttribute('width', String(this.markWidth));
121
+ image.setAttribute('height', String(this.markHeight));
122
+ image.setAttribute('preserveAspectRatio', 'xMidYMid meet');
123
+ group.append(image);
124
+ }
125
+ _renderText(group) {
126
+ const lines = this.content.split(/\r?\n/u);
127
+ const lineHeight = this.fontSize * 1.35;
128
+ const startY = this.markHeight / 2 - ((lines.length - 1) * lineHeight) / 2;
129
+ const text = svgElement('text');
130
+ text.classList.add('mark-text');
131
+ text.part.add('mark', 'text');
132
+ text.setAttribute('x', String(this.markWidth / 2));
133
+ text.setAttribute('y', String(startY));
134
+ text.setAttribute('text-anchor', 'middle');
135
+ text.setAttribute('dominant-baseline', 'middle');
136
+ text.setAttribute('font-size', String(this.fontSize));
137
+ text.setAttribute('font-family', this.fontFamily);
138
+ text.setAttribute('font-weight', this.fontWeight);
139
+ lines.forEach((line, index) => { const span = svgElement('tspan'); span.setAttribute('x', String(this.markWidth / 2)); if (index > 0)
140
+ span.setAttribute('dy', String(lineHeight)); span.textContent = line; text.append(span); });
141
+ group.append(text);
142
+ }
143
+ }
144
+ export function defineWatermarkComponents() {
145
+ if (!customElements.get('hy-watermark'))
146
+ customElements.define('hy-watermark', HYWatermark);
147
+ }
148
+ //# sourceMappingURL=watermark.js.map