From 153e4662a3bdec0958588bb323bd829fef44da01 Mon Sep 17 00:00:00 2001 From: Alber EE <122263897+Alber-Writer@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:54:09 +0100 Subject: [PATCH 1/6] update method calcTextWidth to calcTextDimensions --- ...c-text-width.ts => calc-text-dimensions.ts} | 18 +++++++++++++----- .../tabsbar/business/tabsbar.business.ts | 13 +++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) rename src/common/components/mock-components/front-rich-components/tabsbar/business/{calc-text-width.ts => calc-text-dimensions.ts} (63%) diff --git a/src/common/components/mock-components/front-rich-components/tabsbar/business/calc-text-width.ts b/src/common/components/mock-components/front-rich-components/tabsbar/business/calc-text-dimensions.ts similarity index 63% rename from src/common/components/mock-components/front-rich-components/tabsbar/business/calc-text-width.ts rename to src/common/components/mock-components/front-rich-components/tabsbar/business/calc-text-dimensions.ts index 843bd15e..64c2d8f6 100644 --- a/src/common/components/mock-components/front-rich-components/tabsbar/business/calc-text-width.ts +++ b/src/common/components/mock-components/front-rich-components/tabsbar/business/calc-text-dimensions.ts @@ -1,12 +1,12 @@ import { Layer } from 'konva/lib/Layer'; /** - * Virtually calculates the width that a text will occupy, by using a canvas. + * Virtually calculates the height and width that a text will occupy, by using a canvas. * If a Konva Layer is provided, it will reuse the already existing canvas. * Otherwise, it will create a canvas within the document, on the fly, to perform the measurement. * Finaly, as a safety net, a very generic calculation is provided in case the other options are not available. */ -export const calcTextWidth = ( +export const calcTextDimensions = ( inputText: string, fontSize: number, fontfamily: string, @@ -31,7 +31,10 @@ const _getTextWidthByKonvaMethod = ( ) => { const context = konvaLayer.getContext(); context.font = `${fontSize}px ${fontfamily}`; - return context.measureText(text).width; + const { width, fontBoundingBoxAscent, fontBoundingBoxDescent } = + context.measureText(text); + const totalHeight = fontBoundingBoxAscent + fontBoundingBoxDescent; + return { width, height: totalHeight }; }; const _getTextCreatingNewCanvas = ( @@ -43,8 +46,13 @@ const _getTextCreatingNewCanvas = ( const context = canvas.getContext('2d'); if (context) { context.font = `${fontSize}px ${fontfamily}`; - return context.measureText(text).width; + const { width, fontBoundingBoxAscent, fontBoundingBoxDescent } = + context.measureText(text); + const height = fontBoundingBoxAscent + fontBoundingBoxDescent; + return { width, height }; } const charAverageWidth = fontSize * 0.7; - return text.length * charAverageWidth + charAverageWidth * 0.8; + const width = text.length * charAverageWidth + charAverageWidth * 0.8; + const height = fontSize * 1.5; + return { width, height }; }; diff --git a/src/common/components/mock-components/front-rich-components/tabsbar/business/tabsbar.business.ts b/src/common/components/mock-components/front-rich-components/tabsbar/business/tabsbar.business.ts index ce08f8bb..cead850d 100644 --- a/src/common/components/mock-components/front-rich-components/tabsbar/business/tabsbar.business.ts +++ b/src/common/components/mock-components/front-rich-components/tabsbar/business/tabsbar.business.ts @@ -1,6 +1,6 @@ import { Layer } from 'konva/lib/Layer'; import { balanceSpacePerItem } from './balance-space'; -import { calcTextWidth } from './calc-text-width'; +import { calcTextDimensions } from './calc-text-dimensions'; export const adjustTabWidths = (args: { tabs: string[]; @@ -35,9 +35,14 @@ export const adjustTabWidths = (args: { } const arrangeTabsInfo = tabs.reduce( (acc: OriginalTabInfo[], tab, index): OriginalTabInfo[] => { - const tabFullTextWidth = - calcTextWidth(tab, font.fontSize, font.fontFamily, konvaLayer) + - totalInnerXPadding; + const { width: textWidth } = calcTextDimensions( + tab, + font.fontSize, + font.fontFamily, + konvaLayer + ); + + const tabFullTextWidth = textWidth + totalInnerXPadding; const desiredWidth = Math.max(totalMinTabSpace, tabFullTextWidth); return [ ...acc, From f9834cca27ec029fcbf62bd3221b02f3b156ae14 Mon Sep 17 00:00:00 2001 From: Alber EE <122263897+Alber-Writer@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:59:57 +0100 Subject: [PATCH 2/6] Move calcTextDimensions method to common/utils folder --- .../front-rich-components/tabsbar/business/tabsbar.business.ts | 2 +- .../tabsbar/business => utils}/calc-text-dimensions.ts | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/common/{components/mock-components/front-rich-components/tabsbar/business => utils}/calc-text-dimensions.ts (100%) diff --git a/src/common/components/mock-components/front-rich-components/tabsbar/business/tabsbar.business.ts b/src/common/components/mock-components/front-rich-components/tabsbar/business/tabsbar.business.ts index cead850d..ed119ee4 100644 --- a/src/common/components/mock-components/front-rich-components/tabsbar/business/tabsbar.business.ts +++ b/src/common/components/mock-components/front-rich-components/tabsbar/business/tabsbar.business.ts @@ -1,6 +1,6 @@ import { Layer } from 'konva/lib/Layer'; import { balanceSpacePerItem } from './balance-space'; -import { calcTextDimensions } from './calc-text-dimensions'; +import { calcTextDimensions } from '@/common/utils/calc-text-dimensions'; export const adjustTabWidths = (args: { tabs: string[]; diff --git a/src/common/components/mock-components/front-rich-components/tabsbar/business/calc-text-dimensions.ts b/src/common/utils/calc-text-dimensions.ts similarity index 100% rename from src/common/components/mock-components/front-rich-components/tabsbar/business/calc-text-dimensions.ts rename to src/common/utils/calc-text-dimensions.ts From 7b6864361458e3a802c70070319b81d7a81bd9b5 Mon Sep 17 00:00:00 2001 From: Alber EE <122263897+Alber-Writer@users.noreply.github.com> Date: Wed, 20 Nov 2024 23:08:13 +0100 Subject: [PATCH 3/6] test shape resizing on fontsize changes --- .../heading1-text-shape.tsx | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/common/components/mock-components/front-text-components/heading1-text-shape.tsx b/src/common/components/mock-components/front-text-components/heading1-text-shape.tsx index edd483fe..be965cc1 100644 --- a/src/common/components/mock-components/front-text-components/heading1-text-shape.tsx +++ b/src/common/components/mock-components/front-text-components/heading1-text-shape.tsx @@ -1,4 +1,4 @@ -import { forwardRef } from 'react'; +import { forwardRef, useEffect, useRef } from 'react'; import { Group, Text } from 'react-konva'; import { ShapeProps } from '../shape.model'; import { ShapeSizeRestrictions, ShapeType } from '@/core/model'; @@ -6,6 +6,8 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest import { useShapeProps } from '../../shapes/use-shape-props.hook'; import { BASIC_SHAPE } from '../front-components/shape.const'; import { useGroupShapeProps } from '../mock-components.utils'; +import { calcTextDimensions } from '@/common/utils/calc-text-dimensions'; +import { useCanvasContext } from '@/core/providers'; const heading1SizeRestrictions: ShapeSizeRestrictions = { minWidth: 40, @@ -43,6 +45,8 @@ export const Heading1Shape = forwardRef((props, ref) => { const { textColor, textDecoration, fontStyle, fontVariant, fontSize } = useShapeProps(otherProps, BASIC_SHAPE); + const previousFontSize = useRef(fontSize); + const commonGroupProps = useGroupShapeProps( props, restrictedSize, @@ -50,6 +54,27 @@ export const Heading1Shape = forwardRef((props, ref) => { ref ); + const { updateShapeSizeAndPosition, stageRef } = useCanvasContext(); + const konvaLayer = stageRef.current?.getLayers()[0]; + + useEffect(() => { + if (previousFontSize.current != fontSize) { + previousFontSize.current = fontSize; + const { width, height } = calcTextDimensions( + text, + fontSize, + fontVariant, + konvaLayer + ); + updateShapeSizeAndPosition( + id, + { x, y }, + { width: width * 1.1, height }, + false + ); + } + }, [fontSize]); + return ( Date: Wed, 20 Nov 2024 23:46:33 +0100 Subject: [PATCH 4/6] refactor resize-on-fontsize-change to hook --- .../resize-fontsize-change.hook.ts | 33 +++++++++++++++++++ .../heading1-text-shape.tsx | 28 ++-------------- 2 files changed, 36 insertions(+), 25 deletions(-) create mode 100644 src/common/components/mock-components/front-text-components/front-text-hooks/resize-fontsize-change.hook.ts diff --git a/src/common/components/mock-components/front-text-components/front-text-hooks/resize-fontsize-change.hook.ts b/src/common/components/mock-components/front-text-components/front-text-hooks/resize-fontsize-change.hook.ts new file mode 100644 index 00000000..3037e1e5 --- /dev/null +++ b/src/common/components/mock-components/front-text-components/front-text-hooks/resize-fontsize-change.hook.ts @@ -0,0 +1,33 @@ +import { calcTextDimensions } from '@/common/utils/calc-text-dimensions'; +import { useCanvasContext } from '@/core/providers'; +import { useEffect, useRef } from 'react'; + +export const useResizeOnFontSizeChange = ( + id: string, + coords: { x: number; y: number }, + text: string, + fontSize: number, + fontVariant: string +) => { + const previousFontSize = useRef(fontSize); + const { updateShapeSizeAndPosition, stageRef } = useCanvasContext(); + const konvaLayer = stageRef.current?.getLayers()[0]; + + useEffect(() => { + if (previousFontSize.current != fontSize) { + previousFontSize.current = fontSize; + const { width, height } = calcTextDimensions( + text, + fontSize, + fontVariant, + konvaLayer + ); + updateShapeSizeAndPosition( + id, + coords, + { width: width * 1.15, height }, + true + ); + } + }, [fontSize]); +}; diff --git a/src/common/components/mock-components/front-text-components/heading1-text-shape.tsx b/src/common/components/mock-components/front-text-components/heading1-text-shape.tsx index be965cc1..4e80efd4 100644 --- a/src/common/components/mock-components/front-text-components/heading1-text-shape.tsx +++ b/src/common/components/mock-components/front-text-components/heading1-text-shape.tsx @@ -1,4 +1,4 @@ -import { forwardRef, useEffect, useRef } from 'react'; +import { forwardRef } from 'react'; import { Group, Text } from 'react-konva'; import { ShapeProps } from '../shape.model'; import { ShapeSizeRestrictions, ShapeType } from '@/core/model'; @@ -6,8 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest import { useShapeProps } from '../../shapes/use-shape-props.hook'; import { BASIC_SHAPE } from '../front-components/shape.const'; import { useGroupShapeProps } from '../mock-components.utils'; -import { calcTextDimensions } from '@/common/utils/calc-text-dimensions'; -import { useCanvasContext } from '@/core/providers'; +import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook'; const heading1SizeRestrictions: ShapeSizeRestrictions = { minWidth: 40, @@ -45,8 +44,6 @@ export const Heading1Shape = forwardRef((props, ref) => { const { textColor, textDecoration, fontStyle, fontVariant, fontSize } = useShapeProps(otherProps, BASIC_SHAPE); - const previousFontSize = useRef(fontSize); - const commonGroupProps = useGroupShapeProps( props, restrictedSize, @@ -54,26 +51,7 @@ export const Heading1Shape = forwardRef((props, ref) => { ref ); - const { updateShapeSizeAndPosition, stageRef } = useCanvasContext(); - const konvaLayer = stageRef.current?.getLayers()[0]; - - useEffect(() => { - if (previousFontSize.current != fontSize) { - previousFontSize.current = fontSize; - const { width, height } = calcTextDimensions( - text, - fontSize, - fontVariant, - konvaLayer - ); - updateShapeSizeAndPosition( - id, - { x, y }, - { width: width * 1.1, height }, - false - ); - } - }, [fontSize]); + useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant); return ( From 6ba7fae5596b5620b28d81987fca68c0e574fd67 Mon Sep 17 00:00:00 2001 From: Alber EE <122263897+Alber-Writer@users.noreply.github.com> Date: Sat, 23 Nov 2024 08:03:57 +0100 Subject: [PATCH 5/6] apply resize hook to single line front-text-components --- .../front-text-components/heading2-text-shape.tsx | 3 +++ .../front-text-components/heading3-text-shape.tsx | 3 +++ .../front-text-components/link-text-shape.tsx | 5 ++++- .../front-text-components/normaltext-shape.tsx | 3 +++ .../front-text-components/smalltext-shape.tsx | 3 +++ 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/common/components/mock-components/front-text-components/heading2-text-shape.tsx b/src/common/components/mock-components/front-text-components/heading2-text-shape.tsx index 02d8184b..f3720664 100644 --- a/src/common/components/mock-components/front-text-components/heading2-text-shape.tsx +++ b/src/common/components/mock-components/front-text-components/heading2-text-shape.tsx @@ -6,6 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest import { useShapeProps } from '../../shapes/use-shape-props.hook'; import { BASIC_SHAPE } from '../front-components/shape.const'; import { useGroupShapeProps } from '../mock-components.utils'; +import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook'; const heading2SizeRestrictions: ShapeSizeRestrictions = { minWidth: 40, @@ -50,6 +51,8 @@ export const Heading2Shape = forwardRef((props, ref) => { ref ); + useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant); + return ( ((props, ref) => { ref ); + useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant); + return ( ((props, ref) => { const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { textColor, textDecoration, fontSize } = useShapeProps( + const { textColor, textDecoration, fontSize, fontVariant } = useShapeProps( otherProps, BASIC_SHAPE ); @@ -53,6 +54,8 @@ export const LinkShape = forwardRef((props, ref) => { ref ); + useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant); + return ( ((props, ref) => { ref ); + useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant); + return ( ((props, ref) => { ref ); + useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant); + return ( Date: Sat, 23 Nov 2024 08:13:31 +0100 Subject: [PATCH 6/6] handle resizing when multiline comps fontsize changes --- .../resize-fontsize-change.hook.ts | 38 +++++++++++++++++-- .../paragraph-text-shape.tsx | 8 +++- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/common/components/mock-components/front-text-components/front-text-hooks/resize-fontsize-change.hook.ts b/src/common/components/mock-components/front-text-components/front-text-hooks/resize-fontsize-change.hook.ts index 3037e1e5..a629b2f3 100644 --- a/src/common/components/mock-components/front-text-components/front-text-hooks/resize-fontsize-change.hook.ts +++ b/src/common/components/mock-components/front-text-components/front-text-hooks/resize-fontsize-change.hook.ts @@ -7,27 +7,57 @@ export const useResizeOnFontSizeChange = ( coords: { x: number; y: number }, text: string, fontSize: number, - fontVariant: string + fontVariant: string, + multiline: boolean = false ) => { const previousFontSize = useRef(fontSize); const { updateShapeSizeAndPosition, stageRef } = useCanvasContext(); const konvaLayer = stageRef.current?.getLayers()[0]; useEffect(() => { - if (previousFontSize.current != fontSize) { + if (previousFontSize.current !== fontSize) { previousFontSize.current = fontSize; + + const paragraphLines = _extractParagraphLines(text); + const longestLine = _findLongestString(paragraphLines); + const { width, height } = calcTextDimensions( - text, + multiline ? longestLine : text, fontSize, fontVariant, konvaLayer ); + updateShapeSizeAndPosition( id, coords, - { width: width * 1.15, height }, + { + width: width * 1.15, + height: multiline + ? _calcParagraphTotalHeight(height, 0.8, paragraphLines.length) + : height, + }, true ); } }, [fontSize]); }; + +/* Hook Helper functions */ +function _extractParagraphLines(multilineText: string) { + return multilineText.split(/\r?\n/).filter(line => line.trim() !== ''); +} + +function _findLongestString(stringsArray: string[]): string { + return stringsArray.reduce((longest, current) => + current.length > longest.length ? current : longest + ); +} + +function _calcParagraphTotalHeight( + heightPerLine: number, + lineHeight: number = 1.3, + linesQty: number +) { + return heightPerLine * lineHeight * linesQty; +} diff --git a/src/common/components/mock-components/front-text-components/paragraph-text-shape.tsx b/src/common/components/mock-components/front-text-components/paragraph-text-shape.tsx index 3d44c447..dccf7fcd 100644 --- a/src/common/components/mock-components/front-text-components/paragraph-text-shape.tsx +++ b/src/common/components/mock-components/front-text-components/paragraph-text-shape.tsx @@ -6,6 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest import { BASIC_SHAPE } from '../front-components/shape.const'; import { useShapeProps } from '../../shapes/use-shape-props.hook'; import { useGroupShapeProps } from '../mock-components.utils'; +import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook'; const paragraphSizeRestrictions: ShapeSizeRestrictions = { minWidth: 200, @@ -40,7 +41,10 @@ export const ParagraphShape = forwardRef((props, ref) => { ); const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { textColor, fontSize } = useShapeProps(otherProps, BASIC_SHAPE); + const { textColor, fontSize, fontVariant } = useShapeProps( + otherProps, + BASIC_SHAPE + ); const commonGroupProps = useGroupShapeProps( props, @@ -49,6 +53,8 @@ export const ParagraphShape = forwardRef((props, ref) => { ref ); + useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant, true); + return (