@cloudron/pankow 3.2.15 → 3.2.17
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/components/CircleChart.vue +40 -0
- package/components/Dialog.vue +7 -7
- package/components/Menu.vue +24 -10
- package/components/Menu_BACKUP_252296.vue +336 -0
- package/components/Menu_BACKUP_252415.vue +336 -0
- package/components/Menu_BASE_252296.vue +315 -0
- package/components/Menu_BASE_252415.vue +315 -0
- package/components/Menu_LOCAL_252296.vue +314 -0
- package/components/Menu_LOCAL_252415.vue +314 -0
- package/components/Menu_REMOTE_252296.vue +319 -0
- package/components/Menu_REMOTE_252415.vue +319 -0
- package/components/MultiSelect.vue +1 -1
- package/components/SingleSelect.vue +1 -1
- package/components/TextInput.vue +11 -1
- package/package.json +6 -6
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
|
|
3
|
+
import { computed } from 'vue';
|
|
4
|
+
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
values: Array,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// const normalizedValue = computed(() => {
|
|
10
|
+
// if (props.value < 0) return 0;
|
|
11
|
+
// if (props.value > 100) return 100;
|
|
12
|
+
// return props.value;
|
|
13
|
+
// });
|
|
14
|
+
|
|
15
|
+
// we want 100 to be the circumverence so we can neatly use percentage: radius = 100 / ( 3,14159 * 2 ) = 15,9155
|
|
16
|
+
const radius = 15.9155;
|
|
17
|
+
const stroke = 10;
|
|
18
|
+
|
|
19
|
+
function calculateViewbox() {
|
|
20
|
+
return `0 0 ${radius*2+stroke} ${radius*2+stroke}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function calculateArcPath() {
|
|
24
|
+
return `M${radius+stroke/2} ${stroke/2} a ${radius} ${radius} 0 0 1 0 ${radius*2} a ${radius} ${radius} 0 0 1 0 -${radius*2}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<div class="pankow-circle-chart">
|
|
31
|
+
<svg :viewBox="calculateViewbox()" xmlns="http://www.w3.org/2000/svg">
|
|
32
|
+
<path :d="calculateArcPath()" fill="none" stroke="red" :stroke-width="stroke" stroke-dasharray="75, 100" @click.stop="onClick('red')" />
|
|
33
|
+
<path :d="calculateArcPath()" fill="none" stroke="green" :stroke-width="stroke" stroke-dasharray="30, 100" @click.stop="onClick('green') "/>
|
|
34
|
+
</svg>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style>
|
|
39
|
+
|
|
40
|
+
</style>
|
package/components/Dialog.vue
CHANGED
|
@@ -109,7 +109,7 @@ defineExpose({ open, close });
|
|
|
109
109
|
<div class="pankow-dialog-backdrop" @click="onDismiss" v-show="visible" :style="{ 'z-index': zIndex }"></div>
|
|
110
110
|
</Transition>
|
|
111
111
|
<Transition name="pankow-bounce-center-top">
|
|
112
|
-
<div ref="dialog" class="pankow-dialog"
|
|
112
|
+
<div ref="dialog" class="pankow-dialog" v-bind="$attrs" :class="{ 'pankow-dialog-center': center }" v-show="visible" @keydown.esc="onDismiss" tabindex="0" :style="mergedStyle">
|
|
113
113
|
<div class="pankow-dialog-header" v-show="title">
|
|
114
114
|
{{ title }}
|
|
115
115
|
<Icon v-show="showX" icon="fa-solid fa-xmark" style="cursor: pointer;" @click="onReject"/>
|
|
@@ -167,31 +167,31 @@ defineExpose({ open, close });
|
|
|
167
167
|
|
|
168
168
|
.pankow-dialog-header {
|
|
169
169
|
font-size: 20px;
|
|
170
|
-
padding:
|
|
170
|
+
padding: 15px;
|
|
171
171
|
display: flex;
|
|
172
172
|
justify-content: space-between;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
.pankow-dialog-body {
|
|
176
|
-
padding: 0
|
|
176
|
+
padding: 0 15px;
|
|
177
177
|
overflow: auto;
|
|
178
178
|
transition: all 250ms;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
.pankow-dialog-body-no-header {
|
|
182
|
-
padding:
|
|
182
|
+
padding: 15px;
|
|
183
183
|
padding-bottom: 0;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
.pankow-dialog-body-no-buttons {
|
|
187
|
-
padding-bottom:
|
|
187
|
+
padding-bottom: 15px;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
.pankow-dialog-buttons {
|
|
191
191
|
display: flex;
|
|
192
192
|
gap: 6px;
|
|
193
|
-
padding:
|
|
193
|
+
padding: 15px;
|
|
194
194
|
text-align: right;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
</style>
|
|
197
|
+
</style>
|
package/components/Menu.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
|
|
3
|
-
import { nextTick, useTemplateRef, computed, ref
|
|
3
|
+
import { nextTick, useTemplateRef, computed, ref } from 'vue';
|
|
4
4
|
|
|
5
5
|
import MenuItem from './MenuItem.vue';
|
|
6
6
|
import MenuItemLink from './MenuItemLink.vue';
|
|
@@ -70,6 +70,7 @@ const props = defineProps({
|
|
|
70
70
|
|
|
71
71
|
const openEventTimeStamp = ref(0);
|
|
72
72
|
const forElement = ref(null);
|
|
73
|
+
let prevFocusElement = null;
|
|
73
74
|
const isOpen = ref(false);
|
|
74
75
|
let pageX = 0;
|
|
75
76
|
let targetBottom = 0;
|
|
@@ -145,6 +146,7 @@ async function open(event, element = null) {
|
|
|
145
146
|
targetTop = element ? element.getBoundingClientRect().top : event.pageY;
|
|
146
147
|
offsetY.value = element ? (element.getBoundingClientRect().height + 2) : 0; // offset in case we roll up
|
|
147
148
|
forElement.value = element;
|
|
149
|
+
prevFocusElement = document.activeElement;
|
|
148
150
|
|
|
149
151
|
if (!container.value) return;
|
|
150
152
|
|
|
@@ -162,10 +164,11 @@ async function open(event, element = null) {
|
|
|
162
164
|
if (event.type === 'keydown' && event.key === 'ArrowUp') selectUp();
|
|
163
165
|
else if (event.type === 'keydown') selectDown();
|
|
164
166
|
else container.value.focus();
|
|
167
|
+
}
|
|
165
168
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
+
function onBackdrop(event) {
|
|
170
|
+
close();
|
|
171
|
+
event.preventDefault();
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
function selectUp() {
|
|
@@ -209,9 +212,13 @@ function selectDown() {
|
|
|
209
212
|
}
|
|
210
213
|
|
|
211
214
|
function close() {
|
|
212
|
-
window.document.removeEventListener('click', blurEventHandler);
|
|
213
215
|
isOpen.value = false;
|
|
214
216
|
container.value.style.maxHeight = 'unset';
|
|
217
|
+
|
|
218
|
+
// restore focus
|
|
219
|
+
if (prevFocusElement) prevFocusElement.focus();
|
|
220
|
+
prevFocusElement = null;
|
|
221
|
+
|
|
215
222
|
emit('close');
|
|
216
223
|
}
|
|
217
224
|
|
|
@@ -267,10 +274,6 @@ function position() {
|
|
|
267
274
|
}
|
|
268
275
|
}
|
|
269
276
|
|
|
270
|
-
onUnmounted(() => {
|
|
271
|
-
window.document.removeEventListener('click', blurEventHandler);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
277
|
defineExpose({
|
|
275
278
|
isOpen,
|
|
276
279
|
open,
|
|
@@ -281,9 +284,10 @@ defineExpose({
|
|
|
281
284
|
|
|
282
285
|
<template>
|
|
283
286
|
<teleport to="#app">
|
|
287
|
+
<div class="pankow-menu-backdrop" @click="onBackdrop($event)" @contextmenu="onBackdrop($event)" v-show="isOpen"></div>
|
|
284
288
|
<Transition :name="rollUp ? 'pankow-roll-up' : 'pankow-roll-down'">
|
|
285
289
|
<div class="pankow-menu" v-show="isOpen" ref="container" tabindex="0" @keydown.up.stop="selectUp()" @keydown.down.stop="selectDown()" @keydown.esc.stop="close()" @keydown="onKeyDown">
|
|
286
|
-
<TextInput placeholder="Filter ..." style="border: 0; padding: 8px 12px;" v-model="searchString" v-if="searchThreshold < model.length"/>
|
|
290
|
+
<TextInput placeholder="Filter ..." @keydown.up.stop="selectUp()" @keydown.down.stop="selectDown()" @keydown.stop @keydown.esc.stop="close()" @click.stop style="border: 0; padding: 8px 12px;" v-model="searchString" v-if="searchThreshold < model.length"/>
|
|
287
291
|
<component v-for="item in visibleItems" ref="itemElements" :is="item.type || (item.href ? MenuItemLink : MenuItem)" @activated="onItemActivated(item)" :item="item" :has-icons="hasIcons" />
|
|
288
292
|
<MenuItem v-if="model.length === 0" :item="emptyItem"/>
|
|
289
293
|
</div>
|
|
@@ -293,6 +297,16 @@ defineExpose({
|
|
|
293
297
|
|
|
294
298
|
<style>
|
|
295
299
|
|
|
300
|
+
.pankow-menu-backdrop {
|
|
301
|
+
position: fixed;
|
|
302
|
+
height: 100%;
|
|
303
|
+
width: 100%;
|
|
304
|
+
left: 0px;
|
|
305
|
+
top: 0px;
|
|
306
|
+
z-index: 3001;
|
|
307
|
+
background-color: transparent;
|
|
308
|
+
}
|
|
309
|
+
|
|
296
310
|
.pankow-menu {
|
|
297
311
|
position: fixed;
|
|
298
312
|
box-shadow: var(--pankow-menu-shadow);
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
|
|
3
|
+
import { nextTick, useTemplateRef, computed, ref } from 'vue';
|
|
4
|
+
|
|
5
|
+
import MenuItem from './MenuItem.vue';
|
|
6
|
+
import MenuItemLink from './MenuItemLink.vue';
|
|
7
|
+
import TextInput from './TextInput.vue';
|
|
8
|
+
|
|
9
|
+
function getViewport() {
|
|
10
|
+
const win = window,
|
|
11
|
+
d = document,
|
|
12
|
+
e = d.documentElement,
|
|
13
|
+
g = d.getElementsByTagName('body')[0],
|
|
14
|
+
w = win.innerWidth || e.clientWidth || g.clientWidth,
|
|
15
|
+
h = win.innerHeight || e.clientHeight || g.clientHeight;
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
width: w,
|
|
19
|
+
height: h
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getHiddenElementSize(element) {
|
|
24
|
+
if (element) {
|
|
25
|
+
const originalVisibility = element.style.visibility;
|
|
26
|
+
const originalDisplay = element.style.display;
|
|
27
|
+
|
|
28
|
+
element.style.visibility = 'hidden';
|
|
29
|
+
element.style.display = 'block';
|
|
30
|
+
|
|
31
|
+
element.clientHeight; // force reflow
|
|
32
|
+
|
|
33
|
+
const height = element.offsetHeight;
|
|
34
|
+
const width = element.offsetWidth;
|
|
35
|
+
|
|
36
|
+
element.style.display = originalDisplay;
|
|
37
|
+
element.style.visibility = originalVisibility;
|
|
38
|
+
|
|
39
|
+
return { height, width };
|
|
40
|
+
}
|
|
41
|
+
return { height: 0, width: 0 };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
function getActiveElement(children) {
|
|
46
|
+
for (const child of children) {
|
|
47
|
+
if (child === document.activeElement) return child;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// outsideClickListener: null,
|
|
53
|
+
const container = useTemplateRef('container');
|
|
54
|
+
const itemElements = useTemplateRef('itemElements');
|
|
55
|
+
|
|
56
|
+
const emit = defineEmits([ 'close' ]);
|
|
57
|
+
|
|
58
|
+
const props = defineProps({
|
|
59
|
+
icon: String,
|
|
60
|
+
model: Array,
|
|
61
|
+
closeOnActivation: {
|
|
62
|
+
type: Boolean,
|
|
63
|
+
default: true,
|
|
64
|
+
},
|
|
65
|
+
searchThreshold: {
|
|
66
|
+
type: Number,
|
|
67
|
+
default: Infinity,
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const openEventTimeStamp = ref(0);
|
|
72
|
+
const forElement = ref(null);
|
|
73
|
+
const isOpen = ref(false);
|
|
74
|
+
let pageX = 0;
|
|
75
|
+
let targetBottom = 0;
|
|
76
|
+
let targetTop = 0;
|
|
77
|
+
const offsetY = ref(0);
|
|
78
|
+
const rollUp = ref(false);
|
|
79
|
+
const searchString = ref('');
|
|
80
|
+
const emptyItem = ref({
|
|
81
|
+
label: '',
|
|
82
|
+
disabled: true,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const hasIcons = computed(() => {
|
|
86
|
+
return !!props.model.find((item) => !!item.icon);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const visibleItems = computed(() => {
|
|
90
|
+
if (!searchString.value) return props.model;
|
|
91
|
+
const s = searchString.value.toLowerCase();
|
|
92
|
+
return props.model.filter((item) => item.label.toLowerCase().indexOf(s) !== -1);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// select menu item by .label
|
|
96
|
+
function select(elem) {
|
|
97
|
+
let next = container.value.children[0];
|
|
98
|
+
while (true) {
|
|
99
|
+
if (!next) return;
|
|
100
|
+
|
|
101
|
+
if (next.innerText !== elem.label) {
|
|
102
|
+
next = next.nextElementSibling;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
next.focus();
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let search = '';
|
|
112
|
+
let debounceTimer = null;
|
|
113
|
+
function onKeyDown(event) {
|
|
114
|
+
// only handle alphanumerics
|
|
115
|
+
if (!/^[a-zA-Z0-9]$/.test(event.key)) return;
|
|
116
|
+
|
|
117
|
+
const key = event.key.toLowerCase();
|
|
118
|
+
|
|
119
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
120
|
+
debounceTimer = setTimeout(() => search = '', 500);
|
|
121
|
+
|
|
122
|
+
search += key;
|
|
123
|
+
|
|
124
|
+
const found = visibleItems.value.find(elem => {
|
|
125
|
+
if (!elem.label) return false;
|
|
126
|
+
return elem.label.toLowerCase().indexOf(search) === 0;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (found) select(found)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function onItemActivated(item) {
|
|
133
|
+
if (item.action instanceof Function) item.action(item);
|
|
134
|
+
if (props.closeOnActivation) close();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function open(event, element = null) {
|
|
138
|
+
isOpen.value = true;
|
|
139
|
+
pageX = element ? element.getBoundingClientRect().left : event.pageX;
|
|
140
|
+
targetBottom = element ? element.getBoundingClientRect().bottom : event.pageY;
|
|
141
|
+
targetTop = element ? element.getBoundingClientRect().top : event.pageY;
|
|
142
|
+
offsetY.value = element ? (element.getBoundingClientRect().height + 2) : 0; // offset in case we roll up
|
|
143
|
+
forElement.value = element;
|
|
144
|
+
|
|
145
|
+
if (!container.value) return;
|
|
146
|
+
|
|
147
|
+
if (element) container.value.style.minWidth = element.getBoundingClientRect().width + 'px';
|
|
148
|
+
|
|
149
|
+
await nextTick();
|
|
150
|
+
|
|
151
|
+
position();
|
|
152
|
+
|
|
153
|
+
openEventTimeStamp.value = event.timeStamp;
|
|
154
|
+
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
|
|
157
|
+
// if opened from a key action, immediately select first
|
|
158
|
+
if (event.type === 'keydown' && event.key === 'ArrowUp') selectUp();
|
|
159
|
+
else if (event.type === 'keydown') selectDown();
|
|
160
|
+
else container.value.focus();
|
|
161
|
+
<<<<<<< HEAD
|
|
162
|
+
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
window.document.addEventListener('click', blurEventHandler);
|
|
165
|
+
}, 0);
|
|
166
|
+
=======
|
|
167
|
+
>>>>>>> parent of c42ecf7 (Use click events on document.body instead of a backdrop for menus)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function selectUp() {
|
|
171
|
+
if (!container.value.children[0]) return;
|
|
172
|
+
|
|
173
|
+
const active = getActiveElement(container.value.children);
|
|
174
|
+
|
|
175
|
+
if (!active) return container.value.lastElementChild.focus();
|
|
176
|
+
|
|
177
|
+
let prev = active;
|
|
178
|
+
while (true) {
|
|
179
|
+
prev = prev.previousElementSibling;
|
|
180
|
+
if (!prev) return;
|
|
181
|
+
|
|
182
|
+
if (prev.getAttribute('separator') === 'true') continue;
|
|
183
|
+
else if (prev.classList.contains('pankow-menu-item-disabled')) continue;
|
|
184
|
+
|
|
185
|
+
prev.focus();
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function selectDown() {
|
|
191
|
+
if (!container.value.children[0]) return;
|
|
192
|
+
|
|
193
|
+
const active = getActiveElement(container.value.children);
|
|
194
|
+
|
|
195
|
+
if (!active) return container.value.firstElementChild.focus();
|
|
196
|
+
|
|
197
|
+
let next = active;
|
|
198
|
+
while (true) {
|
|
199
|
+
next = next.nextElementSibling;
|
|
200
|
+
if (!next) return;
|
|
201
|
+
|
|
202
|
+
if (next.getAttribute('separator') === 'true') continue;
|
|
203
|
+
else if (next.classList.contains('pankow-menu-item-disabled')) continue;
|
|
204
|
+
|
|
205
|
+
next.focus();
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function onBackdrop(event) {
|
|
211
|
+
close();
|
|
212
|
+
event.preventDefault();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function close() {
|
|
216
|
+
<<<<<<< HEAD
|
|
217
|
+
window.document.removeEventListener('click', blurEventHandler);
|
|
218
|
+
=======
|
|
219
|
+
>>>>>>> parent of c42ecf7 (Use click events on document.body instead of a backdrop for menus)
|
|
220
|
+
isOpen.value = false;
|
|
221
|
+
container.value.style.maxHeight = 'unset';
|
|
222
|
+
emit('close');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function position() {
|
|
226
|
+
if (!container.value) return;
|
|
227
|
+
|
|
228
|
+
const size = getHiddenElementSize(container.value);
|
|
229
|
+
|
|
230
|
+
let left = pageX;
|
|
231
|
+
let top = targetBottom + 1;
|
|
232
|
+
let width = container.value.offsetParent ? container.value.offsetWidth : size.width;
|
|
233
|
+
let height = container.value.offsetParent ? container.value.offsetHeight : size.height;
|
|
234
|
+
let viewport = getViewport();
|
|
235
|
+
|
|
236
|
+
let bottom = viewport.height - targetTop + 1;
|
|
237
|
+
|
|
238
|
+
//flip
|
|
239
|
+
if (left + width - document.body.scrollLeft > viewport.width) {
|
|
240
|
+
// if this is like a dropdown right align instead of flip
|
|
241
|
+
if (forElement.value) {
|
|
242
|
+
left = forElement.value.getBoundingClientRect().left + (forElement.value.getBoundingClientRect().width - width);
|
|
243
|
+
} else {
|
|
244
|
+
left -= width;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
//flip
|
|
249
|
+
if (top + height - document.body.scrollTop > viewport.height) {
|
|
250
|
+
if (top - document.body.scrollTop > viewport.height/2) {
|
|
251
|
+
rollUp.value = true;
|
|
252
|
+
} else {
|
|
253
|
+
rollUp.value = false;
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
rollUp.value = false;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
//fit
|
|
260
|
+
if (left < document.body.scrollLeft) {
|
|
261
|
+
left = document.body.scrollLeft;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
container.value.style.left = left + 'px';
|
|
265
|
+
if (rollUp.value) {
|
|
266
|
+
container.value.style.top = 'unset';
|
|
267
|
+
container.value.style.bottom = bottom + 'px';
|
|
268
|
+
container.value.style.maxHeight = viewport.height - bottom + 'px';
|
|
269
|
+
container.value.style.height = 'auto';
|
|
270
|
+
} else {
|
|
271
|
+
container.value.style.top = top + 'px';
|
|
272
|
+
container.value.style.bottom = 'unset';
|
|
273
|
+
container.value.style.maxHeight = viewport.height - top + 'px';
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
<<<<<<< HEAD
|
|
278
|
+
onUnmounted(() => {
|
|
279
|
+
window.document.removeEventListener('click', blurEventHandler);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
=======
|
|
283
|
+
>>>>>>> parent of c42ecf7 (Use click events on document.body instead of a backdrop for menus)
|
|
284
|
+
defineExpose({
|
|
285
|
+
isOpen,
|
|
286
|
+
open,
|
|
287
|
+
close,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
</script>
|
|
291
|
+
|
|
292
|
+
<template>
|
|
293
|
+
<teleport to="#app">
|
|
294
|
+
<div class="pankow-menu-backdrop" @click="onBackdrop($event)" @contextmenu="onBackdrop($event)" v-show="isOpen"></div>
|
|
295
|
+
<Transition :name="rollUp ? 'pankow-roll-up' : 'pankow-roll-down'">
|
|
296
|
+
<div class="pankow-menu" v-show="isOpen" ref="container" tabindex="0" @keydown.up.stop="selectUp()" @keydown.down.stop="selectDown()" @keydown.esc.stop="close()" @keydown="onKeyDown">
|
|
297
|
+
<TextInput placeholder="Filter ..." @keydown.up.stop="selectUp()" @keydown.down.stop="selectDown()" @keydown.stop @keydown.esc.stop="close()" @click.stop style="border: 0; padding: 8px 12px;" v-model="searchString" v-if="searchThreshold < model.length"/>
|
|
298
|
+
<component v-for="item in visibleItems" ref="itemElements" :is="item.type || (item.href ? MenuItemLink : MenuItem)" @activated="onItemActivated(item)" :item="item" :has-icons="hasIcons" />
|
|
299
|
+
<MenuItem v-if="model.length === 0" :item="emptyItem"/>
|
|
300
|
+
</div>
|
|
301
|
+
</Transition>
|
|
302
|
+
</teleport>
|
|
303
|
+
</template>
|
|
304
|
+
|
|
305
|
+
<style>
|
|
306
|
+
|
|
307
|
+
.pankow-menu-backdrop {
|
|
308
|
+
position: fixed;
|
|
309
|
+
height: 100%;
|
|
310
|
+
width: 100%;
|
|
311
|
+
left: 0px;
|
|
312
|
+
top: 0px;
|
|
313
|
+
z-index: 3001;
|
|
314
|
+
background-color: transparent;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.pankow-menu {
|
|
318
|
+
position: fixed;
|
|
319
|
+
box-shadow: var(--pankow-menu-shadow);
|
|
320
|
+
border-radius: var(--pankow-border-radius);
|
|
321
|
+
background-color: var(--pankow-input-background-color);
|
|
322
|
+
min-width: 120px;
|
|
323
|
+
z-index: 3001;
|
|
324
|
+
color: var(--pankow-color-dark);
|
|
325
|
+
overflow: auto;
|
|
326
|
+
/*max-height: 100%;*/
|
|
327
|
+
outline: none;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
@media (prefers-color-scheme: dark) {
|
|
331
|
+
.pankow-menu {
|
|
332
|
+
color: var(--pankow-color-light-dark);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
</style>
|