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.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +75 -0
  3. package/README.md +75 -0
  4. package/dist/alert/index.d.ts +10 -0
  5. package/dist/alert/index.js +48 -0
  6. package/dist/alert/style.css +116 -0
  7. package/dist/avatar/index.d.ts +9 -0
  8. package/dist/avatar/index.js +30 -0
  9. package/dist/avatar/style.css +32 -0
  10. package/dist/badge/index.d.ts +32 -0
  11. package/dist/badge/index.js +61 -0
  12. package/dist/badge/style.css +68 -0
  13. package/dist/breadcrumb/index.d.ts +25 -0
  14. package/dist/breadcrumb/index.js +47 -0
  15. package/dist/breadcrumb/style.css +35 -0
  16. package/dist/button/index.d.ts +34 -0
  17. package/dist/button/index.js +57 -0
  18. package/dist/button/style.css +133 -0
  19. package/dist/card/index.d.ts +25 -0
  20. package/dist/card/index.js +29 -0
  21. package/dist/card/style.css +23 -0
  22. package/dist/collapse/index.d.ts +89 -0
  23. package/dist/collapse/index.js +213 -0
  24. package/dist/collapse/style.css +76 -0
  25. package/dist/context-menu/context-menu.d.ts +113 -0
  26. package/dist/context-menu/context-menu.js +150 -0
  27. package/dist/context-menu/index.d.ts +30 -0
  28. package/dist/context-menu/index.js +42 -0
  29. package/dist/context-menu/style.css +46 -0
  30. package/dist/date-picker/calendar-panel.d.ts +44 -0
  31. package/dist/date-picker/calendar-panel.js +180 -0
  32. package/dist/date-picker/date-picker.d.ts +29 -0
  33. package/dist/date-picker/date-picker.js +81 -0
  34. package/dist/date-picker/date-utils.d.ts +63 -0
  35. package/dist/date-picker/date-utils.js +130 -0
  36. package/dist/date-picker/index.d.ts +7 -0
  37. package/dist/date-picker/index.js +5 -0
  38. package/dist/date-picker/layer.d.ts +19 -0
  39. package/dist/date-picker/layer.js +36 -0
  40. package/dist/date-picker/range-picker.d.ts +30 -0
  41. package/dist/date-picker/range-picker.js +117 -0
  42. package/dist/date-picker/style.css +161 -0
  43. package/dist/description-list/index.d.ts +37 -0
  44. package/dist/description-list/index.js +145 -0
  45. package/dist/description-list/style.css +63 -0
  46. package/dist/divider/index.d.ts +29 -0
  47. package/dist/divider/index.js +29 -0
  48. package/dist/divider/style.css +50 -0
  49. package/dist/drawer/index.d.ts +51 -0
  50. package/dist/drawer/index.js +183 -0
  51. package/dist/drawer/style.css +100 -0
  52. package/dist/dropdown/index.d.ts +38 -0
  53. package/dist/dropdown/index.js +97 -0
  54. package/dist/dropdown/style.css +39 -0
  55. package/dist/empty/index.d.ts +11 -0
  56. package/dist/empty/index.js +20 -0
  57. package/dist/empty/style.css +19 -0
  58. package/dist/floating-layer/index.d.ts +109 -0
  59. package/dist/floating-layer/index.js +337 -0
  60. package/dist/floating-layer/style.css +5 -0
  61. package/dist/form/form-item/index.d.ts +26 -0
  62. package/dist/form/form-item/index.js +21 -0
  63. package/dist/form/form-item/style.css +33 -0
  64. package/dist/form/form-modal/index.d.ts +18 -0
  65. package/dist/form/form-modal/index.js +38 -0
  66. package/dist/form/form.d.ts +21 -0
  67. package/dist/form/form.js +28 -0
  68. package/dist/form/index.d.ts +4 -0
  69. package/dist/form/index.js +4 -0
  70. package/dist/form/select/index.d.ts +48 -0
  71. package/dist/form/select/index.js +94 -0
  72. package/dist/form/select/style.css +29 -0
  73. package/dist/global.css +71 -0
  74. package/dist/i18n/de-DE.d.ts +2 -0
  75. package/dist/i18n/de-DE.js +36 -0
  76. package/dist/i18n/en-US.d.ts +2 -0
  77. package/dist/i18n/en-US.js +36 -0
  78. package/dist/i18n/es-ES.d.ts +2 -0
  79. package/dist/i18n/es-ES.js +36 -0
  80. package/dist/i18n/fr-FR.d.ts +2 -0
  81. package/dist/i18n/fr-FR.js +36 -0
  82. package/dist/i18n/index.d.ts +12 -0
  83. package/dist/i18n/index.js +39 -0
  84. package/dist/i18n/ja-JP.d.ts +2 -0
  85. package/dist/i18n/ja-JP.js +36 -0
  86. package/dist/i18n/ko-KR.d.ts +2 -0
  87. package/dist/i18n/ko-KR.js +36 -0
  88. package/dist/i18n/messages.d.ts +40 -0
  89. package/dist/i18n/messages.js +1 -0
  90. package/dist/i18n/wok-ui/de-DE.d.ts +2 -0
  91. package/dist/i18n/wok-ui/de-DE.js +20 -0
  92. package/dist/i18n/wok-ui/es-ES.d.ts +2 -0
  93. package/dist/i18n/wok-ui/es-ES.js +20 -0
  94. package/dist/i18n/wok-ui/fr-FR.d.ts +2 -0
  95. package/dist/i18n/wok-ui/fr-FR.js +20 -0
  96. package/dist/i18n/wok-ui/ja-JP.d.ts +2 -0
  97. package/dist/i18n/wok-ui/ja-JP.js +20 -0
  98. package/dist/i18n/wok-ui/ko-KR.d.ts +2 -0
  99. package/dist/i18n/wok-ui/ko-KR.js +20 -0
  100. package/dist/i18n/wok-ui/zh-TW.d.ts +2 -0
  101. package/dist/i18n/wok-ui/zh-TW.js +20 -0
  102. package/dist/i18n/zh-CN.d.ts +2 -0
  103. package/dist/i18n/zh-CN.js +36 -0
  104. package/dist/i18n/zh-TW.d.ts +2 -0
  105. package/dist/i18n/zh-TW.js +36 -0
  106. package/dist/icons.d.ts +88 -0
  107. package/dist/icons.js +176 -0
  108. package/dist/image/index.d.ts +38 -0
  109. package/dist/image/index.js +97 -0
  110. package/dist/image/style.css +72 -0
  111. package/dist/image-preview/index.d.ts +26 -0
  112. package/dist/image-preview/index.js +134 -0
  113. package/dist/image-preview/style.css +96 -0
  114. package/dist/index.d.ts +46 -0
  115. package/dist/index.js +45 -0
  116. package/dist/list/index.d.ts +32 -0
  117. package/dist/list/index.js +42 -0
  118. package/dist/list/list-item.d.ts +27 -0
  119. package/dist/list/list-item.js +51 -0
  120. package/dist/list/style.css +20 -0
  121. package/dist/loading/index.d.ts +10 -0
  122. package/dist/loading/index.js +46 -0
  123. package/dist/loading/style.css +48 -0
  124. package/dist/menu/index.d.ts +38 -0
  125. package/dist/menu/index.js +62 -0
  126. package/dist/menu/leaf-item.d.ts +12 -0
  127. package/dist/menu/leaf-item.js +41 -0
  128. package/dist/menu/style.css +117 -0
  129. package/dist/menu/sub-menu.d.ts +21 -0
  130. package/dist/menu/sub-menu.js +85 -0
  131. package/dist/menu/types.d.ts +36 -0
  132. package/dist/menu/types.js +1 -0
  133. package/dist/menu/utils.d.ts +11 -0
  134. package/dist/menu/utils.js +24 -0
  135. package/dist/modal/index.d.ts +23 -0
  136. package/dist/modal/index.js +79 -0
  137. package/dist/modal/modal.d.ts +76 -0
  138. package/dist/modal/modal.js +188 -0
  139. package/dist/modal/style.css +90 -0
  140. package/dist/pagination/index.d.ts +31 -0
  141. package/dist/pagination/index.js +215 -0
  142. package/dist/pagination/style.css +50 -0
  143. package/dist/popconfirm/api.d.ts +43 -0
  144. package/dist/popconfirm/api.js +77 -0
  145. package/dist/popconfirm/component.d.ts +47 -0
  146. package/dist/popconfirm/component.js +48 -0
  147. package/dist/popconfirm/index.d.ts +3 -0
  148. package/dist/popconfirm/index.js +3 -0
  149. package/dist/popconfirm/style.css +22 -0
  150. package/dist/popover/api.d.ts +36 -0
  151. package/dist/popover/api.js +61 -0
  152. package/dist/popover/component.d.ts +38 -0
  153. package/dist/popover/component.js +51 -0
  154. package/dist/popover/index.d.ts +3 -0
  155. package/dist/popover/index.js +3 -0
  156. package/dist/popover/style.css +102 -0
  157. package/dist/progress/index.d.ts +34 -0
  158. package/dist/progress/index.js +70 -0
  159. package/dist/progress/style.css +37 -0
  160. package/dist/quick-input/index.d.ts +2 -0
  161. package/dist/quick-input/index.js +2 -0
  162. package/dist/quick-input/number.d.ts +35 -0
  163. package/dist/quick-input/number.js +68 -0
  164. package/dist/quick-input/text.d.ts +20 -0
  165. package/dist/quick-input/text.js +61 -0
  166. package/dist/result/index.d.ts +39 -0
  167. package/dist/result/index.js +71 -0
  168. package/dist/result/style.css +41 -0
  169. package/dist/search-box/index.d.ts +26 -0
  170. package/dist/search-box/index.js +60 -0
  171. package/dist/search-box/style.css +22 -0
  172. package/dist/segmented/index.d.ts +42 -0
  173. package/dist/segmented/index.js +61 -0
  174. package/dist/segmented/style.css +28 -0
  175. package/dist/skeleton/index.d.ts +22 -0
  176. package/dist/skeleton/index.js +68 -0
  177. package/dist/skeleton/style.css +31 -0
  178. package/dist/steps/index.d.ts +46 -0
  179. package/dist/steps/index.js +95 -0
  180. package/dist/steps/style.css +181 -0
  181. package/dist/switch/index.d.ts +32 -0
  182. package/dist/switch/index.js +74 -0
  183. package/dist/switch/style.css +52 -0
  184. package/dist/table/body.d.ts +21 -0
  185. package/dist/table/body.js +35 -0
  186. package/dist/table/column.d.ts +78 -0
  187. package/dist/table/column.js +113 -0
  188. package/dist/table/header.d.ts +13 -0
  189. package/dist/table/header.js +15 -0
  190. package/dist/table/index.d.ts +41 -0
  191. package/dist/table/index.js +84 -0
  192. package/dist/table/row.d.ts +25 -0
  193. package/dist/table/row.js +74 -0
  194. package/dist/table/style.css +98 -0
  195. package/dist/tabs/index.d.ts +49 -0
  196. package/dist/tabs/index.js +76 -0
  197. package/dist/tabs/style.css +41 -0
  198. package/dist/tag/index.d.ts +53 -0
  199. package/dist/tag/index.js +100 -0
  200. package/dist/tag/style.css +134 -0
  201. package/dist/text/index.d.ts +104 -0
  202. package/dist/text/index.js +143 -0
  203. package/dist/text/style.css +113 -0
  204. package/dist/theme/index.d.ts +14 -0
  205. package/dist/theme/index.js +94 -0
  206. package/dist/theme/themes.d.ts +3 -0
  207. package/dist/theme/themes.js +323 -0
  208. package/dist/theme/types.d.ts +58 -0
  209. package/dist/theme/types.js +1 -0
  210. package/dist/timeline/index.d.ts +28 -0
  211. package/dist/timeline/index.js +35 -0
  212. package/dist/timeline/style.css +53 -0
  213. package/dist/toast/index.d.ts +53 -0
  214. package/dist/toast/index.js +131 -0
  215. package/dist/toast/style.css +76 -0
  216. package/dist/tooltip/api.d.ts +44 -0
  217. package/dist/tooltip/api.js +103 -0
  218. package/dist/tooltip/component.d.ts +28 -0
  219. package/dist/tooltip/component.js +35 -0
  220. package/dist/tooltip/index.d.ts +3 -0
  221. package/dist/tooltip/index.js +3 -0
  222. package/dist/tooltip/style.css +46 -0
  223. package/dist/tree/dragging-img.d.ts +13 -0
  224. package/dist/tree/dragging-img.js +23 -0
  225. package/dist/tree/index.d.ts +180 -0
  226. package/dist/tree/index.js +385 -0
  227. package/dist/tree/node-info.d.ts +63 -0
  228. package/dist/tree/node-info.js +128 -0
  229. package/dist/tree/node.d.ts +132 -0
  230. package/dist/tree/node.js +264 -0
  231. package/dist/tree/style.css +99 -0
  232. package/dist/upload/index.d.ts +48 -0
  233. package/dist/upload/index.js +141 -0
  234. package/dist/upload/style.css +41 -0
  235. package/package.json +51 -0
