wok-ui-ext 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.en.md +75 -0
- package/README.md +75 -0
- package/dist/alert/index.d.ts +10 -0
- package/dist/alert/index.js +48 -0
- package/dist/alert/style.css +116 -0
- package/dist/avatar/index.d.ts +9 -0
- package/dist/avatar/index.js +30 -0
- package/dist/avatar/style.css +32 -0
- package/dist/badge/index.d.ts +32 -0
- package/dist/badge/index.js +61 -0
- package/dist/badge/style.css +68 -0
- package/dist/breadcrumb/index.d.ts +25 -0
- package/dist/breadcrumb/index.js +47 -0
- package/dist/breadcrumb/style.css +35 -0
- package/dist/button/index.d.ts +34 -0
- package/dist/button/index.js +57 -0
- package/dist/button/style.css +133 -0
- package/dist/card/index.d.ts +25 -0
- package/dist/card/index.js +29 -0
- package/dist/card/style.css +23 -0
- package/dist/collapse/index.d.ts +89 -0
- package/dist/collapse/index.js +213 -0
- package/dist/collapse/style.css +76 -0
- package/dist/context-menu/context-menu.d.ts +113 -0
- package/dist/context-menu/context-menu.js +150 -0
- package/dist/context-menu/index.d.ts +30 -0
- package/dist/context-menu/index.js +42 -0
- package/dist/context-menu/style.css +46 -0
- package/dist/date-picker/calendar-panel.d.ts +44 -0
- package/dist/date-picker/calendar-panel.js +180 -0
- package/dist/date-picker/date-picker.d.ts +29 -0
- package/dist/date-picker/date-picker.js +81 -0
- package/dist/date-picker/date-utils.d.ts +63 -0
- package/dist/date-picker/date-utils.js +130 -0
- package/dist/date-picker/index.d.ts +7 -0
- package/dist/date-picker/index.js +5 -0
- package/dist/date-picker/layer.d.ts +19 -0
- package/dist/date-picker/layer.js +36 -0
- package/dist/date-picker/range-picker.d.ts +30 -0
- package/dist/date-picker/range-picker.js +117 -0
- package/dist/date-picker/style.css +161 -0
- package/dist/description-list/index.d.ts +37 -0
- package/dist/description-list/index.js +145 -0
- package/dist/description-list/style.css +63 -0
- package/dist/divider/index.d.ts +29 -0
- package/dist/divider/index.js +29 -0
- package/dist/divider/style.css +50 -0
- package/dist/drawer/index.d.ts +51 -0
- package/dist/drawer/index.js +183 -0
- package/dist/drawer/style.css +100 -0
- package/dist/dropdown/index.d.ts +38 -0
- package/dist/dropdown/index.js +97 -0
- package/dist/dropdown/style.css +39 -0
- package/dist/empty/index.d.ts +11 -0
- package/dist/empty/index.js +20 -0
- package/dist/empty/style.css +19 -0
- package/dist/floating-layer/index.d.ts +109 -0
- package/dist/floating-layer/index.js +337 -0
- package/dist/floating-layer/style.css +5 -0
- package/dist/form/form-item/index.d.ts +26 -0
- package/dist/form/form-item/index.js +21 -0
- package/dist/form/form-item/style.css +33 -0
- package/dist/form/form-modal/index.d.ts +18 -0
- package/dist/form/form-modal/index.js +38 -0
- package/dist/form/form.d.ts +21 -0
- package/dist/form/form.js +28 -0
- package/dist/form/index.d.ts +4 -0
- package/dist/form/index.js +4 -0
- package/dist/form/select/index.d.ts +48 -0
- package/dist/form/select/index.js +94 -0
- package/dist/form/select/style.css +29 -0
- package/dist/global.css +71 -0
- package/dist/i18n/de-DE.d.ts +2 -0
- package/dist/i18n/de-DE.js +36 -0
- package/dist/i18n/en-US.d.ts +2 -0
- package/dist/i18n/en-US.js +36 -0
- package/dist/i18n/es-ES.d.ts +2 -0
- package/dist/i18n/es-ES.js +36 -0
- package/dist/i18n/fr-FR.d.ts +2 -0
- package/dist/i18n/fr-FR.js +36 -0
- package/dist/i18n/index.d.ts +12 -0
- package/dist/i18n/index.js +39 -0
- package/dist/i18n/ja-JP.d.ts +2 -0
- package/dist/i18n/ja-JP.js +36 -0
- package/dist/i18n/ko-KR.d.ts +2 -0
- package/dist/i18n/ko-KR.js +36 -0
- package/dist/i18n/messages.d.ts +40 -0
- package/dist/i18n/messages.js +1 -0
- package/dist/i18n/wok-ui/de-DE.d.ts +2 -0
- package/dist/i18n/wok-ui/de-DE.js +20 -0
- package/dist/i18n/wok-ui/es-ES.d.ts +2 -0
- package/dist/i18n/wok-ui/es-ES.js +20 -0
- package/dist/i18n/wok-ui/fr-FR.d.ts +2 -0
- package/dist/i18n/wok-ui/fr-FR.js +20 -0
- package/dist/i18n/wok-ui/ja-JP.d.ts +2 -0
- package/dist/i18n/wok-ui/ja-JP.js +20 -0
- package/dist/i18n/wok-ui/ko-KR.d.ts +2 -0
- package/dist/i18n/wok-ui/ko-KR.js +20 -0
- package/dist/i18n/wok-ui/zh-TW.d.ts +2 -0
- package/dist/i18n/wok-ui/zh-TW.js +20 -0
- package/dist/i18n/zh-CN.d.ts +2 -0
- package/dist/i18n/zh-CN.js +36 -0
- package/dist/i18n/zh-TW.d.ts +2 -0
- package/dist/i18n/zh-TW.js +36 -0
- package/dist/icons.d.ts +88 -0
- package/dist/icons.js +176 -0
- package/dist/image/index.d.ts +38 -0
- package/dist/image/index.js +97 -0
- package/dist/image/style.css +72 -0
- package/dist/image-preview/index.d.ts +26 -0
- package/dist/image-preview/index.js +134 -0
- package/dist/image-preview/style.css +96 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +45 -0
- package/dist/list/index.d.ts +32 -0
- package/dist/list/index.js +42 -0
- package/dist/list/list-item.d.ts +27 -0
- package/dist/list/list-item.js +51 -0
- package/dist/list/style.css +20 -0
- package/dist/loading/index.d.ts +10 -0
- package/dist/loading/index.js +46 -0
- package/dist/loading/style.css +48 -0
- package/dist/menu/index.d.ts +38 -0
- package/dist/menu/index.js +62 -0
- package/dist/menu/leaf-item.d.ts +12 -0
- package/dist/menu/leaf-item.js +41 -0
- package/dist/menu/style.css +117 -0
- package/dist/menu/sub-menu.d.ts +21 -0
- package/dist/menu/sub-menu.js +85 -0
- package/dist/menu/types.d.ts +36 -0
- package/dist/menu/types.js +1 -0
- package/dist/menu/utils.d.ts +11 -0
- package/dist/menu/utils.js +24 -0
- package/dist/modal/index.d.ts +23 -0
- package/dist/modal/index.js +79 -0
- package/dist/modal/modal.d.ts +76 -0
- package/dist/modal/modal.js +188 -0
- package/dist/modal/style.css +90 -0
- package/dist/pagination/index.d.ts +31 -0
- package/dist/pagination/index.js +215 -0
- package/dist/pagination/style.css +50 -0
- package/dist/popconfirm/api.d.ts +43 -0
- package/dist/popconfirm/api.js +77 -0
- package/dist/popconfirm/component.d.ts +47 -0
- package/dist/popconfirm/component.js +48 -0
- package/dist/popconfirm/index.d.ts +3 -0
- package/dist/popconfirm/index.js +3 -0
- package/dist/popconfirm/style.css +22 -0
- package/dist/popover/api.d.ts +36 -0
- package/dist/popover/api.js +61 -0
- package/dist/popover/component.d.ts +38 -0
- package/dist/popover/component.js +51 -0
- package/dist/popover/index.d.ts +3 -0
- package/dist/popover/index.js +3 -0
- package/dist/popover/style.css +102 -0
- package/dist/progress/index.d.ts +34 -0
- package/dist/progress/index.js +70 -0
- package/dist/progress/style.css +37 -0
- package/dist/quick-input/index.d.ts +2 -0
- package/dist/quick-input/index.js +2 -0
- package/dist/quick-input/number.d.ts +35 -0
- package/dist/quick-input/number.js +68 -0
- package/dist/quick-input/text.d.ts +20 -0
- package/dist/quick-input/text.js +61 -0
- package/dist/result/index.d.ts +39 -0
- package/dist/result/index.js +71 -0
- package/dist/result/style.css +41 -0
- package/dist/search-box/index.d.ts +26 -0
- package/dist/search-box/index.js +60 -0
- package/dist/search-box/style.css +22 -0
- package/dist/segmented/index.d.ts +42 -0
- package/dist/segmented/index.js +61 -0
- package/dist/segmented/style.css +28 -0
- package/dist/skeleton/index.d.ts +22 -0
- package/dist/skeleton/index.js +68 -0
- package/dist/skeleton/style.css +31 -0
- package/dist/steps/index.d.ts +46 -0
- package/dist/steps/index.js +95 -0
- package/dist/steps/style.css +181 -0
- package/dist/switch/index.d.ts +32 -0
- package/dist/switch/index.js +74 -0
- package/dist/switch/style.css +52 -0
- package/dist/table/body.d.ts +21 -0
- package/dist/table/body.js +35 -0
- package/dist/table/column.d.ts +78 -0
- package/dist/table/column.js +113 -0
- package/dist/table/header.d.ts +13 -0
- package/dist/table/header.js +15 -0
- package/dist/table/index.d.ts +41 -0
- package/dist/table/index.js +84 -0
- package/dist/table/row.d.ts +25 -0
- package/dist/table/row.js +74 -0
- package/dist/table/style.css +98 -0
- package/dist/tabs/index.d.ts +49 -0
- package/dist/tabs/index.js +76 -0
- package/dist/tabs/style.css +41 -0
- package/dist/tag/index.d.ts +53 -0
- package/dist/tag/index.js +100 -0
- package/dist/tag/style.css +134 -0
- package/dist/text/index.d.ts +104 -0
- package/dist/text/index.js +143 -0
- package/dist/text/style.css +113 -0
- package/dist/theme/index.d.ts +14 -0
- package/dist/theme/index.js +94 -0
- package/dist/theme/themes.d.ts +3 -0
- package/dist/theme/themes.js +323 -0
- package/dist/theme/types.d.ts +58 -0
- package/dist/theme/types.js +1 -0
- package/dist/timeline/index.d.ts +28 -0
- package/dist/timeline/index.js +35 -0
- package/dist/timeline/style.css +53 -0
- package/dist/toast/index.d.ts +53 -0
- package/dist/toast/index.js +131 -0
- package/dist/toast/style.css +76 -0
- package/dist/tooltip/api.d.ts +44 -0
- package/dist/tooltip/api.js +103 -0
- package/dist/tooltip/component.d.ts +28 -0
- package/dist/tooltip/component.js +35 -0
- package/dist/tooltip/index.d.ts +3 -0
- package/dist/tooltip/index.js +3 -0
- package/dist/tooltip/style.css +46 -0
- package/dist/tree/dragging-img.d.ts +13 -0
- package/dist/tree/dragging-img.js +23 -0
- package/dist/tree/index.d.ts +180 -0
- package/dist/tree/index.js +385 -0
- package/dist/tree/node-info.d.ts +63 -0
- package/dist/tree/node-info.js +128 -0
- package/dist/tree/node.d.ts +132 -0
- package/dist/tree/node.js +264 -0
- package/dist/tree/style.css +99 -0
- package/dist/upload/index.d.ts +48 -0
- package/dist/upload/index.js +141 -0
- package/dist/upload/style.css +41 -0
- package/package.json +51 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
.wok-ui-ext-collapse {
|
|
2
|
+
border: 1px solid var(--border-light);
|
|
3
|
+
border-radius: var(--radius-sm);
|
|
4
|
+
overflow: hidden;
|
|
5
|
+
background: var(--bg-card);
|
|
6
|
+
}
|
|
7
|
+
.wok-ui-ext-collapse .wok-ui-ext-panel {
|
|
8
|
+
border-bottom: 1px solid var(--border-light);
|
|
9
|
+
}
|
|
10
|
+
.wok-ui-ext-collapse .wok-ui-ext-panel:last-child {
|
|
11
|
+
border-bottom: none;
|
|
12
|
+
}
|
|
13
|
+
.wok-ui-ext-collapse.ghost {
|
|
14
|
+
border: none;
|
|
15
|
+
background: none;
|
|
16
|
+
}
|
|
17
|
+
.wok-ui-ext-collapse.ghost .wok-ui-ext-panel {
|
|
18
|
+
border-bottom: 1px solid var(--border-light);
|
|
19
|
+
}
|
|
20
|
+
.wok-ui-ext-collapse.ghost .wok-ui-ext-panel .wok-ui-ext-panel-header {
|
|
21
|
+
background: transparent;
|
|
22
|
+
}
|
|
23
|
+
.wok-ui-ext-panel .wok-ui-ext-panel-header {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
padding: 12px 16px;
|
|
27
|
+
background: var(--bg-content);
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
user-select: none;
|
|
30
|
+
transition: background 0.15s;
|
|
31
|
+
gap: 8px;
|
|
32
|
+
}
|
|
33
|
+
.wok-ui-ext-panel .wok-ui-ext-panel-header:hover {
|
|
34
|
+
background: var(--bg-hover);
|
|
35
|
+
}
|
|
36
|
+
.wok-ui-ext-panel .wok-ui-ext-panel-header.disabled {
|
|
37
|
+
cursor: not-allowed;
|
|
38
|
+
opacity: 0.5;
|
|
39
|
+
}
|
|
40
|
+
.wok-ui-ext-panel .wok-ui-ext-panel-header .wok-ui-ext-panel-header-title {
|
|
41
|
+
flex: 1;
|
|
42
|
+
font-size: var(--font-size-normal);
|
|
43
|
+
color: var(--text-primary);
|
|
44
|
+
font-weight: 500;
|
|
45
|
+
}
|
|
46
|
+
.wok-ui-ext-panel .wok-ui-ext-panel-header .wok-ui-ext-panel-header-arrow {
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
justify-content: center;
|
|
50
|
+
width: 20px;
|
|
51
|
+
height: 20px;
|
|
52
|
+
color: var(--text-tertiary);
|
|
53
|
+
font-size: 16px;
|
|
54
|
+
transition: transform 0.2s;
|
|
55
|
+
}
|
|
56
|
+
.wok-ui-ext-panel .wok-ui-ext-panel-header .wok-ui-ext-panel-header-arrow svg {
|
|
57
|
+
width: 16px;
|
|
58
|
+
height: 16px;
|
|
59
|
+
}
|
|
60
|
+
.wok-ui-ext-panel.open .wok-ui-ext-panel-header-arrow {
|
|
61
|
+
transform: rotate(180deg);
|
|
62
|
+
}
|
|
63
|
+
.wok-ui-ext-panel .wok-ui-ext-panel-body-wrapper {
|
|
64
|
+
max-height: 0;
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
transition: max-height 0.25s ease;
|
|
67
|
+
}
|
|
68
|
+
.wok-ui-ext-panel .wok-ui-ext-panel-body-wrapper .wok-ui-ext-panel-body {
|
|
69
|
+
padding: 0 16px 12px;
|
|
70
|
+
font-size: var(--font-size-normal);
|
|
71
|
+
color: var(--text-secondary);
|
|
72
|
+
line-height: 1.6;
|
|
73
|
+
}
|
|
74
|
+
.wok-ui-ext-panel.open .wok-ui-ext-panel-body-wrapper {
|
|
75
|
+
max-height: 1000px;
|
|
76
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ConvertibleModule } from 'wok-ui';
|
|
2
|
+
import { FloatingLayer } from '../floating-layer';
|
|
3
|
+
import { IconInput } from '../icons';
|
|
4
|
+
/**
|
|
5
|
+
* 菜单位置模式。
|
|
6
|
+
* - 'cursor': 跟随指针(默认)
|
|
7
|
+
* - 'top' | 'bottom' | 'left' | 'right': 锚定在触发元素的四边,空间不够时会自动调整
|
|
8
|
+
*/
|
|
9
|
+
export type MenuPosition = 'cursor' | 'top' | 'bottom' | 'left' | 'right';
|
|
10
|
+
/**
|
|
11
|
+
* 菜单在次轴上的对齐方式。
|
|
12
|
+
* - 'start': top/bottom 时左对齐,left/right 时顶对齐(默认)
|
|
13
|
+
* - 'end': top/bottom 时右对齐,left/right 时底对齐
|
|
14
|
+
*/
|
|
15
|
+
export type MenuAlign = 'start' | 'end';
|
|
16
|
+
/**
|
|
17
|
+
* 上下文菜单条目.
|
|
18
|
+
*/
|
|
19
|
+
export interface ContextMenuItem {
|
|
20
|
+
/**
|
|
21
|
+
* 菜单图标,可选.
|
|
22
|
+
*/
|
|
23
|
+
icon?: IconInput;
|
|
24
|
+
/**
|
|
25
|
+
* 菜单标题,太长会显示省略号.
|
|
26
|
+
*/
|
|
27
|
+
label: ConvertibleModule;
|
|
28
|
+
/**
|
|
29
|
+
* 是否是激活状态,可选.激活状态的菜单会显示为选中状态.不会触发点击回调.
|
|
30
|
+
*/
|
|
31
|
+
active?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* 菜单点击后的回调.
|
|
34
|
+
*/
|
|
35
|
+
callback?: () => void;
|
|
36
|
+
/**
|
|
37
|
+
* 子菜单,如果有值,则点击菜单会继续显示子菜单.如果没值,则点击菜单后会关闭掉上下文菜单.
|
|
38
|
+
*/
|
|
39
|
+
children?: ContextMenuItem[];
|
|
40
|
+
}
|
|
41
|
+
interface ContextMenuOpts {
|
|
42
|
+
/**
|
|
43
|
+
* 父菜单
|
|
44
|
+
*/
|
|
45
|
+
parent?: ContextMenu;
|
|
46
|
+
/**
|
|
47
|
+
* 菜单
|
|
48
|
+
*/
|
|
49
|
+
menu: ContextMenuItem[];
|
|
50
|
+
/**
|
|
51
|
+
* 事件,用于定位和阻止冒泡
|
|
52
|
+
*/
|
|
53
|
+
evt: MouseEvent;
|
|
54
|
+
/**
|
|
55
|
+
* 菜单位置模式,默认 'cursor'
|
|
56
|
+
*/
|
|
57
|
+
position?: MenuPosition;
|
|
58
|
+
/**
|
|
59
|
+
* 次轴对齐方式,默认 'start'
|
|
60
|
+
*/
|
|
61
|
+
align?: MenuAlign;
|
|
62
|
+
/**
|
|
63
|
+
* 销毁时的回调
|
|
64
|
+
*/
|
|
65
|
+
onDestroy?: () => void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 上下文菜单,基于 FloatingLayer 实现定位、自动翻转、外部点击/Esc/滚动自动关闭。
|
|
69
|
+
* 上下文菜单可以有无限深的层级。每个上下文菜单中包含了菜单条目列表,
|
|
70
|
+
* 每个条目点击后有可能直接触发回调,也有可能触发弹出子菜单,子菜单是新的上下文菜单实例。
|
|
71
|
+
* 每个上下文菜单之间是单独的模块,独自挂载到 body 上,没有从属关系,内部通过记录关联,适当的进行销毁操作。
|
|
72
|
+
* 每个上下文菜单销毁,都会自动销毁弹出的子菜单。
|
|
73
|
+
*/
|
|
74
|
+
export declare class ContextMenu extends FloatingLayer {
|
|
75
|
+
private readonly opts;
|
|
76
|
+
/**
|
|
77
|
+
* 每个上下文永远只能同一时刻弹出一个子菜单,如果其它条目再次触发弹出子菜单,则销毁已经弹出的子菜单
|
|
78
|
+
*/
|
|
79
|
+
private subMenu?;
|
|
80
|
+
constructor(opts: ContextMenuOpts);
|
|
81
|
+
protected buildContent(): void;
|
|
82
|
+
/**
|
|
83
|
+
* 覆写内部区域判定:子菜单也属于菜单内部,点击子菜单不关闭父菜单。
|
|
84
|
+
* 注意不包含触发元素(target),点击触发元素同样视为外部点击并关闭菜单。
|
|
85
|
+
*/
|
|
86
|
+
protected containsTarget(target: Node): boolean;
|
|
87
|
+
getParent(): ContextMenu | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* 获取根菜单模块
|
|
90
|
+
* @returns
|
|
91
|
+
*/
|
|
92
|
+
getRoot(): ContextMenu;
|
|
93
|
+
/**
|
|
94
|
+
* 判定指定的元素是否在模块内部,如果在子菜单的模块中,也算包含
|
|
95
|
+
* @param el
|
|
96
|
+
*/
|
|
97
|
+
containsEl(el: HTMLElement): boolean;
|
|
98
|
+
destroy(): void;
|
|
99
|
+
/**
|
|
100
|
+
* 根据菜单位置模式解析 FloatingLayer 的定位参数。
|
|
101
|
+
* cursor 模式用点击坐标作为锚点;锚定模式用触发元素定位。
|
|
102
|
+
*/
|
|
103
|
+
private static __resolveLayerOpts;
|
|
104
|
+
/**
|
|
105
|
+
* position + align 映射为 FloatingLayer 的 placement。
|
|
106
|
+
*/
|
|
107
|
+
private static __resolvePlacement;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @deprecated 拼写错误,请使用 ContextMenu
|
|
111
|
+
*/
|
|
112
|
+
export declare const ConextMenu: typeof ContextMenu;
|
|
113
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { FloatingLayer } from '../floating-layer';
|
|
2
|
+
import { resolveIcon, IconAngleRight } from '../icons';
|
|
3
|
+
/**
|
|
4
|
+
* 上下文菜单,基于 FloatingLayer 实现定位、自动翻转、外部点击/Esc/滚动自动关闭。
|
|
5
|
+
* 上下文菜单可以有无限深的层级。每个上下文菜单中包含了菜单条目列表,
|
|
6
|
+
* 每个条目点击后有可能直接触发回调,也有可能触发弹出子菜单,子菜单是新的上下文菜单实例。
|
|
7
|
+
* 每个上下文菜单之间是单独的模块,独自挂载到 body 上,没有从属关系,内部通过记录关联,适当的进行销毁操作。
|
|
8
|
+
* 每个上下文菜单销毁,都会自动销毁弹出的子菜单。
|
|
9
|
+
*/
|
|
10
|
+
export class ContextMenu extends FloatingLayer {
|
|
11
|
+
constructor(opts) {
|
|
12
|
+
super(ContextMenu.__resolveLayerOpts(opts));
|
|
13
|
+
this.opts = opts;
|
|
14
|
+
this.el.classList.add('wok-ui-ext-context-menu');
|
|
15
|
+
}
|
|
16
|
+
buildContent() {
|
|
17
|
+
for (const item of this.opts.menu) {
|
|
18
|
+
this.addChild({
|
|
19
|
+
classNames: ['wok-ui-ext-context-menu-item', item.active ? 'active' : ''],
|
|
20
|
+
children: add => {
|
|
21
|
+
if (item.icon) {
|
|
22
|
+
add({
|
|
23
|
+
classNames: 'icon',
|
|
24
|
+
children: resolveIcon(item.icon)
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
add({
|
|
28
|
+
classNames: 'text',
|
|
29
|
+
children: item.label
|
|
30
|
+
});
|
|
31
|
+
if (item.children && item.children.length) {
|
|
32
|
+
add(new IconAngleRight());
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
onClick: ev => {
|
|
36
|
+
ev.stopPropagation();
|
|
37
|
+
if (item.children && item.children.length) {
|
|
38
|
+
if (this.subMenu) {
|
|
39
|
+
this.subMenu.destroy();
|
|
40
|
+
}
|
|
41
|
+
this.subMenu = new ContextMenu({
|
|
42
|
+
evt: ev,
|
|
43
|
+
menu: item.children,
|
|
44
|
+
parent: this
|
|
45
|
+
});
|
|
46
|
+
this.subMenu.mount(document.body);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// 销毁所有上下文菜单,销毁根菜单,就会自动销毁所有
|
|
50
|
+
this.getRoot().destroy();
|
|
51
|
+
if (item.callback) {
|
|
52
|
+
item.callback();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// 菜单内部滚动不冒泡,避免触发外层滚动相关逻辑
|
|
58
|
+
this.el.onscroll = ev => ev.stopPropagation();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 覆写内部区域判定:子菜单也属于菜单内部,点击子菜单不关闭父菜单。
|
|
62
|
+
* 注意不包含触发元素(target),点击触发元素同样视为外部点击并关闭菜单。
|
|
63
|
+
*/
|
|
64
|
+
containsTarget(target) {
|
|
65
|
+
if (this.el.contains(target))
|
|
66
|
+
return true;
|
|
67
|
+
if (this.subMenu && this.subMenu.containsTarget(target))
|
|
68
|
+
return true;
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
getParent() {
|
|
72
|
+
return this.opts.parent;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 获取根菜单模块
|
|
76
|
+
* @returns
|
|
77
|
+
*/
|
|
78
|
+
getRoot() {
|
|
79
|
+
let root = this;
|
|
80
|
+
while (true) {
|
|
81
|
+
const parent = root.getParent();
|
|
82
|
+
if (parent) {
|
|
83
|
+
root = parent;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return root;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 判定指定的元素是否在模块内部,如果在子菜单的模块中,也算包含
|
|
93
|
+
* @param el
|
|
94
|
+
*/
|
|
95
|
+
containsEl(el) {
|
|
96
|
+
return this.containsTarget(el);
|
|
97
|
+
}
|
|
98
|
+
destroy() {
|
|
99
|
+
if (this.subMenu) {
|
|
100
|
+
this.subMenu.destroy();
|
|
101
|
+
this.subMenu = undefined;
|
|
102
|
+
}
|
|
103
|
+
this.el.onscroll = null;
|
|
104
|
+
super.destroy();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 根据菜单位置模式解析 FloatingLayer 的定位参数。
|
|
108
|
+
* cursor 模式用点击坐标作为锚点;锚定模式用触发元素定位。
|
|
109
|
+
*/
|
|
110
|
+
static __resolveLayerOpts(opts) {
|
|
111
|
+
const position = opts.position || 'cursor';
|
|
112
|
+
const align = opts.align || 'start';
|
|
113
|
+
const triggerEl = opts.evt.currentTarget instanceof HTMLElement ? opts.evt.currentTarget : null;
|
|
114
|
+
// 跟随指针,或触发元素不可用时回退到指针定位
|
|
115
|
+
if (position === 'cursor' || !triggerEl) {
|
|
116
|
+
return {
|
|
117
|
+
anchor: { x: opts.evt.clientX, y: opts.evt.clientY },
|
|
118
|
+
placement: 'bottomLeft',
|
|
119
|
+
offset: 0,
|
|
120
|
+
scrollBehavior: 'destroy'
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const placement = ContextMenu.__resolvePlacement(position, align);
|
|
124
|
+
return {
|
|
125
|
+
target: triggerEl,
|
|
126
|
+
placement,
|
|
127
|
+
offset: 0,
|
|
128
|
+
scrollBehavior: 'destroy'
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* position + align 映射为 FloatingLayer 的 placement。
|
|
133
|
+
*/
|
|
134
|
+
static __resolvePlacement(position, align) {
|
|
135
|
+
switch (position) {
|
|
136
|
+
case 'bottom':
|
|
137
|
+
return align === 'end' ? 'bottomRight' : 'bottomLeft';
|
|
138
|
+
case 'top':
|
|
139
|
+
return align === 'end' ? 'topRight' : 'topLeft';
|
|
140
|
+
case 'left':
|
|
141
|
+
return align === 'end' ? 'leftBottom' : 'leftTop';
|
|
142
|
+
case 'right':
|
|
143
|
+
return align === 'end' ? 'rightBottom' : 'rightTop';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* @deprecated 拼写错误,请使用 ContextMenu
|
|
149
|
+
*/
|
|
150
|
+
export const ConextMenu = ContextMenu;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ContextMenuItem, type MenuPosition, type MenuAlign } from './context-menu';
|
|
2
|
+
import './style.less';
|
|
3
|
+
export interface ShowContextMenuOpts {
|
|
4
|
+
/**
|
|
5
|
+
* 菜单
|
|
6
|
+
*/
|
|
7
|
+
menu: ContextMenuItem[] | ((add: (...item: ContextMenuItem[]) => void) => void);
|
|
8
|
+
/**
|
|
9
|
+
* 事件,用于定位
|
|
10
|
+
*/
|
|
11
|
+
evt: MouseEvent;
|
|
12
|
+
/**
|
|
13
|
+
* 菜单位置模式。
|
|
14
|
+
* - 'cursor':跟随指针(默认)
|
|
15
|
+
* - 'top' | 'bottom' | 'left' | 'right':锚定在触发元素的四边,空间不够时会自动调整
|
|
16
|
+
*/
|
|
17
|
+
position?: MenuPosition;
|
|
18
|
+
/**
|
|
19
|
+
* 次轴对齐方式。
|
|
20
|
+
* - 'start':top/bottom 时左对齐,left/right 时顶对齐(默认)
|
|
21
|
+
* - 'end':top/bottom 时右对齐,left/right 时底对齐
|
|
22
|
+
*/
|
|
23
|
+
align?: MenuAlign;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 显示上下文菜单
|
|
27
|
+
* @param opts
|
|
28
|
+
*/
|
|
29
|
+
export declare function showContextMenu(opts: ShowContextMenuOpts): void;
|
|
30
|
+
export * from './context-menu';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ContextMenu } from './context-menu';
|
|
2
|
+
import './style.css';
|
|
3
|
+
// 实例数组
|
|
4
|
+
const instances = [];
|
|
5
|
+
/**
|
|
6
|
+
* 显示上下文菜单
|
|
7
|
+
* @param opts
|
|
8
|
+
*/
|
|
9
|
+
export function showContextMenu(opts) {
|
|
10
|
+
opts.evt.stopPropagation();
|
|
11
|
+
// 先销毁所有实例
|
|
12
|
+
instances.forEach(instance => instance.destroy());
|
|
13
|
+
let menu = [];
|
|
14
|
+
if (Array.isArray(opts.menu)) {
|
|
15
|
+
menu = opts.menu;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const add = (...item) => {
|
|
19
|
+
menu.push(...item);
|
|
20
|
+
};
|
|
21
|
+
opts.menu(add);
|
|
22
|
+
}
|
|
23
|
+
if (!menu.length) {
|
|
24
|
+
console.warn('showContextMenu: menu is empty, skip showing');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const instance = new ContextMenu({
|
|
28
|
+
menu,
|
|
29
|
+
evt: opts.evt,
|
|
30
|
+
position: opts.position,
|
|
31
|
+
align: opts.align,
|
|
32
|
+
onDestroy: () => {
|
|
33
|
+
const index = instances.indexOf(instance);
|
|
34
|
+
if (index !== -1) {
|
|
35
|
+
instances.splice(index, 1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
instance.mount(document.body);
|
|
40
|
+
instances.push(instance);
|
|
41
|
+
}
|
|
42
|
+
export * from './context-menu';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
.wok-ui-ext-floating-layer.wok-ui-ext-context-menu {
|
|
2
|
+
border-radius: var(--radius-lg);
|
|
3
|
+
overflow: auto;
|
|
4
|
+
position: fixed;
|
|
5
|
+
box-shadow: var(--shadow-lg) !important;
|
|
6
|
+
border: 1px solid var(--color-border);
|
|
7
|
+
max-width: 300px;
|
|
8
|
+
max-height: 100vh;
|
|
9
|
+
z-index: 1100;
|
|
10
|
+
}
|
|
11
|
+
.wok-ui-ext-floating-layer.wok-ui-ext-context-menu > .wok-ui-ext-context-menu-item {
|
|
12
|
+
padding: 10px 20px;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
background-color: var(--bg-card);
|
|
15
|
+
border-bottom: 1px solid var(--color-border);
|
|
16
|
+
font-size: var(--font-size-body);
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
gap: 8px;
|
|
20
|
+
}
|
|
21
|
+
.wok-ui-ext-floating-layer.wok-ui-ext-context-menu > .wok-ui-ext-context-menu-item:last-child {
|
|
22
|
+
border-bottom: none;
|
|
23
|
+
}
|
|
24
|
+
.wok-ui-ext-floating-layer.wok-ui-ext-context-menu > .wok-ui-ext-context-menu-item:hover {
|
|
25
|
+
background-color: var(--bg-hover);
|
|
26
|
+
}
|
|
27
|
+
.wok-ui-ext-floating-layer.wok-ui-ext-context-menu > .wok-ui-ext-context-menu-item.active {
|
|
28
|
+
background: var(--color-primary);
|
|
29
|
+
color: white;
|
|
30
|
+
pointer-events: none;
|
|
31
|
+
cursor: default;
|
|
32
|
+
}
|
|
33
|
+
.wok-ui-ext-floating-layer.wok-ui-ext-context-menu > .wok-ui-ext-context-menu-item.active .icon {
|
|
34
|
+
color: white;
|
|
35
|
+
}
|
|
36
|
+
.wok-ui-ext-floating-layer.wok-ui-ext-context-menu > .wok-ui-ext-context-menu-item .text {
|
|
37
|
+
text-overflow: ellipsis;
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
word-break: break-all;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
flex-grow: 1;
|
|
42
|
+
}
|
|
43
|
+
.wok-ui-ext-floating-layer.wok-ui-ext-context-menu > .wok-ui-ext-context-menu-item .icon {
|
|
44
|
+
flex-shrink: 0;
|
|
45
|
+
color: var(--text-muted);
|
|
46
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { FullRenderingModule } from 'wok-ui';
|
|
2
|
+
/**
|
|
3
|
+
* 共享日历面板组件。
|
|
4
|
+
* 负责月/年导航和 42 格日历渲染,不关心单选还是范围选择。
|
|
5
|
+
*/
|
|
6
|
+
export declare class CalendarPanel extends FullRenderingModule {
|
|
7
|
+
private year;
|
|
8
|
+
private month;
|
|
9
|
+
private readonly today;
|
|
10
|
+
private readonly selectedDates;
|
|
11
|
+
private readonly hoveredDate;
|
|
12
|
+
private readonly disabledDate?;
|
|
13
|
+
private readonly onDayClick;
|
|
14
|
+
private readonly onDayHover?;
|
|
15
|
+
private readonly onMonthChange?;
|
|
16
|
+
private readonly dayCellMap;
|
|
17
|
+
constructor(opts: {
|
|
18
|
+
year: number;
|
|
19
|
+
month: number;
|
|
20
|
+
selectedDates: () => Date[];
|
|
21
|
+
hoveredDate?: () => Date | undefined;
|
|
22
|
+
today?: Date;
|
|
23
|
+
disabledDate?: (date: Date) => boolean;
|
|
24
|
+
onDayClick: (date: Date) => void;
|
|
25
|
+
onDayHover?: (date: Date) => void;
|
|
26
|
+
onMonthChange?: (year: number, month: number) => void;
|
|
27
|
+
});
|
|
28
|
+
protected buildContent(): void;
|
|
29
|
+
private buildHeader;
|
|
30
|
+
private buildWeekdays;
|
|
31
|
+
private buildGrid;
|
|
32
|
+
private buildDayCell;
|
|
33
|
+
/**
|
|
34
|
+
* 局部更新 hover 范围预览的 class,不重新渲染整个面板。
|
|
35
|
+
*/
|
|
36
|
+
setHoveredDate(hovered: Date | undefined): void;
|
|
37
|
+
private clearHoverClass;
|
|
38
|
+
private goToPrevMonth;
|
|
39
|
+
private goToNextMonth;
|
|
40
|
+
/**
|
|
41
|
+
* 公开的刷新方法,供外部强制重绘面板(如 hover 预览变化)。
|
|
42
|
+
*/
|
|
43
|
+
refresh(): void;
|
|
44
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { FullRenderingModule } from 'wok-ui';
|
|
2
|
+
import { compareDate, getCalendarDays, isSameDay, MONTH_NAMES, WEEK_DAYS } from './date-utils';
|
|
3
|
+
/**
|
|
4
|
+
* 共享日历面板组件。
|
|
5
|
+
* 负责月/年导航和 42 格日历渲染,不关心单选还是范围选择。
|
|
6
|
+
*/
|
|
7
|
+
export class CalendarPanel extends FullRenderingModule {
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
super('wok-ui-ext-calendar-panel');
|
|
10
|
+
this.dayCellMap = new Map();
|
|
11
|
+
this.year = opts.year;
|
|
12
|
+
this.month = opts.month;
|
|
13
|
+
this.today = opts.today ?? new Date();
|
|
14
|
+
this.selectedDates = opts.selectedDates;
|
|
15
|
+
this.hoveredDate = opts.hoveredDate ?? (() => undefined);
|
|
16
|
+
this.disabledDate = opts.disabledDate;
|
|
17
|
+
this.onDayClick = opts.onDayClick;
|
|
18
|
+
this.onDayHover = opts.onDayHover;
|
|
19
|
+
this.onMonthChange = opts.onMonthChange;
|
|
20
|
+
this.render();
|
|
21
|
+
}
|
|
22
|
+
buildContent() {
|
|
23
|
+
this.dayCellMap.clear();
|
|
24
|
+
this.addChild(this.buildHeader());
|
|
25
|
+
this.addChild(this.buildWeekdays());
|
|
26
|
+
this.addChild(this.buildGrid());
|
|
27
|
+
}
|
|
28
|
+
buildHeader() {
|
|
29
|
+
return {
|
|
30
|
+
classNames: 'wok-ui-ext-calendar-header',
|
|
31
|
+
children: [
|
|
32
|
+
{
|
|
33
|
+
classNames: 'wok-ui-ext-calendar-nav-btn',
|
|
34
|
+
children: '◀',
|
|
35
|
+
onClick: () => this.goToPrevMonth()
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
classNames: 'wok-ui-ext-calendar-month-label',
|
|
39
|
+
children: `${this.year}年 ${MONTH_NAMES[this.month]}`
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
classNames: 'wok-ui-ext-calendar-nav-btn',
|
|
43
|
+
children: '▶',
|
|
44
|
+
onClick: () => this.goToNextMonth()
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
buildWeekdays() {
|
|
50
|
+
return {
|
|
51
|
+
classNames: 'wok-ui-ext-calendar-weekdays',
|
|
52
|
+
children: WEEK_DAYS.map(day => ({
|
|
53
|
+
classNames: 'wok-ui-ext-calendar-weekday',
|
|
54
|
+
children: day
|
|
55
|
+
}))
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
buildGrid() {
|
|
59
|
+
const days = getCalendarDays(this.year, this.month);
|
|
60
|
+
const selected = this.selectedDates();
|
|
61
|
+
const hovered = this.hoveredDate();
|
|
62
|
+
return {
|
|
63
|
+
classNames: 'wok-ui-ext-calendar-grid',
|
|
64
|
+
children: add => {
|
|
65
|
+
for (const day of days) {
|
|
66
|
+
add(this.buildDayCell(day, selected, hovered));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
buildDayCell(day, selected, hovered) {
|
|
72
|
+
const classNames = ['wok-ui-ext-calendar-day'];
|
|
73
|
+
const isToday = isSameDay(day.date, this.today);
|
|
74
|
+
const isDisabled = this.disabledDate?.(day.date) ?? false;
|
|
75
|
+
if (!day.isCurrentMonth) {
|
|
76
|
+
classNames.push('adjacent-month');
|
|
77
|
+
}
|
|
78
|
+
if (isToday) {
|
|
79
|
+
classNames.push('today');
|
|
80
|
+
}
|
|
81
|
+
if (isDisabled) {
|
|
82
|
+
classNames.push('disabled');
|
|
83
|
+
}
|
|
84
|
+
// 选中状态(单选或范围两端)
|
|
85
|
+
const selectedIdx = selected.findIndex(s => isSameDay(s, day.date));
|
|
86
|
+
if (selectedIdx !== -1) {
|
|
87
|
+
classNames.push('selected');
|
|
88
|
+
if (selected.length > 1) {
|
|
89
|
+
classNames.push(selectedIdx === 0 ? 'range-start' : 'range-end');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// 范围内中间状态(仅范围选择)
|
|
93
|
+
if (selected.length === 2 && !isDisabled) {
|
|
94
|
+
const [s1, s2] = selected;
|
|
95
|
+
const cmp1 = compareDate(day.date, s1);
|
|
96
|
+
const cmp2 = compareDate(day.date, s2);
|
|
97
|
+
if (cmp1 > 0 && cmp2 < 0) {
|
|
98
|
+
classNames.push('in-range');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// hover 范围预览
|
|
102
|
+
if (hovered &&
|
|
103
|
+
selected.length === 1 &&
|
|
104
|
+
!isDisabled &&
|
|
105
|
+
classNames.indexOf('selected') === -1) {
|
|
106
|
+
const cmp1 = compareDate(day.date, selected[0]);
|
|
107
|
+
const cmp2 = compareDate(day.date, hovered);
|
|
108
|
+
if ((cmp1 > 0 && cmp2 < 0) || (cmp1 < 0 && cmp2 > 0)) {
|
|
109
|
+
classNames.push('in-range-hover');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
classNames,
|
|
114
|
+
children: String(day.day),
|
|
115
|
+
onClick: isDisabled ? undefined : () => this.onDayClick(day.date),
|
|
116
|
+
postHandle: el => {
|
|
117
|
+
this.dayCellMap.set(day.date.getTime(), el);
|
|
118
|
+
if (!isDisabled && this.onDayHover) {
|
|
119
|
+
el.addEventListener('mouseenter', () => this.onDayHover(day.date));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 局部更新 hover 范围预览的 class,不重新渲染整个面板。
|
|
126
|
+
*/
|
|
127
|
+
setHoveredDate(hovered) {
|
|
128
|
+
const selected = this.selectedDates();
|
|
129
|
+
if (selected.length !== 1 || !hovered) {
|
|
130
|
+
this.clearHoverClass();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const start = selected[0];
|
|
134
|
+
for (const [time, el] of this.dayCellMap) {
|
|
135
|
+
const date = new Date(time);
|
|
136
|
+
const isDisabled = this.disabledDate?.(date) ?? false;
|
|
137
|
+
if (isDisabled || isSameDay(date, start)) {
|
|
138
|
+
el.classList.remove('in-range-hover');
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const cmpStart = compareDate(date, start);
|
|
142
|
+
const cmpHovered = compareDate(date, hovered);
|
|
143
|
+
const inRange = (cmpStart > 0 && cmpHovered < 0) || (cmpStart < 0 && cmpHovered > 0);
|
|
144
|
+
el.classList.toggle('in-range-hover', inRange);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
clearHoverClass() {
|
|
148
|
+
for (const el of this.dayCellMap.values()) {
|
|
149
|
+
el.classList.remove('in-range-hover');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
goToPrevMonth() {
|
|
153
|
+
if (this.month === 0) {
|
|
154
|
+
this.year -= 1;
|
|
155
|
+
this.month = 11;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
this.month -= 1;
|
|
159
|
+
}
|
|
160
|
+
this.render();
|
|
161
|
+
this.onMonthChange?.(this.year, this.month);
|
|
162
|
+
}
|
|
163
|
+
goToNextMonth() {
|
|
164
|
+
if (this.month === 11) {
|
|
165
|
+
this.year += 1;
|
|
166
|
+
this.month = 0;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
this.month += 1;
|
|
170
|
+
}
|
|
171
|
+
this.render();
|
|
172
|
+
this.onMonthChange?.(this.year, this.month);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 公开的刷新方法,供外部强制重绘面板(如 hover 预览变化)。
|
|
176
|
+
*/
|
|
177
|
+
refresh() {
|
|
178
|
+
this.render();
|
|
179
|
+
}
|
|
180
|
+
}
|