@cloudron/pankow 3.2.17 → 3.2.19
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/ClipboardAction.vue +35 -0
- package/components/ClipboardButton.vue +31 -0
- package/index.js +4 -0
- package/package.json +3 -3
- package/types/index.d.ts +1 -1
- package/components/Menu_BACKUP_252296.vue +0 -336
- package/components/Menu_BACKUP_252415.vue +0 -336
- package/components/Menu_BASE_252296.vue +0 -315
- package/components/Menu_BASE_252415.vue +0 -315
- package/components/Menu_LOCAL_252296.vue +0 -314
- package/components/Menu_LOCAL_252415.vue +0 -314
- package/components/Menu_REMOTE_252296.vue +0 -319
- package/components/Menu_REMOTE_252415.vue +0 -319
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
|
|
3
|
+
import { copyToClipboard } from '../utils';
|
|
4
|
+
import Button from './Button.vue';
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
value: String,
|
|
8
|
+
doneHint: String,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function onClipboard() {
|
|
12
|
+
copyToClipboard(props.value);
|
|
13
|
+
window.pankow.notify({
|
|
14
|
+
text: props.doneHint || 'Copied',
|
|
15
|
+
type: 'success',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<i class="pankow-clipboard-action fa-solid fa-copy" @click="onClipboard()"></i>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<style>
|
|
26
|
+
|
|
27
|
+
.pankow-clipboard-action {
|
|
28
|
+
cursor: copy;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.pankow-clipboard-action:hover {
|
|
32
|
+
color: var(--pankow-color-dark);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
</style>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
|
|
3
|
+
import { copyToClipboard } from '../utils';
|
|
4
|
+
import Button from './Button.vue';
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
value: String,
|
|
8
|
+
doneHint: String,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function onClipboard() {
|
|
12
|
+
copyToClipboard(props.value);
|
|
13
|
+
window.pankow.notify({
|
|
14
|
+
text: props.doneHint || 'Copied',
|
|
15
|
+
type: 'success',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<Button class="pankow-clipboard-button" secondary icon="fa-solid fa-copy" tool @click="onClipboard()" style=""></Button>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<style>
|
|
26
|
+
|
|
27
|
+
.pankow-clipboard-button {
|
|
28
|
+
padding: var(--pankow-input-vertial-padding);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
</style>
|
package/index.js
CHANGED
|
@@ -7,6 +7,8 @@ import BottomBar from './components/BottomBar.vue';
|
|
|
7
7
|
import Breadcrumb from './components/Breadcrumb.vue';
|
|
8
8
|
import Button from './components/Button.vue';
|
|
9
9
|
import ButtonGroup from './components/ButtonGroup.vue';
|
|
10
|
+
import ClipboardAction from './components/ClipboardAction.vue';
|
|
11
|
+
import ClipboardButton from './components/ClipboardButton.vue';
|
|
10
12
|
import Checkbox from './components/Checkbox.vue';
|
|
11
13
|
import Dialog from './components/Dialog.vue';
|
|
12
14
|
import DirectoryView from './components/DirectoryView.vue';
|
|
@@ -49,6 +51,8 @@ export {
|
|
|
49
51
|
Breadcrumb,
|
|
50
52
|
Button,
|
|
51
53
|
ButtonGroup,
|
|
54
|
+
ClipboardAction,
|
|
55
|
+
ClipboardButton,
|
|
52
56
|
Checkbox,
|
|
53
57
|
Dialog,
|
|
54
58
|
DirectoryView,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudron/pankow",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "3.2.
|
|
4
|
+
"version": "3.2.19",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "types/index.d.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"author": "",
|
|
17
17
|
"license": "ISC",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@fontsource/inter": "^5.2.
|
|
19
|
+
"@fontsource/inter": "^5.2.8",
|
|
20
20
|
"@fortawesome/fontawesome-free": "^7.0.1",
|
|
21
21
|
"filesize": "^11.0.2",
|
|
22
22
|
"monaco-editor": "^0.53.0"
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@vitejs/plugin-vue": "^6.0.1",
|
|
26
26
|
"typescript": "^5.9.2",
|
|
27
|
-
"vite": "^7.1.
|
|
27
|
+
"vite": "^7.1.6",
|
|
28
28
|
"vue": "^3.5.21"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ import gestures from './gestures.js';
|
|
|
3
3
|
import tooltip from './tooltip.js';
|
|
4
4
|
import fallbackImage from './fallbackImage.js';
|
|
5
5
|
import utils from './utils.js';
|
|
6
|
-
export { BottomBar, Breadcrumb, Button, ButtonGroup, Checkbox, Dialog, DirectoryView, EmailInput, FileUploader, FormGroup, InputGroup, Icon, InputDialog, MainLayout, Menu, MenuItem, SingleSelect, MultiSelect, Notification, NumberInput, OfflineBanner, PasswordInput, Popover, ProgressBar, Radiobutton, SideBar, Spinner, Switch, TableView, TabView, TagInput, TextInput, TextInputRaw, TopBar, fetcher, gestures, tooltip, fallbackImage, utils };
|
|
6
|
+
export { BottomBar, Breadcrumb, Button, ButtonGroup, ClipboardAction, ClipboardButton, Checkbox, Dialog, DirectoryView, EmailInput, FileUploader, FormGroup, InputGroup, Icon, InputDialog, MainLayout, Menu, MenuItem, SingleSelect, MultiSelect, Notification, NumberInput, OfflineBanner, PasswordInput, Popover, ProgressBar, Radiobutton, SideBar, Spinner, Switch, TableView, TabView, TagInput, TextInput, TextInputRaw, TopBar, fetcher, gestures, tooltip, fallbackImage, utils };
|
|
@@ -1,336 +0,0 @@
|
|
|
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>
|