diff --git a/rsconcept/frontend/src/components/icons.tsx b/rsconcept/frontend/src/components/icons.tsx index dfd1aaaa..4e8bd635 100644 --- a/rsconcept/frontend/src/components/icons.tsx +++ b/rsconcept/frontend/src/components/icons.tsx @@ -1,5 +1,6 @@ // Search new icons at https://reactsvgicons.com/ // Note: save this file using Ctrl + K, Ctrl + Shift + S to disable autoformat +// Note: For Cursor hotkeys are Ctrl + M, Ctrl + Shift + S /* eslint-disable simple-import-sort/exports */ // ==== General actions ======= diff --git a/rsconcept/frontend/src/features/help/items/help-interface.tsx b/rsconcept/frontend/src/features/help/items/help-interface.tsx index d7dec2f2..a2d5aa5e 100644 --- a/rsconcept/frontend/src/features/help/items/help-interface.tsx +++ b/rsconcept/frontend/src/features/help/items/help-interface.tsx @@ -8,6 +8,7 @@ import { IconPin, IconUser2 } from '@/components/icons'; +import { isMac } from '@/utils/utils'; import { Subtopics } from '../components/subtopics'; import { HelpTopic } from '../models/help-topic'; @@ -33,7 +34,7 @@ export function HelpInterface() {

Навигация и настройки

@@ -116,7 +117,7 @@ export function HelpRSEditor() {
  • - Ctrl + Пробел открывает редактирование отсылок + {isMac() ? 'Cmd + Пробел' : 'Ctrl + Пробел'} открывает редактирование отсылок
  • diff --git a/rsconcept/frontend/src/features/library/components/toolbar-item-card.tsx b/rsconcept/frontend/src/features/library/components/toolbar-item-card.tsx index ece1cb06..e1720670 100644 --- a/rsconcept/frontend/src/features/library/components/toolbar-item-card.tsx +++ b/rsconcept/frontend/src/features/library/components/toolbar-item-card.tsx @@ -12,7 +12,7 @@ import { cn } from '@/components/utils'; import { useModificationStore } from '@/stores/modification'; import { usePreferencesStore } from '@/stores/preferences'; import { tooltipText } from '@/utils/labels'; -import { prepareTooltip, sharePage } from '@/utils/utils'; +import { isMac, prepareTooltip, sharePage } from '@/utils/utils'; import { AccessPolicy, type ILibraryItem, LibraryItemType } from '../backend/types'; import { useMutatingLibrary } from '../backend/use-mutating-library'; @@ -71,7 +71,7 @@ export function ToolbarItemCard({ {ossSelector} {isMutable || isModified ? ( } onClick={onSubmit} diff --git a/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/context-menu/menu-operation.tsx b/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/context-menu/menu-operation.tsx index 5e5be8c1..ae3ede5b 100644 --- a/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/context-menu/menu-operation.tsx +++ b/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/context-menu/menu-operation.tsx @@ -1,7 +1,6 @@ 'use client'; import { toast } from 'react-toastify'; -import { urls, useConceptNavigation } from '@/app'; import { useLibrary } from '@/features/library/backend/use-library'; import { DropdownButton } from '@/components/dropdown'; @@ -33,7 +32,6 @@ interface MenuOperationProps { } export function MenuOperation({ operation, onHide }: MenuOperationProps) { - const router = useConceptNavigation(); const { items: libraryItems } = useLibrary(); const { schema, navigateOperationSchema, isMutable, canDeleteOperation } = useOssEdit(); const isProcessing = useMutatingOss(); @@ -134,7 +132,7 @@ export function MenuOperation({ operation, onHide }: MenuOperationProps) { void inputCreate({ itemID: schema.id, data: { target: operation.id, layout: getLayout() } - }).then(new_schema => router.push({ path: urls.schema(new_schema.id), force: true })); + }); } function handleRelocateConstituents() { diff --git a/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/oss-flow.tsx b/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/oss-flow.tsx index c9232482..41046bcb 100644 --- a/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/oss-flow.tsx +++ b/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/oss-flow.tsx @@ -163,14 +163,16 @@ export function OssFlow() { handleSavePositions(); return; } - if ((event.ctrlKey || event.metaKey) && event.code === 'KeyQ') { + if (event.altKey && event.code === 'Key1') { event.preventDefault(); event.stopPropagation(); - if (event.shiftKey) { - handleCreateBlock(); - } else { - handleCreateOperation(); - } + handleCreateBlock(); + return; + } + if (event.altKey && event.code === 'Key2') { + event.preventDefault(); + event.stopPropagation(); + handleCreateOperation(); return; } if (event.key === 'Delete') { @@ -200,8 +202,10 @@ export function OssFlow() { void; onCreateBlock: () => void; + onCreateSchema: () => void; + onImportSchema: () => void; + onCreateSynthesis: () => void; onDelete: () => void; onResetPositions: () => void; @@ -44,8 +51,10 @@ interface ToolbarOssGraphProps extends Styling { } export function ToolbarOssGraph({ - onCreateOperation, onCreateBlock, + onCreateSchema, + onImportSchema, + onCreateSynthesis, onDelete, onResetPositions, @@ -58,18 +67,25 @@ export function ToolbarOssGraph({ const { schema, selectedItems, isMutable, canDeleteOperation: canDelete } = useOssEdit(); const isProcessing = useMutatingOss(); const { resetView, nodes } = useOssFlow(); - const selectedOperation = - selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.OPERATION ? selectedItems[0] : null; - const selectedBlock = - selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.BLOCK ? selectedItems[0] : null; const getLayout = useGetLayout(); - const { updateLayout } = useUpdateLayout(); + const { user } = useAuthSuspense(); + const menu = useDropdown(); const showOptions = useDialogsStore(state => state.showOssOptions); const showSidePanel = usePreferencesStore(state => state.showOssSidePanel); const toggleShowSidePanel = usePreferencesStore(state => state.toggleShowOssSidePanel); + const selectedOperation = + selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.OPERATION ? selectedItems[0] : null; + const selectedBlock = + selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.BLOCK ? selectedItems[0] : null; + + function handleMenuToggle() { + hideContextMenu(); + menu.toggle(); + } + function handleShowOptions() { showOptions(); } @@ -127,40 +143,80 @@ export function ToolbarOssGraph({ {isMutable ? ( -
    +
    } onClick={handleSavePositions} disabled={isProcessing} /> } onClick={handleEditItem} disabled={selectedItems.length !== 1 || isProcessing} /> +
    + } + onClick={handleMenuToggle} + disabled={isProcessing} + /> + + } + onClick={onCreateBlock} + /> + } + onClick={onCreateSchema} + /> + } + onClick={onImportSchema} + /> + } + onClick={onCreateSynthesis} + /> + {user.is_staff ? ( + } + onClick={notImplemented} + /> + ) : null} + {user.is_staff ? ( + } + onClick={notImplemented} + /> + ) : null} + +
    } - onClick={onCreateOperation} - disabled={isProcessing} - /> - } - onClick={onCreateBlock} - disabled={isProcessing} - /> - - } onClick={onDelete} disabled={ diff --git a/rsconcept/frontend/src/features/rsform/components/refs-input/tooltip.ts b/rsconcept/frontend/src/features/rsform/components/refs-input/tooltip.ts index 60be00dd..5ad20b6a 100644 --- a/rsconcept/frontend/src/features/rsform/components/refs-input/tooltip.ts +++ b/rsconcept/frontend/src/features/rsform/components/refs-input/tooltip.ts @@ -4,6 +4,7 @@ import { hoverTooltip, type TooltipView } from '@codemirror/view'; import clsx from 'clsx'; import { findContainedNodes } from '@/utils/codemirror'; +import { isMac } from '@/utils/utils'; import { describeConstituentaTerm, labelGrammeme } from '../../labels'; import { type IEntityReference, type ISyntacticReference } from '../../models/language'; @@ -95,7 +96,9 @@ function domTooltipEntityReference(ref: IEntityReference, cst: IConstituenta | n if (canClick) { const clickTip = document.createElement('p'); clickTip.className = 'text-center text-xs mt-1'; - clickTip.innerHTML = 'Ctrl + клик для перехода
    Ctrl + пробел для редактирования'; + clickTip.innerHTML = isMac() + ? 'Cmd + клик для перехода
    Cmd + пробел для редактирования' + : 'Ctrl + клик для перехода
    Ctrl + пробел для редактирования'; dom.appendChild(clickTip); } @@ -140,7 +143,9 @@ function domTooltipSyntacticReference( if (canClick) { const clickTip = document.createElement('p'); clickTip.className = 'text-center text-xs mt-1'; - clickTip.innerHTML = 'Ctrl + пробел для редактирования'; + clickTip.innerHTML = isMac() + ? 'Cmd + пробел для редактирования' + : 'Ctrl + пробел для редактирования'; dom.appendChild(clickTip); } diff --git a/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-constituenta/toolbar-constituenta.tsx b/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-constituenta/toolbar-constituenta.tsx index 0a8cdfbf..ff4d809d 100644 --- a/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-constituenta/toolbar-constituenta.tsx +++ b/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-constituenta/toolbar-constituenta.tsx @@ -22,7 +22,7 @@ import { cn } from '@/components/utils'; import { useModificationStore } from '@/stores/modification'; import { usePreferencesStore } from '@/stores/preferences'; import { tooltipText } from '@/utils/labels'; -import { prepareTooltip } from '@/utils/utils'; +import { isMac, prepareTooltip } from '@/utils/utils'; import { useMutatingRSForm } from '../../../backend/use-mutating-rsform'; import { type IConstituenta } from '../../../models/rsform'; @@ -97,7 +97,7 @@ export function ToolbarConstituenta({ {isContentEditable ? ( <> } onClick={onSubmit} diff --git a/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-rsexpression/status-bar.tsx b/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-rsexpression/status-bar.tsx index 9731a2f4..1d862338 100644 --- a/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-rsexpression/status-bar.tsx +++ b/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-rsexpression/status-bar.tsx @@ -10,7 +10,7 @@ import { cn } from '@/components/utils'; import { APP_COLORS } from '@/styling/colors'; import { globalIDs } from '@/utils/constants'; import { type RO } from '@/utils/meta'; -import { prepareTooltip } from '@/utils/utils'; +import { isMac, prepareTooltip } from '@/utils/utils'; import { type IExpressionParseDTO, ParsingStatus } from '../../../backend/types'; import { colorStatusBar } from '../../../colors'; @@ -55,7 +55,7 @@ export function StatusBar({ className, isModified, processing, activeCst, parseD )} style={{ backgroundColor: processing ? APP_COLORS.bgDefault : colorStatusBar(status) }} data-tooltip-id={globalIDs.tooltip} - data-tooltip-html={prepareTooltip('Проверить определение', 'Ctrl + Q')} + data-tooltip-html={prepareTooltip('Проверить определение', isMac() ? 'Cmd + Q' : 'Ctrl + Q')} onClick={onAnalyze} > {processing ? ( diff --git a/rsconcept/frontend/src/utils/utils.ts b/rsconcept/frontend/src/utils/utils.ts index fe11fe15..95e29e80 100644 --- a/rsconcept/frontend/src/utils/utils.ts +++ b/rsconcept/frontend/src/utils/utils.ts @@ -176,6 +176,25 @@ export function sharePage() { .catch(console.error); } +/** + * Show error message about not implemented function. + */ +export function notImplemented() { + toast.error('Данная функция еще не реализована'); + console.error('Not implemented'); +} + +/** + * Wrap event handler to prevent default and stop propagation. + */ +export function withPreventDefault(handler: (event: T) => void) { + return (event: T) => { + event.preventDefault(); + event.stopPropagation(); + handler(event); + }; +} + /** * Remove html tags from target string. */ @@ -196,12 +215,22 @@ export function prepareTooltip(text: string, hotkey?: string) { /** * Utility to detect iOS/iPadOS. */ -export function isIOS() { +export function isIOS(): boolean { if (typeof navigator === 'undefined') { return false; } return ( - /iPad|iPhone|iPod/.test(navigator.userAgent) || + /iPad|iPhone|iPod/i.test(navigator.userAgent) || (navigator.userAgent.includes('Macintosh') && 'ontouchend' in document) ); } + +/** + * Utility to detect Mac device. + */ +export function isMac(): boolean { + if (typeof navigator === 'undefined') { + return false; + } + return /Macintosh|MacIntel|MacPPC|Mac68K|Mac OS/i.test(navigator.userAgent); +}