enhance: support whiteboard translations

This commit is contained in:
Konstantinos Kaloutas
2023-06-02 15:49:41 +03:00
committed by Gabriel Horner
parent 776495d187
commit fbf365ceff
14 changed files with 281 additions and 114 deletions

View File

@@ -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" />

View File

@@ -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 => {

View File

@@ -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>
)}
</>

View File

@@ -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>

View File

@@ -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"
/>

View File

@@ -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">

View File

@@ -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>
),
}
})

View File

@@ -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">

View File

@@ -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}

View File

@@ -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}
/>
)}

View File

@@ -42,6 +42,7 @@ export interface LogseqContextValue {
}>
}
handlers: {
t: (key: string) => any
search: (
query: string,
filters: { 'pages?': boolean; 'blocks?': boolean; 'files?': boolean }