mirror of
https://github.com/nextcloud/richdocuments.git
synced 2025-12-17 13:08:43 +01:00
chore: Move from old dialog to a modern vue one
Signed-off-by: Julius Knorr <jus@bitgrid.net>
This commit is contained in:
parent
d82b195294
commit
b0aa47846a
5 changed files with 189 additions and 120 deletions
|
|
@ -134,5 +134,3 @@
|
|||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@import 'templatePicker';
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
@use 'sass:math';
|
||||
|
||||
#template-picker {
|
||||
.template-container:not(.hidden) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
a {
|
||||
$size: 170px;
|
||||
$sizeY: math.div($size, 210) * 297;
|
||||
$space: 10px;
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--color-border);
|
||||
margin: $space;
|
||||
position: relative;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
border-color: var(--color-primary-element);
|
||||
}
|
||||
|
||||
> span {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: $size;
|
||||
margin: $space;
|
||||
cursor: pointer;
|
||||
> img,
|
||||
> span {
|
||||
width: $size;
|
||||
height: $sizeY;
|
||||
background-color: var(--color-background-dark);
|
||||
background-size: 24px;
|
||||
}
|
||||
> h2 {
|
||||
margin-top: $space;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
176
src/components/Modal/TemplatePicker.vue
Normal file
176
src/components/Modal/TemplatePicker.vue
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<NcModal :title="t('richdocuments', 'Select template')" @close="onCancel">
|
||||
<div class="template-picker">
|
||||
<NcTextField v-model="filename"
|
||||
class="filename-input"
|
||||
type="text"
|
||||
:label="t('richdocuments', 'File name')"
|
||||
:error="!!filenameError"
|
||||
:helper-text="filenameError ?? ''"
|
||||
@keyup.enter="onCreate" />
|
||||
<div class="template-container">
|
||||
<div v-for="template in templates"
|
||||
:key="template.id"
|
||||
class="template"
|
||||
:class="{ selected: selectedTemplateId === template.id }"
|
||||
@click="selectTemplate(template.id)">
|
||||
<img v-if="template.preview" :src="templatePreviewUrl(template.id)" :alt="template.name">
|
||||
<h2>{{ stripFileExtension(template.name) }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<NcButton type="tertiary" @click="onCancel">
|
||||
{{ t('core', 'Cancel') }}
|
||||
</NcButton>
|
||||
<NcButton type="primary" :disabled="!!filenameError" @click="onCreate">
|
||||
{{ t('richdocuments', 'Create') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</div>
|
||||
</NcModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { NcModal, NcButton, NcTextField } from '@nextcloud/vue'
|
||||
import { isFilenameValid, getUniqueName } from '@nextcloud/files'
|
||||
|
||||
export default {
|
||||
name: 'TemplatePicker',
|
||||
components: {
|
||||
NcModal,
|
||||
NcButton,
|
||||
NcTextField,
|
||||
},
|
||||
props: {
|
||||
suggestedFilename: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
templates: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
initialTemplateId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
content: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTemplateId: this.initialTemplateId || (this.templates[0]?.id || null),
|
||||
filename: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filenameError() {
|
||||
if (!isFilenameValid(this.filename)) {
|
||||
return t('core', 'Invalid file name')
|
||||
}
|
||||
|
||||
if (this.content.some((n) => n.basename === this.filenameWithExtension || n.basename === this.filename)) {
|
||||
return t('core', 'File name already exists')
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
filenameWithExtension() {
|
||||
if (!this.filename.includes('.')) {
|
||||
const extension = this.suggestedFilename.split('.').pop()
|
||||
return this.filename + '.' + extension
|
||||
}
|
||||
return this.filename
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.filename = getUniqueName(this.suggestedFilename, this.content.map((n) => n.basename))
|
||||
},
|
||||
methods: {
|
||||
t,
|
||||
stripFileExtension(filename) {
|
||||
return filename.replace(/\.[^/.]+$/, '')
|
||||
},
|
||||
templatePreviewUrl(templateId) {
|
||||
return generateUrl('apps/richdocuments/template/preview/' + templateId)
|
||||
},
|
||||
selectTemplate(templateId) {
|
||||
this.selectedTemplateId = templateId
|
||||
},
|
||||
onCancel() {
|
||||
this.$emit('close')
|
||||
},
|
||||
onCreate() {
|
||||
if (this.filenameError) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$emit('close', this.selectedTemplateId, this.filenameWithExtension)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.template-picker {
|
||||
padding: calc(var(--default-grid-baseline) * 3);
|
||||
|
||||
.filename-input {
|
||||
margin-bottom: calc(var(--default-grid-baseline) * 3);
|
||||
}
|
||||
|
||||
.template-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: calc(var(--default-grid-baseline) * 3);
|
||||
margin-bottom: calc(var(--default-grid-baseline) * 3);
|
||||
}
|
||||
|
||||
.template {
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
border: var(--border-width-input-focused) solid transparent;
|
||||
border-radius: var(--border-radius-large);
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-background-hover);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: var(--border-radius-small);
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: calc(var(--default-grid-baseline) * 2) 0 0;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
font-size: var(--font-size-small);
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: calc(var(--default-grid-baseline) * 2);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -7,10 +7,11 @@ import axios from '@nextcloud/axios'
|
|||
import { emit } from '@nextcloud/event-bus'
|
||||
import Types from '../helpers/types.js'
|
||||
import { createEmptyFile } from '../services/api.js'
|
||||
import { generateUrl, generateFilePath, generateOcsUrl } from '@nextcloud/router'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { showError, spawnDialog } from '@nextcloud/dialogs'
|
||||
import { getCapabilities } from '../services/capabilities.ts'
|
||||
import { File, addNewFileMenuEntry, isFilenameValid, getUniqueName } from '@nextcloud/files'
|
||||
import TemplatePicker from '../components/Modal/TemplatePicker.vue'
|
||||
|
||||
const createFileTypeEntry = (type, displayName, iconClass, index) => ({
|
||||
id: 'add-' + type.extension,
|
||||
|
|
@ -87,65 +88,18 @@ const openTemplatePicker = async (context, type, mimetype, filename, content = [
|
|||
return
|
||||
}
|
||||
|
||||
await showTemplatePickerDialog(context, type, mimetype, filename, templates, content)
|
||||
await spawnDialog(TemplatePicker, {
|
||||
templates,
|
||||
suggestedFilename: filename,
|
||||
content,
|
||||
initialTemplateId: templates[0]?.id,
|
||||
}, (templateId, filename) => {
|
||||
if (templateId) {
|
||||
createDocument(context, mimetype, filename, templateId, content)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
showError(t('core', 'Could not load templates'))
|
||||
}
|
||||
}
|
||||
|
||||
const showTemplatePickerDialog = async (context, type, mimetype, filename, templates, content) => {
|
||||
const { data: tmpl } = await axios.get(generateFilePath('richdocuments', 'templates', 'templatePicker.html'))
|
||||
const $tmpl = $(tmpl).eq(2)
|
||||
const $dlg = $tmpl.octemplate({
|
||||
dialog_name: 'template-picker',
|
||||
dialog_title: t('richdocuments', 'Select template'),
|
||||
})
|
||||
|
||||
templates.forEach((template) => {
|
||||
appendTemplateFromData($dlg[0], template)
|
||||
})
|
||||
|
||||
$('body').append($dlg)
|
||||
|
||||
const buttonlist = [
|
||||
{
|
||||
text: t('core', 'Cancel'),
|
||||
classes: 'cancel',
|
||||
click() {
|
||||
$(this).ocdialog('close')
|
||||
},
|
||||
},
|
||||
{
|
||||
text: t('richdocuments', 'Create'),
|
||||
classes: 'primary',
|
||||
click() {
|
||||
const templateId = this.dataset.templateId
|
||||
createDocument(context, mimetype, filename, templateId, content)
|
||||
$(this).ocdialog('close')
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
$('#template-picker').ocdialog({
|
||||
closeOnEscape: true,
|
||||
modal: true,
|
||||
buttons: buttonlist,
|
||||
})
|
||||
}
|
||||
|
||||
const appendTemplateFromData = (dlg, data) => {
|
||||
const template = dlg.querySelector('.template-model').cloneNode(true)
|
||||
template.className = ''
|
||||
template.querySelector('img').src = generateUrl('apps/richdocuments/template/preview/' + data.id)
|
||||
template.querySelector('h2').textContent = data.name
|
||||
template.onclick = function(e) {
|
||||
e.preventDefault()
|
||||
dlg.dataset.templateId = data.id
|
||||
}
|
||||
if (!dlg.dataset.templateId) {
|
||||
dlg.dataset.templateId = data.id
|
||||
}
|
||||
|
||||
dlg.querySelector('.template-container').appendChild(template)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<div id="{dialog_name}" title="{dialog_title}">
|
||||
<div class="template-container">
|
||||
<a class="hidden template-model" href="#">
|
||||
<span>
|
||||
<img src="" alt="" />
|
||||
<h2></h2>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
Loading…
Add table
Reference in a new issue