Skip to content

Commit

Permalink
Feat: Use tooltips instead of titles on whiteboard buttons (#7643)
Browse files Browse the repository at this point in the history
 Move select and pan to main toolbar
 Create tooltip component
 Add tooltip support to button, toggle, toggle groups, select and color input
 Style shortcuts on primary tools
  • Loading branch information
sprocketc committed Dec 12, 2022
1 parent 4bef35a commit 109b491
Show file tree
Hide file tree
Showing 28 changed files with 239 additions and 128 deletions.
9 changes: 1 addition & 8 deletions e2e-tests/whiteboards.spec.ts
Expand Up @@ -80,18 +80,11 @@ test('set whiteboard title', async ({ page }) => {
)
})

test('select rectangle tool', async ({ page }) => {
await page.keyboard.press('7')
await expect(
page.locator('.tl-geometry-tools-pane-anchor [title*="Rectangle"]')
).toHaveAttribute('data-selected', 'true')
})

test('draw a rectangle', async ({ page }) => {
const canvas = await page.waitForSelector('.logseq-tldraw')
const bounds = (await canvas.boundingBox())!

await page.keyboard.press('7')
await page.keyboard.press('r')

await page.mouse.move(bounds.x + 5, bounds.y + 5)
await page.mouse.down()
Expand Down
1 change: 1 addition & 0 deletions tldraw/apps/tldraw-logseq/package.json
Expand Up @@ -19,6 +19,7 @@
"@radix-ui/react-switch": "^1.0.1",
"@radix-ui/react-toggle": "^1.0.1",
"@radix-ui/react-toggle-group": "^1.0.1",
"@radix-ui/react-tooltip": "^1.0.2",
"@tldraw/core": "2.0.0-alpha.1",
"@tldraw/react": "2.0.0-alpha.1",
"@tldraw/vec": "2.0.0-alpha.1",
Expand Down
15 changes: 4 additions & 11 deletions tldraw/apps/tldraw-logseq/src/components/ActionBar/ActionBar.tsx
Expand Up @@ -31,26 +31,19 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
return (
<div className="tl-action-bar">
<div className="tl-toolbar tl-history-bar">
<ToolButton title="Select" id="select" icon="select-cursor" />
<ToolButton
title="Move"
id="move"
icon={app.isIn('move.panning') ? 'hand-grab' : 'hand-stop'}
/>
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />
<Button title="Undo" onClick={undo}>
<Button tooltip="Undo" onClick={undo}>
<TablerIcon name="arrow-back-up" />
</Button>
<Button title="Redo" onClick={redo}>
<Button tooltip="Redo" onClick={redo}>
<TablerIcon name="arrow-forward-up" />
</Button>
</div>

<div className="tl-toolbar tl-zoom-bar">
<Button title="Zoom in" onClick={zoomIn} id="tl-zoom-in">
<Button tooltip="Zoom in" onClick={zoomIn} id="tl-zoom-in">
<TablerIcon name="plus" />
</Button>
<Button title="Zoom out" onClick={zoomOut} id="tl-zoom-out">
<Button tooltip="Zoom out" onClick={zoomOut} id="tl-zoom-out">
<TablerIcon name="minus" />
</Button>
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />
Expand Down
12 changes: 10 additions & 2 deletions tldraw/apps/tldraw-logseq/src/components/Button/Button.tsx
@@ -1,7 +1,15 @@
import { Tooltip } from '../Tooltip'
import type { Side } from '@radix-ui/react-popper'
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode
tooltip?: React.ReactNode
tooltipSide?: Side
}

export function Button({ className, ...rest }: ButtonProps) {
return <button className={'tl-button ' + (className ?? '')} {...rest} />
export function Button({ className, tooltip, tooltipSide, ...rest }: ButtonProps) {
return (
<Tooltip content={tooltip} side={tooltipSide}>
<button className={'tl-button ' + (className ?? '')} {...rest} />
</Tooltip>
)
}
Expand Up @@ -15,7 +15,6 @@ import type {
TextShape,
YouTubeShape,
} from '../../lib'
import { LogseqContext } from '../../lib/logseq-context'
import { Button } from '../Button'
import { TablerIcon } from '../icons'
import { ColorInput } from '../inputs/ColorInput'
Expand Down Expand Up @@ -96,7 +95,7 @@ const EditAction = observer(() => {
return (
<Button
type="button"
title="Edit"
tooltip="Edit"
onClick={() => {
app.api.editShape(shape)
if (shape.props.type === 'logseq-portal') {
Expand Down Expand Up @@ -161,10 +160,12 @@ const LogseqPortalViewModeAction = observer(() => {
{
value: '1',
icon: 'object-compact',
tooltip: 'Collapse',
},
{
value: '0',
icon: 'object-expanded',
tooltip: 'Expand',
},
]
return (
Expand Down Expand Up @@ -214,7 +215,7 @@ const ScaleLevelAction = observer(() => {
]
return (
<SelectInput
title="Scale Level"
tooltip="Scale Level"
options={sizeOptions}
value={scaleLevel}
onValueChange={v => {
Expand Down Expand Up @@ -242,7 +243,7 @@ const IFrameSourceAction = observer(() => {

return (
<span className="flex gap-3">
<Button title="Reload" type="button" onClick={handleReload}>
<Button tooltip="Reload" type="button" onClick={handleReload}>
<TablerIcon name="refresh" />
</Button>
<TextInput
Expand All @@ -251,7 +252,7 @@ const IFrameSourceAction = observer(() => {
value={`${shape.props.url}`}
onChange={handleChange}
/>
<Button title="Open website url" type="button" onClick={() => window.open(shape.props.url)}>
<Button tooltip="Open website url" type="button" onClick={() => window.open(shape.props.url)}>
<TablerIcon name="external-link" />
</Button>
</span>
Expand All @@ -275,7 +276,7 @@ const YoutubeLinkAction = observer(() => {
onChange={handleChange}
/>
<Button
title="Open YouTube Link"
tooltip="Open YouTube Link"
type="button"
onClick={() => window.logseq?.api?.open_external_link?.(shape.props.url)}
>
Expand All @@ -299,12 +300,7 @@ const NoFillAction = observer(() => {
const noFill = shapes.every(s => s.props.noFill)

return (
<ToggleInput
title="Fill Toggle"
className="tl-button"
pressed={noFill}
onPressedChange={handleChange}
>
<ToggleInput title="Fill" className="tl-button" pressed={noFill} onPressedChange={handleChange}>
<TablerIcon name={noFill ? 'droplet-off' : 'droplet'} />
</ToggleInput>
)
Expand Down Expand Up @@ -334,7 +330,6 @@ const SwatchAction = observer(() => {
const color = shapes[0].props.noFill ? shapes[0].props.stroke : shapes[0].props.fill
return (
<ColorInput
title="Color Picker"
popoverSide="top"
color={color}
opacity={shapes[0].props.opacity}
Expand All @@ -354,10 +349,12 @@ const StrokeTypeAction = observer(() => {
{
value: 'line',
icon: 'circle',
tooltip: 'Solid',
},
{
value: 'dashed',
icon: 'circle-dashed',
tooltip: 'Dashed',
},
]

Expand Down
Expand Up @@ -58,26 +58,26 @@ export const ContextMenu = observer(function ContextMenu({
<ReactContextMenu.Item>
<div className="tl-menu-button-row pb-0">
<Button
title="Align left"
tooltip="Align left"
onClick={() => runAndTransition(() => app.align(AlignType.Left))}
>
<TablerIcon name="layout-align-left" />
</Button>
<Button
title="Align center horizontally"
tooltip="Align center horizontally"
onClick={() => runAndTransition(() => app.align(AlignType.CenterHorizontal))}
>
<TablerIcon name="layout-align-center" />
</Button>
<Button
title="Align right"
tooltip="Align right"
onClick={() => runAndTransition(() => app.align(AlignType.Right))}
>
<TablerIcon name="layout-align-right" />
</Button>
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />
<Button
title="Distribute horizontally"
tooltip="Distribute horizontally"
onClick={() =>
runAndTransition(() => app.distribute(DistributeType.Horizontal))
}
Expand All @@ -87,26 +87,26 @@ export const ContextMenu = observer(function ContextMenu({
</div>
<div className="tl-menu-button-row pt-0">
<Button
title="Align top"
tooltip="Align top"
onClick={() => runAndTransition(() => app.align(AlignType.Top))}
>
<TablerIcon name="layout-align-top" />
</Button>
<Button
title="Align center vertically"
tooltip="Align center vertically"
onClick={() => runAndTransition(() => app.align(AlignType.CenterVertical))}
>
<TablerIcon name="layout-align-middle" />
</Button>
<Button
title="Align bottom"
tooltip="Align bottom"
onClick={() => runAndTransition(() => app.align(AlignType.Bottom))}
>
<TablerIcon name="layout-align-bottom" />
</Button>
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />
<Button
title="Distribute vertically"
tooltip="Distribute vertically"
onClick={() => runAndTransition(() => app.distribute(DistributeType.Vertical))}
>
<TablerIcon name="layout-distribute-horizontal" />
Expand Down Expand Up @@ -165,17 +165,17 @@ export const ContextMenu = observer(function ContextMenu({
</div>
</ReactContextMenu.Item>
{app.selectedShapes?.size === 1 && (
<ReactContextMenu.Item
className="tl-menu-item"
onClick={() => runAndTransition(() => app.paste(undefined, true))}
>
Paste as link
<div className="tl-menu-right-slot">
<span className="keyboard-shortcut">
<code>{MOD_KEY}</code> <code></code> <code>V</code>
</span>
</div>
</ReactContextMenu.Item>
<ReactContextMenu.Item
className="tl-menu-item"
onClick={() => runAndTransition(() => app.paste(undefined, true))}
>
Paste as link
<div className="tl-menu-right-slot">
<span className="keyboard-shortcut">
<code>{MOD_KEY}</code> <code></code> <code>V</code>
</span>
</div>
</ReactContextMenu.Item>
)}
<ReactContextMenu.Separator className="menu-separator" />
<ReactContextMenu.Item
Expand Down
Expand Up @@ -10,17 +10,17 @@ export const GeometryTools = observer(function GeometryTools() {
{
id: 'box',
icon: 'square',
title: 'Rectangle',
tooltip: 'Rectangle',
},
{
id: 'ellipse',
icon: 'circle',
title: 'Circle',
tooltip: 'Circle',
},
{
id: 'polygon',
icon: 'triangle',
title: 'Triangle',
tooltip: 'Triangle',
},
]

Expand All @@ -37,13 +37,15 @@ export const GeometryTools = observer(function GeometryTools() {

return (
<Popover.Root>
<Popover.Trigger className="tl-geometry-tools-pane-anchor">
<ToolButton {...geometries.find(geo => geo.id === activeGeomId)!} />
<TablerIcon
data-selected={geometries.some(geo => geo.id === app.selectedTool.id)}
className="tl-popover-indicator"
name="chevron-down-left"
/>
<Popover.Trigger asChild>
<div className="tl-geometry-tools-pane-anchor">
<ToolButton {...geometries.find(geo => geo.id === activeGeomId)!} />
<TablerIcon
data-selected={geometries.some(geo => geo.id === app.selectedTool.id)}
className="tl-popover-indicator"
name="chevron-down-left"
/>
</div>
</Popover.Trigger>

<Popover.Content className="tl-popover-content" side="left" sideOffset={15}>
Expand Down
Expand Up @@ -16,25 +16,26 @@ export const PrimaryTools = observer(function PrimaryTools() {
return (
<div className="tl-primary-tools">
<div className="tl-toolbar tl-tools-floating-panel">
<ToolButton title="Add block or page" id="logseq-portal" icon="circle-plus" />
<ToolButton tooltip="Select" id="select" icon="select-cursor" />
<ToolButton
tooltip="Move"
id="move"
icon={app.isIn('move.panning') ? 'hand-grab' : 'hand-stop'}
/>
<Separator.Root className="tl-toolbar-separator" orientation="horizontal" />
<ToolButton title="Draw" id="pencil" icon="ballpen" />
<ToolButton title="Highlight" id="highlighter" icon="highlight" />
<ToolButton title="Eraser" id="erase" icon="eraser" />
<ToolButton title="Connector" id="line" icon="connector" />
<ToolButton title="Text" id="text" icon="text" />
<ToolButton tooltip="Add block or page" id="logseq-portal" icon="circle-plus" />
<ToolButton tooltip="Draw" id="pencil" icon="ballpen" />
<ToolButton tooltip="Highlight" id="highlighter" icon="highlight" />
<ToolButton tooltip="Eraser" id="erase" icon="eraser" />
<ToolButton tooltip="Connector" id="line" icon="connector" />
<ToolButton tooltip="Text" id="text" icon="text" />
<GeometryTools />
<Separator.Root
className="tl-toolbar-separator"
orientation="horizontal"
style={{ margin: '0 -4px' }}
/>
<ColorInput
title="Color Picker"
popoverSide="left"
color={app.settings.color}
setColor={handleSetColor}
/>
<ColorInput popoverSide="left" color={app.settings.color} setColor={handleSetColor} />
</div>
</div>
)
Expand Down
22 changes: 18 additions & 4 deletions tldraw/apps/tldraw-logseq/src/components/ToolButton/ToolButton.tsx
Expand Up @@ -8,9 +8,10 @@ import { TablerIcon } from '../icons'
export interface ToolButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
id: string
icon: string | React.ReactNode
tooltip: string
}

export const ToolButton = observer(({ id, icon, title, ...props }: ToolButtonProps) => {
export const ToolButton = observer(({ id, icon, tooltip, ...props }: ToolButtonProps) => {
const app = useApp()

const handleToolClick = React.useCallback(
Expand All @@ -24,13 +25,26 @@ export const ToolButton = observer(({ id, icon, title, ...props }: ToolButtonPro
// Tool must exist
const Tool = [...app.Tools, TLSelectTool, TLMoveTool]?.find(T => T.id === id)

const shortcut = ((Tool as any)['shortcut'] as string[])?.join(', ').toUpperCase()
const shortcuts = (Tool as any)['shortcut']

const tooltipContent = shortcuts ? (
<>
{tooltip}
<span className="ml-2 keyboard-shortcut">
{shortcuts
.map((shortcut: string, idx: number) => <code key={idx}>{shortcut.toUpperCase()}</code>)
.reduce((prev: React.ReactNode, curr: React.ReactNode) => [prev, ' | ', curr])}
</span>
</>
) : (
{ tooltip }
)

const titleWithShortcut = shortcut ? `${title} - ${shortcut}` : title
return (
<Button
{...props}
title={titleWithShortcut}
tooltipSide="left"
tooltip={tooltipContent}
data-tool={id}
data-selected={id === app.selectedTool.id}
onClick={handleToolClick}
Expand Down

0 comments on commit 109b491

Please sign in to comment.