Skip to content

Commit 53cd8a1

Browse files
committed
feat(2d): drawable canvas
- fix potential pan offset at the first mouse gesture ref #1076
1 parent 24f3b70 commit 53cd8a1

34 files changed

+890
-100
lines changed
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
import React from 'react';
2-
import { Trans, useLingui } from '@lingui/react/macro';
2+
import { Trans } from '@lingui/react/macro';
33
import { ContextMenuItem } from '../context-menu-item';
4-
import { SHOW_COMMENT_SHORTCUT } from 'csdm/ui/keyboard/keyboard-shortcut';
4+
import { showCommentKey } from 'csdm/ui/keyboard/keyboard-shortcut';
55

66
type CommentItemProps = {
77
onClick: () => void;
88
isDisabled?: boolean;
99
};
1010

1111
export function CommentItem({ onClick, isDisabled = false }: CommentItemProps) {
12-
const { t } = useLingui();
13-
1412
return (
1513
<ContextMenuItem onClick={onClick} isDisabled={isDisabled}>
1614
<p>
1715
<Trans context="Context menu">Comment</Trans>
1816
</p>
19-
<p className="text-caption">{t(SHOW_COMMENT_SHORTCUT.label)}</p>
17+
<p className="text-caption">{showCommentKey}</p>
2018
</ContextMenuItem>
2119
);
2220
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React, { type ReactNode } from 'react';
2+
3+
type Props = {
4+
children: string;
5+
};
6+
7+
export function KeyboardKey({ children }: Props) {
8+
return (
9+
<div className="flex items-center justify-center bg-gray-50 border border-gray-400 text-caption font-bold rounded-4 py-4 px-8">
10+
<p>{children}</p>
11+
</div>
12+
);
13+
}
14+
15+
export function KeyboardKeys({ children }: { children: ReactNode }) {
16+
return <div className="flex items-center gap-x-4">{children}</div>;
17+
}

src/ui/components/popover/popover.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,15 @@ type PopoverOptions = {
3232
open?: boolean;
3333
onOpenChange?: (open: boolean) => void;
3434
openOnHover?: boolean;
35+
openDelay?: number;
36+
closeDelay?: number;
3537
};
3638

3739
function usePopover({
3840
initialOpen = false,
3941
openOnHover = false,
42+
openDelay = 0,
43+
closeDelay = 0,
4044
placement = 'top',
4145
modal,
4246
open: controlledOpen,
@@ -73,6 +77,7 @@ function usePopover({
7377
const hover = useHover(context, {
7478
handleClose: safePolygon(),
7579
enabled: openOnHover,
80+
delay: { open: openDelay, close: closeDelay },
7681
});
7782
const interactions = useInteractions([click, dismiss, role, hover]);
7883

src/ui/components/table/use-table-comment-widget-visibility.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect, useState } from 'react';
2-
import { SHOW_COMMENT_SHORTCUT } from 'csdm/ui/keyboard/keyboard-shortcut';
2+
import { showCommentKey } from 'csdm/ui/keyboard/keyboard-shortcut';
33
import { isCtrlOrCmdEvent } from 'csdm/ui/keyboard/keyboard';
44

55
export function useTableCommentWidgetVisibility() {
@@ -14,7 +14,7 @@ export function useTableCommentWidgetVisibility() {
1414
};
1515

1616
const onKeyDown = (event: KeyboardEvent) => {
17-
if (!isCtrlOrCmdEvent(event) && event.key.toUpperCase() === SHOW_COMMENT_SHORTCUT.key) {
17+
if (!isCtrlOrCmdEvent(event) && event.key.toUpperCase() === showCommentKey) {
1818
setIsWidgetVisible((isVisible) => !isVisible);
1919
}
2020
};

src/ui/demo/next-demo-link.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Trans } from '@lingui/react/macro';
33
import { useLocation, useParams } from 'react-router';
44
import { buildDemoPath } from 'csdm/ui/routes-paths';
55
import { NextLink } from '../components/links';
6+
import { modifierKey } from '../keyboard/keyboard-shortcut';
67

78
export function NextDemoLink() {
89
const location = useLocation();
@@ -14,7 +15,6 @@ export function NextDemoLink() {
1415
const currentDemoIndex = siblingDemoPaths.indexOf(currentDemoPath);
1516
const nextDemoPath = siblingDemoPaths[currentDemoIndex + 1];
1617
const to = nextDemoPath === undefined ? '' : buildDemoPath(nextDemoPath);
17-
const key = window.csdm.isMac ? '⌘' : 'CTRL';
1818

19-
return <NextLink to={to} tooltip={<Trans>Next demo ({key}+→)</Trans>} />;
19+
return <NextLink to={to} tooltip={<Trans>Next demo ({modifierKey}+→)</Trans>} />;
2020
}

src/ui/demo/previous-demo-link.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useLocation, useParams } from 'react-router';
33
import { Trans } from '@lingui/react/macro';
44
import { buildDemoPath } from 'csdm/ui/routes-paths';
55
import { PreviousLink } from '../components/links';
6+
import { modifierKey } from '../keyboard/keyboard-shortcut';
67

78
export function PreviousDemoLink() {
89
const location = useLocation();
@@ -14,7 +15,6 @@ export function PreviousDemoLink() {
1415
const currentDemoIndex = siblingDemoPaths.indexOf(currentDemoPath);
1516
const previousDemoPath = siblingDemoPaths[currentDemoIndex - 1];
1617
const to = previousDemoPath === undefined ? '' : buildDemoPath(previousDemoPath);
17-
const key = window.csdm.isMac ? '⌘' : 'CTRL';
1818

19-
return <PreviousLink to={to} tooltip={<Trans>Previous demo ({key}+←)</Trans>} />;
19+
return <PreviousLink to={to} tooltip={<Trans>Previous demo ({modifierKey}+←)</Trans>} />;
2020
}

src/ui/hooks/use-interactive-map-canvas.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export type InteractiveCanvas = {
2020
zoomedToRadarY: (y: number, z: number) => number;
2121
getMouseX: () => number;
2222
getMouseY: () => number;
23+
pixelToWorldX: (x: number) => number;
24+
pixelToWorldY: (y: number) => number;
2325
canvasSize: { width: number; height: number };
2426
};
2527

@@ -146,11 +148,13 @@ export function useInteractiveMapCanvas(
146148

147149
if (event.type === 'mousedown') {
148150
isDragging.current = true;
151+
mouseWorldX.current = pixelToWorldX(mousePixelX.current);
152+
mouseWorldY.current = pixelToWorldY(mousePixelY.current);
149153
} else if (event.type === 'mouseup' || event.type === 'mouseout') {
150154
isDragging.current = false;
151155
}
152156

153-
if (isDragging.current) {
157+
if (isDragging.current && event.type !== 'mousedown') {
154158
worldOriginX.current -= mouseWorldX.current - lastMouseWorldX;
155159
worldOriginY.current -= mouseWorldY.current - lastMouseWorldY;
156160
mouseWorldX.current = pixelToWorldX(mousePixelX.current);
@@ -227,6 +231,8 @@ export function useInteractiveMapCanvas(
227231
zoomedToRadarY,
228232
getMouseX: () => mousePixelX.current,
229233
getMouseY: () => mousePixelY.current,
234+
pixelToWorldX,
235+
pixelToWorldY,
230236
canvasSize,
231237
};
232238
}

src/ui/icons/eraser-icon.svg

Lines changed: 3 additions & 0 deletions
Loading

src/ui/icons/eraser-icon.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { SVGAttributes } from 'react';
2+
import React from 'react';
3+
4+
type Props = SVGAttributes<SVGElement>;
5+
6+
export function EraserIcon(props: Props) {
7+
return (
8+
<svg viewBox="0 0 640 640" {...props}>
9+
<path d="M210.5 480L333.5 480L398.8 414.7L225.3 241.2L98.6 367.9L210.6 479.9zM256 544L210.5 544C193.5 544 177.2 537.3 165.2 525.3L49 409C38.1 398.1 32 383.4 32 368C32 352.6 38.1 337.9 49 327L295 81C305.9 70.1 320.6 64 336 64C351.4 64 366.1 70.1 377 81L559 263C569.9 273.9 576 288.6 576 304C576 319.4 569.9 334.1 559 345L424 480L544 480C561.7 480 576 494.3 576 512C576 529.7 561.7 544 544 544L256 544z" />
10+
</svg>
11+
);
12+
}

src/ui/icons/pencil-icon.svg

Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)