Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 25 additions & 39 deletions package-lock.json

Large diffs are not rendered by default.

83 changes: 78 additions & 5 deletions src/components/context-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { clearSelected, refreshEdgesThunk, refreshNodesThunk, setSelected } from
import { exportSelectedNodesAndEdges, importSelectedNodesAndEdges } from '../util/clipboard';
import { pointerPosToSVGCoord, roundToMultiple } from '../util/helpers';
import { MAX_PARALLEL_LINES_FREE } from '../util/parallel';
import { flipSelectedNodes, rotateSelectedNodes } from '../util/transform';

interface ContextMenuProps {
isOpen: boolean;
Expand Down Expand Up @@ -38,6 +39,10 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ isOpen, position, onClose })
(autoParallel && !activeSubscriptions.RMP_CLOUD && parallelLinesCount + 1 > MAX_PARALLEL_LINES_FREE);

const hasSelection = selected.size > 0;
const hasMoreThanOneNodeSelection = React.useMemo(
() => [...selected].filter(id => graph.current.hasNode(id)).length > 1,
[selected]
);
const menuRef = React.useRef<HTMLDivElement>(null);

useOutsideClick({
Expand Down Expand Up @@ -141,15 +146,28 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ isOpen, position, onClose })
const handlePlaceDown = useEvent(() => {
if (selected.size > 0) {
const [selectedFirst] = selected;
const currentZIndex = graph.current.hasNode(selectedFirst)
? graph.current.getNodeAttribute(selectedFirst, 'zIndex')
: graph.current.hasEdge(selectedFirst)
? graph.current.getEdgeAttribute(selectedFirst, 'zIndex')
: 0;
let currentZIndex = 0;
if (graph.current.hasNode(selectedFirst)) {
currentZIndex = graph.current.getNodeAttribute(selectedFirst, 'zIndex');
} else if (graph.current.hasEdge(selectedFirst)) {
currentZIndex = graph.current.getEdgeAttribute(selectedFirst, 'zIndex');
}
handleZIndex(currentZIndex - 1);
}
});

const handleRotate = useEvent((angle: number) => {
if (rotateSelectedNodes(graph.current, selected, angle)) {
refreshAndSave();
}
});

const handleFlip = useEvent((direction: 'vertical' | 'horizontal' | 'diagonal45' | 'diagonal135') => {
if (flipSelectedNodes(graph.current, selected, direction)) {
refreshAndSave();
}
});

if (!isOpen) return null;

return (
Expand Down Expand Up @@ -262,6 +280,61 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ isOpen, position, onClose })
>
{t('contextMenu.placeDown')}
</MenuItem>
<Divider />
<MenuItem
onClick={() => {
handleRotate(45);
onClose();
}}
isDisabled={!hasMoreThanOneNodeSelection}
>
{t('contextMenu.rotateCW')}
</MenuItem>
<MenuItem
onClick={() => {
handleRotate(-45);
onClose();
}}
isDisabled={!hasMoreThanOneNodeSelection}
>
{t('contextMenu.rotateCCW')}
</MenuItem>
<MenuItem
onClick={() => {
handleFlip('vertical');
onClose();
}}
isDisabled={!hasMoreThanOneNodeSelection}
>
{t('contextMenu.flipVertical')}
</MenuItem>
<MenuItem
onClick={() => {
handleFlip('horizontal');
onClose();
}}
isDisabled={!hasMoreThanOneNodeSelection}
>
{t('contextMenu.flipHorizontal')}
</MenuItem>
<MenuItem
onClick={() => {
handleFlip('diagonal45');
onClose();
}}
isDisabled={!hasMoreThanOneNodeSelection}
>
{t('contextMenu.flipDiagonal45')}
</MenuItem>
<MenuItem
onClick={() => {
handleFlip('diagonal135');
onClose();
}}
isDisabled={!hasMoreThanOneNodeSelection}
>
{t('contextMenu.flipDiagonal135')}
</MenuItem>
</Box>
</Portal>
);
Expand Down
6 changes: 6 additions & 0 deletions src/components/page-header/settings-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,12 @@ const SettingsModal = (props: { isOpen: boolean; onClose: () => void }) => {
</Td>
<Td>{t('header.settings.shortcuts.ijkl')}</Td>
</Tr>
<Tr>
<Td>
<Kbd>r</Kbd>
</Td>
<Td>{t('header.settings.shortcuts.rotate')}</Td>
</Tr>
<Tr>
<Td>
{isMacClient ? <Kbd sx={macKeyStyle}>&#8679;</Kbd> : <Kbd>shift</Kbd>}
Expand Down
6 changes: 6 additions & 0 deletions src/components/svg-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
} from '../util/helpers';
import { useFonts, useWindowSize } from '../util/hooks';
import { makeParallelIndex, MAX_PARALLEL_LINES_FREE, NonSimpleLinePathAttributes } from '../util/parallel';
import { rotateSelectedNodes } from '../util/transform';
import { useMakeStationName } from '../util/random-station-names';
import { checkAndChangeStationIntType } from '../util/change-types';
import ContextMenu from './context-menu';
Expand Down Expand Up @@ -329,6 +330,11 @@ const SvgWrapper = () => {
dispatch(redoAction());
} else if (e.key === 'c') {
dispatch(setSnapLines(!snapLines));
} else if (e.key === 'r') {
const angle = e.altKey ? 5 : 45;
if (rotateSelectedNodes(graph.current, selected, angle)) {
refreshAndSave();
}
} else if (e.key === 'e') {
// switch startFrom between 'from' and 'to' when an edge is selected
const [selectedFirst] = selected;
Expand Down
9 changes: 8 additions & 1 deletion src/i18n/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,7 @@
"c": "Enable or disable magnetic layout.",
"arrows": "Move the canvas a little bit.",
"ijkl": "Move the selected station(s) a little bit.",
"rotate": "Rotate selected nodes clockwise. Hold Alt for 5° steps.",
"shift": "Multiple selection.",
"alt": "Hold down while dragging to temporarily disable auto-snapping.",
"delete": "Delete the selected station(s).",
Expand Down Expand Up @@ -914,7 +915,13 @@
"placeBottom": "Place bottom",
"placeDefault": "Place to default",
"placeUp": "Place one level up",
"placeDown": "Place one level down"
"placeDown": "Place one level down",
"rotateCW": "Rotate +45°",
"rotateCCW": "Rotate -45°",
"flipVertical": "Flip vertically",
"flipHorizontal": "Flip horizontally",
"flipDiagonal45": "Flip across 45° line",
"flipDiagonal135": "Flip across 135° line"
},

"localStorageQuotaExceeded": "Local storage limit reached. Unable to save new changes."
Expand Down
9 changes: 8 additions & 1 deletion src/i18n/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,7 @@
"c": "マグネティックレイアウトを有効または無効にします。",
"arrows": "キャンバスを少し移動します。",
"ijkl": "選択した駅を少し移動します。",
"rotate": "選択したノードを時計回りに回転します(Alt で 5° ステップ)。",
"shift": "複数選択。",
"alt": "引きずる時に押し続けると自動スナップが一時的に無効化されます。",
"delete": "選択した駅を削除します。",
Expand Down Expand Up @@ -915,7 +916,13 @@
"placeBottom": "最背面へ",
"placeDefault": "デフォルト位置へ",
"placeUp": "一つ前へ",
"placeDown": "一つ後ろへ"
"placeDown": "一つ後ろへ",
"rotateCW": "45° 時計回りに回転",
"rotateCCW": "45° 反時計回りに回転",
"flipVertical": "垂直に反転",
"flipHorizontal": "水平に反転",
"flipDiagonal45": "45° 線で反転",
"flipDiagonal135": "135° 線で反転"
},

