mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-22 16:16:21 +00:00
WIP: simplificar subida de multimedia
This commit is contained in:
parent
8f4566f06c
commit
1bfbff912c
3 changed files with 159 additions and 287 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
import multimedia from './types/multimedia'
|
||||||
|
|
||||||
export const setAuxiliaryToolbar = (editorEl, toolbarName) => {
|
export const setAuxiliaryToolbar = (editorEl, toolbarName) => {
|
||||||
const toolbarEl = editorEl.querySelector(`*[data-editor-auxiliary-toolbar]`)
|
const toolbarEl = editorEl.querySelector(`*[data-editor-auxiliary-toolbar]`)
|
||||||
for (const otherEl of toolbarEl.childNodes) {
|
for (const otherEl of toolbarEl.childNodes) {
|
||||||
|
@ -14,24 +16,6 @@ export const moveChildren = (from, to, toRef) => {
|
||||||
while (from.firstChild) to.insertBefore(from.firstChild, toRef);
|
while (from.firstChild) to.insertBefore(from.firstChild, toRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadFile = (file) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const upload = new ActiveStorage.DirectUpload(
|
|
||||||
file,
|
|
||||||
origin + '/rails/active_storage/direct_uploads',
|
|
||||||
)
|
|
||||||
|
|
||||||
upload.create((error, blob) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
const url = `${origin}/rails/active_storage/blobs/${blob.signed_id}/${blob.filename}`
|
|
||||||
resolve(url)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const marks = {
|
export const marks = {
|
||||||
bold: {
|
bold: {
|
||||||
selector: "strong",
|
selector: "strong",
|
||||||
|
@ -113,65 +97,7 @@ export const blocks = {
|
||||||
selector: "OL",
|
selector: "OL",
|
||||||
setFn: tagNameSetFn("OL"),
|
setFn: tagNameSetFn("OL"),
|
||||||
},
|
},
|
||||||
img: {
|
multimedia: multimedia.block,
|
||||||
selector: "IMG",
|
|
||||||
createFn: editorEl => {
|
|
||||||
const el = document.createElement("IMG")
|
|
||||||
el.src = "/placeholder.png"
|
|
||||||
el.alt = ""
|
|
||||||
return el
|
|
||||||
},
|
|
||||||
},
|
|
||||||
figure: {
|
|
||||||
selector: "FIGURE",
|
|
||||||
noButton: true
|
|
||||||
},
|
|
||||||
figcaption: {
|
|
||||||
selector: "FIGCAPTION",
|
|
||||||
noButton: true,
|
|
||||||
},
|
|
||||||
audio: {
|
|
||||||
selector: "AUDIO",
|
|
||||||
createFn: editorEl => {
|
|
||||||
const el = document.createElement("FIGURE")
|
|
||||||
|
|
||||||
el.appendChild(document.createElement("AUDIO"))
|
|
||||||
el.appendChild(document.createElement("FIGCAPTION"))
|
|
||||||
|
|
||||||
el.children[0].controls = true
|
|
||||||
el.children[1].innerText = "Toca el borde para subir un archivo de audio"
|
|
||||||
|
|
||||||
return el
|
|
||||||
},
|
|
||||||
},
|
|
||||||
video: {
|
|
||||||
selector: "VIDEO",
|
|
||||||
createFn: editorEl => {
|
|
||||||
const el = document.createElement("VIDEO")
|
|
||||||
el.poster = "/placeholder.png"
|
|
||||||
// Para poder seleccionar el video tenemos que sacarle los
|
|
||||||
// controles, pero queremos poder verlos para reproducir el video.
|
|
||||||
// Al hacer click le damos los controles y al salir se los sacamos
|
|
||||||
// para poder hacer click de vuelta
|
|
||||||
el.addEventListener('click', event => event.target.controls = true)
|
|
||||||
el.addEventListener('focusout', event => event.target.controls = false)
|
|
||||||
return el
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// PDF
|
|
||||||
pdf: {
|
|
||||||
selector: "IFRAME",
|
|
||||||
createFn: editorEl => {
|
|
||||||
const el = document.createElement("FIGURE")
|
|
||||||
|
|
||||||
el.appendChild(document.createElement("IFRAME"))
|
|
||||||
el.appendChild(document.createElement("FIGCAPTION"))
|
|
||||||
|
|
||||||
el.children[1].innerText = "Toca el borde para subir un archivo PDF"
|
|
||||||
|
|
||||||
return el
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const divWithStyleCreateFn = styleFn => () => {
|
const divWithStyleCreateFn = styleFn => () => {
|
||||||
|
@ -251,189 +177,5 @@ export const typesWithProperties = {
|
||||||
}, false)
|
}, false)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
img: {
|
multimedia: multimedia.typeWithProperty,
|
||||||
selector: blocks.img.selector,
|
|
||||||
updateInput (el, editorEl) {
|
|
||||||
setAuxiliaryToolbar(editorEl, "img")
|
|
||||||
|
|
||||||
const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`)
|
|
||||||
imgFileEl.disabled = false
|
|
||||||
// XXX: No se puede cambiar el texto, ¡esto puede ser confuso!
|
|
||||||
|
|
||||||
const imgAltEl = editorEl.querySelector(`*[data-prop="img-alt"]`)
|
|
||||||
imgAltEl.disabled = false
|
|
||||||
imgAltEl.value = el.alt
|
|
||||||
},
|
|
||||||
disableInput (editorEl) {
|
|
||||||
const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`)
|
|
||||||
imgFileEl.disabled = true
|
|
||||||
|
|
||||||
const imgAltEl = editorEl.querySelector(`*[data-prop="img-alt"]`)
|
|
||||||
imgAltEl.disabled = true
|
|
||||||
imgAltEl.value = ""
|
|
||||||
},
|
|
||||||
setupInput (editorEl, contentEl) {
|
|
||||||
const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`)
|
|
||||||
imgFileEl.addEventListener("input", event => {
|
|
||||||
const imgEl = contentEl.querySelector("img.selected")
|
|
||||||
if (!imgEl) return
|
|
||||||
|
|
||||||
const file = imgFileEl.files[0]
|
|
||||||
|
|
||||||
imgEl.dataset.editorLoading = true
|
|
||||||
uploadFile(file)
|
|
||||||
.then(url => {
|
|
||||||
imgEl.src = url
|
|
||||||
delete imgEl.dataset.editorError
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// TODO: mostrar error
|
|
||||||
console.error(err)
|
|
||||||
imgEl.dataset.editorError = true
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
delete imgEl.dataset.editorLoading
|
|
||||||
})
|
|
||||||
}, false)
|
|
||||||
|
|
||||||
const imgAltEl = editorEl.querySelector(`*[data-prop="img-alt"]`)
|
|
||||||
imgAltEl.addEventListener("input", event => {
|
|
||||||
const imgEl = contentEl.querySelector("img.selected")
|
|
||||||
if (imgEl) imgEl.alt = imgAltEl.value
|
|
||||||
}, false)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
figure: {
|
|
||||||
selector: blocks.figure.selector,
|
|
||||||
actualInput (el) {
|
|
||||||
// TODO: Cuando tengamos otros iframes hay que seleccionarlos de
|
|
||||||
// otra forma.
|
|
||||||
const tag = el.children[0].tagName.toLowerCase()
|
|
||||||
|
|
||||||
return typesWithProperties[(tag === 'iframe' ? 'pdf' : tag)]
|
|
||||||
},
|
|
||||||
updateInput (el, editorEl) {
|
|
||||||
typesWithProperties.figure.actualInput(el).updateInput(el.children[0], editorEl)
|
|
||||||
},
|
|
||||||
disableInput (editorEl) {},
|
|
||||||
setupInput (editorEl, contentEl) {},
|
|
||||||
},
|
|
||||||
audio: {
|
|
||||||
selector: blocks.audio.selector,
|
|
||||||
updateInput (el, editorEl) {
|
|
||||||
setAuxiliaryToolbar(editorEl, "audio")
|
|
||||||
|
|
||||||
const audioFileEl = editorEl.querySelector(`*[data-prop="audio-file"]`)
|
|
||||||
audioFileEl.disabled = false
|
|
||||||
// XXX: No se puede cambiar el texto, ¡esto puede ser confuso!
|
|
||||||
},
|
|
||||||
disableInput (editorEl) {
|
|
||||||
const audioFileEl = editorEl.querySelector(`*[data-prop="audio-file"]`)
|
|
||||||
audioFileEl.disabled = true
|
|
||||||
},
|
|
||||||
setupInput (editorEl, contentEl) {
|
|
||||||
const audioFileEl = editorEl.querySelector(`*[data-prop="audio-file"]`)
|
|
||||||
audioFileEl.addEventListener("input", event => {
|
|
||||||
const figureEl = getSelected(contentEl)
|
|
||||||
if (!figureEl) return
|
|
||||||
|
|
||||||
const file = audioFileEl.files[0]
|
|
||||||
|
|
||||||
const audioEl = figureEl.querySelector('audio')
|
|
||||||
|
|
||||||
audioEl.dataset.editorLoading = true
|
|
||||||
uploadFile(file)
|
|
||||||
.then(url => {
|
|
||||||
audioEl.src = url
|
|
||||||
delete audioEl.dataset.editorError
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// TODO: mostrar error
|
|
||||||
console.error(err)
|
|
||||||
audioEl.dataset.editorError = true
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
delete audioEl.dataset.editorLoading
|
|
||||||
})
|
|
||||||
}, false)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
video: {
|
|
||||||
selector: blocks.video.selector,
|
|
||||||
updateInput (el, editorEl) {
|
|
||||||
setAuxiliaryToolbar(editorEl, "video")
|
|
||||||
|
|
||||||
const videoFileEl = editorEl.querySelector(`*[data-prop="video-file"]`)
|
|
||||||
videoFileEl.disabled = false
|
|
||||||
// XXX: No se puede cambiar el texto, ¡esto puede ser confuso!
|
|
||||||
},
|
|
||||||
disableInput (editorEl) {
|
|
||||||
const videoFileEl = editorEl.querySelector(`*[data-prop="video-file"]`)
|
|
||||||
videoFileEl.disabled = true
|
|
||||||
},
|
|
||||||
setupInput (editorEl, contentEl) {
|
|
||||||
const videoFileEl = editorEl.querySelector(`*[data-prop="video-file"]`)
|
|
||||||
videoFileEl.addEventListener("input", event => {
|
|
||||||
const videoEl = getSelected(contentEl)
|
|
||||||
if (!videoEl) return
|
|
||||||
|
|
||||||
const file = videoFileEl.files[0]
|
|
||||||
|
|
||||||
videoEl.poster = ""
|
|
||||||
videoEl.dataset.editorLoading = true
|
|
||||||
uploadFile(file)
|
|
||||||
.then(url => {
|
|
||||||
videoEl.src = url
|
|
||||||
delete videoEl.dataset.editorError
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// TODO: mostrar error
|
|
||||||
console.error(err)
|
|
||||||
videoEl.dataset.editorError = true
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
delete videoEl.dataset.editorLoading
|
|
||||||
})
|
|
||||||
}, false)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pdf: {
|
|
||||||
selector: blocks.pdf.selector,
|
|
||||||
updateInput (el, editorEl) {
|
|
||||||
setAuxiliaryToolbar(editorEl, "pdf")
|
|
||||||
|
|
||||||
const pdfFileEl = editorEl.querySelector(`*[data-prop="pdf-file"]`)
|
|
||||||
pdfFileEl.disabled = false
|
|
||||||
// XXX: No se puede cambiar el texto, ¡esto puede ser confuso!
|
|
||||||
},
|
|
||||||
disableInput (editorEl) {
|
|
||||||
const pdfFileEl = editorEl.querySelector(`*[data-prop="pdf-file"]`)
|
|
||||||
pdfFileEl.disabled = true
|
|
||||||
},
|
|
||||||
setupInput (editorEl, contentEl) {
|
|
||||||
const pdfFileEl = editorEl.querySelector(`*[data-prop="pdf-file"]`)
|
|
||||||
pdfFileEl.addEventListener("input", event => {
|
|
||||||
const figureEl = getSelected(contentEl)
|
|
||||||
if (!figureEl) return
|
|
||||||
|
|
||||||
const file = pdfFileEl.files[0]
|
|
||||||
const pdfEl = figureEl.children[0]
|
|
||||||
|
|
||||||
pdfEl.dataset.editorLoading = true
|
|
||||||
uploadFile(file)
|
|
||||||
.then(url => {
|
|
||||||
pdfEl.src = url
|
|
||||||
delete pdfEl.dataset.editorError
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// TODO: mostrar error
|
|
||||||
console.error(err)
|
|
||||||
pdfEl.dataset.editorError = true
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
delete pdfEl.dataset.editorLoading
|
|
||||||
})
|
|
||||||
}, false)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
148
app/javascript/editor/types/multimedia.js
Normal file
148
app/javascript/editor/types/multimedia.js
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
import { setAuxiliaryToolbar } from '../types'
|
||||||
|
|
||||||
|
const uploadFile = (file) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const upload = new ActiveStorage.DirectUpload(
|
||||||
|
file,
|
||||||
|
origin + '/rails/active_storage/direct_uploads',
|
||||||
|
)
|
||||||
|
|
||||||
|
upload.create((error, blob) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
const url = `${origin}/rails/active_storage/blobs/${blob.signed_id}/${blob.filename}`
|
||||||
|
resolve(url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const srcMakeElement = tag => url => {
|
||||||
|
const el = document.createElement(tag)
|
||||||
|
el.src = url
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
const multimediaKinds = {
|
||||||
|
img: {
|
||||||
|
mime: /^image\/.+$/,
|
||||||
|
makeElement: srcMakeElement('img'),
|
||||||
|
},
|
||||||
|
audio: {
|
||||||
|
mime: /^audio\/.+$/,
|
||||||
|
makeElement: srcMakeElement('audio'),
|
||||||
|
},
|
||||||
|
video: {
|
||||||
|
mime: /^video\/.+$/,
|
||||||
|
makeElement: srcMakeElement('video'),
|
||||||
|
},
|
||||||
|
pdf: {
|
||||||
|
mime: /^application\/pdf$/,
|
||||||
|
makeElement: srcMakeElement('iframe'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const setMultimedia = (figureEl, url, mimeType) => {
|
||||||
|
let kind
|
||||||
|
for (const _kind of Object.values(multimediaKinds)) {
|
||||||
|
if (mimeType.match(_kind.mime)) {
|
||||||
|
kind = _kind
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: mostrar error
|
||||||
|
if (!kind)
|
||||||
|
throw new Error('¡Archivo no soportado!')
|
||||||
|
|
||||||
|
const currentMultimedia = figureEl.querySelector('*[data-editor-multimedia-element]')
|
||||||
|
const newMultimedia = kind.makeElement(url)
|
||||||
|
newMultimedia.dataset.editorMultimediaElement = true
|
||||||
|
if (currentMultimedia)
|
||||||
|
figureEl.replaceChild(newMultimedia, currentMultimedia)
|
||||||
|
else figureEl.appendChild(newMultimedia)
|
||||||
|
}
|
||||||
|
|
||||||
|
const block = {
|
||||||
|
selector: "FIGURE[data-editor-multimedia]",
|
||||||
|
createFn: editorEl => {
|
||||||
|
const el = document.createElement("FIGURE")
|
||||||
|
el.dataset.editorMultimedia = true
|
||||||
|
|
||||||
|
const placeholderEl = document.createElement("p")
|
||||||
|
placeholderEl.dataset.editorMultimediaElement = true
|
||||||
|
placeholderEl.append("Toca el borde para subir un archivo")
|
||||||
|
el.appendChild(placeholderEl)
|
||||||
|
|
||||||
|
const figcaptionEl = document.createElement("FIGCAPTION")
|
||||||
|
figcaptionEl.append("Escribí una descripción del archivo")
|
||||||
|
figcaptionEl.controls = true
|
||||||
|
el.appendChild(figcaptionEl)
|
||||||
|
|
||||||
|
return el
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const typeWithProperty = {
|
||||||
|
selector: block.selector,
|
||||||
|
updateInput (el, editorEl) {
|
||||||
|
setAuxiliaryToolbar(editorEl, "multimedia")
|
||||||
|
|
||||||
|
const fileEl = editorEl.querySelector(`*[data-prop="multimedia-file"]`)
|
||||||
|
fileEl.disabled = false
|
||||||
|
// XXX: No se puede cambiar el texto del archivo seleccionado,
|
||||||
|
// ¡esto puede ser confuso!
|
||||||
|
|
||||||
|
const altEl = editorEl.querySelector(`*[data-prop="multimedia-alt"]`)
|
||||||
|
altEl.disabled = false
|
||||||
|
altEl.value = el.alt
|
||||||
|
|
||||||
|
const uploadEl = editorEl.querySelector(`*[data-prop="multimedia-file-upload"]`)
|
||||||
|
uploadEl.disabled = false
|
||||||
|
},
|
||||||
|
disableInput (editorEl) {
|
||||||
|
const fileEl = editorEl.querySelector(`*[data-prop="multimedia-file"]`)
|
||||||
|
fileEl.disabled = true
|
||||||
|
|
||||||
|
const altEl = editorEl.querySelector(`*[data-prop="multimedia-alt"]`)
|
||||||
|
altEl.disabled = true
|
||||||
|
altEl.value = ""
|
||||||
|
|
||||||
|
const uploadEl = editorEl.querySelector(`*[data-prop="multimedia-file-upload"]`)
|
||||||
|
uploadEl.disabled = true
|
||||||
|
},
|
||||||
|
setupInput (editorEl, contentEl) {
|
||||||
|
const fileEl = editorEl.querySelector(`*[data-prop="multimedia-file"]`)
|
||||||
|
const uploadEl = editorEl.querySelector(`*[data-prop="multimedia-file-upload"]`)
|
||||||
|
|
||||||
|
uploadEl.addEventListener("click", event => {
|
||||||
|
const selectedEl = contentEl.querySelector("figure.selected")
|
||||||
|
if (!selectedEl) return
|
||||||
|
|
||||||
|
const file = fileEl.files[0]
|
||||||
|
|
||||||
|
selectedEl.dataset.editorLoading = true
|
||||||
|
uploadFile(file)
|
||||||
|
.then(url => {
|
||||||
|
setMultimedia(selectedEl, url, file.type)
|
||||||
|
delete selectedEl.dataset.editorError
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
// TODO: mostrar error
|
||||||
|
console.error(err)
|
||||||
|
selectedEl.dataset.editorError = true
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
delete selectedEl.dataset.editorLoading
|
||||||
|
})
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
const altEl = editorEl.querySelector(`*[data-prop="multimedia-alt"]`)
|
||||||
|
altEl.addEventListener("input", event => {
|
||||||
|
const imgEl = contentEl.querySelector("img.selected")
|
||||||
|
if (imgEl) imgEl.alt = imgAltEl.value
|
||||||
|
}, false)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { block, typeWithProperty }
|
|
@ -34,10 +34,7 @@
|
||||||
%button.btn{ data: { button: 'left' } }= t('editor.left')
|
%button.btn{ data: { button: 'left' } }= t('editor.left')
|
||||||
%button.btn{ data: { button: 'center' } }= t('editor.center')
|
%button.btn{ data: { button: 'center' } }= t('editor.center')
|
||||||
%button.btn{ data: { button: 'right' } }= t('editor.right')
|
%button.btn{ data: { button: 'right' } }= t('editor.right')
|
||||||
%button.btn{ data: { button: 'img' } }= t('editor.img')
|
%button.btn{ data: { button: 'multimedia' } }= t('editor.multimedia')
|
||||||
%button.btn{ data: { button: 'video' } }= t('editor.video')
|
|
||||||
%button.btn{ data: { button: 'audio' } }= t('editor.audio')
|
|
||||||
%button.btn{ data: { button: 'pdf' } }= t('editor.pdf')
|
|
||||||
|
|
||||||
-#
|
-#
|
||||||
HAML cringe
|
HAML cringe
|
||||||
|
@ -47,31 +44,16 @@
|
||||||
%label{ for: 'mark-color' }= t('editor.color')
|
%label{ for: 'mark-color' }= t('editor.color')
|
||||||
%input.form-control{ type: 'color', data: { prop: 'mark-color' } }/
|
%input.form-control{ type: 'color', data: { prop: 'mark-color' } }/
|
||||||
|
|
||||||
%div{ data: { editor: { auxiliary: 'img' } } }
|
%div{ data: { editor: { auxiliary: 'multimedia' } } }
|
||||||
.row
|
.row
|
||||||
.col-12.col-lg.form-group.d-flex.align-items-end
|
.col-12.col-lg.form-group.d-flex.align-items-end
|
||||||
.custom-file
|
.custom-file
|
||||||
%input.custom-file-input{ type: 'file', data: { prop: 'img-file' }, accept: 'image/*' }/
|
%input.custom-file-input{ type: 'file', data: { prop: 'multimedia-file' }, }/
|
||||||
%label.custom-file-label{ for: 'img-file' }= t('editor.file.img')
|
%label.custom-file-label{ for: 'multimedia-file' }= t('editor.file.multimedia')
|
||||||
|
%button.btn{ type: 'button', data: { prop: 'multimedia-file-upload' }, }= t('editor.file.multimedia-upload')
|
||||||
.col-12.col-lg.form-group
|
.col-12.col-lg.form-group
|
||||||
%label{ for: 'img-alt' }= t('editor.description')
|
%label{ for: 'multimedia-alt' }= t('editor.description')
|
||||||
%input.form-control{ type: 'text', data: { prop: 'img-alt' } }/
|
%input.form-control{ type: 'text', data: { prop: 'multimedia-alt' } }/
|
||||||
|
|
||||||
-# https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
|
|
||||||
.form-group{ data: { editor: { auxiliary: 'audio' } } }
|
|
||||||
.custom-file
|
|
||||||
%input.custom-file-input{ type: 'file', data: { prop: 'audio-file' }, accept: 'audio/*' }/
|
|
||||||
%label.custom-file-label{ for: 'audio-file' }= t('editor.file.audio')
|
|
||||||
|
|
||||||
.form-group{ data: { editor: { auxiliary: 'video' } } }
|
|
||||||
.custom-file
|
|
||||||
%input.custom-file-input{ type: 'file', data: { prop: 'video-file' }, accept: 'video/*' }/
|
|
||||||
%label.custom-file-label{ for: 'video-file' }= t('editor.file.video')
|
|
||||||
|
|
||||||
.form-group{ data: { editor: { auxiliary: 'pdf' } } }
|
|
||||||
.custom-file
|
|
||||||
%input.custom-file-input{ type: 'file', data: { prop: 'pdf-file' }, accept: 'application/pdf' }/
|
|
||||||
%label.custom-file-label{ for: 'pdf-file' }= t('editor.file.pdf')
|
|
||||||
|
|
||||||
.form-group{ data: { editor: { auxiliary: 'a' } } }
|
.form-group{ data: { editor: { auxiliary: 'a' } } }
|
||||||
%label{ for: 'a-href' }= t('editor.url')
|
%label{ for: 'a-href' }= t('editor.url')
|
||||||
|
|
Loading…
Reference in a new issue