@cloudron/pankow 3.5.7 → 3.5.10
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/Dialog.vue +1 -1
- package/components/EmailInput.vue +11 -2
- package/components/FileUploader.vue +20 -3
- package/components/MaskedInput.vue +6 -2
- package/components/Menu.vue +28 -1
- package/components/MultiSelect.vue +17 -12
- package/components/PasswordInput.vue +8 -2
- package/components/SingleSelect.vue +10 -9
- package/components/Switch.vue +2 -2
- package/components/TextInput.vue +6 -3
- package/package.json +4 -4
- package/style.css +12 -0
- package/components/CircleChart.vue +0 -40
package/components/Dialog.vue
CHANGED
|
@@ -64,7 +64,7 @@ async function open() {
|
|
|
64
64
|
|
|
65
65
|
// try to focus some form elements else just the dialog
|
|
66
66
|
await nextTick();
|
|
67
|
-
const focusElement = dialogBody.value.querySelector('input:not([disabled]):not([style*="display:none"]):not([style*="display: none"])') || dialog.value;
|
|
67
|
+
const focusElement = dialogBody.value.querySelector('input:not([disabled]):not([readonly]):not([style*="display:none"]):not([style*="display: none"])') || dialog.value;
|
|
68
68
|
focusElement.focus();
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
|
|
3
|
+
import { useTemplateRef } from 'vue';
|
|
4
|
+
|
|
3
5
|
const model = defineModel();
|
|
4
6
|
const props = defineProps({
|
|
5
7
|
placeholder: String,
|
|
6
8
|
readonly: {
|
|
7
9
|
type: Boolean,
|
|
8
|
-
default: false
|
|
10
|
+
default: false,
|
|
9
11
|
},
|
|
10
12
|
});
|
|
13
|
+
const inputElement = useTemplateRef('inputElement');
|
|
14
|
+
|
|
15
|
+
function focus() {
|
|
16
|
+
inputElement.value.focus();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
defineExpose({ focus });
|
|
11
20
|
|
|
12
21
|
</script>
|
|
13
22
|
|
|
14
23
|
<template>
|
|
15
|
-
<input class="pankow-text-input" type="email" :placeholder="placeholder" :readonly="readonly"
|
|
24
|
+
<input ref="inputElement" class="pankow-text-input" type="email" :placeholder="placeholder" :readonly="readonly" v-model.trim="model"/>
|
|
16
25
|
</template>
|
|
17
26
|
|
|
18
27
|
<style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="file-uploader" v-show="uploadInProgress">
|
|
3
|
-
<div class="label">{{ tr('filemanager.uploader.uploading') }} {{ activeFile ? activeFile.name : '' }}
|
|
2
|
+
<div class="pankow-file-uploader" v-show="uploadInProgress">
|
|
3
|
+
<div class="pankow-file-uploader-label-and-speed-container"><div class="pankow-file-uploader-label">{{ tr('filemanager.uploader.uploading') }} {{ activeFile ? activeFile.name : '' }}</div><div class="pankow-file-uploader-speed">{{ prettyUploadSpeed }}</div></div>
|
|
4
4
|
<div style="display: flex;">
|
|
5
5
|
<ProgressBar :value="parseInt((this.uploadedSize * 100) / this.size) || 0" style="flex-grow: 1"></ProgressBar>
|
|
6
6
|
<Button danger small tool @click="onCancelUpload" icon="fa-solid fa-xmark"></Button>
|
|
@@ -195,10 +195,27 @@ export default {
|
|
|
195
195
|
|
|
196
196
|
<style>
|
|
197
197
|
|
|
198
|
-
.file-uploader {
|
|
198
|
+
.pankow-file-uploader {
|
|
199
199
|
width: 100%;
|
|
200
200
|
padding: 10px;
|
|
201
201
|
background-color: var(--pankow-color-background);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
.pankow-file-uploader-label-and-speed-container {
|
|
205
|
+
display: flex;
|
|
206
|
+
gap: 10px;
|
|
207
|
+
margin-bottom: 5px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.pankow-file-uploader-label {
|
|
211
|
+
flex-grow: 1;
|
|
212
|
+
white-space: nowrap;
|
|
213
|
+
text-overflow: ellipsis;
|
|
214
|
+
overflow: hidden;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.pankow-file-uploader-speed {
|
|
218
|
+
white-space: nowrap;
|
|
219
|
+
}
|
|
220
|
+
|
|
204
221
|
</style>
|
|
@@ -36,8 +36,12 @@ const isValid = computed(() => {
|
|
|
36
36
|
</script>
|
|
37
37
|
|
|
38
38
|
<template>
|
|
39
|
-
<
|
|
40
|
-
|
|
39
|
+
<template v-if="!multiline">
|
|
40
|
+
<input class="pankow-masked-input" :value="displayValue" :required="isValid" @input="displayValue = $event.target.value" @focus="$event.target.select()" />
|
|
41
|
+
</template>
|
|
42
|
+
<template v-else>
|
|
43
|
+
<textarea :required="isValid" :value="displayValue" @input="displayValue = $event.target.value" @focus="$event.target.select()"></textarea>
|
|
44
|
+
</template>
|
|
41
45
|
</template>
|
|
42
46
|
|
|
43
47
|
<style>
|
package/components/Menu.vue
CHANGED
|
@@ -90,7 +90,34 @@ const hasIcons = computed(() => {
|
|
|
90
90
|
const visibleItems = computed(() => {
|
|
91
91
|
if (!searchString.value) return props.model;
|
|
92
92
|
const s = searchString.value.toLowerCase();
|
|
93
|
-
|
|
93
|
+
let results = [];
|
|
94
|
+
|
|
95
|
+
let group = null;
|
|
96
|
+
let filteredGroupItems = [];
|
|
97
|
+
|
|
98
|
+
for (let item of props.model) {
|
|
99
|
+
if (item.separator) {
|
|
100
|
+
if (group && filteredGroupItems.length) {
|
|
101
|
+
results.push(group);
|
|
102
|
+
results = results.concat(filteredGroupItems);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
group = item;
|
|
106
|
+
filteredGroupItems = [];
|
|
107
|
+
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (group && group.label?.toLowerCase().indexOf(s) !== -1) filteredGroupItems.push(item);
|
|
112
|
+
else if (item.label.toLowerCase().indexOf(s) !== -1) filteredGroupItems.push(item);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// add remaining group and filtered items (also matches if no group ever set)
|
|
116
|
+
if (group && filteredGroupItems.length) results.push(group);
|
|
117
|
+
if (filteredGroupItems.length) results = results.concat(filteredGroupItems);
|
|
118
|
+
|
|
119
|
+
return results;
|
|
120
|
+
// return props.model.filter((item) => item.label.toLowerCase().indexOf(s) !== -1);
|
|
94
121
|
});
|
|
95
122
|
|
|
96
123
|
// select menu item by .label
|
|
@@ -47,6 +47,7 @@ let selected = ref([]);
|
|
|
47
47
|
|
|
48
48
|
const elem = useTemplateRef('elem');
|
|
49
49
|
const menu = useTemplateRef('menu');
|
|
50
|
+
const nativeSelect = useTemplateRef('nativeSelect');
|
|
50
51
|
|
|
51
52
|
watch(model, (newValue, oldValue) => {
|
|
52
53
|
selected.value = newValue;
|
|
@@ -63,7 +64,7 @@ const menuModel = computed(() => {
|
|
|
63
64
|
if (item.separator) {
|
|
64
65
|
return {
|
|
65
66
|
separator: true,
|
|
66
|
-
label:
|
|
67
|
+
label: item[props.optionLabel],
|
|
67
68
|
};
|
|
68
69
|
} else {
|
|
69
70
|
return {
|
|
@@ -82,6 +83,11 @@ const menuModel = computed(() => {
|
|
|
82
83
|
|
|
83
84
|
model.value = selected.value;
|
|
84
85
|
emits('select', selected.value);
|
|
86
|
+
|
|
87
|
+
// let the browser know about the changes
|
|
88
|
+
nativeSelect.value.value = selected.value;
|
|
89
|
+
nativeSelect.value.dispatchEvent(new Event('input', { bubbles: true }));
|
|
90
|
+
nativeSelect.value.dispatchEvent(new Event('change', { bubbles: true }));
|
|
85
91
|
}
|
|
86
92
|
};
|
|
87
93
|
}
|
|
@@ -114,11 +120,6 @@ const menuModel = computed(() => {
|
|
|
114
120
|
return ret;
|
|
115
121
|
});
|
|
116
122
|
|
|
117
|
-
function onMenuClosed() {
|
|
118
|
-
elem.value.focus();
|
|
119
|
-
emits('close');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
123
|
function onClick(event) {
|
|
123
124
|
if (props.disabled) return;
|
|
124
125
|
|
|
@@ -132,9 +133,13 @@ function onOpen(event) {
|
|
|
132
133
|
menu.value.open(event, elem.value);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
function onClose(event) {
|
|
136
|
-
menu.value.
|
|
137
|
-
|
|
136
|
+
function onClose(event = null) {
|
|
137
|
+
if (menu.value.isOpen) {
|
|
138
|
+
elem.value.focus();
|
|
139
|
+
if (event) event.stopPropagation();
|
|
140
|
+
menu.value.close();
|
|
141
|
+
emits('close');
|
|
142
|
+
}
|
|
138
143
|
}
|
|
139
144
|
|
|
140
145
|
onMounted(() => {
|
|
@@ -144,14 +149,14 @@ onMounted(() => {
|
|
|
144
149
|
</script>
|
|
145
150
|
|
|
146
151
|
<template>
|
|
147
|
-
<div class="pankow-multiselect" :class="{ 'pankow-multiselect-disabled': disabled }" ref="elem" tabindex="0" @click="onClick" @keydown.enter="onOpen" @keydown.down.stop="onOpen" @keydown.up.stop="onOpen" @keydown.esc
|
|
152
|
+
<div class="pankow-multiselect" :class="{ 'pankow-multiselect-disabled': disabled }" ref="elem" tabindex="0" @click="onClick" @keydown.enter="onOpen" @keydown.down.stop="onOpen" @keydown.up.stop="onOpen" @keydown.esc="onClose">
|
|
148
153
|
<!-- native select for required and accessibility handling -->
|
|
149
|
-
<select
|
|
154
|
+
<select ref="nativeSelect" :required="$attrs['required']" multiple style="display: none">
|
|
150
155
|
<option value=""></option>
|
|
151
156
|
<option v-for="item in options" :value="optionKey ? item[optionKey] : item">{{ optionKey }} - {{ item[optionLabel] }}</option>
|
|
152
157
|
</select>
|
|
153
158
|
|
|
154
|
-
<Menu ref="menu" :model="menuModel" :close-on-activation="false" @close="
|
|
159
|
+
<Menu ref="menu" :model="menuModel" :close-on-activation="false" @close="onClose" :search-threshold="searchThreshold"></Menu>
|
|
155
160
|
{{ buttonLabel }}
|
|
156
161
|
<Icon icon="fa-solid fa-chevron-down" class="pankow-button-icon-right-with-text" />
|
|
157
162
|
</div>
|
|
@@ -67,11 +67,17 @@ function toggleReveal() {
|
|
|
67
67
|
outline: none;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
.pankow-password-input
|
|
70
|
+
.pankow-password-input[readonly],
|
|
71
|
+
.pankow-password-input[disabled] {
|
|
71
72
|
cursor: not-allowed;
|
|
72
|
-
|
|
73
|
+
background-color: var(--pankow-input-readonly-background-color);
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
.pankow-password-input[disabled]:hover {
|
|
77
|
+
border-color: var(--pankow-input-border-color);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
75
81
|
.pankow-password-reveal {
|
|
76
82
|
display: inline-block;
|
|
77
83
|
position: absolute;
|
|
@@ -32,7 +32,7 @@ const props = defineProps({
|
|
|
32
32
|
},
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
const emits = defineEmits(['select']);
|
|
35
|
+
const emits = defineEmits(['select', 'close']);
|
|
36
36
|
|
|
37
37
|
const model = defineModel();
|
|
38
38
|
const selected = ref(null);
|
|
@@ -92,10 +92,6 @@ function selectIndex(index) {
|
|
|
92
92
|
nativeSelect.value.dispatchEvent(new Event('change', { bubbles: true }));
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
function onMenuClosed() {
|
|
96
|
-
elem.value.focus();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
95
|
function onClick(event) {
|
|
100
96
|
if (props.disabled) return;
|
|
101
97
|
|
|
@@ -109,8 +105,13 @@ function onOpen(event) {
|
|
|
109
105
|
menu.value.open(event, elem.value);
|
|
110
106
|
}
|
|
111
107
|
|
|
112
|
-
function onClose(event) {
|
|
113
|
-
menu.value.
|
|
108
|
+
function onClose(event = null) {
|
|
109
|
+
if (menu.value.isOpen) {
|
|
110
|
+
if (event) event.stopPropagation();
|
|
111
|
+
elem.value.focus();
|
|
112
|
+
menu.value.close();
|
|
113
|
+
emits('close');
|
|
114
|
+
}
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
function onClosed() {
|
|
@@ -169,14 +170,14 @@ watchEffect(handleDefaultSelect);
|
|
|
169
170
|
</script>
|
|
170
171
|
|
|
171
172
|
<template>
|
|
172
|
-
<div class="pankow-singleselect" :class="{ 'pankow-singleselect-disabled': disabled }" ref="elem" tabindex="0" @click="onClick" @keydown.enter="onOpen" @keydown.down.stop.prevent="onSelectNext" @keydown.up.stop.prevent="onSelectPrev" @keydown.esc
|
|
173
|
+
<div class="pankow-singleselect" :class="{ 'pankow-singleselect-disabled': disabled }" ref="elem" tabindex="0" @click="onClick" @keydown.enter="onOpen" @keydown.down.stop.prevent="onSelectNext" @keydown.up.stop.prevent="onSelectPrev" @keydown.esc="onClose" @keydown.stop="onKeyDown($event)">
|
|
173
174
|
<!-- native select for required and accessibility handling -->
|
|
174
175
|
<select v-model="selectedKey" ref="nativeSelect" :required="$attrs['required']" style="display: none">
|
|
175
176
|
<option value=""></option>
|
|
176
177
|
<option v-for="item in options" :value="optionKey ? item[optionKey] : item">{{ item[optionLabel] }}</option>
|
|
177
178
|
</select>
|
|
178
179
|
|
|
179
|
-
<Menu ref="menu" :model="menuModel" :search-threshold="searchThreshold" :close-on-activation="true" @close="
|
|
180
|
+
<Menu ref="menu" :model="menuModel" :search-threshold="searchThreshold" :close-on-activation="true" @close="onClose"/>
|
|
180
181
|
<span class="pankow-singleselect-label">
|
|
181
182
|
<Icon v-if="selected ? selected.icon : false" :icon="selected.icon" style="margin-right: 6px" />
|
|
182
183
|
{{ selected ? selected[optionLabel] : placeholder }}
|
package/components/Switch.vue
CHANGED
|
@@ -14,8 +14,8 @@ const internalId = uuidv4();
|
|
|
14
14
|
|
|
15
15
|
const emit = defineEmits(['change']);
|
|
16
16
|
|
|
17
|
-
function onChange() {
|
|
18
|
-
emit('change', model.value);
|
|
17
|
+
function onChange(event) {
|
|
18
|
+
emit('change', model.value); // this contains the value after the checkbox has already toggled
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
</script>
|
package/components/TextInput.vue
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
|
|
3
|
+
// Until we support input type as prop, make sure EmailInput.vue matches the features here
|
|
4
|
+
|
|
3
5
|
import { useTemplateRef } from 'vue';
|
|
4
6
|
|
|
5
7
|
const model = defineModel();
|
|
@@ -54,13 +56,14 @@ defineExpose({ focus });
|
|
|
54
56
|
outline: none;
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
.pankow-text-input[disabled],
|
|
57
60
|
.pankow-text-input[readonly] {
|
|
58
61
|
cursor: not-allowed;
|
|
62
|
+
background-color: var(--pankow-input-readonly-background-color);
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
.pankow-text-input:
|
|
62
|
-
|
|
63
|
-
opacity: 0.5;
|
|
65
|
+
.pankow-text-input[disabled]:hover {
|
|
66
|
+
border-color: var(--pankow-input-border-color);
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
</style>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudron/pankow",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "3.5.
|
|
4
|
+
"version": "3.5.10",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "types/index.d.ts",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"monaco-editor": "^0.54.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
25
|
+
"@vitejs/plugin-vue": "^6.0.2",
|
|
26
26
|
"typescript": "^5.9.3",
|
|
27
|
-
"vite": "^7.2.
|
|
28
|
-
"vue": "^3.5.
|
|
27
|
+
"vite": "^7.2.4",
|
|
28
|
+
"vue": "^3.5.25"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/style.css
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
--pankow-input-vertial-padding: 6px;
|
|
50
50
|
--pankow-input-horizontal-padding: max(7px, var(--pankow-border-radius));
|
|
51
51
|
--pankow-input-background-color: white;
|
|
52
|
+
--pankow-input-readonly-background-color: #f5f5f5;
|
|
52
53
|
--pankow-input-border-color: #ced4da;
|
|
53
54
|
--pankow-input-border-color-hover: var(--pankow-color-dark);
|
|
54
55
|
--pankow-input-border-color-focus: var(--pankow-color-primary-hover);
|
|
@@ -92,6 +93,7 @@ input {
|
|
|
92
93
|
--pankow-input-background-color: #1b1e21;
|
|
93
94
|
--pankow-input-border-color: #1b1e21;
|
|
94
95
|
--pankow-input-border-color-hover: #ced4da;
|
|
96
|
+
--pankow-input-readonly-background-color: #000000;
|
|
95
97
|
|
|
96
98
|
--pankow-color-background: #1b1e21;
|
|
97
99
|
--pankow-color-background-hover: #1f2326;
|
|
@@ -181,6 +183,16 @@ textarea:focus {
|
|
|
181
183
|
outline: none;
|
|
182
184
|
}
|
|
183
185
|
|
|
186
|
+
textarea[disabled],
|
|
187
|
+
textarea[readonly] {
|
|
188
|
+
cursor: not-allowed;
|
|
189
|
+
background-color: var(--pankow-input-readonly-background-color);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
textarea[disabled]:hover {
|
|
193
|
+
border-color: var(--pankow-input-border-color);
|
|
194
|
+
}
|
|
195
|
+
|
|
184
196
|
@media (max-width: 576px) {
|
|
185
197
|
.pankow-no-mobile {
|
|
186
198
|
display: none !important;
|
|
@@ -1,40 +0,0 @@
|
|
|
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>
|