@cloudron/pankow 3.1.8
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/.gitlab-ci.yml +30 -0
- package/.jshintrc +8 -0
- package/LICENSE +21 -0
- package/README.md +20 -0
- package/components/BottomBar.vue +22 -0
- package/components/Breadcrumb.vue +64 -0
- package/components/Button.vue +243 -0
- package/components/ButtonGroup.vue +37 -0
- package/components/Checkbox.vue +112 -0
- package/components/Dialog.vue +178 -0
- package/components/DirectoryView.vue +772 -0
- package/components/DirectoryViewListItem.vue +412 -0
- package/components/EmailInput.vue +22 -0
- package/components/FileUploader.vue +204 -0
- package/components/FormGroup.vue +26 -0
- package/components/Icon.vue +12 -0
- package/components/InputDialog.vue +170 -0
- package/components/InputGroup.vue +32 -0
- package/components/MainLayout.vue +63 -0
- package/components/Menu.vue +284 -0
- package/components/MenuItem.vue +106 -0
- package/components/MenuItemLink.vue +52 -0
- package/components/MultiSelect.vue +202 -0
- package/components/Notification.vue +163 -0
- package/components/NumberInput.vue +31 -0
- package/components/OfflineBanner.vue +47 -0
- package/components/PasswordInput.vue +86 -0
- package/components/Popover.vue +185 -0
- package/components/ProgressBar.vue +75 -0
- package/components/Radiobutton.vue +128 -0
- package/components/SideBar.vue +104 -0
- package/components/SingleSelect.vue +190 -0
- package/components/Spinner.vue +67 -0
- package/components/Switch.vue +94 -0
- package/components/TabView.vue +161 -0
- package/components/TableView.vue +187 -0
- package/components/TagInput.vue +104 -0
- package/components/TextInput.vue +58 -0
- package/components/TopBar.vue +88 -0
- package/fallbackImage.js +29 -0
- package/fetcher.js +81 -0
- package/gallery/CustomMenuItem.vue +40 -0
- package/gallery/DirectoryViewDemo.vue +73 -0
- package/gallery/Index.vue +790 -0
- package/gallery/folder.svg +151 -0
- package/gallery/index.html +25 -0
- package/gallery/index.js +10 -0
- package/gallery/logo.png +0 -0
- package/gallery/vite.config.mjs +9 -0
- package/gestures.js +60 -0
- package/index.js +86 -0
- package/logo.png +0 -0
- package/logo.svg +78 -0
- package/package.json +26 -0
- package/style.css +351 -0
- package/tooltip.js +83 -0
- package/utils.js +383 -0
- package/viewers/GenericViewer.vue +84 -0
- package/viewers/ImageViewer.vue +239 -0
- package/viewers/PdfViewer.vue +82 -0
- package/viewers/TextViewer.vue +221 -0
- package/viewers.js +11 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MainLayout :gap="false" class="main-layout">
|
|
3
|
+
<template #header>
|
|
4
|
+
<TopBar class="navbar" :gap="false">
|
|
5
|
+
<template #left>
|
|
6
|
+
</template>
|
|
7
|
+
<template #center>
|
|
8
|
+
<div class="file-name">{{ entry ? entry.fileName : '' }}</div>
|
|
9
|
+
</template>
|
|
10
|
+
<template #right>
|
|
11
|
+
<Button icon="fa-solid fa-download" v-if="entry && entry.downloadFileUrl" outline tool @click="onDownload" style="margin-right: 5px;">Download</Button>
|
|
12
|
+
<Button icon="fa-solid fa-xmark" @click="onClose">{{ tr('main.dialog.close') }}</Button>
|
|
13
|
+
</template>
|
|
14
|
+
</TopBar>
|
|
15
|
+
</template>
|
|
16
|
+
<template #body>
|
|
17
|
+
<div class="pdf-object-container">
|
|
18
|
+
<iframe :src="entry ? entry.fullFileUrl : ''" frameborder="0" scrolling="no" style="width: 100%;"></iframe>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
</MainLayout>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script>
|
|
25
|
+
|
|
26
|
+
import Button from '../components/Button.vue';
|
|
27
|
+
import MainLayout from '../components/MainLayout.vue';
|
|
28
|
+
import TopBar from '../components/TopBar.vue';
|
|
29
|
+
import utils from '../utils.js';
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
name: 'PdfViewer',
|
|
33
|
+
components: {
|
|
34
|
+
Button,
|
|
35
|
+
MainLayout,
|
|
36
|
+
TopBar
|
|
37
|
+
},
|
|
38
|
+
emits: [ 'close' ],
|
|
39
|
+
props: {
|
|
40
|
+
tr: {
|
|
41
|
+
type: Function,
|
|
42
|
+
default(id) { console.warn('Missing tr for PdfViewer'); return utils.translation(id); }
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
data() {
|
|
46
|
+
return {
|
|
47
|
+
entry: null
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
methods: {
|
|
51
|
+
canHandle(entry) {
|
|
52
|
+
return entry.mimeType === 'application/pdf';
|
|
53
|
+
},
|
|
54
|
+
async open(entry) {
|
|
55
|
+
if (!entry || entry.isDirectory || !this.canHandle(entry)) return;
|
|
56
|
+
|
|
57
|
+
this.entry = entry;
|
|
58
|
+
},
|
|
59
|
+
onClose() {
|
|
60
|
+
this.$emit('close');
|
|
61
|
+
},
|
|
62
|
+
onDownload() {
|
|
63
|
+
window.location.href = this.entry.downloadFileUrl;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<style scoped>
|
|
71
|
+
|
|
72
|
+
.pdf-object-container {
|
|
73
|
+
display: flex;
|
|
74
|
+
height: 100%;
|
|
75
|
+
width: 100%;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.main-layout {
|
|
79
|
+
background-color: white;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
</style>
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MainLayout :gap="false">
|
|
3
|
+
<template #dialogs>
|
|
4
|
+
<InputDialog ref="inputDialog" />
|
|
5
|
+
</template>
|
|
6
|
+
<template #header>
|
|
7
|
+
<TopBar class="navbar" :gap="false">
|
|
8
|
+
<template #left>
|
|
9
|
+
</template>
|
|
10
|
+
<template #center>
|
|
11
|
+
<div class="file-name">{{ entry ? entry.fileName : '' }}</div>
|
|
12
|
+
</template>
|
|
13
|
+
<template #right>
|
|
14
|
+
<Button :icon="busySave ? null : 'fa-regular fa-floppy-disk'" success @click="onSave" :disabled="busySave || !isChanged" style="margin-right: 5px;"><Spinner v-show="busySave" style="stroke: white;"/> {{ tr('filemanager.textEditor.save') }}</Button>
|
|
15
|
+
<Button icon="fa-solid fa-xmark" @click="onClose">{{ tr('main.dialog.close') }}</Button>
|
|
16
|
+
</template>
|
|
17
|
+
</TopBar>
|
|
18
|
+
</template>
|
|
19
|
+
<template #body>
|
|
20
|
+
<div ref="editorView" class="editor"></div>
|
|
21
|
+
</template>
|
|
22
|
+
</MainLayout>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script>
|
|
26
|
+
|
|
27
|
+
import { editor, languages } from 'monaco-editor';
|
|
28
|
+
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
|
29
|
+
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
|
|
30
|
+
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
|
|
31
|
+
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
|
|
32
|
+
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
|
33
|
+
|
|
34
|
+
import Button from '../components/Button.vue';
|
|
35
|
+
import Spinner from '../components/Spinner.vue';
|
|
36
|
+
import InputDialog from '../components/InputDialog.vue';
|
|
37
|
+
import MainLayout from '../components/MainLayout.vue';
|
|
38
|
+
import TopBar from '../components/TopBar.vue';
|
|
39
|
+
import utils from '../utils.js';
|
|
40
|
+
|
|
41
|
+
window.MonacoEnvironment = {
|
|
42
|
+
getWorker(workerId, label) {
|
|
43
|
+
if (label === 'json') return new jsonWorker();
|
|
44
|
+
if (label === 'css' || label === 'scss' || label === 'less') return new cssWorker();
|
|
45
|
+
if (label === 'html' || label === 'handlebars' || label === 'razor') return new htmlWorker();
|
|
46
|
+
if (label === 'typescript' || label === 'javascript') return new tsWorker();
|
|
47
|
+
return new editorWorker();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function getLanguage(filename) {
|
|
52
|
+
var ext = '.' + filename.split('.').pop();
|
|
53
|
+
var language = languages.getLanguages().find(function (l) {
|
|
54
|
+
if (!l.extensions) return false;
|
|
55
|
+
return !!l.extensions.find(function (e) { return e === ext; });
|
|
56
|
+
}) || '';
|
|
57
|
+
return language ? language.id : '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default {
|
|
61
|
+
name: 'TextViewer',
|
|
62
|
+
emits: [ 'close' ],
|
|
63
|
+
props: {
|
|
64
|
+
saveHandler: {
|
|
65
|
+
type: Function,
|
|
66
|
+
default() { console.warn('Missing saveHandler for TextViewer'); }
|
|
67
|
+
},
|
|
68
|
+
tr: {
|
|
69
|
+
type: Function,
|
|
70
|
+
default(id) { console.warn('Missing tr for TextViewer'); return utils.translation(id); }
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
components: {
|
|
74
|
+
Button,
|
|
75
|
+
InputDialog,
|
|
76
|
+
MainLayout,
|
|
77
|
+
TopBar,
|
|
78
|
+
Spinner
|
|
79
|
+
},
|
|
80
|
+
data() {
|
|
81
|
+
return {
|
|
82
|
+
entry: {},
|
|
83
|
+
isChanged: false,
|
|
84
|
+
busySave: false,
|
|
85
|
+
lastSavedVersion: 0
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
methods: {
|
|
89
|
+
canHandle(entry) {
|
|
90
|
+
if (entry.isBinary) return false;
|
|
91
|
+
return utils.getFileTypeGroup(entry) === 'text' ||
|
|
92
|
+
utils.getFileTypeGroup(entry) === 'application';
|
|
93
|
+
},
|
|
94
|
+
open(entry, content) {
|
|
95
|
+
if (!entry || entry.isDirectory || !this.canHandle(entry)) return;
|
|
96
|
+
|
|
97
|
+
this.entry = entry;
|
|
98
|
+
|
|
99
|
+
this.editor.setModel(editor.createModel(content, getLanguage(entry.fileName)));
|
|
100
|
+
|
|
101
|
+
const initialVersion = this.editor.getModel().getAlternativeVersionId();
|
|
102
|
+
let currentVersion = initialVersion;
|
|
103
|
+
let lastVersion = initialVersion;
|
|
104
|
+
this.lastSavedVersion = initialVersion;
|
|
105
|
+
|
|
106
|
+
this.editor.onDidChangeModelContent(e => {
|
|
107
|
+
const versionId = this.editor.getModel().getAlternativeVersionId();
|
|
108
|
+
|
|
109
|
+
this.isChanged = this.lastSavedVersion !== versionId;
|
|
110
|
+
|
|
111
|
+
if (versionId < currentVersion) {
|
|
112
|
+
this.redoPossible = true;
|
|
113
|
+
// no more undo possible
|
|
114
|
+
if (versionId === initialVersion) {
|
|
115
|
+
this.undoPossible = false;
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
if (versionId <= lastVersion) {
|
|
119
|
+
// redoing the last change
|
|
120
|
+
if (versionId == lastVersion) {
|
|
121
|
+
this.redoPossible = false;
|
|
122
|
+
}
|
|
123
|
+
} else { // adding new change, disable redo when adding new changes
|
|
124
|
+
this.redoPossible = false;
|
|
125
|
+
if (currentVersion > lastVersion) {
|
|
126
|
+
lastVersion = currentVersion;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
this.undoPossible = true;
|
|
130
|
+
}
|
|
131
|
+
currentVersion = versionId;
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
async onClose() {
|
|
135
|
+
if (this.isChanged) {
|
|
136
|
+
const yes = await this.$refs.inputDialog.confirm({
|
|
137
|
+
message: this.tr('filemanager.textEditorCloseDialog.title'),
|
|
138
|
+
confirmStyle: 'danger',
|
|
139
|
+
confirmLabel: 'Discard changes and close',
|
|
140
|
+
rejectLabel: 'Cancel'
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (!yes) return;
|
|
144
|
+
|
|
145
|
+
this.editor.setModel(editor.createModel(''));
|
|
146
|
+
this.$emit('close');
|
|
147
|
+
this.isChanged = false;
|
|
148
|
+
} else {
|
|
149
|
+
this.editor.setModel(editor.createModel(''));
|
|
150
|
+
this.$emit('close');
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
async onSave() {
|
|
154
|
+
this.busySave = true;
|
|
155
|
+
|
|
156
|
+
await this.saveHandler(this.entry, this.editor.getValue());
|
|
157
|
+
|
|
158
|
+
this.isChanged = false;
|
|
159
|
+
this.lastSavedVersion = this.editor.getModel().getAlternativeVersionId();
|
|
160
|
+
|
|
161
|
+
this.busySave = false;
|
|
162
|
+
this.editor.focus();
|
|
163
|
+
},
|
|
164
|
+
keydownHandler(event) {
|
|
165
|
+
if((navigator.platform.match('Mac') ? event.metaKey : event.ctrlKey) && event.key === 's') {
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
if (this.isChanged) this.onSave();
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
beforeunloadHandler(event) {
|
|
171
|
+
// this prevents accidental closing or reloading
|
|
172
|
+
if (this.isChanged) event.preventDefault();
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
mounted() {
|
|
176
|
+
const theme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'vs-dark' : 'vs-light';
|
|
177
|
+
|
|
178
|
+
this.editor = editor.create(this.$refs.editorView, {
|
|
179
|
+
automaticLayout: true,
|
|
180
|
+
value: '',
|
|
181
|
+
theme
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
185
|
+
editor.setTheme(event.matches ? 'vs-dark' : 'vs-light');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
window.addEventListener('keydown', this.keydownHandler);
|
|
189
|
+
window.addEventListener('beforeunload', this.beforeunloadHandler);
|
|
190
|
+
},
|
|
191
|
+
beforeUnmount() {
|
|
192
|
+
window.removeEventListener('keydown', this.keydownHandler);
|
|
193
|
+
window.removeEventListener('beforeunload', this.beforeunloadHandler);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
</script>
|
|
198
|
+
|
|
199
|
+
<style scoped>
|
|
200
|
+
|
|
201
|
+
.container {
|
|
202
|
+
position: absolute;
|
|
203
|
+
top: 0;
|
|
204
|
+
left: 0;
|
|
205
|
+
width: 100%;
|
|
206
|
+
height: 100%;
|
|
207
|
+
background-color: rgba(0,0,0,0.8);
|
|
208
|
+
display: flex;
|
|
209
|
+
flex-direction: column;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.editor {
|
|
213
|
+
width: 100%;
|
|
214
|
+
height: 100%;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.file-name {
|
|
218
|
+
margin: 0 0.5rem;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
</style>
|
package/viewers.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import GenericViewer from './viewers/GenericViewer.vue';
|
|
2
|
+
import ImageViewer from './viewers/ImageViewer.vue';
|
|
3
|
+
import PdfViewer from './viewers/PdfViewer.vue';
|
|
4
|
+
import TextViewer from './viewers/TextViewer.vue';
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
GenericViewer,
|
|
8
|
+
ImageViewer,
|
|
9
|
+
PdfViewer,
|
|
10
|
+
TextViewer,
|
|
11
|
+
};
|