@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.
- package/LICENSE +21 -0
- package/README.md +14 -0
- package/controllers/form.d.ts +53 -0
- package/controllers/form.js +214 -0
- package/controllers/has-slot.d.ts +17 -0
- package/controllers/has-slot.js +46 -0
- package/controllers/hover.d.ts +34 -0
- package/controllers/hover.js +62 -0
- package/decorators/default-value.d.ts +16 -0
- package/decorators/default-value.js +40 -0
- package/decorators/watch.d.ts +17 -0
- package/decorators/watch.js +37 -0
- package/helpers/animate.d.ts +2 -0
- package/helpers/animate.js +37 -0
- package/helpers/breakpoint.d.ts +49 -0
- package/helpers/breakpoint.js +102 -0
- package/helpers/decorator.d.ts +16 -0
- package/helpers/decorator.js +18 -0
- package/helpers/delay.d.ts +4 -0
- package/helpers/delay.js +6 -0
- package/helpers/event.d.ts +7 -0
- package/helpers/event.js +17 -0
- package/helpers/modal.d.ts +12 -0
- package/helpers/modal.js +52 -0
- package/helpers/motion.d.ts +13 -0
- package/helpers/motion.js +20 -0
- package/helpers/observeResize.d.ts +16 -0
- package/helpers/observeResize.js +57 -0
- package/helpers/queue.d.ts +18 -0
- package/helpers/queue.js +25 -0
- package/helpers/scroll.d.ts +14 -0
- package/helpers/scroll.js +28 -0
- package/helpers/slot.d.ts +10 -0
- package/helpers/slot.js +31 -0
- package/helpers/tabbable.d.ts +8 -0
- package/helpers/tabbable.js +82 -0
- package/helpers/template.d.ts +2 -0
- package/helpers/template.js +2 -0
- package/helpers/throttle.d.ts +14 -0
- package/helpers/throttle.js +28 -0
- package/helpers/uniqueId.d.ts +4 -0
- package/helpers/uniqueId.js +7 -0
- package/icons/arrow-right.d.ts +11 -0
- package/icons/arrow-right.js +14 -0
- package/icons/cancel--outlined.d.ts +11 -0
- package/icons/cancel--outlined.js +14 -0
- package/icons/check-box-outline-blank.d.ts +11 -0
- package/icons/check-box-outline-blank.js +14 -0
- package/icons/check-box.d.ts +11 -0
- package/icons/check-box.js +14 -0
- package/icons/check.d.ts +11 -0
- package/icons/check.js +14 -0
- package/icons/circle.d.ts +11 -0
- package/icons/circle.js +14 -0
- package/icons/clear.d.ts +11 -0
- package/icons/clear.js +14 -0
- package/icons/error.d.ts +11 -0
- package/icons/error.js +14 -0
- package/icons/indeterminate-check-box.d.ts +11 -0
- package/icons/indeterminate-check-box.js +14 -0
- package/icons/radio-button-unchecked.d.ts +11 -0
- package/icons/radio-button-unchecked.js +14 -0
- package/icons/shared/style.d.ts +1 -0
- package/icons/shared/style.js +2 -0
- package/icons/shared/svg-tag.d.ts +2 -0
- package/icons/shared/svg-tag.js +3 -0
- package/icons/visibility-off.d.ts +11 -0
- package/icons/visibility-off.js +14 -0
- package/icons/visibility.d.ts +11 -0
- package/icons/visibility.js +14 -0
- package/lit-styles/component-style.d.ts +1 -0
- package/lit-styles/component-style.js +2 -0
- package/mixin.less +380 -0
- package/mixins/anchor.d.ts +22 -0
- package/mixins/anchor.js +25 -0
- package/mixins/focusable.d.ts +19 -0
- package/mixins/focusable.js +251 -0
- package/mixins/scrollBehavior.d.ts +24 -0
- package/mixins/scrollBehavior.js +202 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021-present zdhxiong@gmail.com
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import '@mdui/jq/methods/attr.js';
|
|
2
|
+
import '@mdui/jq/methods/css.js';
|
|
3
|
+
import type { FormControl, FormControlValue } from '@mdui/jq/shared/form.js';
|
|
4
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
5
|
+
/**
|
|
6
|
+
* 在执行表单的 reset() 方法后,使用该 WeakMap 存储指定表单中所有的表单控件
|
|
7
|
+
* 在表单控件中监听值变更后,需要从该 WeakMap 中判断是否存在该表单控件,
|
|
8
|
+
* 若存在,则 invalid 设置为 false(不显示验证不通过样式),同时从 WeakMap 中移除该表单控件
|
|
9
|
+
*/
|
|
10
|
+
export declare const formResets: WeakMap<HTMLFormElement, Set<FormControl>>;
|
|
11
|
+
export interface FormControllerOptions {
|
|
12
|
+
form: (control: FormControl) => HTMLFormElement | null;
|
|
13
|
+
name: (control: FormControl) => string;
|
|
14
|
+
value: (control: FormControl) => FormControlValue | undefined;
|
|
15
|
+
defaultValue: (control: FormControl) => FormControlValue;
|
|
16
|
+
setValue: (control: FormControl, value: FormControlValue) => void;
|
|
17
|
+
disabled: (control: FormControl) => boolean;
|
|
18
|
+
reportValidity: (control: FormControl) => boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare class FormController implements ReactiveController {
|
|
21
|
+
private host;
|
|
22
|
+
private form?;
|
|
23
|
+
private options;
|
|
24
|
+
constructor(host: ReactiveControllerHost & FormControl, options?: Partial<FormControllerOptions>);
|
|
25
|
+
hostConnected(): void;
|
|
26
|
+
hostDisconnected(): void;
|
|
27
|
+
hostUpdated(): void;
|
|
28
|
+
/**
|
|
29
|
+
* 获取当前表单控件关联的 `<form>` 元素
|
|
30
|
+
*/
|
|
31
|
+
getForm(): HTMLFormElement | null;
|
|
32
|
+
/**
|
|
33
|
+
* 重置整个表单,所有表单控件恢复成默认值
|
|
34
|
+
*/
|
|
35
|
+
reset(invoker?: HTMLElement & {
|
|
36
|
+
name: string;
|
|
37
|
+
value: string;
|
|
38
|
+
}): void;
|
|
39
|
+
/**
|
|
40
|
+
* 提交整个表单
|
|
41
|
+
*/
|
|
42
|
+
submit(invoker?: HTMLElement & {
|
|
43
|
+
name: string;
|
|
44
|
+
value: string;
|
|
45
|
+
}): void;
|
|
46
|
+
private attachForm;
|
|
47
|
+
private detachForm;
|
|
48
|
+
private doAction;
|
|
49
|
+
private onFormData;
|
|
50
|
+
private onFormSubmit;
|
|
51
|
+
private onFormReset;
|
|
52
|
+
private reportFormValidity;
|
|
53
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 参考:https://github.com/shoelace-style/shoelace/blob/next/src/internal/form.ts
|
|
3
|
+
*/
|
|
4
|
+
import { $ } from '@mdui/jq/$.js';
|
|
5
|
+
import '@mdui/jq/methods/attr.js';
|
|
6
|
+
import '@mdui/jq/methods/css.js';
|
|
7
|
+
import { formCollections } from '@mdui/jq/shared/form.js';
|
|
8
|
+
import { isFunction, isString, isUndefined } from '@mdui/jq/shared/helper.js';
|
|
9
|
+
/**
|
|
10
|
+
* 在执行 `<form>` 元素的 reportValidity() 时,不会执行 mdui 组件的 reportValidity() 方法,
|
|
11
|
+
* 因此在 mdui 表单控件的 hostConnected 中把 `<form>` 的 reportValidity 替换为自定义方法,
|
|
12
|
+
* hostDisconnected 中恢复为 原生 reportValidity 方法
|
|
13
|
+
*
|
|
14
|
+
* 该 WeakMap 用于存储指定 `<form>` 的原生 reportValidity 方法
|
|
15
|
+
*
|
|
16
|
+
* 日后使用 ElementInternals 可不再进行该处理,但当前 safari 浏览器不支持。
|
|
17
|
+
*/
|
|
18
|
+
const reportValidityOverloads = new WeakMap();
|
|
19
|
+
/**
|
|
20
|
+
* 在执行表单的 reset() 方法后,使用该 WeakMap 存储指定表单中所有的表单控件
|
|
21
|
+
* 在表单控件中监听值变更后,需要从该 WeakMap 中判断是否存在该表单控件,
|
|
22
|
+
* 若存在,则 invalid 设置为 false(不显示验证不通过样式),同时从 WeakMap 中移除该表单控件
|
|
23
|
+
*/
|
|
24
|
+
export const formResets = new WeakMap();
|
|
25
|
+
export class FormController {
|
|
26
|
+
constructor(host, options) {
|
|
27
|
+
(this.host = host).addController(this);
|
|
28
|
+
this.options = {
|
|
29
|
+
form: (control) => {
|
|
30
|
+
const formId = $(control).attr('form');
|
|
31
|
+
if (formId) {
|
|
32
|
+
const root = control.getRootNode();
|
|
33
|
+
return root.getElementById(formId);
|
|
34
|
+
}
|
|
35
|
+
return control.closest('form');
|
|
36
|
+
},
|
|
37
|
+
name: (control) => control.name,
|
|
38
|
+
value: (control) => control.value,
|
|
39
|
+
defaultValue: (control) => control.defaultValue,
|
|
40
|
+
setValue: (control, value) => (control.value = value),
|
|
41
|
+
disabled: (control) => control.disabled,
|
|
42
|
+
reportValidity: (control) => isFunction(control.reportValidity) ? control.reportValidity() : true,
|
|
43
|
+
...options,
|
|
44
|
+
};
|
|
45
|
+
this.onFormData = this.onFormData.bind(this);
|
|
46
|
+
this.onFormSubmit = this.onFormSubmit.bind(this);
|
|
47
|
+
this.onFormReset = this.onFormReset.bind(this);
|
|
48
|
+
this.reportFormValidity = this.reportFormValidity.bind(this);
|
|
49
|
+
}
|
|
50
|
+
hostConnected() {
|
|
51
|
+
this.form = this.options.form(this.host);
|
|
52
|
+
if (this.form) {
|
|
53
|
+
this.attachForm(this.form);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
hostDisconnected() {
|
|
57
|
+
this.detachForm();
|
|
58
|
+
}
|
|
59
|
+
hostUpdated() {
|
|
60
|
+
const form = this.options.form(this.host);
|
|
61
|
+
if (!form) {
|
|
62
|
+
this.detachForm();
|
|
63
|
+
}
|
|
64
|
+
if (form && this.form !== form) {
|
|
65
|
+
this.detachForm();
|
|
66
|
+
this.attachForm(form);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 获取当前表单控件关联的 `<form>` 元素
|
|
71
|
+
*/
|
|
72
|
+
getForm() {
|
|
73
|
+
var _a;
|
|
74
|
+
return (_a = this.form) !== null && _a !== void 0 ? _a : null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 重置整个表单,所有表单控件恢复成默认值
|
|
78
|
+
*/
|
|
79
|
+
reset(invoker) {
|
|
80
|
+
this.doAction('reset', invoker);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 提交整个表单
|
|
84
|
+
*/
|
|
85
|
+
submit(invoker) {
|
|
86
|
+
this.doAction('submit', invoker);
|
|
87
|
+
}
|
|
88
|
+
attachForm(form) {
|
|
89
|
+
if (!form) {
|
|
90
|
+
this.form = undefined;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.form = form;
|
|
94
|
+
if (formCollections.has(this.form)) {
|
|
95
|
+
formCollections.get(this.form).add(this.host);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
formCollections.set(this.form, new Set([this.host]));
|
|
99
|
+
}
|
|
100
|
+
this.form.addEventListener('formdata', this.onFormData);
|
|
101
|
+
this.form.addEventListener('submit', this.onFormSubmit);
|
|
102
|
+
this.form.addEventListener('reset', this.onFormReset);
|
|
103
|
+
if (!reportValidityOverloads.has(this.form)) {
|
|
104
|
+
reportValidityOverloads.set(this.form, this.form.reportValidity);
|
|
105
|
+
this.form.reportValidity = () => this.reportFormValidity();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
detachForm() {
|
|
109
|
+
if (this.form) {
|
|
110
|
+
formCollections.get(this.form).delete(this.host);
|
|
111
|
+
this.form.removeEventListener('formdata', this.onFormData);
|
|
112
|
+
this.form.removeEventListener('submit', this.onFormSubmit);
|
|
113
|
+
this.form.removeEventListener('reset', this.onFormReset);
|
|
114
|
+
if (reportValidityOverloads.has(this.form) &&
|
|
115
|
+
!formCollections.get(this.form).size) {
|
|
116
|
+
this.form.reportValidity = reportValidityOverloads.get(this.form);
|
|
117
|
+
reportValidityOverloads.delete(this.form);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
doAction(type, invoker) {
|
|
122
|
+
if (!this.form) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const $button = $(`<button type="${type}">`).css({
|
|
126
|
+
position: 'absolute',
|
|
127
|
+
width: 0,
|
|
128
|
+
height: 0,
|
|
129
|
+
clipPath: 'inset(50%)',
|
|
130
|
+
overflow: 'hidden',
|
|
131
|
+
whiteSpace: 'nowrap',
|
|
132
|
+
});
|
|
133
|
+
const button = $button[0];
|
|
134
|
+
if (invoker) {
|
|
135
|
+
button.name = invoker.name;
|
|
136
|
+
button.value = invoker.value;
|
|
137
|
+
[
|
|
138
|
+
'formaction',
|
|
139
|
+
'formenctype',
|
|
140
|
+
'formmethod',
|
|
141
|
+
'formnovalidate',
|
|
142
|
+
'formtarget',
|
|
143
|
+
].forEach((attr) => {
|
|
144
|
+
$button.attr(attr, $(invoker).attr(attr));
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
this.form.append(button);
|
|
148
|
+
button.click();
|
|
149
|
+
button.remove();
|
|
150
|
+
}
|
|
151
|
+
onFormData(event) {
|
|
152
|
+
const disabled = this.options.disabled(this.host);
|
|
153
|
+
const name = this.options.name(this.host);
|
|
154
|
+
const value = this.options.value(this.host);
|
|
155
|
+
// 对于按钮,仅在 type="submit" 时,才提交值。已在 doAction() 方法中把 name、value 注入到 <button> 元素上
|
|
156
|
+
const isButton = [
|
|
157
|
+
'mdui-button',
|
|
158
|
+
'mdui-button-icon',
|
|
159
|
+
'mdui-chip',
|
|
160
|
+
'mdui-fab',
|
|
161
|
+
'mdui-segmented-button',
|
|
162
|
+
].includes(this.host.tagName.toLowerCase());
|
|
163
|
+
if (!disabled &&
|
|
164
|
+
!isButton &&
|
|
165
|
+
isString(name) &&
|
|
166
|
+
name &&
|
|
167
|
+
!isUndefined(value)) {
|
|
168
|
+
if (Array.isArray(value)) {
|
|
169
|
+
value.forEach((val) => {
|
|
170
|
+
event.formData.append(name, val.toString());
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
event.formData.append(name, value.toString());
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
onFormSubmit(event) {
|
|
179
|
+
const disabled = this.options.disabled(this.host);
|
|
180
|
+
const reportValidity = this.options.reportValidity;
|
|
181
|
+
if (this.form &&
|
|
182
|
+
!this.form.noValidate &&
|
|
183
|
+
!disabled &&
|
|
184
|
+
!reportValidity(this.host)) {
|
|
185
|
+
event.preventDefault();
|
|
186
|
+
event.stopImmediatePropagation();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
onFormReset() {
|
|
190
|
+
if (this.form) {
|
|
191
|
+
this.options.setValue(this.host, this.options.defaultValue(this.host));
|
|
192
|
+
// 取消 invalid 状态。
|
|
193
|
+
// 此外,还需要在各个组件内,监听值的变更,判断 formResets 中是否存在当前表单控件。若存在则 invalid 设为 false;不存在则设置为 checkValidity() 的值
|
|
194
|
+
// @ts-ignore
|
|
195
|
+
this.host.invalid = false;
|
|
196
|
+
if (formResets.has(this.form)) {
|
|
197
|
+
formResets.get(this.form).add(this.host);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
formResets.set(this.form, new Set([this.host]));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
reportFormValidity() {
|
|
205
|
+
if (this.form && !this.form.noValidate) {
|
|
206
|
+
for (const element of this.form.querySelectorAll('*')) {
|
|
207
|
+
if (isFunction(element.reportValidity) && !element.reportValidity()) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
type SlotName = '[default]' | string;
|
|
3
|
+
/**
|
|
4
|
+
* 检查指定的 slot 是否存在
|
|
5
|
+
*/
|
|
6
|
+
export declare class HasSlotController implements ReactiveController {
|
|
7
|
+
private host;
|
|
8
|
+
private slotNames;
|
|
9
|
+
constructor(host: ReactiveControllerHost & Element, ...slotNames: SlotName[]);
|
|
10
|
+
hostConnected(): void;
|
|
11
|
+
hostDisconnected(): void;
|
|
12
|
+
test(slotName: SlotName): boolean;
|
|
13
|
+
private hasDefaultSlot;
|
|
14
|
+
private hasNamedSlot;
|
|
15
|
+
private onSlotChange;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 检查指定的 slot 是否存在
|
|
3
|
+
*/
|
|
4
|
+
export class HasSlotController {
|
|
5
|
+
constructor(host, ...slotNames) {
|
|
6
|
+
this.slotNames = [];
|
|
7
|
+
(this.host = host).addController(this);
|
|
8
|
+
this.slotNames = slotNames;
|
|
9
|
+
this.onSlotChange = this.onSlotChange.bind(this);
|
|
10
|
+
}
|
|
11
|
+
hostConnected() {
|
|
12
|
+
this.host.shadowRoot.addEventListener('slotchange', this.onSlotChange);
|
|
13
|
+
}
|
|
14
|
+
hostDisconnected() {
|
|
15
|
+
this.host.shadowRoot.removeEventListener('slotchange', this.onSlotChange);
|
|
16
|
+
}
|
|
17
|
+
test(slotName) {
|
|
18
|
+
return slotName === '[default]'
|
|
19
|
+
? this.hasDefaultSlot()
|
|
20
|
+
: this.hasNamedSlot(slotName);
|
|
21
|
+
}
|
|
22
|
+
hasDefaultSlot() {
|
|
23
|
+
return [...this.host.childNodes].some((node) => {
|
|
24
|
+
if (node.nodeType === node.TEXT_NODE && node.textContent.trim() !== '') {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (node.nodeType === node.ELEMENT_NODE) {
|
|
28
|
+
const el = node;
|
|
29
|
+
if (!el.hasAttribute('slot')) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
hasNamedSlot(name) {
|
|
37
|
+
return this.host.querySelector(`:scope > [slot="${name}"]`) !== null;
|
|
38
|
+
}
|
|
39
|
+
onSlotChange(event) {
|
|
40
|
+
const slot = event.target;
|
|
41
|
+
if ((this.slotNames.includes('[default]') && !slot.name) ||
|
|
42
|
+
(slot.name && this.slotNames.includes(slot.name))) {
|
|
43
|
+
this.host.requestUpdate();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import '@mdui/jq/methods/off.js';
|
|
2
|
+
import '@mdui/jq/methods/on.js';
|
|
3
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
4
|
+
import type { Ref } from 'lit/directives/ref.js';
|
|
5
|
+
/**
|
|
6
|
+
* 检查当前鼠标是否放在指定元素上,及进入、离开元素执行对于的回调
|
|
7
|
+
*/
|
|
8
|
+
export declare class HoverController implements ReactiveController {
|
|
9
|
+
/**
|
|
10
|
+
* 当前鼠标是否放在元素上
|
|
11
|
+
*/
|
|
12
|
+
isHover: boolean;
|
|
13
|
+
private readonly host;
|
|
14
|
+
private readonly elementRef;
|
|
15
|
+
private readonly uniqueID;
|
|
16
|
+
private readonly enterEventName;
|
|
17
|
+
private readonly leaveEventName;
|
|
18
|
+
private mouseEnterItems;
|
|
19
|
+
private mouseLeaveItems;
|
|
20
|
+
constructor(host: ReactiveControllerHost & Element, elementRef: Ref<HTMLElement>);
|
|
21
|
+
hostDisconnected(): void;
|
|
22
|
+
/**
|
|
23
|
+
* 指定鼠标移入时的回调函数
|
|
24
|
+
* @param callback
|
|
25
|
+
* @param one 是否仅执行一次
|
|
26
|
+
*/
|
|
27
|
+
onMouseEnter(callback: () => void, one?: boolean): void;
|
|
28
|
+
/**
|
|
29
|
+
* 指定鼠标移出时的回调函数
|
|
30
|
+
* @param callback
|
|
31
|
+
* @param one 是否仅执行一次
|
|
32
|
+
*/
|
|
33
|
+
onMouseLeave(callback: () => void, one?: boolean): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { $ } from '@mdui/jq/$.js';
|
|
2
|
+
import '@mdui/jq/methods/off.js';
|
|
3
|
+
import '@mdui/jq/methods/on.js';
|
|
4
|
+
import { uniqueId } from '../helpers/uniqueId.js';
|
|
5
|
+
/**
|
|
6
|
+
* 检查当前鼠标是否放在指定元素上,及进入、离开元素执行对于的回调
|
|
7
|
+
*/
|
|
8
|
+
export class HoverController {
|
|
9
|
+
constructor(host, elementRef) {
|
|
10
|
+
/**
|
|
11
|
+
* 当前鼠标是否放在元素上
|
|
12
|
+
*/
|
|
13
|
+
this.isHover = false;
|
|
14
|
+
this.uniqueID = uniqueId();
|
|
15
|
+
this.enterEventName = `mouseenter.${this.uniqueID}.hoverController`;
|
|
16
|
+
this.leaveEventName = `mouseleave.${this.uniqueID}.hoverController`;
|
|
17
|
+
this.mouseEnterItems = [];
|
|
18
|
+
this.mouseLeaveItems = [];
|
|
19
|
+
(this.host = host).addController(this);
|
|
20
|
+
this.elementRef = elementRef;
|
|
21
|
+
this.host.updateComplete.then(() => {
|
|
22
|
+
$(this.elementRef.value)
|
|
23
|
+
.on(this.enterEventName, () => {
|
|
24
|
+
this.isHover = true;
|
|
25
|
+
this.mouseEnterItems.forEach((item, index, items) => {
|
|
26
|
+
item.callback();
|
|
27
|
+
if (item.one) {
|
|
28
|
+
items.splice(index, 1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
})
|
|
32
|
+
.on(this.leaveEventName, () => {
|
|
33
|
+
this.isHover = false;
|
|
34
|
+
this.mouseLeaveItems.forEach((item, index, items) => {
|
|
35
|
+
item.callback();
|
|
36
|
+
if (item.one) {
|
|
37
|
+
items.splice(index, 1);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
hostDisconnected() {
|
|
44
|
+
$(this.elementRef.value).off(this.enterEventName).off(this.leaveEventName);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 指定鼠标移入时的回调函数
|
|
48
|
+
* @param callback
|
|
49
|
+
* @param one 是否仅执行一次
|
|
50
|
+
*/
|
|
51
|
+
onMouseEnter(callback, one = false) {
|
|
52
|
+
this.mouseEnterItems.push({ callback, one });
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 指定鼠标移出时的回调函数
|
|
56
|
+
* @param callback
|
|
57
|
+
* @param one 是否仅执行一次
|
|
58
|
+
*/
|
|
59
|
+
onMouseLeave(callback, one = false) {
|
|
60
|
+
this.mouseLeaveItems.push({ callback, one });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defaultValue 装饰器。在 attribute 属性变更时,若值和 property 值不一致,则会保存新的 attribute 值
|
|
3
|
+
*
|
|
4
|
+
* 用于在调用表单的 reset() 方法时,还原成初始值
|
|
5
|
+
*
|
|
6
|
+
* @property({ reflect: true }) value = '';
|
|
7
|
+
* @defaultValue() defaultValue = '';
|
|
8
|
+
*
|
|
9
|
+
* @property({ type: Boolean, reflect: true }) checked = false;
|
|
10
|
+
* @defaultValue('checked') defaultChecked = false;
|
|
11
|
+
*/
|
|
12
|
+
import type { ReactiveElement } from 'lit';
|
|
13
|
+
/**
|
|
14
|
+
* @param propertyName 对应的属性名
|
|
15
|
+
*/
|
|
16
|
+
export declare function defaultValue(propertyName?: string): (proto: ReactiveElement, key: string) => void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defaultValue 装饰器。在 attribute 属性变更时,若值和 property 值不一致,则会保存新的 attribute 值
|
|
3
|
+
*
|
|
4
|
+
* 用于在调用表单的 reset() 方法时,还原成初始值
|
|
5
|
+
*
|
|
6
|
+
* @property({ reflect: true }) value = '';
|
|
7
|
+
* @defaultValue() defaultValue = '';
|
|
8
|
+
*
|
|
9
|
+
* @property({ type: Boolean, reflect: true }) checked = false;
|
|
10
|
+
* @defaultValue('checked') defaultChecked = false;
|
|
11
|
+
*/
|
|
12
|
+
import { defaultConverter } from 'lit';
|
|
13
|
+
import { isFunction, isString } from '@mdui/jq/shared/helper.js';
|
|
14
|
+
/**
|
|
15
|
+
* @param propertyName 对应的属性名
|
|
16
|
+
*/
|
|
17
|
+
export function defaultValue(propertyName = 'value') {
|
|
18
|
+
return (proto, key) => {
|
|
19
|
+
const constructor = proto.constructor;
|
|
20
|
+
const attributeChangedCallback = constructor.prototype.attributeChangedCallback;
|
|
21
|
+
constructor.prototype.attributeChangedCallback = function (name, old, value) {
|
|
22
|
+
var _a;
|
|
23
|
+
const options = constructor.getPropertyOptions(propertyName);
|
|
24
|
+
const attributeName = isString(options.attribute)
|
|
25
|
+
? options.attribute
|
|
26
|
+
: propertyName;
|
|
27
|
+
if (name === attributeName) {
|
|
28
|
+
const converter = options.converter || defaultConverter;
|
|
29
|
+
const fromAttribute = isFunction(converter)
|
|
30
|
+
? converter
|
|
31
|
+
: (_a = converter === null || converter === void 0 ? void 0 : converter.fromAttribute) !== null && _a !== void 0 ? _a : defaultConverter.fromAttribute;
|
|
32
|
+
const newValue = fromAttribute(value, options.type);
|
|
33
|
+
if (this[propertyName] !== newValue) {
|
|
34
|
+
this[key] = newValue;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
attributeChangedCallback.call(this, name, old, value);
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* watch 装饰器。在 @property 或 @state 属性变更时,且在组件更新前触发
|
|
3
|
+
*
|
|
4
|
+
* 如果要等属性变更后,且组件更新完成再执行,可以在函数中执行 `await this.updateComplete`
|
|
5
|
+
* 如果要等组件首次渲染完后再监听属性,可以传入第二个参数 true。或者在函数中通过 `this.hasUpdated` 进行判断
|
|
6
|
+
*
|
|
7
|
+
* @watch('propName')
|
|
8
|
+
* handlePropChange(oldValue, newValue) {
|
|
9
|
+
*
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
import type { LitElement } from 'lit';
|
|
13
|
+
/**
|
|
14
|
+
* @param propName 监听的属性名
|
|
15
|
+
* @param waitUntilFirstUpdate 是否等首次渲染完后再监听
|
|
16
|
+
*/
|
|
17
|
+
export declare function watch(propName: string, waitUntilFirstUpdate?: boolean): <T extends LitElement>(proto: T, functionName: string) => void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* watch 装饰器。在 @property 或 @state 属性变更时,且在组件更新前触发
|
|
3
|
+
*
|
|
4
|
+
* 如果要等属性变更后,且组件更新完成再执行,可以在函数中执行 `await this.updateComplete`
|
|
5
|
+
* 如果要等组件首次渲染完后再监听属性,可以传入第二个参数 true。或者在函数中通过 `this.hasUpdated` 进行判断
|
|
6
|
+
*
|
|
7
|
+
* @watch('propName')
|
|
8
|
+
* handlePropChange(oldValue, newValue) {
|
|
9
|
+
*
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* @param propName 监听的属性名
|
|
14
|
+
* @param waitUntilFirstUpdate 是否等首次渲染完后再监听
|
|
15
|
+
*/
|
|
16
|
+
export function watch(propName, waitUntilFirstUpdate = false) {
|
|
17
|
+
return (proto, functionName) => {
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
const { update } = proto;
|
|
20
|
+
if (propName in proto) {
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
proto.update = function (changedProperties) {
|
|
23
|
+
if (changedProperties.has(propName)) {
|
|
24
|
+
const oldValue = changedProperties.get(propName);
|
|
25
|
+
const newValue = this[propName];
|
|
26
|
+
if (oldValue !== newValue) {
|
|
27
|
+
if (!waitUntilFirstUpdate || this.hasUpdated) {
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
this[functionName](oldValue, newValue);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
update.call(this, changedProperties);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isNumber } from '@mdui/jq/shared/helper.js';
|
|
2
|
+
export function animateTo(el, keyframes, options) {
|
|
3
|
+
if (!el) {
|
|
4
|
+
return Promise.resolve();
|
|
5
|
+
}
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
if (options.duration === Infinity) {
|
|
8
|
+
throw new Error('Promise-based animations must be finite.');
|
|
9
|
+
}
|
|
10
|
+
if (isNumber(options.duration) && isNaN(options.duration)) {
|
|
11
|
+
options.duration = 0;
|
|
12
|
+
}
|
|
13
|
+
if (options.easing === '') {
|
|
14
|
+
options.easing = 'linear';
|
|
15
|
+
}
|
|
16
|
+
const animation = el.animate(keyframes, options);
|
|
17
|
+
animation.addEventListener('cancel', resolve, { once: true });
|
|
18
|
+
animation.addEventListener('finish', resolve, { once: true });
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export function stopAnimations(el) {
|
|
22
|
+
if (!el) {
|
|
23
|
+
return Promise.resolve();
|
|
24
|
+
}
|
|
25
|
+
return Promise.all(el.getAnimations().map((animation) => {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
const handleAnimationEvent = requestAnimationFrame(resolve);
|
|
28
|
+
animation.addEventListener('cancel', () => handleAnimationEvent, {
|
|
29
|
+
once: true,
|
|
30
|
+
});
|
|
31
|
+
animation.addEventListener('finish', () => handleAnimationEvent, {
|
|
32
|
+
once: true,
|
|
33
|
+
});
|
|
34
|
+
animation.cancel();
|
|
35
|
+
});
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import '@mdui/jq/methods/innerWidth.js';
|
|
2
|
+
import type { JQ } from '@mdui/jq/shared/core.js';
|
|
3
|
+
export type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
|
|
4
|
+
/**
|
|
5
|
+
* 获取断点对象,通过返回的对象可用于判断指定宽度、或指定元素的宽度、或当前窗口宽度与各个断点值的关系
|
|
6
|
+
*
|
|
7
|
+
* * 未传入参数时,获取的是 `window` 的宽度对应的断点对象
|
|
8
|
+
* * 若传入数值,则获取的是该数值宽度对应的断点对象
|
|
9
|
+
* * 若传入 CSS 选择器,则获取的是该选择器对应元素的宽度对应的断点对象
|
|
10
|
+
* * 若传入 HTML 元素,则获取的是该元素的宽度对应的断点对象
|
|
11
|
+
* * 若传入 JQ 对象,则获取的是该 JQ 对象中的元素的宽度对应的断点对象
|
|
12
|
+
*
|
|
13
|
+
* 返回的对象包含以下方法:
|
|
14
|
+
*
|
|
15
|
+
* * `up(breakpoint)`:判断当前宽度是否大于指定断点值
|
|
16
|
+
* * `down(breakpoint)`:判断当前宽度是否小于指定断点值
|
|
17
|
+
* * `only(breakpoint)`:判断当前宽度是否在指定断点值内
|
|
18
|
+
* * `not(breakpoint)`:判断当前宽度是否不在指定断点值内
|
|
19
|
+
* * `between(startBreakpoint, endBreakpoint)`:判断当前宽度是否在指定断点值之间
|
|
20
|
+
*/
|
|
21
|
+
export declare const breakpoint: (width?: number | string | HTMLElement | JQ<HTMLElement>) => {
|
|
22
|
+
/**
|
|
23
|
+
* 当前宽度是否大于指定断点值
|
|
24
|
+
* @param breakpoint
|
|
25
|
+
*/
|
|
26
|
+
up(breakpoint: Breakpoint): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* 当前宽度是否小于指定断点值
|
|
29
|
+
* @param breakpoint
|
|
30
|
+
*/
|
|
31
|
+
down(breakpoint: Breakpoint): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* 当前宽度是否在指定断点值内
|
|
34
|
+
* @param breakpoint
|
|
35
|
+
*/
|
|
36
|
+
only(breakpoint: Breakpoint): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 当前宽度是否不在指定断点值内
|
|
39
|
+
* @param breakpoint
|
|
40
|
+
*/
|
|
41
|
+
not(breakpoint: Breakpoint): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 当前宽度是否在指定断点值之间
|
|
44
|
+
* @param startBreakpoint
|
|
45
|
+
* @param endBreakpoint
|
|
46
|
+
* @returns
|
|
47
|
+
*/
|
|
48
|
+
between(startBreakpoint: Breakpoint, endBreakpoint: Breakpoint): boolean;
|
|
49
|
+
};
|