mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(reply-modal): keep drag interactions sharp (#1086)
This commit is contained in:
@@ -166,15 +166,26 @@ vi.mock('lodash/debounce', () => ({
|
|||||||
|
|
||||||
vi.mock('@react-spring/web', async () => {
|
vi.mock('@react-spring/web', async () => {
|
||||||
const React = await vi.importActual<typeof import('react')>('react');
|
const React = await vi.importActual<typeof import('react')>('react');
|
||||||
|
const normalizeStyle = (style: Record<string, unknown> | undefined) =>
|
||||||
|
style
|
||||||
|
? Object.fromEntries(
|
||||||
|
Object.entries(style).map(([key, value]) => [
|
||||||
|
key,
|
||||||
|
typeof value === 'object' && value !== null && 'get' in value && typeof (value as { get: unknown }).get === 'function'
|
||||||
|
? (value as { get: () => unknown }).get()
|
||||||
|
: value,
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
animated: {
|
animated: {
|
||||||
div: React.forwardRef(({ style, ...props }: any, ref) => React.createElement('div', { ...props, ref, style: { touchAction: style?.touchAction } })),
|
div: React.forwardRef(({ style, ...props }: any, ref) => React.createElement('div', { ...props, ref, style: normalizeStyle(style) })),
|
||||||
},
|
},
|
||||||
useSpring: () => [
|
useSpring: () => [
|
||||||
{
|
{
|
||||||
x: { get: () => 120 },
|
left: { get: () => 120 },
|
||||||
y: { get: () => 80 },
|
top: { get: () => 80 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start: testState.springStartMock,
|
start: testState.springStartMock,
|
||||||
@@ -437,4 +448,15 @@ describe('ReplyModal', () => {
|
|||||||
expect(container.textContent).not.toContain('warning');
|
expect(container.textContent).not.toContain('warning');
|
||||||
expect(container.textContent).not.toContain('Spoiler?');
|
expect(container.textContent).not.toContain('Spoiler?');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('positions the draggable modal with left/top styles instead of a transform layer', async () => {
|
||||||
|
await renderReplyModal('/mu/thread/post-1');
|
||||||
|
|
||||||
|
const modal = container.querySelector<HTMLDivElement>('[class*="container"]');
|
||||||
|
|
||||||
|
expect(modal?.style.left).toBe('120px');
|
||||||
|
expect(modal?.style.top).toBe('80px');
|
||||||
|
expect(modal?.style.transform).toBe('');
|
||||||
|
expect(modal?.style.touchAction).toBe('none');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,9 +8,6 @@
|
|||||||
z-index: 999;
|
z-index: 999;
|
||||||
background-color: var(--challenge-modal-background-color);
|
background-color: var(--challenge-modal-background-color);
|
||||||
border: var(--challenge-modal-border);
|
border: var(--challenge-modal-border);
|
||||||
will-change: transform;
|
|
||||||
backface-visibility: hidden;
|
|
||||||
-webkit-backface-visibility: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
|||||||
@@ -111,13 +111,16 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
const nodeRef = useRef<HTMLDivElement>(null);
|
const nodeRef = useRef<HTMLDivElement>(null);
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const [{ x, y }, api] = useSpring(() => ({
|
const [{ left, top }, api] = useSpring(() => ({
|
||||||
x: window.innerWidth / 2 - 150,
|
left: Math.round(window.innerWidth / 2 - 150),
|
||||||
y: window.innerHeight / 2 - 200,
|
top: Math.round(window.innerHeight / 2 - 200),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const bind = useDrag(
|
const bind = useDrag(
|
||||||
({ active, event, offset: [ox, oy] }) => {
|
({ active, event, offset: [ox, oy] }) => {
|
||||||
|
const nextLeft = Math.round(ox);
|
||||||
|
const nextTop = Math.round(oy);
|
||||||
|
|
||||||
if (active) {
|
if (active) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
document.body.style.userSelect = 'none';
|
document.body.style.userSelect = 'none';
|
||||||
@@ -126,10 +129,10 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
document.body.style.userSelect = '';
|
document.body.style.userSelect = '';
|
||||||
document.body.style.webkitUserSelect = '';
|
document.body.style.webkitUserSelect = '';
|
||||||
}
|
}
|
||||||
api.start({ x: ox, y: oy, immediate: true });
|
api.start({ left: nextLeft, top: nextTop, immediate: true });
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: () => [x.get(), y.get()],
|
from: () => [left.get(), top.get()],
|
||||||
filterTaps: true,
|
filterTaps: true,
|
||||||
bounds: undefined,
|
bounds: undefined,
|
||||||
},
|
},
|
||||||
@@ -138,10 +141,10 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (nodeRef.current && isMobile) {
|
if (nodeRef.current && isMobile) {
|
||||||
const viewportHeight = window.innerHeight;
|
const viewportHeight = window.innerHeight;
|
||||||
const centeredPosition = scrollY + viewportHeight / 2 - 300;
|
const centeredPosition = Math.round(scrollY + viewportHeight / 2 - 300);
|
||||||
api.start({ y: centeredPosition, immediate: true });
|
api.start({ top: centeredPosition, immediate: true });
|
||||||
}
|
}
|
||||||
}, [isMobile, scrollY, api]);
|
}, [api, isMobile, scrollY]);
|
||||||
|
|
||||||
const parentCidRef = useRef<HTMLSpanElement>(null);
|
const parentCidRef = useRef<HTMLSpanElement>(null);
|
||||||
|
|
||||||
@@ -284,8 +287,8 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
className={styles.container}
|
className={styles.container}
|
||||||
ref={nodeRef}
|
ref={nodeRef}
|
||||||
style={{
|
style={{
|
||||||
x,
|
left,
|
||||||
y,
|
top,
|
||||||
touchAction: 'none',
|
touchAction: 'none',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user