@mdui/shared 1.0.0 → 1.0.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.
- package/controllers/defined.d.ts +69 -0
- package/controllers/defined.js +126 -0
- package/controllers/form.d.ts +1 -0
- package/controllers/form.js +25 -16
- package/controllers/has-slot.js +7 -0
- package/controllers/hover.d.ts +7 -2
- package/controllers/hover.js +16 -8
- package/decorators/default-value.js +1 -2
- package/decorators/watch.d.ts +1 -0
- package/decorators/watch.js +1 -0
- package/helpers/array.d.ts +6 -0
- package/helpers/array.js +13 -0
- package/helpers/modal.js +1 -1
- package/helpers/motion.js +7 -1
- package/helpers/observeResize.js +2 -4
- package/helpers/scroll.d.ts +14 -4
- package/helpers/scroll.js +69 -11
- package/helpers/tabbable.js +7 -7
- package/icons/arrow-right.js +2 -1
- package/icons/cancel--outlined.js +2 -1
- package/icons/check-box-outline-blank.js +2 -1
- package/icons/check-box.js +2 -1
- package/icons/check.js +2 -1
- package/icons/circle.js +2 -1
- package/icons/clear.js +2 -1
- package/icons/error.js +2 -1
- package/icons/indeterminate-check-box.js +2 -1
- package/icons/radio-button-unchecked.js +2 -1
- package/icons/visibility-off.js +2 -1
- package/icons/visibility.js +2 -1
- package/mixins/anchor.d.ts +1 -1
- package/mixins/anchor.js +2 -2
- package/mixins/focusable.d.ts +1 -1
- package/mixins/focusable.js +4 -2
- package/mixins/scrollBehavior.d.ts +2 -0
- package/mixins/scrollBehavior.js +19 -11
- package/package.json +5 -5
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
interface Options {
|
|
3
|
+
/**
|
|
4
|
+
* 是否在整个页面的 DOM 就绪后,才认为组件已定义完成
|
|
5
|
+
*
|
|
6
|
+
* 如果需要操作或读取组件外部、或组件 slot 中的原生 DOM 时,需要设置为 true
|
|
7
|
+
*/
|
|
8
|
+
needDomReady?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* 是否在指定 Web Components 注册完成后,才认为组件定义完成。则 needDomReady 将默认为 true
|
|
11
|
+
*
|
|
12
|
+
* 若值为数组,则监听当前组件 slot 中的组件,如 ['mdui-segmented-button']
|
|
13
|
+
* 若值为对象,则监听对象键名指定的组件,键值为 true,则监听整个页面的组件;否则监听 slot 中的组件
|
|
14
|
+
*
|
|
15
|
+
* 可以在数组中设置空字符串,或在对象中设置空字符串键名,来表示等待所有 Web Components 注册完成
|
|
16
|
+
*/
|
|
17
|
+
relatedElements?: string[] | {
|
|
18
|
+
[localName: string]: boolean;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 判断组件是否定义完成
|
|
23
|
+
*
|
|
24
|
+
* 如果需要在组件操作或读取组件外部、或组件 slot 中的原生 DOM 时,则需要在 DOM 就绪时,才能认为组件定义完成
|
|
25
|
+
* 如果组件需要和其他组件配合使用,则需要等待其他组件定义完成后,才能认为组件定义完成
|
|
26
|
+
*/
|
|
27
|
+
export declare class DefinedController implements ReactiveController {
|
|
28
|
+
private host;
|
|
29
|
+
/**
|
|
30
|
+
* 组件是否已定义完成
|
|
31
|
+
*/
|
|
32
|
+
private defined;
|
|
33
|
+
/**
|
|
34
|
+
* 是否在 DOM 就绪后,才算组件定义完成
|
|
35
|
+
*/
|
|
36
|
+
private readonly needDomReady?;
|
|
37
|
+
/**
|
|
38
|
+
* 在哪些相关组件定义完成后,才算组件定义完成
|
|
39
|
+
*/
|
|
40
|
+
private readonly relatedElements?;
|
|
41
|
+
constructor(host: ReactiveControllerHost & Element, options: Options);
|
|
42
|
+
hostConnected(): void;
|
|
43
|
+
hostDisconnected(): void;
|
|
44
|
+
/**
|
|
45
|
+
* 判断组件是否定义完成
|
|
46
|
+
*/
|
|
47
|
+
isDefined(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* 在组件定义完成后,promise 被 resolve
|
|
50
|
+
*/
|
|
51
|
+
whenDefined(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* slot 中的未完成定义的相关 Web components 组件的 CSS 选择器
|
|
54
|
+
*/
|
|
55
|
+
private getScopeLocalNameSelector;
|
|
56
|
+
/**
|
|
57
|
+
* 整个页面中的未完成定义的相关 Web components 组件的 CSS 选择器
|
|
58
|
+
*/
|
|
59
|
+
private getGlobalLocalNameSelector;
|
|
60
|
+
/**
|
|
61
|
+
* 获取未完成定义的相关 Web components 组件名
|
|
62
|
+
*/
|
|
63
|
+
private getUndefinedLocalNames;
|
|
64
|
+
/**
|
|
65
|
+
* slot 变更时,若 slot 中包含未完成定义的相关 Web components 组件,则组件未定义完成
|
|
66
|
+
*/
|
|
67
|
+
private onSlotChange;
|
|
68
|
+
}
|
|
69
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { getDocument } from 'ssr-window';
|
|
2
|
+
import { unique } from '@mdui/jq/functions/unique.js';
|
|
3
|
+
import { isDomReady } from '@mdui/jq/shared/dom.js';
|
|
4
|
+
/**
|
|
5
|
+
* 判断组件是否定义完成
|
|
6
|
+
*
|
|
7
|
+
* 如果需要在组件操作或读取组件外部、或组件 slot 中的原生 DOM 时,则需要在 DOM 就绪时,才能认为组件定义完成
|
|
8
|
+
* 如果组件需要和其他组件配合使用,则需要等待其他组件定义完成后,才能认为组件定义完成
|
|
9
|
+
*/
|
|
10
|
+
export class DefinedController {
|
|
11
|
+
constructor(host, options) {
|
|
12
|
+
/**
|
|
13
|
+
* 组件是否已定义完成
|
|
14
|
+
*/
|
|
15
|
+
this.defined = false;
|
|
16
|
+
(this.host = host).addController(this);
|
|
17
|
+
this.relatedElements = options.relatedElements;
|
|
18
|
+
this.needDomReady = options.needDomReady || !!options.relatedElements;
|
|
19
|
+
this.onSlotChange = this.onSlotChange.bind(this);
|
|
20
|
+
}
|
|
21
|
+
hostConnected() {
|
|
22
|
+
this.host.shadowRoot.addEventListener('slotchange', this.onSlotChange);
|
|
23
|
+
}
|
|
24
|
+
hostDisconnected() {
|
|
25
|
+
this.host.shadowRoot.removeEventListener('slotchange', this.onSlotChange);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 判断组件是否定义完成
|
|
29
|
+
*/
|
|
30
|
+
isDefined() {
|
|
31
|
+
if (this.defined) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
this.defined =
|
|
35
|
+
(!this.needDomReady || isDomReady()) &&
|
|
36
|
+
!this.getUndefinedLocalNames().length;
|
|
37
|
+
return this.defined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 在组件定义完成后,promise 被 resolve
|
|
41
|
+
*/
|
|
42
|
+
async whenDefined() {
|
|
43
|
+
if (this.defined) {
|
|
44
|
+
return Promise.resolve();
|
|
45
|
+
}
|
|
46
|
+
const document = getDocument();
|
|
47
|
+
if (this.needDomReady && !isDomReady(document)) {
|
|
48
|
+
await new Promise((resolve) => {
|
|
49
|
+
document.addEventListener('DOMContentLoaded', () => resolve(), {
|
|
50
|
+
once: true,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const undefinedLocalNames = this.getUndefinedLocalNames();
|
|
55
|
+
if (undefinedLocalNames.length) {
|
|
56
|
+
const promises = [];
|
|
57
|
+
undefinedLocalNames.forEach((localName) => {
|
|
58
|
+
promises.push(customElements.whenDefined(localName));
|
|
59
|
+
});
|
|
60
|
+
await Promise.all(promises);
|
|
61
|
+
}
|
|
62
|
+
this.defined = true;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* slot 中的未完成定义的相关 Web components 组件的 CSS 选择器
|
|
67
|
+
*/
|
|
68
|
+
getScopeLocalNameSelector() {
|
|
69
|
+
const localNames = this.relatedElements;
|
|
70
|
+
if (!localNames) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(localNames)) {
|
|
74
|
+
return localNames
|
|
75
|
+
.map((localName) => `${localName}:not(:defined)`)
|
|
76
|
+
.join(',');
|
|
77
|
+
}
|
|
78
|
+
return Object.keys(localNames)
|
|
79
|
+
.filter((localName) => !localNames[localName])
|
|
80
|
+
.map((localName) => `${localName}:not(:defined)`)
|
|
81
|
+
.join(',');
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 整个页面中的未完成定义的相关 Web components 组件的 CSS 选择器
|
|
85
|
+
*/
|
|
86
|
+
getGlobalLocalNameSelector() {
|
|
87
|
+
const localNames = this.relatedElements;
|
|
88
|
+
if (!localNames || Array.isArray(localNames)) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return Object.keys(localNames)
|
|
92
|
+
.filter((localName) => localNames[localName])
|
|
93
|
+
.map((localName) => `${localName}:not(:defined)`)
|
|
94
|
+
.join(',');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 获取未完成定义的相关 Web components 组件名
|
|
98
|
+
*/
|
|
99
|
+
getUndefinedLocalNames() {
|
|
100
|
+
const scopeSelector = this.getScopeLocalNameSelector();
|
|
101
|
+
const globalSelector = this.getGlobalLocalNameSelector();
|
|
102
|
+
const undefinedScopeElements = scopeSelector
|
|
103
|
+
? [...this.host.querySelectorAll(scopeSelector)]
|
|
104
|
+
: [];
|
|
105
|
+
const undefinedGlobalElements = globalSelector
|
|
106
|
+
? [...getDocument().querySelectorAll(globalSelector)]
|
|
107
|
+
: [];
|
|
108
|
+
const localNames = [
|
|
109
|
+
...undefinedScopeElements,
|
|
110
|
+
...undefinedGlobalElements,
|
|
111
|
+
].map((element) => element.localName);
|
|
112
|
+
return unique(localNames);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* slot 变更时,若 slot 中包含未完成定义的相关 Web components 组件,则组件未定义完成
|
|
116
|
+
*/
|
|
117
|
+
onSlotChange() {
|
|
118
|
+
const selector = this.getScopeLocalNameSelector();
|
|
119
|
+
if (selector) {
|
|
120
|
+
const undefinedElements = this.host.querySelectorAll(selector);
|
|
121
|
+
if (undefinedElements.length) {
|
|
122
|
+
this.defined = false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
package/controllers/form.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export declare class FormController implements ReactiveController {
|
|
|
21
21
|
private host;
|
|
22
22
|
private form?;
|
|
23
23
|
private options;
|
|
24
|
+
private definedController;
|
|
24
25
|
constructor(host: ReactiveControllerHost & FormControl, options?: Partial<FormControllerOptions>);
|
|
25
26
|
hostConnected(): void;
|
|
26
27
|
hostDisconnected(): void;
|
package/controllers/form.js
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
import { $ } from '@mdui/jq/$.js';
|
|
5
5
|
import '@mdui/jq/methods/attr.js';
|
|
6
6
|
import '@mdui/jq/methods/css.js';
|
|
7
|
-
import { formCollections } from '@mdui/jq/shared/form.js';
|
|
7
|
+
import { formCollections, getFormControls } from '@mdui/jq/shared/form.js';
|
|
8
8
|
import { isFunction, isString, isUndefined } from '@mdui/jq/shared/helper.js';
|
|
9
|
+
import { DefinedController } from './defined.js';
|
|
9
10
|
/**
|
|
10
11
|
* 在执行 `<form>` 元素的 reportValidity() 时,不会执行 mdui 组件的 reportValidity() 方法,
|
|
11
12
|
* 因此在 mdui 表单控件的 hostConnected 中把 `<form>` 的 reportValidity 替换为自定义方法,
|
|
@@ -25,6 +26,9 @@ export const formResets = new WeakMap();
|
|
|
25
26
|
export class FormController {
|
|
26
27
|
constructor(host, options) {
|
|
27
28
|
(this.host = host).addController(this);
|
|
29
|
+
this.definedController = new DefinedController(host, {
|
|
30
|
+
needDomReady: true,
|
|
31
|
+
});
|
|
28
32
|
this.options = {
|
|
29
33
|
form: (control) => {
|
|
30
34
|
const formId = $(control).attr('form');
|
|
@@ -48,30 +52,33 @@ export class FormController {
|
|
|
48
52
|
this.reportFormValidity = this.reportFormValidity.bind(this);
|
|
49
53
|
}
|
|
50
54
|
hostConnected() {
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
this.definedController.whenDefined().then(() => {
|
|
56
|
+
this.form = this.options.form(this.host);
|
|
57
|
+
if (this.form) {
|
|
58
|
+
this.attachForm(this.form);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
55
61
|
}
|
|
56
62
|
hostDisconnected() {
|
|
57
63
|
this.detachForm();
|
|
58
64
|
}
|
|
59
65
|
hostUpdated() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
this.
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
this.definedController.whenDefined().then(() => {
|
|
67
|
+
const form = this.options.form(this.host);
|
|
68
|
+
if (!form) {
|
|
69
|
+
this.detachForm();
|
|
70
|
+
}
|
|
71
|
+
if (form && this.form !== form) {
|
|
72
|
+
this.detachForm();
|
|
73
|
+
this.attachForm(form);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
68
76
|
}
|
|
69
77
|
/**
|
|
70
78
|
* 获取当前表单控件关联的 `<form>` 元素
|
|
71
79
|
*/
|
|
72
80
|
getForm() {
|
|
73
|
-
|
|
74
|
-
return (_a = this.form) !== null && _a !== void 0 ? _a : null;
|
|
81
|
+
return this.form ?? null;
|
|
75
82
|
}
|
|
76
83
|
/**
|
|
77
84
|
* 重置整个表单,所有表单控件恢复成默认值
|
|
@@ -175,6 +182,7 @@ export class FormController {
|
|
|
175
182
|
}
|
|
176
183
|
}
|
|
177
184
|
}
|
|
185
|
+
// todo: 当前组件进行验证的顺序,取决于组件的注册顺序,而不会按在 DOM 中的顺序从上到下验证。如何按 DOM 顺序验证?
|
|
178
186
|
onFormSubmit(event) {
|
|
179
187
|
const disabled = this.options.disabled(this.host);
|
|
180
188
|
const reportValidity = this.options.reportValidity;
|
|
@@ -203,7 +211,8 @@ export class FormController {
|
|
|
203
211
|
}
|
|
204
212
|
reportFormValidity() {
|
|
205
213
|
if (this.form && !this.form.noValidate) {
|
|
206
|
-
|
|
214
|
+
const elements = getFormControls(this.form);
|
|
215
|
+
for (const element of elements) {
|
|
207
216
|
if (isFunction(element.reportValidity) && !element.reportValidity()) {
|
|
208
217
|
return false;
|
|
209
218
|
}
|
package/controllers/has-slot.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { $ } from '@mdui/jq/$.js';
|
|
2
|
+
import { isDomReady } from '@mdui/jq/shared/dom.js';
|
|
1
3
|
/**
|
|
2
4
|
* 检查指定的 slot 是否存在
|
|
3
5
|
*/
|
|
@@ -10,6 +12,11 @@ export class HasSlotController {
|
|
|
10
12
|
}
|
|
11
13
|
hostConnected() {
|
|
12
14
|
this.host.shadowRoot.addEventListener('slotchange', this.onSlotChange);
|
|
15
|
+
if (!isDomReady()) {
|
|
16
|
+
$(() => {
|
|
17
|
+
this.host.requestUpdate();
|
|
18
|
+
});
|
|
19
|
+
}
|
|
13
20
|
}
|
|
14
21
|
hostDisconnected() {
|
|
15
22
|
this.host.shadowRoot.removeEventListener('slotchange', this.onSlotChange);
|
package/controllers/hover.d.ts
CHANGED
|
@@ -17,17 +17,22 @@ export declare class HoverController implements ReactiveController {
|
|
|
17
17
|
private readonly leaveEventName;
|
|
18
18
|
private mouseEnterItems;
|
|
19
19
|
private mouseLeaveItems;
|
|
20
|
+
/**
|
|
21
|
+
* @param host
|
|
22
|
+
* @param elementRef 检查鼠标是否放在该元素上
|
|
23
|
+
*/
|
|
20
24
|
constructor(host: ReactiveControllerHost & Element, elementRef: Ref<HTMLElement>);
|
|
25
|
+
hostConnected(): void;
|
|
21
26
|
hostDisconnected(): void;
|
|
22
27
|
/**
|
|
23
28
|
* 指定鼠标移入时的回调函数
|
|
24
|
-
* @param callback
|
|
29
|
+
* @param callback 要执行的回调函数
|
|
25
30
|
* @param one 是否仅执行一次
|
|
26
31
|
*/
|
|
27
32
|
onMouseEnter(callback: () => void, one?: boolean): void;
|
|
28
33
|
/**
|
|
29
34
|
* 指定鼠标移出时的回调函数
|
|
30
|
-
* @param callback
|
|
35
|
+
* @param callback 要执行的回调函数
|
|
31
36
|
* @param one 是否仅执行一次
|
|
32
37
|
*/
|
|
33
38
|
onMouseLeave(callback: () => void, one?: boolean): void;
|
package/controllers/hover.js
CHANGED
|
@@ -6,6 +6,10 @@ import { uniqueId } from '../helpers/uniqueId.js';
|
|
|
6
6
|
* 检查当前鼠标是否放在指定元素上,及进入、离开元素执行对于的回调
|
|
7
7
|
*/
|
|
8
8
|
export class HoverController {
|
|
9
|
+
/**
|
|
10
|
+
* @param host
|
|
11
|
+
* @param elementRef 检查鼠标是否放在该元素上
|
|
12
|
+
*/
|
|
9
13
|
constructor(host, elementRef) {
|
|
10
14
|
/**
|
|
11
15
|
* 当前鼠标是否放在元素上
|
|
@@ -18,25 +22,29 @@ export class HoverController {
|
|
|
18
22
|
this.mouseLeaveItems = [];
|
|
19
23
|
(this.host = host).addController(this);
|
|
20
24
|
this.elementRef = elementRef;
|
|
25
|
+
}
|
|
26
|
+
hostConnected() {
|
|
21
27
|
this.host.updateComplete.then(() => {
|
|
22
28
|
$(this.elementRef.value)
|
|
23
29
|
.on(this.enterEventName, () => {
|
|
24
30
|
this.isHover = true;
|
|
25
|
-
this.mouseEnterItems.
|
|
31
|
+
for (let i = this.mouseEnterItems.length - 1; i >= 0; i--) {
|
|
32
|
+
const item = this.mouseEnterItems[i];
|
|
26
33
|
item.callback();
|
|
27
34
|
if (item.one) {
|
|
28
|
-
|
|
35
|
+
this.mouseEnterItems.splice(i, 1);
|
|
29
36
|
}
|
|
30
|
-
}
|
|
37
|
+
}
|
|
31
38
|
})
|
|
32
39
|
.on(this.leaveEventName, () => {
|
|
33
40
|
this.isHover = false;
|
|
34
|
-
this.mouseLeaveItems.
|
|
41
|
+
for (let i = this.mouseLeaveItems.length - 1; i >= 0; i--) {
|
|
42
|
+
const item = this.mouseLeaveItems[i];
|
|
35
43
|
item.callback();
|
|
36
44
|
if (item.one) {
|
|
37
|
-
|
|
45
|
+
this.mouseLeaveItems.splice(i, 1);
|
|
38
46
|
}
|
|
39
|
-
}
|
|
47
|
+
}
|
|
40
48
|
});
|
|
41
49
|
});
|
|
42
50
|
}
|
|
@@ -45,7 +53,7 @@ export class HoverController {
|
|
|
45
53
|
}
|
|
46
54
|
/**
|
|
47
55
|
* 指定鼠标移入时的回调函数
|
|
48
|
-
* @param callback
|
|
56
|
+
* @param callback 要执行的回调函数
|
|
49
57
|
* @param one 是否仅执行一次
|
|
50
58
|
*/
|
|
51
59
|
onMouseEnter(callback, one = false) {
|
|
@@ -53,7 +61,7 @@ export class HoverController {
|
|
|
53
61
|
}
|
|
54
62
|
/**
|
|
55
63
|
* 指定鼠标移出时的回调函数
|
|
56
|
-
* @param callback
|
|
64
|
+
* @param callback 要执行的回调函数
|
|
57
65
|
* @param one 是否仅执行一次
|
|
58
66
|
*/
|
|
59
67
|
onMouseLeave(callback, one = false) {
|
|
@@ -19,7 +19,6 @@ export function defaultValue(propertyName = 'value') {
|
|
|
19
19
|
const constructor = proto.constructor;
|
|
20
20
|
const attributeChangedCallback = constructor.prototype.attributeChangedCallback;
|
|
21
21
|
constructor.prototype.attributeChangedCallback = function (name, old, value) {
|
|
22
|
-
var _a;
|
|
23
22
|
const options = constructor.getPropertyOptions(propertyName);
|
|
24
23
|
const attributeName = isString(options.attribute)
|
|
25
24
|
? options.attribute
|
|
@@ -28,7 +27,7 @@ export function defaultValue(propertyName = 'value') {
|
|
|
28
27
|
const converter = options.converter || defaultConverter;
|
|
29
28
|
const fromAttribute = isFunction(converter)
|
|
30
29
|
? converter
|
|
31
|
-
:
|
|
30
|
+
: converter?.fromAttribute ?? defaultConverter.fromAttribute;
|
|
32
31
|
const newValue = fromAttribute(value, options.type);
|
|
33
32
|
if (this[propertyName] !== newValue) {
|
|
34
33
|
this[key] = newValue;
|
package/decorators/watch.d.ts
CHANGED
package/decorators/watch.js
CHANGED
package/helpers/array.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 检查两个数组是否包含相同的元素,不考虑顺序
|
|
3
|
+
* @param a
|
|
4
|
+
* @param b
|
|
5
|
+
*/
|
|
6
|
+
export const arraysEqualIgnoreOrder = (a, b) => {
|
|
7
|
+
if (a.length !== b.length) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
const sortedA = [...a].sort();
|
|
11
|
+
const sortedB = [...b].sort();
|
|
12
|
+
return sortedA.every((value, index) => value === sortedB[index]);
|
|
13
|
+
};
|
package/helpers/modal.js
CHANGED
|
@@ -30,7 +30,7 @@ export class Modal {
|
|
|
30
30
|
if (!this.element.matches(':focus-within')) {
|
|
31
31
|
const { start, end } = getTabbableBoundary(this.element);
|
|
32
32
|
const target = this.tabDirection === 'forward' ? start : end;
|
|
33
|
-
if (typeof
|
|
33
|
+
if (typeof target?.focus === 'function') {
|
|
34
34
|
target.focus({ preventScroll: true });
|
|
35
35
|
}
|
|
36
36
|
}
|
package/helpers/motion.js
CHANGED
|
@@ -16,5 +16,11 @@ export const getEasing = (element, name) => {
|
|
|
16
16
|
*/
|
|
17
17
|
export const getDuration = (element, name) => {
|
|
18
18
|
const cssVariableName = `--mdui-motion-duration-${name}`;
|
|
19
|
-
|
|
19
|
+
const cssValue = $(element).css(cssVariableName).trim().toLowerCase();
|
|
20
|
+
if (cssValue.endsWith('ms')) {
|
|
21
|
+
return parseFloat(cssValue);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return parseFloat(cssValue) * 1000;
|
|
25
|
+
}
|
|
20
26
|
};
|
package/helpers/observeResize.js
CHANGED
|
@@ -16,8 +16,7 @@ export const observeResize = (target, callback) => {
|
|
|
16
16
|
const result = {
|
|
17
17
|
unobserve: () => {
|
|
18
18
|
$target.each((_, target) => {
|
|
19
|
-
|
|
20
|
-
const coArr = (_a = weakMap.get(target)) !== null && _a !== void 0 ? _a : [];
|
|
19
|
+
const coArr = weakMap.get(target) ?? [];
|
|
21
20
|
const index = coArr.findIndex((co) => co.key === key);
|
|
22
21
|
if (index !== -1) {
|
|
23
22
|
coArr.splice(index, 1);
|
|
@@ -47,9 +46,8 @@ export const observeResize = (target, callback) => {
|
|
|
47
46
|
}
|
|
48
47
|
// 添加监听
|
|
49
48
|
$target.each((_, target) => {
|
|
50
|
-
var _a;
|
|
51
49
|
observer.observe(target);
|
|
52
|
-
const coArr =
|
|
50
|
+
const coArr = weakMap.get(target) ?? [];
|
|
53
51
|
coArr.push({ callback, key });
|
|
54
52
|
weakMap.set(target, coArr);
|
|
55
53
|
});
|
package/helpers/scroll.d.ts
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import '@mdui/jq/methods/addClass.js';
|
|
2
|
+
import '@mdui/jq/methods/append.js';
|
|
3
|
+
import '@mdui/jq/methods/appendTo.js';
|
|
4
|
+
import '@mdui/jq/methods/css.js';
|
|
5
|
+
import '@mdui/jq/methods/remove.js';
|
|
2
6
|
import '@mdui/jq/methods/removeClass.js';
|
|
7
|
+
import '@mdui/jq/methods/width.js';
|
|
8
|
+
/**
|
|
9
|
+
* 获取滚动条宽度
|
|
10
|
+
* @param fresh 是否重新计算
|
|
11
|
+
*/
|
|
12
|
+
export declare const getScrollBarSize: (fresh?: boolean) => number;
|
|
3
13
|
/**
|
|
4
14
|
* 锁定指定元素,禁止滚动。对同一个元素多次调用此方法,只会锁定一次
|
|
5
|
-
* @param
|
|
15
|
+
* @param source 由该元素触发锁定
|
|
6
16
|
* @param target 锁定该元素的滚动状态,默认为 body
|
|
7
17
|
*/
|
|
8
|
-
export declare const lockScreen: (
|
|
18
|
+
export declare const lockScreen: (source: HTMLElement, target?: HTMLElement) => void;
|
|
9
19
|
/**
|
|
10
20
|
* 解除指定元素的滚动状态锁定。
|
|
11
|
-
* @param
|
|
21
|
+
* @param source 由该元素触发锁定
|
|
12
22
|
* @param target 锁定该元素的滚动状态,默认为 body
|
|
13
23
|
*/
|
|
14
|
-
export declare const unlockScreen: (
|
|
24
|
+
export declare const unlockScreen: (source: HTMLElement, target?: HTMLElement) => void;
|
package/helpers/scroll.js
CHANGED
|
@@ -1,28 +1,86 @@
|
|
|
1
1
|
import { getDocument } from 'ssr-window';
|
|
2
2
|
import { $ } from '@mdui/jq/$.js';
|
|
3
3
|
import '@mdui/jq/methods/addClass.js';
|
|
4
|
+
import '@mdui/jq/methods/append.js';
|
|
5
|
+
import '@mdui/jq/methods/appendTo.js';
|
|
6
|
+
import '@mdui/jq/methods/css.js';
|
|
7
|
+
import '@mdui/jq/methods/remove.js';
|
|
4
8
|
import '@mdui/jq/methods/removeClass.js';
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
import '@mdui/jq/methods/width.js';
|
|
10
|
+
import { isUndefined } from '@mdui/jq/shared/helper.js';
|
|
11
|
+
// 缓存滚动条宽度
|
|
12
|
+
let scrollBarSizeCached;
|
|
13
|
+
/**
|
|
14
|
+
* 获取滚动条宽度
|
|
15
|
+
* @param fresh 是否重新计算
|
|
16
|
+
*/
|
|
17
|
+
export const getScrollBarSize = (fresh) => {
|
|
18
|
+
if (isUndefined(document)) {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
if (fresh || scrollBarSizeCached === undefined) {
|
|
22
|
+
const $inner = $('<div>').css({
|
|
23
|
+
width: '100%',
|
|
24
|
+
height: '200px',
|
|
25
|
+
});
|
|
26
|
+
const $outer = $('<div>')
|
|
27
|
+
.css({
|
|
28
|
+
position: 'absolute',
|
|
29
|
+
top: '0',
|
|
30
|
+
left: '0',
|
|
31
|
+
pointerEvents: 'none',
|
|
32
|
+
visibility: 'hidden',
|
|
33
|
+
width: '200px',
|
|
34
|
+
height: '150px',
|
|
35
|
+
overflow: 'hidden',
|
|
36
|
+
})
|
|
37
|
+
.append($inner)
|
|
38
|
+
.appendTo(document.body);
|
|
39
|
+
const widthContained = $inner[0].offsetWidth;
|
|
40
|
+
$outer.css('overflow', 'scroll');
|
|
41
|
+
let widthScroll = $inner[0].offsetWidth;
|
|
42
|
+
if (widthContained === widthScroll) {
|
|
43
|
+
widthScroll = $outer[0].clientWidth;
|
|
44
|
+
}
|
|
45
|
+
$outer.remove();
|
|
46
|
+
scrollBarSizeCached = widthContained - widthScroll;
|
|
47
|
+
}
|
|
48
|
+
return scrollBarSizeCached;
|
|
49
|
+
};
|
|
50
|
+
const lockMap = new WeakMap();
|
|
51
|
+
const className = 'mdui-lock-screen';
|
|
7
52
|
/**
|
|
8
53
|
* 锁定指定元素,禁止滚动。对同一个元素多次调用此方法,只会锁定一次
|
|
9
|
-
* @param
|
|
54
|
+
* @param source 由该元素触发锁定
|
|
10
55
|
* @param target 锁定该元素的滚动状态,默认为 body
|
|
11
56
|
*/
|
|
12
|
-
export const lockScreen = (
|
|
57
|
+
export const lockScreen = (source, target) => {
|
|
13
58
|
const document = getDocument();
|
|
14
|
-
|
|
15
|
-
|
|
59
|
+
target ??= document.body;
|
|
60
|
+
if (!lockMap.has(target)) {
|
|
61
|
+
lockMap.set(target, new Set());
|
|
62
|
+
}
|
|
63
|
+
const lock = lockMap.get(target);
|
|
64
|
+
lock.add(source);
|
|
65
|
+
$(target)
|
|
66
|
+
.addClass(className)
|
|
67
|
+
.css('width', `calc(100% - ${getScrollBarSize()}px)`);
|
|
16
68
|
};
|
|
17
69
|
/**
|
|
18
70
|
* 解除指定元素的滚动状态锁定。
|
|
19
|
-
* @param
|
|
71
|
+
* @param source 由该元素触发锁定
|
|
20
72
|
* @param target 锁定该元素的滚动状态,默认为 body
|
|
21
73
|
*/
|
|
22
|
-
export const unlockScreen = (
|
|
74
|
+
export const unlockScreen = (source, target) => {
|
|
23
75
|
const document = getDocument();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
76
|
+
target ??= document.body;
|
|
77
|
+
const lock = lockMap.get(target);
|
|
78
|
+
if (!lock) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
lock.delete(source);
|
|
82
|
+
if (lock.size === 0) {
|
|
83
|
+
lockMap.delete(target);
|
|
84
|
+
$(target).removeClass(className).width('');
|
|
27
85
|
}
|
|
28
86
|
};
|
package/helpers/tabbable.js
CHANGED
|
@@ -3,7 +3,7 @@ import { getWindow } from 'ssr-window';
|
|
|
3
3
|
/** Determines if the specified element is tabbable using heuristics inspired by https://github.com/focus-trap/tabbable */
|
|
4
4
|
function isTabbable(el) {
|
|
5
5
|
const window = getWindow();
|
|
6
|
-
const
|
|
6
|
+
const localName = el.localName;
|
|
7
7
|
// Elements with a -1 tab index are not tabbable
|
|
8
8
|
if (el.getAttribute('tabindex') === '-1') {
|
|
9
9
|
return false;
|
|
@@ -18,7 +18,7 @@ function isTabbable(el) {
|
|
|
18
18
|
return false;
|
|
19
19
|
}
|
|
20
20
|
// Radios without a checked attribute are not tabbable
|
|
21
|
-
if (
|
|
21
|
+
if (localName === 'input' &&
|
|
22
22
|
el.getAttribute('type') === 'radio' &&
|
|
23
23
|
!el.hasAttribute('checked')) {
|
|
24
24
|
return false;
|
|
@@ -32,7 +32,8 @@ function isTabbable(el) {
|
|
|
32
32
|
return false;
|
|
33
33
|
}
|
|
34
34
|
// Audio and video elements with the controls attribute are tabbable
|
|
35
|
-
if ((
|
|
35
|
+
if ((localName === 'audio' || localName === 'video') &&
|
|
36
|
+
el.hasAttribute('controls')) {
|
|
36
37
|
return true;
|
|
37
38
|
}
|
|
38
39
|
// Elements with a tabindex other than -1 are tabbable
|
|
@@ -54,14 +55,13 @@ function isTabbable(el) {
|
|
|
54
55
|
'audio',
|
|
55
56
|
'video',
|
|
56
57
|
'summary',
|
|
57
|
-
].includes(
|
|
58
|
+
].includes(localName);
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
60
61
|
* Returns the first and last bounding elements that are tabbable. This is more performant than checking every single
|
|
61
62
|
* element because it short-circuits after finding the first and last ones.
|
|
62
63
|
*/
|
|
63
64
|
export function getTabbableBoundary(root) {
|
|
64
|
-
var _a, _b;
|
|
65
65
|
const allElements = [];
|
|
66
66
|
function walk(el) {
|
|
67
67
|
if (el instanceof HTMLElement) {
|
|
@@ -76,7 +76,7 @@ export function getTabbableBoundary(root) {
|
|
|
76
76
|
// Collect all elements including the root
|
|
77
77
|
walk(root);
|
|
78
78
|
// Find the first and last tabbable elements
|
|
79
|
-
const start =
|
|
80
|
-
const end =
|
|
79
|
+
const start = allElements.find((el) => isTabbable(el)) ?? null;
|
|
80
|
+
const end = allElements.reverse().find((el) => isTabbable(el)) ?? null;
|
|
81
81
|
return { start, end };
|
|
82
82
|
}
|
package/icons/arrow-right.js
CHANGED
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconArrowRight = class IconArrowRight extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="m10 17 5-5-5-5v10z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconArrowRight.styles = style;
|
|
|
12
12
|
IconArrowRight = __decorate([
|
|
13
13
|
customElement('mdui-icon-arrow-right')
|
|
14
14
|
], IconArrowRight);
|
|
15
|
+
export { IconArrowRight };
|
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconCancel_Outlined = class IconCancel_Outlined extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3.59-13L12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59 13.41 12 17 8.41z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconCancel_Outlined.styles = style;
|
|
|
12
12
|
IconCancel_Outlined = __decorate([
|
|
13
13
|
customElement('mdui-icon-cancel--outlined')
|
|
14
14
|
], IconCancel_Outlined);
|
|
15
|
+
export { IconCancel_Outlined };
|
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconCheckBoxOutlineBlank = class IconCheckBoxOutlineBlank extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconCheckBoxOutlineBlank.styles = style;
|
|
|
12
12
|
IconCheckBoxOutlineBlank = __decorate([
|
|
13
13
|
customElement('mdui-icon-check-box-outline-blank')
|
|
14
14
|
], IconCheckBoxOutlineBlank);
|
|
15
|
+
export { IconCheckBoxOutlineBlank };
|
package/icons/check-box.js
CHANGED
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconCheckBox = class IconCheckBox extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm-9 14-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconCheckBox.styles = style;
|
|
|
12
12
|
IconCheckBox = __decorate([
|
|
13
13
|
customElement('mdui-icon-check-box')
|
|
14
14
|
], IconCheckBox);
|
|
15
|
+
export { IconCheckBox };
|
package/icons/check.js
CHANGED
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconCheck = class IconCheck extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconCheck.styles = style;
|
|
|
12
12
|
IconCheck = __decorate([
|
|
13
13
|
customElement('mdui-icon-check')
|
|
14
14
|
], IconCheck);
|
|
15
|
+
export { IconCheck };
|
package/icons/circle.js
CHANGED
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconCircle = class IconCircle extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconCircle.styles = style;
|
|
|
12
12
|
IconCircle = __decorate([
|
|
13
13
|
customElement('mdui-icon-circle')
|
|
14
14
|
], IconCircle);
|
|
15
|
+
export { IconCircle };
|
package/icons/clear.js
CHANGED
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconClear = class IconClear extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconClear.styles = style;
|
|
|
12
12
|
IconClear = __decorate([
|
|
13
13
|
customElement('mdui-icon-clear')
|
|
14
14
|
], IconClear);
|
|
15
|
+
export { IconClear };
|
package/icons/error.js
CHANGED
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconError = class IconError extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconError.styles = style;
|
|
|
12
12
|
IconError = __decorate([
|
|
13
13
|
customElement('mdui-icon-error')
|
|
14
14
|
], IconError);
|
|
15
|
+
export { IconError };
|
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconIndeterminateCheckBox = class IconIndeterminateCheckBox extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10H7v-2h10v2z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconIndeterminateCheckBox.styles = style;
|
|
|
12
12
|
IconIndeterminateCheckBox = __decorate([
|
|
13
13
|
customElement('mdui-icon-indeterminate-check-box')
|
|
14
14
|
], IconIndeterminateCheckBox);
|
|
15
|
+
export { IconIndeterminateCheckBox };
|
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconRadioButtonUnchecked = class IconRadioButtonUnchecked extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconRadioButtonUnchecked.styles = style;
|
|
|
12
12
|
IconRadioButtonUnchecked = __decorate([
|
|
13
13
|
customElement('mdui-icon-radio-button-unchecked')
|
|
14
14
|
], IconRadioButtonUnchecked);
|
|
15
|
+
export { IconRadioButtonUnchecked };
|
package/icons/visibility-off.js
CHANGED
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconVisibilityOff = class IconVisibilityOff extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46A11.804 11.804 0 0 0 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78 3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconVisibilityOff.styles = style;
|
|
|
12
12
|
IconVisibilityOff = __decorate([
|
|
13
13
|
customElement('mdui-icon-visibility-off')
|
|
14
14
|
], IconVisibilityOff);
|
|
15
|
+
export { IconVisibilityOff };
|
package/icons/visibility.js
CHANGED
|
@@ -3,7 +3,7 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
4
4
|
import { style } from './shared/style.js';
|
|
5
5
|
import { svgTag } from './shared/svg-tag.js';
|
|
6
|
-
|
|
6
|
+
let IconVisibility = class IconVisibility extends LitElement {
|
|
7
7
|
render() {
|
|
8
8
|
return svgTag('<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>');
|
|
9
9
|
}
|
|
@@ -12,3 +12,4 @@ IconVisibility.styles = style;
|
|
|
12
12
|
IconVisibility = __decorate([
|
|
13
13
|
customElement('mdui-icon-visibility')
|
|
14
14
|
], IconVisibility);
|
|
15
|
+
export { IconVisibility };
|
package/mixins/anchor.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ type RenderAnchorOptions = {
|
|
|
8
8
|
className?: string;
|
|
9
9
|
part?: string;
|
|
10
10
|
content?: TemplateResult | typeof nothing | (TemplateResult | typeof nothing)[];
|
|
11
|
-
|
|
11
|
+
tabIndex?: number;
|
|
12
12
|
refDirective?: DirectiveResult<typeof RefDirective>;
|
|
13
13
|
};
|
|
14
14
|
export declare class AnchorMixinInterface extends LitElement {
|
package/mixins/anchor.js
CHANGED
|
@@ -4,8 +4,8 @@ import { property } from 'lit/decorators.js';
|
|
|
4
4
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
5
5
|
export const AnchorMixin = (superclass) => {
|
|
6
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>`;
|
|
7
|
+
renderAnchor({ id, className, part, content = html `<slot></slot>`, refDirective, tabIndex, }) {
|
|
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)}" tabindex="${ifDefined(tabIndex)}">${content}</a>`;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
__decorate([
|
package/mixins/focusable.d.ts
CHANGED
|
@@ -14,6 +14,6 @@ export declare class FocusableMixinInterface extends LitElement {
|
|
|
14
14
|
click(): void;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* 参考:https://github.com/adobe/spectrum-web-components/blob/main/
|
|
17
|
+
* 参考:https://github.com/adobe/spectrum-web-components/blob/main/tools/shared/src/focusable.ts
|
|
18
18
|
*/
|
|
19
19
|
export declare const FocusableMixin: <T extends Constructor<LitElement>>(superclass: T) => Constructor<FocusableMixinInterface> & T;
|
package/mixins/focusable.js
CHANGED
|
@@ -6,6 +6,7 @@ import '@mdui/jq/methods/attr.js';
|
|
|
6
6
|
import '@mdui/jq/methods/css.js';
|
|
7
7
|
import '@mdui/jq/methods/each.js';
|
|
8
8
|
import '@mdui/jq/methods/removeAttr.js';
|
|
9
|
+
import { DefinedController } from '../controllers/defined.js';
|
|
9
10
|
import { booleanConverter } from '../helpers/decorator.js';
|
|
10
11
|
let isClick = true;
|
|
11
12
|
const document = getDocument();
|
|
@@ -16,7 +17,7 @@ document.addEventListener('keydown', () => {
|
|
|
16
17
|
isClick = false;
|
|
17
18
|
});
|
|
18
19
|
/**
|
|
19
|
-
* 参考:https://github.com/adobe/spectrum-web-components/blob/main/
|
|
20
|
+
* 参考:https://github.com/adobe/spectrum-web-components/blob/main/tools/shared/src/focusable.ts
|
|
20
21
|
*/
|
|
21
22
|
export const FocusableMixin = (superclass) => {
|
|
22
23
|
class FocusableMixinClass extends superclass {
|
|
@@ -36,6 +37,7 @@ export const FocusableMixin = (superclass) => {
|
|
|
36
37
|
* 添加到 :host 元素上,供 CSS 选择器添加样式
|
|
37
38
|
*/
|
|
38
39
|
this.focusVisible = false;
|
|
40
|
+
this.focusableDefinedController = new DefinedController(this, { relatedElements: [''] });
|
|
39
41
|
this._manipulatingTabindex = false;
|
|
40
42
|
this._tabIndex = 0;
|
|
41
43
|
}
|
|
@@ -244,7 +246,7 @@ export const FocusableMixin = (superclass) => {
|
|
|
244
246
|
})
|
|
245
247
|
], FocusableMixinClass.prototype, "focusVisible", void 0);
|
|
246
248
|
__decorate([
|
|
247
|
-
property({ type: Number
|
|
249
|
+
property({ type: Number })
|
|
248
250
|
], FocusableMixinClass.prototype, "tabIndex", null);
|
|
249
251
|
// @ts-ignore
|
|
250
252
|
return FocusableMixinClass;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import '@mdui/jq/methods/css.js';
|
|
2
|
+
import { DefinedController } from '../controllers/defined.js';
|
|
2
3
|
import type { JQ } from '@mdui/jq/shared/core.js';
|
|
3
4
|
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
4
5
|
import type { LitElement } from 'lit';
|
|
@@ -8,6 +9,7 @@ export declare class ScrollBehaviorMixinInterface extends LitElement {
|
|
|
8
9
|
scrollTarget?: string | HTMLElement | JQ<HTMLElement>;
|
|
9
10
|
scrollBehavior?: ScrollBehavior;
|
|
10
11
|
scrollThreshold?: number;
|
|
12
|
+
protected scrollBehaviorDefinedController: DefinedController;
|
|
11
13
|
protected updateContainerPadding(): void;
|
|
12
14
|
protected hasScrollBehavior(behavior: ScrollBehavior | ScrollBehavior[]): boolean;
|
|
13
15
|
}
|
package/mixins/scrollBehavior.js
CHANGED
|
@@ -3,6 +3,7 @@ import { property } from 'lit/decorators.js';
|
|
|
3
3
|
import { $ } from '@mdui/jq/$.js';
|
|
4
4
|
import '@mdui/jq/methods/css.js';
|
|
5
5
|
import { isNodeName } from '@mdui/jq/shared/helper.js';
|
|
6
|
+
import { DefinedController } from '../controllers/defined.js';
|
|
6
7
|
import { watch } from '../decorators/watch.js';
|
|
7
8
|
/**
|
|
8
9
|
* 滚动行为
|
|
@@ -18,6 +19,9 @@ export const ScrollBehaviorMixin = (superclass) => {
|
|
|
18
19
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
20
|
constructor(...args) {
|
|
20
21
|
super(...args);
|
|
22
|
+
this.scrollBehaviorDefinedController = new DefinedController(this, {
|
|
23
|
+
needDomReady: true,
|
|
24
|
+
});
|
|
21
25
|
/**
|
|
22
26
|
* 上次滚动后,垂直方向的距离(滚动距离超过 scrollThreshold 才记录)
|
|
23
27
|
*/
|
|
@@ -38,7 +42,8 @@ export const ScrollBehaviorMixin = (superclass) => {
|
|
|
38
42
|
get scrollPaddingPosition() {
|
|
39
43
|
throw new Error('Must implement scrollPaddingPosition getter');
|
|
40
44
|
}
|
|
41
|
-
onScrollTargetChange(oldValue, newValue) {
|
|
45
|
+
async onScrollTargetChange(oldValue, newValue) {
|
|
46
|
+
await this.scrollBehaviorDefinedController.whenDefined();
|
|
42
47
|
// 仅在有值切换到无值、或无值切换到有值时,更新
|
|
43
48
|
if ((oldValue && !newValue) || (!oldValue && newValue)) {
|
|
44
49
|
this.updateContainerPadding();
|
|
@@ -56,7 +61,8 @@ export const ScrollBehaviorMixin = (superclass) => {
|
|
|
56
61
|
newListening.addEventListener('scroll', this.onListeningScroll);
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
|
-
onScrollBehaviorChange(oldValue, newValue) {
|
|
64
|
+
async onScrollBehaviorChange(oldValue, newValue) {
|
|
65
|
+
await this.scrollBehaviorDefinedController.whenDefined();
|
|
60
66
|
// 仅在有值切换到无值、或无值切换到有值时,更新
|
|
61
67
|
if ((oldValue && !newValue) || (!oldValue && newValue)) {
|
|
62
68
|
this.updateContainerPadding();
|
|
@@ -75,12 +81,16 @@ export const ScrollBehaviorMixin = (superclass) => {
|
|
|
75
81
|
}
|
|
76
82
|
connectedCallback() {
|
|
77
83
|
super.connectedCallback();
|
|
78
|
-
this.
|
|
79
|
-
|
|
84
|
+
this.scrollBehaviorDefinedController.whenDefined().then(() => {
|
|
85
|
+
this.isParentLayout = isNodeName(this.parentElement, 'mdui-layout');
|
|
86
|
+
this.updateContainerPadding();
|
|
87
|
+
});
|
|
80
88
|
}
|
|
81
89
|
disconnectedCallback() {
|
|
82
90
|
super.disconnectedCallback();
|
|
83
|
-
this.
|
|
91
|
+
this.scrollBehaviorDefinedController.whenDefined().then(() => {
|
|
92
|
+
this.updateContainerPadding(false);
|
|
93
|
+
});
|
|
84
94
|
}
|
|
85
95
|
/**
|
|
86
96
|
* scrollBehavior 包含多个滚动行为,用空格分割
|
|
@@ -88,8 +98,8 @@ export const ScrollBehaviorMixin = (superclass) => {
|
|
|
88
98
|
* @param behavior 为数组时,只要其中一个行为在 scrollBehavior 中,即返回 `true`
|
|
89
99
|
*/
|
|
90
100
|
hasScrollBehavior(behavior) {
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
const behaviors = (this.scrollBehavior?.split(' ') ??
|
|
102
|
+
[]);
|
|
93
103
|
if (Array.isArray(behavior)) {
|
|
94
104
|
return !!behaviors.filter((v) => behavior.includes(v)).length;
|
|
95
105
|
}
|
|
@@ -146,8 +156,7 @@ export const ScrollBehaviorMixin = (superclass) => {
|
|
|
146
156
|
* 滚动事件,这里过滤掉不符合条件的滚动
|
|
147
157
|
*/
|
|
148
158
|
onScroll(listening) {
|
|
149
|
-
|
|
150
|
-
const scrollTop = (_a = listening.scrollY) !== null && _a !== void 0 ? _a : listening.scrollTop;
|
|
159
|
+
const scrollTop = listening.scrollY ?? listening.scrollTop;
|
|
151
160
|
// 无视 scrollThreshold 的回调
|
|
152
161
|
if (this.lastScrollTopNoThreshold !== scrollTop) {
|
|
153
162
|
this.runScrollNoThreshold(scrollTop < this.lastScrollTopNoThreshold, scrollTop);
|
|
@@ -165,9 +174,8 @@ export const ScrollBehaviorMixin = (superclass) => {
|
|
|
165
174
|
* 用于在 scrollTarget、scrollBehavior 变更时,重新设置 lastScrollTopThreshold、lastScrollTopNoThreshold 的初始值
|
|
166
175
|
*/
|
|
167
176
|
updateScrollTop(listening) {
|
|
168
|
-
var _a;
|
|
169
177
|
this.lastScrollTopThreshold = this.lastScrollTopNoThreshold =
|
|
170
|
-
|
|
178
|
+
listening.scrollY ?? listening.scrollTop;
|
|
171
179
|
}
|
|
172
180
|
/**
|
|
173
181
|
* 获取组件需要监听哪个元素的滚动状态
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mdui/shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "mdui 项目的公共部分",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/zdhxiong/mdui#readme",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@open-wc/dedupe-mixin": "^1.
|
|
30
|
-
"lit": "^
|
|
29
|
+
"@open-wc/dedupe-mixin": "^1.4.0",
|
|
30
|
+
"lit": "^3.0.0",
|
|
31
31
|
"ssr-window": "^4.0.2",
|
|
32
|
-
"tslib": "^2.6.
|
|
33
|
-
"@mdui/jq": "3.0.
|
|
32
|
+
"tslib": "^2.6.2",
|
|
33
|
+
"@mdui/jq": "^3.0.1"
|
|
34
34
|
}
|
|
35
35
|
}
|