BoxShape.tsx 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /* eslint-disable @typescript-eslint/no-explicit-any */
  2. import { SVGContainer, TLComponentProps } from '@tldraw/react'
  3. import { TLBoxShape, TLBoxShapeProps } from '@tldraw/core'
  4. import { observer } from 'mobx-react-lite'
  5. import { CustomStyleProps, withClampedStyles } from './style-props'
  6. import { BindingIndicator } from './BindingIndicator'
  7. export interface BoxShapeProps extends TLBoxShapeProps, CustomStyleProps {
  8. borderRadius: number
  9. type: 'box'
  10. }
  11. export class BoxShape extends TLBoxShape<BoxShapeProps> {
  12. static id = 'box'
  13. static defaultProps: BoxShapeProps = {
  14. id: 'box',
  15. parentId: 'page',
  16. type: 'box',
  17. point: [0, 0],
  18. size: [100, 100],
  19. borderRadius: 2,
  20. stroke: '#000000',
  21. fill: '',
  22. noFill: false,
  23. strokeType: 'line',
  24. strokeWidth: 2,
  25. opacity: 1,
  26. }
  27. ReactComponent = observer(({ events, isErasing, isBinding, isSelected }: TLComponentProps) => {
  28. const {
  29. props: {
  30. size: [w, h],
  31. stroke,
  32. fill,
  33. noFill,
  34. strokeWidth,
  35. strokeType,
  36. borderRadius,
  37. opacity,
  38. },
  39. } = this
  40. return (
  41. <SVGContainer {...events} opacity={isErasing ? 0.2 : opacity}>
  42. {isBinding && <BindingIndicator mode="svg" strokeWidth={strokeWidth} size={[w, h]} />}
  43. <rect
  44. className={isSelected || !noFill ? 'tl-hitarea-fill' : 'tl-hitarea-stroke'}
  45. x={strokeWidth / 2}
  46. y={strokeWidth / 2}
  47. rx={borderRadius}
  48. ry={borderRadius}
  49. width={Math.max(0.01, w - strokeWidth)}
  50. height={Math.max(0.01, h - strokeWidth)}
  51. pointerEvents="all"
  52. />
  53. <rect
  54. x={strokeWidth / 2}
  55. y={strokeWidth / 2}
  56. rx={borderRadius}
  57. ry={borderRadius}
  58. width={Math.max(0.01, w - strokeWidth)}
  59. height={Math.max(0.01, h - strokeWidth)}
  60. strokeWidth={strokeWidth}
  61. stroke={noFill ? fill : stroke}
  62. strokeDasharray={strokeType === 'dashed' ? '8 2' : undefined}
  63. fill={noFill ? 'none' : fill ? `var(--color-${fill}-500)` : "var(--ls-primary-background-color)"}
  64. />
  65. </SVGContainer>
  66. )
  67. })
  68. ReactIndicator = observer(() => {
  69. const {
  70. props: {
  71. size: [w, h],
  72. borderRadius,
  73. },
  74. } = this
  75. return <rect width={w} height={h} rx={borderRadius} ry={borderRadius} fill="transparent" />
  76. })
  77. validateProps = (props: Partial<BoxShapeProps>) => {
  78. if (props.size !== undefined) {
  79. props.size[0] = Math.max(props.size[0], 1)
  80. props.size[1] = Math.max(props.size[1], 1)
  81. }
  82. if (props.borderRadius !== undefined) props.borderRadius = Math.max(0, props.borderRadius)
  83. return withClampedStyles(this, props)
  84. }
  85. }