@mdui/shared 1.0.0

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 (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -0
  3. package/controllers/form.d.ts +53 -0
  4. package/controllers/form.js +214 -0
  5. package/controllers/has-slot.d.ts +17 -0
  6. package/controllers/has-slot.js +46 -0
  7. package/controllers/hover.d.ts +34 -0
  8. package/controllers/hover.js +62 -0
  9. package/decorators/default-value.d.ts +16 -0
  10. package/decorators/default-value.js +40 -0
  11. package/decorators/watch.d.ts +17 -0
  12. package/decorators/watch.js +37 -0
  13. package/helpers/animate.d.ts +2 -0
  14. package/helpers/animate.js +37 -0
  15. package/helpers/breakpoint.d.ts +49 -0
  16. package/helpers/breakpoint.js +102 -0
  17. package/helpers/decorator.d.ts +16 -0
  18. package/helpers/decorator.js +18 -0
  19. package/helpers/delay.d.ts +4 -0
  20. package/helpers/delay.js +6 -0
  21. package/helpers/event.d.ts +7 -0
  22. package/helpers/event.js +17 -0
  23. package/helpers/modal.d.ts +12 -0
  24. package/helpers/modal.js +52 -0
  25. package/helpers/motion.d.ts +13 -0
  26. package/helpers/motion.js +20 -0
  27. package/helpers/observeResize.d.ts +16 -0
  28. package/helpers/observeResize.js +57 -0
  29. package/helpers/queue.d.ts +18 -0
  30. package/helpers/queue.js +25 -0
  31. package/helpers/scroll.d.ts +14 -0
  32. package/helpers/scroll.js +28 -0
  33. package/helpers/slot.d.ts +10 -0
  34. package/helpers/slot.js +31 -0
  35. package/helpers/tabbable.d.ts +8 -0
  36. package/helpers/tabbable.js +82 -0
  37. package/helpers/template.d.ts +2 -0
  38. package/helpers/template.js +2 -0
  39. package/helpers/throttle.d.ts +14 -0
  40. package/helpers/throttle.js +28 -0
  41. package/helpers/uniqueId.d.ts +4 -0
  42. package/helpers/uniqueId.js +7 -0
  43. package/icons/arrow-right.d.ts +11 -0
  44. package/icons/arrow-right.js +14 -0
  45. package/icons/cancel--outlined.d.ts +11 -0
  46. package/icons/cancel--outlined.js +14 -0
  47. package/icons/check-box-outline-blank.d.ts +11 -0
  48. package/icons/check-box-outline-blank.js +14 -0
  49. package/icons/check-box.d.ts +11 -0
  50. package/icons/check-box.js +14 -0
  51. package/icons/check.d.ts +11 -0
  52. package/icons/check.js +14 -0
  53. package/icons/circle.d.ts +11 -0
  54. package/icons/circle.js +14 -0
  55. package/icons/clear.d.ts +11 -0
  56. package/icons/clear.js +14 -0
  57. package/icons/error.d.ts +11 -0
  58. package/icons/error.js +14 -0
  59. package/icons/indeterminate-check-box.d.ts +11 -0
  60. package/icons/indeterminate-check-box.js +14 -0
  61. package/icons/radio-button-unchecked.d.ts +11 -0
  62. package/icons/radio-button-unchecked.js +14 -0
  63. package/icons/shared/style.d.ts +1 -0
  64. package/icons/shared/style.js +2 -0
  65. package/icons/shared/svg-tag.d.ts +2 -0
  66. package/icons/shared/svg-tag.js +3 -0
  67. package/icons/visibility-off.d.ts +11 -0
  68. package/icons/visibility-off.js +14 -0
  69. package/icons/visibility.d.ts +11 -0
  70. package/icons/visibility.js +14 -0
  71. package/lit-styles/component-style.d.ts +1 -0
  72. package/lit-styles/component-style.js +2 -0
  73. package/mixin.less +380 -0
  74. package/mixins/anchor.d.ts +22 -0
  75. package/mixins/anchor.js +25 -0
  76. package/mixins/focusable.d.ts +19 -0
  77. package/mixins/focusable.js +251 -0
  78. package/mixins/scrollBehavior.d.ts +24 -0
  79. package/mixins/scrollBehavior.js +202 -0
  80. package/package.json +35 -0
package/mixin.less ADDED
@@ -0,0 +1,380 @@
1
+ @mdui-root-font-size: 16;
2
+
3
+ @mdui-breakpoint-xs: 0px;
4
+ @mdui-breakpoint-xs-max: 599.98px;
5
+ @mdui-breakpoint-sm: 600px;
6
+ @mdui-breakpoint-sm-max: 839.98px;
7
+ @mdui-breakpoint-md: 840px;
8
+ @mdui-breakpoint-md-max: 1079.98px;
9
+ @mdui-breakpoint-lg: 1080px;
10
+ @mdui-breakpoint-lg-max: 1439.98px;
11
+ @mdui-breakpoint-xl: 1440px;
12
+ @mdui-breakpoint-xl-max: 1919.98px;
13
+ @mdui-breakpoint-xxl: 1920px;
14
+
15
+ // px 值转 rem
16
+ //
17
+ // 输入: .px(font-size: 24)
18
+ // 输出: font-size: 1.5rem
19
+ .px(@name, @px) {
20
+ @{name}: unit((@px / @mdui-root-font-size), rem);
21
+ }
22
+
23
+ // 系统变量
24
+ //
25
+ // 输入: .sys(font-size, typescale-label-large-size)
26
+ // 输出: font-size: var(--mdui-typescale-label-large-size, 0.875rem)
27
+ .sys(@name, @value) {
28
+ @{name}: ~"var(--mdui-@{value})";
29
+ }
30
+
31
+ // 颜色值
32
+ //
33
+ // 输入: .color(on-primary, 30%, background-color)
34
+ // 输出: background-color: rgb(var(--mdui-color-on-primary, 255, 255, 255))
35
+ .color(@rgb, @opacity: 1, @name: color) {
36
+ @cssVariable: "--mdui-color-@{rgb}";
37
+
38
+ & when (@opacity =1) {
39
+ @{name}: ~"rgb(var(@{cssVariable}))";
40
+ }
41
+
42
+ & when not (@opacity =1) {
43
+ @{name}: ~"rgba(var(@{cssVariable}), @{opacity})";
44
+ }
45
+ }
46
+
47
+ .background-color(@rgb, @opacity: 1) {
48
+ .color(@rgb, @opacity, background-color);
49
+ }
50
+
51
+ .border-color(@rgb, @opacity: 1) {
52
+ .color(@rgb, @opacity, border-color);
53
+ }
54
+
55
+ .stroke(@rgb, @opacity: 1) {
56
+ .color(@rgb, @opacity, stroke);
57
+ }
58
+
59
+ .caret-color(@rgb, @opacity: 1) {
60
+ .color(@rgb, @opacity, caret-color);
61
+ }
62
+
63
+ // 设置边框
64
+ //
65
+ // 输入: .border(1px, solid, primary, border)
66
+ // 输出: border: 1px solid rgb(103,80,164)
67
+ //
68
+ // 输入: .border(1px, solid, primary, 30%, border-top)
69
+ // 输出: border-top: 1px solid rgba(103,80,164,30%)
70
+ .border(@width, @style, @rgb, @opacity: 1, @name: border) {
71
+ @cssVariable: "--mdui-color-@{rgb}";
72
+
73
+ & when (@opacity =1) {
74
+ & when (isnumber(@width)) {
75
+ @widthRem: unit((@width / @mdui-root-font-size), rem);
76
+
77
+ @{name}: ~"@{widthRem} @{style} rgb(var(@{cssVariable}))";
78
+ }
79
+
80
+ & when not (isnumber(@width)) {
81
+ @{name}: ~"@{width} @{style} rgb(var(@{cssVariable}))";
82
+ }
83
+ }
84
+
85
+ & when not (@opacity =1) {
86
+ & when (isnumber(@width)) {
87
+ @widthRem: unit((@width / @mdui-root-font-size), rem);
88
+
89
+ @{name}: ~"@{widthRem} @{style} rgba(var(@{cssVariable}), @{opacity})";
90
+ }
91
+
92
+ & when not (isnumber(@width)) {
93
+ @{name}: ~"@{width} @{style} rgba(var(@{cssVariable}), @{opacity})";
94
+ }
95
+ }
96
+ }
97
+
98
+ .border-top(@width, @style, @rgb, @opacity: 1) {
99
+ .border(@width, @style, @rgb, @opacity, border-top);
100
+ }
101
+
102
+ .border-right(@width, @style, @rgb, @opacity: 1) {
103
+ .border(@width, @style, @rgb, @opacity, border-right);
104
+ }
105
+
106
+ .border-bottom(@width, @style, @rgb, @opacity: 1) {
107
+ .border(@width, @style, @rgb, @opacity, border-bottom);
108
+ }
109
+
110
+ .border-left(@width, @style, @rgb, @opacity: 1) {
111
+ .border(@width, @style, @rgb, @opacity, border-left);
112
+ }
113
+
114
+ .border-width(@width) {
115
+ .px(border-width, @width);
116
+ }
117
+
118
+ // 设置 border-radius
119
+ //
120
+ // 输入:.corner(medium)
121
+ // 输出:border-radius: 0.75rem;
122
+ .shape-corner(@value, @placement: null) {
123
+ @cssVariable: "--mdui-shape-corner-@{value}";
124
+
125
+ & when (@placement =null) {
126
+ border-radius: ~"var(@{cssVariable})";
127
+ }
128
+
129
+ & when (@placement =top) {
130
+ border-radius: ~"var(@{cssVariable}) var(@{cssVariable}) 0 0";
131
+ }
132
+
133
+ & when (@placement =bottom) {
134
+ border-radius: ~"0 0 var(@{cssVariable}) var(@{cssVariable})";
135
+ }
136
+
137
+ & when (@placement =start) {
138
+ border-radius: ~"var(@{cssVariable}) 0 0 var(@{cssVariable})";
139
+ }
140
+
141
+ & when (@placement =end) {
142
+ border-radius: ~"0 var(@{cssVariable}) var(@{cssVariable}) 0";
143
+ }
144
+
145
+ & when (@placement =top-start) {
146
+ border-top-left-radius: ~"var(@{cssVariable})";
147
+ }
148
+
149
+ & when (@placement = top-end) {
150
+ border-top-right-radius: ~"var(@{cssVariable})";
151
+ }
152
+
153
+ & when (@placement = bottom-start) {
154
+ border-bottom-left-radius: ~"var(@{cssVariable})";
155
+ }
156
+
157
+ & when (@placement = bottom-end) {
158
+ border-bottom-right-radius: ~"var(@{cssVariable})";
159
+ }
160
+ }
161
+
162
+ .border-radius(@value, @placement: null) {
163
+ & when (@placement =null) {
164
+ .px(border-radius, @value);
165
+ }
166
+
167
+ & when (@placement =top) {
168
+ .px(border-top-left-radius, @value);
169
+ .px(border-top-right-radius, @value);
170
+ }
171
+
172
+ & when (@placement =bottom) {
173
+ .px(border-bottom-right-radius, @value);
174
+ .px(border-bottom-left-radius, @value);
175
+ }
176
+
177
+ & when (@placement =start) {
178
+ .px(border-top-left-radius, @value);
179
+ .px(border-bottom-left-radius, @value);
180
+ }
181
+
182
+ & when (@placement =end) {
183
+ .px(border-top-right-radius, @value);
184
+ .px(border-bottom-right-radius, @value);
185
+ }
186
+ }
187
+
188
+ // padding 或 margin 值中的 px 转 rem。传 1 - 4 个参数都行
189
+ //
190
+ // 输入: .padding(24)
191
+ // 输出: padding: 1.5rem
192
+ .padding(@top, @right: null, @bottom: null, @left: null, @name: padding) {
193
+ & when (@right =null) and (@bottom =null) and (@left =null) {
194
+ @{name}: unit((@top / @mdui-root-font-size), rem);
195
+ }
196
+
197
+ & when (not(@right =null)) and (@bottom =null) and (@left =null) {
198
+ @{name}: unit((@top / @mdui-root-font-size), rem) unit((@right / @mdui-root-font-size), rem);
199
+ }
200
+
201
+ & when (not(@right =null)) and (not(@bottom =null)) and (@left =null) {
202
+ @{name}: unit((@top / @mdui-root-font-size), rem) unit((@right / @mdui-root-font-size), rem) unit((@bottom / @mdui-root-font-size), rem);
203
+ }
204
+
205
+ & when (not(@right =null)) and (not(@bottom =null)) and (not(@left =null)) {
206
+ @{name}: unit((@top / @mdui-root-font-size), rem) unit((@right / @mdui-root-font-size), rem) unit((@bottom / @mdui-root-font-size), rem) unit((@left / @mdui-root-font-size), rem);
207
+ }
208
+ }
209
+
210
+ .margin(@top, @right: null, @bottom: null, @left: null) {
211
+ .padding(@top, @right, @bottom, @left, margin);
212
+ }
213
+
214
+ .padding-top(@value) {
215
+ .px(padding-top, @value);
216
+ }
217
+
218
+ .padding-right(@value) {
219
+ .px(padding-right, @value);
220
+ }
221
+
222
+ .padding-bottom(@value) {
223
+ .px(padding-bottom, @value);
224
+ }
225
+
226
+ .padding-left(@value) {
227
+ .px(padding-left, @value);
228
+ }
229
+
230
+ .margin-top(@value) {
231
+ .px(margin-top, @value);
232
+ }
233
+
234
+ .margin-right(@value) {
235
+ .px(margin-right, @value);
236
+ }
237
+
238
+ .margin-bottom(@value) {
239
+ .px(margin-bottom, @value);
240
+ }
241
+
242
+ .margin-left(@value) {
243
+ .px(margin-left, @value);
244
+ }
245
+
246
+ // 设置 box-shadow,使用该 mixin 时,需要自行在元素上添加 transition-property: box-shadow
247
+ .elevation(@value) {
248
+ .sys(box-shadow, ~"elevation-level@{value}");
249
+ }
250
+
251
+ .state-layer-color(@rgb) {
252
+ .sys(--mdui-comp-ripple-state-layer-color, ~"color-@{rgb}");
253
+ }
254
+
255
+ .translate(@x, @y) {
256
+ transform: translate(unit((@x / @mdui-root-font-size), rem), unit((@y / @mdui-root-font-size), rem));
257
+ }
258
+
259
+ .translate-x(@x) {
260
+ transform: translateX(unit((@x / @mdui-root-font-size), rem));
261
+ }
262
+
263
+ .translate-y(@y) {
264
+ transform: translateY(unit((@y / @mdui-root-font-size), rem));
265
+ }
266
+
267
+ .font-size(@value) {
268
+ & when (isnumber(@value)) {
269
+ .px(font-size, @value);
270
+ }
271
+
272
+ & when not (isnumber(@value)) {
273
+ .sys(font-size, ~"typescale-@{value}-size");
274
+ }
275
+ }
276
+
277
+ .font-weight(@value) {
278
+ .sys(font-weight, ~"typescale-@{value}-weight");
279
+ }
280
+
281
+ .letter-spacing(@value) {
282
+ .sys(letter-spacing, ~"typescale-@{value}-tracking");
283
+ }
284
+
285
+ .height(@value) {
286
+ .px(height, @value);
287
+ }
288
+
289
+ .line-height(@value) {
290
+ & when (isnumber(@value)) {
291
+ .px(line-height, @value);
292
+ }
293
+
294
+ & when not (isnumber(@value)) {
295
+ .sys(line-height, ~"typescale-@{value}-line-height");
296
+ }
297
+ }
298
+
299
+ .typescale(@name) {
300
+ .font-size(@name);
301
+ .font-weight(@name);
302
+ .letter-spacing(@name);
303
+ .line-height(@name);
304
+ }
305
+
306
+ .width(@value) {
307
+ .px(width, @value);
308
+ }
309
+
310
+ .min-width(@value) {
311
+ .px(min-width, @value);
312
+ }
313
+
314
+ .max-width(@value) {
315
+ .px(max-width, @value);
316
+ }
317
+
318
+ .min-height(@value) {
319
+ .px(min-height, @value);
320
+ }
321
+
322
+ .top(@value) {
323
+ .px(top, @value);
324
+ }
325
+
326
+ .right(@value) {
327
+ .px(right, @value);
328
+ }
329
+
330
+ .bottom(@value) {
331
+ .px(bottom, @value);
332
+ }
333
+
334
+ .left(@value) {
335
+ .px(left, @value);
336
+ }
337
+
338
+ .opacity(@value) {
339
+ .sys(opacity, ~"state-layer-@{value}");
340
+ }
341
+
342
+ .animation-timing-function(@value) {
343
+ .sys(animation-timing-function, ~"motion-easing-@{value}");
344
+ }
345
+
346
+ // 生成 transition
347
+ //
348
+ // @duration 为 short1、short2 等
349
+ // @timing-function 为 standard、standard-accelerate、emphasized 等
350
+ // @delay 可以为整数,表示毫秒;也可以为 short1、short2 等
351
+ .transition(@property, @duration, @timing-function, @delay: 0) {
352
+ @durationVariable: "--mdui-motion-duration-@{duration}";
353
+ @timingFunctionVariable: "--mdui-motion-easing-@{timing-function}";
354
+
355
+ & when (@delay =0) {
356
+ transition: ~"@{property} var(@{durationVariable}) var(@{timingFunctionVariable})";
357
+ }
358
+
359
+ & when not (@delay =0) {
360
+ & when (isnumber(@delay)) {
361
+ transition: ~"@{property} var(@{durationVariable}) var(@{timingFunctionVariable}) @{delay}ms";
362
+ }
363
+
364
+ & when not (isnumber(@delay)) {
365
+ transition: ~"@{property} var(@{durationVariable}) var(@{timingFunctionVariable}) var(--mdui-motion-duration-@{delay})";
366
+ }
367
+ }
368
+ }
369
+
370
+ .transition-timing-function(@value) {
371
+ .sys(transition-timing-function, ~"motion-easing-@{value}");
372
+ }
373
+
374
+ .transition-duration(@value) {
375
+ .sys(transition-duration, ~"motion-duration-@{value}");
376
+ }
377
+
378
+ .transition-delay(@value) {
379
+ .sys(transition-delay, ~"motion-duration-@{value}");
380
+ }
@@ -0,0 +1,22 @@
1
+ import { nothing } from 'lit';
2
+ import type { Constructor } from '@open-wc/dedupe-mixin';
3
+ import type { LitElement, TemplateResult } from 'lit';
4
+ import type { DirectiveResult } from 'lit/directive.js';
5
+ import type { RefDirective } from 'lit/directives/ref.js';
6
+ type RenderAnchorOptions = {
7
+ id?: string;
8
+ className?: string;
9
+ part?: string;
10
+ content?: TemplateResult | typeof nothing | (TemplateResult | typeof nothing)[];
11
+ tabindex?: number;
12
+ refDirective?: DirectiveResult<typeof RefDirective>;
13
+ };
14
+ export declare class AnchorMixinInterface extends LitElement {
15
+ href?: string;
16
+ download?: string;
17
+ target?: '_blank' | '_parent' | '_self' | '_top';
18
+ rel?: 'alternate' | 'author' | 'bookmark' | 'external' | 'help' | 'license' | 'me' | 'next' | 'nofollow' | 'noreferrer' | 'opener' | 'prev' | 'search' | 'tag';
19
+ protected renderAnchor(options: RenderAnchorOptions): TemplateResult;
20
+ }
21
+ export declare const AnchorMixin: <T extends Constructor<LitElement>>(superclass: T) => Constructor<AnchorMixinInterface> & T;
22
+ export {};
@@ -0,0 +1,25 @@
1
+ import { __decorate } from "tslib";
2
+ import { html } from 'lit';
3
+ import { property } from 'lit/decorators.js';
4
+ import { ifDefined } from 'lit/directives/if-defined.js';
5
+ export const AnchorMixin = (superclass) => {
6
+ class AnchorMixinClass extends superclass {
7
+ renderAnchor({ id, className, part, content = html `<slot></slot>`, refDirective, }) {
8
+ return html `<a ${refDirective} id="${ifDefined(id)}" class="_a ${className ? className : ''}" part="${ifDefined(part)}" href="${ifDefined(this.href)}" download="${ifDefined(this.download)}" target="${ifDefined(this.target)}" rel="${ifDefined(this.rel)}">${content}</a>`;
9
+ }
10
+ }
11
+ __decorate([
12
+ property({ reflect: true })
13
+ ], AnchorMixinClass.prototype, "href", void 0);
14
+ __decorate([
15
+ property({ reflect: true })
16
+ ], AnchorMixinClass.prototype, "download", void 0);
17
+ __decorate([
18
+ property({ reflect: true })
19
+ ], AnchorMixinClass.prototype, "target", void 0);
20
+ __decorate([
21
+ property({ reflect: true })
22
+ ], AnchorMixinClass.prototype, "rel", void 0);
23
+ // @ts-ignore
24
+ return AnchorMixinClass;
25
+ };
@@ -0,0 +1,19 @@
1
+ import '@mdui/jq/methods/attr.js';
2
+ import '@mdui/jq/methods/css.js';
3
+ import '@mdui/jq/methods/each.js';
4
+ import '@mdui/jq/methods/removeAttr.js';
5
+ import type { Constructor } from '@open-wc/dedupe-mixin';
6
+ import type { LitElement } from 'lit';
7
+ export declare class FocusableMixinInterface extends LitElement {
8
+ autofocus: boolean;
9
+ tabIndex: number;
10
+ protected get focusDisabled(): boolean;
11
+ protected get focusElement(): HTMLElement | null | undefined;
12
+ focus(options?: FocusOptions): void;
13
+ blur(): void;
14
+ click(): void;
15
+ }
16
+ /**
17
+ * 参考:https://github.com/adobe/spectrum-web-components/blob/main/packages/shared/src/focusable.ts
18
+ */
19
+ export declare const FocusableMixin: <T extends Constructor<LitElement>>(superclass: T) => Constructor<FocusableMixinInterface> & T;
@@ -0,0 +1,251 @@
1
+ import { __decorate } from "tslib";
2
+ import { property } from 'lit/decorators.js';
3
+ import { getDocument } from 'ssr-window';
4
+ import { $ } from '@mdui/jq/$.js';
5
+ import '@mdui/jq/methods/attr.js';
6
+ import '@mdui/jq/methods/css.js';
7
+ import '@mdui/jq/methods/each.js';
8
+ import '@mdui/jq/methods/removeAttr.js';
9
+ import { booleanConverter } from '../helpers/decorator.js';
10
+ let isClick = true;
11
+ const document = getDocument();
12
+ document.addEventListener('pointerdown', () => {
13
+ isClick = true;
14
+ });
15
+ document.addEventListener('keydown', () => {
16
+ isClick = false;
17
+ });
18
+ /**
19
+ * 参考:https://github.com/adobe/spectrum-web-components/blob/main/packages/shared/src/focusable.ts
20
+ */
21
+ export const FocusableMixin = (superclass) => {
22
+ class FocusableMixinClass extends superclass {
23
+ constructor() {
24
+ super(...arguments);
25
+ /**
26
+ * 是否在页面加载完成后自动获得焦点
27
+ */
28
+ this.autofocus = false;
29
+ /**
30
+ * 是否获得了焦点,不管是鼠标点击,还是键盘切换获得的焦点,都会添加该属性
31
+ * 添加到 :host 元素上,供 CSS 选择器添加样式
32
+ */
33
+ this.focused = false;
34
+ /**
35
+ * 是否通过键盘切换获得了焦点
36
+ * 添加到 :host 元素上,供 CSS 选择器添加样式
37
+ */
38
+ this.focusVisible = false;
39
+ this._manipulatingTabindex = false;
40
+ this._tabIndex = 0;
41
+ }
42
+ /**
43
+ * 通过 Tab 键在元素之间切换焦点时,tabIndex 属性指定了元素获取焦点的顺序
44
+ */
45
+ get tabIndex() {
46
+ const $this = $(this);
47
+ if (this.focusElement === this) {
48
+ return Number($this.attr('tabindex') || -1);
49
+ }
50
+ const tabIndexAttribute = Number($this.attr('tabindex') || 0);
51
+ if (this.focusDisabled || tabIndexAttribute < 0) {
52
+ return -1;
53
+ }
54
+ if (!this.focusElement) {
55
+ return tabIndexAttribute;
56
+ }
57
+ return this.focusElement.tabIndex;
58
+ }
59
+ set tabIndex(tabIndex) {
60
+ if (this._manipulatingTabindex) {
61
+ this._manipulatingTabindex = false;
62
+ return;
63
+ }
64
+ const $this = $(this);
65
+ if (this.focusElement === this) {
66
+ if (tabIndex !== null) {
67
+ this._tabIndex = tabIndex;
68
+ }
69
+ $this.attr('tabindex', this.focusDisabled ? null : tabIndex);
70
+ return;
71
+ }
72
+ const onPointerDown = () => {
73
+ if (this.tabIndex === -1) {
74
+ this.tabIndex = 0;
75
+ this.focus({ preventScroll: true });
76
+ }
77
+ };
78
+ if (tabIndex === -1) {
79
+ this.addEventListener('pointerdown', onPointerDown);
80
+ }
81
+ else {
82
+ this._manipulatingTabindex = true;
83
+ this.removeEventListener('pointerdown', onPointerDown);
84
+ }
85
+ if (tabIndex === -1 || this.focusDisabled) {
86
+ $this.attr('tabindex', -1);
87
+ if (tabIndex !== -1) {
88
+ this.manageFocusElementTabindex(tabIndex);
89
+ }
90
+ return;
91
+ }
92
+ if (!this.hasAttribute('tabindex')) {
93
+ this._manipulatingTabindex = false;
94
+ }
95
+ this.manageFocusElementTabindex(tabIndex);
96
+ }
97
+ /**
98
+ * 父类要实现该属性,表示是否禁用 focus 状态
99
+ */
100
+ get focusDisabled() {
101
+ throw new Error('Must implement focusDisabled getter!');
102
+ }
103
+ /**
104
+ * 最终获得焦点的元素
105
+ */
106
+ get focusElement() {
107
+ throw new Error('Must implement focusElement getter!');
108
+ }
109
+ connectedCallback() {
110
+ super.connectedCallback();
111
+ this.updateComplete.then(() => {
112
+ requestAnimationFrame(() => {
113
+ this.manageAutoFocus();
114
+ });
115
+ });
116
+ }
117
+ /**
118
+ * 模拟鼠标点击元素
119
+ */
120
+ click() {
121
+ if (this.focusDisabled) {
122
+ return;
123
+ }
124
+ if (this.focusElement !== this) {
125
+ this.focusElement.click();
126
+ }
127
+ else {
128
+ HTMLElement.prototype.click.apply(this);
129
+ }
130
+ }
131
+ /**
132
+ * 将焦点设置在当前元素。
133
+ *
134
+ * 可传入一个对象作为参数。对象属性为:
135
+ *
136
+ * * `preventScroll`:默认情况下,在聚焦后会滚动页面,以将聚焦的元素滚动到视图中。可将该属性设为 `true` 以阻止页面滚动。
137
+ */
138
+ focus(options) {
139
+ if (this.focusDisabled || !this.focusElement) {
140
+ return;
141
+ }
142
+ if (this.focusElement !== this) {
143
+ this.focusElement.focus(options);
144
+ }
145
+ else {
146
+ HTMLElement.prototype.focus.apply(this, [options]);
147
+ }
148
+ }
149
+ /**
150
+ * 从当前元素中移除焦点
151
+ */
152
+ blur() {
153
+ if (this.focusElement !== this) {
154
+ this.focusElement.blur();
155
+ }
156
+ else {
157
+ HTMLElement.prototype.blur.apply(this);
158
+ }
159
+ }
160
+ firstUpdated(changedProperties) {
161
+ super.firstUpdated(changedProperties);
162
+ this.focusElement.addEventListener('focus', () => {
163
+ this.focused = true;
164
+ this.focusVisible = !isClick;
165
+ });
166
+ this.focusElement.addEventListener('blur', () => {
167
+ this.focused = false;
168
+ this.focusVisible = false;
169
+ });
170
+ }
171
+ update(changedProperties) {
172
+ if (this._lastFocusDisabled === undefined ||
173
+ this._lastFocusDisabled !== this.focusDisabled) {
174
+ this._lastFocusDisabled = this.focusDisabled;
175
+ const $this = $(this);
176
+ if (this.focusDisabled) {
177
+ $this.removeAttr('tabindex');
178
+ }
179
+ else {
180
+ if (this.focusElement === this) {
181
+ this._manipulatingTabindex = true;
182
+ $this.attr('tabindex', this._tabIndex);
183
+ }
184
+ else if (this.tabIndex > -1) {
185
+ $this.removeAttr('tabindex');
186
+ }
187
+ }
188
+ }
189
+ super.update(changedProperties);
190
+ }
191
+ updated(changedProperties) {
192
+ super.updated(changedProperties);
193
+ if (this.focused && this.focusDisabled) {
194
+ this.blur();
195
+ }
196
+ }
197
+ async manageFocusElementTabindex(tabIndex) {
198
+ if (!this.focusElement) {
199
+ await this.updateComplete;
200
+ }
201
+ if (tabIndex === null) {
202
+ this.focusElement.removeAttribute('tabindex');
203
+ }
204
+ else {
205
+ this.focusElement.tabIndex = tabIndex;
206
+ }
207
+ }
208
+ manageAutoFocus() {
209
+ if (this.autofocus) {
210
+ this.dispatchEvent(new KeyboardEvent('keydown', {
211
+ code: 'Tab',
212
+ }));
213
+ this.focusElement.focus();
214
+ }
215
+ }
216
+ }
217
+ __decorate([
218
+ property({
219
+ type: Boolean,
220
+ /**
221
+ * 哪些属性需要 reflect: true?
222
+ * 一般所有属性都需要 reflect,但以下情况除外:
223
+ * 1. 会频繁变更的属性
224
+ * 2. 属性同步会造成较大性能开销的属性
225
+ * 3. 复杂类型属性(数组、对象等,仅提供 property,不提供 attribute)
226
+ */
227
+ reflect: true,
228
+ converter: booleanConverter,
229
+ })
230
+ ], FocusableMixinClass.prototype, "autofocus", void 0);
231
+ __decorate([
232
+ property({
233
+ type: Boolean,
234
+ reflect: true,
235
+ converter: booleanConverter,
236
+ })
237
+ ], FocusableMixinClass.prototype, "focused", void 0);
238
+ __decorate([
239
+ property({
240
+ type: Boolean,
241
+ reflect: true,
242
+ converter: booleanConverter,
243
+ attribute: 'focus-visible',
244
+ })
245
+ ], FocusableMixinClass.prototype, "focusVisible", void 0);
246
+ __decorate([
247
+ property({ type: Number, reflect: true, attribute: 'tabindex' })
248
+ ], FocusableMixinClass.prototype, "tabIndex", null);
249
+ // @ts-ignore
250
+ return FocusableMixinClass;
251
+ };