layplux 1.0.1 → 2.0.1
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/dist/cjs/components/center-view/index.cjs +18 -28
- package/dist/cjs/components/corner-glow/index.cjs +11 -28
- package/dist/cjs/components/dropdown/index.cjs +136 -130
- package/dist/cjs/components/icon/index.cjs +29 -51
- package/dist/cjs/components/index.cjs +24 -25
- package/dist/cjs/components/panel-view/index.cjs +111 -114
- package/dist/cjs/components/popup/index.cjs +166 -151
- package/dist/cjs/components/title/index.cjs +34 -47
- package/dist/cjs/components/tooltip/index.cjs +70 -61
- package/dist/cjs/components/widget/index.cjs +52 -72
- package/dist/cjs/index.cjs +13 -40
- package/dist/cjs/layout/glass-overlay.cjs +15 -28
- package/dist/cjs/layout/layered-manager.cjs +20 -29
- package/dist/cjs/layout/layplux.cjs +19 -32
- package/dist/cjs/layout/root-pane.cjs +20 -38
- package/dist/cjs/layout/skeleton/bottom-area.cjs +26 -43
- package/dist/cjs/layout/skeleton/bottom-left-area.cjs +12 -29
- package/dist/cjs/layout/skeleton/bottom-right-area.cjs +11 -28
- package/dist/cjs/layout/skeleton/center-area.cjs +278 -371
- package/dist/cjs/layout/skeleton/index.cjs +7 -24
- package/dist/cjs/layout/skeleton/left-bottom-area.cjs +12 -29
- package/dist/cjs/layout/skeleton/left-top-area.cjs +12 -29
- package/dist/cjs/layout/skeleton/right-bottom-area.cjs +11 -28
- package/dist/cjs/layout/skeleton/right-top-area.cjs +11 -28
- package/dist/cjs/layout/skeleton/skeleton.cjs +55 -60
- package/dist/cjs/layout/skeleton/top-area.cjs +26 -43
- package/dist/cjs/locales/en-US.cjs +11 -30
- package/dist/cjs/locales/index.cjs +12 -30
- package/dist/cjs/locales/zh-CN.cjs +11 -30
- package/dist/cjs/managers/area.cjs +12 -25
- package/dist/cjs/managers/index.cjs +12 -20
- package/dist/cjs/managers/pane.cjs +12 -26
- package/dist/cjs/managers/skeleton.cjs +112 -124
- package/dist/cjs/managers/theme.cjs +8 -29
- package/dist/cjs/managers/widget-container.cjs +31 -31
- package/dist/cjs/managers/widget.cjs +63 -50
- package/dist/cjs/types/config.cjs +2 -16
- package/dist/cjs/types/index.cjs +2 -18
- package/dist/cjs/types/locale.cjs +2 -16
- package/dist/cjs/utils/event-bus.cjs +53 -49
- package/dist/cjs/utils/focus-tracker.cjs +66 -42
- package/dist/cjs/utils/index.cjs +23 -31
- package/dist/cjs/utils/unique-id.cjs +5 -24
- package/dist/cjs/utils/vue.cjs +20 -30
- package/dist/esm/components/center-view/index.mjs +15 -7
- package/dist/esm/components/corner-glow/index.mjs +8 -7
- package/dist/esm/components/dropdown/index.mjs +117 -101
- package/dist/esm/components/icon/index.mjs +24 -30
- package/dist/esm/components/index.mjs +7 -8
- package/dist/esm/components/panel-view/index.mjs +107 -98
- package/dist/esm/components/popup/index.mjs +155 -130
- package/dist/esm/components/title/index.mjs +29 -24
- package/dist/esm/components/tooltip/index.mjs +67 -40
- package/dist/esm/components/widget/index.mjs +45 -48
- package/dist/esm/index.mjs +4 -10
- package/dist/esm/layout/glass-overlay.mjs +12 -7
- package/dist/esm/layout/layered-manager.mjs +17 -8
- package/dist/esm/layout/layplux.mjs +14 -11
- package/dist/esm/layout/root-pane.mjs +16 -16
- package/dist/esm/layout/skeleton/bottom-area.mjs +23 -22
- package/dist/esm/layout/skeleton/bottom-left-area.mjs +9 -8
- package/dist/esm/layout/skeleton/bottom-right-area.mjs +8 -7
- package/dist/esm/layout/skeleton/center-area.mjs +251 -333
- package/dist/esm/layout/skeleton/index.mjs +1 -4
- package/dist/esm/layout/skeleton/left-bottom-area.mjs +9 -8
- package/dist/esm/layout/skeleton/left-top-area.mjs +9 -8
- package/dist/esm/layout/skeleton/right-bottom-area.mjs +8 -7
- package/dist/esm/layout/skeleton/right-top-area.mjs +8 -7
- package/dist/esm/layout/skeleton/skeleton.mjs +52 -39
- package/dist/esm/layout/skeleton/top-area.mjs +23 -22
- package/dist/esm/locales/en-US.mjs +9 -10
- package/dist/esm/locales/index.mjs +7 -9
- package/dist/esm/locales/zh-CN.mjs +9 -10
- package/dist/esm/managers/area.mjs +10 -5
- package/dist/esm/managers/index.mjs +3 -3
- package/dist/esm/managers/pane.mjs +9 -5
- package/dist/esm/managers/skeleton.mjs +97 -95
- package/dist/esm/managers/theme.mjs +6 -9
- package/dist/esm/managers/widget-container.mjs +28 -10
- package/dist/esm/managers/widget.mjs +55 -25
- package/dist/esm/types/config.mjs +1 -0
- package/dist/esm/types/index.mjs +1 -1
- package/dist/esm/types/locale.mjs +1 -0
- package/dist/esm/utils/event-bus.mjs +46 -17
- package/dist/esm/utils/focus-tracker.mjs +63 -23
- package/dist/esm/utils/index.mjs +7 -10
- package/dist/esm/utils/unique-id.mjs +3 -4
- package/dist/esm/utils/vue.mjs +13 -5
- package/dist/types/managers/skeleton.d.ts.map +1 -1
- package/dist/umd/index.js +2 -30
- package/package.json +18 -14
|
@@ -1,113 +1,104 @@
|
|
|
1
|
-
import { ref } from
|
|
2
|
-
import { useArea } from
|
|
3
|
-
import { isWidget, useWidget } from
|
|
4
|
-
import { useWidgetContainer } from
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import { injectThemeCSS } from "./theme.mjs";
|
|
1
|
+
import { ref } from 'vue';
|
|
2
|
+
import { useArea } from './area.mjs';
|
|
3
|
+
import { isWidget, useWidget } from './widget.mjs';
|
|
4
|
+
import { useWidgetContainer } from './widget-container.mjs';
|
|
5
|
+
import { injectThemeCSS } from './theme.mjs';
|
|
6
|
+
import { createPluginEventBus } from '../utils/event-bus.mjs';
|
|
7
|
+
import { getBuiltInLocale } from '../locales/index.mjs';
|
|
8
|
+
import { FocusTracker } from '../utils/focus-tracker.mjs';
|
|
9
|
+
|
|
11
10
|
function useSkeleton() {
|
|
12
11
|
const widgets = [];
|
|
13
12
|
const self = {};
|
|
14
|
-
const containers =
|
|
13
|
+
const containers = new Map();
|
|
15
14
|
const focusTracker = new FocusTracker();
|
|
16
|
-
const event = createPluginEventBus(
|
|
17
|
-
const locale = ref(getBuiltInLocale(
|
|
15
|
+
const event = createPluginEventBus('skeleton');
|
|
16
|
+
const locale = ref(getBuiltInLocale('zh-CN'));
|
|
18
17
|
function setLocale(name) {
|
|
19
18
|
locale.value = getBuiltInLocale(name);
|
|
20
19
|
}
|
|
21
|
-
const theme = ref(
|
|
22
|
-
const systemDark = ref(
|
|
23
|
-
typeof window !== "undefined" ? window.matchMedia("(prefers-color-scheme: dark)").matches : false
|
|
24
|
-
);
|
|
20
|
+
const theme = ref('system');
|
|
21
|
+
const systemDark = ref(typeof window !== 'undefined' ? window.matchMedia('(prefers-color-scheme: dark)').matches : false);
|
|
25
22
|
function resolveTheme() {
|
|
26
|
-
if (theme.value ===
|
|
27
|
-
return systemDark.value ?
|
|
23
|
+
if (theme.value === 'system') {
|
|
24
|
+
return systemDark.value ? 'dark' : 'light';
|
|
28
25
|
}
|
|
29
26
|
return theme.value;
|
|
30
27
|
}
|
|
31
28
|
function isDark() {
|
|
32
|
-
return resolveTheme() ===
|
|
29
|
+
return resolveTheme() === 'dark';
|
|
33
30
|
}
|
|
34
31
|
function setTheme(t) {
|
|
35
32
|
theme.value = t;
|
|
36
33
|
}
|
|
37
|
-
const themeName = ref(
|
|
34
|
+
const themeName = ref('default');
|
|
38
35
|
function setThemeName(name) {
|
|
39
36
|
themeName.value = name;
|
|
40
37
|
}
|
|
41
38
|
function registerTheme(name, vars) {
|
|
42
39
|
injectThemeCSS(name, vars);
|
|
43
40
|
}
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
|
|
42
|
+
// 监听系统主题变化
|
|
43
|
+
if (typeof window !== 'undefined') {
|
|
44
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
|
46
45
|
systemDark.value = e.matches;
|
|
47
46
|
});
|
|
48
47
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const bottomArea = useArea(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
createContainer
|
|
95
|
-
},
|
|
96
|
-
"bottomLeftArea",
|
|
97
|
-
(config, container) => createWidget(config, container)
|
|
98
|
-
);
|
|
99
|
-
const centerArea = useArea(
|
|
100
|
-
{ createContainer },
|
|
101
|
-
"centerArea",
|
|
102
|
-
(config, container) => createWidget(config, container)
|
|
103
|
-
);
|
|
48
|
+
|
|
49
|
+
// 顶部工具栏
|
|
50
|
+
const topArea = useArea({
|
|
51
|
+
createContainer
|
|
52
|
+
}, 'topArea', (config, container) => createWidget(config, container));
|
|
53
|
+
|
|
54
|
+
// 底部状态栏
|
|
55
|
+
const bottomArea = useArea({
|
|
56
|
+
createContainer
|
|
57
|
+
}, 'bottomArea', (config, container) => createWidget(config, container));
|
|
58
|
+
|
|
59
|
+
// 左侧顶部主区域
|
|
60
|
+
const leftTopArea = useArea({
|
|
61
|
+
createContainer
|
|
62
|
+
}, 'leftTopArea', (config, container) => createWidget(config, container));
|
|
63
|
+
|
|
64
|
+
// 左侧底部快捷区域
|
|
65
|
+
const leftBottomArea = useArea({
|
|
66
|
+
createContainer
|
|
67
|
+
}, 'leftBottomArea', (config, container) => createWidget(config, container));
|
|
68
|
+
|
|
69
|
+
// 右侧顶部主区域
|
|
70
|
+
const rightTopArea = useArea({
|
|
71
|
+
createContainer
|
|
72
|
+
}, 'rightTopArea', (config, container) => createWidget(config, container));
|
|
73
|
+
|
|
74
|
+
// 右侧底部区域
|
|
75
|
+
const rightBottomArea = useArea({
|
|
76
|
+
createContainer
|
|
77
|
+
}, 'rightBottomArea', (config, container) => createWidget(config, container));
|
|
78
|
+
|
|
79
|
+
// 右侧最底部快捷操作
|
|
80
|
+
const bottomRightArea = useArea({
|
|
81
|
+
createContainer
|
|
82
|
+
}, 'bottomRightArea', (config, container) => createWidget(config, container));
|
|
83
|
+
|
|
84
|
+
// 左侧最底部快捷操作(交互型)
|
|
85
|
+
const bottomLeftArea = useArea({
|
|
86
|
+
createContainer
|
|
87
|
+
}, 'bottomLeftArea', (config, container) => createWidget(config, container));
|
|
88
|
+
|
|
89
|
+
// 中心区域
|
|
90
|
+
const centerArea = useArea({
|
|
91
|
+
createContainer
|
|
92
|
+
}, 'centerArea', (config, container) => createWidget(config, container));
|
|
104
93
|
function createWidget(config, container) {
|
|
105
94
|
if (isWidget(config)) {
|
|
106
95
|
return config;
|
|
107
96
|
}
|
|
108
97
|
const widget = useWidget(config, container, self);
|
|
109
98
|
widgets.push(widget);
|
|
110
|
-
event.emitGlobal(
|
|
99
|
+
event.emitGlobal('skeleton:widget-added', {
|
|
100
|
+
widget
|
|
101
|
+
});
|
|
111
102
|
return widget;
|
|
112
103
|
}
|
|
113
104
|
const focusedId = ref(null);
|
|
@@ -120,34 +111,44 @@ function useSkeleton() {
|
|
|
120
111
|
}
|
|
121
112
|
function focus(id) {
|
|
122
113
|
focusedId.value = id;
|
|
123
|
-
event.emitGlobal(
|
|
114
|
+
event.emitGlobal('skeleton:focus-changed', {
|
|
115
|
+
focusedId: id
|
|
116
|
+
});
|
|
124
117
|
}
|
|
125
118
|
function blur() {
|
|
126
119
|
focusedId.value = null;
|
|
127
|
-
event.emitGlobal(
|
|
120
|
+
event.emitGlobal('skeleton:focus-changed', {
|
|
121
|
+
focusedId: null
|
|
122
|
+
});
|
|
128
123
|
}
|
|
129
124
|
function add(config, extraConfig) {
|
|
125
|
+
// TODO: 处理extraConfig
|
|
130
126
|
if (extraConfig) {
|
|
131
|
-
config = {
|
|
127
|
+
config = {
|
|
128
|
+
...config,
|
|
129
|
+
...extraConfig
|
|
130
|
+
};
|
|
132
131
|
}
|
|
133
|
-
const {
|
|
134
|
-
|
|
132
|
+
const {
|
|
133
|
+
area
|
|
134
|
+
} = config;
|
|
135
|
+
if (area === 'topArea') {
|
|
135
136
|
topArea.add(config);
|
|
136
|
-
} else if (area ===
|
|
137
|
+
} else if (area === 'bottomArea') {
|
|
137
138
|
bottomArea.add(config);
|
|
138
|
-
} else if (area ===
|
|
139
|
+
} else if (area === 'leftTopArea') {
|
|
139
140
|
leftTopArea.add(config);
|
|
140
|
-
} else if (area ===
|
|
141
|
+
} else if (area === 'leftBottomArea') {
|
|
141
142
|
leftBottomArea.add(config);
|
|
142
|
-
} else if (area ===
|
|
143
|
+
} else if (area === 'bottomLeftArea') {
|
|
143
144
|
bottomLeftArea.add(config);
|
|
144
|
-
} else if (area ===
|
|
145
|
+
} else if (area === 'rightTopArea') {
|
|
145
146
|
rightTopArea.add(config);
|
|
146
|
-
} else if (area ===
|
|
147
|
+
} else if (area === 'rightBottomArea') {
|
|
147
148
|
rightBottomArea.add(config);
|
|
148
|
-
} else if (area ===
|
|
149
|
+
} else if (area === 'bottomRightArea') {
|
|
149
150
|
bottomRightArea.add(config);
|
|
150
|
-
} else if (area ===
|
|
151
|
+
} else if (area === 'centerArea') {
|
|
151
152
|
centerArea.add(config);
|
|
152
153
|
}
|
|
153
154
|
}
|
|
@@ -156,6 +157,8 @@ function useSkeleton() {
|
|
|
156
157
|
containers.set(name, container);
|
|
157
158
|
return container;
|
|
158
159
|
}
|
|
160
|
+
|
|
161
|
+
// 4. 填充 self 的真正属性
|
|
159
162
|
Object.assign(self, {
|
|
160
163
|
widgets,
|
|
161
164
|
topArea,
|
|
@@ -187,6 +190,5 @@ function useSkeleton() {
|
|
|
187
190
|
});
|
|
188
191
|
return self;
|
|
189
192
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
};
|
|
193
|
+
|
|
194
|
+
export { useSkeleton };
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
+
/** 为指定 theme 注入 CSS 变量样式,重复注册会替换 */
|
|
1
2
|
function injectThemeCSS(name, vars) {
|
|
2
3
|
const styleId = `layplux-theme-${name}`;
|
|
3
4
|
let styleEl = document.getElementById(styleId);
|
|
4
5
|
if (!styleEl) {
|
|
5
|
-
styleEl = document.createElement(
|
|
6
|
+
styleEl = document.createElement('style');
|
|
6
7
|
styleEl.id = styleId;
|
|
7
8
|
document.head.appendChild(styleEl);
|
|
8
9
|
}
|
|
9
|
-
const varLines = Object.entries(vars).map(([key, value]) => ` ${key}: ${value};`).join(
|
|
10
|
-
styleEl.textContent = `.layplux-root[data-theme='${name}'] {
|
|
11
|
-
${varLines}
|
|
10
|
+
const varLines = Object.entries(vars).map(([key, value]) => ` ${key}: ${value};`).join('\n');
|
|
11
|
+
styleEl.textContent = `.layplux-root[data-theme='${name}'] {\n${varLines}\n}\n`;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
export {
|
|
16
|
-
injectThemeCSS
|
|
17
|
-
};
|
|
13
|
+
|
|
14
|
+
export { injectThemeCSS };
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import { ref } from
|
|
1
|
+
import { ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* handle 函数签名:把原始 item(config 或 widget)转换成 widget 实例。
|
|
5
|
+
* 第二个参数是所属 container 的引用,方便在创建 widget 时回引。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* widget container 用于管理 widget 的添加、删除、获取等操作
|
|
10
|
+
* T 为 widget 的类型
|
|
11
|
+
* G 为 widgetconfig 的配置类型
|
|
12
|
+
*/
|
|
2
13
|
function useWidgetContainer(handle, skeleton) {
|
|
3
14
|
const maps = {};
|
|
4
15
|
const items = ref([]);
|
|
5
|
-
const activeId = ref(null);
|
|
16
|
+
const activeId = ref(null); // ✅ 单一数据源
|
|
17
|
+
|
|
6
18
|
const self = {
|
|
7
19
|
items,
|
|
8
20
|
activeId,
|
|
@@ -16,6 +28,7 @@ function useWidgetContainer(handle, skeleton) {
|
|
|
16
28
|
toggleActive
|
|
17
29
|
};
|
|
18
30
|
function add(item) {
|
|
31
|
+
// 将config转换为widget,将创建widget的能力交给外部,并把 container 自身传出去
|
|
19
32
|
const nItem = handle(item, self);
|
|
20
33
|
const origin = get(nItem.name);
|
|
21
34
|
if (origin === nItem) return origin;
|
|
@@ -43,23 +56,29 @@ function useWidgetContainer(handle, skeleton) {
|
|
|
43
56
|
const i = items.value.indexOf(item);
|
|
44
57
|
if (i > -1) items.value.splice(i, 1);
|
|
45
58
|
delete maps[name];
|
|
46
|
-
skeleton.event.emitGlobal(
|
|
59
|
+
skeleton.event.emitGlobal('skeleton:widget-removed', {
|
|
60
|
+
name
|
|
61
|
+
});
|
|
47
62
|
return item;
|
|
48
63
|
}
|
|
49
64
|
function activate(id) {
|
|
50
65
|
if (!maps[id]) return;
|
|
51
66
|
activeId.value = id;
|
|
52
67
|
skeleton.focus(id);
|
|
53
|
-
maps[id].focusable.active();
|
|
54
|
-
skeleton.event.emitGlobal(`widget:${id}:activated`, {
|
|
68
|
+
maps[id].focusable.active(); // 面板激活 → 同步焦点栈
|
|
69
|
+
skeleton.event.emitGlobal(`widget:${id}:activated`, {
|
|
70
|
+
widget: maps[id]
|
|
71
|
+
});
|
|
55
72
|
}
|
|
56
73
|
function deactivate() {
|
|
57
74
|
const current = activeId.value;
|
|
58
75
|
activeId.value = null;
|
|
59
76
|
skeleton.blur();
|
|
60
77
|
if (current && maps[current]) {
|
|
61
|
-
maps[current].focusable.suspense();
|
|
62
|
-
skeleton.event.emitGlobal(`widget:${current}:deactivated`, {
|
|
78
|
+
maps[current].focusable.suspense(); // 面板收起 → 从焦点栈移除
|
|
79
|
+
skeleton.event.emitGlobal(`widget:${current}:deactivated`, {
|
|
80
|
+
widget: maps[current]
|
|
81
|
+
});
|
|
63
82
|
}
|
|
64
83
|
}
|
|
65
84
|
function toggleActive(id) {
|
|
@@ -71,6 +90,5 @@ function useWidgetContainer(handle, skeleton) {
|
|
|
71
90
|
}
|
|
72
91
|
return self;
|
|
73
92
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
93
|
+
|
|
94
|
+
export { useWidgetContainer };
|
|
@@ -1,50 +1,80 @@
|
|
|
1
|
-
import { computed, h, watch } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { computed, h, watch } from 'vue';
|
|
2
|
+
import { usePane } from './pane.mjs';
|
|
3
|
+
import { uniqueId } from '../utils/unique-id.mjs';
|
|
4
|
+
import { createContent } from '../utils/vue.mjs';
|
|
5
|
+
import { WidgetView, WidgetTitleView } from '../components/widget/index.mjs';
|
|
6
|
+
|
|
5
7
|
function useWidget(config, container, skeleton) {
|
|
6
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
name,
|
|
10
|
+
props,
|
|
11
|
+
type
|
|
12
|
+
} = config;
|
|
7
13
|
const active = computed(() => container?.activeId.value === name);
|
|
8
14
|
const focused = computed(() => skeleton?.focusedId.value === name);
|
|
9
15
|
const id = uniqueId(type);
|
|
10
|
-
const align = props?.align ??
|
|
16
|
+
const align = props?.align ?? 'left';
|
|
11
17
|
const pane = usePane();
|
|
18
|
+
|
|
19
|
+
// ─── Focusable 注册 ──────────────────────────────────────────────────────
|
|
20
|
+
// range 初始为 () => false,PanelView 挂载后通过 focusable.setRange(el) 注入真实 DOM
|
|
12
21
|
const focusable = skeleton.focusTracker.create({
|
|
13
|
-
range:
|
|
22
|
+
range: e => {
|
|
14
23
|
const target = e.target;
|
|
15
24
|
if (!target) {
|
|
16
25
|
return false;
|
|
17
26
|
}
|
|
27
|
+
// 当点击的是panel时,激活
|
|
18
28
|
const el = document.getElementById(id);
|
|
19
29
|
if (el?.contains(target)) {
|
|
20
30
|
return true;
|
|
21
31
|
}
|
|
22
|
-
|
|
32
|
+
// 当class中包含layplux-resize-handle则不失去焦点
|
|
33
|
+
if (target.classList.contains('layplux-resize-handle')) {
|
|
23
34
|
return true;
|
|
24
35
|
}
|
|
25
36
|
return false;
|
|
26
37
|
},
|
|
27
38
|
onActive: () => {
|
|
28
39
|
widget.container?.activate(name);
|
|
29
|
-
skeleton.event?.emitGlobal(`widget:${name}:focus`, {
|
|
40
|
+
skeleton.event?.emitGlobal(`widget:${name}:focus`, {
|
|
41
|
+
widget
|
|
42
|
+
});
|
|
30
43
|
},
|
|
31
44
|
onBlur: () => {
|
|
45
|
+
// 焦点离开 → 清除 focusedId
|
|
32
46
|
skeleton.blur();
|
|
33
|
-
skeleton.event?.emitGlobal(`widget:${name}:blur`, {
|
|
34
|
-
|
|
47
|
+
skeleton.event?.emitGlobal(`widget:${name}:blur`, {
|
|
48
|
+
widget
|
|
49
|
+
});
|
|
50
|
+
// DockUnpinned:失焦自动收起
|
|
51
|
+
if (pane.viewMode.value === 'DockUnpinned' || pane.viewMode.value === 'Undock') {
|
|
35
52
|
container?.deactivate();
|
|
36
53
|
}
|
|
37
54
|
}
|
|
38
55
|
});
|
|
39
56
|
function renderBody() {
|
|
40
|
-
const {
|
|
41
|
-
|
|
57
|
+
const {
|
|
58
|
+
content,
|
|
59
|
+
contentProps
|
|
60
|
+
} = config;
|
|
61
|
+
return createContent(content, {
|
|
62
|
+
...contentProps,
|
|
63
|
+
config,
|
|
64
|
+
event: widget.event
|
|
65
|
+
});
|
|
42
66
|
}
|
|
43
67
|
function renderContent() {
|
|
44
|
-
return h(WidgetView, {
|
|
68
|
+
return h(WidgetView, {
|
|
69
|
+
key: id,
|
|
70
|
+
widget
|
|
71
|
+
});
|
|
45
72
|
}
|
|
46
73
|
function renderTitle() {
|
|
47
|
-
return h(WidgetTitleView, {
|
|
74
|
+
return h(WidgetTitleView, {
|
|
75
|
+
key: id,
|
|
76
|
+
widget
|
|
77
|
+
});
|
|
48
78
|
}
|
|
49
79
|
const widget = {
|
|
50
80
|
id,
|
|
@@ -64,20 +94,20 @@ function useWidget(config, container, skeleton) {
|
|
|
64
94
|
renderTitle
|
|
65
95
|
};
|
|
66
96
|
props?.onInit?.(widget);
|
|
97
|
+
|
|
98
|
+
// Emit view-mode-changed events
|
|
67
99
|
if (skeleton?.event) {
|
|
68
|
-
watch(
|
|
69
|
-
(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
);
|
|
100
|
+
watch(() => pane.viewMode.value, mode => {
|
|
101
|
+
skeleton.event.emitGlobal(`widget:${name}:view-mode-changed`, {
|
|
102
|
+
widget,
|
|
103
|
+
mode
|
|
104
|
+
});
|
|
105
|
+
});
|
|
74
106
|
}
|
|
75
107
|
return widget;
|
|
76
108
|
}
|
|
77
109
|
function isWidget(obj) {
|
|
78
110
|
return obj && obj.isWidget;
|
|
79
111
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
useWidget
|
|
83
|
-
};
|
|
112
|
+
|
|
113
|
+
export { isWidget, useWidget };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
package/dist/esm/types/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,27 +1,57 @@
|
|
|
1
|
-
import EventEmitter2
|
|
1
|
+
import EventEmitter2 from 'eventemitter2';
|
|
2
|
+
|
|
3
|
+
// =================================================================
|
|
4
|
+
// event-bus.ts — 基于 EventEmitter2 的插件事件总线
|
|
5
|
+
//
|
|
6
|
+
// 利用 EventEmitter2 的三个核心特性:
|
|
7
|
+
// 1. wildcard: true → 支持 'terminal:*' 订阅整个命名空间
|
|
8
|
+
// 2. delimiter: ':' → 'namespace:event' 格式的原生支持
|
|
9
|
+
// 3. listener.off() → 订阅返回 Listener 对象,无需手动传 handler 引用
|
|
10
|
+
// =================================================================
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
// ── 全局总线单例(所有插件共享,跨插件通信用)──────────────────────
|
|
14
|
+
//
|
|
15
|
+
// wildcard + delimiter 让命名空间隔离变成原生能力:
|
|
16
|
+
// - 插件 A 订阅 'terminal:*' 可以收到所有 terminal 前缀的事件
|
|
17
|
+
// - 全局订阅 '**' 可以监听所有事件(调试用)
|
|
18
|
+
// - maxListeners 设大一点,避免大量插件时出现警告
|
|
19
|
+
|
|
2
20
|
const globalEmitter = new EventEmitter2({
|
|
3
21
|
wildcard: true,
|
|
4
22
|
// 开启通配符
|
|
5
|
-
delimiter:
|
|
23
|
+
delimiter: ':',
|
|
6
24
|
// 命名空间分隔符,与原版 createModuleEventBus 保持一致
|
|
7
25
|
newListener: false,
|
|
8
26
|
// 不触发 newListener 事件,减少不必要开销
|
|
9
27
|
maxListeners: 200,
|
|
10
28
|
// 对标原版 createModuleEventBus(pluginName, 200) 的容量参数
|
|
11
|
-
verboseMemoryLeak: true
|
|
12
|
-
// 超出 maxListeners 时打印详细的内存泄漏警告
|
|
29
|
+
verboseMemoryLeak: true // 超出 maxListeners 时打印详细的内存泄漏警告
|
|
13
30
|
});
|
|
31
|
+
// ── 插件私有事件总线工厂函数 ──────────────────────────────────────
|
|
32
|
+
//
|
|
33
|
+
// 每个插件获得一个独立的 emitter 实例(私有事件不污染全局),
|
|
34
|
+
// 同时持有 globalEmitter 引用用于跨插件通信。
|
|
35
|
+
//
|
|
36
|
+
// 命名规则:
|
|
37
|
+
// ctx.event.emit('data-loaded') → 实际发出 'terminal:data-loaded'(自动加前缀)
|
|
38
|
+
// ctx.event.on('data-loaded', fn) → 订阅 'terminal:data-loaded'
|
|
39
|
+
// ctx.event.onGlobal('layout:*', fn) → 订阅全局总线上所有 layout 事件(wildcard)
|
|
40
|
+
// ctx.event.emitGlobal('layout:ready')→ 向全局总线发出事件,所有插件都能收到
|
|
41
|
+
|
|
14
42
|
function createPluginEventBus(namespace) {
|
|
43
|
+
// 每个插件自己的私有 emitter(同样开启 wildcard)
|
|
15
44
|
const privateEmitter = new EventEmitter2({
|
|
16
45
|
wildcard: true,
|
|
17
|
-
delimiter:
|
|
46
|
+
delimiter: ':',
|
|
18
47
|
newListener: false,
|
|
19
48
|
maxListeners: 200,
|
|
20
49
|
verboseMemoryLeak: true
|
|
21
50
|
});
|
|
22
|
-
const prefixed =
|
|
51
|
+
const prefixed = event => `${namespace}:${event}`;
|
|
23
52
|
return {
|
|
24
53
|
// ── 私有事件(自动加 namespace 前缀)──────────────────────────
|
|
54
|
+
|
|
25
55
|
emit(event, payload) {
|
|
26
56
|
privateEmitter.emit(prefixed(event), payload);
|
|
27
57
|
},
|
|
@@ -31,11 +61,9 @@ function createPluginEventBus(namespace) {
|
|
|
31
61
|
* 无需调用方保存 handler 引用。
|
|
32
62
|
*/
|
|
33
63
|
on(event, handler) {
|
|
34
|
-
const listener = privateEmitter.on(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{ objectify: true }
|
|
38
|
-
// 返回 Listener 对象而非 emitter 本身
|
|
64
|
+
const listener = privateEmitter.on(prefixed(event), handler, {
|
|
65
|
+
objectify: true
|
|
66
|
+
} // 返回 Listener 对象而非 emitter 本身
|
|
39
67
|
);
|
|
40
68
|
return () => listener.off();
|
|
41
69
|
},
|
|
@@ -52,6 +80,7 @@ function createPluginEventBus(namespace) {
|
|
|
52
80
|
return () => listener.off();
|
|
53
81
|
},
|
|
54
82
|
// ── 全局事件(跨插件通信,走 globalEmitter)────────────────────
|
|
83
|
+
|
|
55
84
|
emitGlobal(event, payload) {
|
|
56
85
|
globalEmitter.emit(event, payload);
|
|
57
86
|
},
|
|
@@ -95,6 +124,7 @@ function createPluginEventBus(namespace) {
|
|
|
95
124
|
});
|
|
96
125
|
},
|
|
97
126
|
// ── 清理(插件 destroy 时调用)─────────────────────────────────
|
|
127
|
+
|
|
98
128
|
/**
|
|
99
129
|
* 移除该插件私有 emitter 上的所有监听器。
|
|
100
130
|
* 全局监听器需要单独 offGlobal(插件应在 teardown 中手动清理)。
|
|
@@ -104,6 +134,9 @@ function createPluginEventBus(namespace) {
|
|
|
104
134
|
}
|
|
105
135
|
};
|
|
106
136
|
}
|
|
137
|
+
|
|
138
|
+
// ── 系统级全局事件工具函数(PluginManager 使用)────────────────────
|
|
139
|
+
|
|
107
140
|
function emitSystemEvent(event, payload) {
|
|
108
141
|
globalEmitter.emit(event, payload);
|
|
109
142
|
}
|
|
@@ -116,9 +149,5 @@ function onSystemEvent(event, handler) {
|
|
|
116
149
|
function getGlobalEmitter() {
|
|
117
150
|
return globalEmitter;
|
|
118
151
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
emitSystemEvent,
|
|
122
|
-
getGlobalEmitter,
|
|
123
|
-
onSystemEvent
|
|
124
|
-
};
|
|
152
|
+
|
|
153
|
+
export { createPluginEventBus, emitSystemEvent, getGlobalEmitter, onSystemEvent };
|