godown 3.0.0-canary.7 → 3.0.0-canary.8

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 (142) hide show
  1. package/components/alert.d.ts.map +1 -1
  2. package/components/alert.js +3 -2
  3. package/components/alert.js.map +1 -1
  4. package/components/breath.js +2 -2
  5. package/components/breath.js.map +1 -1
  6. package/components/card.d.ts +1 -1
  7. package/components/card.d.ts.map +1 -1
  8. package/components/card.js +3 -4
  9. package/components/card.js.map +1 -1
  10. package/components/carousel.js +1 -1
  11. package/components/carousel.js.map +1 -1
  12. package/components/input.d.ts +0 -1
  13. package/components/input.d.ts.map +1 -1
  14. package/components/input.js +0 -3
  15. package/components/input.js.map +1 -1
  16. package/components/layout.d.ts +1 -1
  17. package/components/layout.d.ts.map +1 -1
  18. package/components/layout.js +5 -6
  19. package/components/layout.js.map +1 -1
  20. package/components/router.js +1 -1
  21. package/components/router.js.map +1 -1
  22. package/components/select.d.ts +0 -4
  23. package/components/select.d.ts.map +1 -1
  24. package/components/select.js +0 -7
  25. package/components/select.js.map +1 -1
  26. package/components/skeleton.d.ts.map +1 -1
  27. package/components/skeleton.js +1 -2
  28. package/components/skeleton.js.map +1 -1
  29. package/components/split.d.ts +1 -1
  30. package/components/split.d.ts.map +1 -1
  31. package/components/split.js +8 -2
  32. package/components/split.js.map +1 -1
  33. package/components/text.js +1 -1
  34. package/components/text.js.map +1 -1
  35. package/core/super-input.d.ts +0 -1
  36. package/core/super-input.d.ts.map +1 -1
  37. package/core/super-input.js +0 -3
  38. package/core/super-input.js.map +1 -1
  39. package/custom-elements.json +1 -1
  40. package/dev/components/alert.d.ts.map +1 -1
  41. package/dev/components/alert.js +3 -2
  42. package/dev/components/alert.js.map +1 -1
  43. package/dev/components/breath.d.ts.map +1 -1
  44. package/dev/components/breath.js +2 -1
  45. package/dev/components/breath.js.map +1 -1
  46. package/dev/components/card.d.ts +1 -1
  47. package/dev/components/card.d.ts.map +1 -1
  48. package/dev/components/card.js +3 -4
  49. package/dev/components/card.js.map +1 -1
  50. package/dev/components/carousel.d.ts.map +1 -1
  51. package/dev/components/carousel.js +1 -0
  52. package/dev/components/carousel.js.map +1 -1
  53. package/dev/components/input.d.ts +0 -1
  54. package/dev/components/input.d.ts.map +1 -1
  55. package/dev/components/input.js +0 -3
  56. package/dev/components/input.js.map +1 -1
  57. package/dev/components/layout.d.ts +1 -1
  58. package/dev/components/layout.d.ts.map +1 -1
  59. package/dev/components/layout.js +7 -6
  60. package/dev/components/layout.js.map +1 -1
  61. package/dev/components/router.js +1 -1
  62. package/dev/components/router.js.map +1 -1
  63. package/dev/components/select.d.ts +0 -4
  64. package/dev/components/select.d.ts.map +1 -1
  65. package/dev/components/select.js +0 -7
  66. package/dev/components/select.js.map +1 -1
  67. package/dev/components/skeleton.d.ts.map +1 -1
  68. package/dev/components/skeleton.js +1 -2
  69. package/dev/components/skeleton.js.map +1 -1
  70. package/dev/components/split.d.ts +1 -1
  71. package/dev/components/split.d.ts.map +1 -1
  72. package/dev/components/split.js +9 -3
  73. package/dev/components/split.js.map +1 -1
  74. package/dev/components/text.js +3 -3
  75. package/dev/core/super-input.d.ts +0 -1
  76. package/dev/core/super-input.d.ts.map +1 -1
  77. package/dev/core/super-input.js +0 -3
  78. package/dev/core/super-input.js.map +1 -1
  79. package/package.json +7 -6
  80. package/src/alert.ts +11 -0
  81. package/src/avatar.ts +11 -0
  82. package/src/breath.ts +13 -0
  83. package/src/button.ts +11 -0
  84. package/src/card.ts +11 -0
  85. package/src/carousel.ts +11 -0
  86. package/src/components/alert.ts +282 -0
  87. package/src/components/avatar.ts +93 -0
  88. package/src/components/breath.ts +151 -0
  89. package/src/components/button.ts +276 -0
  90. package/src/components/card.ts +85 -0
  91. package/src/components/carousel.ts +166 -0
  92. package/src/components/details.ts +112 -0
  93. package/src/components/dialog.ts +160 -0
  94. package/src/components/divider.ts +44 -0
  95. package/src/components/dragbox.ts +126 -0
  96. package/src/components/flex.ts +65 -0
  97. package/src/components/form.ts +83 -0
  98. package/src/components/grid.ts +66 -0
  99. package/src/components/input.ts +71 -0
  100. package/src/components/layout.ts +84 -0
  101. package/src/components/link.ts +36 -0
  102. package/src/components/progress.ts +101 -0
  103. package/src/components/range.ts +409 -0
  104. package/src/components/rotate.ts +92 -0
  105. package/src/components/router.ts +264 -0
  106. package/src/components/select.ts +268 -0
  107. package/src/components/skeleton.ts +116 -0
  108. package/src/components/split.ts +190 -0
  109. package/src/components/switch.ts +176 -0
  110. package/src/components/text.ts +95 -0
  111. package/src/components/time.ts +77 -0
  112. package/src/components/tooltip.ts +118 -0
  113. package/src/components/typewriter.ts +147 -0
  114. package/src/core/global-style.ts +86 -0
  115. package/src/core/super-anchor.ts +52 -0
  116. package/src/core/super-input.ts +230 -0
  117. package/src/core/super-openable.ts +51 -0
  118. package/src/details.ts +11 -0
  119. package/src/dialog.ts +11 -0
  120. package/src/divider.ts +11 -0
  121. package/src/dragbox.ts +11 -0
  122. package/src/flex.ts +11 -0
  123. package/src/form.ts +11 -0
  124. package/src/grid.ts +11 -0
  125. package/src/index.ts +28 -0
  126. package/src/input.ts +13 -0
  127. package/src/layout.ts +12 -0
  128. package/src/link.ts +13 -0
  129. package/src/progress.ts +12 -0
  130. package/src/range.ts +13 -0
  131. package/src/rotate.ts +13 -0
  132. package/src/router.ts +12 -0
  133. package/src/select.ts +13 -0
  134. package/src/skeleton.ts +13 -0
  135. package/src/split.ts +13 -0
  136. package/src/switch.ts +13 -0
  137. package/src/text.ts +13 -0
  138. package/src/time.ts +13 -0
  139. package/src/tooltip.ts +13 -0
  140. package/src/typewriter.ts +11 -0
  141. package/vscode.html-custom-data.json +1 -1
  142. package/web-types.json +1 -1
