UNPKG

tldraw

Version:

A tiny little drawing editor.

8 lines (7 loc) • 11.7 kB
{ "version": 3, "sources": ["../../../../src/lib/shapes/frame/FrameShapeUtil.tsx"], "sourcesContent": ["import {\n\tBaseBoxShapeUtil,\n\tGeometry2d,\n\tGroup2d,\n\tRectangle2d,\n\tSVGContainer,\n\tSvgExportContext,\n\tTLFrameShape,\n\tTLFrameShapeProps,\n\tTLGroupShape,\n\tTLResizeInfo,\n\tTLShape,\n\tframeShapeMigrations,\n\tframeShapeProps,\n\tgetDefaultColorTheme,\n\tlerp,\n\tresizeBox,\n\ttoDomPrecision,\n\tuseValue,\n} from '@tldraw/editor'\nimport classNames from 'classnames'\n\nimport {\n\tTLCreateTextJsxFromSpansOpts,\n\tcreateTextJsxFromSpans,\n} from '../shared/createTextJsxFromSpans'\nimport { useDefaultColorTheme } from '../shared/useDefaultColorTheme'\nimport { FrameHeading } from './components/FrameHeading'\nimport {\n\tgetFrameHeadingInfo,\n\tgetFrameHeadingOpts,\n\tgetFrameHeadingSide,\n\tgetFrameHeadingTranslation,\n} from './frameHelpers'\n\nexport function defaultEmptyAs(str: string, dflt: string) {\n\tif (str.match(/^\\s*$/)) {\n\t\treturn dflt\n\t}\n\treturn str\n}\n\n/** @public */\nexport class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {\n\tstatic override type = 'frame' as const\n\tstatic override props = frameShapeProps\n\tstatic override migrations = frameShapeMigrations\n\n\toverride canEdit() {\n\t\treturn true\n\t}\n\n\toverride getDefaultProps(): TLFrameShape['props'] {\n\t\treturn { w: 160 * 2, h: 90 * 2, name: '' }\n\t}\n\n\toverride getGeometry(shape: TLFrameShape): Geometry2d {\n\t\tconst { editor } = this\n\t\tconst z = editor.getZoomLevel()\n\t\tconst opts = getFrameHeadingOpts(shape, 'black')\n\t\tconst headingInfo = getFrameHeadingInfo(editor, shape, opts)\n\t\tconst labelSide = getFrameHeadingSide(editor, shape)\n\n\t\t// wow this fucking sucks!!!\n\t\tlet x: number, y: number, w: number, h: number\n\n\t\tconst { w: hw, h: hh } = headingInfo.box\n\t\tconst scaledW = Math.min(hw, shape.props.w * z)\n\t\tconst scaledH = Math.min(hh, shape.props.h * z)\n\n\t\tswitch (labelSide) {\n\t\t\tcase 0: {\n\t\t\t\tx = -8 / z\n\t\t\t\ty = (-hh - 4) / z\n\t\t\t\tw = (scaledW + 16) / z\n\t\t\t\th = hh / z\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 1: {\n\t\t\t\tx = (-hh - 4) / z\n\t\t\t\th = (scaledH + 16) / z\n\t\t\t\ty = shape.props.h - h + 8 / z\n\t\t\t\tw = hh / z\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 2: {\n\t\t\t\tx = shape.props.w - (scaledW + 8) / z\n\t\t\t\ty = shape.props.h + 4 / z\n\t\t\t\tw = (scaledH + 16) / z\n\t\t\t\th = hh / z\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 3: {\n\t\t\t\tx = shape.props.w + 4 / z\n\t\t\t\th = (scaledH + 16) / z\n\t\t\t\ty = -8 / z\n\t\t\t\tw = hh / z\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\treturn new Group2d({\n\t\t\tchildren: [\n\t\t\t\tnew Rectangle2d({\n\t\t\t\t\twidth: shape.props.w,\n\t\t\t\t\theight: shape.props.h,\n\t\t\t\t\tisFilled: false,\n\t\t\t\t}),\n\t\t\t\tnew Rectangle2d({\n\t\t\t\t\tx,\n\t\t\t\t\ty,\n\t\t\t\t\twidth: w,\n\t\t\t\t\theight: h,\n\t\t\t\t\tisFilled: true,\n\t\t\t\t\tisLabel: true,\n\t\t\t\t}),\n\t\t\t],\n\t\t})\n\t}\n\n\toverride getText(shape: TLFrameShape): string | undefined {\n\t\treturn shape.props.name\n\t}\n\n\toverride component(shape: TLFrameShape) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\t\t// eslint-disable-next-line react-hooks/rules-of-hooks\n\t\tconst theme = useDefaultColorTheme()\n\n\t\t// eslint-disable-next-line react-hooks/rules-of-hooks\n\t\tconst isCreating = useValue(\n\t\t\t'is creating this shape',\n\t\t\t() => {\n\t\t\t\tconst resizingState = this.editor.getStateDescendant('select.resizing')\n\t\t\t\tif (!resizingState) return false\n\t\t\t\tif (!resizingState.getIsActive()) return false\n\t\t\t\tconst info = (resizingState as typeof resizingState & { info: { isCreating: boolean } })\n\t\t\t\t\t?.info\n\t\t\t\tif (!info) return false\n\t\t\t\treturn info.isCreating && this.editor.getOnlySelectedShapeId() === shape.id\n\t\t\t},\n\t\t\t[shape.id]\n\t\t)\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t<SVGContainer>\n\t\t\t\t\t<rect\n\t\t\t\t\t\tclassName={classNames('tl-frame__body', { 'tl-frame__creating': isCreating })}\n\t\t\t\t\t\twidth={bounds.width}\n\t\t\t\t\t\theight={bounds.height}\n\t\t\t\t\t\tfill={theme.solid}\n\t\t\t\t\t\tstroke={theme.text}\n\t\t\t\t\t/>\n\t\t\t\t</SVGContainer>\n\t\t\t\t{isCreating ? null : (\n\t\t\t\t\t<FrameHeading\n\t\t\t\t\t\tid={shape.id}\n\t\t\t\t\t\tname={shape.props.name}\n\t\t\t\t\t\twidth={bounds.width}\n\t\t\t\t\t\theight={bounds.height}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</>\n\t\t)\n\t}\n\n\toverride toSvg(shape: TLFrameShape, ctx: SvgExportContext) {\n\t\tconst theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })\n\n\t\t// rotate right 45 deg\n\t\tconst labelSide = getFrameHeadingSide(this.editor, shape)\n\t\tconst labelTranslate = getFrameHeadingTranslation(shape, labelSide, true)\n\n\t\t// Truncate with ellipsis\n\t\tconst opts: TLCreateTextJsxFromSpansOpts = getFrameHeadingOpts(shape, theme.text)\n\n\t\tconst { box: labelBounds, spans } = getFrameHeadingInfo(this.editor, shape, opts)\n\t\tconst text = createTextJsxFromSpans(this.editor, spans, opts)\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t<rect\n\t\t\t\t\twidth={shape.props.w}\n\t\t\t\t\theight={shape.props.h}\n\t\t\t\t\tfill={theme.solid}\n\t\t\t\t\tstroke={theme.black.solid}\n\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\trx={1}\n\t\t\t\t\try={1}\n\t\t\t\t/>\n\t\t\t\t<g transform={labelTranslate}>\n\t\t\t\t\t<rect\n\t\t\t\t\t\tx={labelBounds.x - 8}\n\t\t\t\t\t\ty={labelBounds.y - 4}\n\t\t\t\t\t\twidth={labelBounds.width + 20}\n\t\t\t\t\t\theight={labelBounds.height}\n\t\t\t\t\t\tfill={theme.background}\n\t\t\t\t\t\trx={4}\n\t\t\t\t\t\try={4}\n\t\t\t\t\t/>\n\t\t\t\t\t{text}\n\t\t\t\t</g>\n\t\t\t</>\n\t\t)\n\t}\n\n\tindicator(shape: TLFrameShape) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\n\t\treturn (\n\t\t\t<rect\n\t\t\t\twidth={toDomPrecision(bounds.width)}\n\t\t\t\theight={toDomPrecision(bounds.height)}\n\t\t\t\tclassName={`tl-frame-indicator`}\n\t\t\t/>\n\t\t)\n\t}\n\n\toverride canReceiveNewChildrenOfType(shape: TLShape, _type: TLShape['type']) {\n\t\treturn !shape.isLocked\n\t}\n\n\toverride providesBackgroundForChildren(): boolean {\n\t\treturn true\n\t}\n\n\toverride canDropShapes(shape: TLFrameShape, _shapes: TLShape[]): boolean {\n\t\treturn !shape.isLocked\n\t}\n\n\toverride onDragShapesOver(frame: TLFrameShape, shapes: TLShape[]) {\n\t\tif (!shapes.every((child) => child.parentId === frame.id)) {\n\t\t\tthis.editor.reparentShapes(shapes, frame.id)\n\t\t}\n\t}\n\n\toverride onDragShapesOut(_shape: TLFrameShape, shapes: TLShape[]): void {\n\t\tconst parent = this.editor.getShape(_shape.parentId)\n\t\tconst isInGroup = parent && this.editor.isShapeOfType<TLGroupShape>(parent, 'group')\n\n\t\t// If frame is in a group, keep the shape\n\t\t// moved out in that group\n\n\t\tif (isInGroup) {\n\t\t\tthis.editor.reparentShapes(shapes, parent.id)\n\t\t} else {\n\t\t\tthis.editor.reparentShapes(shapes, this.editor.getCurrentPageId())\n\t\t}\n\t}\n\n\toverride onResize(shape: any, info: TLResizeInfo<any>) {\n\t\treturn resizeBox(shape, info)\n\t}\n\toverride getInterpolatedProps(\n\t\tstartShape: TLFrameShape,\n\t\tendShape: TLFrameShape,\n\t\tt: number\n\t): TLFrameShapeProps {\n\t\treturn {\n\t\t\t...(t > 0.5 ? endShape.props : startShape.props),\n\t\t\tw: lerp(startShape.props.w, endShape.props.w, t),\n\t\t\th: lerp(startShape.props.h, endShape.props.h, t),\n\t\t}\n\t}\n}\n"], "mappings": "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", "names": [] }