PrimaryTools.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { TLMoveTool, TLSelectTool } from '@tldraw/core'
  2. import { useApp } from '@tldraw/react'
  3. import { observer } from 'mobx-react-lite'
  4. import * as React from 'react'
  5. import { Button } from '../Button'
  6. import { TablerIcon, LogseqIcon } from '../icons'
  7. interface ToolButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  8. id: string
  9. icon: string | React.ReactNode
  10. }
  11. const ToolButton = observer(({ id, icon, title, ...props }: ToolButtonProps) => {
  12. const app = useApp()
  13. const handleToolClick = React.useCallback(
  14. (e: React.MouseEvent<HTMLButtonElement>) => {
  15. const tool = e.currentTarget.dataset.tool
  16. if (tool) app.selectTool(tool)
  17. },
  18. [app]
  19. )
  20. // Tool must exist
  21. const Tool = [...app.Tools, TLSelectTool, TLMoveTool]?.find(T => T.id === id)
  22. const shortcut = ((Tool as any)['shortcut'] as string[])?.[0]
  23. const titleWithShortcut = shortcut ? `${title} (${shortcut})` : title
  24. return (
  25. <Button
  26. {...props}
  27. title={titleWithShortcut}
  28. data-tool={id}
  29. data-selected={id === app.selectedTool.id}
  30. onClick={handleToolClick}
  31. >
  32. {typeof icon === 'string' ? <TablerIcon name={icon} /> : icon}
  33. </Button>
  34. )
  35. })
  36. const GeometryToolButtons = observer(() => {
  37. const geometries = [
  38. {
  39. id: 'box',
  40. icon: 'square',
  41. title: 'Rectangle',
  42. },
  43. {
  44. id: 'ellipse',
  45. icon: 'circle',
  46. title: 'Circle',
  47. },
  48. {
  49. id: 'polygon',
  50. icon: 'triangle',
  51. title: 'Triangle',
  52. },
  53. ]
  54. const app = useApp()
  55. const [activeGeomId, setActiveGeomId] = React.useState(
  56. () => (geometries.find(geo => geo.id === app.selectedTool.id) ?? geometries[0]).id
  57. )
  58. const [paneActive, setPaneActive] = React.useState(false)
  59. React.useEffect(() => {
  60. setActiveGeomId(prevId => {
  61. return geometries.find(geo => geo.id === app.selectedTool.id)?.id ?? prevId
  62. })
  63. }, [app.selectedTool.id])
  64. return (
  65. <div
  66. className="tl-geometry-tools-pane-anchor"
  67. onMouseEnter={() => setPaneActive(true)}
  68. onMouseLeave={() => setPaneActive(false)}
  69. >
  70. {<ToolButton {...geometries.find(geo => geo.id === activeGeomId)!} />}
  71. {paneActive && (
  72. <div className="tl-geometry-tools-pane">
  73. {geometries.map(props => (
  74. <ToolButton key={props.id} {...props} />
  75. ))}
  76. </div>
  77. )}
  78. </div>
  79. )
  80. })
  81. export const PrimaryTools = observer(function PrimaryTools() {
  82. const app = useApp()
  83. return (
  84. <div className="tl-primary-tools">
  85. <div className="tl-tools-floating-panel" data-tool-locked={app.settings.isToolLocked}>
  86. <ToolButton title="Select" id="select" icon="select-cursor" />
  87. <ToolButton
  88. title="Move"
  89. id="move"
  90. icon={app.isIn('move.panning') ? 'hand-grab' : 'hand-stop'}
  91. />
  92. <ToolButton title="Draw" id="pencil" icon="ballpen" />
  93. <ToolButton title="Highlight" id="highlighter" icon="highlight" />
  94. <ToolButton title="Eraser" id="erase" icon="eraser" />
  95. <ToolButton title="Connector" id="line" icon="connector" />
  96. <ToolButton title="Text" id="text" icon="text" />
  97. <GeometryToolButtons />
  98. <ToolButton title="Logseq Portal" id="logseq-portal" icon={<LogseqIcon />} />
  99. </div>
  100. </div>
  101. )
  102. })