Compare commits
28 Commits
af694f6f6c
...
f01f3b02f4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f01f3b02f4 | ||
|
|
ad4e873d2f | ||
|
|
a77e22a3ea | ||
|
|
1fa91279e2 | ||
|
|
debdb93935 | ||
|
|
81cdb4afd9 | ||
|
|
bc08cd3c8c | ||
|
|
c24078b29d | ||
|
|
f04748aa5c | ||
|
|
5ce97283f1 | ||
|
|
d6f794a094 | ||
|
|
47bbf12176 | ||
|
|
2cee988ada | ||
|
|
04989762d9 | ||
|
|
89db6591a0 | ||
|
|
d173fb7842 | ||
|
|
4133c13cf8 | ||
|
|
a1eddf813d | ||
|
|
39b4a6d8a6 | ||
|
|
e4cf9d6a68 | ||
|
|
f29538762b | ||
|
|
7616a49ff8 | ||
|
|
42aefdd2f1 | ||
|
|
5fadb76a20 | ||
|
|
5474eac554 | ||
|
|
a12a8830d4 | ||
|
|
6c5f3aac85 | ||
|
|
6e164cbdfb |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"deno.enable": true,
|
"deno.enable": true,
|
||||||
"deno.disablePaths": [
|
"deno.disablePaths": [
|
||||||
"./thewhitesilk_data"
|
"./thewhitesilk_data",
|
||||||
|
"./client/mdui_patched"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
20
client/MapJson.ts
Normal file
20
client/MapJson.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// https://stackoverflow.com/questions/29085197/how-do-you-json-stringify-an-es6-map
|
||||||
|
|
||||||
|
export default class MapJson {
|
||||||
|
static replacer(key: unknown, value: unknown) {
|
||||||
|
if (value instanceof Map) {
|
||||||
|
return {
|
||||||
|
dataType: 'Map',
|
||||||
|
value: Array.from(value.entries()), // or with spread: value: [...value]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static reviver(key: unknown, value: any) {
|
||||||
|
if (value?.dataType === 'Map') {
|
||||||
|
return new Map(value.value)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,17 +13,23 @@ export type CallMethod =
|
|||||||
"User.addContact" |
|
"User.addContact" |
|
||||||
"User.removeContacts" |
|
"User.removeContacts" |
|
||||||
|
|
||||||
|
"User.getMyRecentChats" |
|
||||||
|
|
||||||
"Chat.getInfo" |
|
"Chat.getInfo" |
|
||||||
|
|
||||||
|
"Chat.getIdForPrivate" |
|
||||||
|
"Chat.getAnotherUserIdFromPrivate" |
|
||||||
|
|
||||||
"Chat.sendMessage" |
|
"Chat.sendMessage" |
|
||||||
"Chat.getMessageHistory" |
|
"Chat.getMessageHistory" |
|
||||||
|
|
||||||
"Chat.uploadFile"
|
"Chat.uploadFile"
|
||||||
|
|
||||||
export const CallableMethodBeforeAuth = [
|
export type ClientEvent =
|
||||||
|
"Client.onMessage"
|
||||||
|
|
||||||
|
export const CallableMethodBeforeAuth = [
|
||||||
"User.auth",
|
"User.auth",
|
||||||
"User.register",
|
"User.register",
|
||||||
"User.login",
|
"User.login",
|
||||||
]
|
]
|
||||||
|
|
||||||
export type ClientEvent =
|
|
||||||
"Client.onMessage"
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import ChatType from "./ChatType.ts"
|
||||||
|
|
||||||
export default class Chat {
|
export default class Chat {
|
||||||
declare type: "paivate" | "group"
|
declare type: ChatType
|
||||||
declare id: string
|
declare id: string
|
||||||
declare title: string
|
declare title: string
|
||||||
declare avatar_file_hash?: string
|
declare avatar?: string
|
||||||
declare user_a_id?: string
|
|
||||||
declare user_b_id?: string
|
|
||||||
|
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|||||||
3
client/api/client_data/ChatType.ts
Normal file
3
client/api/client_data/ChatType.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
type ChatType = 'private' | 'group'
|
||||||
|
|
||||||
|
export default ChatType
|
||||||
@@ -9,6 +9,11 @@
|
|||||||
"jsxImportSource": "react",
|
"jsxImportSource": "react",
|
||||||
"jsxImportSourceTypes": "@types/react"
|
"jsxImportSourceTypes": "@types/react"
|
||||||
},
|
},
|
||||||
|
"patch": [
|
||||||
|
"./mdui_patched",
|
||||||
|
],
|
||||||
|
"nodeModulesDir": "auto",
|
||||||
|
"unstable": ["npm-patch"],
|
||||||
"imports": {
|
"imports": {
|
||||||
"@deno/vite-plugin": "npm:@deno/vite-plugin@1.0.5",
|
"@deno/vite-plugin": "npm:@deno/vite-plugin@1.0.5",
|
||||||
"@types/react": "npm:@types/react@18.3.1",
|
"@types/react": "npm:@types/react@18.3.1",
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ import AppMobile from './ui/AppMobile.tsx'
|
|||||||
import isMobileUI from "./ui/isMobileUI.ts"
|
import isMobileUI from "./ui/isMobileUI.ts"
|
||||||
ReactDOM.createRoot(document.getElementById('app') as HTMLElement).render(React.createElement(isMobileUI() ? AppMobile : App, null))
|
ReactDOM.createRoot(document.getElementById('app') as HTMLElement).render(React.createElement(isMobileUI() ? AppMobile : App, null))
|
||||||
|
|
||||||
const onResize = () => setTimeout(() => {
|
const onResize = () => {
|
||||||
document.body.style.setProperty('--whitesilk-widget-message-maxwidth', breakpoint().down('md') ? "80%" : "70%")
|
document.body.style.setProperty('--whitesilk-widget-message-maxwidth', breakpoint().down('md') ? "80%" : "70%")
|
||||||
// deno-lint-ignore no-window
|
// deno-lint-ignore no-window
|
||||||
document.body.style.setProperty('--whitesilk-window-width', window.innerWidth + 'px')
|
document.body.style.setProperty('--whitesilk-window-width', window.innerWidth + 'px')
|
||||||
// deno-lint-ignore no-window
|
// deno-lint-ignore no-window
|
||||||
document.body.style.setProperty('--whitesilk-window-height', window.innerHeight + 'px')
|
document.body.style.setProperty('--whitesilk-window-height', window.innerHeight + 'px')
|
||||||
}, 100)
|
}
|
||||||
// deno-lint-ignore no-window no-window-prefix
|
// deno-lint-ignore no-window no-window-prefix
|
||||||
window.addEventListener('resize', onResize)
|
window.addEventListener('resize', onResize)
|
||||||
onResize()
|
onResize()
|
||||||
21
client/mdui_patched/LICENSE
Normal file
21
client/mdui_patched/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-present zdhxiong@gmail.com
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
49
client/mdui_patched/README.md
Normal file
49
client/mdui_patched/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# mdui
|
||||||
|
|
||||||
|
[](https://badge.fury.io/gh/zdhxiong%2Fmdui)
|
||||||
|
[](https://www.npmjs.com/package/mdui)
|
||||||
|
[](https://cdnjs.com/libraries/mdui)
|
||||||
|
|
||||||
|
[官网](https://www.mdui.org) | [文档](https://www.mdui.org/docs/2/)
|
||||||
|
|
||||||
|
使用 Web Components 实现,遵循 Material You 设计规范的 Web 前端组件库。
|
||||||
|
|
||||||
|
* **Web Components**:mdui 组件全部使用 Web Components 开发,使用组件就像使用 `<div>` 标签一样简单。
|
||||||
|
* **Material You**:遵循最新的 Material Design 3(Material You)设计规范,使你的产品美观、易用。
|
||||||
|
* **动态配色**:支持根据给定颜色值,或给定一张图片,mdui 能自动计算出颜色值,生成整套配色方案,并在所有 mdui 组件中生效。
|
||||||
|
* **暗色模式**:所有组件都支持暗色模式、及支持根据操作系统设置自动切换亮色模式和暗色模式。
|
||||||
|
* **轻量级**:gzip 后的 CSS + JavaScript 仅 85KB,使用按需导入可进一步减小体积,使加载更迅速。
|
||||||
|
* **IDE 支持**:在 VSCode 和 WebStorm 中能获得完美的代码提示。且提供了 VSCode 扩展和 WebStorm 插件,使开发更便捷。
|
||||||
|
* **兼容所有框架**:mdui 能兼容 Vue、React、Angular 等框架,只要在浏览器上运行的应用,都能使用 mdui。
|
||||||
|
* **TypeScript 支持**:mdui 完全使用 TypeScript 开发,拥有完美的类型提示。
|
||||||
|
* **无依赖**:不需要依赖任何第三方库,节约网络流量,使加载更迅速。
|
||||||
|
* **组件丰富**:mdui 包含 30 多个组件,及十余个工具函数,常用组件都有。
|
||||||
|
* **Material Icons 图标库**:提供了超过 1 万个图标组件,可按需导入所需图标。
|
||||||
|
* **低学习成本**:只需懂一点 HTML、CSS、JavaScript 的基础知识,就能使用 mdui。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install mdui --save
|
||||||
|
```
|
||||||
|
|
||||||
|
### 导入 CSS 及 JS 文件
|
||||||
|
|
||||||
|
```js
|
||||||
|
import 'mdui/mdui.css';
|
||||||
|
import 'mdui';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用组件
|
||||||
|
|
||||||
|
```html
|
||||||
|
<mdui-button>Button</mdui-button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 赞助
|
||||||
|
|
||||||
|
赞助以帮助 mdui 持续更新
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
[](https://www.paypal.me/zdhxiong/5)
|
||||||
1
client/mdui_patched/components/avatar.d.ts
vendored
Normal file
1
client/mdui_patched/components/avatar.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './avatar/index.js';
|
||||||
1
client/mdui_patched/components/avatar.js
Normal file
1
client/mdui_patched/components/avatar.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './avatar/index.js';
|
||||||
51
client/mdui_patched/components/avatar/index.d.ts
vendored
Normal file
51
client/mdui_patched/components/avatar/index.d.ts
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import type { CSSResultGroup, TemplateResult } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 头像组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-avatar src="https://avatars.githubusercontent.com/u/3030330?s=40&v=4"></mdui-avatar>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @slot - 自定义头像内容,可以为字母、汉字、`<img>` 元素、图标等
|
||||||
|
*
|
||||||
|
* @csspart image - 使用图片作为头像时,组件内部的 `<img>` 元素
|
||||||
|
* @csspart icon - 使用图标作为头像时,组件内部的 `<mdui-icon>` 元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
export declare class Avatar extends MduiElement<AvatarEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 头像图片的 URL 地址
|
||||||
|
*/
|
||||||
|
src?: string;
|
||||||
|
/**
|
||||||
|
* 图片如何适应容器框,与原生的 [`object-fit`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) 属性相同。可选值包括:
|
||||||
|
*
|
||||||
|
* * `contain`:保持图片原有尺寸比例,内容会被等比例缩放
|
||||||
|
* * `cover`:保持图片原有尺寸比例,但部分内容可能被剪切
|
||||||
|
* * `fill`:默认值,不保持图片原有尺寸比例,内容会被拉伸以填充整个容器
|
||||||
|
* * `none`:保留图片原有尺寸,内容不会被缩放或拉伸
|
||||||
|
* * `scale-down`:保持图片原有尺寸比例,内容尺寸与 `none` 或 `contain` 中较小的一个相同
|
||||||
|
*/
|
||||||
|
fit?: /*保持图片原有尺寸比例,内容会被等比例缩放*/ 'contain' | /*保持图片原有尺寸比例,但部分内容可能被剪切*/ 'cover' | /*默认值,不保持图片原有尺寸比例,内容会被拉伸以填充整个容器*/ 'fill' | /*保留图片原有尺寸,内容不会被缩放或拉伸*/ 'none' | /*保持图片原有尺寸比例,内容尺寸与 `none` 或 `contain` 中较小的一个相同*/ 'scale-down';
|
||||||
|
/**
|
||||||
|
* 头像的 Material Icons 图标名
|
||||||
|
*/
|
||||||
|
icon?: string;
|
||||||
|
/**
|
||||||
|
* 头像的替代文本描述
|
||||||
|
*/
|
||||||
|
label?: string;
|
||||||
|
private readonly hasSlotController;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
}
|
||||||
|
export interface AvatarEventMap {
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-avatar': Avatar;
|
||||||
|
}
|
||||||
|
}
|
||||||
57
client/mdui_patched/components/avatar/index.js
Normal file
57
client/mdui_patched/components/avatar/index.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import { HasSlotController } from '@mdui/shared/controllers/has-slot.js';
|
||||||
|
import { nothingTemplate } from '@mdui/shared/helpers/template.js';
|
||||||
|
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 头像组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-avatar src="https://avatars.githubusercontent.com/u/3030330?s=40&v=4"></mdui-avatar>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @slot - 自定义头像内容,可以为字母、汉字、`<img>` 元素、图标等
|
||||||
|
*
|
||||||
|
* @csspart image - 使用图片作为头像时,组件内部的 `<img>` 元素
|
||||||
|
* @csspart icon - 使用图标作为头像时,组件内部的 `<mdui-icon>` 元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
let Avatar = class Avatar extends MduiElement {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
this.hasSlotController = new HasSlotController(this, '[default]');
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return this.hasSlotController.test('[default]')
|
||||||
|
? html `<slot></slot>`
|
||||||
|
: this.src
|
||||||
|
? html `<img part="image" alt="${ifDefined(this.label)}" src="${this.src}" style="${styleMap({ objectFit: this.fit })}">`
|
||||||
|
: this.icon
|
||||||
|
? html `<mdui-icon part="icon" name="${this.icon}"></mdui-icon>`
|
||||||
|
: nothingTemplate;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Avatar.styles = [componentStyle, style];
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Avatar.prototype, "src", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Avatar.prototype, "fit", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Avatar.prototype, "icon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Avatar.prototype, "label", void 0);
|
||||||
|
Avatar = __decorate([
|
||||||
|
customElement('mdui-avatar')
|
||||||
|
], Avatar);
|
||||||
|
export { Avatar };
|
||||||
1
client/mdui_patched/components/avatar/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/avatar/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
2
client/mdui_patched/components/avatar/style.js
Normal file
2
client/mdui_patched/components/avatar/style.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{--shape-corner:var(--mdui-shape-corner-full);position:relative;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;white-space:nowrap;vertical-align:middle;border-radius:var(--shape-corner);-webkit-user-select:none;user-select:none;width:2.5rem;height:2.5rem;background-color:rgb(var(--mdui-color-primary-container));color:rgb(var(--mdui-color-on-primary-container));font-size:var(--mdui-typescale-title-medium-size);font-weight:var(--mdui-typescale-title-medium-weight);letter-spacing:var(--mdui-typescale-title-medium-tracking);line-height:var(--mdui-typescale-title-medium-line-height)}img{width:100%;height:100%}::slotted(mdui-icon),mdui-icon{font-size:1.5em}`;
|
||||||
1
client/mdui_patched/components/badge.d.ts
vendored
Normal file
1
client/mdui_patched/components/badge.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './badge/index.js';
|
||||||
1
client/mdui_patched/components/badge.js
Normal file
1
client/mdui_patched/components/badge.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './badge/index.js';
|
||||||
31
client/mdui_patched/components/badge/index.d.ts
vendored
Normal file
31
client/mdui_patched/components/badge/index.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import type { CSSResultGroup, TemplateResult } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 徽标组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-badge>12</mdui-badge>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @slot - 徽标中显示的文本
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
export declare class Badge extends MduiElement<BadgeEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 徽标的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `small`:小型徽标,不显示文本
|
||||||
|
* * `large`:大型徽标,会显示文本
|
||||||
|
*/
|
||||||
|
variant: /*小型徽标,不显示文本*/ 'small' | /*大型徽标,会显示文本*/ 'large';
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
}
|
||||||
|
export interface BadgeEventMap {
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-badge': Badge;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
client/mdui_patched/components/badge/index.js
Normal file
44
client/mdui_patched/components/badge/index.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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 { nothingTemplate } from '@mdui/shared/helpers/template.js';
|
||||||
|
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||||
|
import { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 徽标组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-badge>12</mdui-badge>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @slot - 徽标中显示的文本
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
let Badge = class Badge extends MduiElement {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 徽标的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `small`:小型徽标,不显示文本
|
||||||
|
* * `large`:大型徽标,会显示文本
|
||||||
|
*/
|
||||||
|
this.variant = 'large';
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
if (this.variant === 'small') {
|
||||||
|
return nothingTemplate;
|
||||||
|
}
|
||||||
|
return html `<slot></slot>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Badge.styles = [componentStyle, style];
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Badge.prototype, "variant", void 0);
|
||||||
|
Badge = __decorate([
|
||||||
|
customElement('mdui-badge')
|
||||||
|
], Badge);
|
||||||
|
export { Badge };
|
||||||
1
client/mdui_patched/components/badge/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/badge/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
2
client/mdui_patched/components/badge/style.js
Normal file
2
client/mdui_patched/components/badge/style.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{--shape-corner:var(--mdui-shape-corner-full);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;border-radius:var(--shape-corner);padding-left:.25rem;padding-right:.25rem;color:rgb(var(--mdui-color-on-error));background-color:rgb(var(--mdui-color-error));height:1rem;min-width:1rem;font-size:var(--mdui-typescale-label-small-size);font-weight:var(--mdui-typescale-label-small-weight);letter-spacing:var(--mdui-typescale-label-small-tracking);line-height:var(--mdui-typescale-label-small-line-height)}:host([variant=small]){min-width:0;padding:0;width:.375rem;height:.375rem}`;
|
||||||
1
client/mdui_patched/components/bottom-app-bar.d.ts
vendored
Normal file
1
client/mdui_patched/components/bottom-app-bar.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './bottom-app-bar/index.js';
|
||||||
1
client/mdui_patched/components/bottom-app-bar.js
Normal file
1
client/mdui_patched/components/bottom-app-bar.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './bottom-app-bar/index.js';
|
||||||
67
client/mdui_patched/components/bottom-app-bar/index.d.ts
vendored
Normal file
67
client/mdui_patched/components/bottom-app-bar/index.d.ts
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { LayoutItemBase } from '../layout/layout-item-base.js';
|
||||||
|
import type { LayoutPlacement } from '../layout/helper.js';
|
||||||
|
import type { ScrollPaddingPosition } from '@mdui/shared/mixins/scrollBehavior.js';
|
||||||
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
|
||||||
|
declare const BottomAppBar_base: import("@lit/reactive-element/decorators/base.js").Constructor<import("@mdui/shared/mixins/scrollBehavior.js").ScrollBehaviorMixinInterface> & typeof LayoutItemBase;
|
||||||
|
/**
|
||||||
|
* @summary 底部应用栏组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-bottom-app-bar>
|
||||||
|
* ..<mdui-button-icon icon="check_box--outlined"></mdui-button-icon>
|
||||||
|
* ..<mdui-button-icon icon="edit--outlined"></mdui-button-icon>
|
||||||
|
* ..<mdui-button-icon icon="mic_none--outlined"></mdui-button-icon>
|
||||||
|
* ..<mdui-button-icon icon="image--outlined"></mdui-button-icon>
|
||||||
|
* ..<div style="flex-grow: 1"></div>
|
||||||
|
* ..<mdui-fab icon="add"></mdui-fab>
|
||||||
|
* </mdui-bottom-app-bar>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event show - 开始显示时,事件被触发。可以通过调用 `event.preventDefault()` 阻止显示
|
||||||
|
* @event shown - 显示动画完成时,事件被触发
|
||||||
|
* @event hide - 开始隐藏时,事件被触发。可以通过调用 `event.preventDefault()` 阻止隐藏
|
||||||
|
* @event hidden - 隐藏动画完成时,事件被触发
|
||||||
|
*
|
||||||
|
* @slot - 底部应用栏内部的元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
* @cssprop --z-index - 组件的 CSS `z-index` 值
|
||||||
|
*/
|
||||||
|
export declare class BottomAppBar extends BottomAppBar_base<BottomAppBarEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 是否隐藏
|
||||||
|
*/
|
||||||
|
hide: boolean;
|
||||||
|
/**
|
||||||
|
* 是否让底部应用栏中的 [`<mdui-fab>`](/docs/2/components/fab) 组件脱离应用栏。如果为 `true`,则当应用栏隐藏后,[`<mdui-fab>`](/docs/2/components/fab) 仍会停留在页面上
|
||||||
|
*/
|
||||||
|
fabDetach: boolean;
|
||||||
|
/**
|
||||||
|
* 滚动行为。可选值为:
|
||||||
|
*
|
||||||
|
* * `hide`:滚动时隐藏
|
||||||
|
*/
|
||||||
|
scrollBehavior?: 'hide';
|
||||||
|
protected get scrollPaddingPosition(): ScrollPaddingPosition;
|
||||||
|
protected get layoutPlacement(): LayoutPlacement;
|
||||||
|
protected firstUpdated(_changedProperties: PropertyValues): void;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
/**
|
||||||
|
* 滚动行为
|
||||||
|
* 当前仅支持 hide 这一个行为,所以不做行为类型判断
|
||||||
|
*/
|
||||||
|
protected runScrollThreshold(isScrollingUp: boolean): void;
|
||||||
|
}
|
||||||
|
export interface BottomAppBarEventMap {
|
||||||
|
show: CustomEvent<void>;
|
||||||
|
shown: CustomEvent<void>;
|
||||||
|
hide: CustomEvent<void>;
|
||||||
|
hidden: CustomEvent<void>;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-bottom-app-bar': BottomAppBar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {};
|
||||||
105
client/mdui_patched/components/bottom-app-bar/index.js
Normal file
105
client/mdui_patched/components/bottom-app-bar/index.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
|
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||||
|
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||||
|
import { ScrollBehaviorMixin } from '@mdui/shared/mixins/scrollBehavior.js';
|
||||||
|
import { LayoutItemBase } from '../layout/layout-item-base.js';
|
||||||
|
import { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 底部应用栏组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-bottom-app-bar>
|
||||||
|
* ..<mdui-button-icon icon="check_box--outlined"></mdui-button-icon>
|
||||||
|
* ..<mdui-button-icon icon="edit--outlined"></mdui-button-icon>
|
||||||
|
* ..<mdui-button-icon icon="mic_none--outlined"></mdui-button-icon>
|
||||||
|
* ..<mdui-button-icon icon="image--outlined"></mdui-button-icon>
|
||||||
|
* ..<div style="flex-grow: 1"></div>
|
||||||
|
* ..<mdui-fab icon="add"></mdui-fab>
|
||||||
|
* </mdui-bottom-app-bar>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event show - 开始显示时,事件被触发。可以通过调用 `event.preventDefault()` 阻止显示
|
||||||
|
* @event shown - 显示动画完成时,事件被触发
|
||||||
|
* @event hide - 开始隐藏时,事件被触发。可以通过调用 `event.preventDefault()` 阻止隐藏
|
||||||
|
* @event hidden - 隐藏动画完成时,事件被触发
|
||||||
|
*
|
||||||
|
* @slot - 底部应用栏内部的元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
* @cssprop --z-index - 组件的 CSS `z-index` 值
|
||||||
|
*/
|
||||||
|
let BottomAppBar = class BottomAppBar extends ScrollBehaviorMixin(LayoutItemBase) {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 是否隐藏
|
||||||
|
*/
|
||||||
|
this.hide = false;
|
||||||
|
/**
|
||||||
|
* 是否让底部应用栏中的 [`<mdui-fab>`](/docs/2/components/fab) 组件脱离应用栏。如果为 `true`,则当应用栏隐藏后,[`<mdui-fab>`](/docs/2/components/fab) 仍会停留在页面上
|
||||||
|
*/
|
||||||
|
this.fabDetach = false;
|
||||||
|
}
|
||||||
|
get scrollPaddingPosition() {
|
||||||
|
return 'bottom';
|
||||||
|
}
|
||||||
|
get layoutPlacement() {
|
||||||
|
return 'bottom';
|
||||||
|
}
|
||||||
|
firstUpdated(_changedProperties) {
|
||||||
|
super.firstUpdated(_changedProperties);
|
||||||
|
this.addEventListener('transitionend', (event) => {
|
||||||
|
if (event.target === this) {
|
||||||
|
this.emit(this.hide ? 'hidden' : 'shown');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return html `<slot></slot>`;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 滚动行为
|
||||||
|
* 当前仅支持 hide 这一个行为,所以不做行为类型判断
|
||||||
|
*/
|
||||||
|
runScrollThreshold(isScrollingUp) {
|
||||||
|
// 向下滚动
|
||||||
|
if (!isScrollingUp && !this.hide) {
|
||||||
|
const eventProceeded = this.emit('hide', { cancelable: true });
|
||||||
|
if (eventProceeded) {
|
||||||
|
this.hide = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 向上滚动
|
||||||
|
if (isScrollingUp && this.hide) {
|
||||||
|
const eventProceeded = this.emit('show', { cancelable: true });
|
||||||
|
if (eventProceeded) {
|
||||||
|
this.hide = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BottomAppBar.styles = [componentStyle, style];
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], BottomAppBar.prototype, "hide", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
attribute: 'fab-detach',
|
||||||
|
})
|
||||||
|
], BottomAppBar.prototype, "fabDetach", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'scroll-behavior' })
|
||||||
|
], BottomAppBar.prototype, "scrollBehavior", void 0);
|
||||||
|
BottomAppBar = __decorate([
|
||||||
|
customElement('mdui-bottom-app-bar')
|
||||||
|
], BottomAppBar);
|
||||||
|
export { BottomAppBar };
|
||||||
1
client/mdui_patched/components/bottom-app-bar/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/bottom-app-bar/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
2
client/mdui_patched/components/bottom-app-bar/style.js
Normal file
2
client/mdui_patched/components/bottom-app-bar/style.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{--shape-corner:var(--mdui-shape-corner-none);--z-index:2000;position:fixed;right:0;bottom:0;left:0;display:flex;flex:0 0 auto;align-items:center;justify-content:flex-start;border-radius:var(--shape-corner) var(--shape-corner) 0 0;z-index:var(--z-index);transition:bottom var(--mdui-motion-duration-long2) var(--mdui-motion-easing-emphasized);padding:0 1rem;height:5rem;background-color:rgb(var(--mdui-color-surface-container));box-shadow:var(--mdui-elevation-level2)}:host([scroll-target]:not([scroll-target=''])){position:absolute}:host([hide]:not([hide=false i])){transition-duration:var(--mdui-motion-duration-short4);bottom:-5.625rem}::slotted(:not(:first-child)){margin-left:.5rem}::slotted(mdui-fab){box-shadow:var(--mdui-elevation-level0)}:host([fab-detach]:not([fab-detach=false i])) ::slotted(mdui-fab){position:absolute;transition:bottom var(--mdui-motion-duration-long2) var(--mdui-motion-easing-standard);right:1rem;bottom:.75rem}:host([fab-detach][hide][scroll-behavior~=hide]:not([hide=false i],[fab-detach=false i])) ::slotted(mdui-fab){transition-duration:var(--mdui-motion-duration-short4);bottom:1rem;box-shadow:var(--mdui-elevation-level2)}:host([fab-detach][hide][scroll-behavior~=hide][scroll-target]:not([fab-detach=false i],[hide=false i],[scroll-target=''])) ::slotted(mdui-fab){bottom:6.625rem}:host([hide]:not([hide=false i])) ::slotted(:not(mdui-fab)),:host([hide]:not([hide=false i],[fab-detach])) ::slotted(mdui-fab),:host([hide][fab-detach=false i]:not([hide=false i])) ::slotted(mdui-fab){transform:translateY(8.75rem);transition:transform var(--mdui-motion-duration-0) var(--mdui-motion-easing-emphasized-accelerate) var(--mdui-motion-duration-short4)}::slotted(:first-child){transition:transform var(--mdui-motion-duration-short3) var(--mdui-motion-easing-emphasized-decelerate) var(--mdui-motion-duration-short1)}::slotted(:nth-child(2)){transition:transform var(--mdui-motion-duration-short3) var(--mdui-motion-easing-emphasized-decelerate) var(--mdui-motion-duration-short3)}::slotted(:nth-child(3)){transition:transform var(--mdui-motion-duration-short3) var(--mdui-motion-easing-emphasized-decelerate) var(--mdui-motion-duration-short4)}::slotted(:nth-child(4)){transition:transform var(--mdui-motion-duration-short3) var(--mdui-motion-easing-emphasized-decelerate) var(--mdui-motion-duration-medium1)}::slotted(:nth-child(5)){transition:transform var(--mdui-motion-duration-short3) var(--mdui-motion-easing-emphasized-decelerate) var(--mdui-motion-duration-medium2)}::slotted(:nth-child(6)){transition:transform var(--mdui-motion-duration-short3) var(--mdui-motion-easing-emphasized-decelerate) var(--mdui-motion-duration-medium3)}`;
|
||||||
1
client/mdui_patched/components/button-icon.d.ts
vendored
Normal file
1
client/mdui_patched/components/button-icon.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './button-icon/index.js';
|
||||||
1
client/mdui_patched/components/button-icon.js
Normal file
1
client/mdui_patched/components/button-icon.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './button-icon/index.js';
|
||||||
72
client/mdui_patched/components/button-icon/index.d.ts
vendored
Normal file
72
client/mdui_patched/components/button-icon/index.d.ts
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { ButtonBase } from '../button/button-base.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import type { Ripple } from '../ripple/index.js';
|
||||||
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 图标按钮组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-button-icon icon="search"></mdui-button-icon>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
* @event change - 选中状态变更时触发
|
||||||
|
* @event invalid - 表单字段验证未通过时触发
|
||||||
|
*
|
||||||
|
* @slot - 图标组件
|
||||||
|
* @slot selected-icon 选中状态显示的图标元素
|
||||||
|
*
|
||||||
|
* @csspart button - 内部的 `<button>` 或 `<a>` 元素
|
||||||
|
* @csspart icon - 未选中状态的图标
|
||||||
|
* @csspart selected-icon 选中状态的图标
|
||||||
|
* @csspart loading - 加载中状态的 `<mdui-circular-progress>` 元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
export declare class ButtonIcon extends ButtonBase<ButtonIconEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 图标按钮的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `standard`:适用于最低优先级的操作
|
||||||
|
* * `filled`:视觉效果强烈,适用于高优先级的操作
|
||||||
|
* * `tonal`:视觉效果介于 `filled` 和 `outlined` 之间,适用于中高优先级的操作
|
||||||
|
* * `outlined`:适用于中等优先级的操作
|
||||||
|
*/
|
||||||
|
variant: /*适用于最低优先级的操作*/ 'standard' | /*视觉效果强烈,适用于高优先级的操作*/ 'filled' | /*视觉效果介于 `filled` 和 `outlined` 之间,适用于中高优先级的操作*/ 'tonal' | /*适用于中等优先级的操作*/ 'outlined';
|
||||||
|
/**
|
||||||
|
* Material Icons 图标名。也可以通过 default slot 设置
|
||||||
|
*/
|
||||||
|
icon?: string;
|
||||||
|
/**
|
||||||
|
* 选中状态的 Material Icons 图标名。也可以通过 `slot="selected-icon"` 设置
|
||||||
|
*/
|
||||||
|
selectedIcon?: string;
|
||||||
|
/**
|
||||||
|
* 是否可选中
|
||||||
|
*/
|
||||||
|
selectable: boolean;
|
||||||
|
/**
|
||||||
|
* 是否已被选中
|
||||||
|
*/
|
||||||
|
selected: boolean;
|
||||||
|
private readonly rippleRef;
|
||||||
|
private readonly hasSlotController;
|
||||||
|
protected get rippleElement(): Ripple;
|
||||||
|
private onSelectedChange;
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues): void;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
private renderIcon;
|
||||||
|
}
|
||||||
|
export interface ButtonIconEventMap {
|
||||||
|
focus: FocusEvent;
|
||||||
|
blur: FocusEvent;
|
||||||
|
change: CustomEvent<void>;
|
||||||
|
invalid: CustomEvent<void>;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-button-icon': ButtonIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
129
client/mdui_patched/components/button-icon/index.js
Normal file
129
client/mdui_patched/components/button-icon/index.js
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
|
import { createRef, ref } from 'lit/directives/ref.js';
|
||||||
|
import { HasSlotController } from '@mdui/shared/controllers/has-slot.js';
|
||||||
|
import { watch } from '@mdui/shared/decorators/watch.js';
|
||||||
|
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||||
|
import { nothingTemplate } from '@mdui/shared/helpers/template.js';
|
||||||
|
import { ButtonBase } from '../button/button-base.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 图标按钮组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-button-icon icon="search"></mdui-button-icon>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
* @event change - 选中状态变更时触发
|
||||||
|
* @event invalid - 表单字段验证未通过时触发
|
||||||
|
*
|
||||||
|
* @slot - 图标组件
|
||||||
|
* @slot selected-icon 选中状态显示的图标元素
|
||||||
|
*
|
||||||
|
* @csspart button - 内部的 `<button>` 或 `<a>` 元素
|
||||||
|
* @csspart icon - 未选中状态的图标
|
||||||
|
* @csspart selected-icon 选中状态的图标
|
||||||
|
* @csspart loading - 加载中状态的 `<mdui-circular-progress>` 元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
let ButtonIcon = class ButtonIcon extends ButtonBase {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 图标按钮的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `standard`:适用于最低优先级的操作
|
||||||
|
* * `filled`:视觉效果强烈,适用于高优先级的操作
|
||||||
|
* * `tonal`:视觉效果介于 `filled` 和 `outlined` 之间,适用于中高优先级的操作
|
||||||
|
* * `outlined`:适用于中等优先级的操作
|
||||||
|
*/
|
||||||
|
this.variant = 'standard';
|
||||||
|
/**
|
||||||
|
* 是否可选中
|
||||||
|
*/
|
||||||
|
this.selectable = false;
|
||||||
|
/**
|
||||||
|
* 是否已被选中
|
||||||
|
*/
|
||||||
|
this.selected = false;
|
||||||
|
this.rippleRef = createRef();
|
||||||
|
this.hasSlotController = new HasSlotController(this, '[default]', 'selected-icon');
|
||||||
|
}
|
||||||
|
get rippleElement() {
|
||||||
|
return this.rippleRef.value;
|
||||||
|
}
|
||||||
|
onSelectedChange() {
|
||||||
|
this.emit('change');
|
||||||
|
}
|
||||||
|
firstUpdated(changedProperties) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
this.addEventListener('click', () => {
|
||||||
|
if (!this.selectable || this.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.selected = !this.selected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return html `<mdui-ripple ${ref(this.rippleRef)} .noRipple="${this.noRipple}"></mdui-ripple>${this.isButton()
|
||||||
|
? this.renderButton({
|
||||||
|
className: 'button',
|
||||||
|
part: 'button',
|
||||||
|
content: this.renderIcon(),
|
||||||
|
})
|
||||||
|
: this.disabled || this.loading
|
||||||
|
? html `<span part="button" class="button _a">${this.renderIcon()}</span>`
|
||||||
|
: this.renderAnchor({
|
||||||
|
className: 'button',
|
||||||
|
part: 'button',
|
||||||
|
content: this.renderIcon(),
|
||||||
|
})} ${this.renderLoading()}`;
|
||||||
|
}
|
||||||
|
renderIcon() {
|
||||||
|
const icon = () => this.hasSlotController.test('[default]')
|
||||||
|
? html `<slot></slot>`
|
||||||
|
: this.icon
|
||||||
|
? html `<mdui-icon part="icon" class="icon" name="${this.icon}"></mdui-icon>`
|
||||||
|
: nothingTemplate;
|
||||||
|
const selectedIcon = () => this.hasSlotController.test('selected-icon') || this.selectedIcon
|
||||||
|
? html `<slot name="selected-icon" part="selected-icon" class="selected-icon"><mdui-icon name="${this.selectedIcon}"></mdui-icon></slot>`
|
||||||
|
: icon();
|
||||||
|
return this.selected ? selectedIcon() : icon();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ButtonIcon.styles = [ButtonBase.styles, style];
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], ButtonIcon.prototype, "variant", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], ButtonIcon.prototype, "icon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'selected-icon' })
|
||||||
|
], ButtonIcon.prototype, "selectedIcon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], ButtonIcon.prototype, "selectable", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], ButtonIcon.prototype, "selected", void 0);
|
||||||
|
__decorate([
|
||||||
|
watch('selected', true)
|
||||||
|
], ButtonIcon.prototype, "onSelectedChange", null);
|
||||||
|
ButtonIcon = __decorate([
|
||||||
|
customElement('mdui-button-icon')
|
||||||
|
], ButtonIcon);
|
||||||
|
export { ButtonIcon };
|
||||||
1
client/mdui_patched/components/button-icon/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/button-icon/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
4
client/mdui_patched/components/button-icon/style.js
Normal file
4
client/mdui_patched/components/button-icon/style.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{--shape-corner:var(--mdui-shape-corner-full);position:relative;display:inline-block;flex-shrink:0;overflow:hidden;text-align:center;border-radius:var(--shape-corner);cursor:pointer;-webkit-tap-highlight-color:transparent;font-size:1.5rem;width:2.5rem;height:2.5rem}:host([variant=standard]){color:rgb(var(--mdui-color-on-surface-variant));--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-surface-variant)}:host([variant=filled]){color:rgb(var(--mdui-color-primary));background-color:rgb(var(--mdui-color-surface-container-highest));--mdui-comp-ripple-state-layer-color:var(--mdui-color-primary)}:host([variant=tonal]){color:rgb(var(--mdui-color-on-surface-variant));background-color:rgb(var(--mdui-color-surface-container-highest));--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-surface-variant)}:host([variant=outlined]){border:.0625rem solid rgb(var(--mdui-color-outline));color:rgb(var(--mdui-color-on-surface-variant));--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-surface-variant)}:host([variant=outlined][pressed]){color:rgb(var(--mdui-color-on-surface));--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-surface)}:host([variant=standard][selected]:not([selected=false i])){color:rgb(var(--mdui-color-primary));--mdui-comp-ripple-state-layer-color:var(--mdui-color-primary)}:host([variant=filled]:not([selectable])),:host([variant=filled][selectable=false i]),:host([variant=filled][selected]:not([selected=false i])){color:rgb(var(--mdui-color-on-primary));background-color:rgb(var(--mdui-color-primary));--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-primary)}:host([variant=tonal]:not([selectable])),:host([variant=tonal][selectable=false i]),:host([variant=tonal][selected]:not([selected=false i])){color:rgb(var(--mdui-color-on-secondary-container));background-color:rgb(var(--mdui-color-secondary-container));--mdui-comp-ripple-state-layer-color:var(
|
||||||
|
--mdui-color-on-secondary-container
|
||||||
|
)}:host([variant=outlined][selected]:not([selected=false i])){border:none;color:rgb(var(--mdui-color-inverse-on-surface));background-color:rgb(var(--mdui-color-inverse-surface));--mdui-comp-ripple-state-layer-color:var(--mdui-color-inverse-on-surface)}:host([variant=filled][disabled]:not([disabled=false i])),:host([variant=outlined][disabled]:not([disabled=false i])),:host([variant=tonal][disabled]:not([disabled=false i])){background-color:rgba(var(--mdui-color-on-surface),.12);border-color:rgba(var(--mdui-color-on-surface),.12)}:host([disabled]:not([disabled=false i])),:host([loading]:not([loading=false i])){cursor:default;pointer-events:none}:host([disabled]:not([disabled=false i])){color:rgba(var(--mdui-color-on-surface),.38)!important}.button{float:left;width:100%}:host([loading]:not([loading=false i])) .button,:host([loading]:not([loading=false i])) mdui-ripple{opacity:0}.icon,.selected-icon mdui-icon,::slotted(*){font-size:inherit}mdui-circular-progress{display:flex;position:absolute;top:calc(50% - 1.5rem / 2);left:calc(50% - 1.5rem / 2);width:1.5rem;height:1.5rem}:host([variant=filled]:not([disabled])) mdui-circular-progress,:host([variant=filled][disabled=false i]) mdui-circular-progress{stroke:rgb(var(--mdui-color-on-primary))}:host([disabled]:not([disabled=false i])) mdui-circular-progress{stroke:rgba(var(--mdui-color-on-surface),38%)}`;
|
||||||
1
client/mdui_patched/components/button.d.ts
vendored
Normal file
1
client/mdui_patched/components/button.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './button/index.js';
|
||||||
1
client/mdui_patched/components/button.js
Normal file
1
client/mdui_patched/components/button.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './button/index.js';
|
||||||
1
client/mdui_patched/components/button/button-base-style.d.ts
vendored
Normal file
1
client/mdui_patched/components/button/button-base-style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const buttonBaseStyle: import("lit").CSSResult;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const buttonBaseStyle = css `.button{position:relative;display:inline-flex;align-items:center;justify-content:center;height:100%;padding:0;overflow:hidden;color:inherit;font-size:inherit;font-family:inherit;font-weight:inherit;letter-spacing:inherit;white-space:nowrap;text-align:center;text-decoration:none;vertical-align:middle;background:0 0;border:none;outline:0;cursor:inherit;-webkit-user-select:none;user-select:none;touch-action:manipulation;zoom:1;-webkit-user-drag:none}`;
|
||||||
137
client/mdui_patched/components/button/button-base.d.ts
vendored
Normal file
137
client/mdui_patched/components/button/button-base.d.ts
vendored
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import '../circular-progress.js';
|
||||||
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
|
||||||
|
type RenderButtonOptions = {
|
||||||
|
id?: string;
|
||||||
|
className?: string;
|
||||||
|
part?: string;
|
||||||
|
content?: TemplateResult | TemplateResult[];
|
||||||
|
tabindex?: number;
|
||||||
|
};
|
||||||
|
declare const ButtonBase_base: import("@lit/reactive-element/decorators/base.js").Constructor<import("@mdui/shared/mixins/anchor.js").AnchorMixinInterface> & 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;
|
||||||
|
export declare class ButtonBase<E> extends ButtonBase_base<E> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 是否禁用
|
||||||
|
*/
|
||||||
|
disabled: boolean;
|
||||||
|
/**
|
||||||
|
* 是否处于加载中状态
|
||||||
|
*/
|
||||||
|
loading: boolean;
|
||||||
|
/**
|
||||||
|
* 按钮的名称,将与表单数据一起提交。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未设置 `href` 属性时,此属性才有效。
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* 按钮的初始值,将与表单数据一起提交。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未设置 `href` 属性时,此属性才有效。
|
||||||
|
*/
|
||||||
|
value: string;
|
||||||
|
/**
|
||||||
|
* 按钮的类型。默认类型为 `button`。可选类型包括:
|
||||||
|
*
|
||||||
|
* * `submit`:点击按钮会提交表单数据到服务器
|
||||||
|
* * `reset`:点击按钮会将表单中的所有字段重置为初始值
|
||||||
|
* * `button`:此类型的按钮没有默认行为
|
||||||
|
*
|
||||||
|
* **Note**:仅在未指定 `href` 属性时,此属性才有效。
|
||||||
|
*/
|
||||||
|
type: /*此按钮将表单数据提交给服务器*/ 'submit' | /*此按钮重置所有组件为初始值*/ 'reset' | /*此按钮没有默认行为*/ 'button';
|
||||||
|
/**
|
||||||
|
* 关联的 `<form>` 元素。此属性值应为同一页面中的一个 `<form>` 元素的 `id`。
|
||||||
|
*
|
||||||
|
* 如果未指定此属性,则该元素必须是 `<form>` 元素的子元素。通过此属性,你可以将元素放置在页面的任何位置,而不仅仅是 `<form>` 元素的子元素。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未指定 `href` 属性时,此属性才有效。
|
||||||
|
*/
|
||||||
|
form?: string;
|
||||||
|
/**
|
||||||
|
* 指定提交表单的 URL。
|
||||||
|
*
|
||||||
|
* 如果指定了此属性,将覆盖 `<form>` 元素的 `action` 属性。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未指定 `href` 属性且 `type="submit"` 时,此属性才有效。
|
||||||
|
*/
|
||||||
|
formAction?: string;
|
||||||
|
/**
|
||||||
|
* 指定提交表单到服务器的内容类型。可选值包括:
|
||||||
|
*
|
||||||
|
* * `application/x-www-form-urlencoded`:未指定该属性时的默认值
|
||||||
|
* * `multipart/form-data`:当表单包含 `<input type="file">` 元素时使用
|
||||||
|
* * `text/plain`:HTML5 新增,用于调试
|
||||||
|
*
|
||||||
|
* 如果指定了此属性,将覆盖 `<form>` 元素的 `enctype` 属性。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未指定 `href` 属性且 `type="submit"` 时,此属性才有效。
|
||||||
|
*/
|
||||||
|
formEnctype?: /*未指定该属性时的默认值*/ 'application/x-www-form-urlencoded' | /*当表单包含 `<input type="file">` 元素时使用*/ 'multipart/form-data' | /*HTML5 新增,用于调试*/ 'text/plain';
|
||||||
|
/**
|
||||||
|
* 指定提交表单时使用的 HTTP 方法。可选值包括:
|
||||||
|
*
|
||||||
|
* * `post`:表单数据包含在表单内容中,发送到服务器
|
||||||
|
* * `get`:表单数据以 `?` 作为分隔符附加到表单的 URI 属性中,生成的 URI 发送到服务器。当表单没有副作用,并且仅包含 ASCII 字符时,使用此方法
|
||||||
|
*
|
||||||
|
* 如果设置了此属性,将覆盖 `<form>` 元素的 `method` 属性。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未设置 `href` 属性且 `type="submit"` 时,此属性才有效。
|
||||||
|
*/
|
||||||
|
formMethod?: /*表单数据包含在表单内容中,发送到服务器*/ 'post' | /*表单数据以 `?` 作为分隔符附加到表单的 URI 属性中,生成的 URI 发送到服务器。当表单没有副作用,并且仅包含 ASCII 字符时,使用此方法*/ 'get';
|
||||||
|
/**
|
||||||
|
* 如果设置了此属性,表单提交时将不执行表单验证。
|
||||||
|
*
|
||||||
|
* 如果设置了此属性,将覆盖 `<form>` 元素的 `novalidate` 属性。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未设置 `href` 属性且 `type="submit"` 时,此属性才有效。
|
||||||
|
*/
|
||||||
|
formNoValidate: boolean;
|
||||||
|
/**
|
||||||
|
* 提交表单后接收到的响应应显示在何处。可选值包括:
|
||||||
|
*
|
||||||
|
* * `_self`:默认选项,在当前框架中打开
|
||||||
|
* * `_blank`:在新窗口中打开
|
||||||
|
* * `_parent`:在父框架中打开
|
||||||
|
* * `_top`:在整个窗口中打开
|
||||||
|
*
|
||||||
|
* 如果设置了此属性,将覆盖 `<form>` 元素的 `target` 属性。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未设置 `href` 属性且 `type="submit"` 时,此属性才有效。
|
||||||
|
*/
|
||||||
|
formTarget?: /*默认选项,在当前框架中打开*/ '_self' | /*在新窗口中打开*/ '_blank' | /*在父框架中打开*/ '_parent' | /*在整个窗口中打开*/ '_top';
|
||||||
|
private readonly formController;
|
||||||
|
/**
|
||||||
|
* 表单验证状态对象,具体参见 [`ValidityState`](https://developer.mozilla.org/zh-CN/docs/Web/API/ValidityState)
|
||||||
|
*/
|
||||||
|
get validity(): ValidityState | undefined;
|
||||||
|
/**
|
||||||
|
* 如果表单验证未通过,此属性将包含提示信息。如果验证通过,此属性将为空字符串
|
||||||
|
*/
|
||||||
|
get validationMessage(): string | undefined;
|
||||||
|
protected get rippleDisabled(): boolean;
|
||||||
|
protected get focusElement(): HTMLElement | null;
|
||||||
|
protected get focusDisabled(): boolean;
|
||||||
|
/**
|
||||||
|
* 检查表单字段是否通过验证。如果未通过,返回 `false` 并触发 `invalid` 事件;如果通过,返回 `true`
|
||||||
|
*/
|
||||||
|
checkValidity(): boolean;
|
||||||
|
/**
|
||||||
|
* 检查表单字段是否通过验证。如果未通过,返回 `false` 并触发 `invalid` 事件;如果通过,返回 `true`。
|
||||||
|
*
|
||||||
|
* 如果验证未通过,还会在组件上显示验证失败的提示。
|
||||||
|
*/
|
||||||
|
reportValidity(): boolean;
|
||||||
|
/**
|
||||||
|
* 设置自定义的错误提示文本。只要这个文本不为空,就表示字段未通过验证
|
||||||
|
*
|
||||||
|
* @param message 自定义的错误提示文本
|
||||||
|
*/
|
||||||
|
setCustomValidity(message: string): void;
|
||||||
|
protected firstUpdated(_changedProperties: PropertyValues): void;
|
||||||
|
protected renderLoading(): TemplateResult;
|
||||||
|
protected renderButton({ id, className, part, content, }: RenderButtonOptions): TemplateResult;
|
||||||
|
protected isButton(): boolean;
|
||||||
|
}
|
||||||
|
export {};
|
||||||
209
client/mdui_patched/components/button/button-base.js
Normal file
209
client/mdui_patched/components/button/button-base.js
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { property } from 'lit/decorators.js';
|
||||||
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
|
import cc from 'classcat';
|
||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import { FormController } from '@mdui/shared/controllers/form.js';
|
||||||
|
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||||
|
import { nothingTemplate } from '@mdui/shared/helpers/template.js';
|
||||||
|
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||||
|
import { AnchorMixin } from '@mdui/shared/mixins/anchor.js';
|
||||||
|
import { FocusableMixin } from '@mdui/shared/mixins/focusable.js';
|
||||||
|
import '../circular-progress.js';
|
||||||
|
import { RippleMixin } from '../ripple/ripple-mixin.js';
|
||||||
|
import { buttonBaseStyle } from './button-base-style.js';
|
||||||
|
export class ButtonBase extends AnchorMixin(RippleMixin(FocusableMixin(MduiElement))) {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 是否禁用
|
||||||
|
*/
|
||||||
|
this.disabled = false;
|
||||||
|
/**
|
||||||
|
* 是否处于加载中状态
|
||||||
|
*/
|
||||||
|
this.loading = false;
|
||||||
|
/**
|
||||||
|
* 按钮的名称,将与表单数据一起提交。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未设置 `href` 属性时,此属性才有效。
|
||||||
|
*/
|
||||||
|
this.name = '';
|
||||||
|
/**
|
||||||
|
* 按钮的初始值,将与表单数据一起提交。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未设置 `href` 属性时,此属性才有效。
|
||||||
|
*/
|
||||||
|
this.value = '';
|
||||||
|
/**
|
||||||
|
* 按钮的类型。默认类型为 `button`。可选类型包括:
|
||||||
|
*
|
||||||
|
* * `submit`:点击按钮会提交表单数据到服务器
|
||||||
|
* * `reset`:点击按钮会将表单中的所有字段重置为初始值
|
||||||
|
* * `button`:此类型的按钮没有默认行为
|
||||||
|
*
|
||||||
|
* **Note**:仅在未指定 `href` 属性时,此属性才有效。
|
||||||
|
*/
|
||||||
|
this.type = 'button';
|
||||||
|
/**
|
||||||
|
* 如果设置了此属性,表单提交时将不执行表单验证。
|
||||||
|
*
|
||||||
|
* 如果设置了此属性,将覆盖 `<form>` 元素的 `novalidate` 属性。
|
||||||
|
*
|
||||||
|
* **Note**:仅在未设置 `href` 属性且 `type="submit"` 时,此属性才有效。
|
||||||
|
*/
|
||||||
|
this.formNoValidate = false;
|
||||||
|
this.formController = new FormController(this);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 表单验证状态对象,具体参见 [`ValidityState`](https://developer.mozilla.org/zh-CN/docs/Web/API/ValidityState)
|
||||||
|
*/
|
||||||
|
get validity() {
|
||||||
|
if (this.isButton()) {
|
||||||
|
return this.focusElement.validity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 如果表单验证未通过,此属性将包含提示信息。如果验证通过,此属性将为空字符串
|
||||||
|
*/
|
||||||
|
get validationMessage() {
|
||||||
|
if (this.isButton()) {
|
||||||
|
return this.focusElement.validationMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get rippleDisabled() {
|
||||||
|
return this.disabled || this.loading;
|
||||||
|
}
|
||||||
|
get focusElement() {
|
||||||
|
return this.isButton()
|
||||||
|
? this.renderRoot?.querySelector('._button')
|
||||||
|
: !this.focusDisabled
|
||||||
|
? this.renderRoot?.querySelector('._a')
|
||||||
|
: this;
|
||||||
|
}
|
||||||
|
get focusDisabled() {
|
||||||
|
return this.disabled || this.loading;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查表单字段是否通过验证。如果未通过,返回 `false` 并触发 `invalid` 事件;如果通过,返回 `true`
|
||||||
|
*/
|
||||||
|
checkValidity() {
|
||||||
|
if (this.isButton()) {
|
||||||
|
const valid = this.focusElement.checkValidity();
|
||||||
|
if (!valid) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.emit('invalid', {
|
||||||
|
bubbles: false,
|
||||||
|
cancelable: true,
|
||||||
|
composed: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查表单字段是否通过验证。如果未通过,返回 `false` 并触发 `invalid` 事件;如果通过,返回 `true`。
|
||||||
|
*
|
||||||
|
* 如果验证未通过,还会在组件上显示验证失败的提示。
|
||||||
|
*/
|
||||||
|
reportValidity() {
|
||||||
|
if (this.isButton()) {
|
||||||
|
const invalid = !this.focusElement.reportValidity();
|
||||||
|
if (invalid) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.emit('invalid', {
|
||||||
|
bubbles: false,
|
||||||
|
cancelable: true,
|
||||||
|
composed: false,
|
||||||
|
});
|
||||||
|
// todo 考虑是否要支持 preventDefault() 方法,当前 invalid 状态没有样式
|
||||||
|
}
|
||||||
|
return !invalid;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 设置自定义的错误提示文本。只要这个文本不为空,就表示字段未通过验证
|
||||||
|
*
|
||||||
|
* @param message 自定义的错误提示文本
|
||||||
|
*/
|
||||||
|
setCustomValidity(message) {
|
||||||
|
if (this.isButton()) {
|
||||||
|
this.focusElement.setCustomValidity(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
firstUpdated(_changedProperties) {
|
||||||
|
super.firstUpdated(_changedProperties);
|
||||||
|
this.addEventListener('click', () => {
|
||||||
|
if (this.type === 'submit') {
|
||||||
|
this.formController.submit(this);
|
||||||
|
}
|
||||||
|
if (this.type === 'reset') {
|
||||||
|
this.formController.reset(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
renderLoading() {
|
||||||
|
return this.loading
|
||||||
|
? html `<mdui-circular-progress part="loading"></mdui-circular-progress>`
|
||||||
|
: nothingTemplate;
|
||||||
|
}
|
||||||
|
renderButton({ id, className, part, content = html `<slot></slot>`, }) {
|
||||||
|
return html `<button id="${ifDefined(id)}" class="${cc(['_button', className])}" part="${ifDefined(part)}" ?disabled="${this.rippleDisabled || this.focusDisabled}">${content}</button>`;
|
||||||
|
}
|
||||||
|
isButton() {
|
||||||
|
return !this.href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ButtonBase.styles = [
|
||||||
|
componentStyle,
|
||||||
|
buttonBaseStyle,
|
||||||
|
];
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], ButtonBase.prototype, "disabled", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], ButtonBase.prototype, "loading", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], ButtonBase.prototype, "name", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], ButtonBase.prototype, "value", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], ButtonBase.prototype, "type", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], ButtonBase.prototype, "form", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'formaction' })
|
||||||
|
], ButtonBase.prototype, "formAction", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'formenctype' })
|
||||||
|
], ButtonBase.prototype, "formEnctype", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'formmethod' })
|
||||||
|
], ButtonBase.prototype, "formMethod", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
attribute: 'formnovalidate',
|
||||||
|
})
|
||||||
|
], ButtonBase.prototype, "formNoValidate", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'formtarget' })
|
||||||
|
], ButtonBase.prototype, "formTarget", void 0);
|
||||||
69
client/mdui_patched/components/button/index.d.ts
vendored
Normal file
69
client/mdui_patched/components/button/index.d.ts
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import '../icon.js';
|
||||||
|
import { ButtonBase } from './button-base.js';
|
||||||
|
import type { Ripple } from '../ripple/index.js';
|
||||||
|
import type { TemplateResult, CSSResultGroup } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 按钮组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-button>Button</mdui-button>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
* @event invalid - 表单字段验证未通过时触发
|
||||||
|
*
|
||||||
|
* @slot - 按钮的文本
|
||||||
|
* @slot icon - 按钮左侧的元素
|
||||||
|
* @slot end-icon - 按钮右侧的元素
|
||||||
|
*
|
||||||
|
* @csspart button - 内部的 `<button>` 或 `<a>` 元素
|
||||||
|
* @csspart label - 按钮的文本
|
||||||
|
* @csspart icon - 按钮左侧的图标
|
||||||
|
* @csspart end-icon - 按钮右侧的图标
|
||||||
|
* @csspart loading - 加载中状态的 `<mdui-circular-progress>` 元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
export declare class Button extends ButtonBase<ButtonEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 按钮的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `elevated`:带阴影的按钮,适用于需要将按钮与背景视觉分离的场景
|
||||||
|
* * `filled`:视觉效果强烈,适用于重要流程的最终操作,如“保存”、“确认”等
|
||||||
|
* * `tonal`:视觉效果介于 `filled` 和 `outlined` 之间,适用于中高优先级的操作,如流程中的“下一步”
|
||||||
|
* * `outlined`:带边框的按钮,适用于中等优先级,且次要的操作,如“返回”
|
||||||
|
* * `text`:文本按钮,适用于最低优先级的操作
|
||||||
|
*/
|
||||||
|
variant: /*带阴影的按钮,适用于需要将按钮与背景视觉分离的场景*/ 'elevated' | /*视觉效果强烈,适用于重要流程的最终操作,如“保存”、“确认”等*/ 'filled' | /*视觉效果介于 `filled` 和 `outlined` 之间,适用于中高优先级的操作,如流程中的“下一步”*/ 'tonal' | /*带边框的按钮,适用于中等优先级,且次要的操作,如“返回”*/ 'outlined' | /*文本按钮,适用于最低优先级的操作*/ 'text';
|
||||||
|
/**
|
||||||
|
* 是否填满父元素宽度
|
||||||
|
*/
|
||||||
|
fullWidth: boolean;
|
||||||
|
/**
|
||||||
|
* 左侧的 Material Icons 图标名。也可以通过 `slot="icon"` 设置
|
||||||
|
*/
|
||||||
|
icon?: string;
|
||||||
|
/**
|
||||||
|
* 右侧的 Material Icons 图标名。也可以通过 `slot="end-icon"` 设置
|
||||||
|
*/
|
||||||
|
endIcon?: string;
|
||||||
|
private readonly rippleRef;
|
||||||
|
protected get rippleElement(): Ripple;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
private renderIcon;
|
||||||
|
private renderLabel;
|
||||||
|
private renderEndIcon;
|
||||||
|
private renderInner;
|
||||||
|
}
|
||||||
|
export interface ButtonEventMap {
|
||||||
|
focus: FocusEvent;
|
||||||
|
blur: FocusEvent;
|
||||||
|
invalid: CustomEvent<void>;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-button': Button;
|
||||||
|
}
|
||||||
|
}
|
||||||
111
client/mdui_patched/components/button/index.js
Normal file
111
client/mdui_patched/components/button/index.js
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
|
import { createRef, ref } from 'lit/directives/ref.js';
|
||||||
|
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||||
|
import { nothingTemplate } from '@mdui/shared/helpers/template.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import { ButtonBase } from './button-base.js';
|
||||||
|
import { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 按钮组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-button>Button</mdui-button>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
* @event invalid - 表单字段验证未通过时触发
|
||||||
|
*
|
||||||
|
* @slot - 按钮的文本
|
||||||
|
* @slot icon - 按钮左侧的元素
|
||||||
|
* @slot end-icon - 按钮右侧的元素
|
||||||
|
*
|
||||||
|
* @csspart button - 内部的 `<button>` 或 `<a>` 元素
|
||||||
|
* @csspart label - 按钮的文本
|
||||||
|
* @csspart icon - 按钮左侧的图标
|
||||||
|
* @csspart end-icon - 按钮右侧的图标
|
||||||
|
* @csspart loading - 加载中状态的 `<mdui-circular-progress>` 元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
let Button = class Button extends ButtonBase {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 按钮的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `elevated`:带阴影的按钮,适用于需要将按钮与背景视觉分离的场景
|
||||||
|
* * `filled`:视觉效果强烈,适用于重要流程的最终操作,如“保存”、“确认”等
|
||||||
|
* * `tonal`:视觉效果介于 `filled` 和 `outlined` 之间,适用于中高优先级的操作,如流程中的“下一步”
|
||||||
|
* * `outlined`:带边框的按钮,适用于中等优先级,且次要的操作,如“返回”
|
||||||
|
* * `text`:文本按钮,适用于最低优先级的操作
|
||||||
|
*/
|
||||||
|
this.variant = 'filled';
|
||||||
|
/**
|
||||||
|
* 是否填满父元素宽度
|
||||||
|
*/
|
||||||
|
this.fullWidth = false;
|
||||||
|
this.rippleRef = createRef();
|
||||||
|
}
|
||||||
|
get rippleElement() {
|
||||||
|
return this.rippleRef.value;
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return html `<mdui-ripple ${ref(this.rippleRef)} .noRipple="${this.noRipple}"></mdui-ripple>${this.isButton()
|
||||||
|
? this.renderButton({
|
||||||
|
className: 'button',
|
||||||
|
part: 'button',
|
||||||
|
content: this.renderInner(),
|
||||||
|
})
|
||||||
|
: this.disabled || this.loading
|
||||||
|
? html `<span part="button" class="button _a">${this.renderInner()}</span>`
|
||||||
|
: this.renderAnchor({
|
||||||
|
className: 'button',
|
||||||
|
part: 'button',
|
||||||
|
content: this.renderInner(),
|
||||||
|
})}`;
|
||||||
|
}
|
||||||
|
renderIcon() {
|
||||||
|
if (this.loading) {
|
||||||
|
return this.renderLoading();
|
||||||
|
}
|
||||||
|
return html `<slot name="icon" part="icon" class="icon">${this.icon
|
||||||
|
? html `<mdui-icon name="${this.icon}"></mdui-icon>`
|
||||||
|
: nothingTemplate}</slot>`;
|
||||||
|
}
|
||||||
|
renderLabel() {
|
||||||
|
return html `<slot part="label" class="label"></slot>`;
|
||||||
|
}
|
||||||
|
renderEndIcon() {
|
||||||
|
return html `<slot name="end-icon" part="end-icon" class="end-icon">${this.endIcon
|
||||||
|
? html `<mdui-icon name="${this.endIcon}"></mdui-icon>`
|
||||||
|
: nothingTemplate}</slot>`;
|
||||||
|
}
|
||||||
|
renderInner() {
|
||||||
|
return [this.renderIcon(), this.renderLabel(), this.renderEndIcon()];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Button.styles = [ButtonBase.styles, style];
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Button.prototype, "variant", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
attribute: 'full-width',
|
||||||
|
})
|
||||||
|
], Button.prototype, "fullWidth", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Button.prototype, "icon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'end-icon' })
|
||||||
|
], Button.prototype, "endIcon", void 0);
|
||||||
|
Button = __decorate([
|
||||||
|
customElement('mdui-button')
|
||||||
|
], Button);
|
||||||
|
export { Button };
|
||||||
1
client/mdui_patched/components/button/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/button/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
4
client/mdui_patched/components/button/style.js
Normal file
4
client/mdui_patched/components/button/style.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{--shape-corner:var(--mdui-shape-corner-full);position:relative;display:inline-block;flex-shrink:0;overflow:hidden;text-align:center;border-radius:var(--shape-corner);cursor:pointer;-webkit-tap-highlight-color:transparent;transition:box-shadow var(--mdui-motion-duration-short4) var(--mdui-motion-easing-linear);min-width:3rem;height:2.5rem;color:rgb(var(--mdui-color-primary));font-size:var(--mdui-typescale-label-large-size);font-weight:var(--mdui-typescale-label-large-weight);letter-spacing:var(--mdui-typescale-label-large-tracking);line-height:var(--mdui-typescale-label-large-line-height)}.button{width:100%;padding:0 1rem}:host([full-width]:not([full-width=false i])){display:block}:host([variant=elevated]){box-shadow:var(--mdui-elevation-level1);background-color:rgb(var(--mdui-color-surface-container-low));--mdui-comp-ripple-state-layer-color:var(--mdui-color-primary)}:host([variant=filled]){color:rgb(var(--mdui-color-on-primary));background-color:rgb(var(--mdui-color-primary));--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-primary)}:host([variant=tonal]){color:rgb(var(--mdui-color-on-secondary-container));background-color:rgb(var(--mdui-color-secondary-container));--mdui-comp-ripple-state-layer-color:var(
|
||||||
|
--mdui-color-on-secondary-container
|
||||||
|
)}:host([variant=outlined]){border:.0625rem solid rgb(var(--mdui-color-outline));--mdui-comp-ripple-state-layer-color:var(--mdui-color-primary)}:host([variant=text]){--mdui-comp-ripple-state-layer-color:var(--mdui-color-primary)}:host([variant=outlined][focus-visible]){border-color:rgb(var(--mdui-color-primary))}:host([variant=elevated][hover]){box-shadow:var(--mdui-elevation-level2)}:host([variant=filled][hover]),:host([variant=tonal][hover]){box-shadow:var(--mdui-elevation-level1)}:host([disabled]:not([disabled=false i])),:host([loading]:not([loading=false i])){cursor:default;pointer-events:none}:host([disabled]:not([disabled=false i])){color:rgba(var(--mdui-color-on-surface),38%);box-shadow:var(--mdui-elevation-level0)}:host([variant=elevated][disabled]:not([disabled=false i])),:host([variant=filled][disabled]:not([disabled=false i])),:host([variant=tonal][disabled]:not([disabled=false i])){background-color:rgba(var(--mdui-color-on-surface),12%)}:host([variant=outlined][disabled]:not([disabled=false i])){border-color:rgba(var(--mdui-color-on-surface),12%)}.label{display:inline-flex;padding-right:.5rem;padding-left:.5rem}.end-icon,.icon{display:inline-flex;font-size:1.28571429em}.end-icon mdui-icon,.icon mdui-icon,::slotted([slot=end-icon]),::slotted([slot=icon]){font-size:inherit}mdui-circular-progress{display:inline-flex;width:1.125rem;height:1.125rem}:host([variant=filled]) mdui-circular-progress{stroke:rgb(var(--mdui-color-on-primary))}:host([variant=tonal]) mdui-circular-progress{stroke:rgb(var(--mdui-color-on-secondary-container))}:host([disabled]:not([disabled=false i])) mdui-circular-progress{stroke:rgba(var(--mdui-color-on-surface),38%)}`;
|
||||||
1
client/mdui_patched/components/card.d.ts
vendored
Normal file
1
client/mdui_patched/components/card.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './card/index.js';
|
||||||
1
client/mdui_patched/components/card.js
Normal file
1
client/mdui_patched/components/card.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './card/index.js';
|
||||||
53
client/mdui_patched/components/card/index.d.ts
vendored
Normal file
53
client/mdui_patched/components/card/index.d.ts
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import type { Ripple } from '../ripple/index.js';
|
||||||
|
import type { TemplateResult, CSSResultGroup } from 'lit';
|
||||||
|
declare const Card_base: import("@lit/reactive-element/decorators/base.js").Constructor<import("@mdui/shared/mixins/anchor.js").AnchorMixinInterface> & 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 卡片组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-card>card content</mdui-card>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
*
|
||||||
|
* @slot - 卡片的内容
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
export declare class Card extends Card_base<CardEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 卡片的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `elevated`:带阴影的卡片,与背景的视觉分离度较高
|
||||||
|
* * `filled`:带填充色的卡片,与背景的视觉分离度较低
|
||||||
|
* * `outlined`:带边框的卡片,与背景的视觉分离度最高
|
||||||
|
*/
|
||||||
|
variant: /*带阴影的卡片,与背景的视觉分离度较高*/ 'elevated' | /*带填充色的卡片,与背景的视觉分离度较低*/ 'filled' | /*带边框的卡片,与背景的视觉分离度最高*/ 'outlined';
|
||||||
|
/**
|
||||||
|
* 是否可点击。为 `true` 时,卡片将具有鼠标悬浮效果和点击涟漪效果
|
||||||
|
*/
|
||||||
|
clickable: boolean;
|
||||||
|
/**
|
||||||
|
* 是否禁用
|
||||||
|
*/
|
||||||
|
disabled: boolean;
|
||||||
|
private readonly rippleRef;
|
||||||
|
protected get rippleElement(): Ripple;
|
||||||
|
protected get rippleDisabled(): boolean;
|
||||||
|
protected get focusElement(): HTMLElement | null;
|
||||||
|
protected get focusDisabled(): boolean;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
}
|
||||||
|
export interface CardEventMap {
|
||||||
|
focus: FocusEvent;
|
||||||
|
blur: FocusEvent;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-card': Card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {};
|
||||||
91
client/mdui_patched/components/card/index.js
Normal file
91
client/mdui_patched/components/card/index.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
|
import { createRef, ref } from 'lit/directives/ref.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 { AnchorMixin } from '@mdui/shared/mixins/anchor.js';
|
||||||
|
import { FocusableMixin } from '@mdui/shared/mixins/focusable.js';
|
||||||
|
import { RippleMixin } from '../ripple/ripple-mixin.js';
|
||||||
|
import { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 卡片组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-card>card content</mdui-card>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
*
|
||||||
|
* @slot - 卡片的内容
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
let Card = class Card extends AnchorMixin(RippleMixin(FocusableMixin(MduiElement))) {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 卡片的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `elevated`:带阴影的卡片,与背景的视觉分离度较高
|
||||||
|
* * `filled`:带填充色的卡片,与背景的视觉分离度较低
|
||||||
|
* * `outlined`:带边框的卡片,与背景的视觉分离度最高
|
||||||
|
*/
|
||||||
|
this.variant = 'elevated';
|
||||||
|
/**
|
||||||
|
* 是否可点击。为 `true` 时,卡片将具有鼠标悬浮效果和点击涟漪效果
|
||||||
|
*/
|
||||||
|
this.clickable = false;
|
||||||
|
/**
|
||||||
|
* 是否禁用
|
||||||
|
*/
|
||||||
|
this.disabled = false;
|
||||||
|
this.rippleRef = createRef();
|
||||||
|
}
|
||||||
|
get rippleElement() {
|
||||||
|
return this.rippleRef.value;
|
||||||
|
}
|
||||||
|
get rippleDisabled() {
|
||||||
|
return this.disabled || (!this.href && !this.clickable);
|
||||||
|
}
|
||||||
|
get focusElement() {
|
||||||
|
return this.href && !this.disabled
|
||||||
|
? this.renderRoot.querySelector('._a')
|
||||||
|
: this;
|
||||||
|
}
|
||||||
|
get focusDisabled() {
|
||||||
|
return this.rippleDisabled;
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return html `<mdui-ripple ${ref(this.rippleRef)} .noRipple="${this.noRipple}"></mdui-ripple>${this.href && !this.disabled
|
||||||
|
? this.renderAnchor({
|
||||||
|
className: 'link',
|
||||||
|
content: html `<slot></slot>`,
|
||||||
|
})
|
||||||
|
: html `<slot></slot>`}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Card.styles = [componentStyle, style];
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Card.prototype, "variant", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Card.prototype, "clickable", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Card.prototype, "disabled", void 0);
|
||||||
|
Card = __decorate([
|
||||||
|
customElement('mdui-card')
|
||||||
|
], Card);
|
||||||
|
export { Card };
|
||||||
1
client/mdui_patched/components/card/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/card/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
2
client/mdui_patched/components/card/style.js
Normal file
2
client/mdui_patched/components/card/style.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{--shape-corner:var(--mdui-shape-corner-medium);position:relative;display:inline-block;overflow:hidden;border-radius:var(--shape-corner);-webkit-tap-highlight-color:transparent;transition:box-shadow var(--mdui-motion-duration-short4) var(--mdui-motion-easing-linear);--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-surface)}:host([clickable]:not([clickable=false i])){cursor:pointer}:host([variant=elevated]){background-color:rgb(var(--mdui-color-surface-container-low));box-shadow:var(--mdui-elevation-level1)}:host([variant=filled]){background-color:rgb(var(--mdui-color-surface-container-highest))}:host([variant=outlined]){background-color:rgb(var(--mdui-color-surface));border:.0625rem solid rgb(var(--mdui-color-outline))}:host([variant=elevated][hover]){box-shadow:var(--mdui-elevation-level2)}:host([variant=filled][hover]),:host([variant=outlined][hover]){box-shadow:var(--mdui-elevation-level1)}:host([variant=elevated][dragged]),:host([variant=filled][dragged]),:host([variant=outlined][dragged]){box-shadow:var(--mdui-elevation-level3)}:host([disabled]:not([disabled=false i])){opacity:.38;cursor:default;-webkit-user-select:none;user-select:none}:host([variant=elevated][disabled]:not([disabled=false i])){background-color:rgb(var(--mdui-color-surface-variant));box-shadow:var(--mdui-elevation-level0)}:host([variant=filled][disabled]:not([disabled=false i])){background-color:rgb(var(--mdui-color-surface));box-shadow:var(--mdui-elevation-level1)}:host([variant=outlined][disabled]:not([disabled=false i])){box-shadow:var(--mdui-elevation-level0);border-color:rgba(var(--mdui-color-outline),.32)}.link{position:relative;display:inline-block;width:100%;height:100%;color:inherit;font-size:inherit;letter-spacing:inherit;text-decoration:none;touch-action:manipulation;-webkit-user-drag:none}`;
|
||||||
1
client/mdui_patched/components/checkbox.d.ts
vendored
Normal file
1
client/mdui_patched/components/checkbox.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './checkbox/index.js';
|
||||||
1
client/mdui_patched/components/checkbox.js
Normal file
1
client/mdui_patched/components/checkbox.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './checkbox/index.js';
|
||||||
137
client/mdui_patched/components/checkbox/index.d.ts
vendored
Normal file
137
client/mdui_patched/components/checkbox/index.d.ts
vendored
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import '@mdui/shared/icons/check-box-outline-blank.js';
|
||||||
|
import '@mdui/shared/icons/check-box.js';
|
||||||
|
import '@mdui/shared/icons/indeterminate-check-box.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import type { Ripple } from '../ripple/index.js';
|
||||||
|
import type { FormControl } from '@mdui/jq/shared/form.js';
|
||||||
|
import type { TemplateResult, CSSResultGroup } from 'lit';
|
||||||
|
declare const Checkbox_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 复选框组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-checkbox>Checkbox</mdui-checkbox>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
* @event change - 选中状态变更时触发
|
||||||
|
* @event input - 选中状态变更时触发
|
||||||
|
* @event invalid - 表单字段验证未通过时触发
|
||||||
|
*
|
||||||
|
* @slot - 复选框文本
|
||||||
|
* @slot unchecked-icon - 未选中状态的图标
|
||||||
|
* @slot checked-icon - 选中状态的图标
|
||||||
|
* @slot indeterminate-icon - 不确定状态的图标
|
||||||
|
*
|
||||||
|
* @csspart control - 左侧图标容器
|
||||||
|
* @csspart unchecked-icon - 未选中状态的图标
|
||||||
|
* @csspart checked-icon - 选中状态的图标
|
||||||
|
* @csspart indeterminate-icon - 不确定状态的图标
|
||||||
|
* @csspart label - 复选框文本
|
||||||
|
*/
|
||||||
|
export declare class Checkbox extends Checkbox_base<CheckboxEventMap> implements FormControl {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 是否为禁用状态
|
||||||
|
*/
|
||||||
|
disabled: boolean;
|
||||||
|
/**
|
||||||
|
* 是否为选中状态
|
||||||
|
*/
|
||||||
|
checked: boolean;
|
||||||
|
/**
|
||||||
|
* 默认选中状态。在重置表单时,将恢复为此状态。此属性只能通过 JavaScript 属性设置
|
||||||
|
*/
|
||||||
|
defaultChecked: boolean;
|
||||||
|
/**
|
||||||
|
* 是否处于不确定状态
|
||||||
|
*/
|
||||||
|
indeterminate: boolean;
|
||||||
|
/**
|
||||||
|
* 提交表单时,是否必须选中此复选框
|
||||||
|
*/
|
||||||
|
required: boolean;
|
||||||
|
/**
|
||||||
|
* 关联的 `<form>` 元素。此属性值应为同一页面中的一个 `<form>` 元素的 `id`。
|
||||||
|
*
|
||||||
|
* 如果未指定此属性,则该元素必须是 `<form>` 元素的子元素。通过此属性,你可以将元素放置在页面的任何位置,而不仅仅是 `<form>` 元素的子元素。
|
||||||
|
*/
|
||||||
|
form?: string;
|
||||||
|
/**
|
||||||
|
* 复选框名称,将与表单数据一起提交
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* 复选框的值,将于表单数据一起提交
|
||||||
|
*/
|
||||||
|
value: string;
|
||||||
|
/**
|
||||||
|
* 未选中状态的 Material Icons 图标名。也可以通过 `slot="unchecked-icon"` 设置
|
||||||
|
*/
|
||||||
|
uncheckedIcon?: string;
|
||||||
|
/**
|
||||||
|
* 选中状态的 Material Icons 图标名。也可以通过 `slot="checked-icon"` 设置
|
||||||
|
*/
|
||||||
|
checkedIcon?: string;
|
||||||
|
/**
|
||||||
|
* 不确定状态的 Material Icons 图标名。也可以通过 `slot="indeterminate-icon"` 设置
|
||||||
|
*/
|
||||||
|
indeterminateIcon?: string;
|
||||||
|
/**
|
||||||
|
* 是否验证未通过
|
||||||
|
*/
|
||||||
|
private invalid;
|
||||||
|
private readonly inputRef;
|
||||||
|
private readonly rippleRef;
|
||||||
|
private readonly formController;
|
||||||
|
/**
|
||||||
|
* 表单验证状态对象,具体参见 [`ValidityState`](https://developer.mozilla.org/zh-CN/docs/Web/API/ValidityState)
|
||||||
|
*/
|
||||||
|
get validity(): ValidityState;
|
||||||
|
/**
|
||||||
|
* 如果表单验证未通过,此属性将包含提示信息。如果验证通过,此属性将为空字符串
|
||||||
|
*/
|
||||||
|
get validationMessage(): string;
|
||||||
|
protected get rippleElement(): Ripple;
|
||||||
|
protected get rippleDisabled(): boolean;
|
||||||
|
protected get focusElement(): HTMLElement | undefined;
|
||||||
|
protected get focusDisabled(): boolean;
|
||||||
|
private onDisabledChange;
|
||||||
|
private onCheckedChange;
|
||||||
|
/**
|
||||||
|
* 检查表单字段是否通过验证。如果未通过,返回 `false` 并触发 `invalid` 事件;如果通过,返回 `true`
|
||||||
|
*/
|
||||||
|
checkValidity(): boolean;
|
||||||
|
/**
|
||||||
|
* 检查表单字段是否通过验证。如果未通过,返回 `false` 并触发 `invalid` 事件;如果通过,返回 `true`。
|
||||||
|
*
|
||||||
|
* 如果验证未通过,还会在组件上显示验证失败的提示。
|
||||||
|
*/
|
||||||
|
reportValidity(): boolean;
|
||||||
|
/**
|
||||||
|
* 设置自定义的错误提示文本。只要这个文本不为空,就表示字段未通过验证
|
||||||
|
*
|
||||||
|
* @param message 自定义的错误提示文本
|
||||||
|
*/
|
||||||
|
setCustomValidity(message: string): void;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
/**
|
||||||
|
* input[type="checkbox"] 的 change 事件无法冒泡越过 shadow dom
|
||||||
|
*/
|
||||||
|
private onChange;
|
||||||
|
}
|
||||||
|
export interface CheckboxEventMap {
|
||||||
|
focus: FocusEvent;
|
||||||
|
blur: FocusEvent;
|
||||||
|
change: CustomEvent<void>;
|
||||||
|
input: Event;
|
||||||
|
invalid: CustomEvent<void>;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-checkbox': Checkbox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {};
|
||||||
254
client/mdui_patched/components/checkbox/index.js
Normal file
254
client/mdui_patched/components/checkbox/index.js
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
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 { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
|
import { live } from 'lit/directives/live.js';
|
||||||
|
import { createRef, ref } from 'lit/directives/ref.js';
|
||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import { FormController, formResets } from '@mdui/shared/controllers/form.js';
|
||||||
|
import { defaultValue } from '@mdui/shared/decorators/default-value.js';
|
||||||
|
import { watch } from '@mdui/shared/decorators/watch.js';
|
||||||
|
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||||
|
import '@mdui/shared/icons/check-box-outline-blank.js';
|
||||||
|
import '@mdui/shared/icons/check-box.js';
|
||||||
|
import '@mdui/shared/icons/indeterminate-check-box.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 { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 复选框组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-checkbox>Checkbox</mdui-checkbox>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
* @event change - 选中状态变更时触发
|
||||||
|
* @event input - 选中状态变更时触发
|
||||||
|
* @event invalid - 表单字段验证未通过时触发
|
||||||
|
*
|
||||||
|
* @slot - 复选框文本
|
||||||
|
* @slot unchecked-icon - 未选中状态的图标
|
||||||
|
* @slot checked-icon - 选中状态的图标
|
||||||
|
* @slot indeterminate-icon - 不确定状态的图标
|
||||||
|
*
|
||||||
|
* @csspart control - 左侧图标容器
|
||||||
|
* @csspart unchecked-icon - 未选中状态的图标
|
||||||
|
* @csspart checked-icon - 选中状态的图标
|
||||||
|
* @csspart indeterminate-icon - 不确定状态的图标
|
||||||
|
* @csspart label - 复选框文本
|
||||||
|
*/
|
||||||
|
let Checkbox = class Checkbox extends RippleMixin(FocusableMixin(MduiElement)) {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 是否为禁用状态
|
||||||
|
*/
|
||||||
|
this.disabled = false;
|
||||||
|
/**
|
||||||
|
* 是否为选中状态
|
||||||
|
*/
|
||||||
|
this.checked = false;
|
||||||
|
/**
|
||||||
|
* 默认选中状态。在重置表单时,将恢复为此状态。此属性只能通过 JavaScript 属性设置
|
||||||
|
*/
|
||||||
|
this.defaultChecked = false;
|
||||||
|
/**
|
||||||
|
* 是否处于不确定状态
|
||||||
|
*/
|
||||||
|
this.indeterminate = false;
|
||||||
|
/**
|
||||||
|
* 提交表单时,是否必须选中此复选框
|
||||||
|
*/
|
||||||
|
this.required = false;
|
||||||
|
/**
|
||||||
|
* 复选框名称,将与表单数据一起提交
|
||||||
|
*/
|
||||||
|
this.name = '';
|
||||||
|
/**
|
||||||
|
* 复选框的值,将于表单数据一起提交
|
||||||
|
*/
|
||||||
|
this.value = 'on';
|
||||||
|
/**
|
||||||
|
* 是否验证未通过
|
||||||
|
*/
|
||||||
|
this.invalid = false;
|
||||||
|
this.inputRef = createRef();
|
||||||
|
this.rippleRef = createRef();
|
||||||
|
this.formController = new FormController(this, {
|
||||||
|
value: (control) => (control.checked ? control.value : undefined),
|
||||||
|
defaultValue: (control) => control.defaultChecked,
|
||||||
|
setValue: (control, checked) => (control.checked = checked),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 表单验证状态对象,具体参见 [`ValidityState`](https://developer.mozilla.org/zh-CN/docs/Web/API/ValidityState)
|
||||||
|
*/
|
||||||
|
get validity() {
|
||||||
|
return this.inputRef.value.validity;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 如果表单验证未通过,此属性将包含提示信息。如果验证通过,此属性将为空字符串
|
||||||
|
*/
|
||||||
|
get validationMessage() {
|
||||||
|
return this.inputRef.value.validationMessage;
|
||||||
|
}
|
||||||
|
get rippleElement() {
|
||||||
|
return this.rippleRef.value;
|
||||||
|
}
|
||||||
|
get rippleDisabled() {
|
||||||
|
return this.disabled;
|
||||||
|
}
|
||||||
|
get focusElement() {
|
||||||
|
return this.inputRef.value;
|
||||||
|
}
|
||||||
|
get focusDisabled() {
|
||||||
|
return this.disabled;
|
||||||
|
}
|
||||||
|
async onDisabledChange() {
|
||||||
|
await this.updateComplete;
|
||||||
|
this.invalid = !this.inputRef.value.checkValidity();
|
||||||
|
}
|
||||||
|
async onCheckedChange() {
|
||||||
|
await this.updateComplete;
|
||||||
|
// reset 引起的值变更,不执行验证;直接修改值引起的变更,需要进行验证
|
||||||
|
const form = this.formController.getForm();
|
||||||
|
if (form && formResets.get(form)?.has(this)) {
|
||||||
|
this.invalid = false;
|
||||||
|
formResets.get(form).delete(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.invalid = !this.inputRef.value.checkValidity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查表单字段是否通过验证。如果未通过,返回 `false` 并触发 `invalid` 事件;如果通过,返回 `true`
|
||||||
|
*/
|
||||||
|
checkValidity() {
|
||||||
|
const valid = this.inputRef.value.checkValidity();
|
||||||
|
if (!valid) {
|
||||||
|
this.emit('invalid', {
|
||||||
|
bubbles: false,
|
||||||
|
cancelable: true,
|
||||||
|
composed: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查表单字段是否通过验证。如果未通过,返回 `false` 并触发 `invalid` 事件;如果通过,返回 `true`。
|
||||||
|
*
|
||||||
|
* 如果验证未通过,还会在组件上显示验证失败的提示。
|
||||||
|
*/
|
||||||
|
reportValidity() {
|
||||||
|
this.invalid = !this.inputRef.value.reportValidity();
|
||||||
|
if (this.invalid) {
|
||||||
|
const eventProceeded = this.emit('invalid', {
|
||||||
|
bubbles: false,
|
||||||
|
cancelable: true,
|
||||||
|
composed: false,
|
||||||
|
});
|
||||||
|
// 调用了 preventDefault() 时,隐藏默认的表单错误提示
|
||||||
|
if (!eventProceeded) {
|
||||||
|
this.blur();
|
||||||
|
this.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !this.invalid;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 设置自定义的错误提示文本。只要这个文本不为空,就表示字段未通过验证
|
||||||
|
*
|
||||||
|
* @param message 自定义的错误提示文本
|
||||||
|
*/
|
||||||
|
setCustomValidity(message) {
|
||||||
|
this.inputRef.value.setCustomValidity(message);
|
||||||
|
this.invalid = !this.inputRef.value.checkValidity();
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return html `<label class="${classMap({ invalid: this.invalid })}"><input ${ref(this.inputRef)} type="checkbox" name="${ifDefined(this.name)}" value="${ifDefined(this.value)}" .indeterminate="${live(this.indeterminate)}" .disabled="${this.disabled}" .checked="${live(this.checked)}" .required="${this.required}" @change="${this.onChange}"> <i part="control"><mdui-ripple ${ref(this.rippleRef)} .noRipple="${this.noRipple}"></mdui-ripple><slot name="unchecked-icon" part="unchecked-icon" class="icon unchecked-icon">${this.uncheckedIcon
|
||||||
|
? html `<mdui-icon name="${this.uncheckedIcon}" class="i"></mdui-icon>`
|
||||||
|
: html `<mdui-icon-check-box-outline-blank class="i"></mdui-icon-check-box-outline-blank>`}</slot><slot name="checked-icon" part="checked-icon" class="icon checked-icon">${this.checkedIcon
|
||||||
|
? html `<mdui-icon name="${this.checkedIcon}" class="i"></mdui-icon>`
|
||||||
|
: html `<mdui-icon-check-box class="i"></mdui-icon-check-box>`}</slot><slot name="indeterminate-icon" part="indeterminate-icon" class="icon indeterminate-icon">${this.indeterminateIcon
|
||||||
|
? html `<mdui-icon name="${this.indeterminateIcon}" class="i"></mdui-icon>`
|
||||||
|
: html `<mdui-icon-indeterminate-check-box class="i"></mdui-icon-indeterminate-check-box>`}</slot></i><slot part="label" class="label"></slot></label>`;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* input[type="checkbox"] 的 change 事件无法冒泡越过 shadow dom
|
||||||
|
*/
|
||||||
|
onChange() {
|
||||||
|
this.checked = this.inputRef.value.checked;
|
||||||
|
this.indeterminate = false;
|
||||||
|
this.emit('change');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Checkbox.styles = [componentStyle, style];
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Checkbox.prototype, "disabled", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Checkbox.prototype, "checked", void 0);
|
||||||
|
__decorate([
|
||||||
|
defaultValue('checked')
|
||||||
|
], Checkbox.prototype, "defaultChecked", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Checkbox.prototype, "indeterminate", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Checkbox.prototype, "required", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Checkbox.prototype, "form", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Checkbox.prototype, "name", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Checkbox.prototype, "value", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'unchecked-icon' })
|
||||||
|
], Checkbox.prototype, "uncheckedIcon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'checked-icon' })
|
||||||
|
], Checkbox.prototype, "checkedIcon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'indeterminate-icon' })
|
||||||
|
], Checkbox.prototype, "indeterminateIcon", void 0);
|
||||||
|
__decorate([
|
||||||
|
state()
|
||||||
|
], Checkbox.prototype, "invalid", void 0);
|
||||||
|
__decorate([
|
||||||
|
watch('disabled', true),
|
||||||
|
watch('indeterminate', true),
|
||||||
|
watch('required', true)
|
||||||
|
], Checkbox.prototype, "onDisabledChange", null);
|
||||||
|
__decorate([
|
||||||
|
watch('checked', true)
|
||||||
|
], Checkbox.prototype, "onCheckedChange", null);
|
||||||
|
Checkbox = __decorate([
|
||||||
|
customElement('mdui-checkbox')
|
||||||
|
], Checkbox);
|
||||||
|
export { Checkbox };
|
||||||
1
client/mdui_patched/components/checkbox/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/checkbox/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
2
client/mdui_patched/components/checkbox/style.js
Normal file
2
client/mdui_patched/components/checkbox/style.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{position:relative;display:inline-flex;cursor:pointer;-webkit-tap-highlight-color:transparent;border-radius:.125rem;font-size:var(--mdui-typescale-label-large-size);font-weight:var(--mdui-typescale-label-large-weight);letter-spacing:var(--mdui-typescale-label-large-tracking);line-height:var(--mdui-typescale-label-large-line-height)}label{display:inline-flex;align-items:center;width:100%;cursor:inherit;-webkit-user-select:none;user-select:none;touch-action:manipulation;zoom:1;-webkit-user-drag:none}input{position:absolute;padding:0;opacity:0;pointer-events:none;width:1.125rem;height:1.125rem;margin:0 0 0 .6875rem}.icon{display:flex;position:absolute;opacity:1;transform:scale(1);color:rgb(var(--mdui-color-on-surface));font-size:1.5rem;transition:color var(--mdui-motion-duration-short4) var(--mdui-motion-easing-standard)}.checked-icon,.indeterminate-icon{opacity:0;transform:scale(.5);transition-property:color,opacity,transform;transition-duration:var(--mdui-motion-duration-short4);transition-timing-function:var(--mdui-motion-easing-standard)}.icon .i,::slotted([slot=checked-icon]),::slotted([slot=indeterminate-icon]),::slotted([slot=unchecked-icon]){color:inherit;font-size:inherit}i{position:relative;display:flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;border-radius:50%;width:2.5rem;height:2.5rem;--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-surface)}.label{display:flex;width:100%;padding-top:.625rem;padding-bottom:.625rem;color:rgb(var(--mdui-color-on-surface));transition:color var(--mdui-motion-duration-short4) var(--mdui-motion-easing-standard)}:host([checked]:not([checked=false i])) i{--mdui-comp-ripple-state-layer-color:var(--mdui-color-primary)}:host([checked]:not([checked=false i])) .icon{color:rgb(var(--mdui-color-primary))}:host([checked]:not([checked=false i])) .indeterminate-icon{opacity:0;transform:scale(.5)}:host([checked]:not([checked=false i])) .checked-icon{opacity:1;transform:scale(1)}:host([indeterminate]:not([indeterminate=false i])) i{--mdui-comp-ripple-state-layer-color:var(--mdui-color-primary)}:host([indeterminate]:not([indeterminate=false i])) .icon{color:rgb(var(--mdui-color-primary))}:host([indeterminate]:not([indeterminate=false i])) .checked-icon{opacity:0;transform:scale(.5)}:host([indeterminate]:not([indeterminate=false i])) .indeterminate-icon{opacity:1;transform:scale(1)}.invalid i{--mdui-comp-ripple-state-layer-color:var(--mdui-color-error)}.invalid .icon{color:rgb(var(--mdui-color-error))}.invalid .label{color:rgb(var(--mdui-color-error))}:host([disabled]:not([disabled=false i])){cursor:default;pointer-events:none}:host([disabled]:not([disabled=false i])) .icon{color:rgba(var(--mdui-color-on-surface),38%)}:host([disabled]:not([disabled=false i])) .label{color:rgba(var(--mdui-color-on-surface),38%)}:host([disabled][checked]:not([disabled=false i],[checked=false i])) .unchecked-icon,:host([disabled][indeterminate]:not([disabled=false i],[indeterminate=false i])) .unchecked-icon{opacity:0}`;
|
||||||
1
client/mdui_patched/components/chip.d.ts
vendored
Normal file
1
client/mdui_patched/components/chip.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './chip/index.js';
|
||||||
1
client/mdui_patched/components/chip.js
Normal file
1
client/mdui_patched/components/chip.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './chip/index.js';
|
||||||
109
client/mdui_patched/components/chip/index.d.ts
vendored
Normal file
109
client/mdui_patched/components/chip/index.d.ts
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import '@mdui/shared/icons/check.js';
|
||||||
|
import '@mdui/shared/icons/clear.js';
|
||||||
|
import { ButtonBase } from '../button/button-base.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import type { Ripple } from '../ripple/index.js';
|
||||||
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 纸片组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-chip>Chip</mdui-chip>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
* @event invalid - 表单字段验证未通过时触发
|
||||||
|
* @event change - 选中状态变更时触发
|
||||||
|
* @event delete - 点击删除图标时触发
|
||||||
|
*
|
||||||
|
* @slot - 纸片文本
|
||||||
|
* @slot icon - 左侧元素
|
||||||
|
* @slot end-icon - 右侧元素
|
||||||
|
* @slot selected-icon - 选中状态下的左侧元素
|
||||||
|
* @slot delete-icon - 可删除时的右侧删除元素
|
||||||
|
*
|
||||||
|
* @csspart button - 内部的 `<button>` 或 `<a>` 元素
|
||||||
|
* @csspart label - 纸片文本
|
||||||
|
* @csspart icon - 左侧图标
|
||||||
|
* @csspart end-icon - 右侧图标
|
||||||
|
* @csspart selected-icon - 选中状态下的左侧图标
|
||||||
|
* @csspart delete-icon - 可删除时的右侧删除图标
|
||||||
|
* @csspart loading - 加载中状态的 `<mdui-circular-progress>` 元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
export declare class Chip extends ButtonBase<ChipEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 纸片的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `assist`:用于显示与当前上下文相关的辅助操作,如在点餐页面提供分享、收藏等功能
|
||||||
|
* * `filter`:用于对内容进行筛选,如在搜索结果页过滤搜索结果
|
||||||
|
* * `input`:用于表示用户输入的信息片段,如在 Gmail 的“收件人”字段中的联系人
|
||||||
|
* * `suggestion`:用于提供动态生成的推荐信息,以简化用户操作,如在聊天应用中预测用户可能想发送的信息
|
||||||
|
*/
|
||||||
|
variant: /*用于显示与当前上下文相关的辅助操作,如在点餐页面提供分享、收藏等功能*/ 'assist' | /*用于对内容进行筛选,如在搜索结果页过滤搜索结果*/ 'filter' | /*用于表示用户输入的信息片段,如在 Gmail 的“收件人”字段中的联系人*/ 'input' | /*用于提供动态生成的推荐信息,以简化用户操作,如在聊天应用中预测用户可能想发送的信息*/ 'suggestion';
|
||||||
|
/**
|
||||||
|
* 是否显示阴影
|
||||||
|
*/
|
||||||
|
elevated: boolean;
|
||||||
|
/**
|
||||||
|
* 是否可选中
|
||||||
|
*/
|
||||||
|
selectable: boolean;
|
||||||
|
/**
|
||||||
|
* 是否已选中
|
||||||
|
*/
|
||||||
|
selected: boolean;
|
||||||
|
/**
|
||||||
|
* 是否可删除。为 `true` 时,纸片右侧会显示删除图标
|
||||||
|
*/
|
||||||
|
deletable: boolean;
|
||||||
|
/**
|
||||||
|
* 左侧的 Material Icons 图标名。也可以通过 `slot="icon"` 设置
|
||||||
|
*/
|
||||||
|
icon?: string;
|
||||||
|
/**
|
||||||
|
* 选中状态下左侧的 Material Icons 图标名。也可以通过 `slot="selected-icon"` 设置
|
||||||
|
*/
|
||||||
|
selectedIcon?: string;
|
||||||
|
/**
|
||||||
|
* 右侧的 Material Icons 图标名。也可以通过 `slot="end-icon"` 设置
|
||||||
|
*/
|
||||||
|
endIcon?: string;
|
||||||
|
/**
|
||||||
|
* 可删除时,右侧删除图标的 Material Icons 图标名。也可以通过 `slot="delete-icon"` 设置
|
||||||
|
*/
|
||||||
|
deleteIcon?: string;
|
||||||
|
private readonly rippleRef;
|
||||||
|
private readonly hasSlotController;
|
||||||
|
constructor();
|
||||||
|
protected get rippleElement(): Ripple;
|
||||||
|
private onSelectedChange;
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues): void;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
private onClick;
|
||||||
|
private onKeyDown;
|
||||||
|
/**
|
||||||
|
* 点击删除按钮
|
||||||
|
*/
|
||||||
|
private onDelete;
|
||||||
|
private renderIcon;
|
||||||
|
private renderLabel;
|
||||||
|
private renderEndIcon;
|
||||||
|
private renderDeleteIcon;
|
||||||
|
private renderInner;
|
||||||
|
}
|
||||||
|
export interface ChipEventMap {
|
||||||
|
focus: FocusEvent;
|
||||||
|
blur: FocusEvent;
|
||||||
|
invalid: CustomEvent<void>;
|
||||||
|
change: CustomEvent<void>;
|
||||||
|
delete: CustomEvent<void>;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-chip': Chip;
|
||||||
|
}
|
||||||
|
}
|
||||||
243
client/mdui_patched/components/chip/index.js
Normal file
243
client/mdui_patched/components/chip/index.js
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
|
import { createRef, ref } from 'lit/directives/ref.js';
|
||||||
|
import cc from 'classcat';
|
||||||
|
import { HasSlotController } from '@mdui/shared/controllers/has-slot.js';
|
||||||
|
import { watch } from '@mdui/shared/decorators/watch.js';
|
||||||
|
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||||
|
import { nothingTemplate } from '@mdui/shared/helpers/template.js';
|
||||||
|
import '@mdui/shared/icons/check.js';
|
||||||
|
import '@mdui/shared/icons/clear.js';
|
||||||
|
import { ButtonBase } from '../button/button-base.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 纸片组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-chip>Chip</mdui-chip>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
* @event invalid - 表单字段验证未通过时触发
|
||||||
|
* @event change - 选中状态变更时触发
|
||||||
|
* @event delete - 点击删除图标时触发
|
||||||
|
*
|
||||||
|
* @slot - 纸片文本
|
||||||
|
* @slot icon - 左侧元素
|
||||||
|
* @slot end-icon - 右侧元素
|
||||||
|
* @slot selected-icon - 选中状态下的左侧元素
|
||||||
|
* @slot delete-icon - 可删除时的右侧删除元素
|
||||||
|
*
|
||||||
|
* @csspart button - 内部的 `<button>` 或 `<a>` 元素
|
||||||
|
* @csspart label - 纸片文本
|
||||||
|
* @csspart icon - 左侧图标
|
||||||
|
* @csspart end-icon - 右侧图标
|
||||||
|
* @csspart selected-icon - 选中状态下的左侧图标
|
||||||
|
* @csspart delete-icon - 可删除时的右侧删除图标
|
||||||
|
* @csspart loading - 加载中状态的 `<mdui-circular-progress>` 元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
let Chip = class Chip extends ButtonBase {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
/**
|
||||||
|
* 纸片的形状。可选值包括:
|
||||||
|
*
|
||||||
|
* * `assist`:用于显示与当前上下文相关的辅助操作,如在点餐页面提供分享、收藏等功能
|
||||||
|
* * `filter`:用于对内容进行筛选,如在搜索结果页过滤搜索结果
|
||||||
|
* * `input`:用于表示用户输入的信息片段,如在 Gmail 的“收件人”字段中的联系人
|
||||||
|
* * `suggestion`:用于提供动态生成的推荐信息,以简化用户操作,如在聊天应用中预测用户可能想发送的信息
|
||||||
|
*/
|
||||||
|
this.variant = 'assist';
|
||||||
|
/**
|
||||||
|
* 是否显示阴影
|
||||||
|
*/
|
||||||
|
this.elevated = false;
|
||||||
|
/**
|
||||||
|
* 是否可选中
|
||||||
|
*/
|
||||||
|
this.selectable = false;
|
||||||
|
/**
|
||||||
|
* 是否已选中
|
||||||
|
*/
|
||||||
|
this.selected = false;
|
||||||
|
/**
|
||||||
|
* 是否可删除。为 `true` 时,纸片右侧会显示删除图标
|
||||||
|
*/
|
||||||
|
this.deletable = false;
|
||||||
|
this.rippleRef = createRef();
|
||||||
|
this.hasSlotController = new HasSlotController(this, 'icon', 'selected-icon', 'end-icon');
|
||||||
|
this.onClick = this.onClick.bind(this);
|
||||||
|
this.onKeyDown = this.onKeyDown.bind(this);
|
||||||
|
}
|
||||||
|
get rippleElement() {
|
||||||
|
return this.rippleRef.value;
|
||||||
|
}
|
||||||
|
onSelectedChange() {
|
||||||
|
this.emit('change');
|
||||||
|
}
|
||||||
|
firstUpdated(changedProperties) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
this.addEventListener('click', this.onClick);
|
||||||
|
this.addEventListener('keydown', this.onKeyDown);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const hasIcon = this.icon || this.hasSlotController.test('icon');
|
||||||
|
const hasEndIcon = this.endIcon || this.hasSlotController.test('end-icon');
|
||||||
|
const hasSelectedIcon = this.selectedIcon ||
|
||||||
|
['assist', 'filter'].includes(this.variant) ||
|
||||||
|
hasIcon ||
|
||||||
|
this.hasSlotController.test('selected-icon');
|
||||||
|
const className = cc({
|
||||||
|
button: true,
|
||||||
|
'has-icon': this.loading ||
|
||||||
|
(!this.selected && hasIcon) ||
|
||||||
|
(this.selected && hasSelectedIcon),
|
||||||
|
'has-end-icon': hasEndIcon,
|
||||||
|
});
|
||||||
|
return html `<mdui-ripple ${ref(this.rippleRef)} .noRipple="${this.noRipple}"></mdui-ripple>${this.isButton()
|
||||||
|
? this.renderButton({
|
||||||
|
className,
|
||||||
|
part: 'button',
|
||||||
|
content: this.renderInner(),
|
||||||
|
})
|
||||||
|
: this.disabled || this.loading
|
||||||
|
? html `<span part="button" class="${className} _a">${this.renderInner()}</span>`
|
||||||
|
: this.renderAnchor({
|
||||||
|
className,
|
||||||
|
part: 'button',
|
||||||
|
content: this.renderInner(),
|
||||||
|
})}`;
|
||||||
|
}
|
||||||
|
onClick() {
|
||||||
|
if (this.disabled || this.loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 点击时,切换选中状态
|
||||||
|
if (this.selectable) {
|
||||||
|
this.selected = !this.selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onKeyDown(event) {
|
||||||
|
if (this.disabled || this.loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 按下空格键时,切换选中状态
|
||||||
|
if (this.selectable && event.key === ' ') {
|
||||||
|
event.preventDefault();
|
||||||
|
this.selected = !this.selected;
|
||||||
|
}
|
||||||
|
// 按下 Delete 或 BackSpace 键时,触发 delete 事件
|
||||||
|
if (this.deletable && ['Delete', 'Backspace'].includes(event.key)) {
|
||||||
|
this.emit('delete');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 点击删除按钮
|
||||||
|
*/
|
||||||
|
onDelete(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.emit('delete');
|
||||||
|
}
|
||||||
|
renderIcon() {
|
||||||
|
if (this.loading) {
|
||||||
|
return this.renderLoading();
|
||||||
|
}
|
||||||
|
const icon = () => {
|
||||||
|
return this.icon
|
||||||
|
? html `<mdui-icon name="${this.icon}" class="i"></mdui-icon>`
|
||||||
|
: nothingTemplate;
|
||||||
|
};
|
||||||
|
const selectedIcon = () => {
|
||||||
|
if (this.selectedIcon) {
|
||||||
|
return html `<mdui-icon name="${this.selectedIcon}" class="i"></mdui-icon>`;
|
||||||
|
}
|
||||||
|
if (this.variant === 'assist' || this.variant === 'filter') {
|
||||||
|
return html `<mdui-icon-check class="i"></mdui-icon-check>`;
|
||||||
|
}
|
||||||
|
return icon();
|
||||||
|
};
|
||||||
|
return !this.selected
|
||||||
|
? html `<slot name="icon" part="icon" class="icon">${icon()}</slot>`
|
||||||
|
: html `<slot name="selected-icon" part="selected-icon" class="selected-icon">${selectedIcon()}</slot>`;
|
||||||
|
}
|
||||||
|
renderLabel() {
|
||||||
|
return html `<slot part="label" class="label"></slot>`;
|
||||||
|
}
|
||||||
|
renderEndIcon() {
|
||||||
|
return html `<slot name="end-icon" part="end-icon" class="end-icon">${this.endIcon
|
||||||
|
? html `<mdui-icon name="${this.endIcon}" class="i"></mdui-icon>`
|
||||||
|
: nothingTemplate}</slot>`;
|
||||||
|
}
|
||||||
|
renderDeleteIcon() {
|
||||||
|
if (!this.deletable) {
|
||||||
|
return nothingTemplate;
|
||||||
|
}
|
||||||
|
return html `<slot name="delete-icon" part="delete-icon" class="delete-icon" @click="${this.onDelete}">${this.deleteIcon
|
||||||
|
? html `<mdui-icon name="${this.deleteIcon}" class="i"></mdui-icon>`
|
||||||
|
: html `<mdui-icon-clear class="i"></mdui-icon-clear>`}</slot>`;
|
||||||
|
}
|
||||||
|
renderInner() {
|
||||||
|
return [
|
||||||
|
this.renderIcon(),
|
||||||
|
this.renderLabel(),
|
||||||
|
this.renderEndIcon(),
|
||||||
|
this.renderDeleteIcon(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Chip.styles = [ButtonBase.styles, style];
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Chip.prototype, "variant", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Chip.prototype, "elevated", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Chip.prototype, "selectable", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Chip.prototype, "selected", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Chip.prototype, "deletable", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Chip.prototype, "icon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'selected-icon' })
|
||||||
|
], Chip.prototype, "selectedIcon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'end-icon' })
|
||||||
|
], Chip.prototype, "endIcon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true, attribute: 'delete-icon' })
|
||||||
|
], Chip.prototype, "deleteIcon", void 0);
|
||||||
|
__decorate([
|
||||||
|
watch('selected', true)
|
||||||
|
], Chip.prototype, "onSelectedChange", null);
|
||||||
|
Chip = __decorate([
|
||||||
|
customElement('mdui-chip')
|
||||||
|
], Chip);
|
||||||
|
export { Chip };
|
||||||
1
client/mdui_patched/components/chip/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/chip/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
4
client/mdui_patched/components/chip/style.js
Normal file
4
client/mdui_patched/components/chip/style.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{--shape-corner:var(--mdui-shape-corner-small);position:relative;display:inline-block;flex-shrink:0;overflow:hidden;border-radius:var(--shape-corner);cursor:pointer;-webkit-tap-highlight-color:transparent;transition:box-shadow var(--mdui-motion-duration-short4) var(--mdui-motion-easing-linear);height:2rem;background-color:rgb(var(--mdui-color-surface));border:.0625rem solid rgb(var(--mdui-color-outline));color:rgb(var(--mdui-color-on-surface-variant));font-size:var(--mdui-typescale-label-large-size);font-weight:var(--mdui-typescale-label-large-weight);letter-spacing:var(--mdui-typescale-label-large-tracking);line-height:var(--mdui-typescale-label-large-line-height);--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-surface-variant)}.button{padding-right:.4375rem;padding-left:.4375rem}:host([variant=input]) .button{padding-right:.1875rem;padding-left:.1875rem}:host([selected]:not([selected=false i])) .button{padding-right:.5rem;padding-left:.5rem}:host([selected][variant=input]:not([selected=false i])) .button{padding-right:.25rem;padding-left:.25rem}:host([elevated]:not([elevated=false i])) .button{padding-right:.5rem;padding-left:.5rem}:host([variant=assist]){color:rgb(var(--mdui-color-on-surface));--mdui-comp-ripple-state-layer-color:var(--mdui-color-on-surface)}:host([elevated]:not([elevated=false i])){border-width:0;background-color:rgb(var(--mdui-color-surface-container-low));box-shadow:var(--mdui-elevation-level1)}:host([selected]:not([selected=false i])){color:rgb(var(--mdui-color-on-secondary-container));background-color:rgb(var(--mdui-color-secondary-container));border-width:0;--mdui-comp-ripple-state-layer-color:var(
|
||||||
|
--mdui-color-on-secondary-container
|
||||||
|
)}:host([disabled]:not([disabled=false i])),:host([loading]:not([loading=false i])){cursor:default;pointer-events:none}:host([disabled]:not([disabled=false i])){border-color:rgba(var(--mdui-color-on-surface),12%);color:rgba(var(--mdui-color-on-surface),38%);box-shadow:var(--mdui-elevation-level0)}:host([disabled][elevated]:not([disabled=false i],[elevated=false i])),:host([disabled][selected]:not([disabled=false i],[selected=false i])){background-color:rgba(var(--mdui-color-on-surface),12%)}:host([selected][hover]:not([selected=false i])){box-shadow:var(--mdui-elevation-level1)}:host([elevated][hover]:not([elevated=false i])){color:rgb(var(--mdui-color-on-secondary-container));box-shadow:var(--mdui-elevation-level2)}:host([variant=filter][hover]),:host([variant=input][hover]),:host([variant=suggestion][hover]){color:rgb(var(--mdui-color-on-surface-variant))}:host([variant=filter][focus-visible]),:host([variant=input][focus-visible]),:host([variant=suggestion][focus-visible]){border-color:rgb(var(--mdui-color-on-surface-variant))}:host([dragged]),:host([dragged][hover]){box-shadow:var(--mdui-elevation-level4)}.button{overflow:visible}.label{display:inline-flex;padding-right:.5rem;padding-left:.5rem}.end-icon,.icon,.selected-icon{display:inline-flex;font-size:1.28571429em;color:rgb(var(--mdui-color-on-surface-variant))}:host([variant=assist]) .end-icon,:host([variant=assist]) .icon,:host([variant=assist]) .selected-icon{color:rgb(var(--mdui-color-primary))}:host([selected]:not([selected=false i])) .end-icon,:host([selected]:not([selected=false i])) .icon,:host([selected]:not([selected=false i])) .selected-icon{color:rgb(var(--mdui-color-on-secondary-container))}:host([disabled]:not([disabled=false i])) .end-icon,:host([disabled]:not([disabled=false i])) .icon,:host([disabled]:not([disabled=false i])) .selected-icon{opacity:.38;color:rgb(var(--mdui-color-on-surface))}.end-icon .i,.icon .i,.selected-icon .i,::slotted([slot=end-icon]),::slotted([slot=icon]),::slotted([slot=selected-icon]){font-size:inherit}:host([variant=input]) .has-icon .icon,:host([variant=input]) .has-icon .selected-icon,:host([variant=input]) .has-icon mdui-circular-progress{margin-left:.25rem}:host([variant=input]) .has-end-icon .end-icon{margin-right:.25rem}mdui-circular-progress{display:inline-flex;width:1.125rem;height:1.125rem}:host([disabled]:not([disabled=false i])) mdui-circular-progress{stroke:rgba(var(--mdui-color-on-surface),38%)}::slotted(mdui-avatar[slot=end-icon]),::slotted(mdui-avatar[slot=icon]),::slotted(mdui-avatar[slot=selected-icon]){width:1.5rem;height:1.5rem}:host([disabled]:not([disabled=false i])) ::slotted(mdui-avatar[slot=end-icon]),:host([disabled]:not([disabled=false i])) ::slotted(mdui-avatar[slot=icon]),:host([disabled]:not([disabled=false i])) ::slotted(mdui-avatar[slot=selected-icon]){opacity:.38}::slotted(mdui-avatar[slot=icon]),::slotted(mdui-avatar[slot=selected-icon]){margin-left:-.25rem;margin-right:-.125rem}::slotted(mdui-avatar[slot=end-icon]){margin-right:-.25rem;margin-left:-.125rem}.delete-icon{display:inline-flex;font-size:1.28571429em;transition:background-color var(--mdui-motion-duration-short4) var(--mdui-motion-easing-linear);border-radius:var(--mdui-shape-corner-full);margin-right:-.25rem;margin-left:-.25rem;padding:.25rem;color:rgb(var(--mdui-color-on-surface-variant))}.delete-icon:hover{background-color:rgba(var(--mdui-color-on-surface-variant),12%)}.has-end-icon .delete-icon{margin-left:.25rem}:host([variant=assiat]) .delete-icon{color:rgb(var(--mdui-color-primary))}:host([variant=input]) .delete-icon{margin-right:.0625rem}:host([disabled]:not([disabled=false i])) .delete-icon{color:rgba(var(--mdui-color-on-surface),38%)}.delete-icon .i,::slotted([slot=delete-icon]){font-size:inherit}::slotted(mdui-avatar[slot=delete-icon]){width:1.125rem;height:1.125rem}`;
|
||||||
1
client/mdui_patched/components/circular-progress.d.ts
vendored
Normal file
1
client/mdui_patched/components/circular-progress.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './circular-progress/index.js';
|
||||||
1
client/mdui_patched/components/circular-progress.js
Normal file
1
client/mdui_patched/components/circular-progress.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './circular-progress/index.js';
|
||||||
30
client/mdui_patched/components/circular-progress/index.d.ts
vendored
Normal file
30
client/mdui_patched/components/circular-progress/index.d.ts
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import type { CSSResultGroup, TemplateResult } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 圆形进度指示器组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-circular-progress></mdui-circular-progress>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export declare class CircularProgress extends MduiElement<CircularProgressEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 进度指示器的最大值。默认为 `1`
|
||||||
|
*/
|
||||||
|
max: number;
|
||||||
|
/**
|
||||||
|
* 进度指示器的当前值。如果未指定该值,则显示为不确定状态
|
||||||
|
*/
|
||||||
|
value?: number;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
private renderDeterminate;
|
||||||
|
private renderInDeterminate;
|
||||||
|
}
|
||||||
|
export interface CircularProgressEventMap {
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-circular-progress': CircularProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
62
client/mdui_patched/components/circular-progress/index.js
Normal file
62
client/mdui_patched/components/circular-progress/index.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
|
import { isUndefined } 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 { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 圆形进度指示器组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-circular-progress></mdui-circular-progress>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
let CircularProgress = class CircularProgress extends MduiElement {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 进度指示器的最大值。默认为 `1`
|
||||||
|
*/
|
||||||
|
this.max = 1;
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const isDeterminate = !isUndefined(this.value);
|
||||||
|
return html `<div class="progress ${classMap({
|
||||||
|
determinate: isDeterminate,
|
||||||
|
indeterminate: !isDeterminate,
|
||||||
|
})}">${isDeterminate ? this.renderDeterminate() : this.renderInDeterminate()}</div>`;
|
||||||
|
}
|
||||||
|
renderDeterminate() {
|
||||||
|
const value = this.value;
|
||||||
|
const strokeWidth = 4; // 圆环宽度
|
||||||
|
const circleRadius = 18; // 圆环宽度中心点的半径
|
||||||
|
const π = 3.1415926;
|
||||||
|
const center = circleRadius + strokeWidth / 2;
|
||||||
|
const circumference = 2 * π * circleRadius;
|
||||||
|
const determinateStrokeDashOffset = (1 - value / Math.max(this.max ?? value, value)) * circumference;
|
||||||
|
return html `<svg viewBox="0 0 ${center * 2} ${center * 2}"><circle class="track" cx="${center}" cy="${center}" r="${circleRadius}" stroke-width="${strokeWidth}"></circle><circle class="circle" cx="${center}" cy="${center}" r="${circleRadius}" stroke-dasharray="${2 * π * circleRadius}" stroke-dashoffset="${determinateStrokeDashOffset}" stroke-width="${strokeWidth}"></circle></svg>`;
|
||||||
|
}
|
||||||
|
renderInDeterminate() {
|
||||||
|
const strokeWidth = 4; // 圆环宽度
|
||||||
|
const circleRadius = 18; // 圆环宽度中心点的半径
|
||||||
|
const π = 3.1415926;
|
||||||
|
const center = circleRadius + strokeWidth / 2;
|
||||||
|
const circumference = 2 * π * circleRadius;
|
||||||
|
const halfCircumference = 0.5 * circumference;
|
||||||
|
const circle = (thisStrokeWidth) => html `<svg class="circle" viewBox="0 0 ${center * 2} ${center * 2}"><circle cx="${center}" cy="${center}" r="${circleRadius}" stroke-dasharray="${circumference}" stroke-dashoffset="${halfCircumference}" stroke-width="${thisStrokeWidth}"></circle></svg>`;
|
||||||
|
return html `<div class="layer"><div class="clipper left">${circle(strokeWidth)}</div><div class="gap-patch">${circle(strokeWidth * 0.8)}</div><div class="clipper right">${circle(strokeWidth)}</div></div>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CircularProgress.styles = [componentStyle, style];
|
||||||
|
__decorate([
|
||||||
|
property({ type: Number, reflect: true })
|
||||||
|
], CircularProgress.prototype, "max", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ type: Number })
|
||||||
|
], CircularProgress.prototype, "value", void 0);
|
||||||
|
CircularProgress = __decorate([
|
||||||
|
customElement('mdui-circular-progress')
|
||||||
|
], CircularProgress);
|
||||||
|
export { CircularProgress };
|
||||||
1
client/mdui_patched/components/circular-progress/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/circular-progress/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{position:relative;display:inline-block;flex-shrink:0;width:2.5rem;height:2.5rem;stroke:rgb(var(--mdui-color-primary))}.progress{position:relative;display:inline-block;width:100%;height:100%;text-align:left;transition:opacity var(--mdui-motion-duration-medium1) var(--mdui-motion-easing-linear)}.determinate svg{transform:rotate(-90deg);fill:transparent}.determinate .track{stroke:transparent}.determinate .circle{stroke:inherit;transition:stroke-dashoffset var(--mdui-motion-duration-long2) var(--mdui-motion-easing-standard)}.indeterminate{font-size:0;letter-spacing:0;white-space:nowrap;animation:mdui-comp-circular-progress-rotate 1568ms var(--mdui-motion-easing-linear) infinite}.indeterminate .circle,.indeterminate .layer{position:absolute;width:100%;height:100%}.indeterminate .layer{animation:mdui-comp-circular-progress-layer-rotate 5332ms var(--mdui-motion-easing-standard) infinite both}.indeterminate .circle{fill:transparent;stroke:inherit}.indeterminate .gap-patch{position:absolute;top:0;left:47.5%;width:5%;height:100%;overflow:hidden}.indeterminate .gap-patch .circle{left:-900%;width:2000%;transform:rotate(180deg)}.indeterminate .clipper{position:relative;display:inline-block;width:50%;height:100%;overflow:hidden}.indeterminate .clipper .circle{width:200%}.indeterminate .clipper.left .circle{animation:mdui-comp-circular-progress-left-spin 1333ms var(--mdui-motion-easing-standard) infinite both}.indeterminate .clipper.right .circle{left:-100%;animation:mdui-comp-circular-progress-right-spin 1333ms var(--mdui-motion-easing-standard) infinite both}@keyframes mdui-comp-circular-progress-rotate{to{transform:rotate(360deg)}}@keyframes mdui-comp-circular-progress-layer-rotate{12.5%{transform:rotate(135deg)}25%{transform:rotate(270deg)}37.5%{transform:rotate(405deg)}50%{transform:rotate(540deg)}62.5%{transform:rotate(675deg)}75%{transform:rotate(810deg)}87.5%{transform:rotate(945deg)}100%{transform:rotate(1080deg)}}@keyframes mdui-comp-circular-progress-left-spin{0%{transform:rotate(265deg)}50%{transform:rotate(130deg)}100%{transform:rotate(265deg)}}@keyframes mdui-comp-circular-progress-right-spin{0%{transform:rotate(-265deg)}50%{transform:rotate(-130deg)}100%{transform:rotate(-265deg)}}`;
|
||||||
1
client/mdui_patched/components/collapse-item.d.ts
vendored
Normal file
1
client/mdui_patched/components/collapse-item.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './collapse/collapse-item.js';
|
||||||
1
client/mdui_patched/components/collapse-item.js
Normal file
1
client/mdui_patched/components/collapse-item.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './collapse/collapse-item.js';
|
||||||
1
client/mdui_patched/components/collapse.d.ts
vendored
Normal file
1
client/mdui_patched/components/collapse.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './collapse/collapse.js';
|
||||||
1
client/mdui_patched/components/collapse.js
Normal file
1
client/mdui_patched/components/collapse.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './collapse/collapse.js';
|
||||||
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 };
|
||||||
1
client/mdui_patched/components/dialog.d.ts
vendored
Normal file
1
client/mdui_patched/components/dialog.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './dialog/index.js';
|
||||||
1
client/mdui_patched/components/dialog.js
Normal file
1
client/mdui_patched/components/dialog.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './dialog/index.js';
|
||||||
107
client/mdui_patched/components/dialog/index.d.ts
vendored
Normal file
107
client/mdui_patched/components/dialog/index.d.ts
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 对话框组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-dialog>content</mdui-dialog>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event open - 对话框开始打开时触发。可以通过调用 `event.preventDefault()` 阻止对话框打开
|
||||||
|
* @event opened - 对话框打开动画完成后触发
|
||||||
|
* @event close - 对话框开始关闭时触发。可以通过调用 `event.preventDefault()` 阻止对话框关闭
|
||||||
|
* @event closed - 对话框关闭动画完成后触发
|
||||||
|
* @event overlay-click - 点击遮罩层时触发
|
||||||
|
*
|
||||||
|
* @slot header - 顶部元素,默认包含 `icon` slot 和 `headline` slot
|
||||||
|
* @slot icon - 顶部图标
|
||||||
|
* @slot headline - 顶部标题
|
||||||
|
* @slot description - 标题下方的文本
|
||||||
|
* @slot - 对话框主体内容
|
||||||
|
* @slot action - 底部操作栏中的元素
|
||||||
|
*
|
||||||
|
* @csspart overlay - 遮罩层
|
||||||
|
* @csspart panel - 对话框容器
|
||||||
|
* @csspart header - 对话框 header 部分,包含 icon 和 headline
|
||||||
|
* @csspart icon - 顶部图标,位于 header 中
|
||||||
|
* @csspart headline - 顶部标题,位于 header 中
|
||||||
|
* @csspart body - 对话框 body 部分
|
||||||
|
* @csspart description - 副文本部分,位于 body 中
|
||||||
|
* @csspart action - 底部操作按钮
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
* @cssprop --z-index - 组件的 CSS `z-index` 值
|
||||||
|
*/
|
||||||
|
export declare class Dialog extends MduiElement<DialogEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 顶部的 Material Icons 图标名。也可以通过 `slot="icon"` 设置
|
||||||
|
*/
|
||||||
|
icon?: string;
|
||||||
|
/**
|
||||||
|
* 标题。也可以通过 `slot="headline"` 设置
|
||||||
|
*/
|
||||||
|
headline?: string;
|
||||||
|
/**
|
||||||
|
* 标题下方的文本。也可以通过 `slot="description"` 设置
|
||||||
|
*/
|
||||||
|
description?: string;
|
||||||
|
/**
|
||||||
|
* 是否打开对话框
|
||||||
|
*/
|
||||||
|
open: boolean;
|
||||||
|
/**
|
||||||
|
* 是否全屏显示对话框
|
||||||
|
*/
|
||||||
|
fullscreen: boolean;
|
||||||
|
/**
|
||||||
|
* 是否允许按下 ESC 键关闭对话框
|
||||||
|
*/
|
||||||
|
closeOnEsc: boolean;
|
||||||
|
/**
|
||||||
|
* 是否允许点击遮罩层关闭对话框
|
||||||
|
*/
|
||||||
|
closeOnOverlayClick: boolean;
|
||||||
|
/**
|
||||||
|
* 是否垂直排列底部操作按钮
|
||||||
|
*/
|
||||||
|
stackedActions: boolean;
|
||||||
|
/**
|
||||||
|
* 是否可拖拽移动位置
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 是否可拖拽改变大小
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* dialog 组件内包含的 mdui-top-app-bar 组件
|
||||||
|
*/
|
||||||
|
private readonly topAppBarElements;
|
||||||
|
private originalTrigger;
|
||||||
|
private modalHelper;
|
||||||
|
private readonly overlayRef;
|
||||||
|
private readonly panelRef;
|
||||||
|
private readonly bodyRef;
|
||||||
|
private readonly hasSlotController;
|
||||||
|
private readonly definedController;
|
||||||
|
private onOpenChange;
|
||||||
|
disconnectedCallback(): void;
|
||||||
|
protected firstUpdated(_changedProperties: PropertyValues): void;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
private onOverlayClick;
|
||||||
|
private renderIcon;
|
||||||
|
private renderHeadline;
|
||||||
|
private renderDescription;
|
||||||
|
}
|
||||||
|
export interface DialogEventMap {
|
||||||
|
open: CustomEvent<void>;
|
||||||
|
opened: CustomEvent<void>;
|
||||||
|
close: CustomEvent<void>;
|
||||||
|
closed: CustomEvent<void>;
|
||||||
|
'overlay-click': CustomEvent<void>;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-dialog': Dialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
311
client/mdui_patched/components/dialog/index.js
Normal file
311
client/mdui_patched/components/dialog/index.js
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { customElement, property, queryAssignedElements, } 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 { DefinedController } from '@mdui/shared/controllers/defined.js';
|
||||||
|
import { HasSlotController } from '@mdui/shared/controllers/has-slot.js';
|
||||||
|
import { watch } from '@mdui/shared/decorators/watch.js';
|
||||||
|
import { animateTo, stopAnimations } from '@mdui/shared/helpers/animate.js';
|
||||||
|
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||||
|
import { Modal } from '@mdui/shared/helpers/modal.js';
|
||||||
|
import { getDuration, getEasing } from '@mdui/shared/helpers/motion.js';
|
||||||
|
import { lockScreen, unlockScreen } from '@mdui/shared/helpers/scroll.js';
|
||||||
|
import { nothingTemplate } from '@mdui/shared/helpers/template.js';
|
||||||
|
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||||
|
import { offLocaleReady } from '../../internal/localize.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 对话框组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-dialog>content</mdui-dialog>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event open - 对话框开始打开时触发。可以通过调用 `event.preventDefault()` 阻止对话框打开
|
||||||
|
* @event opened - 对话框打开动画完成后触发
|
||||||
|
* @event close - 对话框开始关闭时触发。可以通过调用 `event.preventDefault()` 阻止对话框关闭
|
||||||
|
* @event closed - 对话框关闭动画完成后触发
|
||||||
|
* @event overlay-click - 点击遮罩层时触发
|
||||||
|
*
|
||||||
|
* @slot header - 顶部元素,默认包含 `icon` slot 和 `headline` slot
|
||||||
|
* @slot icon - 顶部图标
|
||||||
|
* @slot headline - 顶部标题
|
||||||
|
* @slot description - 标题下方的文本
|
||||||
|
* @slot - 对话框主体内容
|
||||||
|
* @slot action - 底部操作栏中的元素
|
||||||
|
*
|
||||||
|
* @csspart overlay - 遮罩层
|
||||||
|
* @csspart panel - 对话框容器
|
||||||
|
* @csspart header - 对话框 header 部分,包含 icon 和 headline
|
||||||
|
* @csspart icon - 顶部图标,位于 header 中
|
||||||
|
* @csspart headline - 顶部标题,位于 header 中
|
||||||
|
* @csspart body - 对话框 body 部分
|
||||||
|
* @csspart description - 副文本部分,位于 body 中
|
||||||
|
* @csspart action - 底部操作按钮
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
* @cssprop --z-index - 组件的 CSS `z-index` 值
|
||||||
|
*/
|
||||||
|
let Dialog = class Dialog extends MduiElement {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 是否打开对话框
|
||||||
|
*/
|
||||||
|
this.open = false;
|
||||||
|
/**
|
||||||
|
* 是否全屏显示对话框
|
||||||
|
*/
|
||||||
|
this.fullscreen = false;
|
||||||
|
/**
|
||||||
|
* 是否允许按下 ESC 键关闭对话框
|
||||||
|
*/
|
||||||
|
this.closeOnEsc = false;
|
||||||
|
/**
|
||||||
|
* 是否允许点击遮罩层关闭对话框
|
||||||
|
*/
|
||||||
|
this.closeOnOverlayClick = false;
|
||||||
|
/**
|
||||||
|
* 是否垂直排列底部操作按钮
|
||||||
|
*/
|
||||||
|
this.stackedActions = false;
|
||||||
|
this.overlayRef = createRef();
|
||||||
|
this.panelRef = createRef();
|
||||||
|
this.bodyRef = createRef();
|
||||||
|
this.hasSlotController = new HasSlotController(this, 'header', 'icon', 'headline', 'description', 'action', '[default]');
|
||||||
|
this.definedController = new DefinedController(this, {
|
||||||
|
relatedElements: ['mdui-top-app-bar'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async onOpenChange() {
|
||||||
|
const hasUpdated = this.hasUpdated;
|
||||||
|
// 默认为关闭状态。因此首次渲染时,且为关闭状态,不执行
|
||||||
|
if (!this.open && !hasUpdated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.definedController.whenDefined();
|
||||||
|
if (!hasUpdated) {
|
||||||
|
await this.updateComplete;
|
||||||
|
}
|
||||||
|
// 内部的 header, body, actions 元素
|
||||||
|
const children = Array.from(this.panelRef.value.querySelectorAll('.header, .body, .actions'));
|
||||||
|
const easingLinear = getEasing(this, 'linear');
|
||||||
|
const easingEmphasizedDecelerate = getEasing(this, 'emphasized-decelerate');
|
||||||
|
const easingEmphasizedAccelerate = getEasing(this, 'emphasized-accelerate');
|
||||||
|
const stopAnimation = () => Promise.all([
|
||||||
|
stopAnimations(this.overlayRef.value),
|
||||||
|
stopAnimations(this.panelRef.value),
|
||||||
|
...children.map((child) => stopAnimations(child)),
|
||||||
|
]);
|
||||||
|
// 打开
|
||||||
|
// 要区分是否首次渲染,首次渲染不触发事件,不执行动画;非首次渲染,触发事件,执行动画
|
||||||
|
if (this.open) {
|
||||||
|
if (hasUpdated) {
|
||||||
|
const eventProceeded = this.emit('open', { cancelable: true });
|
||||||
|
if (!eventProceeded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.style.display = 'flex';
|
||||||
|
// 包含 <mdui-top-app-bar slot="header"> 时
|
||||||
|
const topAppBarElements = this.topAppBarElements ?? [];
|
||||||
|
if (topAppBarElements.length) {
|
||||||
|
const topAppBarElement = topAppBarElements[0];
|
||||||
|
// top-app-bar 未设置 scrollTarget 时,默认设置为 bodyRef
|
||||||
|
if (!topAppBarElement.scrollTarget) {
|
||||||
|
topAppBarElement.scrollTarget = this.bodyRef.value;
|
||||||
|
}
|
||||||
|
// 移除 header 和 body 之间的 margin
|
||||||
|
this.bodyRef.value.style.marginTop = '0';
|
||||||
|
}
|
||||||
|
this.originalTrigger = document.activeElement;
|
||||||
|
this.modalHelper.activate();
|
||||||
|
lockScreen(this);
|
||||||
|
await stopAnimation();
|
||||||
|
// 设置聚焦
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const autoFocusTarget = this.querySelector('[autofocus]');
|
||||||
|
if (autoFocusTarget) {
|
||||||
|
autoFocusTarget.focus({ preventScroll: true });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.panelRef.value.focus({ preventScroll: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const duration = getDuration(this, 'medium4');
|
||||||
|
await Promise.all([
|
||||||
|
animateTo(this.overlayRef.value, [{ opacity: 0 }, { opacity: 1, offset: 0.3 }, { opacity: 1 }], {
|
||||||
|
duration: hasUpdated ? duration : 0,
|
||||||
|
easing: easingLinear,
|
||||||
|
}),
|
||||||
|
animateTo(this.panelRef.value, [
|
||||||
|
{ transform: 'translateY(-1.875rem) scaleY(0)' },
|
||||||
|
{ transform: 'translateY(0) scaleY(1)' },
|
||||||
|
], {
|
||||||
|
duration: hasUpdated ? duration : 0,
|
||||||
|
easing: easingEmphasizedDecelerate,
|
||||||
|
}),
|
||||||
|
animateTo(this.panelRef.value, [{ opacity: 0 }, { opacity: 1, offset: 0.1 }, { opacity: 1 }], {
|
||||||
|
duration: hasUpdated ? duration : 0,
|
||||||
|
easing: easingLinear,
|
||||||
|
}),
|
||||||
|
...children.map((child) => animateTo(child, [
|
||||||
|
{ opacity: 0 },
|
||||||
|
{ opacity: 0, offset: 0.2 },
|
||||||
|
{ opacity: 1, offset: 0.8 },
|
||||||
|
{ opacity: 1 },
|
||||||
|
], {
|
||||||
|
duration: hasUpdated ? duration : 0,
|
||||||
|
easing: easingLinear,
|
||||||
|
})),
|
||||||
|
]);
|
||||||
|
if (hasUpdated) {
|
||||||
|
this.emit('opened');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const eventProceeded = this.emit('close', { cancelable: true });
|
||||||
|
if (!eventProceeded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.modalHelper.deactivate();
|
||||||
|
await stopAnimation();
|
||||||
|
const duration = getDuration(this, 'short4');
|
||||||
|
await Promise.all([
|
||||||
|
animateTo(this.overlayRef.value, [{ opacity: 1 }, { opacity: 0 }], {
|
||||||
|
duration,
|
||||||
|
easing: easingLinear,
|
||||||
|
}),
|
||||||
|
animateTo(this.panelRef.value, [
|
||||||
|
{ transform: 'translateY(0) scaleY(1)' },
|
||||||
|
{ transform: 'translateY(-1.875rem) scaleY(0.6)' },
|
||||||
|
], { duration, easing: easingEmphasizedAccelerate }),
|
||||||
|
animateTo(this.panelRef.value, [{ opacity: 1 }, { opacity: 1, offset: 0.75 }, { opacity: 0 }], { duration, easing: easingLinear }),
|
||||||
|
...children.map((child) => animateTo(child, [{ opacity: 1 }, { opacity: 0, offset: 0.75 }, { opacity: 0 }], { duration, easing: easingLinear })),
|
||||||
|
]);
|
||||||
|
this.style.display = 'none';
|
||||||
|
unlockScreen(this);
|
||||||
|
// 对话框关闭后,恢复焦点到原有的元素上
|
||||||
|
const trigger = this.originalTrigger;
|
||||||
|
if (typeof trigger?.focus === 'function') {
|
||||||
|
setTimeout(() => trigger.focus());
|
||||||
|
}
|
||||||
|
this.emit('closed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
unlockScreen(this);
|
||||||
|
// alert, confirm, prompt 函数支持 localize。这里确保在组件销毁时,取消监听 localize ready 事件
|
||||||
|
offLocaleReady(this);
|
||||||
|
}
|
||||||
|
firstUpdated(_changedProperties) {
|
||||||
|
super.firstUpdated(_changedProperties);
|
||||||
|
this.modalHelper = new Modal(this);
|
||||||
|
this.addEventListener('keydown', (event) => {
|
||||||
|
if (this.open && this.closeOnEsc && event.key === 'Escape') {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.open = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const hasActionSlot = this.hasSlotController.test('action');
|
||||||
|
const hasDefaultSlot = this.hasSlotController.test('[default]');
|
||||||
|
const hasIcon = !!this.icon || this.hasSlotController.test('icon');
|
||||||
|
const hasHeadline = !!this.headline || this.hasSlotController.test('headline');
|
||||||
|
const hasDescription = !!this.description || this.hasSlotController.test('description');
|
||||||
|
const hasHeader = hasIcon || hasHeadline || this.hasSlotController.test('header');
|
||||||
|
const hasBody = hasDescription || hasDefaultSlot;
|
||||||
|
// modify: 移除了 tabindex="0", 换为 tabindex
|
||||||
|
return html `<div ${ref(this.overlayRef)} part="overlay" class="overlay" @click="${this.onOverlayClick}" tabindex="-1"></div><div ${ref(this.panelRef)} part="panel" class="panel ${classMap({
|
||||||
|
'has-icon': hasIcon,
|
||||||
|
'has-description': hasDescription,
|
||||||
|
'has-default': hasDefaultSlot,
|
||||||
|
})}" tabindex>${when(hasHeader, () => html `<slot name="header" part="header" class="header">${when(hasIcon, () => this.renderIcon())} ${when(hasHeadline, () => this.renderHeadline())}</slot>`)} ${when(hasBody, () => html `<div ${ref(this.bodyRef)} part="body" class="body">${when(hasDescription, () => this.renderDescription())}<slot></slot></div>`)} ${when(hasActionSlot, () => html `<slot name="action" part="action" class="action"></slot>`)}</div>`;
|
||||||
|
}
|
||||||
|
onOverlayClick() {
|
||||||
|
this.emit('overlay-click');
|
||||||
|
if (!this.closeOnOverlayClick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.open = false;
|
||||||
|
}
|
||||||
|
renderIcon() {
|
||||||
|
return html `<slot name="icon" part="icon" class="icon">${this.icon
|
||||||
|
? html `<mdui-icon name="${this.icon}"></mdui-icon>`
|
||||||
|
: nothingTemplate}</slot>`;
|
||||||
|
}
|
||||||
|
renderHeadline() {
|
||||||
|
return html `<slot name="headline" part="headline" class="headline">${this.headline}</slot>`;
|
||||||
|
}
|
||||||
|
renderDescription() {
|
||||||
|
return html `<slot name="description" part="description" class="description">${this.description}</slot>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Dialog.styles = [componentStyle, style];
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Dialog.prototype, "icon", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Dialog.prototype, "headline", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Dialog.prototype, "description", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Dialog.prototype, "open", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Dialog.prototype, "fullscreen", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
attribute: 'close-on-esc',
|
||||||
|
})
|
||||||
|
], Dialog.prototype, "closeOnEsc", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
attribute: 'close-on-overlay-click',
|
||||||
|
})
|
||||||
|
], Dialog.prototype, "closeOnOverlayClick", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
attribute: 'stacked-actions',
|
||||||
|
})
|
||||||
|
], Dialog.prototype, "stackedActions", void 0);
|
||||||
|
__decorate([
|
||||||
|
queryAssignedElements({
|
||||||
|
slot: 'header',
|
||||||
|
selector: 'mdui-top-app-bar',
|
||||||
|
flatten: true,
|
||||||
|
})
|
||||||
|
], Dialog.prototype, "topAppBarElements", void 0);
|
||||||
|
__decorate([
|
||||||
|
watch('open')
|
||||||
|
], Dialog.prototype, "onOpenChange", null);
|
||||||
|
Dialog = __decorate([
|
||||||
|
customElement('mdui-dialog')
|
||||||
|
], Dialog);
|
||||||
|
export { Dialog };
|
||||||
1
client/mdui_patched/components/dialog/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/dialog/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
2
client/mdui_patched/components/dialog/style.js
Normal file
2
client/mdui_patched/components/dialog/style.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{--shape-corner:var(--mdui-shape-corner-extra-large);--z-index:2300;position:fixed;z-index:var(--z-index);display:none;align-items:center;justify-content:center;inset:0;padding:3rem}::slotted(mdui-top-app-bar[slot=header]){position:absolute;border-top-left-radius:var(--mdui-shape-corner-extra-large);border-top-right-radius:var(--mdui-shape-corner-extra-large);background-color:rgb(var(--mdui-color-surface-container-high))}:host([fullscreen]:not([fullscreen=false i])){--shape-corner:var(--mdui-shape-corner-none);padding:0}:host([fullscreen]:not([fullscreen=false i])) ::slotted(mdui-top-app-bar[slot=header]){border-top-left-radius:var(--mdui-shape-corner-none);border-top-right-radius:var(--mdui-shape-corner-none)}.overlay{position:fixed;inset:0;background-color:rgba(var(--mdui-color-scrim),.4)}.panel{--mdui-color-background:var(--mdui-color-surface-container-high);position:relative;display:flex;flex-direction:column;max-height:100%;border-radius:var(--shape-corner);outline:0;transform-origin:top;min-width:17.5rem;max-width:35rem;padding:1.5rem;background-color:rgb(var(--mdui-color-surface-container-high));box-shadow:var(--mdui-elevation-level3)}:host([fullscreen]:not([fullscreen=false i])) .panel{width:100%;max-width:100%;height:100%;max-height:100%;box-shadow:var(--mdui-elevation-level0)}.header{display:flex;flex-direction:column}.has-icon .header{align-items:center}.icon{display:flex;color:rgb(var(--mdui-color-secondary));font-size:1.5rem}.icon mdui-icon,::slotted([slot=icon]){font-size:inherit}.headline{display:flex;color:rgb(var(--mdui-color-on-surface));font-size:var(--mdui-typescale-headline-small-size);font-weight:var(--mdui-typescale-headline-small-weight);letter-spacing:var(--mdui-typescale-headline-small-tracking);line-height:var(--mdui-typescale-headline-small-line-height)}.icon+.headline{padding-top:1rem}.body{overflow:auto}.header+.body{margin-top:1rem}.description{display:flex;color:rgb(var(--mdui-color-on-surface-variant));font-size:var(--mdui-typescale-body-medium-size);font-weight:var(--mdui-typescale-body-medium-weight);letter-spacing:var(--mdui-typescale-body-medium-tracking);line-height:var(--mdui-typescale-body-medium-line-height)}:host([fullscreen]:not([fullscreen=false i])) .description{color:rgb(var(--mdui-color-on-surface))}.has-description.has-default .description{margin-bottom:1rem}.action{display:flex;justify-content:flex-end;padding-top:1.5rem}.action::slotted(:not(:first-child)){margin-left:.5rem}:host([stacked-actions]:not([stacked-actions=false i])) .action{flex-direction:column;align-items:end}:host([stacked-actions]:not([stacked-actions=false i])) .action::slotted(:not(:first-child)){margin-left:0;margin-top:.5rem}`;
|
||||||
1
client/mdui_patched/components/divider.d.ts
vendored
Normal file
1
client/mdui_patched/components/divider.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './divider/index.js';
|
||||||
1
client/mdui_patched/components/divider.js
Normal file
1
client/mdui_patched/components/divider.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './divider/index.js';
|
||||||
32
client/mdui_patched/components/divider/index.d.ts
vendored
Normal file
32
client/mdui_patched/components/divider/index.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import type { CSSResultGroup, TemplateResult } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 分割线组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-divider></mdui-divider>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export declare class Divider extends MduiElement<DividerEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 是否为垂直分割线
|
||||||
|
*/
|
||||||
|
vertical: boolean;
|
||||||
|
/**
|
||||||
|
* 是否进行左侧缩进
|
||||||
|
*/
|
||||||
|
inset: boolean;
|
||||||
|
/**
|
||||||
|
* 是否进行左右两侧缩进
|
||||||
|
*/
|
||||||
|
middle: boolean;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
}
|
||||||
|
export interface DividerEventMap {
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-divider': Divider;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
client/mdui_patched/components/divider/index.js
Normal file
60
client/mdui_patched/components/divider/index.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
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 { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 分割线组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-divider></mdui-divider>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
let Divider = class Divider extends MduiElement {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
/**
|
||||||
|
* 是否为垂直分割线
|
||||||
|
*/
|
||||||
|
this.vertical = false;
|
||||||
|
/**
|
||||||
|
* 是否进行左侧缩进
|
||||||
|
*/
|
||||||
|
this.inset = false;
|
||||||
|
/**
|
||||||
|
* 是否进行左右两侧缩进
|
||||||
|
*/
|
||||||
|
this.middle = false;
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return html ``;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Divider.styles = [componentStyle, style];
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Divider.prototype, "vertical", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Divider.prototype, "inset", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Divider.prototype, "middle", void 0);
|
||||||
|
Divider = __decorate([
|
||||||
|
customElement('mdui-divider')
|
||||||
|
], Divider);
|
||||||
|
export { Divider };
|
||||||
1
client/mdui_patched/components/divider/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/divider/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
2
client/mdui_patched/components/divider/style.js
Normal file
2
client/mdui_patched/components/divider/style.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{display:block;height:.0625rem;background-color:rgb(var(--mdui-color-surface-variant))}:host([inset]:not([inset=false i])){margin-left:1rem}:host([middle]:not([middle=false i])){margin-left:1rem;margin-right:1rem}:host([vertical]:not([vertical=false i])){height:100%;width:.0625rem}`;
|
||||||
1
client/mdui_patched/components/dropdown.d.ts
vendored
Normal file
1
client/mdui_patched/components/dropdown.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './dropdown/index.js';
|
||||||
1
client/mdui_patched/components/dropdown.js
Normal file
1
client/mdui_patched/components/dropdown.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './dropdown/index.js';
|
||||||
137
client/mdui_patched/components/dropdown/index.d.ts
vendored
Normal file
137
client/mdui_patched/components/dropdown/index.d.ts
vendored
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import '@mdui/jq/methods/height.js';
|
||||||
|
import '@mdui/jq/methods/is.js';
|
||||||
|
import '@mdui/jq/methods/width.js';
|
||||||
|
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
|
||||||
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 下拉组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-dropdown>
|
||||||
|
* ..<mdui-button slot="trigger">open dropdown</mdui-button>
|
||||||
|
* ..<mdui-menu>
|
||||||
|
* ....<mdui-menu-item>Item 1</mdui-menu-item>
|
||||||
|
* ....<mdui-menu-item>Item 2</mdui-menu-item>
|
||||||
|
* ..</mdui-menu>
|
||||||
|
* </mdui-dropdown>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event open - 下拉组件开始打开时,事件被触发。可以通过调用 `event.preventDefault()` 阻止下拉组件打开
|
||||||
|
* @event opened - 下拉组件打开动画完成时,事件被触发
|
||||||
|
* @event close - 下拉组件开始关闭时,事件被触发。可以通过调用 `event.preventDefault()` 阻止下拉组件关闭
|
||||||
|
* @event closed - 下拉组件关闭动画完成时,事件被触发
|
||||||
|
*
|
||||||
|
* @slot - 下拉组件的内容
|
||||||
|
* @slot trigger - 触发下拉组件的元素,例如 [`<mdui-button>`](/docs/2/components/button) 元素
|
||||||
|
*
|
||||||
|
* @csspart trigger - 触发下拉组件的元素的容器,即 `trigger` slot 的容器
|
||||||
|
* @csspart panel - 下拉组件内容的容器
|
||||||
|
*
|
||||||
|
* @cssprop --z-index - 组件的 CSS `z-index` 值
|
||||||
|
*/
|
||||||
|
export declare class Dropdown extends MduiElement<DropdownEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* 是否打开下拉组件
|
||||||
|
*/
|
||||||
|
open: boolean;
|
||||||
|
/**
|
||||||
|
* 是否禁用下拉组件
|
||||||
|
*/
|
||||||
|
disabled: boolean;
|
||||||
|
/**
|
||||||
|
* 下拉组件的触发方式,支持多个值,用空格分隔。可选值包括:
|
||||||
|
*
|
||||||
|
* * `click`:点击触发
|
||||||
|
* * `hover`:鼠标悬浮触发
|
||||||
|
* * `focus`:聚焦触发
|
||||||
|
* * `contextmenu`:鼠标右键点击、或触摸长按触发
|
||||||
|
* * `manual`:仅能通过编程方式打开和关闭下拉组件,不能再指定其他触发方式
|
||||||
|
*/
|
||||||
|
trigger: /*点击触发*/ 'click' | /*鼠标悬浮触发*/ 'hover' | /*聚焦触发*/ 'focus' | /*鼠标右键点击、或触摸长按触发*/ 'contextmenu' | /*仅能通过编程方式打开和关闭下拉组件,不能再指定其他触发方式*/ 'manual' | string;
|
||||||
|
/**
|
||||||
|
* 下拉组件内容的位置。可选值包括:
|
||||||
|
*
|
||||||
|
* * `auto`:自动判断位置
|
||||||
|
* * `top-start`:上方左对齐
|
||||||
|
* * `top`:上方居中
|
||||||
|
* * `top-end`:上方右对齐
|
||||||
|
* * `bottom-start`:下方左对齐
|
||||||
|
* * `bottom`:下方居中
|
||||||
|
* * `bottom-end`:下方右对齐
|
||||||
|
* * `left-start`:左侧顶部对齐
|
||||||
|
* * `left`:左侧居中
|
||||||
|
* * `left-end`:左侧底部对齐
|
||||||
|
* * `right-start`:右侧顶部对齐
|
||||||
|
* * `right`:右侧居中
|
||||||
|
* * `right-end`:右侧底部对齐
|
||||||
|
*/
|
||||||
|
placement: /*自动判断位置*/ 'auto' | /*上方左对齐*/ 'top-start' | /*上方居中*/ 'top' | /*上方右对齐*/ 'top-end' | /*下方左对齐*/ 'bottom-start' | /*下方居中*/ 'bottom' | /*下方右对齐*/ 'bottom-end' | /*左侧顶部对齐*/ 'left-start' | /*左侧居中*/ 'left' | /*左侧底部对齐*/ 'left-end' | /*右侧顶部对齐*/ 'right-start' | /*右侧居中*/ 'right' | /*右侧底部对齐*/ 'right-end';
|
||||||
|
/**
|
||||||
|
* 点击 [`<mdui-menu-item>`](/docs/2/components/menu#menu-item-api) 后,下拉组件是否保持打开状态
|
||||||
|
*/
|
||||||
|
stayOpenOnClick: boolean;
|
||||||
|
/**
|
||||||
|
* 鼠标悬浮触发下拉组件打开的延时,单位为毫秒
|
||||||
|
*/
|
||||||
|
openDelay: number;
|
||||||
|
/**
|
||||||
|
* 鼠标悬浮触发下拉组件关闭的延时,单位为毫秒
|
||||||
|
*/
|
||||||
|
closeDelay: number;
|
||||||
|
/**
|
||||||
|
* 是否在触发下拉组件的光标位置打开下拉组件,常用于打开鼠标右键菜单
|
||||||
|
*/
|
||||||
|
openOnPointer: boolean;
|
||||||
|
private readonly triggerElements;
|
||||||
|
private readonly panelElements;
|
||||||
|
private pointerOffsetX;
|
||||||
|
private pointerOffsetY;
|
||||||
|
private animateDirection;
|
||||||
|
private openTimeout;
|
||||||
|
private closeTimeout;
|
||||||
|
private observeResize?;
|
||||||
|
private overflowAncestors?;
|
||||||
|
private readonly panelRef;
|
||||||
|
private readonly definedController;
|
||||||
|
constructor();
|
||||||
|
private get triggerElement();
|
||||||
|
private onPositionChange;
|
||||||
|
private onOpenChange;
|
||||||
|
connectedCallback(): void;
|
||||||
|
disconnectedCallback(): void;
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues): void;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
/**
|
||||||
|
* 获取 dropdown 打开、关闭动画的 CSS scaleX 或 scaleY
|
||||||
|
*/
|
||||||
|
private getCssScaleName;
|
||||||
|
/**
|
||||||
|
* 在 document 上点击时,根据条件判断是否要关闭 dropdown
|
||||||
|
*/
|
||||||
|
private onDocumentClick;
|
||||||
|
/**
|
||||||
|
* 在 document 上按下按键时,根据条件判断是否要关闭 dropdown
|
||||||
|
*/
|
||||||
|
private onDocumentKeydown;
|
||||||
|
private onWindowScroll;
|
||||||
|
private hasTrigger;
|
||||||
|
private onFocus;
|
||||||
|
private onClick;
|
||||||
|
private onPanelClick;
|
||||||
|
private onContextMenu;
|
||||||
|
private onMouseEnter;
|
||||||
|
private onMouseLeave;
|
||||||
|
private updatePositioner;
|
||||||
|
}
|
||||||
|
export interface DropdownEventMap {
|
||||||
|
open: CustomEvent<void>;
|
||||||
|
opened: CustomEvent<void>;
|
||||||
|
close: CustomEvent<void>;
|
||||||
|
closed: CustomEvent<void>;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-dropdown': Dropdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
583
client/mdui_patched/components/dropdown/index.js
Normal file
583
client/mdui_patched/components/dropdown/index.js
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
import { __decorate } from "tslib";
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { customElement, property, queryAssignedElements, } from 'lit/decorators.js';
|
||||||
|
import { createRef, ref } from 'lit/directives/ref.js';
|
||||||
|
import { getOverflowAncestors } from '@floating-ui/utils/dom';
|
||||||
|
import { $ } from '@mdui/jq/$.js';
|
||||||
|
import '@mdui/jq/methods/height.js';
|
||||||
|
import '@mdui/jq/methods/is.js';
|
||||||
|
import '@mdui/jq/methods/width.js';
|
||||||
|
import { isFunction } 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 { animateTo, stopAnimations } from '@mdui/shared/helpers/animate.js';
|
||||||
|
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
|
||||||
|
import { getDuration, getEasing } from '@mdui/shared/helpers/motion.js';
|
||||||
|
import { observeResize } from '@mdui/shared/helpers/observeResize.js';
|
||||||
|
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
|
||||||
|
import { style } from './style.js';
|
||||||
|
/**
|
||||||
|
* @summary 下拉组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-dropdown>
|
||||||
|
* ..<mdui-button slot="trigger">open dropdown</mdui-button>
|
||||||
|
* ..<mdui-menu>
|
||||||
|
* ....<mdui-menu-item>Item 1</mdui-menu-item>
|
||||||
|
* ....<mdui-menu-item>Item 2</mdui-menu-item>
|
||||||
|
* ..</mdui-menu>
|
||||||
|
* </mdui-dropdown>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event open - 下拉组件开始打开时,事件被触发。可以通过调用 `event.preventDefault()` 阻止下拉组件打开
|
||||||
|
* @event opened - 下拉组件打开动画完成时,事件被触发
|
||||||
|
* @event close - 下拉组件开始关闭时,事件被触发。可以通过调用 `event.preventDefault()` 阻止下拉组件关闭
|
||||||
|
* @event closed - 下拉组件关闭动画完成时,事件被触发
|
||||||
|
*
|
||||||
|
* @slot - 下拉组件的内容
|
||||||
|
* @slot trigger - 触发下拉组件的元素,例如 [`<mdui-button>`](/docs/2/components/button) 元素
|
||||||
|
*
|
||||||
|
* @csspart trigger - 触发下拉组件的元素的容器,即 `trigger` slot 的容器
|
||||||
|
* @csspart panel - 下拉组件内容的容器
|
||||||
|
*
|
||||||
|
* @cssprop --z-index - 组件的 CSS `z-index` 值
|
||||||
|
*/
|
||||||
|
let Dropdown = class Dropdown extends MduiElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
/**
|
||||||
|
* 是否打开下拉组件
|
||||||
|
*/
|
||||||
|
this.open = false;
|
||||||
|
/**
|
||||||
|
* 是否禁用下拉组件
|
||||||
|
*/
|
||||||
|
this.disabled = false;
|
||||||
|
/**
|
||||||
|
* 下拉组件的触发方式,支持多个值,用空格分隔。可选值包括:
|
||||||
|
*
|
||||||
|
* * `click`:点击触发
|
||||||
|
* * `hover`:鼠标悬浮触发
|
||||||
|
* * `focus`:聚焦触发
|
||||||
|
* * `contextmenu`:鼠标右键点击、或触摸长按触发
|
||||||
|
* * `manual`:仅能通过编程方式打开和关闭下拉组件,不能再指定其他触发方式
|
||||||
|
*/
|
||||||
|
this.trigger = 'click';
|
||||||
|
/**
|
||||||
|
* 下拉组件内容的位置。可选值包括:
|
||||||
|
*
|
||||||
|
* * `auto`:自动判断位置
|
||||||
|
* * `top-start`:上方左对齐
|
||||||
|
* * `top`:上方居中
|
||||||
|
* * `top-end`:上方右对齐
|
||||||
|
* * `bottom-start`:下方左对齐
|
||||||
|
* * `bottom`:下方居中
|
||||||
|
* * `bottom-end`:下方右对齐
|
||||||
|
* * `left-start`:左侧顶部对齐
|
||||||
|
* * `left`:左侧居中
|
||||||
|
* * `left-end`:左侧底部对齐
|
||||||
|
* * `right-start`:右侧顶部对齐
|
||||||
|
* * `right`:右侧居中
|
||||||
|
* * `right-end`:右侧底部对齐
|
||||||
|
*/
|
||||||
|
this.placement = 'auto';
|
||||||
|
/**
|
||||||
|
* 点击 [`<mdui-menu-item>`](/docs/2/components/menu#menu-item-api) 后,下拉组件是否保持打开状态
|
||||||
|
*/
|
||||||
|
this.stayOpenOnClick = false;
|
||||||
|
/**
|
||||||
|
* 鼠标悬浮触发下拉组件打开的延时,单位为毫秒
|
||||||
|
*/
|
||||||
|
this.openDelay = 150;
|
||||||
|
/**
|
||||||
|
* 鼠标悬浮触发下拉组件关闭的延时,单位为毫秒
|
||||||
|
*/
|
||||||
|
this.closeDelay = 150;
|
||||||
|
/**
|
||||||
|
* 是否在触发下拉组件的光标位置打开下拉组件,常用于打开鼠标右键菜单
|
||||||
|
*/
|
||||||
|
this.openOnPointer = false;
|
||||||
|
this.panelRef = createRef();
|
||||||
|
this.definedController = new DefinedController(this, {
|
||||||
|
relatedElements: [''],
|
||||||
|
});
|
||||||
|
this.onDocumentClick = this.onDocumentClick.bind(this);
|
||||||
|
this.onDocumentKeydown = this.onDocumentKeydown.bind(this);
|
||||||
|
this.onWindowScroll = this.onWindowScroll.bind(this);
|
||||||
|
this.onMouseLeave = this.onMouseLeave.bind(this);
|
||||||
|
this.onFocus = this.onFocus.bind(this);
|
||||||
|
this.onClick = this.onClick.bind(this);
|
||||||
|
this.onContextMenu = this.onContextMenu.bind(this);
|
||||||
|
this.onMouseEnter = this.onMouseEnter.bind(this);
|
||||||
|
this.onPanelClick = this.onPanelClick.bind(this);
|
||||||
|
}
|
||||||
|
get triggerElement() {
|
||||||
|
return this.triggerElements[0];
|
||||||
|
}
|
||||||
|
// 这些属性变更时,需要更新样式
|
||||||
|
async onPositionChange() {
|
||||||
|
// 如果是打开状态,则更新 panel 的位置
|
||||||
|
if (this.open) {
|
||||||
|
await this.definedController.whenDefined();
|
||||||
|
this.updatePositioner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async onOpenChange() {
|
||||||
|
const hasUpdated = this.hasUpdated;
|
||||||
|
// 默认为关闭状态。因此首次渲染时,且为关闭状态,不执行
|
||||||
|
if (!this.open && !hasUpdated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.definedController.whenDefined();
|
||||||
|
if (!hasUpdated) {
|
||||||
|
await this.updateComplete;
|
||||||
|
}
|
||||||
|
const easingLinear = getEasing(this, 'linear');
|
||||||
|
const easingEmphasizedDecelerate = getEasing(this, 'emphasized-decelerate');
|
||||||
|
const easingEmphasizedAccelerate = getEasing(this, 'emphasized-accelerate');
|
||||||
|
// 打开
|
||||||
|
// 要区分是否首次渲染,首次渲染时不触发事件,不执行动画;非首次渲染,触发事件,执行动画
|
||||||
|
if (this.open) {
|
||||||
|
if (hasUpdated) {
|
||||||
|
const eventProceeded = this.emit('open', { cancelable: true });
|
||||||
|
if (!eventProceeded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dropdown 打开时,尝试把焦点放到 panel 中
|
||||||
|
const focusablePanel = this.panelElements.find((panel) => isFunction(panel.focus));
|
||||||
|
setTimeout(() => {
|
||||||
|
focusablePanel?.focus();
|
||||||
|
});
|
||||||
|
const duration = getDuration(this, 'medium4');
|
||||||
|
await stopAnimations(this.panelRef.value);
|
||||||
|
this.panelRef.value.hidden = false;
|
||||||
|
this.updatePositioner();
|
||||||
|
await Promise.all([
|
||||||
|
animateTo(this.panelRef.value, [
|
||||||
|
{ transform: `${this.getCssScaleName()}(0.45)` },
|
||||||
|
{ transform: `${this.getCssScaleName()}(1)` },
|
||||||
|
], {
|
||||||
|
duration: hasUpdated ? duration : 0,
|
||||||
|
easing: easingEmphasizedDecelerate,
|
||||||
|
}),
|
||||||
|
animateTo(this.panelRef.value, [{ opacity: 0 }, { opacity: 1, offset: 0.125 }, { opacity: 1 }], {
|
||||||
|
duration: hasUpdated ? duration : 0,
|
||||||
|
easing: easingLinear,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
if (hasUpdated) {
|
||||||
|
this.emit('opened');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const eventProceeded = this.emit('close', { cancelable: true });
|
||||||
|
if (!eventProceeded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// dropdown 关闭时,如果不支持 focus 触发,且焦点在 dropdown 内,则焦点回到 trigger 上
|
||||||
|
if (!this.hasTrigger('focus') &&
|
||||||
|
isFunction(this.triggerElement?.focus) &&
|
||||||
|
(this.contains(document.activeElement) ||
|
||||||
|
this.contains(document.activeElement?.assignedSlot ?? null))) {
|
||||||
|
this.triggerElement.focus();
|
||||||
|
}
|
||||||
|
const duration = getDuration(this, 'short4');
|
||||||
|
await stopAnimations(this.panelRef.value);
|
||||||
|
await Promise.all([
|
||||||
|
animateTo(this.panelRef.value, [
|
||||||
|
{ transform: `${this.getCssScaleName()}(1)` },
|
||||||
|
{ transform: `${this.getCssScaleName()}(0.45)` },
|
||||||
|
], { duration, easing: easingEmphasizedAccelerate }),
|
||||||
|
animateTo(this.panelRef.value, [{ opacity: 1 }, { opacity: 1, offset: 0.875 }, { opacity: 0 }], { duration, easing: easingLinear }),
|
||||||
|
]);
|
||||||
|
// 可能关闭 dropdown 时该元素已经不存在了(比如页面直接跳转了)
|
||||||
|
if (this.panelRef.value) {
|
||||||
|
this.panelRef.value.hidden = true;
|
||||||
|
}
|
||||||
|
this.emit('closed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.definedController.whenDefined().then(() => {
|
||||||
|
document.addEventListener('pointerdown', this.onDocumentClick);
|
||||||
|
document.addEventListener('keydown', this.onDocumentKeydown);
|
||||||
|
this.overflowAncestors = getOverflowAncestors(this.triggerElement);
|
||||||
|
this.overflowAncestors.forEach((ancestor) => {
|
||||||
|
ancestor.addEventListener('scroll', this.onWindowScroll);
|
||||||
|
});
|
||||||
|
// triggerElement 的尺寸变化时,重新调整 panel 的位置
|
||||||
|
this.observeResize = observeResize(this.triggerElement, () => {
|
||||||
|
this.updatePositioner();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
disconnectedCallback() {
|
||||||
|
// 移除组件时,如果关闭动画正在进行中,则会导致关闭动画无法执行完成,最终组件无法隐藏
|
||||||
|
// 具体场景为 vue 的 <keep-alive> 中切换走,再切换回来时,面板仍然打开着
|
||||||
|
if (!this.open && this.panelRef.value) {
|
||||||
|
this.panelRef.value.hidden = true;
|
||||||
|
}
|
||||||
|
super.disconnectedCallback();
|
||||||
|
document.removeEventListener('pointerdown', this.onDocumentClick);
|
||||||
|
document.removeEventListener('keydown', this.onDocumentKeydown);
|
||||||
|
this.overflowAncestors?.forEach((ancestor) => {
|
||||||
|
ancestor.removeEventListener('scroll', this.onWindowScroll);
|
||||||
|
});
|
||||||
|
this.observeResize?.unobserve();
|
||||||
|
}
|
||||||
|
firstUpdated(changedProperties) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
this.addEventListener('mouseleave', this.onMouseLeave);
|
||||||
|
this.definedController.whenDefined().then(() => {
|
||||||
|
this.triggerElement.addEventListener('focus', this.onFocus);
|
||||||
|
this.triggerElement.addEventListener('click', this.onClick);
|
||||||
|
this.triggerElement.addEventListener('contextmenu', this.onContextMenu);
|
||||||
|
this.triggerElement.addEventListener('mouseenter', this.onMouseEnter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return html `<slot name="trigger" part="trigger" class="trigger"></slot><slot ${ref(this.panelRef)} part="panel" class="panel" hidden @click="${this.onPanelClick}"></slot>`;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取 dropdown 打开、关闭动画的 CSS scaleX 或 scaleY
|
||||||
|
*/
|
||||||
|
getCssScaleName() {
|
||||||
|
return this.animateDirection === 'horizontal' ? 'scaleX' : 'scaleY';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 在 document 上点击时,根据条件判断是否要关闭 dropdown
|
||||||
|
*/
|
||||||
|
onDocumentClick(e) {
|
||||||
|
if (this.disabled || !this.open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const path = e.composedPath();
|
||||||
|
// 点击 dropdown 外部区域,直接关闭
|
||||||
|
if (!path.includes(this)) {
|
||||||
|
this.open = false;
|
||||||
|
}
|
||||||
|
// 当包含 contextmenu 且不包含 click 时,点击 trigger,关闭
|
||||||
|
if (this.hasTrigger('contextmenu') &&
|
||||||
|
!this.hasTrigger('click') &&
|
||||||
|
path.includes(this.triggerElement)) {
|
||||||
|
this.open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 在 document 上按下按键时,根据条件判断是否要关闭 dropdown
|
||||||
|
*/
|
||||||
|
onDocumentKeydown(event) {
|
||||||
|
if (this.disabled || !this.open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 按下 ESC 键时,关闭 dropdown
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
this.open = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 按下 Tab 键时,关闭 dropdown
|
||||||
|
if (event.key === 'Tab') {
|
||||||
|
// 如果不支持 focus 触发,则焦点回到 trigger 上(这个会在 onOpenChange 中执行 )这里只需阻止默认的 Tab 行为
|
||||||
|
if (!this.hasTrigger('focus') && isFunction(this.triggerElement?.focus)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
this.open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onWindowScroll() {
|
||||||
|
window.requestAnimationFrame(() => this.onPositionChange());
|
||||||
|
}
|
||||||
|
hasTrigger(trigger) {
|
||||||
|
const triggers = this.trigger.split(' ');
|
||||||
|
return triggers.includes(trigger);
|
||||||
|
}
|
||||||
|
onFocus() {
|
||||||
|
if (this.disabled || this.open || !this.hasTrigger('focus')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.open = true;
|
||||||
|
}
|
||||||
|
onClick(e) {
|
||||||
|
// e.button 为 0 时,为鼠标左键点击。忽略鼠标中间和右键
|
||||||
|
if (this.disabled || e.button || !this.hasTrigger('click')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 支持 hover 或 focus 触发时,点击时,不关闭 dropdown
|
||||||
|
if (this.open && (this.hasTrigger('hover') || this.hasTrigger('focus'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.pointerOffsetX = e.offsetX;
|
||||||
|
this.pointerOffsetY = e.offsetY;
|
||||||
|
this.open = !this.open;
|
||||||
|
}
|
||||||
|
onPanelClick(e) {
|
||||||
|
if (!this.disabled &&
|
||||||
|
!this.stayOpenOnClick &&
|
||||||
|
$(e.target).is('mdui-menu-item')) {
|
||||||
|
this.open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onContextMenu(e) {
|
||||||
|
if (this.disabled || !this.hasTrigger('contextmenu')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
this.pointerOffsetX = e.offsetX;
|
||||||
|
this.pointerOffsetY = e.offsetY;
|
||||||
|
this.open = true;
|
||||||
|
}
|
||||||
|
onMouseEnter() {
|
||||||
|
// 不做 open 状态的判断,因为可以延时打开和关闭
|
||||||
|
if (this.disabled || !this.hasTrigger('hover')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.clearTimeout(this.closeTimeout);
|
||||||
|
if (this.openDelay) {
|
||||||
|
this.openTimeout = window.setTimeout(() => {
|
||||||
|
this.open = true;
|
||||||
|
}, this.openDelay);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.open = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMouseLeave() {
|
||||||
|
// 不做 open 状态的判断,因为可以延时打开和关闭
|
||||||
|
if (this.disabled || !this.hasTrigger('hover')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.clearTimeout(this.openTimeout);
|
||||||
|
this.closeTimeout = window.setTimeout(() => {
|
||||||
|
this.open = false;
|
||||||
|
}, this.closeDelay || 50);
|
||||||
|
}
|
||||||
|
// 更新 panel 的位置
|
||||||
|
updatePositioner() {
|
||||||
|
const $panel = $(this.panelRef.value);
|
||||||
|
const $window = $(window);
|
||||||
|
const panelElements = this.panelElements;
|
||||||
|
const panelRect = {
|
||||||
|
width: Math.max(...(panelElements?.map((panel) => panel.offsetWidth) ?? [])),
|
||||||
|
height: panelElements
|
||||||
|
?.map((panel) => panel.offsetHeight)
|
||||||
|
.reduce((total, height) => total + height, 0),
|
||||||
|
};
|
||||||
|
// 在光标位置触发时,假设 triggerElement 的宽高为 0,位置位于光标位置
|
||||||
|
const triggerClientRect = this.triggerElement.getBoundingClientRect();
|
||||||
|
const triggerRect = this.openOnPointer
|
||||||
|
? {
|
||||||
|
top: this.pointerOffsetY + triggerClientRect.top,
|
||||||
|
left: this.pointerOffsetX + triggerClientRect.left,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
}
|
||||||
|
: triggerClientRect;
|
||||||
|
// dropdown 与屏幕边界至少保留 8px 间距
|
||||||
|
const screenMargin = 8;
|
||||||
|
let transformOriginX;
|
||||||
|
let transformOriginY;
|
||||||
|
let top;
|
||||||
|
let left;
|
||||||
|
let placement = this.placement;
|
||||||
|
// 自动判断 dropdown 的方位
|
||||||
|
// 优先级为 bottom>top>right>left,start>center>end
|
||||||
|
if (placement === 'auto') {
|
||||||
|
const windowWidth = $window.width();
|
||||||
|
const windowHeight = $window.height();
|
||||||
|
let position;
|
||||||
|
let alignment;
|
||||||
|
if (windowHeight - triggerRect.top - triggerRect.height >
|
||||||
|
panelRect.height + screenMargin) {
|
||||||
|
// 下方放得下,放下方
|
||||||
|
position = 'bottom';
|
||||||
|
}
|
||||||
|
else if (triggerRect.top > panelRect.height + screenMargin) {
|
||||||
|
// 上方放得下,放上方
|
||||||
|
position = 'top';
|
||||||
|
}
|
||||||
|
else if (windowWidth - triggerRect.left - triggerRect.width >
|
||||||
|
panelRect.width + screenMargin) {
|
||||||
|
// 右侧放得下,放右侧
|
||||||
|
position = 'right';
|
||||||
|
}
|
||||||
|
else if (triggerRect.left > panelRect.width + screenMargin) {
|
||||||
|
// 左侧放得下,放左侧
|
||||||
|
position = 'left';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 默认放下方
|
||||||
|
position = 'bottom';
|
||||||
|
}
|
||||||
|
if (['top', 'bottom'].includes(position)) {
|
||||||
|
if (windowWidth - triggerRect.left > panelRect.width + screenMargin) {
|
||||||
|
// 左对齐放得下,左对齐
|
||||||
|
alignment = 'start';
|
||||||
|
}
|
||||||
|
else if (triggerRect.left + triggerRect.width / 2 >
|
||||||
|
panelRect.width / 2 + screenMargin &&
|
||||||
|
windowWidth - triggerRect.left - triggerRect.width / 2 >
|
||||||
|
panelRect.width / 2 + screenMargin) {
|
||||||
|
// 居中对齐放得下,居中对齐
|
||||||
|
alignment = undefined;
|
||||||
|
}
|
||||||
|
else if (triggerRect.left + triggerRect.width >
|
||||||
|
panelRect.width + screenMargin) {
|
||||||
|
// 右对齐放得下,右对齐
|
||||||
|
alignment = 'end';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 默认左对齐
|
||||||
|
alignment = 'start';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (windowHeight - triggerRect.top > panelRect.height + screenMargin) {
|
||||||
|
// 顶部对齐放得下,顶部对齐
|
||||||
|
alignment = 'start';
|
||||||
|
}
|
||||||
|
else if (triggerRect.top + triggerRect.height / 2 >
|
||||||
|
panelRect.height / 2 + screenMargin &&
|
||||||
|
windowHeight - triggerRect.top - triggerRect.height / 2 >
|
||||||
|
panelRect.height / 2 + screenMargin) {
|
||||||
|
// 居中对齐放得下,居中对齐
|
||||||
|
alignment = undefined;
|
||||||
|
}
|
||||||
|
else if (triggerRect.top + triggerRect.height >
|
||||||
|
panelRect.height + screenMargin) {
|
||||||
|
// 底部对齐放得下,底部对齐
|
||||||
|
alignment = 'end';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 默认顶部对齐
|
||||||
|
alignment = 'start';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
placement = alignment
|
||||||
|
? [position, alignment].join('-')
|
||||||
|
: position;
|
||||||
|
}
|
||||||
|
// 根据 placement 计算 panel 的位置和方向
|
||||||
|
const [position, alignment] = placement.split('-');
|
||||||
|
this.animateDirection = ['top', 'bottom'].includes(position)
|
||||||
|
? 'vertical'
|
||||||
|
: 'horizontal';
|
||||||
|
switch (position) {
|
||||||
|
case 'top':
|
||||||
|
transformOriginY = 'bottom';
|
||||||
|
top = triggerRect.top - panelRect.height;
|
||||||
|
break;
|
||||||
|
case 'bottom':
|
||||||
|
transformOriginY = 'top';
|
||||||
|
top = triggerRect.top + triggerRect.height;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
transformOriginY = 'center';
|
||||||
|
switch (alignment) {
|
||||||
|
case 'start':
|
||||||
|
top = triggerRect.top;
|
||||||
|
break;
|
||||||
|
case 'end':
|
||||||
|
top = triggerRect.top + triggerRect.height - panelRect.height;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
top =
|
||||||
|
triggerRect.top + triggerRect.height / 2 - panelRect.height / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (position) {
|
||||||
|
case 'left':
|
||||||
|
transformOriginX = 'right';
|
||||||
|
left = triggerRect.left - panelRect.width;
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
transformOriginX = 'left';
|
||||||
|
left = triggerRect.left + triggerRect.width;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
transformOriginX = 'center';
|
||||||
|
switch (alignment) {
|
||||||
|
case 'start':
|
||||||
|
left = triggerRect.left;
|
||||||
|
break;
|
||||||
|
case 'end':
|
||||||
|
left = triggerRect.left + triggerRect.width - panelRect.width;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
left =
|
||||||
|
triggerRect.left + triggerRect.width / 2 - panelRect.width / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$panel.css({
|
||||||
|
top,
|
||||||
|
left,
|
||||||
|
transformOrigin: [transformOriginX, transformOriginY].join(' '),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Dropdown.styles = [componentStyle, style];
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Dropdown.prototype, "open", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
})
|
||||||
|
], Dropdown.prototype, "disabled", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Dropdown.prototype, "trigger", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ reflect: true })
|
||||||
|
], Dropdown.prototype, "placement", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
attribute: 'stay-open-on-click',
|
||||||
|
})
|
||||||
|
], Dropdown.prototype, "stayOpenOnClick", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ type: Number, reflect: true, attribute: 'open-delay' })
|
||||||
|
], Dropdown.prototype, "openDelay", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({ type: Number, reflect: true, attribute: 'close-delay' })
|
||||||
|
], Dropdown.prototype, "closeDelay", void 0);
|
||||||
|
__decorate([
|
||||||
|
property({
|
||||||
|
type: Boolean,
|
||||||
|
reflect: true,
|
||||||
|
converter: booleanConverter,
|
||||||
|
attribute: 'open-on-pointer',
|
||||||
|
})
|
||||||
|
], Dropdown.prototype, "openOnPointer", void 0);
|
||||||
|
__decorate([
|
||||||
|
queryAssignedElements({ slot: 'trigger', flatten: true })
|
||||||
|
], Dropdown.prototype, "triggerElements", void 0);
|
||||||
|
__decorate([
|
||||||
|
queryAssignedElements({ flatten: true })
|
||||||
|
], Dropdown.prototype, "panelElements", void 0);
|
||||||
|
__decorate([
|
||||||
|
watch('placement', true),
|
||||||
|
watch('openOnPointer', true)
|
||||||
|
], Dropdown.prototype, "onPositionChange", null);
|
||||||
|
__decorate([
|
||||||
|
watch('open')
|
||||||
|
], Dropdown.prototype, "onOpenChange", null);
|
||||||
|
Dropdown = __decorate([
|
||||||
|
customElement('mdui-dropdown')
|
||||||
|
], Dropdown);
|
||||||
|
export { Dropdown };
|
||||||
1
client/mdui_patched/components/dropdown/style.d.ts
vendored
Normal file
1
client/mdui_patched/components/dropdown/style.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const style: import("lit").CSSResult;
|
||||||
2
client/mdui_patched/components/dropdown/style.js
Normal file
2
client/mdui_patched/components/dropdown/style.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { css } from 'lit';
|
||||||
|
export const style = css `:host{--z-index:2100;display:contents}.panel{display:block;position:fixed;z-index:var(--z-index)}`;
|
||||||
1
client/mdui_patched/components/fab.d.ts
vendored
Normal file
1
client/mdui_patched/components/fab.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './fab/index.js';
|
||||||
1
client/mdui_patched/components/fab.js
Normal file
1
client/mdui_patched/components/fab.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './fab/index.js';
|
||||||
76
client/mdui_patched/components/fab/index.d.ts
vendored
Normal file
76
client/mdui_patched/components/fab/index.d.ts
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { ButtonBase } from '../button/button-base.js';
|
||||||
|
import '../icon.js';
|
||||||
|
import type { Ripple } from '../ripple/index.js';
|
||||||
|
import type { TemplateResult, CSSResultGroup } from 'lit';
|
||||||
|
/**
|
||||||
|
* @summary 浮动操作按钮组件
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <mdui-fab icon="edit"></mdui-fab>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @event focus - 获得焦点时触发
|
||||||
|
* @event blur - 失去焦点时触发
|
||||||
|
* @event invalid - 表单字段验证未通过时触发
|
||||||
|
*
|
||||||
|
* @slot - 文本
|
||||||
|
* @slot icon - 图标
|
||||||
|
*
|
||||||
|
* @csspart button - 内部的 `<button>` 或 `<a>` 元素
|
||||||
|
* @csspart label - 右侧的文本
|
||||||
|
* @csspart icon - 左侧的图标
|
||||||
|
* @csspart loading - 加载中状态的 `<mdui-circular-progress>` 元素
|
||||||
|
*
|
||||||
|
* @cssprop --shape-corner-small - `size="small"` 时,组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
* @cssprop --shape-corner-normal - `size="normal"` 时,组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
* @cssprop --shape-corner-large - `size="large"` 时,组件的圆角大小。可以指定一个具体的像素值;但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
|
||||||
|
*/
|
||||||
|
export declare class Fab extends ButtonBase<FabEventMap> {
|
||||||
|
static styles: CSSResultGroup;
|
||||||
|
/**
|
||||||
|
* FAB 的形状,此组件的不同形状之间只有颜色不一样。可选值包括:
|
||||||
|
*
|
||||||
|
* * `primary`:使用 Primary container 背景色
|
||||||
|
* * `surface`:使用 Surface container high 背景色
|
||||||
|
* * `secondary`:使用 Secondary container 背景色
|
||||||
|
* * `tertiary`:使用 Tertiary container 背景色
|
||||||
|
*/
|
||||||
|
variant: /*使用 Primary container 背景色*/ 'primary' | /*使用 Surface container high 背景色*/ 'surface' | /*使用 Secondary container 背景色*/ 'secondary' | /*使用 Tertiary container 背景色*/ 'tertiary';
|
||||||
|
/**
|
||||||
|
* FAB 的大小。可选值包括:
|
||||||
|
* * `normal`:普通大小 FAB
|
||||||
|
* * `small`:小型 FAB
|
||||||
|
* * `large`:大型 FAB
|
||||||
|
*/
|
||||||
|
size: /*普通大小 FAB*/ 'normal' | /*小型 FAB*/ 'small' | /*大型 FAB*/ 'large';
|
||||||
|
/**
|
||||||
|
* Material Icons 图标名。也可以通过 `slot="icon"` 设置
|
||||||
|
*/
|
||||||
|
icon?: string;
|
||||||
|
/**
|
||||||
|
* 是否为展开状态
|
||||||
|
*/
|
||||||
|
extended: boolean;
|
||||||
|
private readonly rippleRef;
|
||||||
|
private readonly hasSlotController;
|
||||||
|
private readonly definedController;
|
||||||
|
protected get rippleElement(): Ripple;
|
||||||
|
/**
|
||||||
|
* extended 变更时,设置动画
|
||||||
|
*/
|
||||||
|
private onExtendedChange;
|
||||||
|
protected render(): TemplateResult;
|
||||||
|
private renderLabel;
|
||||||
|
private renderIcon;
|
||||||
|
private renderInner;
|
||||||
|
}
|
||||||
|
export interface FabEventMap {
|
||||||
|
focus: FocusEvent;
|
||||||
|
blur: FocusEvent;
|
||||||
|
invalid: CustomEvent<void>;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'mdui-fab': Fab;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user