summaryrefslogtreecommitdiffstats
path: root/apps/files/src/views/TemplatePicker.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src/views/TemplatePicker.vue')
-rw-r--r--apps/files/src/views/TemplatePicker.vue268
1 files changed, 268 insertions, 0 deletions
diff --git a/apps/files/src/views/TemplatePicker.vue b/apps/files/src/views/TemplatePicker.vue
new file mode 100644
index 00000000000..84c7c38aba4
--- /dev/null
+++ b/apps/files/src/views/TemplatePicker.vue
@@ -0,0 +1,268 @@
+<!--
+ - @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
+ -
+ - @author John Molakvoæ <skjnldsv@protonmail.com>
+ -
+ - @license GNU AGPL version 3 or any later version
+ -
+ - This program is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU Affero General Public License as
+ - published by the Free Software Foundation, either version 3 of the
+ - License, or (at your option) any later version.
+ -
+ - This program is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU Affero General Public License for more details.
+ -
+ - You should have received a copy of the GNU Affero General Public License
+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -
+ -->
+
+<template>
+ <Modal v-if="opened"
+ :clear-view-delay="-1"
+ class="templates-picker"
+ size="large"
+ @close="close">
+ <form class="templates-picker__form"
+ :style="style"
+ @submit.prevent.stop="onSubmit">
+ <h3>{{ t('files', 'Pick a template') }}</h3>
+
+ <!-- Templates list -->
+ <ul class="templates-picker__list">
+ <TemplatePreview
+ v-bind="emptyTemplate"
+ :checked="checked === emptyTemplate.fileid"
+ @check="onCheck" />
+
+ <TemplatePreview
+ v-for="template in provider.templates"
+ :key="template.fileid"
+ v-bind="template"
+ :checked="checked === template.fileid"
+ :ratio="provider.ratio"
+ @check="onCheck" />
+ </ul>
+
+ <!-- Cancel and submit -->
+ <div class="templates-picker__buttons">
+ <button @click="close">
+ {{ t('files', 'Cancel') }}
+ </button>
+ <input type="submit"
+ class="primary"
+ :value="t('files', 'Create')"
+ :aria-label="t('files', 'Create a new file with the ')">
+ </div>
+ </form>
+
+ <EmptyContent class="templates-picker__loading" v-if="loading" icon="icon-loading">
+ {{ t('files', 'Creating file') }}
+ </EmptyContent>
+ </Modal>
+</template>
+
+<script>
+import { generateOcsUrl } from '@nextcloud/router'
+import { showError } from '@nextcloud/dialogs'
+
+import axios from '@nextcloud/axios'
+import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
+import Modal from '@nextcloud/vue/dist/Components/Modal'
+
+import TemplatePreview from '../components/TemplatePreview'
+
+const border = 2
+const margin = 8
+const width = margin * 20
+
+export default {
+ name: 'TemplatePicker',
+
+ components: {
+ EmptyContent,
+ Modal,
+ TemplatePreview,
+ },
+
+ props: {
+ logger: {
+ type: Object,
+ required: true,
+ },
+ },
+
+ data() {
+ return {
+ // Check empty template by default
+ checked: -1,
+ loading: false,
+ name: null,
+ opened: false,
+ provider: null,
+ }
+ },
+
+ computed: {
+ emptyTemplate() {
+ return {
+ basename: t('files', 'Blank'),
+ fileid: -1,
+ filename: this.t('files', 'Blank'),
+ hasPreview: false,
+ mime: this.provider?.mimetypes[0] || this.provider?.mimetypes,
+ }
+ },
+
+ selectedTemplate() {
+ return this.provider.templates.find(template => template.fileid === this.checked)
+ },
+
+ /**
+ * Style css vars bin,d
+ * @returns {Object}
+ */
+ style() {
+ return {
+ '--margin': margin + 'px',
+ '--width': width + 'px',
+ '--border': border + 'px',
+ '--fullwidth': width + 2 * margin + 2 * border + 'px',
+ '--height': this.ratio ? width * this.ratio + 'px' : null,
+ }
+ },
+ },
+
+ methods: {
+ /**
+ * Open the picker
+ * @param {string} name the file name to create
+ * @param {object} provider the template provider picked
+ */
+ open(name, provider) {
+ this.checked = this.emptyTemplate.fileid
+ this.name = name
+ this.opened = true
+ this.provider = provider
+ },
+
+ /**
+ * Close the picker and reset variables
+ */
+ close() {
+ this.checked = this.emptyTemplate.fileid
+ this.loading = false
+ this.name = null
+ this.opened = false
+ this.provider = null
+ },
+
+ /**
+ * Manages the radio template picker change
+ * @param {number} fileid the selected template file id
+ */
+ onCheck(fileid) {
+ this.checked = fileid
+ },
+
+ async onSubmit() {
+ this.loading = true
+ const currentDirectory = this.getCurrentDirectory()
+ const fileList = OCA?.Files?.App?.currentFileList
+
+ try {
+ const response = await axios.post(generateOcsUrl('apps/files/api/v1/templates', 2) + 'create', {
+ filePath: `${currentDirectory}/${this.name}`,
+ templatePath: this.selectedTemplate?.filename,
+ templateType: this.selectedTemplate?.templateType,
+ })
+
+ const fileInfo = response.data.ocs.data
+ this.logger.debug('Created new file', fileInfo)
+
+ // Run default action
+ const fileAction = OCA.Files.fileActions.getDefaultFileAction(fileInfo.mime, 'file', OC.PERMISSION_ALL)
+ fileAction.action(fileInfo.basename, {
+ $file: null,
+ dir: currentDirectory,
+ fileList,
+ fileActions: fileList?.fileActions,
+ })
+
+ // Reload files list
+ fileList?.reload?.() || window.location.reload()
+
+ this.close()
+ } catch (error) {
+ this.logger.error('Error while creating the new file from template', error)
+ showError(this.t('files', 'Unable to create new file from template'))
+ } finally {
+ this.loading = false
+ }
+ },
+
+ /**
+ * Return the current directory, fallback to root
+ * @returns {string}
+ */
+ getCurrentDirectory() {
+ const currentDirInfo = OCA?.Files?.App?.currentFileList?.dirInfo
+ || { path: '/', name: '' }
+
+ // Make sure we don't have double slashes
+ return `${currentDirInfo.path}/${currentDirInfo.name}`.replace(/\/\//gi, '/')
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.templates-picker {
+ &__form {
+ padding: calc(var(--margin) * 2);
+ // Will be handled by the buttons
+ padding-bottom: 0;
+ }
+
+ &__list {
+ display: grid;
+ grid-gap: calc(var(--margin) * 2);
+ grid-auto-columns: 1fr;
+ // We want maximum 5 columns. Putting 6 as we don't count the grid gap. So it will always be lower than 6
+ max-width: calc(var(--fullwidth) * 6);
+ grid-template-columns: repeat(auto-fit, minmax(var(--fullwidth), 1fr));
+ // Make sure all rows are the same height
+ grid-auto-rows: 1fr;
+ }
+ &__buttons {
+ display: flex;
+ justify-content: space-between;
+ padding: calc(var(--margin) * 2) var(--margin);
+ position: sticky;
+ // Make sure the templates list doesn't weirdly peak under when scrolled. Happens on some rare occasions
+ bottom: -1px;
+ background-color: var(--color-main-background);
+ }
+
+ // Make sure we're relative for the loading emptycontent on top
+ /deep/ .modal-container {
+ position: relative;
+ overflow-y: auto !important;
+ }
+
+ &__loading {
+ position: absolute;
+ top: 0;
+ left: 0;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ background-color: var(--color-main-background-translucent);
+ }
+}
+
+</style>