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:
Nolan Lawson 2019-08-24 19:28:12 -07:00 committed by GitHub
parent 7b32c71c93
commit 7f9195c2af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 298 additions and 119 deletions

View file

@ -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: {

View file

@ -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)

View 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>

View 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>

View file

@ -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()

View file

@ -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
})
}

View file

@ -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
})
}