@@ -0,0 +1,51 @@
1
+ import { DivModule } from 'wok-ui';
2
+ /**
3
+ * 列表项
4
+ *
5
+ * 配合 List 使用的标准列表项,包含标题、描述和额外操作区。
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * new ListItem({
10
+ * title: '用户名称',
11
+ * description: 'user@example.com',
12
+ * extra: new Tag({ text: '管理员' })
13
+ * })
14
+ * ```
15
+ */
16
+ export class ListItem extends DivModule {
17
+ constructor(opts) {
18
+ super('wok-ui-ext-list-item');
19
+ this.opts = opts;
20
+ // 左侧内容区
21
+ const body = document.createElement('div');
22
+ body.style.cssText = 'flex:1;min-width:0';
23
+ // 标题
24
+ const titleDiv = document.createElement('div');
25
+ titleDiv.style.cssText =
26
+ 'font-size:var(--font-size-normal);color:var(--text-primary);font-weight:500';
27
+ if (typeof opts.title === 'string') {
28
+ titleDiv.textContent = opts.title;
29
+ }
30
+ body.appendChild(titleDiv);
31
+ // 描述
32
+ if (opts.description) {
33
+ const descDiv = document.createElement('div');
34
+ descDiv.style.cssText =
35
+ 'font-size:var(--font-size-small);color:var(--text-tertiary);margin-top:2px';
36
+ if (typeof opts.description === 'string') {
37
+ descDiv.textContent = opts.description;
38
+ }
39
+ body.appendChild(descDiv);
40
+ }
41
+ this.addChild({
42
+ children: body
43
+ });
44
+ // 右侧额外内容
45
+ if (opts.extra) {
46
+ this.addChild({
47
+ children: opts.extra
48
+ });
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,20 @@
1
+ .wok-ui-ext-list.bordered {
2
+ border: 1px solid var(--border-light);
3
+ border-radius: var(--radius-sm);
4
+ }
5
+ .wok-ui-ext-list.small .wok-ui-ext-list-item {
6
+ padding: 8px 12px;
7
+ }
8
+ .wok-ui-ext-list.large .wok-ui-ext-list-item {
9
+ padding: 16px 20px;
10
+ }
11
+ .wok-ui-ext-list .wok-ui-ext-list-item {
12
+ padding: 12px 16px;
13
+ border-bottom: 1px solid var(--border-light);
14
+ display: flex;
15
+ align-items: center;
16
+ gap: 12px;
17
+ }
18
+ .wok-ui-ext-list .wok-ui-ext-list-item:last-child {
19
+ border-bottom: none;
20
+ }
@@ -0,0 +1,10 @@
1
+ import './style.less';
2
+ /**
3
+ * 显示全屏 loading
4
+ *
5
+ */
6
+ export declare function showLoading(msg?: string): void;
7
+ /**
8
+ * 关闭全屏 loading
9
+ */
10
+ export declare function hideLoading(): void;
@@ -0,0 +1,46 @@
1
+ import { DivModule } from 'wok-ui';
2
+ import { getWokUiExtI18n } from '../i18n';
3
+ import { IconCircleNotch } from '../icons';
4
+ import './style.css';
5
+ /**
6
+ * <div class="wok-ui-ext-fullscreen-loading">
7
+ * <span class="wok-ui-ext-loading-spinner"></span>
8
+ * <span>Loading...</span>
9
+ * </div>
10
+ */
11
+ class FullscreenLoading extends DivModule {
12
+ constructor(text) {
13
+ super('wok-ui-ext-fullscreen-loading');
14
+ this.addChild({
15
+ classNames: 'wok-ui-ext-loading-spinner',
16
+ children: new IconCircleNotch()
17
+ });
18
+ if (text) {
19
+ this.addChild({
20
+ tag: 'span',
21
+ innerText: text
22
+ });
23
+ }
24
+ }
25
+ }
26
+ let fullscreenLoadingInstance;
27
+ /**
28
+ * 显示全屏 loading
29
+ *
30
+ */
31
+ export function showLoading(msg) {
32
+ if (fullscreenLoadingInstance) {
33
+ hideLoading();
34
+ }
35
+ fullscreenLoadingInstance = new FullscreenLoading(msg || getWokUiExtI18n().buildMsg('loading-text'));
36
+ fullscreenLoadingInstance.mount(document.body);
37
+ }
38
+ /**
39
+ * 关闭全屏 loading
40
+ */
41
+ export function hideLoading() {
42
+ if (fullscreenLoadingInstance) {
43
+ fullscreenLoadingInstance.destroy();
44
+ fullscreenLoadingInstance = undefined;
45
+ }
46
+ }
@@ -0,0 +1,48 @@
1
+ /* 全屏加载 */
2
+ .wok-ui-ext-fullscreen-loading {
3
+ position: fixed;
4
+ top: 0;
5
+ left: 0;
6
+ width: 100%;
7
+ height: 100%;
8
+ /* 初始完全透明、无模糊,动画结束后停留在最终状态 */
9
+ background: transparent;
10
+ backdrop-filter: blur(0);
11
+ /* 动画:持续 2 秒,结束时保持最终状态 */
12
+ animation: loading-bg-fade 2s forwards;
13
+ align-items: center;
14
+ justify-content: center;
15
+ z-index: 2000;
16
+ flex-direction: column;
17
+ color: white;
18
+ gap: 16px;
19
+ display: flex;
20
+ cursor: wait;
21
+ }
22
+ .wok-ui-ext-fullscreen-loading .wok-ui-ext-loading-spinner {
23
+ display: flex;
24
+ font-size: 3rem;
25
+ color: var(--color-primary);
26
+ animation: spin 1s linear infinite;
27
+ filter: drop-shadow(0 0 10px var(--color-primary));
28
+ }
29
+ .wok-ui-ext-fullscreen-loading span {
30
+ font-size: var(--font-size-title);
31
+ font-weight: 500;
32
+ letter-spacing: 1px;
33
+ }
34
+ /* 背景与模糊渐变动画 */
35
+ @keyframes loading-bg-fade {
36
+ to {
37
+ background: rgba(8, 20, 40, 0.7);
38
+ backdrop-filter: blur(5px);
39
+ }
40
+ }
41
+ @keyframes spin {
42
+ 0% {
43
+ transform: rotate(0deg);
44
+ }
45
+ 100% {
46
+ transform: rotate(360deg);
47
+ }
48
+ }
@@ -0,0 +1,38 @@
1
+ import { DivModule } from 'wok-ui';
2
+ import { MenuOptions } from './types';
3
+ import './style.less';
4
+ export type { MenuItemData, MenuOptions } from './types';
5
+ /**
6
+ * 导航菜单。
7
+ *
8
+ * 垂直菜单,支持多级嵌套、图标、折叠模式。
9
+ * 适用于侧边栏导航。
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const menu = new Menu({
14
+ * items: [
15
+ * { key: 'dashboard', icon: new SvgIcon({ svgHtml: '...' }), label: '仪表盘' },
16
+ * {
17
+ * key: 'system', label: '系统设置',
18
+ * children: [
19
+ * { key: 'system-basic', label: '基本设置' },
20
+ * { key: 'system-security', label: '安全设置' },
21
+ * ]
22
+ * },
23
+ * ],
24
+ * selectedKey: 'dashboard',
25
+ * onClick: key => router.push(key)
26
+ * })
27
+ * ```
28
+ */
29
+ export declare class Menu extends DivModule {
30
+ private nodes;
31
+ constructor(opts: MenuOptions);
32
+ /** 设置选中项 */
33
+ setSelectedKey(key: string): void;
34
+ /** 设置折叠模式 */
35
+ setCollapsed(collapsed: boolean): void;
36
+ /** 获取当前选中 key */
37
+ getSelectedKey(): string | undefined;
38
+ }
@@ -0,0 +1,62 @@
1
+ import { DivModule } from 'wok-ui';
2
+ import { createItemModule } from './sub-menu';
3
+ import './style.css';
4
+ /**
5
+ * 导航菜单。
6
+ *
7
+ * 垂直菜单,支持多级嵌套、图标、折叠模式。
8
+ * 适用于侧边栏导航。
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const menu = new Menu({
13
+ * items: [
14
+ * { key: 'dashboard', icon: new SvgIcon({ svgHtml: '...' }), label: '仪表盘' },
15
+ * {
16
+ * key: 'system', label: '系统设置',
17
+ * children: [
18
+ * { key: 'system-basic', label: '基本设置' },
19
+ * { key: 'system-security', label: '安全设置' },
20
+ * ]
21
+ * },
22
+ * ],
23
+ * selectedKey: 'dashboard',
24
+ * onClick: key => router.push(key)
25
+ * })
26
+ * ```
27
+ */
28
+ export class Menu extends DivModule {
29
+ constructor(opts) {
30
+ super('wok-ui-ext-menu');
31
+ this.nodes = [];
32
+ if (opts.collapsed)
33
+ this.el.classList.add('wok-ui-ext-menu-collapsed');
34
+ const ctx = {
35
+ selectedKey: opts.selectedKey,
36
+ collapsed: opts.collapsed ?? false,
37
+ onClick: opts.onClick,
38
+ setSelectedKey: (key) => {
39
+ ctx.selectedKey = key;
40
+ this.nodes.forEach(n => n.setSelected(key));
41
+ }
42
+ };
43
+ opts.items.forEach(item => {
44
+ const node = createItemModule(item, 0, ctx);
45
+ this.nodes.push(node);
46
+ this.addChild(node);
47
+ });
48
+ }
49
+ /** 设置选中项 */
50
+ setSelectedKey(key) {
51
+ this.nodes.forEach(n => n.setSelected(key));
52
+ }
53
+ /** 设置折叠模式 */
54
+ setCollapsed(collapsed) {
55
+ this.el.classList.toggle('wok-ui-ext-menu-collapsed', collapsed);
56
+ }
57
+ /** 获取当前选中 key */
58
+ getSelectedKey() {
59
+ const el = this.el.querySelector('.wok-ui-ext-menu-item.selected');
60
+ return el?.dataset.key;
61
+ }
62
+ }
@@ -0,0 +1,12 @@
1
+ import { DivModule } from 'wok-ui';
2
+ import { MenuItemData, ItemContext } from './types';
3
+ /**
4
+ * 叶子菜单项模块。
5
+ * @internal
6
+ */
7
+ export declare class LeafItem extends DivModule {
8
+ private key;
9
+ constructor(item: MenuItemData, level: number, ctx: ItemContext);
10
+ /** 更新选中态 */
11
+ setSelected(sk?: string): void;
12
+ }
@@ -0,0 +1,41 @@
1
+ import { DivModule } from 'wok-ui';
2
+ /**
3
+ * 叶子菜单项模块。
4
+ * @internal
5
+ */
6
+ export class LeafItem extends DivModule {
7
+ constructor(item, level, ctx) {
8
+ const classes = ['wok-ui-ext-menu-item'];
9
+ if (item.disabled)
10
+ classes.push('disabled');
11
+ if (ctx.selectedKey === item.key)
12
+ classes.push('selected');
13
+ super(...classes);
14
+ this.key = item.key;
15
+ this.el.dataset.key = item.key;
16
+ if (level > 0)
17
+ this.el.dataset.level = String(level);
18
+ // 图标
19
+ this.addChild({
20
+ classNames: 'wok-ui-ext-menu-item-icon',
21
+ children: item.icon ?? []
22
+ });
23
+ // 标签
24
+ this.addChild({
25
+ classNames: 'wok-ui-ext-menu-item-label',
26
+ innerText: item.label
27
+ });
28
+ // 点击
29
+ if (!item.disabled) {
30
+ this.el.addEventListener('click', (e) => {
31
+ e.stopPropagation();
32
+ ctx.setSelectedKey(item.key);
33
+ ctx.onClick?.(item.key);
34
+ });
35
+ }
36
+ }
37
+ /** 更新选中态 */
38
+ setSelected(sk) {
39
+ this.el.classList.toggle('selected', sk === this.key);
40
+ }
41
+ }
@@ -0,0 +1,117 @@
1
+ .wok-ui-ext-menu {
2
+ background: var(--bg-card);
3
+ width: 240px;
4
+ display: flex;
5
+ flex-direction: column;
6
+ transition: width 0.25s ease;
7
+ overflow: hidden;
8
+ user-select: none;
9
+ border-right: 1px solid var(--border-medium);
10
+ }
11
+ .wok-ui-ext-menu .wok-ui-ext-menu-item {
12
+ display: flex;
13
+ align-items: center;
14
+ height: 44px;
15
+ padding: 0 16px;
16
+ cursor: pointer;
17
+ transition: background var(--transition-speed), color var(--transition-speed);
18
+ color: var(--text-secondary);
19
+ gap: 12px;
20
+ white-space: nowrap;
21
+ position: relative;
22
+ }
23
+ .wok-ui-ext-menu .wok-ui-ext-menu-item:hover {
24
+ background: var(--bg-hover);
25
+ color: var(--text-primary);
26
+ }
27
+ .wok-ui-ext-menu .wok-ui-ext-menu-item.selected {
28
+ color: var(--color-primary);
29
+ background: var(--bg-hover);
30
+ }
31
+ .wok-ui-ext-menu .wok-ui-ext-menu-item.selected::before {
32
+ content: '';
33
+ position: absolute;
34
+ left: 0;
35
+ top: 6px;
36
+ bottom: 6px;
37
+ width: 3px;
38
+ background: var(--color-primary);
39
+ border-radius: 0 3px 3px 0;
40
+ }
41
+ .wok-ui-ext-menu .wok-ui-ext-menu-item.sub-active {
42
+ color: var(--text-primary);
43
+ }
44
+ .wok-ui-ext-menu .wok-ui-ext-menu-item.disabled {
45
+ opacity: 0.4;
46
+ cursor: not-allowed;
47
+ }
48
+ .wok-ui-ext-menu .wok-ui-ext-menu-item.disabled:hover {
49
+ background: transparent;
50
+ color: var(--text-secondary);
51
+ }
52
+ .wok-ui-ext-menu .wok-ui-ext-menu-item-icon {
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ width: 20px;
57
+ height: 20px;
58
+ flex-shrink: 0;
59
+ font-size: 18px;
60
+ }
61
+ .wok-ui-ext-menu .wok-ui-ext-menu-item-label {
62
+ flex: 1;
63
+ font-size: 14px;
64
+ overflow: hidden;
65
+ text-overflow: ellipsis;
66
+ white-space: nowrap;
67
+ }
68
+ .wok-ui-ext-menu .wok-ui-ext-menu-item-arrow {
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ width: 16px;
73
+ height: 16px;
74
+ flex-shrink: 0;
75
+ font-size: 14px;
76
+ color: var(--text-placeholder);
77
+ transition: transform 0.2s ease;
78
+ }
79
+ .wok-ui-ext-menu .wok-ui-ext-menu-item[data-level='1'] {
80
+ padding-left: 48px;
81
+ }
82
+ .wok-ui-ext-menu .wok-ui-ext-menu-item[data-level='2'] {
83
+ padding-left: 64px;
84
+ }
85
+ .wok-ui-ext-menu .wok-ui-ext-menu-submenu[data-level='1'] .wok-ui-ext-menu-item {
86
+ padding-left: 48px;
87
+ }
88
+ .wok-ui-ext-menu.wok-ui-ext-menu-collapsed {
89
+ width: 56px;
90
+ }
91
+ .wok-ui-ext-menu.wok-ui-ext-menu-collapsed .wok-ui-ext-menu-item {
92
+ justify-content: center;
93
+ padding: 0 8px;
94
+ }
95
+ .wok-ui-ext-menu.wok-ui-ext-menu-collapsed .wok-ui-ext-menu-item-label,
96
+ .wok-ui-ext-menu.wok-ui-ext-menu-collapsed .wok-ui-ext-menu-item-arrow {
97
+ display: none;
98
+ }
99
+ .wok-ui-ext-menu.wok-ui-ext-menu-collapsed .wok-ui-ext-menu-submenu-children {
100
+ display: none !important;
101
+ }
102
+ .wok-ui-ext-menu.wok-ui-ext-menu-collapsed .wok-ui-ext-menu-item.selected::before {
103
+ left: auto;
104
+ right: 0;
105
+ width: 3px;
106
+ }
107
+ .wok-ui-ext-menu-submenu {
108
+ display: flex;
109
+ flex-direction: column;
110
+ }
111
+ .wok-ui-ext-menu-submenu .wok-ui-ext-menu-submenu-children {
112
+ overflow: hidden;
113
+ transition: display 0.2s ease;
114
+ }
115
+ .wok-ui-ext-menu-submenu + .wok-ui-ext-menu-submenu .wok-ui-ext-menu-item {
116
+ border-top: none;
117
+ }
@@ -0,0 +1,21 @@
1
+ import { DivModule } from 'wok-ui';
2
+ import { MenuItemData, ItemContext } from './types';
3
+ import { LeafItem } from './leaf-item';
4
+ /**
5
+ * 子菜单模块。
6
+ * @internal
7
+ */
8
+ export declare class SubMenuComp extends DivModule {
9
+ private open;
10
+ private childNodes;
11
+ private headerEl;
12
+ private childrenContainerEl;
13
+ constructor(item: MenuItemData, level: number, ctx: ItemContext);
14
+ /** 更新选中态 */
15
+ setSelected(sk?: string): void;
16
+ }
17
+ /**
18
+ * 根据菜单项数据创建对应的菜单模块实例。
19
+ * @internal
20
+ */
21
+ export declare function createItemModule(item: MenuItemData, level: number, ctx: ItemContext): LeafItem | SubMenuComp;
@@ -0,0 +1,85 @@
1
+ import { DivModule } from 'wok-ui';
2
+ import { getArrowSvg, hasSelectedDescendant } from './utils';
3
+ import { LeafItem } from './leaf-item';
4
+ /**
5
+ * 子菜单模块。
6
+ * @internal
7
+ */
8
+ export class SubMenuComp extends DivModule {
9
+ constructor(item, level, ctx) {
10
+ super('wok-ui-ext-menu-submenu');
11
+ this.childNodes = [];
12
+ if (level > 0)
13
+ this.el.dataset.level = String(level);
14
+ this.open = hasSelectedDescendant(item, ctx.selectedKey);
15
+ // 头部
16
+ const headerClasses = ['wok-ui-ext-menu-item'];
17
+ if (this.open)
18
+ headerClasses.push('sub-active');
19
+ if (item.disabled)
20
+ headerClasses.push('disabled');
21
+ this.addChild({
22
+ classNames: headerClasses,
23
+ children: add => {
24
+ // 图标
25
+ add({
26
+ classNames: 'wok-ui-ext-menu-item-icon',
27
+ children: item.icon ?? []
28
+ });
29
+ // 标签
30
+ add({
31
+ classNames: 'wok-ui-ext-menu-item-label',
32
+ innerText: item.label
33
+ });
34
+ // 展开箭头
35
+ add({
36
+ classNames: 'wok-ui-ext-menu-item-arrow',
37
+ innerHTML: getArrowSvg(this.open)
38
+ });
39
+ }
40
+ });
41
+ // 子节点容器
42
+ this.addChild({
43
+ classNames: 'wok-ui-ext-menu-submenu-children',
44
+ style: { display: this.open ? '' : 'none' },
45
+ children: add => {
46
+ ;
47
+ (item.children || []).forEach(child => {
48
+ const node = createItemModule(child, level + 1, ctx);
49
+ this.childNodes.push(node);
50
+ add(node);
51
+ });
52
+ }
53
+ });
54
+ // 通过 DOM 查找获取引用,用于后续操作
55
+ this.headerEl = this.el.querySelector('.wok-ui-ext-menu-item');
56
+ this.childrenContainerEl = this.el.querySelector('.wok-ui-ext-menu-submenu-children');
57
+ // 切换展开/收起
58
+ if (!item.disabled) {
59
+ this.headerEl.addEventListener('click', (e) => {
60
+ e.stopPropagation();
61
+ this.open = !this.open;
62
+ this.childrenContainerEl.style.display = this.open ? '' : 'none';
63
+ const arrow = this.headerEl.querySelector('.wok-ui-ext-menu-item-arrow');
64
+ if (arrow)
65
+ arrow.innerHTML = getArrowSvg(this.open);
66
+ });
67
+ }
68
+ }
69
+ /** 更新选中态 */
70
+ setSelected(sk) {
71
+ this.childNodes.forEach(n => n.setSelected(sk));
72
+ const hasSelected = !!this.childrenContainerEl.querySelector('.wok-ui-ext-menu-item.selected');
73
+ this.headerEl.classList.toggle('sub-active', hasSelected);
74
+ }
75
+ }
76
+ /**
77
+ * 根据菜单项数据创建对应的菜单模块实例。
78
+ * @internal
79
+ */
80
+ export function createItemModule(item, level, ctx) {
81
+ if (!item.children || item.children.length === 0) {
82
+ return new LeafItem(item, level, ctx);
83
+ }
84
+ return new SubMenuComp(item, level, ctx);
85
+ }
@@ -0,0 +1,36 @@
1
+ import { Module } from 'wok-ui';
2
+ /**
3
+ * 菜单项数据。
4
+ */
5
+ export interface MenuItemData {
6
+ /** 唯一标识 */
7
+ key: string;
8
+ /** 图标模块(可选,传入 SvgIcon / RemoteSvgIcon 等 Module 实例) */
9
+ icon?: Module;
10
+ /** 显示文本 */
11
+ label: string;
12
+ /** 是否禁用 */
13
+ disabled?: boolean;
14
+ /** 子菜单项 */
15
+ children?: MenuItemData[];
16
+ }
17
+ /**
18
+ * 菜单配置选项。
19
+ */
20
+ export interface MenuOptions {
21
+ /** 菜单数据 */
22
+ items: MenuItemData[];
23
+ /** 当前选中项 key */
24
+ selectedKey?: string;
25
+ /** 是否折叠(仅显示图标) */
26
+ collapsed?: boolean;
27
+ /** 点击菜单项回调 */
28
+ onClick?: (key: string) => void;
29
+ }
30
+ /** @internal 菜单内部上下文 */
31
+ export interface ItemContext {
32
+ selectedKey?: string;
33
+ collapsed: boolean;
34
+ onClick?: (key: string) => void;
35
+ setSelectedKey: (key: string) => void;
36
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import { MenuItemData } from './types';
2
+ /**
3
+ * 子菜单箭头 SVG。
4
+ * @internal
5
+ */
6
+ export declare function getArrowSvg(open: boolean): string;
7
+ /**
8
+ * 检查指定菜单项是否有子节点被选中。
9
+ * @internal
10
+ */
11
+ export declare function hasSelectedDescendant(item: MenuItemData, selectedKey?: string): boolean;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 子菜单箭头 SVG。
3
+ * @internal
4
+ */
5
+ export function getArrowSvg(open) {
6
+ return open
7
+ ? `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px;height:14px"><polyline points="6 9 12 15 18 9"></polyline></svg>`
8
+ : `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px;height:14px"><polyline points="9 18 15 12 9 6"></polyline></svg>`;
9
+ }
10
+ /**
11
+ * 检查指定菜单项是否有子节点被选中。
12
+ * @internal
13
+ */
14
+ export function hasSelectedDescendant(item, selectedKey) {
15
+ if (!selectedKey || !item.children)
16
+ return false;
17
+ return item.children.some(child => {
18
+ if (child.key === selectedKey)
19
+ return true;
20
+ if (child.children)
21
+ return hasSelectedDescendant(child, selectedKey);
22
+ return false;
23
+ });
24
+ }
@@ -0,0 +1,23 @@
1
+ import { ModalOptions } from './modal';
2
+ import './style.less';
3
+ export * from './modal';
4
+ export interface ModalHandle {
5
+ close: () => void;
6
+ }
7
+ /**
8
+ * 显示模态框
9
+ * @param opts 模态框选项
10
+ * @returns 模态框句柄
11
+ */
12
+ export declare function showModal(opts: ModalOptions): ModalHandle;
13
+ export declare function showAlert(msg: string): Promise<void>;
14
+ export declare function showAlert(opts: {
15
+ msg: string;
16
+ onConfirm?: () => void;
17
+ }): void;
18
+ export declare function showConfirm(msg: string): Promise<boolean>;
19
+ export declare function showConfirm(opts: {
20
+ msg: string;
21
+ onConfirm?: () => void;
22
+ onCancel?: () => void;
23
+ }): void;