fix: 本地 patch MDUI 以解决 tabindex = 0 导致的一系列玄学问题
This commit is contained in:
1
client/mdui_patched/components/collapse/collapse-item-style.d.ts
vendored
Normal file
1
client/mdui_patched/components/collapse/collapse-item-style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const collapseItemStyle: import("lit").CSSResult;
|
||||
@@ -0,0 +1,2 @@
|
||||
import { css } from 'lit';
|
||||
export const collapseItemStyle = css `:host{display:flex;flex-direction:column}.header{display:block}.body{display:block;overflow:hidden;transition:height var(--mdui-motion-duration-short4) var(--mdui-motion-easing-emphasized)}.body.opened{overflow:visible}.body.active{transition-duration:var(--mdui-motion-duration-medium4)}`;
|
||||
68
client/mdui_patched/components/collapse/collapse-item.d.ts
vendored
Normal file
68
client/mdui_patched/components/collapse/collapse-item.d.ts
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
import '@mdui/jq/methods/height.js';
|
||||
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||
import type { JQ } from '@mdui/jq/shared/core.js';
|
||||
import type { CSSResultGroup, TemplateResult, PropertyValues } from 'lit';
|
||||
/**
|
||||
* @summary 折叠面板项组件,需配合 `<mdui-collapse>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-collapse>
|
||||
* ..<mdui-collapse-item header="header-1">content-1</mdui-collapse-item>
|
||||
* ..<mdui-collapse-item header="header-2">content-2</mdui-collapse-item>
|
||||
* </mdui-collapse>
|
||||
* ```
|
||||
*
|
||||
* @event open - 开始打开时,事件被触发
|
||||
* @event opened - 打开动画完成时,事件被触发
|
||||
* @event close - 开始关闭时,事件被触发
|
||||
* @event closed - 关闭动画完成时,事件被触发
|
||||
*
|
||||
* @slot - 折叠面板项的正文内容
|
||||
* @slot header - 折叠面板项的头部内容
|
||||
*
|
||||
* @csspart header - 折叠面板的头部内容
|
||||
* @csspart body - 折叠面板的正文内容
|
||||
*/
|
||||
export declare class CollapseItem extends MduiElement<CollapseItemEventMap> {
|
||||
static styles: CSSResultGroup;
|
||||
/**
|
||||
* 此折叠面板项的值
|
||||
*/
|
||||
value?: string;
|
||||
/**
|
||||
* 此折叠面板项的头部文本
|
||||
*/
|
||||
header?: string;
|
||||
/**
|
||||
* 是否禁用此折叠面板项
|
||||
*/
|
||||
disabled: boolean;
|
||||
/**
|
||||
* 点击该元素时触发折叠,值可以是 CSS 选择器、DOM 元素、或 [JQ 对象](/docs/2/functions/jq)。默认为点击整个 header 区域触发
|
||||
*/
|
||||
trigger?: string | HTMLElement | JQ<HTMLElement>;
|
||||
/**
|
||||
* 是否为激活状态,由 `collapse` 组件控制该参数
|
||||
*/
|
||||
protected active: boolean;
|
||||
private state;
|
||||
protected isInitial: boolean;
|
||||
protected readonly key: number;
|
||||
private readonly bodyRef;
|
||||
private onActiveChange;
|
||||
protected firstUpdated(changedProperties: PropertyValues): void;
|
||||
protected render(): TemplateResult;
|
||||
private onTransitionEnd;
|
||||
private updateBodyHeight;
|
||||
}
|
||||
export interface CollapseItemEventMap {
|
||||
open: CustomEvent<void>;
|
||||
opened: CustomEvent<void>;
|
||||
close: CustomEvent<void>;
|
||||
closed: CustomEvent<void>;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'mdui-collapse-item': CollapseItem;
|
||||
}
|
||||
}
|
||||
130
client/mdui_patched/components/collapse/collapse-item.js
Normal file
130
client/mdui_patched/components/collapse/collapse-item.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import { __decorate } from "tslib";
|
||||
import { html } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { createRef, ref } from 'lit/directives/ref.js';
|
||||
import { $ } from '@mdui/jq/$.js';
|
||||
import '@mdui/jq/methods/height.js';
|
||||
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||
import { watch } from '@mdui/shared/decorators/watch.js';
|
||||
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||
import { uniqueId } from '@mdui/shared/helpers/uniqueId.js';
|
||||
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||
import { collapseItemStyle } from './collapse-item-style.js';
|
||||
/**
|
||||
* @summary 折叠面板项组件,需配合 `<mdui-collapse>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-collapse>
|
||||
* ..<mdui-collapse-item header="header-1">content-1</mdui-collapse-item>
|
||||
* ..<mdui-collapse-item header="header-2">content-2</mdui-collapse-item>
|
||||
* </mdui-collapse>
|
||||
* ```
|
||||
*
|
||||
* @event open - 开始打开时,事件被触发
|
||||
* @event opened - 打开动画完成时,事件被触发
|
||||
* @event close - 开始关闭时,事件被触发
|
||||
* @event closed - 关闭动画完成时,事件被触发
|
||||
*
|
||||
* @slot - 折叠面板项的正文内容
|
||||
* @slot header - 折叠面板项的头部内容
|
||||
*
|
||||
* @csspart header - 折叠面板的头部内容
|
||||
* @csspart body - 折叠面板的正文内容
|
||||
*/
|
||||
let CollapseItem = class CollapseItem extends MduiElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
/**
|
||||
* 是否禁用此折叠面板项
|
||||
*/
|
||||
this.disabled = false;
|
||||
/**
|
||||
* 是否为激活状态,由 `collapse` 组件控制该参数
|
||||
*/
|
||||
this.active = false;
|
||||
this.state = 'closed';
|
||||
// 是否是初始状态,不显示动画
|
||||
this.isInitial = true;
|
||||
// 每一个 `collapse-item` 元素都添加一个唯一的 key
|
||||
this.key = uniqueId();
|
||||
this.bodyRef = createRef();
|
||||
}
|
||||
onActiveChange() {
|
||||
if (this.isInitial) {
|
||||
this.state = this.active ? 'opened' : 'closed';
|
||||
if (this.hasUpdated) {
|
||||
this.updateBodyHeight();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.state = this.active ? 'open' : 'close';
|
||||
this.emit(this.state);
|
||||
this.updateBodyHeight();
|
||||
}
|
||||
}
|
||||
firstUpdated(changedProperties) {
|
||||
super.firstUpdated(changedProperties);
|
||||
this.updateBodyHeight();
|
||||
}
|
||||
render() {
|
||||
return html `<slot name="header" part="header" class="header">${this.header}</slot><slot part="body" class="body ${classMap({
|
||||
opened: this.state === 'opened',
|
||||
active: this.active,
|
||||
})}" ${ref(this.bodyRef)} @transitionend="${this.onTransitionEnd}"></slot>`;
|
||||
}
|
||||
onTransitionEnd(event) {
|
||||
if (event.target === this.bodyRef.value) {
|
||||
this.state = this.active ? 'opened' : 'closed';
|
||||
this.emit(this.state);
|
||||
this.updateBodyHeight();
|
||||
}
|
||||
}
|
||||
updateBodyHeight() {
|
||||
const scrollHeight = this.bodyRef.value.scrollHeight;
|
||||
// 如果是从 opened 状态开始关闭,则先设置高度值,并等重绘完成
|
||||
if (this.state === 'close') {
|
||||
$(this.bodyRef.value).height(scrollHeight);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
this.bodyRef.value.clientLeft;
|
||||
}
|
||||
$(this.bodyRef.value).height(this.state === 'opened'
|
||||
? 'auto'
|
||||
: this.state === 'open'
|
||||
? scrollHeight
|
||||
: 0);
|
||||
}
|
||||
};
|
||||
CollapseItem.styles = [
|
||||
componentStyle,
|
||||
collapseItemStyle,
|
||||
];
|
||||
__decorate([
|
||||
property({ reflect: true })
|
||||
], CollapseItem.prototype, "value", void 0);
|
||||
__decorate([
|
||||
property({ reflect: true })
|
||||
], CollapseItem.prototype, "header", void 0);
|
||||
__decorate([
|
||||
property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
converter: booleanConverter,
|
||||
})
|
||||
], CollapseItem.prototype, "disabled", void 0);
|
||||
__decorate([
|
||||
property()
|
||||
], CollapseItem.prototype, "trigger", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], CollapseItem.prototype, "active", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], CollapseItem.prototype, "state", void 0);
|
||||
__decorate([
|
||||
watch('active')
|
||||
], CollapseItem.prototype, "onActiveChange", null);
|
||||
CollapseItem = __decorate([
|
||||
customElement('mdui-collapse-item')
|
||||
], CollapseItem);
|
||||
export { CollapseItem };
|
||||
1
client/mdui_patched/components/collapse/collapse-style.d.ts
vendored
Normal file
1
client/mdui_patched/components/collapse/collapse-style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const collapseStyle: import("lit").CSSResult;
|
||||
@@ -0,0 +1,2 @@
|
||||
import { css } from 'lit';
|
||||
export const collapseStyle = css `:host{display:block}`;
|
||||
54
client/mdui_patched/components/collapse/collapse.d.ts
vendored
Normal file
54
client/mdui_patched/components/collapse/collapse.d.ts
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import '@mdui/jq/methods/is.js';
|
||||
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||
import type { CSSResultGroup, TemplateResult } from 'lit';
|
||||
/**
|
||||
* @summary 折叠面板组件,需配合 `<mdui-collapse-item>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-collapse>
|
||||
* ..<mdui-collapse-item header="header-1">content-1</mdui-collapse-item>
|
||||
* ..<mdui-collapse-item header="header-2">content-2</mdui-collapse-item>
|
||||
* </mdui-collapse>
|
||||
* ```
|
||||
*
|
||||
* @event change - 当前展开的折叠面板项变化时触发
|
||||
*
|
||||
* @slot - `<mdui-collapse-item>` 组件
|
||||
*/
|
||||
export declare class Collapse extends MduiElement<CollapseEventMap> {
|
||||
static styles: CSSResultGroup;
|
||||
/**
|
||||
* 是否启用手风琴模式
|
||||
*/
|
||||
accordion: boolean;
|
||||
/**
|
||||
* 当前展开的 `<mdui-collapse-item>` 的值
|
||||
*
|
||||
* **Note**:该属性的 HTML 属性始终为字符串,只有在 `accordion` 为 `true` 时,才能设置初始值;该属性的 JavaScript 属性值在 `accordion` 为 `true` 时为字符串,在 `accordion` 为 `false` 时为字符串数组。因此,当 `accordion` 为 `false` 时,只能通过修改 JavaScript 属性值来改变此值。
|
||||
*/
|
||||
value?: string | string[];
|
||||
/**
|
||||
* 是否禁用此折叠面板
|
||||
*/
|
||||
disabled: boolean;
|
||||
private activeKeys;
|
||||
private readonly items;
|
||||
private isInitial;
|
||||
private definedController;
|
||||
private onActiveKeysChange;
|
||||
private onValueChange;
|
||||
protected render(): TemplateResult;
|
||||
private setActiveKeys;
|
||||
private setValue;
|
||||
private onClick;
|
||||
private onSlotChange;
|
||||
private updateItems;
|
||||
}
|
||||
export interface CollapseEventMap {
|
||||
change: CustomEvent<void>;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'mdui-collapse': Collapse;
|
||||
}
|
||||
}
|
||||
198
client/mdui_patched/components/collapse/collapse.js
Normal file
198
client/mdui_patched/components/collapse/collapse.js
Normal file
@@ -0,0 +1,198 @@
|
||||
import { __decorate } from "tslib";
|
||||
import { html } from 'lit';
|
||||
import { customElement, property, queryAssignedElements, state, } from 'lit/decorators.js';
|
||||
import { $ } from '@mdui/jq/$.js';
|
||||
import '@mdui/jq/methods/is.js';
|
||||
import { isElement, isUndefined } from '@mdui/jq/shared/helper.js';
|
||||
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||
import { DefinedController } from '@mdui/shared/controllers/defined.js';
|
||||
import { watch } from '@mdui/shared/decorators/watch.js';
|
||||
import { arraysEqualIgnoreOrder } from '@mdui/shared/helpers/array.js';
|
||||
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||
import { collapseStyle } from './collapse-style.js';
|
||||
/**
|
||||
* @summary 折叠面板组件,需配合 `<mdui-collapse-item>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-collapse>
|
||||
* ..<mdui-collapse-item header="header-1">content-1</mdui-collapse-item>
|
||||
* ..<mdui-collapse-item header="header-2">content-2</mdui-collapse-item>
|
||||
* </mdui-collapse>
|
||||
* ```
|
||||
*
|
||||
* @event change - 当前展开的折叠面板项变化时触发
|
||||
*
|
||||
* @slot - `<mdui-collapse-item>` 组件
|
||||
*/
|
||||
let Collapse = class Collapse extends MduiElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
/**
|
||||
* 是否启用手风琴模式
|
||||
*/
|
||||
this.accordion = false;
|
||||
/**
|
||||
* 是否禁用此折叠面板
|
||||
*/
|
||||
this.disabled = false;
|
||||
// 因为 collapse-item 的 value 可能会重复,所以在每个 collapse-item 元素上都添加了一个唯一的 key,通过 activeKey 来记录激活状态的 key
|
||||
this.activeKeys = [];
|
||||
// 是否是初始状态,初始状态不触发 change 事件,没有动画
|
||||
this.isInitial = true;
|
||||
this.definedController = new DefinedController(this, {
|
||||
relatedElements: ['mdui-collapse-item'],
|
||||
});
|
||||
}
|
||||
async onActiveKeysChange() {
|
||||
await this.definedController.whenDefined();
|
||||
// 根据 activeKeys 读取对应 collapse-item 的值
|
||||
const value = this.accordion
|
||||
? this.items.find((item) => this.activeKeys.includes(item.key))?.value
|
||||
: this.items
|
||||
.filter((item) => this.activeKeys.includes(item.key))
|
||||
.map((item) => item.value);
|
||||
this.setValue(value);
|
||||
if (!this.isInitial) {
|
||||
this.emit('change');
|
||||
}
|
||||
}
|
||||
async onValueChange() {
|
||||
this.isInitial = !this.hasUpdated;
|
||||
await this.definedController.whenDefined();
|
||||
if (this.accordion) {
|
||||
const value = this.value;
|
||||
if (!value) {
|
||||
this.setActiveKeys([]);
|
||||
}
|
||||
else {
|
||||
const item = this.items.find((item) => item.value === value);
|
||||
this.setActiveKeys(item ? [item.key] : []);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const value = this.value;
|
||||
if (!value.length) {
|
||||
this.setActiveKeys([]);
|
||||
}
|
||||
else {
|
||||
const activeKeys = this.items
|
||||
.filter((item) => value.includes(item.value))
|
||||
.map((item) => item.key);
|
||||
this.setActiveKeys(activeKeys);
|
||||
}
|
||||
}
|
||||
this.updateItems();
|
||||
}
|
||||
render() {
|
||||
return html `<slot @slotchange="${this.onSlotChange}" @click="${this.onClick}"></slot>`;
|
||||
}
|
||||
setActiveKeys(activeKeys) {
|
||||
if (!arraysEqualIgnoreOrder(this.activeKeys, activeKeys)) {
|
||||
this.activeKeys = activeKeys;
|
||||
}
|
||||
}
|
||||
setValue(value) {
|
||||
if (this.accordion || isUndefined(this.value) || isUndefined(value)) {
|
||||
this.value = value;
|
||||
}
|
||||
else if (!arraysEqualIgnoreOrder(this.value, value)) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
onClick(event) {
|
||||
// 全部禁用
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
// event.button 为 0 时,为鼠标左键点击。忽略鼠标中键和右键
|
||||
if (event.button) {
|
||||
return;
|
||||
}
|
||||
const target = event.target;
|
||||
const item = target.closest('mdui-collapse-item');
|
||||
// collapse-item 被禁用,忽略
|
||||
if (!item || item.disabled) {
|
||||
return;
|
||||
}
|
||||
const path = event.composedPath();
|
||||
// 指定了 trigger 时,点击了其他地方时,忽略
|
||||
if (item.trigger &&
|
||||
!path.find((element) => isElement(element) && $(element).is(item.trigger))) {
|
||||
return;
|
||||
}
|
||||
// header 元素,忽略点击 header 以外的元素
|
||||
if (!path.find((element) => isElement(element) && element.part.contains('header'))) {
|
||||
return;
|
||||
}
|
||||
if (this.accordion) {
|
||||
if (this.activeKeys.includes(item.key)) {
|
||||
this.setActiveKeys([]);
|
||||
}
|
||||
else {
|
||||
this.setActiveKeys([item.key]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 直接修改 this.activeKeys 无法被 watch 监听到,需要先克隆一份 this.activeKeys
|
||||
const activeKeys = [...this.activeKeys];
|
||||
if (activeKeys.includes(item.key)) {
|
||||
activeKeys.splice(activeKeys.indexOf(item.key), 1);
|
||||
}
|
||||
else {
|
||||
activeKeys.push(item.key);
|
||||
}
|
||||
this.setActiveKeys(activeKeys);
|
||||
}
|
||||
this.isInitial = false;
|
||||
this.updateItems();
|
||||
}
|
||||
async onSlotChange() {
|
||||
await this.definedController.whenDefined();
|
||||
this.updateItems();
|
||||
}
|
||||
// 更新 <mdui-collapse-item> 的状态
|
||||
updateItems() {
|
||||
this.items.forEach((item) => {
|
||||
item.active = this.activeKeys.includes(item.key);
|
||||
item.isInitial = this.isInitial;
|
||||
});
|
||||
}
|
||||
};
|
||||
Collapse.styles = [
|
||||
componentStyle,
|
||||
collapseStyle,
|
||||
];
|
||||
__decorate([
|
||||
property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
converter: booleanConverter,
|
||||
})
|
||||
], Collapse.prototype, "accordion", void 0);
|
||||
__decorate([
|
||||
property()
|
||||
], Collapse.prototype, "value", void 0);
|
||||
__decorate([
|
||||
property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
converter: booleanConverter,
|
||||
})
|
||||
], Collapse.prototype, "disabled", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], Collapse.prototype, "activeKeys", void 0);
|
||||
__decorate([
|
||||
queryAssignedElements({ selector: 'mdui-collapse-item', flatten: true })
|
||||
], Collapse.prototype, "items", void 0);
|
||||
__decorate([
|
||||
watch('activeKeys', true)
|
||||
], Collapse.prototype, "onActiveKeysChange", null);
|
||||
__decorate([
|
||||
watch('value')
|
||||
], Collapse.prototype, "onValueChange", null);
|
||||
Collapse = __decorate([
|
||||
customElement('mdui-collapse')
|
||||
], Collapse);
|
||||
export { Collapse };
|
||||
Reference in New Issue
Block a user