@@ -0,0 +1,36 @@
1
+ import { godown } from "@godown/element/decorators/godown.js";
2
+ import { property } from "lit/decorators.js";
3
+
4
+ import SuperAnchor from "../core/super-anchor.js";
5
+ import Router from "./router.js";
6
+
7
+ const protoName = "link";
8
+
9
+ /**
10
+ * {@linkcode Link} is used for link jumping.
11
+ *
12
+ * Set `type` to `"push" `or `"replace"`,
13
+ * will invoke the history api and trigger the {@linkcode Router.updateAll}.
14
+ *
15
+ * @category navigation
16
+ */
17
+ @godown(protoName)
18
+ class Link extends SuperAnchor {
19
+ /**
20
+ * If "push", call `history.pushState`.
21
+ *
22
+ * If "replace", call `history.replaceState`
23
+ */
24
+ @property()
25
+ type: "push" | "replace" | "normal" = "normal";
26
+
27
+ protected _handleClick(e: MouseEvent) {
28
+ if (this.type === "push" || this.type === "replace") {
29
+ e.preventDefault();
30
+ (history[`${this.type}State`])(null, "", this.href);
31
+ Router.updateAll();
32
+ }
33
+ }
34
+ }
35
+
36
+ export default Link;
@@ -0,0 +1,101 @@
1
+ import { godown } from "@godown/element/decorators/godown.js";
2
+ import { styles } from "@godown/element/decorators/styles.js";
3
+ import { css, html } from "lit";
4
+ import { property } from "lit/decorators.js";
5
+
6
+ import { cssGlobalVars, GlobalStyle } from "../core/global-style.js";
7
+
8
+ const protoName = "progress";
9
+
10
+ /**
11
+ * {@linkcode Progress} similar to `<progress>`.
12
+ *
13
+ * @category feedback
14
+ */
15
+ @godown(protoName)
16
+ @styles(css`
17
+ :host {
18
+ width: 100%;
19
+ height: 0.5em;
20
+ display: inline-block;
21
+ border-radius: 0.25em;
22
+ background: var(${cssGlobalVars.passive});
23
+ color: var(${cssGlobalVars.active});
24
+ }
25
+
26
+ [part="root"] {
27
+ height: inherit;
28
+ z-index: 1;
29
+ position: relative;
30
+ border-radius: inherit;
31
+ }
32
+
33
+ [part="value"] {
34
+ position: absolute;
35
+ z-index: 2;
36
+ top: 0;
37
+ left: 0;
38
+ height: 100%;
39
+ border-radius: inherit;
40
+ transition: all 0.3s;
41
+ animation: progress 1.8s ease-in-out infinite alternate;
42
+ background: currentColor;
43
+ }
44
+
45
+ @keyframes progress {
46
+ from {
47
+ left: 0;
48
+ }
49
+ to {
50
+ left: 80%;
51
+ }
52
+ }
53
+
54
+ .static [part="value"] {
55
+ animation: none;
56
+ }
57
+ `)
58
+ class Progress extends GlobalStyle {
59
+ /**
60
+ * Maximum.
61
+ */
62
+ @property({ type: Number })
63
+ max = 1;
64
+ /**
65
+ * Minimum.
66
+ */
67
+ @property({ type: Number })
68
+ min = 0;
69
+ /**
70
+ * Input value.
71
+ */
72
+ @property({ type: Number, reflect: true })
73
+ value = null;
74
+
75
+ protected render() {
76
+ let width = 20;
77
+ let className: string;
78
+ if (this.value !== null) {
79
+ width = this.parsePercent(this.value);
80
+ className = "static";
81
+ }
82
+ return html`<div part="root" class="${className}">
83
+ <i part="value" style="width:${width}%;"></i>
84
+ </div>`;
85
+ }
86
+
87
+ /**
88
+ * Convert s to a percentage without a percent sign.
89
+ *
90
+ * @param s String or number to convert.
91
+ * @returns Percentage without a percent sign.
92
+ */
93
+ parsePercent(s: string | number = "0"): number {
94
+ if (String(s).includes("%")) {
95
+ return parseFloat(String(s));
96
+ }
97
+ return (parseFloat(String(s)) / (this.max - this.min)) * 100;
98
+ }
99
+ }
100
+
101
+ export default Progress;
@@ -0,0 +1,409 @@
1
+ import { godown } from "@godown/element/decorators/godown.js";
2
+ import { part } from "@godown/element/decorators/part.js";
3
+ import { styles } from "@godown/element/decorators/styles.js";
4
+ import { classList } from "@godown/element/directives/class-list.js";
5
+ import { joinProperties } from "@godown/element/tools/css.js";
6
+ import { isNil } from "@godown/element/tools/lib.js";
7
+ import { css, html } from "lit";
8
+ import { property, queryAll, state } from "lit/decorators.js";
9
+
10
+ import { cssGlobalVars, scopePrefix } from "../core/global-style.js";
11
+ import SuperInput from "../core/super-input.js";
12
+
13
+ const protoName = "range";
14
+ const cssScope = scopePrefix(protoName);
15
+
16
+ /**
17
+ * {@linkcode Range} is similar to `<input type="range">`.
18
+ *
19
+ * Value accepts number, or array.
20
+ *
21
+ * Number has 1 handle, the array has the number of its elements and the minimum is 2.
22
+ *
23
+ * @category input
24
+ */
25
+ @godown(protoName)
26
+ @styles(
27
+ css`
28
+ :host {
29
+ ${cssScope}-handle-active: var(${cssGlobalVars._colors.blue[7]});
30
+ ${cssScope}-track-width: .5em;
31
+ background: var(${cssGlobalVars.input}-background);
32
+ width: var(${cssGlobalVars.input}-width);
33
+ display: inline-block;
34
+ margin: 0.25em 0;
35
+ }
36
+
37
+ :host([vertical]) {
38
+ height: var(${cssGlobalVars.input}-width);
39
+ width: fit-content;
40
+ }
41
+
42
+ :host(:not([disabled])) .last-focus {
43
+ z-index: 1;
44
+ ${cssScope}-handle-scale:1.05;
45
+ background: var(${cssScope}-handle-active);
46
+ }
47
+
48
+ [part="root"] {
49
+ min-height:inherit;
50
+ position: relative;
51
+ border-radius: inherit;
52
+ width: 100%;
53
+ --from: 0%;
54
+ --to: 50%;
55
+ height: var(${cssScope}-track-width);
56
+ }
57
+
58
+ [part="track"] {
59
+ height: 100%;
60
+ min-height:inherit;
61
+ display: flex;
62
+ position: absolute;
63
+ pointer-events: none;
64
+ border-radius: inherit;
65
+ justify-content: space-between;
66
+ left: min(var(--from), var(--to));
67
+ background: var(${cssGlobalVars.active});
68
+ width: max(calc(var(--to) - var(--from)), calc(var(--from) - var(--to)));
69
+ }
70
+
71
+ [part="handle"] {
72
+ width: 1em;
73
+ height: 1em;
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ user-select: none;
78
+ position: absolute;
79
+ border: 0.1em solid;
80
+ border-radius: 50%;
81
+ transform-origin: 0% 25%;
82
+ transform: scale(var(${cssScope}-handle-scale, 1)) translate(-50%, -25%);
83
+ background: var(${cssGlobalVars.active});
84
+ border-color: var(${cssGlobalVars.input}-control);
85
+ }
86
+ `,
87
+ css`
88
+ .vertical {
89
+ height: 100%;
90
+ width: var(${cssScope}-track-width);
91
+ }
92
+
93
+ .vertical i {
94
+ transform: translate(-25%, -50%);
95
+ }
96
+
97
+ .vertical [part="track"] {
98
+ width: 100%;
99
+ height: max(calc(var(--to) - var(--from)), calc(var(--from) - var(--to)));
100
+ top: min(var(--from), var(--to));
101
+ left: 0;
102
+ }
103
+ `,
104
+ css`
105
+ [part="handle"] {
106
+ left: var(--handle);
107
+ top: 0;
108
+ }
109
+
110
+ .vertical [part="handle"] {
111
+ top: var(--handle);
112
+ left: 0;
113
+ }
114
+ `,
115
+ )
116
+ class Range extends SuperInput {
117
+ /**
118
+ * Minimum value.
119
+ */
120
+ @property({ type: Number })
121
+ min = 0;
122
+
123
+ /**
124
+ * Maximum value.
125
+ */
126
+ @property({ type: Number })
127
+ max = 100;
128
+
129
+ /**
130
+ * Sliding step length.
131
+ */
132
+ @property({ type: Number })
133
+ step: number;
134
+
135
+ /**
136
+ * Display vertically
137
+ */
138
+ @property({ type: Boolean, reflect: true })
139
+ vertical: boolean;
140
+
141
+ /**
142
+ * When `this.range` is true, it should be [number, number], otherwise number.
143
+ */
144
+ @property({ type: Array })
145
+ value: number | number[];
146
+ /**
147
+ * The default of `this.value`.
148
+ */
149
+ @property({ type: Array })
150
+ default: typeof this.value;
151
+
152
+ @part("root")
153
+ _root: HTMLElement;
154
+
155
+ @queryAll("[part=handle]")
156
+ _handles: NodeListOf<HTMLElement>;
157
+
158
+ @state()
159
+ lastFocus?: number;
160
+
161
+ /**
162
+ * Returns true when the second number is greater than the first number
163
+ */
164
+ get reverse() {
165
+ return this.range ? this.value[0] > this.value[1] : false;
166
+ }
167
+
168
+ /**
169
+ * If value is array.
170
+ */
171
+ get range(): boolean {
172
+ return Array.isArray(this.value);
173
+ }
174
+
175
+ /**
176
+ * Return values in the form of an array.
177
+ */
178
+ get rangeValue(): number[] {
179
+ return (this.range ? this.value : [this.value]) as number[];
180
+ }
181
+
182
+ /**
183
+ * @param len Minimum result length.
184
+ * @param value Fill value.
185
+ * @returns Array with length of len.
186
+ */
187
+ padValue(len: number, value = 0): number[] {
188
+ const { rangeValue } = this;
189
+ const miss = len - rangeValue.length;
190
+ if (miss > 0) {
191
+ return new Array(miss).fill(value).concat(rangeValue);
192
+ }
193
+ return rangeValue;
194
+ }
195
+
196
+ protected render() {
197
+ const rangeValue = this.padValue(2);
198
+ const from = Math.min(...rangeValue);
199
+ const to = Math.max(...rangeValue);
200
+ const gap = this.max - this.min;
201
+
202
+ return html`<div
203
+ part="root"
204
+ class="${
205
+ classList({
206
+ vertical: this.vertical,
207
+ range: this.range,
208
+ reverse: this.reverse,
209
+ })
210
+ }"
211
+ @mousedown="${this.disabled ? null : this._handleMousedownRoot}"
212
+ style="${
213
+ joinProperties({
214
+ "--from": `${((from - this.min) / gap) * 100}%`,
215
+ "--to": `${((to - this.min) / gap) * 100}%`,
216
+ ...(this.range
217
+ ? Object.fromEntries(
218
+ rangeValue.map((value, index) => [
219
+ `--handle-${index}`,
220
+ `${((value - this.min) / gap) * 100}%`,
221
+ ]),
222
+ )
223
+ : {}),
224
+ })
225
+ }"><div part="track"></div>
226
+ ${
227
+ this.range
228
+ ? (this.value as number[]).map((_, index) => this.renderHandle(index))
229
+ : this.renderHandle(0)
230
+ }
231
+ </div>`;
232
+ }
233
+
234
+ protected renderHandle(index: number) {
235
+ const { range } = this;
236
+ const end = !range || index === (this.value as number[]).length - 1;
237
+ return html`<i
238
+ tabindex="0"
239
+ part="handle"
240
+ class="${classList({ "last-focus": this.lastFocus === index })}"
241
+ @mousedown="${this.disabled ? null : this.createMouseDown(index)}"
242
+ @focus="${this.disabled ? null : () => this.focusHandle(index)}"
243
+ @blur="${this.disabled ? null : this.blurHandle}"
244
+ style="--handle:var(--${
245
+ /* In single-handle mod or end, it is max value */
246
+ (!range && end) ? `to` : `handle-${index}`})"
247
+ ></i>
248
+ `;
249
+ }
250
+
251
+ private _keydownEvent: EventListenerOrEventListenerObject;
252
+
253
+ focusHandle(index: number) {
254
+ this.lastFocus = index;
255
+ const handleItem = this._handles.item(index);
256
+ handleItem?.focus();
257
+ if (!this._keydownEvent) {
258
+ this._keydownEvent = this.events.add(document, "keydown", this.createKeydownEvent(index));
259
+ }
260
+ }
261
+
262
+ blurHandle() {
263
+ this.lastFocus = undefined;
264
+ this._keydownEvent = this.events.remove(document, "keydown", this._keydownEvent);
265
+ }
266
+
267
+ protected createKeydownEvent(index: number) {
268
+ if (!this.range) {
269
+ index = 0;
270
+ }
271
+ return (e: KeyboardEvent) => {
272
+ if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
273
+ e.preventDefault();
274
+ this.createSetValue(index)(old => old - this.step);
275
+ } else if (e.key === "ArrowRight" || e.key === "ArrowUp") {
276
+ e.preventDefault();
277
+ this.createSetValue(index)(old => old + this.step);
278
+ }
279
+ };
280
+ }
281
+
282
+ createMouseDown(index) {
283
+ return (e) => {
284
+ this.focusHandle(index);
285
+ this.createMousedownListener(this.createSetValue(index))(e);
286
+ };
287
+ }
288
+
289
+ protected _handleMousedownEnd(e: MouseEvent) {
290
+ this.lastFocus = 0;
291
+ this.createMousedownListener(this.setEnd)(e);
292
+ this.focusHandle(0);
293
+ }
294
+
295
+ createSetValue(index?: number) {
296
+ return (numberOrModifier: number | ((value: number) => number)) => {
297
+ const number = typeof numberOrModifier === "number"
298
+ ? this.normalizeValue(numberOrModifier)
299
+ : numberOrModifier(this.rangeValue[index]);
300
+ let newValue: any = number;
301
+ if (this.range) {
302
+ newValue = [...this.rangeValue];
303
+ newValue[index] = number;
304
+ }
305
+ this.value = newValue;
306
+ };
307
+ }
308
+
309
+ setEnd(value: number) {
310
+ this.createSetValue((this.value as any)?.length - 1 || 0)(value);
311
+ }
312
+
313
+ /**
314
+ * Compute value from event.
315
+ */
316
+ protected _computeValue(e: MouseEvent) {
317
+ const rect = this._root.getBoundingClientRect();
318
+ const div = this.vertical ? (e.clientY - rect.top) / rect.height : (e.clientX - rect.left) / rect.width;
319
+ const value = Math.round((div * (this.max - this.min)) / this.step) * this.step;
320
+ return this.normalizeValue(value);
321
+ }
322
+
323
+ /**
324
+ * Ensure that the values do not exceed the range of max and min.
325
+ */
326
+ protected normalizeValue(value: number) {
327
+ if (value > this.max) { value -= this.step; }
328
+ else if (value < this.min) { value += this.step; }
329
+ return value;
330
+ }
331
+
332
+ protected _handleMousedownRoot(e: MouseEvent) {
333
+ const value = this._computeValue(e);
334
+ const index = this.range
335
+ ? this.rangeValue.reduce((acc, item, index) => {
336
+ const diff = Math.abs(value - item);
337
+ const prevDiff = Math.abs(value - this.rangeValue[acc]);
338
+ return diff < prevDiff ? index : acc;
339
+ }, 0)
340
+ : 0;
341
+
342
+ const set = this.createSetValue(index);
343
+ set(value);
344
+ this.createMousedownListener(set)(e);
345
+ this.focusHandle(index);
346
+ }
347
+
348
+ protected createMousedownListener(mouseMoveCallback: (arg0: number) => void) {
349
+ return (e: MouseEvent) => {
350
+ e.preventDefault();
351
+ e.stopPropagation();
352
+ const move = this.createMousemoveListener(mouseMoveCallback);
353
+ this.events.add(document, "mousemove", move);
354
+ const stop = () => {
355
+ this.events.remove(document, "mousemove", move);
356
+ this.events.remove(document, "mouseup", stop);
357
+ };
358
+ this.events.add(document, "mouseup", stop);
359
+ };
360
+ }
361
+
362
+ protected createMousemoveListener(callback: (arg0: number) => void) {
363
+ return (e: MouseEvent) => {
364
+ const value = this._computeValue(e);
365
+ if (value > this.max || value < this.min) {
366
+ return;
367
+ }
368
+ callback?.call(this, value);
369
+ };
370
+ }
371
+
372
+ protected _connectedInit() {
373
+ const gap = this.max - this.min;
374
+ this.step ||= gap / 100;
375
+ if (isNil(this.value)) {
376
+ if (!isNil(this.default)) {
377
+ this.value = this.default;
378
+ } else {
379
+ this.value = Math.round(gap / 2 / this.step) * this.step;
380
+ }
381
+ }
382
+ this.default ??= this.value;
383
+ }
384
+
385
+ reset() {
386
+ this.value = this.default;
387
+ }
388
+
389
+ swap() {
390
+ if (this.range) {
391
+ const [a, b] = this.value as [number, number];
392
+ this.value = [b, a];
393
+ }
394
+ }
395
+
396
+ sort() {
397
+ this.value = this.toSorted();
398
+ }
399
+
400
+ toSorted(): typeof this.value {
401
+ if (this.range) {
402
+ const [a, b] = this.value as [number, number];
403
+ return a > b ? [b, a] : [a, b];
404
+ }
405
+ return this.value;
406
+ }
407
+ }
408
+
409
+ export default Range;
@@ -0,0 +1,92 @@
1
+ import { godown } from "@godown/element/decorators/godown.js";
2
+ import { part } from "@godown/element/decorators/part.js";
3
+ import { styles } from "@godown/element/decorators/styles.js";
4
+ import { htmlSlot } from "@godown/element/directives/html-slot.js";
5
+ import { css, html } from "lit";
6
+
7
+ import { GlobalStyle, scopePrefix } from "../core/global-style.js";
8
+
9
+ const protoName = "rotate";
10
+ const cssScope = scopePrefix(protoName);
11
+
12
+ /**
13
+ * {@linkcode Rotate} Make child elements rotate.
14
+ *
15
+ * @category wrapper
16
+ */
17
+ @godown(protoName)
18
+ @styles(
19
+ css`
20
+ :host {
21
+ display: block;
22
+ width: -moz-fit-content;
23
+ width: fit-content;
24
+ transition: all 0.5s ease-in-out;
25
+ ${cssScope}--padding: .75em;
26
+ ${cssScope}--offset: .5em;
27
+ }
28
+
29
+ div {
30
+ position: relative;
31
+ transition: inherit;
32
+ transition-property: transform;
33
+ }
34
+
35
+ i {
36
+ width: 100%;
37
+ height: 100%;
38
+ position: absolute;
39
+ top: 0;
40
+ box-sizing: content-box;
41
+ padding: var(${cssScope}--offset);
42
+ margin: calc(-1 * var(${cssScope}--offset));
43
+ }
44
+
45
+ [part="slot"]{
46
+ z-index: 2;
47
+ }
48
+ `,
49
+ )
50
+ class Rotate extends GlobalStyle {
51
+ @part("root")
52
+ _root: HTMLElement;
53
+
54
+ protected render() {
55
+ return html`<div part="root">
56
+ <div part="slot" @mousemove="${this._handleRotate}">${htmlSlot()}</div>
57
+ <i @mouseleave="${this.reset}"></i>
58
+ </div>`;
59
+ }
60
+
61
+ reset() {
62
+ this._root.style.removeProperty("transform");
63
+ this._root.style.removeProperty("transition");
64
+ }
65
+
66
+ protected _handleRotate(e: MouseEvent) {
67
+ const { rotateX, rotateY } = this._computeOffset(e);
68
+ this._root.style.setProperty("transform", `rotateX(${rotateX}rad) rotateY(${rotateY}rad)`);
69
+ this._root.style.setProperty("transition", "0s");
70
+ }
71
+
72
+ /**
73
+ * Compute offset.
74
+ * ```
75
+ * `rotateX(${rotateX}rad) rotateY(${rotateY}rad)`
76
+ * ```
77
+ * @param e Mouse move event.
78
+ * @returns rotateX, rotateY
79
+ */
80
+ _computeOffset(e: MouseEvent) {
81
+ const { left, top, width, height } = this._root.getBoundingClientRect();
82
+ const { clientX, clientY } = e;
83
+ const offsetX = clientX - left;
84
+ const offsetY = clientY - top;
85
+
86
+ const rotateX = -(offsetY - height / 2) / height / 2;
87
+ const rotateY = (offsetX - width / 2) / width / 2;
88
+ return { rotateX, rotateY };
89
+ }
90
+ }
91
+
92
+ export default Rotate;