diff --git a/src/common/components/mock-components/front-components/shape.const.ts b/src/common/components/mock-components/front-components/shape.const.ts index f256236e..458d4178 100644 --- a/src/common/components/mock-components/front-components/shape.const.ts +++ b/src/common/components/mock-components/front-components/shape.const.ts @@ -89,6 +89,26 @@ export const POSTIT_SHAPE: DefaultStyleShape = { DEFAULT_TEXT_DECORATION, }; +interface FontValues { + HEADING1: number; + HEADING2: number; + HEADING3: number; + NORMALTEXT: number; + SMALLTEXT: number; + PARAGRAPH: number; + LINK: number; +} + +export const FONT_SIZE_VALUES: FontValues = { + HEADING1: 28, + HEADING2: 24, + HEADING3: 18, + NORMALTEXT: 18, + SMALLTEXT: 14, + PARAGRAPH: 14, + LINK: 20, +}; + export const LINK_SHAPE: DefaultStyleShape = { ...BASIC_SHAPE, DEFAULT_FILL_TEXT: '#0000FF', diff --git a/src/common/components/mock-components/front-rich-components/modal/modal.tsx b/src/common/components/mock-components/front-rich-components/modal/modal.tsx index a4e265f4..c1996891 100644 --- a/src/common/components/mock-components/front-rich-components/modal/modal.tsx +++ b/src/common/components/mock-components/front-rich-components/modal/modal.tsx @@ -53,7 +53,7 @@ export const Modal = forwardRef((props, ref) => { const buttonWidth = (restrictedWidth - (buttons.length + 1) * buttonSpacing) / buttons.length; - const { stroke, strokeStyle, fill, textColor } = useShapeProps( + const { stroke, strokeStyle, fill, textColor, fontSize } = useShapeProps( otherProps, BASIC_SHAPE ); @@ -99,7 +99,7 @@ export const Modal = forwardRef((props, ref) => { width={restrictedWidth - 60} text={modalTitle} fontFamily="Arial" - fontSize={18} + fontSize={fontSize} fill="white" wrap="none" ellipsis={true} 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 38d35911..edd483fe 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 @@ -40,10 +40,8 @@ export const Heading1Shape = forwardRef((props, ref) => { ); const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { textColor, textDecoration, fontStyle, fontVariant } = useShapeProps( - otherProps, - BASIC_SHAPE - ); + const { textColor, textDecoration, fontStyle, fontVariant, fontSize } = + useShapeProps(otherProps, BASIC_SHAPE); const commonGroupProps = useGroupShapeProps( props, @@ -61,7 +59,7 @@ export const Heading1Shape = forwardRef((props, ref) => { height={restrictedHeight} text={text} fontFamily={BASIC_SHAPE.DEFAULT_FONT_FAMILY} - fontSize={30} + fontSize={fontSize} fill={textColor} align="center" verticalAlign="middle" 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 52359915..02d8184b 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 @@ -40,10 +40,8 @@ export const Heading2Shape = forwardRef((props, ref) => { ); const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { textColor, fontVariant, fontStyle, textDecoration } = useShapeProps( - otherProps, - BASIC_SHAPE - ); + const { textColor, fontVariant, fontStyle, textDecoration, fontSize } = + useShapeProps(otherProps, BASIC_SHAPE); const commonGroupProps = useGroupShapeProps( props, @@ -61,7 +59,7 @@ export const Heading2Shape = forwardRef((props, ref) => { height={restrictedHeight} text={text} fontFamily={BASIC_SHAPE.DEFAULT_FONT_FAMILY} - fontSize={25} + fontSize={fontSize} fill={textColor} align="center" verticalAlign="middle" diff --git a/src/common/components/mock-components/front-text-components/heading3-text-shape.tsx b/src/common/components/mock-components/front-text-components/heading3-text-shape.tsx index 42b994bf..4f02a862 100644 --- a/src/common/components/mock-components/front-text-components/heading3-text-shape.tsx +++ b/src/common/components/mock-components/front-text-components/heading3-text-shape.tsx @@ -41,10 +41,8 @@ export const Heading3Shape = forwardRef((props, ref) => { const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { textColor, fontVariant, fontStyle, textDecoration } = useShapeProps( - otherProps, - BASIC_SHAPE - ); + const { textColor, fontVariant, fontStyle, textDecoration, fontSize } = + useShapeProps(otherProps, BASIC_SHAPE); const commonGroupProps = useGroupShapeProps( props, @@ -62,7 +60,7 @@ export const Heading3Shape = forwardRef((props, ref) => { height={restrictedHeight} text={text} fontFamily={BASIC_SHAPE.DEFAULT_FONT_FAMILY} - fontSize={20} + fontSize={fontSize} fill={textColor} align="center" verticalAlign="middle" diff --git a/src/common/components/mock-components/front-text-components/link-text-shape.tsx b/src/common/components/mock-components/front-text-components/link-text-shape.tsx index 28f5c9bc..e139ce70 100644 --- a/src/common/components/mock-components/front-text-components/link-text-shape.tsx +++ b/src/common/components/mock-components/front-text-components/link-text-shape.tsx @@ -41,7 +41,10 @@ export const LinkShape = forwardRef((props, ref) => { const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { textColor, textDecoration } = useShapeProps(otherProps, BASIC_SHAPE); + const { textColor, textDecoration, fontSize } = useShapeProps( + otherProps, + BASIC_SHAPE + ); const commonGroupProps = useGroupShapeProps( props, @@ -59,7 +62,7 @@ export const LinkShape = forwardRef((props, ref) => { height={restrictedHeight} text={text} fontFamily={BASIC_SHAPE.DEFAULT_FONT_FAMILY} - fontSize={20} + fontSize={fontSize} fill={textColor} align="center" verticalAlign="middle" diff --git a/src/common/components/mock-components/front-text-components/normaltext-shape.tsx b/src/common/components/mock-components/front-text-components/normaltext-shape.tsx index e1c39371..6f3bd419 100644 --- a/src/common/components/mock-components/front-text-components/normaltext-shape.tsx +++ b/src/common/components/mock-components/front-text-components/normaltext-shape.tsx @@ -40,10 +40,8 @@ export const NormaltextShape = forwardRef((props, ref) => { ); const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { textColor, fontVariant, fontStyle, textDecoration } = useShapeProps( - otherProps, - BASIC_SHAPE - ); + const { textColor, fontVariant, fontStyle, textDecoration, fontSize } = + useShapeProps(otherProps, BASIC_SHAPE); const commonGroupProps = useGroupShapeProps( props, @@ -61,7 +59,7 @@ export const NormaltextShape = forwardRef((props, ref) => { height={restrictedHeight} text={text} fontFamily={BASIC_SHAPE.DEFAULT_FONT_FAMILY} - fontSize={18} + fontSize={fontSize} fill={textColor} align="center" verticalAlign="middle" 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 00824939..3d44c447 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 @@ -40,7 +40,7 @@ export const ParagraphShape = forwardRef((props, ref) => { ); const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { textColor } = useShapeProps(otherProps, BASIC_SHAPE); + const { textColor, fontSize } = useShapeProps(otherProps, BASIC_SHAPE); const commonGroupProps = useGroupShapeProps( props, @@ -58,7 +58,7 @@ export const ParagraphShape = forwardRef((props, ref) => { height={restrictedHeight} text={text} fontFamily={BASIC_SHAPE.DEFAULT_FONT_FAMILY} - fontSize={14} + fontSize={fontSize} fill={textColor} align="left" ellipsis={true} diff --git a/src/common/components/mock-components/front-text-components/smalltext-shape.tsx b/src/common/components/mock-components/front-text-components/smalltext-shape.tsx index aa392a5f..ec0393a2 100644 --- a/src/common/components/mock-components/front-text-components/smalltext-shape.tsx +++ b/src/common/components/mock-components/front-text-components/smalltext-shape.tsx @@ -40,10 +40,8 @@ export const SmalltextShape = forwardRef((props, ref) => { ); const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { textColor, fontVariant, fontStyle, textDecoration } = useShapeProps( - otherProps, - BASIC_SHAPE - ); + const { textColor, fontVariant, fontStyle, textDecoration, fontSize } = + useShapeProps(otherProps, BASIC_SHAPE); const commonGroupProps = useGroupShapeProps( props, @@ -61,7 +59,7 @@ export const SmalltextShape = forwardRef((props, ref) => { height={restrictedHeight} text={text} fontFamily={BASIC_SHAPE.DEFAULT_FONT_FAMILY} - fontSize={14} + fontSize={fontSize} fill={textColor} align="center" verticalAlign="middle" diff --git a/src/common/components/shapes/use-shape-props.hook.ts b/src/common/components/shapes/use-shape-props.hook.ts index 99625bb8..1a68f6d9 100644 --- a/src/common/components/shapes/use-shape-props.hook.ts +++ b/src/common/components/shapes/use-shape-props.hook.ts @@ -32,6 +32,11 @@ export const useShapeProps = ( [otherProps?.fontStyle] ); + const fontSize = useMemo( + () => otherProps?.fontSize ?? defaultStyleShape.DEFAULT_FONT_SIZE, + [otherProps?.fontSize] + ); + const textDecoration = useMemo( () => otherProps?.textDecoration ?? defaultStyleShape.DEFAULT_TEXT_DECORATION, @@ -74,6 +79,7 @@ export const useShapeProps = ( selectedBackgroundColor, fontVariant, fontStyle, + fontSize, textDecoration, }; }; diff --git a/src/core/model/index.ts b/src/core/model/index.ts index 473b1964..843d0979 100644 --- a/src/core/model/index.ts +++ b/src/core/model/index.ts @@ -163,6 +163,7 @@ export interface OtherProps { textColor?: string; fontVariant?: string; fontStyle?: string; + fontSize?: number; textDecoration?: string; checked?: boolean; icon?: IconInfo; diff --git a/src/core/providers/canvas/canvas.model.ts b/src/core/providers/canvas/canvas.model.ts index b623b656..645736dc 100644 --- a/src/core/providers/canvas/canvas.model.ts +++ b/src/core/providers/canvas/canvas.model.ts @@ -101,6 +101,7 @@ export interface CanvasContextModel { setActivePage: (pageId: string) => void; deletePage: (pageIndex: number) => void; editPageTitle: (pageIndex: number, newName: string) => void; + activePageIndex: number; isThumbnailContextMenuVisible: boolean; setIsThumbnailContextMenuVisible: React.Dispatch< React.SetStateAction diff --git a/src/core/providers/canvas/canvas.provider.tsx b/src/core/providers/canvas/canvas.provider.tsx index 2d3ba3a4..fef9d809 100644 --- a/src/core/providers/canvas/canvas.provider.tsx +++ b/src/core/providers/canvas/canvas.provider.tsx @@ -307,6 +307,7 @@ export const CanvasProvider: React.FC = props => { setActivePage, deletePage, editPageTitle, + activePageIndex: document.activePageIndex, isThumbnailContextMenuVisible, setIsThumbnailContextMenuVisible, }} diff --git a/src/pods/canvas/model/shape-other-props.utils.ts b/src/pods/canvas/model/shape-other-props.utils.ts index 34d70c98..afff3625 100644 --- a/src/pods/canvas/model/shape-other-props.utils.ts +++ b/src/pods/canvas/model/shape-other-props.utils.ts @@ -1,6 +1,7 @@ import { INPUT_SHAPE, BASIC_SHAPE, + FONT_SIZE_VALUES, LINK_SHAPE, } from '@/common/components/mock-components/front-components/shape.const'; import { ShapeType, OtherProps } from '@/core/model'; @@ -124,6 +125,7 @@ export const generateDefaultOtherProps = ( fontVariant: `${INPUT_SHAPE.DEFAULT_FONT_VARIANT}`, fontStyle: `${INPUT_SHAPE.DEFAULT_FONT_STYLE}`, textDecoration: `${INPUT_SHAPE.DEFAULT_TEXT_DECORATION}`, + fontSize: FONT_SIZE_VALUES.HEADING1, }; case 'heading2': @@ -132,6 +134,7 @@ export const generateDefaultOtherProps = ( fontVariant: `${INPUT_SHAPE.DEFAULT_FONT_VARIANT}`, fontStyle: `${INPUT_SHAPE.DEFAULT_FONT_STYLE}`, textDecoration: `${INPUT_SHAPE.DEFAULT_TEXT_DECORATION}`, + fontSize: FONT_SIZE_VALUES.HEADING2, }; case 'heading3': return { @@ -139,11 +142,13 @@ export const generateDefaultOtherProps = ( fontVariant: `${INPUT_SHAPE.DEFAULT_FONT_VARIANT}`, fontStyle: `${INPUT_SHAPE.DEFAULT_FONT_STYLE}`, textDecoration: `${INPUT_SHAPE.DEFAULT_TEXT_DECORATION}`, + fontSize: FONT_SIZE_VALUES.HEADING3, }; case 'link': return { textColor: `${LINK_SHAPE.DEFAULT_FILL_TEXT}`, textDecoration: 'underline', + fontSize: FONT_SIZE_VALUES.LINK, }; case 'normaltext': return { @@ -151,6 +156,7 @@ export const generateDefaultOtherProps = ( fontVariant: `${INPUT_SHAPE.DEFAULT_FONT_VARIANT}`, fontStyle: `${INPUT_SHAPE.DEFAULT_FONT_STYLE}`, textDecoration: `${INPUT_SHAPE.DEFAULT_TEXT_DECORATION}`, + fontSize: FONT_SIZE_VALUES.NORMALTEXT, }; case 'smalltext': return { @@ -158,8 +164,12 @@ export const generateDefaultOtherProps = ( fontVariant: `${INPUT_SHAPE.DEFAULT_FONT_VARIANT}`, fontStyle: `${INPUT_SHAPE.DEFAULT_FONT_STYLE}`, textDecoration: `${INPUT_SHAPE.DEFAULT_TEXT_DECORATION}`, + fontSize: FONT_SIZE_VALUES.SMALLTEXT, }; case 'paragraph': + return { + fontSize: FONT_SIZE_VALUES.PARAGRAPH, + }; case 'label': return { textColor: '#000000', diff --git a/src/pods/properties/components/font-size/font-size.module.css b/src/pods/properties/components/font-size/font-size.module.css new file mode 100644 index 00000000..8e51ebc3 --- /dev/null +++ b/src/pods/properties/components/font-size/font-size.module.css @@ -0,0 +1,32 @@ +.container { + display: flex; + gap: 0.5em; + align-items: center; + padding: var(--space-xs) var(--space-md); + border-bottom: 1px solid var(--primary-300); +} + +.container :first-child { + flex: 1; +} + +.button { + border: none; + color: var(--text-color); + background-color: inherit; + width: var(--space-lg); + height: var(--space-lg); + border-radius: var(--border-radius-s); + font-size: var(--fs-xs); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--space-s); + transition: all 0.3s ease-in-out; + cursor: pointer; +} + +.button:hover { + background-color: var(--primary-100); +} diff --git a/src/pods/properties/components/font-size/font-size.tsx b/src/pods/properties/components/font-size/font-size.tsx new file mode 100644 index 00000000..684cadc5 --- /dev/null +++ b/src/pods/properties/components/font-size/font-size.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import classes from './font-size.module.css'; + +interface Props { + fontSize: number | undefined; + label: string; + onChange: (fontSize: number) => void; +} + +export const FontSize: React.FC = props => { + const { label, fontSize, onChange } = props; + const handleChange = (e: React.ChangeEvent) => { + const value = e.target.value; + onChange(Number(value)); + }; + + return ( +
+

{label}

+ +
+ ); +}; diff --git a/src/pods/properties/components/font-size/index.ts b/src/pods/properties/components/font-size/index.ts new file mode 100644 index 00000000..9835905b --- /dev/null +++ b/src/pods/properties/components/font-size/index.ts @@ -0,0 +1 @@ +export * from './font-size'; diff --git a/src/pods/properties/properties.pod.tsx b/src/pods/properties/properties.pod.tsx index 635afcf9..452577cd 100644 --- a/src/pods/properties/properties.pod.tsx +++ b/src/pods/properties/properties.pod.tsx @@ -12,6 +12,7 @@ import { ActiveElementSelector } from './components/active-element-selector/acti import { FontStyle } from './components/font-style'; import { FontVariant } from './components/font-variant/font-variant'; import { TextDecoration } from './components/text-decoration/text-decoration'; +import { FontSize } from './components/font-size'; export const PropertiesPod = () => { const { selectionInfo } = useCanvasContext(); @@ -165,6 +166,15 @@ export const PropertiesPod = () => { } /> )} + {selectedShapeData?.otherProps?.fontSize && ( + + updateOtherPropsOnSelected('fontSize', fontSize) + } + /> + )} )} {selectedShapeData?.otherProps?.activeElement !== undefined && ( diff --git a/src/pods/thumb-pages/components/thumb-page.tsx b/src/pods/thumb-pages/components/thumb-page.tsx index eb853117..49496d52 100644 --- a/src/pods/thumb-pages/components/thumb-page.tsx +++ b/src/pods/thumb-pages/components/thumb-page.tsx @@ -13,13 +13,15 @@ import React from 'react'; interface Props { pageIndex: number; + isVisible: boolean; onSetActivePage: (pageId: string) => void; setPageTitleBeingEdited: (index: number) => void; } export const ThumbPage: React.FunctionComponent = props => { - const { pageIndex, onSetActivePage, setPageTitleBeingEdited } = props; - const { fullDocument } = useCanvasContext(); + const { pageIndex, onSetActivePage, setPageTitleBeingEdited, isVisible } = + props; + const { fullDocument, activePageIndex } = useCanvasContext(); const page = fullDocument.pages[pageIndex]; const shapes = page.shapes; const fakeShapeRefs = useRef({}); @@ -33,33 +35,41 @@ export const ThumbPage: React.FunctionComponent = props => { const divRef = useRef(null); const [key, setKey] = React.useState(0); - React.useEffect(() => { + const handleResizeAndForceRedraw = () => { const newCanvaSize = { width: divRef.current?.clientWidth || 1, height: divRef.current?.clientHeight || 1, }; - window.addEventListener('resize', () => { - setCanvasSize({ - width: divRef.current?.clientWidth || 1, - height: divRef.current?.clientHeight || 1, - }); - }); - setCanvasSize(newCanvaSize); setFinalScale(calculateScaleBasedOnBounds(shapes, newCanvaSize)); + setTimeout(() => { + setKey(key => key + 1); + }, 100); + }; + + React.useLayoutEffect(() => { + handleResizeAndForceRedraw(); + }, []); + + React.useEffect(() => { + if (!isVisible) return; + handleResizeAndForceRedraw(); + }, [isVisible]); + + React.useEffect(() => { + setTimeout(() => { + handleResizeAndForceRedraw(); + }, 100); + }, [shapes, activePageIndex]); - setKey(key => key + 1); + React.useEffect(() => { + window.addEventListener('resize', handleResizeAndForceRedraw); return () => { - window.removeEventListener('resize', () => { - setCanvasSize({ - width: divRef.current?.clientWidth || 1, - height: divRef.current?.clientHeight || 1, - }); - }); + window.removeEventListener('resize', handleResizeAndForceRedraw); }; - }, [divRef.current, shapes]); + }, [divRef.current]); const { showContextMenu, diff --git a/src/pods/thumb-pages/thumb-pages.pod.tsx b/src/pods/thumb-pages/thumb-pages.pod.tsx index 40c7cc5b..cf5a596f 100644 --- a/src/pods/thumb-pages/thumb-pages.pod.tsx +++ b/src/pods/thumb-pages/thumb-pages.pod.tsx @@ -4,7 +4,12 @@ import { useCanvasContext } from '@/core/providers'; import { PageTitleInlineEdit, ThumbPage } from './components'; import { PlusIcon } from '@/common/components/icons'; -export const ThumbPagesPod: React.FC = () => { +interface Props { + isVisible: boolean; +} + +export const ThumbPagesPod: React.FC = props => { + const { isVisible } = props; const { fullDocument, addNewPage, setActivePage, getActivePage } = useCanvasContext(); const [pageTitleBeingEdited, setPageTitleBeingEdited] = React.useState< @@ -34,6 +39,7 @@ export const ThumbPagesPod: React.FC = () => { pageIndex={index} onSetActivePage={handleSetActivePage} setPageTitleBeingEdited={setPageTitleBeingEdited} + isVisible={isVisible} /> {pageTitleBeingEdited === index ? ( { + const [isThumbPagesPodOpen, setIsThumbPagesPodOpen] = useState(false); + const thumbPagesPodRef = useRef(null); + + useEffect(() => { + const handleToggle = () => { + setIsThumbPagesPodOpen(thumbPagesPodRef.current?.open ?? false); + }; + + const detailsElement = thumbPagesPodRef.current; + if (detailsElement) { + detailsElement.addEventListener('toggle', handleToggle); + } + + // Cleanup event listener on component unmount + return () => { + if (detailsElement) { + detailsElement.removeEventListener('toggle', handleToggle); + } + }; + }, []); + + return { + thumbPagesPodRef, + isThumbPagesPodOpen, + }; +}; diff --git a/src/scenes/main.scene.tsx b/src/scenes/main.scene.tsx index 93d74453..c25929b3 100644 --- a/src/scenes/main.scene.tsx +++ b/src/scenes/main.scene.tsx @@ -13,15 +13,23 @@ import { import { PropertiesPod } from '@/pods/properties'; import { FooterPod } from '@/pods/footer/footer.pod'; import { ThumbPagesPod } from '@/pods/thumb-pages'; +import { useAccordionSectionVisibility } from './accordion-section-visibility.hook'; export const MainScene = () => { + const { isThumbPagesPodOpen, thumbPagesPodRef } = + useAccordionSectionVisibility(); + return (
-
+
Pages - +
Devices