移动目录

This commit is contained in:
CrescentLeaf
2025-11-23 13:27:15 +08:00
parent f13623f4fc
commit 1cb8ac3fff
479 changed files with 49 additions and 49 deletions

View File

@@ -0,0 +1,66 @@
import '@mdui/jq/methods/children.js';
import '@mdui/jq/methods/css.js';
import '@mdui/jq/methods/get.js';
import type { LayoutItemBase } from './layout-item-base.js';
import type { LayoutMain } from './layout-main.js';
import type { Layout } from './layout.js';
export type LayoutPlacement = 'top' | 'left' | 'right' | 'bottom';
export declare class LayoutManager {
private $main?;
private states;
private items?;
/**
* 注册 `<mdui-layout-main>`
*/
registerMain(element: LayoutMain): void;
/**
* 取消注册 `<mdui-layout-main>`
*/
unregisterMain(): void;
/**
* 注册新的 `<mdui-layout-item>`
*/
registerItem(element: LayoutItemBase): void;
/**
* 取消注册 `<mdui-layout-item>`
*/
unregisterItem(element: LayoutItemBase): void;
/**
* 获取所有 `<mdui-layout-item>` 元素(按在 DOM 中的顺序)
*/
getItems(): LayoutItemBase[];
/**
* 获取 `<mdui-layout-main>` 元素
*/
getMain(): LayoutMain | undefined;
/**
* 获取 `<mdui-layout-item>` 及 `<mdui-layout-main>` 元素
*/
getItemsAndMain(): (LayoutItemBase | LayoutMain)[];
/**
* 更新 `order` 值,更新完后重新计算布局
*/
updateOrder(): void;
/**
* 重新计算布局
* @param element 从哪一个元素开始更新;若未传入参数,则将更新所有元素
* @param size 此次更新中,元素的宽高(仅在此次更新中使用)。若不传则自动计算
*/
updateLayout(element?: LayoutItemBase, size?: {
width?: number;
height?: number;
}): void;
/**
* 按 order 排序order 相同时,按在 DOM 中的顺序排序
*/
private resort;
/**
* 组件宽度是否为 0
* mdui-navigation-drawer 较为特殊,在为模态化时,占据的宽度为 0
*/
private isNoWidth;
}
/**
* 获取 layout 实例
*/
export declare const getLayout: (element: Layout) => LayoutManager;

View File

@@ -0,0 +1,206 @@
import { $ } from '@mdui/jq/$.js';
import '@mdui/jq/methods/children.js';
import '@mdui/jq/methods/css.js';
import '@mdui/jq/methods/get.js';
import { isNodeName } from '@mdui/jq/shared/helper.js';
import { observeResize } from '@mdui/shared/helpers/observeResize.js';
export class LayoutManager {
constructor() {
this.states = [];
}
/**
* 注册 `<mdui-layout-main>`
*/
registerMain(element) {
this.$main = $(element);
}
/**
* 取消注册 `<mdui-layout-main>`
*/
unregisterMain() {
this.$main = undefined;
}
/**
* 注册新的 `<mdui-layout-item>`
*/
registerItem(element) {
const state = { element };
this.states.push(state);
// 监听元素尺寸变化
state.observeResize = observeResize(state.element, () => {
this.updateLayout(state.element, {
width: this.isNoWidth(state) ? 0 : undefined,
});
});
this.items = undefined;
this.resort();
// 从头更新布局
this.updateLayout();
}
/**
* 取消注册 `<mdui-layout-item>`
*/
unregisterItem(element) {
const index = this.states.findIndex((item) => item.element === element);
if (index < 0) {
return;
}
// 取消监听尺寸变化
const item = this.states[index];
item.observeResize?.unobserve();
this.items = undefined;
// 移除一个元素,并从下一个元素开始更新
this.states.splice(index, 1);
if (this.states[index]) {
this.updateLayout(this.states[index].element);
}
}
/**
* 获取所有 `<mdui-layout-item>` 元素(按在 DOM 中的顺序)
*/
getItems() {
if (!this.items) {
const items = this.states.map((state) => state.element);
this.items = items.sort((a, b) => {
const position = a.compareDocumentPosition(b);
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
return -1;
}
else if (position & Node.DOCUMENT_POSITION_PRECEDING) {
return 1;
}
else {
return 0;
}
});
}
return this.items;
}
/**
* 获取 `<mdui-layout-main>` 元素
*/
getMain() {
return this.$main ? this.$main[0] : undefined;
}
/**
* 获取 `<mdui-layout-item>` 及 `<mdui-layout-main>` 元素
*/
getItemsAndMain() {
return [...this.getItems(), this.getMain()].filter((i) => i);
}
/**
* 更新 `order` 值,更新完后重新计算布局
*/
updateOrder() {
this.resort();
this.updateLayout();
}
/**
* 重新计算布局
* @param element 从哪一个元素开始更新;若未传入参数,则将更新所有元素
* @param size 此次更新中,元素的宽高(仅在此次更新中使用)。若不传则自动计算
*/
updateLayout(element, size) {
const state = element
? {
element,
width: size?.width,
height: size?.height,
}
: undefined;
const index = state
? this.states.findIndex((v) => v.element === state.element)
: 0;
if (index < 0) {
return;
}
Object.assign(this.states[index], state);
this.states.forEach((currState, currIndex) => {
if (currIndex < index) {
return;
}
// @ts-ignore
const placement = currState.element.layoutPlacement;
// 前一个元素
const prevState = currIndex > 0 ? this.states[currIndex - 1] : undefined;
const top = prevState?.top ?? 0;
const right = prevState?.right ?? 0;
const bottom = prevState?.bottom ?? 0;
const left = prevState?.left ?? 0;
Object.assign(currState, { top, right, bottom, left });
switch (placement) {
case 'top':
case 'bottom':
currState[placement] +=
currState.height ?? currState.element.offsetHeight;
break;
case 'right':
case 'left':
currState[placement] +=
(this.isNoWidth(currState) ? 0 : currState.width) ??
currState.element.offsetWidth;
break;
}
currState.height = currState.width = undefined;
$(currState.element).css({
position: 'absolute',
top: placement === 'bottom' ? null : top,
right: placement === 'left' ? null : right,
bottom: placement === 'top' ? null : bottom,
left: placement === 'right' ? null : left,
});
});
// 更新完后,设置 layout-main 的 padding
const lastState = this.states[this.states.length - 1];
if (this.$main) {
this.$main.css({
paddingTop: lastState.top,
paddingRight: lastState.right,
paddingBottom: lastState.bottom,
paddingLeft: lastState.left,
});
}
}
/**
* 按 order 排序order 相同时,按在 DOM 中的顺序排序
*/
resort() {
const items = this.getItems();
this.states.sort((a, b) => {
const aOrder = a.element.order ?? 0;
const bOrder = b.element.order ?? 0;
if (aOrder > bOrder) {
return 1;
}
if (aOrder < bOrder) {
return -1;
}
if (items.indexOf(a.element) > items.indexOf(b.element)) {
return 1;
}
if (items.indexOf(a.element) < items.indexOf(b.element)) {
return -1;
}
return 0;
});
}
/**
* 组件宽度是否为 0
* mdui-navigation-drawer 较为特殊,在为模态化时,占据的宽度为 0
*/
isNoWidth(state) {
return (isNodeName(state.element, 'mdui-navigation-drawer') &&
// @ts-ignore
state.element.isModal);
}
}
const layoutManagerMap = new WeakMap();
/**
* 获取 layout 实例
*/
export const getLayout = (element) => {
if (!layoutManagerMap.has(element)) {
layoutManagerMap.set(element, new LayoutManager());
}
return layoutManagerMap.get(element);
};

View File

@@ -0,0 +1,18 @@
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
import type { LayoutManager, LayoutPlacement } from './helper.js';
import type { PlainObject } from '@mdui/jq/shared/helper.js';
export declare class LayoutItemBase<E = PlainObject> extends MduiElement<E> {
/**
* 该组件在 [`<mdui-layout>`](/docs/2/components/layout) 中的布局顺序,按从小到大排序。默认为 `0`
*/
order?: number;
protected layoutManager?: LayoutManager;
protected isParentLayout: boolean;
/**
* 当前布局组件所处的位置,父类必须实现该 getter
*/
protected get layoutPlacement(): LayoutPlacement;
private onOrderChange;
connectedCallback(): void;
disconnectedCallback(): void;
}

View File

