feat: combine alt/focal point into single "media edit" dialog (#1430)
* feat: combine alt/focal point into single "media edit" dialog * resize text automatically
This commit is contained in:
parent
7b32c71c93
commit
7f9195c2af
|
@ -7,12 +7,11 @@
|
|||
aria-hidden="true"
|
||||
/>
|
||||
<div class="compose-media-buttons">
|
||||
<button class="compose-media-button compose-media-focal-button {focalHidden ? 'compose-media-hidden' : ''}"
|
||||
aria-hidden={focalHidden}
|
||||
aria-label="Change preview"
|
||||
title="Change preview"
|
||||
on:click="onSetFocalPoint()" >
|
||||
<SvgIcon className="compose-media-button-svg" href="#fa-crosshairs" />
|
||||
<button class="compose-media-button compose-media-focal-button"
|
||||
aria-label="Edit"
|
||||
title="Edit"
|
||||
on:click="onEdit()" >
|
||||
<SvgIcon className="compose-media-button-svg" href="#fa-pencil" />
|
||||
</button>
|
||||
<button class="compose-media-button compose-media-delete-button"
|
||||
aria-label="Delete"
|
||||
|
@ -27,6 +26,7 @@
|
|||
placeholder="Description"
|
||||
ref:textarea
|
||||
bind:value=rawText
|
||||
maxlength="420"
|
||||
></textarea>
|
||||
<label for="compose-media-input-{uuid}" class="sr-only">
|
||||
Describe for the visually impaired (image, video) or auditorily impaired (audio, video)
|
||||
|
@ -98,11 +98,6 @@
|
|||
height: 18px;
|
||||
}
|
||||
|
||||
.compose-media-hidden {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.audio-preview {
|
||||
background: var(--audio-bg);
|
||||
}
|
||||
|
@ -130,10 +125,11 @@
|
|||
import { ONE_TRANSPARENT_PIXEL } from '../../_static/media'
|
||||
import { get } from '../../_utils/lodash-lite'
|
||||
import { coordsToPercent } from '../../_utils/coordsToPercent'
|
||||
import { importMediaFocalPointDialog } from '../dialog/asyncDialogs'
|
||||
import { importShowMediaEditDialog } from '../dialog/asyncDialogs'
|
||||
import { throttleTimer } from '../../_utils/throttleTimer'
|
||||
|
||||
const updateMediaInStore = throttleTimer(scheduleIdleTask)
|
||||
const resizeTextarea = process.browser && throttleTimer(requestAnimationFrame)
|
||||
|
||||
export default {
|
||||
oncreate () {
|
||||
|
@ -165,8 +161,7 @@
|
|||
return 'center center'
|
||||
}
|
||||
return `${coordsToPercent(focusX)}% ${100 - coordsToPercent(focusY)}%`
|
||||
},
|
||||
focalHidden: ({ type }) => type !== 'image'
|
||||
}
|
||||
},
|
||||
store: () => store,
|
||||
methods: {
|
||||
|
@ -178,6 +173,7 @@
|
|||
const text = get(media, [index, 'description'], '')
|
||||
if (rawText !== text) {
|
||||
this.set({ rawText: text })
|
||||
resizeTextarea(() => autosize.update(this.refs.textarea))
|
||||
}
|
||||
const focusX = get(media, [index, 'focusX'], 0)
|
||||
const focusY = get(media, [index, 'focusY'], 0)
|
||||
|
@ -206,10 +202,10 @@
|
|||
const { realm, index } = this.get()
|
||||
deleteMedia(realm, index)
|
||||
},
|
||||
async onSetFocalPoint () {
|
||||
const { realm, index } = this.get()
|
||||
const showMediaFocalPointDialog = await importMediaFocalPointDialog()
|
||||
showMediaFocalPointDialog(realm, index)
|
||||
async onEdit () {
|
||||
const { realm, index, type } = this.get()
|
||||
const showMediaEditDialog = await importShowMediaEditDialog()
|
||||
showMediaEditDialog(realm, index, type)
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
|
@ -44,6 +44,6 @@ export const importShowReportDialog = () => import(
|
|||
/* webpackChunkName: 'showReportDialog' */ './creators/showReportDialog'
|
||||
).then(getDefault)
|
||||
|
||||
export const importMediaFocalPointDialog = () => import(
|
||||
/* webpackChunkName: 'mediaFocalPointDialog' */ './creators/mediaFocalPointDialog'
|
||||
export const importShowMediaEditDialog = () => import(
|
||||
/* webpackChunkName: 'showMediaEditDialog' */ './creators/showMediaEditDialog'
|
||||
).then(getDefault)
|
||||
|
|
104
src/routes/_components/dialog/components/MediaAltEditor.html
Normal file
104
src/routes/_components/dialog/components/MediaAltEditor.html
Normal file
|
@ -0,0 +1,104 @@
|
|||
<div class="media-alt-editor">
|
||||
<textarea
|
||||
id="the-media-alt-input-{realm}-{index}"
|
||||
class="media-alt-input"
|
||||
placeholder="Description"
|
||||
ref:textarea
|
||||
bind:value=rawText
|
||||
maxlength="420"
|
||||
></textarea>
|
||||
<label for="the-media-alt-input-{realm}-{index}" class="sr-only">
|
||||
Describe for the visually impaired
|
||||
</label>
|
||||
</div>
|
||||
<style>
|
||||
.media-alt-editor {
|
||||
display: flex;
|
||||
}
|
||||
.media-alt-input {
|
||||
padding: 5px;
|
||||
border: 1px solid var(--input-border);
|
||||
min-height: 75px;
|
||||
resize: none;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
/* Text must be at least 16px or else iOS Safari zooms in */
|
||||
font-size: 1.2em;
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
@media (max-height: 767px) {
|
||||
.media-alt-input {
|
||||
max-height: 40vh;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { requestPostAnimationFrame } from '../../../_utils/requestPostAnimationFrame'
|
||||
import { mark, stop } from '../../../_utils/marks'
|
||||
import { autosize } from '../../../_thirdparty/autosize/autosize'
|
||||
import { observe } from 'svelte-extras'
|
||||
import { throttleTimer } from '../../../_utils/throttleTimer'
|
||||
import { get } from '../../../_utils/lodash-lite'
|
||||
import { store } from '../../../_store/store'
|
||||
|
||||
const updateRawTextInStore = throttleTimer(requestPostAnimationFrame)
|
||||
|
||||
export default {
|
||||
oncreate () {
|
||||
this.setupAutosize()
|
||||
this.setupSyncFromStore()
|
||||
this.setupSyncToStore()
|
||||
},
|
||||
ondestroy () {
|
||||
this.teardownAutosize()
|
||||
},
|
||||
store: () => store,
|
||||
data: () => ({
|
||||
rawText: ''
|
||||
}),
|
||||
methods: {
|
||||
observe,
|
||||
setupSyncFromStore () {
|
||||
this.observe('media', media => {
|
||||
media = media || []
|
||||
const { index, rawText } = this.get()
|
||||
const text = get(media, [index, 'description'], '')
|
||||
if (rawText !== text) {
|
||||
this.set({ rawText: text })
|
||||
}
|
||||
})
|
||||
},
|
||||
setupSyncToStore () {
|
||||
this.observe('rawText', rawText => {
|
||||
updateRawTextInStore(() => {
|
||||
const { realm, index, media } = this.get()
|
||||
if (media[index].description !== rawText) {
|
||||
media[index].description = rawText
|
||||
this.store.setComposeData(realm, { media })
|
||||
this.store.save()
|
||||
}
|
||||
this.fire('resize')
|
||||
})
|
||||
}, { init: false })
|
||||
},
|
||||
setupAutosize () {
|
||||
const textarea = this.refs.textarea
|
||||
requestPostAnimationFrame(() => {
|
||||
mark('autosize()')
|
||||
autosize(textarea)
|
||||
stop('autosize()')
|
||||
})
|
||||
},
|
||||
teardownAutosize () {
|
||||
mark('autosize.destroy()')
|
||||
autosize.destroy(this.refs.textarea)
|
||||
stop('autosize.destroy()')
|
||||
},
|
||||
measure () {
|
||||
autosize.update(this.refs.textarea)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
100
src/routes/_components/dialog/components/MediaEditDialog.html
Normal file
100
src/routes/_components/dialog/components/MediaEditDialog.html
Normal file
|
@ -0,0 +1,100 @@
|
|||
<ModalDialog
|
||||
{id}
|
||||
{label}
|
||||
{title}
|
||||
background="var(--main-bg)"
|
||||
className="media-edit-dialog"
|
||||
on:show="measure()"
|
||||
>
|
||||
<div class="media-edit-dialog-container">
|
||||
<div class="media-edit-header-and-item">
|
||||
<h2>Description</h2>
|
||||
<MediaAltEditor
|
||||
{realm}
|
||||
{index}
|
||||
{media}
|
||||
on:resize="measure()"
|
||||
ref:altEditor
|
||||
/>
|
||||
</div>
|
||||
{#if type === 'image'}
|
||||
<div class="media-edit-header-and-item">
|
||||
<h2>Preview (focal point)</h2>
|
||||
<MediaFocalPointEditor
|
||||
{realm}
|
||||
{index}
|
||||
{media}
|
||||
ref:focalPointEditor
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</ModalDialog>
|
||||
<style>
|
||||
:global(.media-edit-dialog) {
|
||||
max-width: calc(100%);
|
||||
}
|
||||
|
||||
.media-edit-dialog-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-height: calc(100% - 44px); /* 44px X button height */
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.media-edit-header-and-item {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.media-edit-header-and-item h2 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.media-edit-dialog-container {
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.media-edit-dialog-container {
|
||||
max-height: calc(100% - 25px); /* 25px X button height */
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
import ModalDialog from './ModalDialog.html'
|
||||
import { show } from '../helpers/showDialog'
|
||||
import { close } from '../helpers/closeDialog'
|
||||
import { oncreate } from '../helpers/onCreateDialog'
|
||||
import MediaFocalPointEditor from '../components/MediaFocalPointEditor.html'
|
||||
import MediaAltEditor from '../components/MediaAltEditor.html'
|
||||
import { store } from '../../../_store/store'
|
||||
import { get } from '../../../_utils/lodash-lite'
|
||||
|
||||
export default {
|
||||
oncreate,
|
||||
components: {
|
||||
ModalDialog,
|
||||
MediaFocalPointEditor,
|
||||
MediaAltEditor
|
||||
},
|
||||
methods: {
|
||||
show,
|
||||
close,
|
||||
measure () {
|
||||
this.refs.altEditor.measure()
|
||||
if (this.refs.focalPointEditor) {
|
||||
this.refs.focalPointEditor.measure()
|
||||
}
|
||||
}
|
||||
},
|
||||
store: () => store,
|
||||
computed: {
|
||||
media: ({ $currentInstance, $composeData, realm }) => (
|
||||
get($composeData, [$currentInstance, realm, 'media'])
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,86 +1,71 @@
|
|||
<ModalDialog
|
||||
{id}
|
||||
{label}
|
||||
{title}
|
||||
background="var(--main-bg)"
|
||||
className="media-focal-point-dialog"
|
||||
on:show="measure()"
|
||||
<form class="media-focal-point-container"
|
||||
aria-label="Enter the focal point (X, Y) for this media"
|
||||
on:resize="measure()"
|
||||
>
|
||||
<form class="media-focal-point-container"
|
||||
aria-label="Enter the focal point (X, Y) for this media"
|
||||
on:resize="measure()"
|
||||
>
|
||||
<div class="media-focal-point-image-container" ref:container>
|
||||
<img
|
||||
{intrinsicsize}
|
||||
class="media-focal-point-image"
|
||||
src={previewSrc}
|
||||
alt={shortName}
|
||||
on:load="onImageLoad()"
|
||||
/>
|
||||
<div class="media-focal-point-backdrop"></div>
|
||||
<div class="media-draggable-area"
|
||||
style={draggableAreaStyle}
|
||||
<div class="media-focal-point-image-container" ref:container>
|
||||
<img
|
||||
{intrinsicsize}
|
||||
class="media-focal-point-image"
|
||||
src={previewSrc}
|
||||
alt={shortName}
|
||||
on:load="onImageLoad()"
|
||||
/>
|
||||
<div class="media-focal-point-backdrop"></div>
|
||||
<div class="media-draggable-area"
|
||||
style={draggableAreaStyle}
|
||||
>
|
||||
<!-- 52px == 32px icon width + 10px padding -->
|
||||
<Draggable
|
||||
draggableClass="media-draggable-area-inner"
|
||||
indicatorClass="media-focal-point-indicator {imageLoaded ? '': 'hidden'} {dragging ? 'dragging' : ''}"
|
||||
indicatorWidth={52}
|
||||
indicatorHeight={52}
|
||||
x={indicatorX}
|
||||
y={indicatorY}
|
||||
on:dragStart="onDragStart()"
|
||||
on:dragEnd="onDragEnd()"
|
||||
on:change="onDraggableChange(event)"
|
||||
>
|
||||
<!-- 52px == 32px icon width + 10px padding -->
|
||||
<Draggable
|
||||
draggableClass="media-draggable-area-inner"
|
||||
indicatorClass="media-focal-point-indicator {imageLoaded ? '': 'hidden'} {dragging ? 'dragging' : ''}"
|
||||
indicatorWidth={52}
|
||||
indicatorHeight={52}
|
||||
x={indicatorX}
|
||||
y={indicatorY}
|
||||
on:dragStart="onDragStart()"
|
||||
on:dragEnd="onDragEnd()"
|
||||
on:change="onDraggableChange(event)"
|
||||
>
|
||||
<SvgIcon
|
||||
className="media-focal-point-indicator-svg"
|
||||
href="#fa-crosshairs"
|
||||
/>
|
||||
</Draggable>
|
||||
</div>
|
||||
<SvgIcon
|
||||
className="media-focal-point-indicator-svg"
|
||||
href="#fa-crosshairs"
|
||||
/>
|
||||
</Draggable>
|
||||
</div>
|
||||
<div class="media-focal-point-inputs">
|
||||
<div class="media-focal-point-input-pair">
|
||||
<label for="media-focal-point-x-input-{realm}">
|
||||
X coordinate
|
||||
</label>
|
||||
<input type="number"
|
||||
step="0.01"
|
||||
min="-1"
|
||||
max="1"
|
||||
inputmode="decimal"
|
||||
placeholder="0"
|
||||
id="media-focal-point-x-input-{realm}"
|
||||
bind:value="rawFocusX"
|
||||
/>
|
||||
</div>
|
||||
<div class="media-focal-point-input-pair">
|
||||
<label for="media-focal-point-y-input-{realm}">
|
||||
Y coordinate
|
||||
</label>
|
||||
<input type="number"
|
||||
step="0.01"
|
||||
min="-1"
|
||||
max="1"
|
||||
inputmode="decimal"
|
||||
placeholder="0"
|
||||
id="media-focal-point-y-input-{realm}"
|
||||
bind:value="rawFocusY"
|
||||
</div>
|
||||
<div class="media-focal-point-inputs">
|
||||
<div class="media-focal-point-input-pair">
|
||||
<label for="media-focal-point-x-input-{realm}">
|
||||
X coordinate
|
||||
</label>
|
||||
<input type="number"
|
||||
step="0.01"
|
||||
min="-1"
|
||||
max="1"
|
||||
inputmode="decimal"
|
||||
placeholder="0"
|
||||
id="media-focal-point-x-input-{realm}"
|
||||
bind:value="rawFocusX"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ModalDialog>
|
||||
<div class="media-focal-point-input-pair">
|
||||
<label for="media-focal-point-y-input-{realm}">
|
||||
Y coordinate
|
||||
</label>
|
||||
<input type="number"
|
||||
step="0.01"
|
||||
min="-1"
|
||||
max="1"
|
||||
inputmode="decimal"
|
||||
placeholder="0"
|
||||
id="media-focal-point-y-input-{realm}"
|
||||
bind:value="rawFocusY"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<style>
|
||||
:global(.media-focal-point-dialog) {
|
||||
max-width: calc(100%);
|
||||
}
|
||||
.media-focal-point-container {
|
||||
height: calc(100% - 44px); /* 44px X button height */
|
||||
width: calc(100vw - 40px);
|
||||
padding-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@ -120,7 +105,7 @@
|
|||
|
||||
.media-focal-point-inputs {
|
||||
display: flex;
|
||||
padding: 20px 40px;
|
||||
padding: 10px;
|
||||
justify-content: space-around;
|
||||
width: auto;
|
||||
}
|
||||
|
@ -130,6 +115,10 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.media-focal-point-input-pair:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.media-focal-point-input-pair input {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
@ -180,10 +169,6 @@
|
|||
}
|
||||
</style>
|
||||
<script>
|
||||
import ModalDialog from './ModalDialog.html'
|
||||
import { show } from '../helpers/showDialog'
|
||||
import { close } from '../helpers/closeDialog'
|
||||
import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
|
||||
import { store } from '../../../_store/store'
|
||||
import { get } from '../../../_utils/lodash-lite'
|
||||
import { observe } from 'svelte-extras'
|
||||
|
@ -207,12 +192,10 @@
|
|||
|
||||
export default {
|
||||
oncreate () {
|
||||
onCreateDialog.call(this)
|
||||
this.setupSyncFromStore()
|
||||
this.setupSyncToStore()
|
||||
},
|
||||
components: {
|
||||
ModalDialog,
|
||||
SvgIcon,
|
||||
Draggable
|
||||
},
|
||||
|
@ -226,9 +209,6 @@
|
|||
}),
|
||||
store: () => store,
|
||||
computed: {
|
||||
media: ({ $currentInstance, $composeData, realm }) => (
|
||||
get($composeData, [$currentInstance, realm, 'media'])
|
||||
),
|
||||
mediaItem: ({ media, index }) => get(media, [index]),
|
||||
focusX: ({ mediaItem }) => get(mediaItem, ['focusX'], 0),
|
||||
focusY: ({ mediaItem }) => get(mediaItem, ['focusY'], 0),
|
||||
|
@ -267,8 +247,6 @@
|
|||
},
|
||||
methods: {
|
||||
observe,
|
||||
show,
|
||||
close,
|
||||
setupSyncFromStore () {
|
||||
this.observe('mediaItem', mediaItem => {
|
||||
const { rawFocusX, rawFocusY } = this.get()
|
|
@ -1,11 +0,0 @@
|
|||
import MediaFocalPointDialog from '../components/MediaFocalPointDialog.html'
|
||||
import { showDialog } from './showDialog'
|
||||
|
||||
export default function showMediaFocalPointDialog (realm, index) {
|
||||
return showDialog(MediaFocalPointDialog, {
|
||||
label: 'Change preview dialog',
|
||||
title: 'Change preview (focal point)',
|
||||
realm,
|
||||
index
|
||||
})
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import MediaFocalPointDialog from '../components/MediaEditDialog.html'
|
||||
import { showDialog } from './showDialog'
|
||||
|
||||
export default function showMediaEditDialog (realm, index, type) {
|
||||
return showDialog(MediaFocalPointDialog, {
|
||||
label: 'Edit media',
|
||||
title: 'Edit media',
|
||||
realm,
|
||||
index,
|
||||
type
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue