layplux 2.0.0 → 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 +0 -2
- package/package.json +11 -8
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
import { createContent } from "../../utils/index.mjs";
|
|
12
|
-
import { getBuiltInLocale } from "../../locales/index.mjs";
|
|
13
|
-
const viewModeKeys = /* @__PURE__ */ new Set(["DockPinned", "DockUnpinned", "Undock"]);
|
|
1
|
+
import { defineComponent, ref, inject, createVNode, createTextVNode, isVNode } from 'vue';
|
|
2
|
+
import { Dropdown, DropdownMenu, DropdownDivider, DropdownSubmenu, DropdownItem } from '../dropdown/index.mjs';
|
|
3
|
+
import { MoreIcon, MinimizeIcon } from '../icon/index.mjs';
|
|
4
|
+
import { getBuiltInLocale } from '../../locales/index.mjs';
|
|
5
|
+
import { createContent } from '../../utils/vue.mjs';
|
|
6
|
+
|
|
7
|
+
function _isSlot(s) {
|
|
8
|
+
return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !isVNode(s);
|
|
9
|
+
}
|
|
10
|
+
const viewModeKeys = new Set(['DockPinned', 'DockUnpinned', 'Undock']);
|
|
14
11
|
function findItem(items, key) {
|
|
15
12
|
if (!items) return;
|
|
16
13
|
for (const item of items) {
|
|
@@ -22,20 +19,25 @@ function findItem(items, key) {
|
|
|
22
19
|
}
|
|
23
20
|
}
|
|
24
21
|
const PanelView = defineComponent({
|
|
25
|
-
name:
|
|
22
|
+
name: 'PanelView',
|
|
26
23
|
props: {
|
|
27
24
|
anchor: String,
|
|
28
25
|
title: String,
|
|
29
26
|
widget: Object,
|
|
30
27
|
menuItems: Array
|
|
31
28
|
},
|
|
32
|
-
setup(props, {
|
|
29
|
+
setup(props, {
|
|
30
|
+
slots
|
|
31
|
+
}) {
|
|
33
32
|
const panelRef = ref();
|
|
34
|
-
const defaultLocale = ref(getBuiltInLocale(
|
|
35
|
-
const locale = inject(
|
|
36
|
-
const handleClick =
|
|
33
|
+
const defaultLocale = ref(getBuiltInLocale('zh-CN'));
|
|
34
|
+
const locale = inject('layplux-locale', defaultLocale);
|
|
35
|
+
const handleClick = key => {
|
|
37
36
|
const widget = props.widget;
|
|
38
|
-
widget?.event?.emitGlobal(`panel:${widget.name}:menu-click`, {
|
|
37
|
+
widget?.event?.emitGlobal(`panel:${widget.name}:menu-click`, {
|
|
38
|
+
widget,
|
|
39
|
+
key
|
|
40
|
+
});
|
|
39
41
|
const widgetProps = widget?.config.props;
|
|
40
42
|
const panelItems = widgetProps?.panelMenuItems;
|
|
41
43
|
const panelItem = findItem(panelItems, key);
|
|
@@ -45,7 +47,7 @@ const PanelView = defineComponent({
|
|
|
45
47
|
}
|
|
46
48
|
if (viewModeKeys.has(key)) {
|
|
47
49
|
widget?.pane.setViewMode(key);
|
|
48
|
-
} else if (key ===
|
|
50
|
+
} else if (key === 'help') {
|
|
49
51
|
widgetProps?.onHelpClick?.();
|
|
50
52
|
}
|
|
51
53
|
};
|
|
@@ -53,29 +55,32 @@ const PanelView = defineComponent({
|
|
|
53
55
|
props.widget?.focusable.active();
|
|
54
56
|
}
|
|
55
57
|
function renderItems(items, currentMode) {
|
|
56
|
-
return items.map(
|
|
57
|
-
if (item.type ===
|
|
58
|
-
return
|
|
58
|
+
return items.map(item => {
|
|
59
|
+
if (item.type === 'divider') {
|
|
60
|
+
return createVNode(DropdownDivider, {
|
|
61
|
+
"key": item.key ?? 'divider'
|
|
62
|
+
}, null);
|
|
59
63
|
}
|
|
60
|
-
const k = item.key ??
|
|
64
|
+
const k = item.key ?? '';
|
|
61
65
|
if (item.children?.length) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
);
|
|
66
|
+
let _slot;
|
|
67
|
+
return createVNode(DropdownSubmenu, {
|
|
68
|
+
"key": k,
|
|
69
|
+
"title": item.label,
|
|
70
|
+
"icon": item.icon,
|
|
71
|
+
"getContainer": () => panelRef.value
|
|
72
|
+
}, _isSlot(_slot = renderItems(item.children, currentMode)) ? _slot : {
|
|
73
|
+
default: () => [_slot]
|
|
74
|
+
});
|
|
72
75
|
}
|
|
73
|
-
const disabled = currentMode !==
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
const disabled = currentMode !== undefined && viewModeKeys.has(k) && currentMode === k;
|
|
77
|
+
return createVNode(DropdownItem, {
|
|
78
|
+
"key": k,
|
|
79
|
+
"eventKey": k,
|
|
80
|
+
"disabled": disabled
|
|
81
|
+
}, {
|
|
82
|
+
default: () => [item.icon, createTextVNode(" "), item.label]
|
|
83
|
+
});
|
|
79
84
|
});
|
|
80
85
|
}
|
|
81
86
|
return () => {
|
|
@@ -87,66 +92,70 @@ const PanelView = defineComponent({
|
|
|
87
92
|
const hasPanelMenuItems = panelMenuItems && panelMenuItems.length > 0;
|
|
88
93
|
const showHelp = widgetProps?.showHelp !== false;
|
|
89
94
|
const loc = locale.value.panel;
|
|
90
|
-
const finalInnerItems = [
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
95
|
+
const finalInnerItems = [{
|
|
96
|
+
key: 'viewMode',
|
|
97
|
+
label: loc.viewMode,
|
|
98
|
+
children: [{
|
|
99
|
+
key: 'DockPinned',
|
|
100
|
+
label: loc.dockPinned
|
|
101
|
+
}, {
|
|
102
|
+
key: 'DockUnpinned',
|
|
103
|
+
label: loc.dockUnpinned
|
|
104
|
+
}, {
|
|
105
|
+
key: 'Undock',
|
|
106
|
+
label: loc.undock
|
|
107
|
+
}]
|
|
108
|
+
}, {
|
|
109
|
+
type: 'divider'
|
|
110
|
+
}, ...(showHelp ? [{
|
|
111
|
+
key: 'help',
|
|
112
|
+
label: loc.help
|
|
113
|
+
}] : [])];
|
|
103
114
|
const panelTitleExtra = widgetProps?.panelTitleExtra;
|
|
104
115
|
const panelActionsExtra = widgetProps?.panelActionsExtra;
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
] });
|
|
116
|
+
return createVNode("div", {
|
|
117
|
+
"ref": panelRef,
|
|
118
|
+
"id": widget?.id,
|
|
119
|
+
"class": "layplux-panel",
|
|
120
|
+
"onClick": handlePanelClick
|
|
121
|
+
}, [createVNode("div", {
|
|
122
|
+
"class": "layplux-panel__header"
|
|
123
|
+
}, [createVNode("span", {
|
|
124
|
+
"class": "layplux-panel__title"
|
|
125
|
+
}, [props.title ?? widget?.name]), panelTitleExtra && createContent(panelTitleExtra), createVNode("div", {
|
|
126
|
+
"class": "layplux-panel__actions"
|
|
127
|
+
}, [panelActionsExtra && createContent(panelActionsExtra), slots.actionsExtra?.(), createVNode(Dropdown, {
|
|
128
|
+
"trigger": "click",
|
|
129
|
+
"placement": "bottom-start",
|
|
130
|
+
"onClick": handleClick,
|
|
131
|
+
"getContainer": () => panelRef.value
|
|
132
|
+
}, {
|
|
133
|
+
default: () => createVNode("button", {
|
|
134
|
+
"class": "layplux-panel__action-btn",
|
|
135
|
+
"title": loc.more
|
|
136
|
+
}, [createVNode(MoreIcon, {
|
|
137
|
+
"size": 16
|
|
138
|
+
}, null)]),
|
|
139
|
+
overlay: () => createVNode(DropdownMenu, null, {
|
|
140
|
+
default: () => [hasPanelMenuItems && renderItems(panelMenuItems), hasPanelMenuItems && hasCustomItems && createVNode(DropdownDivider, null, null), hasCustomItems && renderItems(props.menuItems), (hasPanelMenuItems || hasCustomItems) && createVNode(DropdownDivider, null, null), renderItems(finalInnerItems, currentMode)]
|
|
141
|
+
})
|
|
142
|
+
}), createVNode("button", {
|
|
143
|
+
"class": "layplux-panel__action-btn",
|
|
144
|
+
"title": loc.minimize,
|
|
145
|
+
"onClick": () => {
|
|
146
|
+
widget?.event?.emitGlobal(`panel:${widget.name}:minimize`, {
|
|
147
|
+
widget
|
|
148
|
+
});
|
|
149
|
+
widget?.container?.deactivate();
|
|
150
|
+
}
|
|
151
|
+
}, [createVNode(MinimizeIcon, {
|
|
152
|
+
"size": 16
|
|
153
|
+
}, null)])])]), createVNode("div", {
|
|
154
|
+
"id": props.anchor,
|
|
155
|
+
"class": "layplux-panel__body"
|
|
156
|
+
}, null)]);
|
|
147
157
|
};
|
|
148
158
|
}
|
|
149
159
|
});
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
};
|
|
160
|
+
|
|
161
|
+
export { PanelView };
|
|
@@ -1,81 +1,115 @@
|
|
|
1
|
-
import { Fragment,
|
|
2
|
-
|
|
3
|
-
defineComponent,
|
|
4
|
-
ref,
|
|
5
|
-
watch,
|
|
6
|
-
nextTick,
|
|
7
|
-
onMounted,
|
|
8
|
-
onUnmounted,
|
|
9
|
-
Teleport
|
|
10
|
-
} from "vue";
|
|
1
|
+
import { defineComponent, ref, watch, onMounted, onUnmounted, createVNode, Fragment, Teleport, nextTick } from 'vue';
|
|
2
|
+
|
|
11
3
|
function computePosition(triggerRect, popupW, popupH, placement, offsetX, offsetY) {
|
|
12
4
|
const vw = window.innerWidth;
|
|
13
5
|
const vh = window.innerHeight;
|
|
14
6
|
const PADDING = 8;
|
|
15
7
|
let top = 0;
|
|
16
8
|
let left = 0;
|
|
17
|
-
const [main, align] = placement.split(
|
|
18
|
-
|
|
9
|
+
const [main, align] = placement.split('-');
|
|
10
|
+
|
|
11
|
+
// Vertical placements
|
|
12
|
+
if (main === 'bottom') {
|
|
19
13
|
top = triggerRect.bottom + offsetY;
|
|
20
|
-
if (align ===
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
if (align === 'start') left = triggerRect.left + offsetX;else if (align === 'end') left = triggerRect.right - popupW + offsetX;else left = triggerRect.left + triggerRect.width / 2 - popupW / 2 + offsetX;
|
|
15
|
+
|
|
16
|
+
// Flip to top if not enough space
|
|
23
17
|
if (top + popupH > vh - PADDING) {
|
|
24
18
|
top = triggerRect.top - popupH - offsetY;
|
|
25
|
-
placement = align ? `top-${align}` :
|
|
19
|
+
placement = align ? `top-${align}` : 'top';
|
|
26
20
|
}
|
|
27
|
-
} else if (main ===
|
|
21
|
+
} else if (main === 'top') {
|
|
28
22
|
top = triggerRect.top - popupH - offsetY;
|
|
29
|
-
if (align ===
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
if (align === 'start') left = triggerRect.left + offsetX;else if (align === 'end') left = triggerRect.right - popupW + offsetX;else left = triggerRect.left + triggerRect.width / 2 - popupW / 2 + offsetX;
|
|
24
|
+
|
|
25
|
+
// Flip to bottom if not enough space
|
|
32
26
|
if (top < PADDING) {
|
|
33
27
|
top = triggerRect.bottom + offsetY;
|
|
34
|
-
placement = align ? `bottom-${align}` :
|
|
28
|
+
placement = align ? `bottom-${align}` : 'bottom';
|
|
35
29
|
}
|
|
36
|
-
}
|
|
30
|
+
}
|
|
31
|
+
// Horizontal placements
|
|
32
|
+
else if (main === 'right') {
|
|
37
33
|
left = triggerRect.right + offsetX;
|
|
38
|
-
if (align ===
|
|
39
|
-
else if (align === "end") top = triggerRect.bottom - popupH + offsetY;
|
|
40
|
-
else top = triggerRect.top + triggerRect.height / 2 - popupH / 2 + offsetY;
|
|
34
|
+
if (align === 'start') top = triggerRect.top + offsetY;else if (align === 'end') top = triggerRect.bottom - popupH + offsetY;else top = triggerRect.top + triggerRect.height / 2 - popupH / 2 + offsetY;
|
|
41
35
|
if (left + popupW > vw - PADDING) {
|
|
42
36
|
left = triggerRect.left - popupW - offsetX;
|
|
43
|
-
placement = align ? `left-${align}` :
|
|
37
|
+
placement = align ? `left-${align}` : 'left';
|
|
44
38
|
}
|
|
45
|
-
} else if (main ===
|
|
39
|
+
} else if (main === 'left') {
|
|
46
40
|
left = triggerRect.left - popupW - offsetX;
|
|
47
|
-
if (align ===
|
|
48
|
-
else if (align === "end") top = triggerRect.bottom - popupH + offsetY;
|
|
49
|
-
else top = triggerRect.top + triggerRect.height / 2 - popupH / 2 + offsetY;
|
|
41
|
+
if (align === 'start') top = triggerRect.top + offsetY;else if (align === 'end') top = triggerRect.bottom - popupH + offsetY;else top = triggerRect.top + triggerRect.height / 2 - popupH / 2 + offsetY;
|
|
50
42
|
if (left < PADDING) {
|
|
51
43
|
left = triggerRect.right + offsetX;
|
|
52
|
-
placement = align ? `right-${align}` :
|
|
44
|
+
placement = align ? `right-${align}` : 'right';
|
|
53
45
|
}
|
|
54
46
|
}
|
|
47
|
+
|
|
48
|
+
// Clamp to viewport
|
|
55
49
|
top = Math.max(PADDING, Math.min(top, vh - popupH - PADDING));
|
|
56
50
|
left = Math.max(PADDING, Math.min(left, vw - popupW - PADDING));
|
|
57
|
-
return {
|
|
51
|
+
return {
|
|
52
|
+
position: {
|
|
53
|
+
top,
|
|
54
|
+
left
|
|
55
|
+
},
|
|
56
|
+
placement
|
|
57
|
+
};
|
|
58
58
|
}
|
|
59
59
|
const Popup = defineComponent({
|
|
60
|
-
name:
|
|
60
|
+
name: 'LaypluxPopup',
|
|
61
61
|
props: {
|
|
62
62
|
visible: Boolean,
|
|
63
|
-
trigger: {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
trigger: {
|
|
64
|
+
type: String,
|
|
65
|
+
default: 'hover'
|
|
66
|
+
},
|
|
67
|
+
placement: {
|
|
68
|
+
type: String,
|
|
69
|
+
default: 'bottom'
|
|
70
|
+
},
|
|
71
|
+
offset: {
|
|
72
|
+
type: Object,
|
|
73
|
+
default: () => ({
|
|
74
|
+
y: 4
|
|
75
|
+
})
|
|
76
|
+
},
|
|
77
|
+
mouseEnterDelay: {
|
|
78
|
+
type: Number,
|
|
79
|
+
default: 100
|
|
80
|
+
},
|
|
81
|
+
mouseLeaveDelay: {
|
|
82
|
+
type: Number,
|
|
83
|
+
default: 100
|
|
84
|
+
},
|
|
85
|
+
disabled: {
|
|
86
|
+
type: Boolean,
|
|
87
|
+
default: false
|
|
88
|
+
},
|
|
89
|
+
destroyOnClose: {
|
|
90
|
+
type: Boolean,
|
|
91
|
+
default: true
|
|
92
|
+
},
|
|
93
|
+
getContainer: {
|
|
94
|
+
type: Function,
|
|
95
|
+
default: () => document.body
|
|
96
|
+
}
|
|
71
97
|
},
|
|
72
|
-
emits: [
|
|
73
|
-
setup(props, {
|
|
98
|
+
emits: ['update:visible'],
|
|
99
|
+
setup(props, {
|
|
100
|
+
emit,
|
|
101
|
+
slots
|
|
102
|
+
}) {
|
|
74
103
|
const triggerRef = ref();
|
|
75
104
|
const popupRef = ref();
|
|
76
|
-
const position = ref({
|
|
105
|
+
const position = ref({
|
|
106
|
+
top: 0,
|
|
107
|
+
left: 0
|
|
108
|
+
});
|
|
77
109
|
const currentPlacement = ref(props.placement);
|
|
110
|
+
// Whether the popup DOM should exist
|
|
78
111
|
const mounted = ref(false);
|
|
112
|
+
// Whether the popup is in its visible animation state
|
|
79
113
|
const animatingIn = ref(false);
|
|
80
114
|
let enterTimer = null;
|
|
81
115
|
let leaveTimer = null;
|
|
@@ -98,15 +132,11 @@ const Popup = defineComponent({
|
|
|
98
132
|
if (!triggerRef.value || !popupRef.value) return;
|
|
99
133
|
const triggerRect = triggerRef.value.getBoundingClientRect();
|
|
100
134
|
const popupRect = popupRef.value.getBoundingClientRect();
|
|
101
|
-
const {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
props.placement,
|
|
107
|
-
ox,
|
|
108
|
-
oy
|
|
109
|
-
);
|
|
135
|
+
const {
|
|
136
|
+
x: ox = 0,
|
|
137
|
+
y: oy = 0
|
|
138
|
+
} = props.offset;
|
|
139
|
+
const result = computePosition(triggerRect, popupRect.width, popupRect.height, props.placement, ox, oy);
|
|
110
140
|
position.value = result.position;
|
|
111
141
|
currentPlacement.value = result.placement;
|
|
112
142
|
};
|
|
@@ -115,9 +145,10 @@ const Popup = defineComponent({
|
|
|
115
145
|
clearTimers();
|
|
116
146
|
enterTimer = setTimeout(() => {
|
|
117
147
|
mounted.value = true;
|
|
118
|
-
emit(
|
|
148
|
+
emit('update:visible', true);
|
|
119
149
|
void nextTick(() => {
|
|
120
150
|
updatePosition();
|
|
151
|
+
// Trigger enter animation
|
|
121
152
|
requestAnimationFrame(() => {
|
|
122
153
|
animatingIn.value = true;
|
|
123
154
|
});
|
|
@@ -129,10 +160,12 @@ const Popup = defineComponent({
|
|
|
129
160
|
const delay = immediate ? 0 : props.mouseLeaveDelay;
|
|
130
161
|
leaveTimer = setTimeout(() => {
|
|
131
162
|
animatingIn.value = false;
|
|
163
|
+
|
|
164
|
+
// Wait for CSS transition to finish before removing DOM
|
|
132
165
|
leaveAnimationTimer = setTimeout(() => {
|
|
133
166
|
mounted.value = false;
|
|
134
|
-
emit(
|
|
135
|
-
}, 200);
|
|
167
|
+
emit('update:visible', false);
|
|
168
|
+
}, 200); // Match CSS transition duration
|
|
136
169
|
}, delay);
|
|
137
170
|
};
|
|
138
171
|
const toggle = () => {
|
|
@@ -143,75 +176,80 @@ const Popup = defineComponent({
|
|
|
143
176
|
show();
|
|
144
177
|
}
|
|
145
178
|
};
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
watch(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
currentPlacement.value = p;
|
|
157
|
-
}
|
|
158
|
-
);
|
|
179
|
+
|
|
180
|
+
// Sync external visible changes
|
|
181
|
+
watch(() => props.visible, v => {
|
|
182
|
+
if (v && !mounted.value) show();else if (!v && mounted.value) hide(true);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Sync external placement changes
|
|
186
|
+
watch(() => props.placement, p => {
|
|
187
|
+
currentPlacement.value = p;
|
|
188
|
+
});
|
|
159
189
|
const onResize = () => {
|
|
160
190
|
if (mounted.value && animatingIn.value) updatePosition();
|
|
161
191
|
};
|
|
162
192
|
onMounted(() => {
|
|
163
|
-
window.addEventListener(
|
|
164
|
-
window.addEventListener(
|
|
165
|
-
document.addEventListener(
|
|
166
|
-
document.addEventListener(
|
|
193
|
+
window.addEventListener('resize', onResize);
|
|
194
|
+
window.addEventListener('scroll', onResize, true);
|
|
195
|
+
document.addEventListener('mousedown', onClickOutside, true);
|
|
196
|
+
document.addEventListener('keydown', onEsc);
|
|
167
197
|
});
|
|
168
198
|
onUnmounted(() => {
|
|
169
199
|
clearTimers();
|
|
170
|
-
window.removeEventListener(
|
|
171
|
-
window.removeEventListener(
|
|
172
|
-
document.removeEventListener(
|
|
173
|
-
document.removeEventListener(
|
|
200
|
+
window.removeEventListener('resize', onResize);
|
|
201
|
+
window.removeEventListener('scroll', onResize, true);
|
|
202
|
+
document.removeEventListener('mousedown', onClickOutside, true);
|
|
203
|
+
document.removeEventListener('keydown', onEsc);
|
|
174
204
|
});
|
|
175
|
-
|
|
205
|
+
|
|
206
|
+
// Click outside
|
|
207
|
+
const onClickOutside = e => {
|
|
176
208
|
if (!mounted.value) return;
|
|
177
209
|
const target = e.target;
|
|
178
210
|
const trigger = triggerRef.value;
|
|
179
211
|
const popup = popupRef.value;
|
|
180
212
|
if (trigger && trigger.contains(target)) return;
|
|
181
213
|
if (popup && popup.contains(target)) return;
|
|
182
|
-
if (props.trigger ===
|
|
214
|
+
if (props.trigger === 'click' || props.trigger === 'contextmenu') {
|
|
183
215
|
hide(true);
|
|
184
216
|
}
|
|
185
217
|
};
|
|
186
|
-
|
|
187
|
-
|
|
218
|
+
|
|
219
|
+
// ESC key
|
|
220
|
+
const onEsc = e => {
|
|
221
|
+
if (e.key === 'Escape' && mounted.value) {
|
|
188
222
|
hide(true);
|
|
189
223
|
}
|
|
190
224
|
};
|
|
225
|
+
|
|
226
|
+
// Trigger event handlers
|
|
191
227
|
const onTriggerMouseEnter = () => {
|
|
192
|
-
if (props.trigger ===
|
|
228
|
+
if (props.trigger === 'hover') show();
|
|
193
229
|
};
|
|
194
230
|
const onTriggerMouseLeave = () => {
|
|
195
|
-
if (props.trigger ===
|
|
231
|
+
if (props.trigger === 'hover') hide();
|
|
196
232
|
};
|
|
197
|
-
const onTriggerClick =
|
|
198
|
-
if (props.trigger ===
|
|
233
|
+
const onTriggerClick = e => {
|
|
234
|
+
if (props.trigger === 'click') {
|
|
199
235
|
e.stopPropagation();
|
|
200
236
|
toggle();
|
|
201
237
|
}
|
|
202
238
|
};
|
|
203
239
|
const onTriggerFocus = () => {
|
|
204
|
-
if (props.trigger ===
|
|
240
|
+
if (props.trigger === 'focus') show();
|
|
205
241
|
};
|
|
206
242
|
const onTriggerBlur = () => {
|
|
207
|
-
if (props.trigger ===
|
|
243
|
+
if (props.trigger === 'focus') hide(true);
|
|
208
244
|
};
|
|
209
|
-
const onTriggerContextmenu =
|
|
210
|
-
if (props.trigger ===
|
|
245
|
+
const onTriggerContextmenu = e => {
|
|
246
|
+
if (props.trigger === 'contextmenu') {
|
|
211
247
|
e.preventDefault();
|
|
212
248
|
show();
|
|
213
249
|
}
|
|
214
250
|
};
|
|
251
|
+
|
|
252
|
+
// Popup hover (for safe triangle)
|
|
215
253
|
const onPopupMouseEnter = () => {
|
|
216
254
|
if (leaveTimer) {
|
|
217
255
|
clearTimeout(leaveTimer);
|
|
@@ -219,50 +257,37 @@ const Popup = defineComponent({
|
|
|
219
257
|
}
|
|
220
258
|
};
|
|
221
259
|
const onPopupMouseLeave = () => {
|
|
222
|
-
if (props.trigger ===
|
|
260
|
+
if (props.trigger === 'hover') hide();
|
|
223
261
|
};
|
|
224
262
|
return () => {
|
|
225
263
|
const container = props.getContainer();
|
|
226
|
-
return
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
style: {
|
|
252
|
-
position: "fixed",
|
|
253
|
-
top: `${position.value.top}px`,
|
|
254
|
-
left: `${position.value.left}px`,
|
|
255
|
-
zIndex: "var(--layplux-popup-z-index, 2000)"
|
|
256
|
-
},
|
|
257
|
-
onMouseenter: onPopupMouseEnter,
|
|
258
|
-
onMouseleave: onPopupMouseLeave,
|
|
259
|
-
children: slots.content?.()
|
|
260
|
-
}
|
|
261
|
-
) })
|
|
262
|
-
] });
|
|
264
|
+
return createVNode(Fragment, null, [createVNode("span", {
|
|
265
|
+
"ref": triggerRef,
|
|
266
|
+
"class": "layplux-popup-trigger",
|
|
267
|
+
"onMouseenter": onTriggerMouseEnter,
|
|
268
|
+
"onMouseleave": onTriggerMouseLeave,
|
|
269
|
+
"onClick": onTriggerClick,
|
|
270
|
+
"onFocus": onTriggerFocus,
|
|
271
|
+
"onBlur": onTriggerBlur,
|
|
272
|
+
"onContextmenu": onTriggerContextmenu
|
|
273
|
+
}, [slots.default?.()]), (mounted.value || !props.destroyOnClose) && createVNode(Teleport, {
|
|
274
|
+
"to": container
|
|
275
|
+
}, {
|
|
276
|
+
default: () => [createVNode("div", {
|
|
277
|
+
"ref": popupRef,
|
|
278
|
+
"class": ['layplux-portal', 'layplux-popup', currentPlacement.value, animatingIn.value && 'layplux-popup--visible'],
|
|
279
|
+
"style": {
|
|
280
|
+
position: 'fixed',
|
|
281
|
+
top: `${position.value.top}px`,
|
|
282
|
+
left: `${position.value.left}px`,
|
|
283
|
+
zIndex: 'var(--layplux-popup-z-index, 2000)'
|
|
284
|
+
},
|
|
285
|
+
"onMouseenter": onPopupMouseEnter,
|
|
286
|
+
"onMouseleave": onPopupMouseLeave
|
|
287
|
+
}, [slots.content?.()])]
|
|
288
|
+
})]);
|
|
263
289
|
};
|
|
264
290
|
}
|
|
265
291
|
});
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
};
|
|
292
|
+
|
|
293
|
+
export { Popup };
|