mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-02-22 22:28:40 +08:00
Started migration of attachment manager from vue
- Created new dropzone component. - Added standard component event system using custom DOM events. - Added tabs component. - Added ajax-delete-row component.
This commit is contained in:
parent
8dc9689c6d
commit
14b6cd1091
@ -152,7 +152,9 @@ class AttachmentController extends Controller
|
|||||||
{
|
{
|
||||||
$page = $this->pageRepo->getById($pageId);
|
$page = $this->pageRepo->getById($pageId);
|
||||||
$this->checkOwnablePermission('page-view', $page);
|
$this->checkOwnablePermission('page-view', $page);
|
||||||
return response()->json($page->attachments);
|
return view('pages.attachment-list', [
|
||||||
|
'attachments' => $page->attachments->all(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,14 +165,13 @@ class AttachmentController extends Controller
|
|||||||
public function sortForPage(Request $request, int $pageId)
|
public function sortForPage(Request $request, int $pageId)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'files' => 'required|array',
|
'order' => 'required|array',
|
||||||
'files.*.id' => 'required|integer',
|
|
||||||
]);
|
]);
|
||||||
$page = $this->pageRepo->getById($pageId);
|
$page = $this->pageRepo->getById($pageId);
|
||||||
$this->checkOwnablePermission('page-update', $page);
|
$this->checkOwnablePermission('page-update', $page);
|
||||||
|
|
||||||
$attachments = $request->get('files');
|
$attachmentOrder = $request->get('order');
|
||||||
$this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
|
$this->attachmentService->updateFileOrderWithinPage($attachmentOrder, $pageId);
|
||||||
return response()->json(['message' => trans('entities.attachments_order_updated')]);
|
return response()->json(['message' => trans('entities.attachments_order_updated')]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,9 +30,8 @@ class Attachment extends Ownable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the url of this file.
|
* Get the url of this file.
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getUrl()
|
public function getUrl(): string
|
||||||
{
|
{
|
||||||
if ($this->external && strpos($this->path, 'http') !== 0) {
|
if ($this->external && strpos($this->path, 'http') !== 0) {
|
||||||
return $this->path;
|
return $this->path;
|
||||||
|
@ -109,14 +109,14 @@ class AttachmentService extends UploadService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the file ordering for a listing of attached files.
|
* Updates the ordering for a listing of attached files.
|
||||||
* @param array $attachmentList
|
|
||||||
* @param $pageId
|
|
||||||
*/
|
*/
|
||||||
public function updateFileOrderWithinPage($attachmentList, $pageId)
|
public function updateFileOrderWithinPage(array $attachmentOrder, string $pageId)
|
||||||
{
|
{
|
||||||
foreach ($attachmentList as $index => $attachment) {
|
foreach ($attachmentOrder as $index => $attachmentId) {
|
||||||
Attachment::where('uploaded_to', '=', $pageId)->where('id', '=', $attachment['id'])->update(['order' => $index]);
|
Attachment::query()->where('uploaded_to', '=', $pageId)
|
||||||
|
->where('id', '=', $attachmentId)
|
||||||
|
->update(['order' => $index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
32
resources/js/components/ajax-delete-row.js
Normal file
32
resources/js/components/ajax-delete-row.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* AjaxDelete
|
||||||
|
* @extends {Component}
|
||||||
|
*/
|
||||||
|
import {onSelect} from "../services/dom";
|
||||||
|
|
||||||
|
class AjaxDeleteRow {
|
||||||
|
setup() {
|
||||||
|
this.row = this.$el;
|
||||||
|
this.url = this.$opts.url;
|
||||||
|
this.deleteButtons = this.$manyRefs.delete;
|
||||||
|
|
||||||
|
onSelect(this.deleteButtons, this.runDelete.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
runDelete() {
|
||||||
|
this.row.style.opacity = '0.7';
|
||||||
|
this.row.style.pointerEvents = 'none';
|
||||||
|
|
||||||
|
window.$http.delete(this.url).then(resp => {
|
||||||
|
if (typeof resp.data === 'object' && resp.data.message) {
|
||||||
|
window.$events.emit('success', resp.data.message);
|
||||||
|
}
|
||||||
|
this.row.remove();
|
||||||
|
}).catch(err => {
|
||||||
|
this.row.style.opacity = null;
|
||||||
|
this.row.style.pointerEvents = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AjaxDeleteRow;
|
46
resources/js/components/attachments.js
Normal file
46
resources/js/components/attachments.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Attachments
|
||||||
|
* @extends {Component}
|
||||||
|
*/
|
||||||
|
class Attachments {
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
this.container = this.$el;
|
||||||
|
this.pageId = this.$opts.pageId;
|
||||||
|
this.editContainer = this.$refs.editContainer;
|
||||||
|
this.mainTabs = this.$refs.mainTabs;
|
||||||
|
this.list = this.$refs.list;
|
||||||
|
|
||||||
|
this.setupListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupListeners() {
|
||||||
|
this.container.addEventListener('dropzone-success', event => {
|
||||||
|
this.mainTabs.components.tabs.show('items');
|
||||||
|
window.$http.get(`/attachments/get/page/${this.pageId}`).then(resp => {
|
||||||
|
this.list.innerHTML = resp.data;
|
||||||
|
window.components.init(this.list);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
this.container.addEventListener('sortable-list-sort', event => {
|
||||||
|
this.updateOrder(event.detail.ids);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editContainer.addEventListener('keypress', event => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
// TODO - Update editing file
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOrder(idOrder) {
|
||||||
|
window.$http.put(`/attachments/sort/page/${this.pageId}`, {order: idOrder}).then(resp => {
|
||||||
|
window.$events.emit('success', resp.data.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Attachments;
|
69
resources/js/components/dropzone.js
Normal file
69
resources/js/components/dropzone.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import DropZoneLib from "dropzone";
|
||||||
|
import {fadeOut} from "../services/animations";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dropzone
|
||||||
|
* @extends {Component}
|
||||||
|
*/
|
||||||
|
class Dropzone {
|
||||||
|
setup() {
|
||||||
|
this.container = this.$el;
|
||||||
|
this.url = this.$opts.url;
|
||||||
|
|
||||||
|
const _this = this;
|
||||||
|
this.dz = new DropZoneLib(this.container, {
|
||||||
|
addRemoveLinks: true,
|
||||||
|
dictRemoveFile: window.trans('components.image_upload_remove'),
|
||||||
|
timeout: Number(window.uploadTimeout) || 60000,
|
||||||
|
maxFilesize: Number(window.uploadLimit) || 256,
|
||||||
|
url: this.url,
|
||||||
|
withCredentials: true,
|
||||||
|
init() {
|
||||||
|
this.dz = this;
|
||||||
|
this.dz.on('sending', _this.onSending.bind(_this));
|
||||||
|
this.dz.on('success', _this.onSuccess.bind(_this));
|
||||||
|
this.dz.on('error', _this.onError.bind(_this));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSending(file, xhr, data) {
|
||||||
|
|
||||||
|
const token = window.document.querySelector('meta[name=token]').getAttribute('content');
|
||||||
|
data.append('_token', token);
|
||||||
|
|
||||||
|
xhr.ontimeout = function (e) {
|
||||||
|
this.dz.emit('complete', file);
|
||||||
|
this.dz.emit('error', file, window.trans('errors.file_upload_timeout'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccess(file, data) {
|
||||||
|
this.container.dispatchEvent(new Event('dropzone'))
|
||||||
|
this.$emit('success', {file, data});
|
||||||
|
fadeOut(file.previewElement, 800, () => {
|
||||||
|
this.dz.removeFile(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onError(file, errorMessage, xhr) {
|
||||||
|
this.$emit('error', {file, errorMessage, xhr});
|
||||||
|
|
||||||
|
const setMessage = (message) => {
|
||||||
|
const messsageEl = file.previewElement.querySelector('[data-dz-errormessage]');
|
||||||
|
messsageEl.textContent = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xhr && xhr.status === 413) {
|
||||||
|
setMessage(window.trans('errors.server_upload_limit'))
|
||||||
|
} else if (errorMessage.file) {
|
||||||
|
setMessage(errorMessage.file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAll() {
|
||||||
|
this.dz.removeAllFiles(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Dropzone;
|
@ -40,6 +40,14 @@ function initComponent(name, element) {
|
|||||||
instance.$refs = allRefs.refs;
|
instance.$refs = allRefs.refs;
|
||||||
instance.$manyRefs = allRefs.manyRefs;
|
instance.$manyRefs = allRefs.manyRefs;
|
||||||
instance.$opts = parseOpts(name, element);
|
instance.$opts = parseOpts(name, element);
|
||||||
|
instance.$emit = (eventName, data = {}) => {
|
||||||
|
data.from = instance;
|
||||||
|
const event = new CustomEvent(`${name}-${eventName}`, {
|
||||||
|
bubbles: true,
|
||||||
|
detail: data
|
||||||
|
});
|
||||||
|
instance.$el.dispatchEvent(event);
|
||||||
|
};
|
||||||
if (typeof instance.setup === 'function') {
|
if (typeof instance.setup === 'function') {
|
||||||
instance.setup();
|
instance.setup();
|
||||||
}
|
}
|
||||||
@ -158,4 +166,5 @@ export default initAll;
|
|||||||
* @property {Object<String, HTMLElement>} $refs
|
* @property {Object<String, HTMLElement>} $refs
|
||||||
* @property {Object<String, HTMLElement[]>} $manyRefs
|
* @property {Object<String, HTMLElement[]>} $manyRefs
|
||||||
* @property {Object<String, String>} $opts
|
* @property {Object<String, String>} $opts
|
||||||
|
* @property {function(string, Object)} $emit
|
||||||
*/
|
*/
|
@ -9,9 +9,12 @@ class SortableList {
|
|||||||
this.container = this.$el;
|
this.container = this.$el;
|
||||||
this.handleSelector = this.$opts.handleSelector;
|
this.handleSelector = this.$opts.handleSelector;
|
||||||
|
|
||||||
new Sortable(this.container, {
|
const sortable = new Sortable(this.container, {
|
||||||
handle: this.handleSelector,
|
handle: this.handleSelector,
|
||||||
animation: 150,
|
animation: 150,
|
||||||
|
onSort: () => {
|
||||||
|
this.$emit('sort', {ids: sortable.toArray()});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
51
resources/js/components/tabs.js
Normal file
51
resources/js/components/tabs.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Tabs
|
||||||
|
* Works by matching 'tabToggle<Key>' with 'tabContent<Key>' sections.
|
||||||
|
* @extends {Component}
|
||||||
|
*/
|
||||||
|
import {onSelect} from "../services/dom";
|
||||||
|
|
||||||
|
class Tabs {
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
this.tabContentsByName = {};
|
||||||
|
this.tabButtonsByName = {};
|
||||||
|
this.allContents = [];
|
||||||
|
this.allButtons = [];
|
||||||
|
|
||||||
|
for (const [key, elems] of Object.entries(this.$manyRefs || {})) {
|
||||||
|
if (key.startsWith('toggle')) {
|
||||||
|
const cleanKey = key.replace('toggle', '').toLowerCase();
|
||||||
|
onSelect(elems, e => this.show(cleanKey));
|
||||||
|
this.allButtons.push(...elems);
|
||||||
|
this.tabButtonsByName[cleanKey] = elems;
|
||||||
|
}
|
||||||
|
if (key.startsWith('content')) {
|
||||||
|
const cleanKey = key.replace('content', '').toLowerCase();
|
||||||
|
this.tabContentsByName[cleanKey] = elems;
|
||||||
|
this.allContents.push(...elems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show(key) {
|
||||||
|
this.allContents.forEach(c => {
|
||||||
|
c.classList.add('hidden');
|
||||||
|
c.classList.remove('selected');
|
||||||
|
});
|
||||||
|
this.allButtons.forEach(b => b.classList.remove('selected'));
|
||||||
|
|
||||||
|
const contents = this.tabContentsByName[key] || [];
|
||||||
|
const buttons = this.tabButtonsByName[key] || [];
|
||||||
|
if (contents.length > 0) {
|
||||||
|
contents.forEach(c => {
|
||||||
|
c.classList.remove('hidden')
|
||||||
|
c.classList.add('selected')
|
||||||
|
});
|
||||||
|
buttons.forEach(b => b.classList.add('selected'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tabs;
|
@ -256,7 +256,7 @@ return [
|
|||||||
'attachments_upload' => 'Upload File',
|
'attachments_upload' => 'Upload File',
|
||||||
'attachments_link' => 'Attach Link',
|
'attachments_link' => 'Attach Link',
|
||||||
'attachments_set_link' => 'Set Link',
|
'attachments_set_link' => 'Set Link',
|
||||||
'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
|
'attachments_delete' => 'Are you sure you want to delete this attachment?',
|
||||||
'attachments_dropzone' => 'Drop files or click here to attach a file',
|
'attachments_dropzone' => 'Drop files or click here to attach a file',
|
||||||
'attachments_no_files' => 'No files have been uploaded',
|
'attachments_no_files' => 'No files have been uploaded',
|
||||||
'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
|
'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
|
||||||
|
@ -126,6 +126,10 @@ body.flexbox {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.justify-flex-end {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display and float utilities
|
* Display and float utilities
|
||||||
|
@ -66,4 +66,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@include('pages.attachment-manager', ['page' => \BookStack\Entities\Page::first()])
|
||||||
|
|
||||||
@stop
|
@stop
|
||||||
|
9
resources/views/components/dropzone.blade.php
Normal file
9
resources/views/components/dropzone.blade.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{{--
|
||||||
|
@url - URL to upload to.
|
||||||
|
@placeholder - Placeholder text
|
||||||
|
--}}
|
||||||
|
<div component="dropzone"
|
||||||
|
option:dropzone:url="{{ $url }}"
|
||||||
|
class="dropzone-container text-center">
|
||||||
|
<button type="button" class="dz-message">{{ $placeholder }}</button>
|
||||||
|
</div>
|
28
resources/views/pages/attachment-list.blade.php
Normal file
28
resources/views/pages/attachment-list.blade.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<div component="sortable-list" option:sortable-list:handle-selector=".handle">
|
||||||
|
@foreach($attachments as $attachment)
|
||||||
|
<div component="ajax-delete-row"
|
||||||
|
option:ajax-delete-row:url="{{ url('/attachments/' . $attachment->id) }}"
|
||||||
|
data-id="{{ $attachment->id }}"
|
||||||
|
class="card drag-card">
|
||||||
|
<div class="handle">@icon('grip')</div>
|
||||||
|
<div class="py-s">
|
||||||
|
<a href="{{ $attachment->getUrl() }}" target="_blank">{{ $attachment->name }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="flex-fill justify-flex-end">
|
||||||
|
<button type="button" class="drag-card-action text-center text-primary">@icon('edit')</button>
|
||||||
|
<div component="dropdown" class="flex-fill relative">
|
||||||
|
<button refs="dropdown@toggle" type="button" class="drag-card-action text-center text-neg">@icon('close')</button>
|
||||||
|
<div refs="dropdown@menu" class="dropdown-menu">
|
||||||
|
<p class="text-neg small px-m mb-xs">{{ trans('entities.attachments_delete') }}</p>
|
||||||
|
<button refs="ajax-delete-row@delete" type="button" class="text-primary small delete">{{ trans('common.confirm') }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
@if (count($attachments) === 0)
|
||||||
|
<p class="small text-muted">
|
||||||
|
{{ trans('entities.attachments_no_files') }}
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
@ -1,101 +1,91 @@
|
|||||||
<div toolbox-tab-content="files" id="attachment-manager" page-id="{{ $page->id ?? 0 }}">
|
<div style="display: block;" toolbox-tab-content="files"
|
||||||
|
component="attachments"
|
||||||
|
option:attachments:page-id="{{ $page->id ?? 0 }}">
|
||||||
|
|
||||||
@exposeTranslations([
|
@exposeTranslations([
|
||||||
'entities.attachments_file_uploaded',
|
'entities.attachments_file_uploaded',
|
||||||
'entities.attachments_file_updated',
|
'entities.attachments_file_updated',
|
||||||
'entities.attachments_link_attached',
|
'entities.attachments_link_attached',
|
||||||
'entities.attachments_updated_success',
|
'entities.attachments_updated_success',
|
||||||
'errors.server_upload_limit',
|
'errors.server_upload_limit',
|
||||||
'components.image_upload_remove',
|
'components.image_upload_remove',
|
||||||
'components.file_upload_timeout',
|
'components.file_upload_timeout',
|
||||||
])
|
])
|
||||||
|
|
||||||
<h4>{{ trans('entities.attachments') }}</h4>
|
<h4>{{ trans('entities.attachments') }}</h4>
|
||||||
<div class="px-l files">
|
<div class="px-l files">
|
||||||
|
|
||||||
<div id="file-list" v-show="!fileToEdit">
|
<div id="file-list">
|
||||||
<p class="text-muted small">{{ trans('entities.attachments_explain') }} <span class="text-warn">{{ trans('entities.attachments_explain_instant_save') }}</span></p>
|
<p class="text-muted small">{{ trans('entities.attachments_explain') }} <span class="text-warn">{{ trans('entities.attachments_explain_instant_save') }}</span></p>
|
||||||
|
|
||||||
<div class="tab-container">
|
<div component="tabs" refs="attachments@mainTabs" class="tab-container">
|
||||||
<div class="nav-tabs">
|
<div class="nav-tabs">
|
||||||
<button type="button" @click="tab = 'list'" :class="{selected: tab === 'list'}"
|
<button refs="tabs@toggleItems" type="button" class="selected tab-item">{{ trans('entities.attachments_items') }}</button>
|
||||||
class="tab-item">{{ trans('entities.attachments_items') }}</button>
|
<button refs="tabs@toggleUpload" type="button" class="tab-item">{{ trans('entities.attachments_upload') }}</button>
|
||||||
<button type="button" @click="tab = 'file'" :class="{selected: tab === 'file'}"
|
<button refs="tabs@toggleLinks" type="button" class="tab-item">{{ trans('entities.attachments_link') }}</button>
|
||||||
class="tab-item">{{ trans('entities.attachments_upload') }}</button>
|
|
||||||
<button type="button" @click="tab = 'link'" :class="{selected: tab === 'link'}"
|
|
||||||
class="tab-item">{{ trans('entities.attachments_link') }}</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-show="tab === 'list'">
|
<div refs="tabs@contentItems attachments@list">
|
||||||
<draggable style="width: 100%;" :options="{handle: '.handle'}" @change="fileSortUpdate" :list="files" element="div">
|
@include('pages.attachment-list', ['attachments' => $page->attachments->all()])
|
||||||
<div v-for="(file, index) in files" :key="file.id" class="card drag-card">
|
|
||||||
<div class="handle">@icon('grip')</div>
|
|
||||||
<div class="py-s">
|
|
||||||
<a :href="getFileUrl(file)" target="_blank" v-text="file.name"></a>
|
|
||||||
<div v-if="file.deleting">
|
|
||||||
<span class="text-neg small">{{ trans('entities.attachments_delete_confirm') }}</span>
|
|
||||||
<br>
|
|
||||||
<button type="button" class="text-primary small" @click="file.deleting = false;">{{ trans('common.cancel') }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" @click="startEdit(file)" class="drag-card-action text-center text-primary">@icon('edit')</button>
|
|
||||||
<button type="button" @click="deleteFile(file)" class="drag-card-action text-center text-neg">@icon('close')</button>
|
|
||||||
</div>
|
|
||||||
</draggable>
|
|
||||||
<p class="small text-muted" v-if="files.length === 0">
|
|
||||||
{{ trans('entities.attachments_no_files') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-show="tab === 'file'">
|
<div refs="tabs@contentUpload" class="hiden">
|
||||||
<dropzone placeholder="{{ trans('entities.attachments_dropzone') }}" :upload-url="getUploadUrl()" :uploaded-to="pageId" @success="uploadSuccess"></dropzone>
|
@include('components.dropzone', [
|
||||||
|
'placeholder' => trans('entities.attachments_dropzone'),
|
||||||
|
'url' => url('/attachments/upload?uploaded_to=' . $page->id)
|
||||||
|
])
|
||||||
</div>
|
</div>
|
||||||
<div v-show="tab === 'link'" @keypress.enter.prevent="attachNewLink(file)">
|
<div refs="tabs@contentLinks" class="hidden">
|
||||||
<p class="text-muted small">{{ trans('entities.attachments_explain_link') }}</p>
|
<p class="text-muted small">{{ trans('entities.attachments_explain_link') }}</p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="attachment-via-link">{{ trans('entities.attachments_link_name') }}</label>
|
<label for="attachment_link_name">{{ trans('entities.attachments_link_name') }}</label>
|
||||||
<input type="text" placeholder="{{ trans('entities.attachments_link_name') }}" v-model="file.name">
|
<input name="attachment_link_name" id="attachment_link_name" type="text" placeholder="{{ trans('entities.attachments_link_name') }}">
|
||||||
<p class="small text-neg" v-for="error in errors.link.name" v-text="error"></p>
|
<p class="small text-neg"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="attachment-via-link">{{ trans('entities.attachments_link_url') }}</label>
|
<label for="attachment_link_url">{{ trans('entities.attachments_link_url') }}</label>
|
||||||
<input type="text" placeholder="{{ trans('entities.attachments_link_url_hint') }}" v-model="file.link">
|
<input name="attachment_link_url" id="attachment_link_url" type="text" placeholder="{{ trans('entities.attachments_link_url_hint') }}">
|
||||||
<p class="small text-neg" v-for="error in errors.link.link" v-text="error"></p>
|
<p class="small text-neg"></p>
|
||||||
</div>
|
</div>
|
||||||
<button @click.prevent="attachNewLink(file)" class="button">{{ trans('entities.attach') }}</button>
|
<button class="button">{{ trans('entities.attach') }}</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="file-edit" v-if="fileToEdit" @keypress.enter.prevent="updateFile(fileToEdit)">
|
<div refs="attachments@editContainer" class="hidden">
|
||||||
<h5>{{ trans('entities.attachments_edit_file') }}</h5>
|
<h5>{{ trans('entities.attachments_edit_file') }}</h5>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="attachment-name-edit">{{ trans('entities.attachments_edit_file_name') }}</label>
|
<label for="attachment-name-edit">{{ trans('entities.attachments_edit_file_name') }}</label>
|
||||||
<input type="text" id="attachment-name-edit" placeholder="{{ trans('entities.attachments_edit_file_name') }}" v-model="fileToEdit.name">
|
<input type="text" id="attachment-name-edit"
|
||||||
<p class="small text-neg" v-for="error in errors.edit.name" v-text="error"></p>
|
name="attachment_name"
|
||||||
|
placeholder="{{ trans('entities.attachments_edit_file_name') }}">
|
||||||
|
<p class="small text-neg"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-container">
|
<div component="tabs" class="tab-container">
|
||||||
<div class="nav-tabs">
|
<div class="nav-tabs">
|
||||||
<button type="button" @click="editTab = 'file'" :class="{selected: editTab === 'file'}" class="tab-item">{{ trans('entities.attachments_upload') }}</button>
|
<button refs="tabs@toggleFile" type="button" class="tab-item selected">{{ trans('entities.attachments_upload') }}</button>
|
||||||
<button type="button" @click="editTab = 'link'" :class="{selected: editTab === 'link'}" class="tab-item">{{ trans('entities.attachments_set_link') }}</button>
|
<button refs="tabs@toggleLink" type="button" class="tab-item">{{ trans('entities.attachments_set_link') }}</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="editTab === 'file'">
|
<div refs="tabs@contentFile">
|
||||||
|
@include('components.dropzone', [
|
||||||
|
'placeholder' => trans('entities.attachments_edit_drop_upload'),
|
||||||
|
'url' => url('/attachments')
|
||||||
|
])
|
||||||
<dropzone :upload-url="getUploadUrl(fileToEdit)" :uploaded-to="pageId" placeholder="{{ trans('entities.attachments_edit_drop_upload') }}" @success="uploadSuccessUpdate"></dropzone>
|
<dropzone :upload-url="getUploadUrl(fileToEdit)" :uploaded-to="pageId" placeholder="{{ trans('entities.attachments_edit_drop_upload') }}" @success="uploadSuccessUpdate"></dropzone>
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="editTab === 'link'">
|
<div refs="tabs@contentLink" class="hidden">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="attachment-link-edit">{{ trans('entities.attachments_link_url') }}</label>
|
<label for="attachment-link-edit">{{ trans('entities.attachments_link_url') }}</label>
|
||||||
<input type="text" id="attachment-link-edit" placeholder="{{ trans('entities.attachment_link') }}" v-model="fileToEdit.link">
|
<input type="text" id="attachment-link-edit" placeholder="{{ trans('entities.attachment_link') }}" v-model="fileToEdit.link">
|
||||||
<p class="small text-neg" v-for="error in errors.edit.link" v-text="error"></p>
|
<p class="small text-neg"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="button outline" @click="cancelEdit">{{ trans('common.back') }}</button>
|
<button type="button" class="button outline">{{ trans('common.back') }}</button>
|
||||||
<button @click.enter.prevent="updateFile(fileToEdit)" class="button">{{ trans('common.save') }}</button>
|
<button class="button">{{ trans('common.save') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user