DropTarget: Correctly display background image animation in Safari (#5053)
Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com>
This commit is contained in:
parent
66ec9f5732
commit
f2def08a13
@ -136,56 +136,71 @@
|
|||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drop-target {
|
.dropTarget {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
&::before,
|
.dropOutlineContainer {
|
||||||
&::after {
|
pointer-events: none;
|
||||||
content: "";
|
overflow: visible !important;
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
border-radius: var(--border-radius-default);
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
opacity: 0;
|
position: absolute;
|
||||||
transition: 250ms opacity;
|
top: 0.5rem;
|
||||||
z-index: 1;
|
left: 0.5rem;
|
||||||
}
|
width: calc(100% - 1rem);
|
||||||
|
height: calc(100% - 1rem);
|
||||||
|
|
||||||
&::before {
|
background-color: var(--color-background);
|
||||||
background-image: var(--drag-target-border-hovered);
|
|
||||||
background-color: var(--color-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
opacity: 0;
|
||||||
content: attr(data-attach-description);
|
transition: opacity 0.2s;
|
||||||
display: flex;
|
z-index: 1;
|
||||||
justify-content: center;
|
}
|
||||||
align-items: center;
|
|
||||||
color: var(--color-primary);
|
.dropOutline {
|
||||||
}
|
fill: none;
|
||||||
|
|
||||||
|
stroke: var(--color-primary);
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke-dasharray: 11, 8;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
|
||||||
|
animation: outline 0.5s linear infinite;
|
||||||
|
animation-play-state: paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hovered {
|
.hovered {
|
||||||
.drop-target::before {
|
.dropOutline {
|
||||||
opacity: 0.95;
|
animation-play-state: running;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drop-target::after {
|
.dropOutlineContainer {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.caption-wrapper,
|
.dropTarget {
|
||||||
.attachments,
|
&::after {
|
||||||
:global(.input-scroller) {
|
content: attr(data-attach-description);
|
||||||
pointer-events: none;
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
color: var(--color-primary);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.caption-wrapper,
|
||||||
|
.attachments,
|
||||||
|
:global(.input-scroller),
|
||||||
|
:global(.custom-scroll),
|
||||||
|
:global(.custom-scroll-x) {
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,3 +227,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes outline {
|
||||||
|
from {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: -19;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -33,11 +33,13 @@ import { getHtmlTextLength } from './helpers/getHtmlTextLength';
|
|||||||
import useAppLayout from '../../../hooks/useAppLayout';
|
import useAppLayout from '../../../hooks/useAppLayout';
|
||||||
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
|
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
|
||||||
import useDerivedState from '../../../hooks/useDerivedState';
|
import useDerivedState from '../../../hooks/useDerivedState';
|
||||||
|
import useEffectOnce from '../../../hooks/useEffectOnce';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useGetSelectionRange from '../../../hooks/useGetSelectionRange';
|
import useGetSelectionRange from '../../../hooks/useGetSelectionRange';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated';
|
import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated';
|
||||||
|
import useResizeObserver from '../../../hooks/useResizeObserver';
|
||||||
import useScrolledState from '../../../hooks/useScrolledState';
|
import useScrolledState from '../../../hooks/useScrolledState';
|
||||||
import useCustomEmojiTooltip from './hooks/useCustomEmojiTooltip';
|
import useCustomEmojiTooltip from './hooks/useCustomEmojiTooltip';
|
||||||
import useEmojiTooltip from './hooks/useEmojiTooltip';
|
import useEmojiTooltip from './hooks/useEmojiTooltip';
|
||||||
@ -139,6 +141,10 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
|
|||||||
onRemoveSymbol,
|
onRemoveSymbol,
|
||||||
onEmojiSelect,
|
onEmojiSelect,
|
||||||
}) => {
|
}) => {
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const svgRef = useRef<SVGSVGElement>(null);
|
||||||
const { addRecentCustomEmoji, addRecentEmoji, updateAttachmentSettings } = getActions();
|
const { addRecentCustomEmoji, addRecentEmoji, updateAttachmentSettings } = getActions();
|
||||||
|
|
||||||
const lang = useOldLang();
|
const lang = useOldLang();
|
||||||
@ -388,6 +394,23 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleResize = useLastCallback(() => {
|
||||||
|
const svg = svgRef.current;
|
||||||
|
if (!svg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get inner width, without padding
|
||||||
|
const { width, height } = svg.getBoundingClientRect();
|
||||||
|
svg.viewBox.baseVal.width = width;
|
||||||
|
svg.viewBox.baseVal.height = height;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Can't listen for SVG resize
|
||||||
|
useResizeObserver(ref, handleResize);
|
||||||
|
|
||||||
|
useEffectOnce(handleResize);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mainButton = mainButtonRef.current;
|
const mainButton = mainButtonRef.current;
|
||||||
const input = document.getElementById(ATTACHMENT_MODAL_INPUT_ID);
|
const input = document.getElementById(ATTACHMENT_MODAL_INPUT_ID);
|
||||||
@ -575,6 +598,9 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
|
|||||||
data-attach-description={lang('Preview.Dragging.AddItems', 10)}
|
data-attach-description={lang('Preview.Dragging.AddItems', 10)}
|
||||||
data-dropzone
|
data-dropzone
|
||||||
>
|
>
|
||||||
|
<svg className={styles.dropOutlineContainer}>
|
||||||
|
<rect className={styles.dropOutline} x="0" y="0" width="100%" height="100%" rx="8" />
|
||||||
|
</svg>
|
||||||
<div
|
<div
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
styles.attachments,
|
styles.attachments,
|
||||||
|
|||||||
@ -8,6 +8,8 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-bottom: 0.3125rem;
|
margin-bottom: 0.3125rem;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
color: #a4acb3;
|
color: #a4acb3;
|
||||||
box-shadow: 0 1px 2px var(--color-default-shadow);
|
box-shadow: 0 1px 2px var(--color-default-shadow);
|
||||||
@ -16,9 +18,15 @@
|
|||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hovered .target-content {
|
&.hovered {
|
||||||
color: var(--color-primary);
|
.target-content {
|
||||||
background-image: var(--drag-target-border-hovered);
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.target-outline {
|
||||||
|
stroke: var(--color-primary);
|
||||||
|
animation-play-state: running;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& + & {
|
& + & {
|
||||||
@ -29,16 +37,41 @@
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.target-outline-container {
|
||||||
|
pointer-events: none;
|
||||||
|
overflow: visible;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.target-outline {
|
||||||
|
fill: none;
|
||||||
|
|
||||||
|
stroke: var(--color-placeholders);
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke-dasharray: 11, 8;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
|
||||||
|
transition: 0.2s stroke;
|
||||||
|
animation: outline 0.5s linear infinite;
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
.target-content {
|
.target-content {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background-image: var(--drag-target-border);
|
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
flex: 1 1 100%;
|
flex: 1 1 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: 0.2s color, 0.2s background-image;
|
transition: 0.2s color;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@ -78,4 +111,13 @@
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes outline {
|
||||||
|
from {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: -19;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
import type { FC } from '../../../lib/teact/teact';
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
import React, { memo } from '../../../lib/teact/teact';
|
import React, { memo, useRef } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
|
||||||
|
import useEffectOnce from '../../../hooks/useEffectOnce';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
import useResizeObserver from '../../../hooks/useResizeObserver';
|
||||||
|
|
||||||
import './DropTarget.scss';
|
import './DropTarget.scss';
|
||||||
|
|
||||||
@ -14,9 +17,14 @@ export type OwnProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DropTarget: FC<OwnProps> = ({ isQuick, isGeneric, onFileSelect }) => {
|
const DropTarget: FC<OwnProps> = ({ isQuick, isGeneric, onFileSelect }) => {
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const svgRef = useRef<SVGSVGElement>(null);
|
||||||
|
|
||||||
const [isHovered, markHovered, unmarkHovered] = useFlag();
|
const [isHovered, markHovered, unmarkHovered] = useFlag();
|
||||||
|
|
||||||
const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
|
const handleDragLeave = useLastCallback((e: React.DragEvent<HTMLDivElement>) => {
|
||||||
const { relatedTarget: toTarget } = e;
|
const { relatedTarget: toTarget } = e;
|
||||||
|
|
||||||
if (toTarget) {
|
if (toTarget) {
|
||||||
@ -24,7 +32,24 @@ const DropTarget: FC<OwnProps> = ({ isQuick, isGeneric, onFileSelect }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unmarkHovered();
|
unmarkHovered();
|
||||||
};
|
});
|
||||||
|
|
||||||
|
const handleResize = useLastCallback(() => {
|
||||||
|
const svg = svgRef.current;
|
||||||
|
if (!svg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get inner width, without padding
|
||||||
|
const { width, height } = svg.getBoundingClientRect();
|
||||||
|
svg.viewBox.baseVal.width = width;
|
||||||
|
svg.viewBox.baseVal.height = height;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Can't listen for SVG resize
|
||||||
|
useResizeObserver(ref, handleResize);
|
||||||
|
|
||||||
|
useEffectOnce(handleResize);
|
||||||
|
|
||||||
const className = buildClassName(
|
const className = buildClassName(
|
||||||
'DropTarget',
|
'DropTarget',
|
||||||
@ -34,11 +59,15 @@ const DropTarget: FC<OwnProps> = ({ isQuick, isGeneric, onFileSelect }) => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={className}
|
className={className}
|
||||||
|
ref={ref}
|
||||||
onDrop={onFileSelect}
|
onDrop={onFileSelect}
|
||||||
onDragEnter={markHovered}
|
onDragEnter={markHovered}
|
||||||
onDragLeave={handleDragLeave}
|
onDragLeave={handleDragLeave}
|
||||||
data-dropzone
|
data-dropzone
|
||||||
>
|
>
|
||||||
|
<svg className="target-outline-container">
|
||||||
|
<rect className="target-outline" x="0" y="0" width="100%" height="100%" rx="8" />
|
||||||
|
</svg>
|
||||||
<div className="target-content">
|
<div className="target-content">
|
||||||
<div className={`icon icon-${isQuick ? 'photo' : 'document'}`} />
|
<div className={`icon icon-${isQuick ? 'photo' : 'document'}`} />
|
||||||
<div className="title">Drop files here to send them</div>
|
<div className="title">Drop files here to send them</div>
|
||||||
|
|||||||
@ -286,9 +286,6 @@ $color-message-story-mention-to: #74bcff;
|
|||||||
--spinner-gray-data: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEwLjggMjIuNEM2IDIxLjkgMi4xIDE4IDEuNiAxMy4yLjkgNy4xIDUuNCAxLjkgMTEuMyAxLjVjLjQgMCAuNy0uMy43LS43IDAtLjQtLjQtLjgtLjgtLjhDNC44LjQtLjIgNS45IDAgMTIuNS4yIDE4LjYgNS40IDIzLjggMTEuNSAyNGM2LjYuMiAxMi00LjggMTIuNC0xMS4yIDAtLjQtLjMtLjgtLjgtLjgtLjQgMC0uNy4zLS43LjctLjMgNS45LTUuNSAxMC40LTExLjYgOS43eiIgZmlsbD0iIzcwNzU3OSIvPjwvc3ZnPg==);
|
--spinner-gray-data: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEwLjggMjIuNEM2IDIxLjkgMi4xIDE4IDEuNiAxMy4yLjkgNy4xIDUuNCAxLjkgMTEuMyAxLjVjLjQgMCAuNy0uMy43LS43IDAtLjQtLjQtLjgtLjgtLjhDNC44LjQtLjIgNS45IDAgMTIuNS4yIDE4LjYgNS40IDIzLjggMTEuNSAyNGM2LjYuMiAxMi00LjggMTIuNC0xMS4yIDAtLjQtLjMtLjgtLjgtLjgtLjQgMC0uNy4zLS43LjctLjMgNS45LTUuNSAxMC40LTExLjYgOS43eiIgZmlsbD0iIzcwNzU3OSIvPjwvc3ZnPg==);
|
||||||
--spinner-yellow-data: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEwLjggMjIuNEM2IDIxLjkgMi4xIDE4IDEuNiAxMy4yLjkgNy4xIDUuNCAxLjkgMTEuMyAxLjVjLjQgMCAuNy0uMy43LS43IDAtLjQtLjQtLjgtLjgtLjhDNC44LjQtLjIgNS45IDAgMTIuNS4yIDE4LjYgNS40IDIzLjggMTEuNSAyNGM2LjYuMiAxMi00LjggMTIuNC0xMS4yIDAtLjQtLjMtLjgtLjgtLjgtLjQgMC0uNy4zLS43LjctLjMgNS45LTUuNSAxMC40LTExLjYgOS43eiIgZmlsbD0iI0ZERDc2NCIvPjwvc3ZnPg==);
|
--spinner-yellow-data: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEwLjggMjIuNEM2IDIxLjkgMi4xIDE4IDEuNiAxMy4yLjkgNy4xIDUuNCAxLjkgMTEuMyAxLjVjLjQgMCAuNy0uMy43LS43IDAtLjQtLjQtLjgtLjgtLjhDNC44LjQtLjIgNS45IDAgMTIuNS4yIDE4LjYgNS40IDIzLjggMTEuNSAyNGM2LjYuMiAxMi00LjggMTIuNC0xMS4yIDAtLjQtLjMtLjgtLjgtLjgtLjQgMC0uNy4zLS43LjctLjMgNS45LTUuNSAxMC40LTExLjYgOS43eiIgZmlsbD0iI0ZERDc2NCIvPjwvc3ZnPg==);
|
||||||
|
|
||||||
--drag-target-border: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='%23DDDFE0' stroke-width='4' stroke-dasharray='9.1%2c 10.5' stroke-dashoffset='3' stroke-linecap='round'/%3e%3c/svg%3e");
|
|
||||||
--drag-target-border-hovered: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='%2363A2E3' stroke-width='4' stroke-dasharray='9.1%2c 10.5' stroke-dashoffset='3' stroke-linecap='round'/%3e%3c/svg%3e");
|
|
||||||
|
|
||||||
--premium-gradient: linear-gradient(84.4deg, #6C93FF -4.85%, #976FFF 51.72%, #DF69D1 110.7%);
|
--premium-gradient: linear-gradient(84.4deg, #6C93FF -4.85%, #976FFF 51.72%, #DF69D1 110.7%);
|
||||||
|
|
||||||
--layer-blackout-opacity: 0.1;
|
--layer-blackout-opacity: 0.1;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user