mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
enhance: support whiteboard translations
This commit is contained in:
committed by
Gabriel Horner
parent
776495d187
commit
fbf365ceff
@@ -8,9 +8,13 @@ import { TablerIcon } from '../icons'
|
||||
import { Button } from '../Button'
|
||||
import { ZoomMenu } from '../ZoomMenu'
|
||||
import * as Separator from '@radix-ui/react-separator'
|
||||
import { LogseqContext } from '../../lib/logseq-context'
|
||||
|
||||
export const ActionBar = observer(function ActionBar(): JSX.Element {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
|
||||
const undo = React.useCallback(() => {
|
||||
app.api.undo()
|
||||
@@ -32,20 +36,20 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
|
||||
<div className="tl-action-bar" data-html2canvas-ignore="true">
|
||||
{!app.readOnly && (
|
||||
<div className="tl-toolbar tl-history-bar">
|
||||
<Button tooltip="Undo" onClick={undo}>
|
||||
<Button tooltip={t('whiteboard/undo')} onClick={undo}>
|
||||
<TablerIcon name="arrow-back-up" />
|
||||
</Button>
|
||||
<Button tooltip="Redo" onClick={redo}>
|
||||
<Button tooltip={t('whiteboard/redo')} onClick={redo}>
|
||||
<TablerIcon name="arrow-forward-up" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={`tl-toolbar tl-zoom-bar ${app.readOnly ? '' : 'ml-4'}`}>
|
||||
<Button tooltip="Zoom in" onClick={zoomIn} id="tl-zoom-in">
|
||||
<Button tooltip={t('whiteboard/zoom-in')} onClick={zoomIn} id="tl-zoom-in">
|
||||
<TablerIcon name="plus" />
|
||||
</Button>
|
||||
<Button tooltip="Zoom out" onClick={zoomOut} id="tl-zoom-out">
|
||||
<Button tooltip={t('whiteboard/zoom-out')} onClick={zoomOut} id="tl-zoom-out">
|
||||
<TablerIcon name="minus" />
|
||||
</Button>
|
||||
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />
|
||||
|
||||
@@ -92,13 +92,16 @@ function filterShapeByAction<S extends Shape>(type: ContextBarActionType) {
|
||||
|
||||
const AutoResizingAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const shapes = filterShapeByAction<LogseqPortalShape | TextShape | HTMLShape>('AutoResizing')
|
||||
|
||||
const pressed = shapes.every(s => s.props.isAutoResizing)
|
||||
|
||||
return (
|
||||
<ToggleInput
|
||||
tooltip="Auto Resize"
|
||||
tooltip={t('whiteboard/auto-resize')}
|
||||
toggle={shapes.every(s => s.props.type === 'logseq-portal')}
|
||||
className="tl-button"
|
||||
pressed={pressed}
|
||||
@@ -122,6 +125,9 @@ const AutoResizingAction = observer(() => {
|
||||
|
||||
const LogseqPortalViewModeAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const shapes = filterShapeByAction<LogseqPortalShape>('LogseqPortalViewMode')
|
||||
|
||||
const collapsed = shapes.every(s => s.collapsed)
|
||||
@@ -131,7 +137,7 @@ const LogseqPortalViewModeAction = observer(() => {
|
||||
|
||||
const tooltip = (
|
||||
<div className="flex">
|
||||
{collapsed ? 'Expand' : 'Collapse'}
|
||||
{collapsed ? t('whiteboard/expand') : t('whiteboard/collapse')}
|
||||
<KeyboardShortcut
|
||||
action={collapsed ? 'editor/expand-block-children' : 'editor/collapse-block-children'}
|
||||
/>
|
||||
@@ -164,6 +170,9 @@ const ScaleLevelAction = observer(() => {
|
||||
|
||||
const IFrameSourceAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const shape = filterShapeByAction<IFrameShape>('IFrameSource')[0]
|
||||
|
||||
const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -177,16 +186,20 @@ const IFrameSourceAction = observer(() => {
|
||||
|
||||
return (
|
||||
<span className="flex gap-3">
|
||||
<Button tooltip="Reload" type="button" onClick={handleReload}>
|
||||
<Button tooltip={t('whiteboard/reload')} type="button" onClick={handleReload}>
|
||||
<TablerIcon name="refresh" />
|
||||
</Button>
|
||||
<TextInput
|
||||
title="Website Url"
|
||||
title={t('whiteboard/website-url')}
|
||||
className="tl-iframe-src"
|
||||
value={`${shape.props.url}`}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<Button tooltip="Open website url" type="button" onClick={() => window.open(shape.props.url)}>
|
||||
<Button
|
||||
tooltip={t('whiteboard/open-website-url')}
|
||||
type="button"
|
||||
onClick={() => window.open(shape.props.url)}
|
||||
>
|
||||
<TablerIcon name="external-link" />
|
||||
</Button>
|
||||
</span>
|
||||
@@ -195,6 +208,9 @@ const IFrameSourceAction = observer(() => {
|
||||
|
||||
const YoutubeLinkAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const shape = filterShapeByAction<YouTubeShape>('YoutubeLink')[0]
|
||||
const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
shape.onYoutubeLinkChange(e.target.value)
|
||||
@@ -204,13 +220,13 @@ const YoutubeLinkAction = observer(() => {
|
||||
return (
|
||||
<span className="flex gap-3">
|
||||
<TextInput
|
||||
title="YouTube Link"
|
||||
title={t('whiteboard/youtube-url')}
|
||||
className="tl-youtube-link"
|
||||
value={`${shape.props.url}`}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<Button
|
||||
tooltip="Open YouTube Link"
|
||||
tooltip={t('whiteboard/open-youtube-url')}
|
||||
type="button"
|
||||
onClick={() => window.logseq?.api?.open_external_link?.(shape.props.url)}
|
||||
>
|
||||
@@ -222,6 +238,9 @@ const YoutubeLinkAction = observer(() => {
|
||||
|
||||
const TwitterLinkAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const shape = filterShapeByAction<TweetShape>('TwitterLink')[0]
|
||||
const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
shape.onTwitterLinkChange(e.target.value)
|
||||
@@ -231,13 +250,13 @@ const TwitterLinkAction = observer(() => {
|
||||
return (
|
||||
<span className="flex gap-3">
|
||||
<TextInput
|
||||
title="Twitter Link"
|
||||
title={t('whiteboard/twitter-url')}
|
||||
className="tl-twitter-link"
|
||||
value={`${shape.props.url}`}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<Button
|
||||
tooltip="Open Twitter Link"
|
||||
tooltip={t('whiteboard/open-twitter-url')}
|
||||
type="button"
|
||||
onClick={() => window.logseq?.api?.open_external_link?.(shape.props.url)}
|
||||
>
|
||||
@@ -249,6 +268,9 @@ const TwitterLinkAction = observer(() => {
|
||||
|
||||
const NoFillAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const shapes = filterShapeByAction<BoxShape | PolygonShape | EllipseShape>('NoFill')
|
||||
const handleChange = React.useCallback((v: boolean) => {
|
||||
app.selectedShapesArray.forEach(s => s.update({ noFill: v }))
|
||||
@@ -259,7 +281,7 @@ const NoFillAction = observer(() => {
|
||||
|
||||
return (
|
||||
<ToggleInput
|
||||
tooltip="Fill"
|
||||
tooltip={t('whiteboard/fill')}
|
||||
className="tl-button"
|
||||
pressed={noFill}
|
||||
onPressedChange={handleChange}
|
||||
@@ -315,6 +337,9 @@ const GeometryAction = observer(() => {
|
||||
|
||||
const StrokeTypeAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const shapes = filterShapeByAction<
|
||||
BoxShape | PolygonShape | EllipseShape | LineShape | PencilShape
|
||||
>('StrokeType')
|
||||
@@ -340,7 +365,7 @@ const StrokeTypeAction = observer(() => {
|
||||
|
||||
return (
|
||||
<ToggleGroupInput
|
||||
title="Stroke Type"
|
||||
title={t('whiteboard/stroke-type')}
|
||||
options={StrokeTypeOptions}
|
||||
value={value}
|
||||
onValueChange={v => {
|
||||
@@ -357,6 +382,9 @@ const StrokeTypeAction = observer(() => {
|
||||
|
||||
const ArrowModeAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const shapes = filterShapeByAction<LineShape>('ArrowMode')
|
||||
|
||||
const StrokeTypeOptions: ToggleGroupInputOption[] = [
|
||||
@@ -384,7 +412,7 @@ const ArrowModeAction = observer(() => {
|
||||
|
||||
return (
|
||||
<ToggleGroupMultipleInput
|
||||
title="Arrow Head"
|
||||
title={t('whiteboard/arrow-head')}
|
||||
options={StrokeTypeOptions}
|
||||
value={value}
|
||||
onValueChange={v => {
|
||||
@@ -401,6 +429,9 @@ const ArrowModeAction = observer(() => {
|
||||
|
||||
const TextStyleAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const shapes = filterShapeByAction<TextShape>('TextStyle')
|
||||
|
||||
const bold = shapes.every(s => s.props.fontWeight > 500)
|
||||
@@ -409,7 +440,7 @@ const TextStyleAction = observer(() => {
|
||||
return (
|
||||
<span className="flex gap-1">
|
||||
<ToggleInput
|
||||
tooltip="Bold"
|
||||
tooltip={t('whiteboard/bold')}
|
||||
className="tl-button"
|
||||
pressed={bold}
|
||||
onPressedChange={v => {
|
||||
@@ -425,7 +456,7 @@ const TextStyleAction = observer(() => {
|
||||
<TablerIcon name="bold" />
|
||||
</ToggleInput>
|
||||
<ToggleInput
|
||||
tooltip="Italic"
|
||||
tooltip={t('whiteboard/italic')}
|
||||
className="tl-button"
|
||||
pressed={italic}
|
||||
onPressedChange={v => {
|
||||
|
||||
@@ -21,7 +21,9 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
collisionRef,
|
||||
}: ContextMenuProps) {
|
||||
const app = useApp()
|
||||
const { handlers } = React.useContext(LogseqContext)
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
const rContent = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
const runAndTransition = (f: Function) => {
|
||||
@@ -64,26 +66,26 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
<ReactContextMenu.Item>
|
||||
<div className="tl-menu-button-row pb-0">
|
||||
<Button
|
||||
tooltip="Align left"
|
||||
tooltip={t('whiteboard/align-left')}
|
||||
onClick={() => runAndTransition(() => app.align(AlignType.Left))}
|
||||
>
|
||||
<TablerIcon name="layout-align-left" />
|
||||
</Button>
|
||||
<Button
|
||||
tooltip="Align center horizontally"
|
||||
tooltip={t('whiteboard/align-center-horizontally')}
|
||||
onClick={() => runAndTransition(() => app.align(AlignType.CenterHorizontal))}
|
||||
>
|
||||
<TablerIcon name="layout-align-center" />
|
||||
</Button>
|
||||
<Button
|
||||
tooltip="Align right"
|
||||
tooltip={t('whiteboard/align-right')}
|
||||
onClick={() => runAndTransition(() => app.align(AlignType.Right))}
|
||||
>
|
||||
<TablerIcon name="layout-align-right" />
|
||||
</Button>
|
||||
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />
|
||||
<Button
|
||||
tooltip="Distribute horizontally"
|
||||
tooltip={t('whiteboard/distribute-horizontally')}
|
||||
onClick={() =>
|
||||
runAndTransition(() => app.distribute(DistributeType.Horizontal))
|
||||
}
|
||||
@@ -93,26 +95,26 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
</div>
|
||||
<div className="tl-menu-button-row pt-0">
|
||||
<Button
|
||||
tooltip="Align top"
|
||||
tooltip={t('whiteboard/align-top')}
|
||||
onClick={() => runAndTransition(() => app.align(AlignType.Top))}
|
||||
>
|
||||
<TablerIcon name="layout-align-top" />
|
||||
</Button>
|
||||
<Button
|
||||
tooltip="Align center vertically"
|
||||
tooltip={t('whiteboard/align-center-vertically')}
|
||||
onClick={() => runAndTransition(() => app.align(AlignType.CenterVertical))}
|
||||
>
|
||||
<TablerIcon name="layout-align-middle" />
|
||||
</Button>
|
||||
<Button
|
||||
tooltip="Align bottom"
|
||||
tooltip={t('whiteboard/align-bottom')}
|
||||
onClick={() => runAndTransition(() => app.align(AlignType.Bottom))}
|
||||
>
|
||||
<TablerIcon name="layout-align-bottom" />
|
||||
</Button>
|
||||
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />
|
||||
<Button
|
||||
tooltip="Distribute vertically"
|
||||
tooltip={t('whiteboard/distribute-vertically')}
|
||||
onClick={() =>
|
||||
runAndTransition(() => app.distribute(DistributeType.Vertical))
|
||||
}
|
||||
@@ -127,7 +129,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
onClick={() => runAndTransition(app.packIntoRectangle)}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="layout-grid" />
|
||||
Pack into rectangle
|
||||
{t('whiteboard/pack-into-rectangle')}
|
||||
</ReactContextMenu.Item>
|
||||
<ReactContextMenu.Separator className="menu-separator" />
|
||||
</>
|
||||
@@ -138,7 +140,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(app.api.zoomToSelection)}
|
||||
>
|
||||
Zoom to fit
|
||||
{t('whiteboard/zoom-to-fit')}
|
||||
<KeyboardShortcut action="whiteboard/zoom-to-fit" />
|
||||
</ReactContextMenu.Item>
|
||||
<ReactContextMenu.Separator className="menu-separator" />
|
||||
@@ -155,7 +157,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
onClick={() => runAndTransition(app.api.unGroup)}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="ungroup" />
|
||||
Ungroup
|
||||
{t('whiteboard/ungroup')}
|
||||
<KeyboardShortcut action="whiteboard/ungroup" />
|
||||
</ReactContextMenu.Item>
|
||||
)}
|
||||
@@ -166,7 +168,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
onClick={() => runAndTransition(app.api.doGroup)}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="group" />
|
||||
Group
|
||||
{t('whiteboard/group')}
|
||||
<KeyboardShortcut action="whiteboard/group" />
|
||||
</ReactContextMenu.Item>
|
||||
)}
|
||||
@@ -181,7 +183,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
onClick={() => runAndTransition(app.cut)}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="cut" />
|
||||
Cut
|
||||
{t('whiteboard/cut')}
|
||||
</ReactContextMenu.Item>
|
||||
)}
|
||||
<ReactContextMenu.Item
|
||||
@@ -189,7 +191,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
onClick={() => runAndTransition(app.copy)}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="copy" />
|
||||
Copy
|
||||
{t('whiteboard/copy')}
|
||||
<KeyboardShortcut action="editor/copy" />
|
||||
</ReactContextMenu.Item>
|
||||
</>
|
||||
@@ -200,7 +202,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
onClick={() => runAndTransition(app.paste)}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="clipboard" />
|
||||
Paste
|
||||
{t('whiteboard/paste')}
|
||||
<div className="tl-menu-right-slot">
|
||||
<span className="keyboard-shortcut">
|
||||
<code>{MOD_KEY}</code> <code>v</code>
|
||||
@@ -213,7 +215,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(() => app.paste(undefined, true))}
|
||||
>
|
||||
Paste as link
|
||||
{t('whiteboard/paste-as-link')}
|
||||
<div className="tl-menu-right-slot">
|
||||
<span className="keyboard-shortcut">
|
||||
<code>{MOD_KEY}</code> <code>⇧</code> <code>v</code>
|
||||
@@ -239,7 +241,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="file-export" />
|
||||
Export
|
||||
{t('whiteboard/export')}
|
||||
<div className="tl-menu-right-slot">
|
||||
<span className="keyboard-shortcut"></span>
|
||||
</div>
|
||||
@@ -251,7 +253,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(app.api.selectAll)}
|
||||
>
|
||||
Select all
|
||||
{t('whiteboard/select-all')}
|
||||
<KeyboardShortcut action="editor/select-parent" />
|
||||
</ReactContextMenu.Item>
|
||||
{app.selectedShapes?.size > 1 && (
|
||||
@@ -259,29 +261,33 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(app.api.deselectAll)}
|
||||
>
|
||||
Deselect all
|
||||
</ReactContextMenu.Item>
|
||||
)}
|
||||
{!app.readOnly && app.selectedShapes?.size > 0 && app.selectedShapesArray?.some(s => !s.props.isLocked) && (
|
||||
<ReactContextMenu.Item
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(() => app.setLocked(true))}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="lock" />
|
||||
Lock
|
||||
<KeyboardShortcut action="whiteboard/lock" />
|
||||
</ReactContextMenu.Item>
|
||||
)}
|
||||
{!app.readOnly && app.selectedShapes?.size > 0 && app.selectedShapesArray?.some(s => s.props.isLocked) && (
|
||||
<ReactContextMenu.Item
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(() => app.setLocked(false))}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="lock-open" />
|
||||
Unlock
|
||||
<KeyboardShortcut action="whiteboard/unlock" />
|
||||
{t('whiteboard/deselect-all')}
|
||||
</ReactContextMenu.Item>
|
||||
)}
|
||||
{!app.readOnly &&
|
||||
app.selectedShapes?.size > 0 &&
|
||||
app.selectedShapesArray?.some(s => !s.props.isLocked) && (
|
||||
<ReactContextMenu.Item
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(() => app.setLocked(true))}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="lock" />
|
||||
{t('whiteboard/lock')}
|
||||
<KeyboardShortcut action="whiteboard/lock" />
|
||||
</ReactContextMenu.Item>
|
||||
)}
|
||||
{!app.readOnly &&
|
||||
app.selectedShapes?.size > 0 &&
|
||||
app.selectedShapesArray?.some(s => s.props.isLocked) && (
|
||||
<ReactContextMenu.Item
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(() => app.setLocked(false))}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="lock-open" />
|
||||
{t('whiteboard/unlock')}
|
||||
<KeyboardShortcut action="whiteboard/unlock" />
|
||||
</ReactContextMenu.Item>
|
||||
)}
|
||||
{app.selectedShapes?.size > 0 &&
|
||||
!app.readOnly &&
|
||||
app.selectedShapesArray?.some(s => !s.props.isLocked) && (
|
||||
@@ -291,7 +297,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
onClick={() => runAndTransition(app.api.deleteShapes)}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="backspace" />
|
||||
Delete
|
||||
{t('whiteboard/delete')}
|
||||
<KeyboardShortcut action="editor/delete" />
|
||||
</ReactContextMenu.Item>
|
||||
{app.selectedShapes?.size > 1 && !app.readOnly && (
|
||||
@@ -302,14 +308,14 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
onClick={() => runAndTransition(app.flipHorizontal)}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="flip-horizontal" />
|
||||
Flip horizontally
|
||||
{t('whiteboard/flip-horizontally')}
|
||||
</ReactContextMenu.Item>
|
||||
<ReactContextMenu.Item
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(app.flipVertical)}
|
||||
>
|
||||
<TablerIcon className="tl-menu-icon" name="flip-vertical" />
|
||||
Flip vertically
|
||||
{t('whiteboard/flip-vertically')}
|
||||
</ReactContextMenu.Item>
|
||||
</>
|
||||
)}
|
||||
@@ -320,14 +326,14 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(app.bringToFront)}
|
||||
>
|
||||
Move to front
|
||||
{t('whiteboard/move-to-front')}
|
||||
<KeyboardShortcut action="whiteboard/bring-to-front" />
|
||||
</ReactContextMenu.Item>
|
||||
<ReactContextMenu.Item
|
||||
className="tl-menu-item"
|
||||
onClick={() => runAndTransition(app.sendToBack)}
|
||||
>
|
||||
Move to back
|
||||
{t('whiteboard/move-to-back')}
|
||||
<KeyboardShortcut action="whiteboard/send-to-back" />
|
||||
</ReactContextMenu.Item>
|
||||
</>
|
||||
@@ -344,7 +350,7 @@ export const ContextMenu = observer(function ContextMenu({
|
||||
}
|
||||
}}
|
||||
>
|
||||
(Dev) Print shape props
|
||||
{t('whiteboard/dev-print-shape-props')}
|
||||
</ReactContextMenu.Item>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -3,6 +3,8 @@ import type { Side } from '@radix-ui/react-popper'
|
||||
import { ToolButton } from '../ToolButton'
|
||||
import * as Popover from '@radix-ui/react-popover'
|
||||
import { TablerIcon } from '../icons'
|
||||
import React from 'react'
|
||||
import { LogseqContext } from '../../lib/logseq-context'
|
||||
|
||||
interface GeometryToolsProps extends React.HTMLAttributes<HTMLElement> {
|
||||
popoverSide?: Side
|
||||
@@ -12,56 +14,72 @@ interface GeometryToolsProps extends React.HTMLAttributes<HTMLElement> {
|
||||
}
|
||||
|
||||
export const GeometryTools = observer(function GeometryTools({
|
||||
popoverSide = "left",
|
||||
popoverSide = 'left',
|
||||
setGeometry,
|
||||
activeGeometry,
|
||||
chevron = true,
|
||||
...rest}: GeometryToolsProps) {
|
||||
...rest
|
||||
}: GeometryToolsProps) {
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
|
||||
const geometries = [
|
||||
{
|
||||
id: 'box',
|
||||
icon: 'square',
|
||||
tooltip: 'Rectangle',
|
||||
tooltip: t('whiteboard/rectangle'),
|
||||
},
|
||||
{
|
||||
id: 'ellipse',
|
||||
icon: 'circle',
|
||||
tooltip: 'Circle',
|
||||
tooltip: t('whiteboard/circle'),
|
||||
},
|
||||
{
|
||||
id: 'polygon',
|
||||
icon: 'triangle',
|
||||
tooltip: 'Triangle',
|
||||
tooltip: t('whiteboard/triangle'),
|
||||
},
|
||||
]
|
||||
|
||||
const shapes = {
|
||||
id: 'shapes',
|
||||
icon: 'triangle-square-circle',
|
||||
tooltip: 'Shape',
|
||||
tooltip: t('whiteboard/shape'),
|
||||
}
|
||||
|
||||
const activeTool = activeGeometry ? geometries.find(geo => geo.id === activeGeometry) : shapes
|
||||
|
||||
return (
|
||||
<Popover.Root>
|
||||
<Popover.Trigger asChild >
|
||||
<Popover.Trigger asChild>
|
||||
<div {...rest} className="tl-geometry-tools-pane-anchor">
|
||||
<ToolButton {...activeTool} tooltipSide={popoverSide} />
|
||||
{chevron &&
|
||||
{chevron && (
|
||||
<TablerIcon
|
||||
data-selected={activeGeometry}
|
||||
className="tl-popover-indicator"
|
||||
name="chevron-down-left"
|
||||
/>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</Popover.Trigger>
|
||||
|
||||
<Popover.Content className="tl-popover-content" side={popoverSide} sideOffset={15}>
|
||||
<div className={`tl-toolbar tl-geometry-toolbar ${["left", "right"].includes(popoverSide) ? "flex-col" : "flex-row" }`}>
|
||||
<div
|
||||
className={`tl-toolbar tl-geometry-toolbar ${
|
||||
['left', 'right'].includes(popoverSide) ? 'flex-col' : 'flex-row'
|
||||
}`}
|
||||
>
|
||||
{geometries.map(props => (
|
||||
<ToolButton key={props.id} id={props.id} icon={props.icon} tooltip={activeGeometry ? props.tooltip : ''} handleClick={setGeometry} tooltipSide={popoverSide} />
|
||||
<ToolButton
|
||||
key={props.id}
|
||||
id={props.id}
|
||||
icon={props.icon}
|
||||
tooltip={activeGeometry ? props.tooltip : ''}
|
||||
handleClick={setGeometry}
|
||||
tooltipSide={popoverSide}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,9 +7,13 @@ import { GeometryTools } from '../GeometryTools'
|
||||
import { ColorInput } from '../inputs/ColorInput'
|
||||
import { ScaleInput } from '../inputs/ScaleInput'
|
||||
import * as Separator from '@radix-ui/react-separator'
|
||||
import { LogseqContext } from '../../lib/logseq-context'
|
||||
|
||||
export const PrimaryTools = observer(function PrimaryTools() {
|
||||
const app = useApp()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
|
||||
const handleSetColor = React.useCallback((color: string) => {
|
||||
app.api.setColor(color)
|
||||
@@ -37,50 +41,50 @@ export const PrimaryTools = observer(function PrimaryTools() {
|
||||
<div className="tl-toolbar tl-tools-floating-panel">
|
||||
<ToolButton
|
||||
handleClick={() => app.selectTool('select')}
|
||||
tooltip="Select"
|
||||
tooltip={t('whiteboard/select')}
|
||||
id="select"
|
||||
icon="select-cursor"
|
||||
/>
|
||||
<ToolButton
|
||||
handleClick={() => app.selectTool('move')}
|
||||
tooltip="Pan"
|
||||
tooltip={t('whiteboard/pan')}
|
||||
id="move"
|
||||
icon={app.isIn('move.panning') ? 'hand-grab' : 'hand-stop'}
|
||||
/>
|
||||
<Separator.Root className="tl-toolbar-separator" orientation="horizontal" />
|
||||
<ToolButton
|
||||
handleClick={() => app.selectTool('logseq-portal')}
|
||||
tooltip="Add block or page"
|
||||
tooltip={t('whiteboard/add-block-or-page')}
|
||||
id="logseq-portal"
|
||||
icon="circle-plus"
|
||||
/>
|
||||
<ToolButton
|
||||
handleClick={() => app.selectTool('pencil')}
|
||||
tooltip="Draw"
|
||||
tooltip={t('whiteboard/draw')}
|
||||
id="pencil"
|
||||
icon="ballpen"
|
||||
/>
|
||||
<ToolButton
|
||||
handleClick={() => app.selectTool('highlighter')}
|
||||
tooltip="Highlight"
|
||||
tooltip={t('whiteboard/highlight')}
|
||||
id="highlighter"
|
||||
icon="highlight"
|
||||
/>
|
||||
<ToolButton
|
||||
handleClick={() => app.selectTool('erase')}
|
||||
tooltip="Eraser"
|
||||
tooltip={t('whiteboard/eraser')}
|
||||
id="erase"
|
||||
icon="eraser"
|
||||
/>
|
||||
<ToolButton
|
||||
handleClick={() => app.selectTool('line')}
|
||||
tooltip="Connector"
|
||||
tooltip={t('whiteboard/connector')}
|
||||
id="line"
|
||||
icon="connector"
|
||||
/>
|
||||
<ToolButton
|
||||
handleClick={() => app.selectTool('text')}
|
||||
tooltip="Text"
|
||||
tooltip={t('whiteboard/text')}
|
||||
id="text"
|
||||
icon="text"
|
||||
/>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { BlockLink } from '../BlockLink'
|
||||
export const QuickLinks: TLQuickLinksComponent<Shape> = observer(({ shape }) => {
|
||||
const app = useApp()
|
||||
const { handlers } = React.useContext(LogseqContext)
|
||||
const t = handlers.t
|
||||
const links = React.useMemo(() => {
|
||||
const links = [...(shape.props.refs ?? [])].map<[ref: string, showReferenceContent: boolean]>(
|
||||
// user added links should show the referenced block content
|
||||
@@ -30,7 +31,7 @@ export const QuickLinks: TLQuickLinksComponent<Shape> = observer(({ shape }) =>
|
||||
if (links.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="tl-quick-links" title="Shape Quick Links">
|
||||
<div className="tl-quick-links" title={t('whiteboard/shape-quick-links')}>
|
||||
{links.map(([ref, showReferenceContent]) => {
|
||||
return (
|
||||
<div key={ref} className="tl-quick-links-row">
|
||||
|
||||
@@ -101,6 +101,7 @@ export const LogseqQuickSearch = observer(
|
||||
)
|
||||
const rInput = React.useRef<HTMLInputElement>(null)
|
||||
const { handlers, renderers } = React.useContext(LogseqContext)
|
||||
const t = handlers.t
|
||||
|
||||
const finishSearching = React.useCallback((id: string) => {
|
||||
onChange(id)
|
||||
@@ -172,11 +173,11 @@ export const LogseqQuickSearch = observer(
|
||||
<LogseqTypeTag active type="BA" />
|
||||
{q.length > 0 ? (
|
||||
<>
|
||||
<strong>New block:</strong>
|
||||
<strong>{t('whiteboard/new-block')}</strong>
|
||||
{q}
|
||||
</>
|
||||
) : (
|
||||
<strong>New block</strong>
|
||||
<strong>{t('whiteboard/new-block-no-colon')}</strong>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
@@ -195,7 +196,7 @@ export const LogseqQuickSearch = observer(
|
||||
element: (
|
||||
<div className="tl-quick-search-option-row">
|
||||
<LogseqTypeTag active type="PA" />
|
||||
<strong>New page:</strong>
|
||||
<strong>{t('whiteboard/new-page')}</strong>
|
||||
{q}
|
||||
</div>
|
||||
),
|
||||
@@ -210,7 +211,7 @@ export const LogseqQuickSearch = observer(
|
||||
element: (
|
||||
<div className="tl-quick-search-option-row">
|
||||
<LogseqTypeTag active type="WA" />
|
||||
<strong>New whiteboard:</strong>
|
||||
<strong>{t('whiteboard/new-whiteboard')}</strong>
|
||||
{q}
|
||||
</div>
|
||||
),
|
||||
@@ -230,7 +231,7 @@ export const LogseqQuickSearch = observer(
|
||||
element: (
|
||||
<div className="tl-quick-search-option-row">
|
||||
<LogseqTypeTag type="BS" />
|
||||
Search only blocks
|
||||
{t('whiteboard/search-only-blocks')}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -243,7 +244,7 @@ export const LogseqQuickSearch = observer(
|
||||
element: (
|
||||
<div className="tl-quick-search-option-row">
|
||||
<LogseqTypeTag type="PS" />
|
||||
Search only pages
|
||||
{t('whiteboard/search-only-pages')}
|
||||
</div>
|
||||
),
|
||||
}
|
||||
@@ -302,10 +303,7 @@ export const LogseqQuickSearch = observer(
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="tl-quick-search-option-row">
|
||||
Cache is outdated. Please click the 'Re-index' button in the graph's dropdown
|
||||
menu.
|
||||
</div>
|
||||
<div className="tl-quick-search-option-row">{t('whiteboard/cache-outdated')}</div>
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import { TablerIcon } from '../icons'
|
||||
import { PopoverButton } from '../PopoverButton'
|
||||
import { Tooltip } from '../Tooltip'
|
||||
import React from 'react'
|
||||
import { LogseqContext } from '../../lib/logseq-context'
|
||||
|
||||
interface ColorInputProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
color?: string
|
||||
@@ -22,6 +23,10 @@ export function ColorInput({
|
||||
setOpacity,
|
||||
...rest
|
||||
}: ColorInputProps) {
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
|
||||
function renderColor(color?: string) {
|
||||
return color ? (
|
||||
<div className="tl-color-bg" style={{ backgroundColor: color }}>
|
||||
@@ -57,7 +62,7 @@ export function ColorInput({
|
||||
arrow
|
||||
side={popoverSide}
|
||||
label={
|
||||
<Tooltip content={'Color'} side={popoverSide} sideOffset={14}>
|
||||
<Tooltip content={t('whiteboard/color')} side={popoverSide} sideOffset={14}>
|
||||
{renderColor(color)}
|
||||
</Tooltip>
|
||||
}
|
||||
@@ -82,7 +87,7 @@ export function ColorInput({
|
||||
className="color-input cursor-pointer"
|
||||
id="tl-custom-color-input"
|
||||
type="color"
|
||||
value={isHexColor(color) ? color : "#000000"}
|
||||
value={isHexColor(color) ? color : '#000000'}
|
||||
onChange={handleChangeDebounced}
|
||||
style={{ opacity: isBuiltInColor(color) ? 0 : 1 }}
|
||||
{...rest}
|
||||
@@ -90,7 +95,7 @@ export function ColorInput({
|
||||
</div>
|
||||
</div>
|
||||
<label htmlFor="tl-custom-color-input" className="cursor-pointer">
|
||||
Select custom color
|
||||
{t('whiteboard/select-custom-color')}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -101,7 +106,7 @@ export function ColorInput({
|
||||
onValueCommit={value => setOpacity(value[0])}
|
||||
max={1}
|
||||
step={0.1}
|
||||
aria-label="Opacity"
|
||||
aria-label={t('whiteboard/opacity')}
|
||||
className="tl-slider-root"
|
||||
>
|
||||
<Slider.Track className="tl-slider-track">
|
||||
|
||||
@@ -2,6 +2,8 @@ import { SelectInput, type SelectOption } from '../inputs/SelectInput'
|
||||
import type { Side } from '@radix-ui/react-popper'
|
||||
import type { SizeLevel } from '../../lib'
|
||||
import { useApp } from '@tldraw/react'
|
||||
import React from 'react'
|
||||
import { LogseqContext } from '../../lib/logseq-context'
|
||||
|
||||
interface ScaleInputProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
scaleLevel?: SizeLevel
|
||||
@@ -11,37 +13,40 @@ interface ScaleInputProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
|
||||
export function ScaleInput({ scaleLevel, compact, popoverSide, ...rest }: ScaleInputProps) {
|
||||
const app = useApp<Shape>()
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
|
||||
const sizeOptions: SelectOption[] = [
|
||||
{
|
||||
label: compact ? 'XS' : 'Extra Small',
|
||||
label: compact ? 'XS' : t('whiteboard/extra-small'),
|
||||
value: 'xs',
|
||||
},
|
||||
{
|
||||
label: compact ? 'SM' : 'Small',
|
||||
label: compact ? 'SM' : t('whiteboard/small'),
|
||||
value: 'sm',
|
||||
},
|
||||
{
|
||||
label: compact ? 'MD' : 'Medium',
|
||||
label: compact ? 'MD' : t('whiteboard/medium'),
|
||||
value: 'md',
|
||||
},
|
||||
{
|
||||
label: compact ? 'LG' : 'Large',
|
||||
label: compact ? 'LG' : t('whiteboard/large'),
|
||||
value: 'lg',
|
||||
},
|
||||
{
|
||||
label: compact ? 'XL' : 'Extra Large',
|
||||
label: compact ? 'XL' : t('whiteboard/extra-large'),
|
||||
value: 'xl',
|
||||
},
|
||||
{
|
||||
label: compact ? 'XXL' : 'Huge',
|
||||
label: compact ? 'XXL' : t('whiteboard/huge'),
|
||||
value: 'xxl',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<SelectInput
|
||||
tooltip="Scale level"
|
||||
tooltip={t('whiteboard/scale-level')}
|
||||
options={sizeOptions}
|
||||
value={scaleLevel}
|
||||
popoverSide={popoverSide}
|
||||
|
||||
@@ -34,6 +34,7 @@ function ShapeLinkItem({
|
||||
}) {
|
||||
const app = useApp<Shape>()
|
||||
const { handlers } = React.useContext(LogseqContext)
|
||||
const t = handlers.t
|
||||
|
||||
return (
|
||||
<div className="tl-shape-links-panel-item color-level relative">
|
||||
@@ -42,12 +43,16 @@ function ShapeLinkItem({
|
||||
</div>
|
||||
<div className="flex-1" />
|
||||
{handlers.getBlockPageName(id) !== app.currentPage.name && (
|
||||
<Button tooltip="Open Page" type="button" onClick={() => handlers?.redirectToPage(id)}>
|
||||
<Button
|
||||
tooltip={t('whiteboard/open-page')}
|
||||
type="button"
|
||||
onClick={() => handlers?.redirectToPage(id)}
|
||||
>
|
||||
<TablerIcon name="open-as-page" />
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
tooltip="Open Page in Right Sidebar"
|
||||
tooltip={t('whiteboard/open-page-in-sidebar')}
|
||||
type="button"
|
||||
onClick={() => handlers?.sidebarAddBlock(id, type === 'B' ? 'block' : 'page')}
|
||||
>
|
||||
@@ -56,7 +61,7 @@ function ShapeLinkItem({
|
||||
{onRemove && (
|
||||
<Button
|
||||
className="tl-shape-links-panel-item-remove-button"
|
||||
tooltip="Remove link"
|
||||
tooltip={t('whiteboard/remove-link')}
|
||||
type="button"
|
||||
onClick={onRemove}
|
||||
>
|
||||
@@ -76,6 +81,10 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
|
||||
onRefsChange,
|
||||
...rest
|
||||
}: ShapeLinksInputProps) {
|
||||
const {
|
||||
handlers: { t },
|
||||
} = React.useContext(LogseqContext)
|
||||
|
||||
const noOfLinks = refs.length + (pageId ? 1 : 0)
|
||||
const canAddLink = refs.length === 0
|
||||
|
||||
@@ -94,7 +103,7 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
|
||||
align="start"
|
||||
alignOffset={-6}
|
||||
label={
|
||||
<Tooltip content={'Link'} sideOffset={14}>
|
||||
<Tooltip content={t('whiteboard/link')} sideOffset={14}>
|
||||
<div className="flex gap-1 relative items-center justify-center px-1">
|
||||
<TablerIcon name={noOfLinks > 0 ? 'link' : 'add-link'} />
|
||||
{noOfLinks > 0 && <div className="tl-shape-links-count">{noOfLinks}</div>}
|
||||
@@ -107,7 +116,7 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
|
||||
<div className="tl-shape-links-reference-panel">
|
||||
<div className="text-base inline-flex gap-1 items-center">
|
||||
<TablerIcon className="opacity-50" name="internal-link" />
|
||||
References
|
||||
{t('whiteboard/references')}
|
||||
</div>
|
||||
<ShapeLinkItem type={portalType} id={pageId} />
|
||||
</div>
|
||||
@@ -115,7 +124,7 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
|
||||
<div className="tl-shape-links-panel color-level">
|
||||
<div className="text-base inline-flex gap-1 items-center">
|
||||
<TablerIcon className="opacity-50" name="add-link" />
|
||||
Link to any page or block
|
||||
{t('whiteboard/link-to-any-page-or-block')}
|
||||
</div>
|
||||
|
||||
{canAddLink && (
|
||||
@@ -124,7 +133,7 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
|
||||
width: 'calc(100% - 46px)',
|
||||
marginLeft: '46px',
|
||||
}}
|
||||
placeholder="Start typing to search..."
|
||||
placeholder={t('whiteboard/start-typing-to-search')}
|
||||
onChange={addNewRef}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -42,6 +42,7 @@ export interface LogseqContextValue {
|
||||
}>
|
||||
}
|
||||
handlers: {
|
||||
t: (key: string) => any
|
||||
search: (
|
||||
query: string,
|
||||
filters: { 'pages?': boolean; 'blocks?': boolean; 'files?': boolean }
|
||||
|
||||
Reference in New Issue
Block a user