"localStorageQuotaExceeded": "ローカルストレージの上限に達しました。新しい変更は保存できません。"
Expand Down
9 changes: 8 additions & 1 deletion src/i18n/translations/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@
"c": "자기 레이아웃을 활성화하거나 비활성화합니다.",
"arrows": "캔버스를 약간 이동합니다.",
"ijkl": "선택한 역을 약간 이동합니다.",
"rotate": "선택한 노드를 시계 방향으로 회전합니다(Alt는 5° 단위).",
"shift": "여러 항목 선택.",
"alt": "드래그 중 누르고 있으면 자동 스냅이 일시적으로 비활성화됩니다.",
"delete": "선택한 역을 삭제합니다.",
Expand Down Expand Up @@ -913,7 +914,13 @@
"placeBottom": "맨 뒤로",
"placeDefault": "기본 위치로",
"placeUp": "한 단계 앞으로",
"placeDown": "한 단계 뒤로"
"placeDown": "한 단계 뒤로",
"rotateCW": "시계 방향 45° 회전",
"rotateCCW": "반시계 방향 45° 회전",
"flipVertical": "수직 뒤집기",
"flipHorizontal": "수평 뒤집기",
"flipDiagonal45": "45° 선을 기준으로 뒤집기",
"flipDiagonal135": "135° 선을 기준으로 뒤집기"
},

"localStorageQuotaExceeded": "로컬 저장소 한도에 도달했습니다. 새로운 변경 사항을 저장할 수 없습니다."
Expand Down
9 changes: 8 additions & 1 deletion src/i18n/translations/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@
"c": "启用或停用磁性布局。",
"arrows": "稍微移动画布。",
"ijkl": "稍微移动所选站点。",
"rotate": "顺时针旋转所选节点(按住 Alt 为 5° 步进)。",
"shift": "多选。",
"alt": "拖动时按下临时停用磁性布局。",
"delete": "删除所选站点。",
Expand Down Expand Up @@ -913,7 +914,13 @@
"placeBottom": "置底",
"placeDefault": "还原位置",
"placeUp": "上移一层",
"placeDown": "下移一层"
"placeDown": "下移一层",
"rotateCW": "顺时针旋转45°",
"rotateCCW": "逆时针旋转45°",
"flipVertical": "垂直翻转",
"flipHorizontal": "水平翻转",
"flipDiagonal45": "沿45°线翻转",
"flipDiagonal135": "沿135°线翻转"
},

"localStorageQuotaExceeded": "本地存储空间已达上限。无法保存新更改。"
Expand Down
9 changes: 8 additions & 1 deletion src/i18n/translations/zh-Hant.json
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@
"c": "啟用或停用磁性佈局。",
"arrows": "稍微移動畫布。",
"ijkl": "稍微移動所選站點。",
"rotate": "順時針旋轉所選節點(按住 Alt 為 5° 步進)。",
"shift": "多選。",
"alt": "拖動時按下以臨時停用自動吸附。",
"delete": "刪除所選站點。",
Expand Down Expand Up @@ -913,7 +914,13 @@
"placeBottom": "置底",
"placeDefault": "還原位置",
"placeUp": "上移一層",
"placeDown": "下移一層"
"placeDown": "下移一層",
"rotateCW": "順時針旋轉45°",
"rotateCCW": "逆時針旋轉45°",
"flipVertical": "垂直翻轉",
"flipHorizontal": "水平翻轉",
"flipDiagonal45": "沿45°線翻轉",
"flipDiagonal135": "沿135°線翻轉"
},

"localStorageQuotaExceeded": "本機儲存空間已達上限。無法儲存新的變更。"
Expand Down
Loading