mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-24 08:48:47 +01:00
Modals for media attachments in composer
Dedicated editor experience per media attachment
This commit is contained in:
parent
228c74655a
commit
3ca696dd3d
3 changed files with 161 additions and 29 deletions
|
@ -655,6 +655,9 @@ button.carousel-dot[disabled].active {
|
|||
padding-right: max(16px, env(safe-area-inset-right));
|
||||
user-select: none;
|
||||
}
|
||||
.sheet header :is(h1, h2, h3) {
|
||||
margin: 0;
|
||||
}
|
||||
.sheet main {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
|
|
|
@ -255,16 +255,34 @@
|
|||
align-items: stretch;
|
||||
}
|
||||
#compose-container .media-preview {
|
||||
flex-shrink: 1;
|
||||
flex-shrink: 0;
|
||||
border: 1px solid var(--outline-color);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
/* checkerboard background */
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
var(--img-bg-color) 25%,
|
||||
transparent 25%
|
||||
),
|
||||
linear-gradient(-45deg, var(--img-bg-color) 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, var(--img-bg-color) 75%),
|
||||
linear-gradient(-45deg, transparent 75%, var(--img-bg-color) 75%);
|
||||
background-size: 10px 10px;
|
||||
background-position: 0 0, 0 5px, 5px -5px, -5px 0px;
|
||||
}
|
||||
#compose-container .media-preview > * {
|
||||
min-width: 80px;
|
||||
width: 80px !important;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
object-fit: contain;
|
||||
background-color: var(--img-bg-color);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--outline-color);
|
||||
vertical-align: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
#compose-container .media-preview:hover {
|
||||
box-shadow: 0 0 0 2px var(--link-light-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
#compose-container .media-attachment textarea {
|
||||
height: 80px;
|
||||
|
@ -389,3 +407,39 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#media-sheet main {
|
||||
padding-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
#media-sheet textarea {
|
||||
width: 100%;
|
||||
height: 10em;
|
||||
margin-top: 8px;
|
||||
}
|
||||
#media-sheet .media-preview {
|
||||
border: 2px solid var(--outline-color);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 16px var(--img-bg-color);
|
||||
/* checkerboard background */
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
var(--img-bg-color) 25%,
|
||||
transparent 25%
|
||||
),
|
||||
linear-gradient(-45deg, var(--img-bg-color) 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, var(--img-bg-color) 75%),
|
||||
linear-gradient(-45deg, transparent 75%, var(--img-bg-color) 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
|
||||
}
|
||||
#media-sheet .media-preview > * {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 50vh;
|
||||
object-fit: contain;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@ import emojifyText from '../utils/emojify-text';
|
|||
import openCompose from '../utils/open-compose';
|
||||
import states from '../utils/states';
|
||||
import store from '../utils/store';
|
||||
import useDebouncedCallback from '../utils/useDebouncedCallback';
|
||||
import visibilityIconsMap from '../utils/visibility-icons-map';
|
||||
|
||||
import Avatar from './avatar';
|
||||
import Icon from './icon';
|
||||
import Loader from './loader';
|
||||
import Modal from './modal';
|
||||
import Status from './status';
|
||||
|
||||
const supportedLanguagesMap = supportedLanguages.reduce((acc, l) => {
|
||||
|
@ -1090,26 +1092,41 @@ function MediaAttachment({
|
|||
onDescriptionChange = () => {},
|
||||
onRemove = () => {},
|
||||
}) {
|
||||
const { url, type, id, description } = attachment;
|
||||
const { url, type, id } = attachment;
|
||||
console.log({ attachment });
|
||||
const [description, setDescription] = useState(attachment.description);
|
||||
const suffixType = type.split('/')[0];
|
||||
return (
|
||||
<div class="media-attachment">
|
||||
<div class="media-preview">
|
||||
{suffixType === 'image' ? (
|
||||
<img src={url} alt="" />
|
||||
) : suffixType === 'video' || suffixType === 'gifv' ? (
|
||||
<video src={url} playsinline muted />
|
||||
) : suffixType === 'audio' ? (
|
||||
<audio src={url} controls />
|
||||
) : null}
|
||||
</div>
|
||||
const debouncedOnDescriptionChange = useDebouncedCallback(
|
||||
onDescriptionChange,
|
||||
500,
|
||||
);
|
||||
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const textareaRef = useRef(null);
|
||||
useEffect(() => {
|
||||
let timer;
|
||||
if (showModal && textareaRef.current) {
|
||||
timer = setTimeout(() => {
|
||||
textareaRef.current.focus();
|
||||
}, 100);
|
||||
}
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [showModal]);
|
||||
|
||||
const descTextarea = (
|
||||
<>
|
||||
{!!id ? (
|
||||
<div class="media-desc">
|
||||
<span class="tag">Uploaded</span>
|
||||
<p title={description}>{description || <i>No description</i>}</p>
|
||||
<p title={description}>
|
||||
{attachment.description || <i>No description</i>}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
value={description || ''}
|
||||
placeholder={
|
||||
{
|
||||
|
@ -1128,21 +1145,79 @@ function MediaAttachment({
|
|||
// TODO: Un-hard-code this maxlength, ref: https://github.com/mastodon/mastodon/blob/b59fb28e90bc21d6fd1a6bafd13cfbd81ab5be54/app/models/media_attachment.rb#L39
|
||||
onInput={(e) => {
|
||||
const { value } = e.target;
|
||||
onDescriptionChange(value);
|
||||
setDescription(value);
|
||||
debouncedOnDescriptionChange(value);
|
||||
}}
|
||||
></textarea>
|
||||
)}
|
||||
<div class="media-aside">
|
||||
<button
|
||||
type="button"
|
||||
class="plain close-button"
|
||||
disabled={disabled}
|
||||
onClick={onRemove}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="media-attachment">
|
||||
<div
|
||||
class="media-preview"
|
||||
onClick={() => {
|
||||
setShowModal(true);
|
||||
}}
|
||||
>
|
||||
<Icon icon="x" />
|
||||
</button>
|
||||
{suffixType === 'image' ? (
|
||||
<img src={url} alt="" />
|
||||
) : suffixType === 'video' || suffixType === 'gifv' ? (
|
||||
<video src={url} playsinline muted />
|
||||
) : suffixType === 'audio' ? (
|
||||
<audio src={url} controls />
|
||||
) : null}
|
||||
</div>
|
||||
{descTextarea}
|
||||
<div class="media-aside">
|
||||
<button
|
||||
type="button"
|
||||
class="plain close-button"
|
||||
disabled={disabled}
|
||||
onClick={onRemove}
|
||||
>
|
||||
<Icon icon="x" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{showModal && (
|
||||
<Modal
|
||||
onClick={(e) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
setShowModal(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div id="media-sheet" class="sheet">
|
||||
<header>
|
||||
<h2>
|
||||
{
|
||||
{
|
||||
image: 'Edit image description',
|
||||
video: 'Edit video description',
|
||||
audio: 'Edit audio description',
|
||||
}[suffixType]
|
||||
}
|
||||
</h2>
|
||||
</header>
|
||||
<main tabIndex="-1">
|
||||
<div class="media-preview">
|
||||
{suffixType === 'image' ? (
|
||||
<img src={url} alt="" />
|
||||
) : suffixType === 'video' || suffixType === 'gifv' ? (
|
||||
<video src={url} playsinline controls />
|
||||
) : suffixType === 'audio' ? (
|
||||
<audio src={url} controls />
|
||||
) : null}
|
||||
</div>
|
||||
{descTextarea}
|
||||
</main>
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue