fix: 本地 patch MDUI 以解决 tabindex = 0 导致的一系列玄学问题
This commit is contained in:
1
client/mdui_patched/components/tabs/tab-panel-style.d.ts
vendored
Normal file
1
client/mdui_patched/components/tabs/tab-panel-style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const tabPanelStyle: import("lit").CSSResult;
|
||||
2
client/mdui_patched/components/tabs/tab-panel-style.js
Normal file
2
client/mdui_patched/components/tabs/tab-panel-style.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import { css } from 'lit';
|
||||
export const tabPanelStyle = css `:host{display:block;overflow-y:auto;flex:1 1 auto}:host(:not([active])){display:none}`;
|
||||
38
client/mdui_patched/components/tabs/tab-panel.d.ts
vendored
Normal file
38
client/mdui_patched/components/tabs/tab-panel.d.ts
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||
import type { CSSResultGroup, TemplateResult } from 'lit';
|
||||
/**
|
||||
* @summary 选项卡面板项组件。需配合 `<mdui-tabs>` 和 `<mdui-tab>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-tabs value="tab-1">
|
||||
* ..<mdui-tab value="tab-1">Tab 1</mdui-tab>
|
||||
* ..<mdui-tab value="tab-2">Tab 2</mdui-tab>
|
||||
* ..<mdui-tab value="tab-3">Tab 3</mdui-tab>
|
||||
*
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-1">Panel 1</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-2">Panel 2</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-3">Panel 3</mdui-tab-panel>
|
||||
* </mdui-tabs>
|
||||
* ```
|
||||
*
|
||||
* @slot - 选项卡面板项内容
|
||||
*/
|
||||
export declare class TabPanel extends MduiElement<TabPanelEventMap> {
|
||||
static styles: CSSResultGroup;
|
||||
/**
|
||||
* 选项卡面板项的值
|
||||
*/
|
||||
value?: string;
|
||||
/**
|
||||
* 是否为激活状态,由 `<mdui-tabs>` 组件控制该状态
|
||||
*/
|
||||
protected active: boolean;
|
||||
protected render(): TemplateResult;
|
||||
}
|
||||
export interface TabPanelEventMap {
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'mdui-tab-panel': TabPanel;
|
||||
}
|
||||
}
|
||||
54
client/mdui_patched/components/tabs/tab-panel.js
Normal file
54
client/mdui_patched/components/tabs/tab-panel.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import { __decorate } from "tslib";
|
||||
import { html } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||
import { tabPanelStyle } from './tab-panel-style.js';
|
||||
/**
|
||||
* @summary 选项卡面板项组件。需配合 `<mdui-tabs>` 和 `<mdui-tab>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-tabs value="tab-1">
|
||||
* ..<mdui-tab value="tab-1">Tab 1</mdui-tab>
|
||||
* ..<mdui-tab value="tab-2">Tab 2</mdui-tab>
|
||||
* ..<mdui-tab value="tab-3">Tab 3</mdui-tab>
|
||||
*
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-1">Panel 1</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-2">Panel 2</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-3">Panel 3</mdui-tab-panel>
|
||||
* </mdui-tabs>
|
||||
* ```
|
||||
*
|
||||
* @slot - 选项卡面板项内容
|
||||
*/
|
||||
let TabPanel = class TabPanel extends MduiElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
/**
|
||||
* 是否为激活状态,由 `<mdui-tabs>` 组件控制该状态
|
||||
*/
|
||||
this.active = false;
|
||||
}
|
||||
render() {
|
||||
return html `<slot></slot>`;
|
||||
}
|
||||
};
|
||||
TabPanel.styles = [
|
||||
componentStyle,
|
||||
tabPanelStyle,
|
||||
];
|
||||
__decorate([
|
||||
property({ reflect: true })
|
||||
], TabPanel.prototype, "value", void 0);
|
||||
__decorate([
|
||||
property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
converter: booleanConverter,
|
||||
})
|
||||
], TabPanel.prototype, "active", void 0);
|
||||
TabPanel = __decorate([
|
||||
customElement('mdui-tab-panel')
|
||||
], TabPanel);
|
||||
export { TabPanel };
|
||||
1
client/mdui_patched/components/tabs/tab-style.d.ts
vendored
Normal file
1
client/mdui_patched/components/tabs/tab-style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const tabStyle: import("lit").CSSResult;
|
||||
2
client/mdui_patched/components/tabs/tab-style.js
Normal file
2
client/mdui_patched/components/tabs/tab-style.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import { css } from 'lit';
|
||||
export const tabStyle = css `:host{position:relative;--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-surface)}:host([active]){--mdui-comp-ripple-state-layer-color:var(--mdui-color-primary)}.container{display:flex;justify-content:center;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;height:100%}.preset{flex-direction:column;min-height:3rem;padding:.625rem 1rem}:host([inline]:not([inline=false i])) .preset{flex-direction:row}.icon-container,.label-container{position:relative;display:flex;align-items:center;justify-content:center}.icon-container ::slotted([slot=badge]){position:absolute;transform:translate(50%,-50%)}.icon-container ::slotted([slot=badge][variant=small]){transform:translate(.5625rem,-.5625rem)}.label-container ::slotted([slot=badge]){position:absolute;left:100%;bottom:100%;transform:translate(-.75rem,.625rem)}.label-container ::slotted([slot=badge][variant=small]){transform:translate(-.375rem,.375rem)}.icon,.label{display:flex;color:rgb(var(--mdui-color-on-surface-variant))}:host([focused]) .icon,:host([focused]) .label,:host([hover]) .icon,:host([hover]) .label,:host([pressed]) .icon,:host([pressed]) .label{color:rgb(var(--mdui-color-on-surface))}:host([active]) .icon,:host([active]) .label{color:rgb(var(--mdui-color-primary))}:host([active]) .variant-secondary .icon,:host([active]) .variant-secondary .label{color:rgb(var(--mdui-color-on-surface))}.icon{font-size:1.5rem}.label{font-size:var(--mdui-typescale-title-small-size);font-weight:var(--mdui-typescale-title-small-weight);letter-spacing:var(--mdui-typescale-title-small-tracking);line-height:var(--mdui-typescale-title-small-line-height)}.icon mdui-icon,::slotted([slot=icon]){font-size:inherit;color:inherit}`;
|
||||
73
client/mdui_patched/components/tabs/tab.d.ts
vendored
Normal file
73
client/mdui_patched/components/tabs/tab.d.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||
import '../icon.js';
|
||||
import type { Ripple } from '../ripple/index.js';
|
||||
import type { CSSResultGroup, TemplateResult } from 'lit';
|
||||
declare const Tab_base: import("@lit/reactive-element/decorators/base.js").Constructor<import("../ripple/ripple-mixin.js").RippleMixinInterface> & import("@lit/reactive-element/decorators/base.js").Constructor<import("@mdui/shared/mixins/focusable.js").FocusableMixinInterface> & typeof MduiElement;
|
||||
/**
|
||||
* @summary 选项卡导航项组件。需配合 `<mdui-tabs>` 和 `<mdui-tab-panel>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-tabs value="tab-1">
|
||||
* ..<mdui-tab value="tab-1">Tab 1</mdui-tab>
|
||||
* ..<mdui-tab value="tab-2">Tab 2</mdui-tab>
|
||||
* ..<mdui-tab value="tab-3">Tab 3</mdui-tab>
|
||||
*
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-1">Panel 1</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-2">Panel 2</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-3">Panel 3</mdui-tab-panel>
|
||||
* </mdui-tabs>
|
||||
* ```
|
||||
*
|
||||
* @event focus - 获得焦点时触发
|
||||
* @event blur - 失去焦点时触发
|
||||
*
|
||||
* @slot - 选项卡导航项的文本内容
|
||||
* @slot icon - 选项卡导航项中的图标
|
||||
* @slot badge - 徽标
|
||||
* @slot custom - 自定义整个选项卡导航项中的内容
|
||||
*
|
||||
* @csspart container - 选项卡导航项容器
|
||||
* @csspart icon - 选项卡导航项中的图标
|
||||
* @csspart label - 选项卡导航项的文本
|
||||
*/
|
||||
export declare class Tab extends Tab_base<TabEventMap> {
|
||||
static styles: CSSResultGroup;
|
||||
/**
|
||||
* 选项卡导航项的值
|
||||
*/
|
||||
value?: string;
|
||||
/**
|
||||
* Material Icons 图标名。也可以通过 `slot="icon"` 设置
|
||||
*/
|
||||
icon?: string;
|
||||
/**
|
||||
* 是否把图标和文本水平排列
|
||||
*/
|
||||
inline: boolean;
|
||||
/**
|
||||
* 是否为激活状态,由 `<mdui-tabs>` 组件控制该参数
|
||||
*/
|
||||
protected active: boolean;
|
||||
/**
|
||||
* 选项卡形状。由 `<mdui-tabs>` 组件控制该参数
|
||||
*/
|
||||
protected variant: 'primary' | 'secondary';
|
||||
protected readonly key: number;
|
||||
private readonly rippleRef;
|
||||
private readonly hasSlotController;
|
||||
protected get rippleElement(): Ripple;
|
||||
protected get rippleDisabled(): boolean;
|
||||
protected get focusElement(): HTMLElement;
|
||||
protected get focusDisabled(): boolean;
|
||||
protected render(): TemplateResult;
|
||||
}
|
||||
export interface TabEventMap {
|
||||
focus: FocusEvent;
|
||||
blur: FocusEvent;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'mdui-tab': Tab;
|
||||
}
|
||||
}
|
||||
export {};
|
||||
116
client/mdui_patched/components/tabs/tab.js
Normal file
116
client/mdui_patched/components/tabs/tab.js
Normal file
@@ -0,0 +1,116 @@
|
||||
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 { when } from 'lit/directives/when.js';
|
||||
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||
import { HasSlotController } from '@mdui/shared/controllers/has-slot.js';
|
||||
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||
import { nothingTemplate } from '@mdui/shared/helpers/template.js';
|
||||
import { uniqueId } from '@mdui/shared/helpers/uniqueId.js';
|
||||
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||
import { FocusableMixin } from '@mdui/shared/mixins/focusable.js';
|
||||
import '../icon.js';
|
||||
import { RippleMixin } from '../ripple/ripple-mixin.js';
|
||||
import { tabStyle } from './tab-style.js';
|
||||
/**
|
||||
* @summary 选项卡导航项组件。需配合 `<mdui-tabs>` 和 `<mdui-tab-panel>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-tabs value="tab-1">
|
||||
* ..<mdui-tab value="tab-1">Tab 1</mdui-tab>
|
||||
* ..<mdui-tab value="tab-2">Tab 2</mdui-tab>
|
||||
* ..<mdui-tab value="tab-3">Tab 3</mdui-tab>
|
||||
*
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-1">Panel 1</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-2">Panel 2</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-3">Panel 3</mdui-tab-panel>
|
||||
* </mdui-tabs>
|
||||
* ```
|
||||
*
|
||||
* @event focus - 获得焦点时触发
|
||||
* @event blur - 失去焦点时触发
|
||||
*
|
||||
* @slot - 选项卡导航项的文本内容
|
||||
* @slot icon - 选项卡导航项中的图标
|
||||
* @slot badge - 徽标
|
||||
* @slot custom - 自定义整个选项卡导航项中的内容
|
||||
*
|
||||
* @csspart container - 选项卡导航项容器
|
||||
* @csspart icon - 选项卡导航项中的图标
|
||||
* @csspart label - 选项卡导航项的文本
|
||||
*/
|
||||
let Tab = class Tab extends RippleMixin(FocusableMixin(MduiElement)) {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
/**
|
||||
* 是否把图标和文本水平排列
|
||||
*/
|
||||
this.inline = false;
|
||||
/**
|
||||
* 是否为激活状态,由 `<mdui-tabs>` 组件控制该参数
|
||||
*/
|
||||
this.active = false;
|
||||
/**
|
||||
* 选项卡形状。由 `<mdui-tabs>` 组件控制该参数
|
||||
*/
|
||||
this.variant = 'primary';
|
||||
// 每一个 `<mdui-tab>` 元素都添加一个唯一的 key
|
||||
this.key = uniqueId();
|
||||
this.rippleRef = createRef();
|
||||
this.hasSlotController = new HasSlotController(this, 'icon', 'custom');
|
||||
}
|
||||
get rippleElement() {
|
||||
return this.rippleRef.value;
|
||||
}
|
||||
get rippleDisabled() {
|
||||
return false;
|
||||
}
|
||||
get focusElement() {
|
||||
return this;
|
||||
}
|
||||
get focusDisabled() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const hasIcon = this.icon || this.hasSlotController.test('icon');
|
||||
const hasCustomSlot = this.hasSlotController.test('custom');
|
||||
const renderBadge = () => html `<slot name="badge"></slot>`;
|
||||
return html `<mdui-ripple ${ref(this.rippleRef)} .noRipple="${this.noRipple}"></mdui-ripple><div part="container" class="${classMap({
|
||||
container: true,
|
||||
preset: !hasCustomSlot,
|
||||
'variant-secondary': this.variant === 'secondary',
|
||||
})}"><slot name="custom"><div class="icon-container">${when(hasIcon || this.icon, renderBadge)}<slot name="icon" part="icon" class="icon">${this.icon
|
||||
? html `<mdui-icon name="${this.icon}"></mdui-icon>`
|
||||
: nothingTemplate}</slot></div><div class="label-container">${when(!hasIcon, renderBadge)}<slot part="label" class="label"></slot></div></slot></div>`;
|
||||
}
|
||||
};
|
||||
Tab.styles = [componentStyle, tabStyle];
|
||||
__decorate([
|
||||
property({ reflect: true })
|
||||
], Tab.prototype, "value", void 0);
|
||||
__decorate([
|
||||
property({ reflect: true })
|
||||
], Tab.prototype, "icon", void 0);
|
||||
__decorate([
|
||||
property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
converter: booleanConverter,
|
||||
})
|
||||
], Tab.prototype, "inline", void 0);
|
||||
__decorate([
|
||||
property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
converter: booleanConverter,
|
||||
})
|
||||
], Tab.prototype, "active", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], Tab.prototype, "variant", void 0);
|
||||
Tab = __decorate([
|
||||
customElement('mdui-tab')
|
||||
], Tab);
|
||||
export { Tab };
|
||||
1
client/mdui_patched/components/tabs/tabs-style.d.ts
vendored
Normal file
1
client/mdui_patched/components/tabs/tabs-style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const tabsStyle: import("lit").CSSResult;
|
||||
2
client/mdui_patched/components/tabs/tabs-style.js
Normal file
2
client/mdui_patched/components/tabs/tabs-style.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import { css } from 'lit';
|
||||
export const tabsStyle = css `:host{position:relative;display:flex}:host([placement^=top]){flex-direction:column}:host([placement^=bottom]){flex-direction:column-reverse}:host([placement^=left]){flex-direction:row}:host([placement^=right]){flex-direction:row-reverse}.container{position:relative;display:flex;flex:0 0 auto;overflow-x:auto;background-color:rgb(var(--mdui-color-surface))}:host([placement^=bottom]) .container,:host([placement^=top]) .container{flex-direction:row}:host([placement^=left]) .container,:host([placement^=right]) .container{flex-direction:column}:host([placement$='-start']) .container{justify-content:flex-start}:host([placement=bottom]) .container,:host([placement=left]) .container,:host([placement=right]) .container,:host([placement=top]) .container{justify-content:center}:host([placement$='-end']) .container{justify-content:flex-end}.container::after{content:' ';position:absolute;background-color:rgb(var(--mdui-color-surface-variant))}:host([placement^=bottom]) .container::after,:host([placement^=top]) .container::after{left:0;width:100%;height:.0625rem}:host([placement^=top]) .container::after{bottom:0}:host([placement^=bottom]) .container::after{top:0}:host([placement^=left]) .container::after,:host([placement^=right]) .container::after{top:0;height:100%;width:.0625rem}:host([placement^=left]) .container::after{right:0}:host([placement^=right]) .container::after{left:0}.indicator{position:absolute;z-index:1;background-color:rgb(var(--mdui-color-primary))}.container:not(.initial) .indicator{transition-duration:var(--mdui-motion-duration-medium2);transition-timing-function:var(--mdui-motion-easing-standard-decelerate)}:host([placement^=bottom]) .indicator,:host([placement^=top]) .indicator{transition-property:transform,left,width}:host([placement^=left]) .indicator,:host([placement^=right]) .indicator{transition-property:transform,top,height}:host([placement^=top]) .indicator{bottom:0}:host([placement^=bottom]) .indicator{top:0}:host([placement^=left]) .indicator{right:0}:host([placement^=right]) .indicator{left:0}:host([placement^=bottom][variant=primary]) .indicator,:host([placement^=top][variant=primary]) .indicator{height:.1875rem}:host([placement^=bottom][variant=secondary]) .indicator,:host([placement^=top][variant=secondary]) .indicator{height:.125rem}:host([placement^=left][variant=primary]) .indicator,:host([placement^=right][variant=primary]) .indicator{width:.1875rem}:host([placement^=left][variant=secondary]) .indicator,:host([placement^=right][variant=secondary]) .indicator{width:.125rem}:host([placement^=top][variant=primary]) .indicator{border-top-left-radius:.1875rem;border-top-right-radius:.1875rem}:host([placement^=bottom][variant=primary]) .indicator{border-bottom-right-radius:.1875rem;border-bottom-left-radius:.1875rem}:host([placement^=left][variant=primary]) .indicator{border-top-left-radius:.1875rem;border-bottom-left-radius:.1875rem}:host([placement^=right][variant=primary]) .indicator{border-top-right-radius:.1875rem;border-bottom-right-radius:.1875rem}:host([full-width]:not([full-width=false i])) ::slotted(mdui-tab){flex:1}`;
|
||||
91
client/mdui_patched/components/tabs/tabs.d.ts
vendored
Normal file
91
client/mdui_patched/components/tabs/tabs.d.ts
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
import '@mdui/jq/methods/children.js';
|
||||
import '@mdui/jq/methods/css.js';
|
||||
import '@mdui/jq/methods/find.js';
|
||||
import '@mdui/jq/methods/get.js';
|
||||
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||
import type { CSSResultGroup, TemplateResult } from 'lit';
|
||||
/**
|
||||
* @summary 选项卡组件。需配合 `<mdui-tab>` 和 `<mdui-tab-panel>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-tabs value="tab-1">
|
||||
* ..<mdui-tab value="tab-1">Tab 1</mdui-tab>
|
||||
* ..<mdui-tab value="tab-2">Tab 2</mdui-tab>
|
||||
* ..<mdui-tab value="tab-3">Tab 3</mdui-tab>
|
||||
*
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-1">Panel 1</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-2">Panel 2</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-3">Panel 3</mdui-tab-panel>
|
||||
* </mdui-tabs>
|
||||
* ```
|
||||
*
|
||||
* @event change - 选中的值变化时触发
|
||||
*
|
||||
* @slot - `<mdui-tab>` 元素
|
||||
* @slot panel - `<mdui-tab-panel>` 元素
|
||||
*
|
||||
* @csspart container - `<mdui-tab>` 元素的容器
|
||||
* @csspart indicator - 激活状态指示器
|
||||
*/
|
||||
export declare class Tabs extends MduiElement<TabsEventMap> {
|
||||
static styles: CSSResultGroup;
|
||||
/**
|
||||
* 选项卡形状。可选值包括:
|
||||
*
|
||||
* * `primary`:适用于位于 `<mdui-top-app-bar>` 下方,用于切换应用的主页面的场景
|
||||
* * `secondary`:适用于位于页面中,用于切换一组相关内容的场景
|
||||
*/
|
||||
variant: /*适用于位于 `<mdui-top-app-bar>` 下方,用于切换应用的主页面的场景*/ 'primary' | /*适用于位于页面中,用于切换一组相关内容的场景*/ 'secondary';
|
||||
/**
|
||||
* 当前激活的 `<mdui-tab>` 的值
|
||||
*/
|
||||
value?: string;
|
||||
/**
|
||||
* 选项卡位置。默认为 `top-start`。可选值包括:
|
||||
*
|
||||
* * `top-start`:位于上方,左对齐
|
||||
* * `top`:位于上方,居中对齐
|
||||
* * `top-end`:位于上方,右对齐
|
||||
* * `bottom-start`:位于下方,左对齐
|
||||
* * `bottom`:位于下方,居中对齐
|
||||
* * `bottom-end`:位于下方,右对齐
|
||||
* * `left-start`:位于左侧,顶部对齐
|
||||
* * `left`:位于左侧,居中对齐
|
||||
* * `left-end`:位于左侧,底部对齐
|
||||
* * `right-start`:位于右侧,顶部对齐
|
||||
* * `right`:位于右侧,居中对齐
|
||||
* * `right-end`:位于右侧,底部对齐
|
||||
*/
|
||||
placement: /*位于上方,左对齐*/ 'top-start' | /*位于上方,居中对齐*/ 'top' | /*位于上方,右对齐*/ 'top-end' | /*位于下方,左对齐*/ 'bottom-start' | /*位于下方,居中对齐*/ 'bottom' | /*位于下方,右对齐*/ 'bottom-end' | /*位于左侧,顶部对齐*/ 'left-start' | /*位于左侧,居中对齐*/ 'left' | /*位于左侧,底部对齐*/ 'left-end' | /*位于右侧,顶部对齐*/ 'right-start' | /*位于右侧,居中对齐*/ 'right' | /*位于右侧,底部对齐*/ 'right-end';
|
||||
/**
|
||||
* 是否填满父元素宽度
|
||||
*/
|
||||
fullWidth: boolean;
|
||||
private activeKey;
|
||||
private isInitial;
|
||||
private readonly tabs;
|
||||
private readonly panels;
|
||||
private activeTab?;
|
||||
private observeResize?;
|
||||
private readonly containerRef;
|
||||
private readonly indicatorRef;
|
||||
private readonly definedController;
|
||||
private onActiveKeyChange;
|
||||
private onValueChange;
|
||||
private onIndicatorChange;
|
||||
connectedCallback(): void;
|
||||
disconnectedCallback(): void;
|
||||
protected render(): TemplateResult;
|
||||
private onSlotChange;
|
||||
private onClick;
|
||||
private updateActive;
|
||||
private updateIndicator;
|
||||
}
|
||||
export interface TabsEventMap {
|
||||
change: CustomEvent<void>;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'mdui-tabs': Tabs;
|
||||
}
|
||||
}
|
||||
240
client/mdui_patched/components/tabs/tabs.js
Normal file
240
client/mdui_patched/components/tabs/tabs.js
Normal file
@@ -0,0 +1,240 @@
|
||||
import { __decorate } from "tslib";
|
||||
import { html } from 'lit';
|
||||
import { customElement, property, queryAssignedElements, 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/children.js';
|
||||
import '@mdui/jq/methods/css.js';
|
||||
import '@mdui/jq/methods/find.js';
|
||||
import '@mdui/jq/methods/get.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 { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||
import { observeResize } from '@mdui/shared/helpers/observeResize.js';
|
||||
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||
import { tabsStyle } from './tabs-style.js';
|
||||
/**
|
||||
* @summary 选项卡组件。需配合 `<mdui-tab>` 和 `<mdui-tab-panel>` 组件使用
|
||||
*
|
||||
* ```html
|
||||
* <mdui-tabs value="tab-1">
|
||||
* ..<mdui-tab value="tab-1">Tab 1</mdui-tab>
|
||||
* ..<mdui-tab value="tab-2">Tab 2</mdui-tab>
|
||||
* ..<mdui-tab value="tab-3">Tab 3</mdui-tab>
|
||||
*
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-1">Panel 1</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-2">Panel 2</mdui-tab-panel>
|
||||
* ..<mdui-tab-panel slot="panel" value="tab-3">Panel 3</mdui-tab-panel>
|
||||
* </mdui-tabs>
|
||||
* ```
|
||||
*
|
||||
* @event change - 选中的值变化时触发
|
||||
*
|
||||
* @slot - `<mdui-tab>` 元素
|
||||
* @slot panel - `<mdui-tab-panel>` 元素
|
||||
*
|
||||
* @csspart container - `<mdui-tab>` 元素的容器
|
||||
* @csspart indicator - 激活状态指示器
|
||||
*/
|
||||
let Tabs = class Tabs extends MduiElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
/**
|
||||
* 选项卡形状。可选值包括:
|
||||
*
|
||||
* * `primary`:适用于位于 `<mdui-top-app-bar>` 下方,用于切换应用的主页面的场景
|
||||
* * `secondary`:适用于位于页面中,用于切换一组相关内容的场景
|
||||
*/
|
||||
this.variant = 'primary';
|
||||
/**
|
||||
* 选项卡位置。默认为 `top-start`。可选值包括:
|
||||
*
|
||||
* * `top-start`:位于上方,左对齐
|
||||
* * `top`:位于上方,居中对齐
|
||||
* * `top-end`:位于上方,右对齐
|
||||
* * `bottom-start`:位于下方,左对齐
|
||||
* * `bottom`:位于下方,居中对齐
|
||||
* * `bottom-end`:位于下方,右对齐
|
||||
* * `left-start`:位于左侧,顶部对齐
|
||||
* * `left`:位于左侧,居中对齐
|
||||
* * `left-end`:位于左侧,底部对齐
|
||||
* * `right-start`:位于右侧,顶部对齐
|
||||
* * `right`:位于右侧,居中对齐
|
||||
* * `right-end`:位于右侧,底部对齐
|
||||
*/
|
||||
this.placement = 'top-start';
|
||||
/**
|
||||
* 是否填满父元素宽度
|
||||
*/
|
||||
this.fullWidth = false;
|
||||
// 因为 tab 的 value 可能会重复,所以在每个 tab 元素上都添加了一个唯一的 key,通过 activeKey 来记录激活状态的 key
|
||||
this.activeKey = 0;
|
||||
// 是否为初始状态,初始状态不触发 change 事件
|
||||
this.isInitial = true;
|
||||
this.containerRef = createRef();
|
||||
this.indicatorRef = createRef();
|
||||
this.definedController = new DefinedController(this, {
|
||||
relatedElements: ['mdui-tab', 'mdui-tab-panel'],
|
||||
});
|
||||
}
|
||||
async onActiveKeyChange() {
|
||||
await this.definedController.whenDefined();
|
||||
// 根据 activeKey 读取对应 tab 的值
|
||||
this.value = this.tabs.find((tab) => tab.key === this.activeKey)?.value;
|
||||
this.updateActive();
|
||||
if (!this.isInitial) {
|
||||
this.emit('change');
|
||||
}
|
||||
}
|
||||
async onValueChange() {
|
||||
this.isInitial = !this.hasUpdated;
|
||||
await this.definedController.whenDefined();
|
||||
const tab = this.tabs.find((tab) => tab.value === this.value);
|
||||
this.activeKey = tab?.key ?? 0;
|
||||
}
|
||||
async onIndicatorChange() {
|
||||
await this.updateComplete;
|
||||
this.updateIndicator();
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.updateComplete.then(() => {
|
||||
this.observeResize = observeResize(this.containerRef.value, () => this.updateIndicator());
|
||||
});
|
||||
}
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.observeResize?.unobserve();
|
||||
}
|
||||
render() {
|
||||
return html `<div ${ref(this.containerRef)} part="container" class="container ${classMap({ initial: this.isInitial })}"><slot @slotchange="${this.onSlotChange}" @click="${this.onClick}"></slot><div ${ref(this.indicatorRef)} part="indicator" class="indicator"></div></div><slot name="panel" @slotchange="${this.onSlotChange}"></slot>`;
|
||||
}
|
||||
async onSlotChange() {
|
||||
await this.definedController.whenDefined();
|
||||
this.updateActive();
|
||||
}
|
||||
async onClick(event) {
|
||||
// event.button 为 0 时,为鼠标左键点击。忽略鼠标中键和右键
|
||||
if (event.button) {
|
||||
return;
|
||||
}
|
||||
await this.definedController.whenDefined();
|
||||
const target = event.target;
|
||||
const tab = target.closest('mdui-tab');
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
this.activeKey = tab.key;
|
||||
this.isInitial = false;
|
||||
this.updateActive();
|
||||
}
|
||||
updateActive() {
|
||||
this.activeTab = this.tabs
|
||||
.map((tab) => {
|
||||
tab.active = this.activeKey === tab.key;
|
||||
return tab;
|
||||
})
|
||||
.find((tab) => tab.active);
|
||||
this.panels.forEach((panel) => (panel.active = panel.value === this.activeTab?.value));
|
||||
this.updateIndicator();
|
||||
}
|
||||
updateIndicator() {
|
||||
const activeTab = this.activeTab;
|
||||
const $indicator = $(this.indicatorRef.value);
|
||||
const isVertical = this.placement.startsWith('left') || this.placement.startsWith('right');
|
||||
// 没有激活的,不显示指示器
|
||||
if (!activeTab) {
|
||||
$indicator.css({
|
||||
transform: isVertical ? 'scaleY(0)' : 'scaleX(0)',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const $activeTab = $(activeTab);
|
||||
const offsetTop = activeTab.offsetTop;
|
||||
const offsetLeft = activeTab.offsetLeft;
|
||||
const commonStyle = isVertical
|
||||
? { transform: 'scaleY(1)', width: '', left: '' }
|
||||
: { transform: 'scaleX(1)', height: '', top: '' };
|
||||
let shownStyle = {};
|
||||
if (this.variant === 'primary') {
|
||||
const $customSlots = $activeTab.find(':scope > [slot="custom"]');
|
||||
const children = $customSlots.length
|
||||
? $customSlots.get()
|
||||
: $(activeTab.renderRoot)
|
||||
.find('slot[name="custom"]')
|
||||
.children()
|
||||
.get();
|
||||
if (isVertical) {
|
||||
// 最上方的元素的顶部,距离容器顶部距离
|
||||
const top = Math.min(...children.map((child) => child.offsetTop)) + offsetTop;
|
||||
// 最下方的元素的底部,距离容器顶部的距离
|
||||
const bottom = Math.max(...children.map((child) => child.offsetTop + child.offsetHeight)) + offsetTop;
|
||||
shownStyle = { top, height: bottom - top };
|
||||
}
|
||||
else {
|
||||
// 最左侧的元素的左侧,距离容器左侧的距离
|
||||
const left = Math.min(...children.map((child) => child.offsetLeft)) + offsetLeft;
|
||||
// 最右侧的元素的右侧,距离容器左侧的距离
|
||||
const right = Math.max(...children.map((child) => child.offsetLeft + child.offsetWidth)) + offsetLeft;
|
||||
shownStyle = { left, width: right - left };
|
||||
}
|
||||
}
|
||||
if (this.variant === 'secondary') {
|
||||
shownStyle = isVertical
|
||||
? { top: offsetTop, height: activeTab.offsetHeight }
|
||||
: { left: offsetLeft, width: activeTab.offsetWidth };
|
||||
}
|
||||
$indicator.css({ ...commonStyle, ...shownStyle });
|
||||
}
|
||||
};
|
||||
Tabs.styles = [componentStyle, tabsStyle];
|
||||
__decorate([
|
||||
property({ reflect: true })
|
||||
], Tabs.prototype, "variant", void 0);
|
||||
__decorate([
|
||||
property({ reflect: true })
|
||||
], Tabs.prototype, "value", void 0);
|
||||
__decorate([
|
||||
property({ reflect: true })
|
||||
], Tabs.prototype, "placement", void 0);
|
||||
__decorate([
|
||||
property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
converter: booleanConverter,
|
||||
attribute: 'full-width',
|
||||
})
|
||||
], Tabs.prototype, "fullWidth", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], Tabs.prototype, "activeKey", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], Tabs.prototype, "isInitial", void 0);
|
||||
__decorate([
|
||||
queryAssignedElements({ selector: 'mdui-tab', flatten: true })
|
||||
], Tabs.prototype, "tabs", void 0);
|
||||
__decorate([
|
||||
queryAssignedElements({
|
||||
selector: 'mdui-tab-panel',
|
||||
slot: 'panel',
|
||||
flatten: true,
|
||||
})
|
||||
], Tabs.prototype, "panels", void 0);
|
||||
__decorate([
|
||||
watch('activeKey', true)
|
||||
], Tabs.prototype, "onActiveKeyChange", null);
|
||||
__decorate([
|
||||
watch('value')
|
||||
], Tabs.prototype, "onValueChange", null);
|
||||
__decorate([
|
||||
watch('variant', true),
|
||||
watch('placement', true),
|
||||
watch('fullWidth', true)
|
||||
], Tabs.prototype, "onIndicatorChange", null);
|
||||
Tabs = __decorate([
|
||||
customElement('mdui-tabs')
|
||||
], Tabs);
|
||||
export { Tabs };
|
||||
Reference in New Issue
Block a user