@@ -0,0 +1,44 @@
import { __decorate } from "tslib";
import { property } from 'lit/decorators.js';
import { isNodeName } from '@mdui/jq/shared/helper.js';
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
import { watch } from '@mdui/shared/decorators/watch.js';
import { getLayout } from './helper.js';
export class LayoutItemBase extends MduiElement {
constructor() {
super(...arguments);
// 父元素是否是 `mdui-layout`
this.isParentLayout = false;
}
/**
* 当前布局组件所处的位置,父类必须实现该 getter
*/
get layoutPlacement() {
throw new Error('Must implement placement getter!');
}
// order 变更时,需要重新调整布局
onOrderChange() {
this.layoutManager?.updateOrder();
}
connectedCallback() {
super.connectedCallback();
const parentElement = this.parentElement;
this.isParentLayout = isNodeName(parentElement, 'mdui-layout');
if (this.isParentLayout) {
this.layoutManager = getLayout(parentElement);
this.layoutManager.registerItem(this);
}
}
disconnectedCallback() {
super.disconnectedCallback();
if (this.layoutManager) {
this.layoutManager.unregisterItem(this);
}
}
}
__decorate([
property({ type: Number, reflect: true })
], LayoutItemBase.prototype, "order", void 0);
__decorate([
watch('order', true)
], LayoutItemBase.prototype, "onOrderChange", null);

View File

@@ -0,0 +1 @@
export declare const layoutItemStyle: import("lit").CSSResult;

View File

@@ -0,0 +1,2 @@
import { css } from 'lit';
export const layoutItemStyle = css `:host{display:flex;z-index:1}`;

View File

@@ -0,0 +1,38 @@
import { LayoutItemBase } from './layout-item-base.js';
import type { LayoutPlacement } from './helper.js';
import type { CSSResultGroup, TemplateResult } from 'lit';
/**
* @summary 布局项组件
*
* ```html
* <mdui-layout>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-main></mdui-layout-main>
* </mdui-layout>
* ```
*
* @slot - 可以包含任意内容
*/
export declare class LayoutItem extends LayoutItemBase<LayoutItemEventMap> {
static styles: CSSResultGroup;
/**
* 组件的位置。可选值包括:
*
* * `top`:上方
* * `bottom`:下方
* * `left`:左侧
* * `right`:右侧
*/
placement: /*上方*/ 'top' | /*下方*/ 'bottom' | /*左侧*/ 'left' | /*右侧*/ 'right';
protected get layoutPlacement(): LayoutPlacement;
private onPlacementChange;
protected render(): TemplateResult;
}
export interface LayoutItemEventMap {
}
declare global {
interface HTMLElementTagNameMap {
'mdui-layout-item': LayoutItem;
}
}

View File

@@ -0,0 +1,58 @@
import { __decorate } from "tslib";
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { watch } from '@mdui/shared/decorators/watch.js';
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
import { LayoutItemBase } from './layout-item-base.js';
import { layoutItemStyle } from './layout-item-style.js';
/**
* @summary 布局项组件
*
* ```html
* <mdui-layout>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-main></mdui-layout-main>
* </mdui-layout>
* ```
*
* @slot - 可以包含任意内容
*/
let LayoutItem = class LayoutItem extends LayoutItemBase {
constructor() {
super(...arguments);
/**
* 组件的位置。可选值包括:
*
* * `top`:上方
* * `bottom`:下方
* * `left`:左侧
* * `right`:右侧
*/
this.placement = 'top';
}
get layoutPlacement() {
return this.placement;
}
// placement 变更时,需要重新调整布局
onPlacementChange() {
this.layoutManager?.updateLayout(this);
}
render() {
return html `<slot></slot>`;
}
};
LayoutItem.styles = [
componentStyle,
layoutItemStyle,
];
__decorate([
property({ reflect: true })
], LayoutItem.prototype, "placement", void 0);
__decorate([
watch('placement', true)
], LayoutItem.prototype, "onPlacementChange", null);
LayoutItem = __decorate([
customElement('mdui-layout-item')
], LayoutItem);
export { LayoutItem };

View File

@@ -0,0 +1 @@
export declare const layoutMainStyle: import("lit").CSSResult;

View File

@@ -0,0 +1,2 @@
import { css } from 'lit';
export const layoutMainStyle = css `:host{flex:1 0 auto;max-width:100%;overflow:auto}`;

View File

@@ -0,0 +1,29 @@
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
import type { CSSResultGroup, TemplateResult } from 'lit';
/**
* @summary 布局主体内容组件
*
* ```html
* <mdui-layout>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-main></mdui-layout-main>
* </mdui-layout>
* ```
*
* @slot - 可以包含任意内容
*/
export declare class LayoutMain extends MduiElement<LayoutMainEventMap> {
static styles: CSSResultGroup;
private layoutManager?;
connectedCallback(): void;
disconnectedCallback(): void;
protected render(): TemplateResult;
}
export interface LayoutMainEventMap {
}
declare global {
interface HTMLElementTagNameMap {
'mdui-layout-main': LayoutMain;
}
}

View File

@@ -0,0 +1,48 @@
import { __decorate } from "tslib";
import { html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { isNodeName } from '@mdui/jq/shared/helper.js';
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
import { getLayout } from './helper.js';
import { layoutMainStyle } from './layout-main-style.js';
/**
* @summary 布局主体内容组件
*
* ```html
* <mdui-layout>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-main></mdui-layout-main>
* </mdui-layout>
* ```
*
* @slot - 可以包含任意内容
*/
let LayoutMain = class LayoutMain extends MduiElement {
connectedCallback() {
super.connectedCallback();
const parentElement = this.parentElement;
if (isNodeName(parentElement, 'mdui-layout')) {
this.layoutManager = getLayout(parentElement);
this.layoutManager.registerMain(this);
}
}
disconnectedCallback() {
super.disconnectedCallback();
if (this.layoutManager) {
this.layoutManager.unregisterMain();
}
}
render() {
return html `<slot></slot>`;
}
};
LayoutMain.styles = [
componentStyle,
layoutMainStyle,
];
LayoutMain = __decorate([
customElement('mdui-layout-main')
], LayoutMain);
export { LayoutMain };

View File

@@ -0,0 +1 @@
export declare const layoutStyle: import("lit").CSSResult;

View File

@@ -0,0 +1,2 @@
import { css } from 'lit';
export const layoutStyle = css `:host{position:relative;display:flex;flex:1 1 auto;overflow:hidden}:host([full-height]:not([full-height=false i])){height:100%}`;

View File

@@ -0,0 +1,30 @@
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
import type { CSSResultGroup, TemplateResult } from 'lit';
/**
* @summary 布局组件
*
* ```html
* <mdui-layout>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-main></mdui-layout-main>
* </mdui-layout>
* ```
*
* @slot - 可以包含 [`<mdui-top-app-bar>`](/docs/2/components/top-app-bar)、[`<mdui-bottom-app-bar>`](/docs/2/components/bottom-app-bar)、[`<mdui-navigation-bar>`](/docs/2/components/navigation-bar)、[`<mdui-navigation-drawer>`](/docs/2/components/navigation-drawer)、[`<mdui-navigation-rail>`](/docs/2/components/navigation-rail)、`<mdui-layout-item>`、`<mdui-layout-main>` 元素
*/
export declare class Layout extends MduiElement<LayoutEventMap> {
static styles: CSSResultGroup;
/**
* 设置当前布局的高度为 100%
*/
fullHeight: boolean;
protected render(): TemplateResult;
}
export interface LayoutEventMap {
}
declare global {
interface HTMLElementTagNameMap {
'mdui-layout': Layout;
}
}

View File

@@ -0,0 +1,45 @@
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 { layoutStyle } from './layout-style.js';
/**
* @summary 布局组件
*
* ```html
* <mdui-layout>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-item></mdui-layout-item>
* ..<mdui-layout-main></mdui-layout-main>
* </mdui-layout>
* ```
*
* @slot - 可以包含 [`<mdui-top-app-bar>`](/docs/2/components/top-app-bar)、[`<mdui-bottom-app-bar>`](/docs/2/components/bottom-app-bar)、[`<mdui-navigation-bar>`](/docs/2/components/navigation-bar)、[`<mdui-navigation-drawer>`](/docs/2/components/navigation-drawer)、[`<mdui-navigation-rail>`](/docs/2/components/navigation-rail)、`<mdui-layout-item>`、`<mdui-layout-main>` 元素
*/
let Layout = class Layout extends MduiElement {
constructor() {
super(...arguments);
/**
* 设置当前布局的高度为 100%
*/
this.fullHeight = false;
}
render() {
return html `<slot></slot>`;
}
};
Layout.styles = [componentStyle, layoutStyle];
__decorate([
property({
type: Boolean,
reflect: true,
converter: booleanConverter,
attribute: 'full-height',
})
], Layout.prototype, "fullHeight", void 0);
Layout = __decorate([
customElement('mdui-layout')
], Layout);
export { Layout };