From 0a76f01fce8d67b8750fe53198980ecc3c80962e Mon Sep 17 00:00:00 2001 From: Johan Sosa Date: Wed, 20 Dec 2023 18:18:02 +0100 Subject: [PATCH] feat(Rebranding): ImagePreviewInput, InputLabels, ContextContainer. feat(CardEmptyCover): Added cache to increase performance --- .../ImagePreviewInput/ImagePreviewInput.js | 22 +++++----- .../InputDescription.styles.js | 4 +- .../src/form/InputLabel/InputLabel.styles.js | 7 +++- .../form/InputWrapper/InputWrapper.styles.js | 34 +++++++-------- .../src/form/TagsInput/TagsInput.js | 2 +- .../components/src/hooks/useHTMLToCanvas.js | 31 +++++++++++--- .../ContextContainer/ContextContainer.js | 34 ++++++++++----- .../src/misc/CardEmptyCover/CardEmptyCover.js | 3 +- packages/components/src/tokens.compiled.js | 41 +++++++++++++++---- 9 files changed, 122 insertions(+), 56 deletions(-) diff --git a/packages/components/src/form/ImagePreviewInput/ImagePreviewInput.js b/packages/components/src/form/ImagePreviewInput/ImagePreviewInput.js index 176a6b64c..28e0172c0 100644 --- a/packages/components/src/form/ImagePreviewInput/ImagePreviewInput.js +++ b/packages/components/src/form/ImagePreviewInput/ImagePreviewInput.js @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { isFunction, isString, isNil } from 'lodash'; -import { CloudUploadIcon, UndoIcon } from '@bubbles-ui/icons/outline/'; +import { CloudUploadIcon, DeleteBinIcon } from '@bubbles-ui/icons/outline/'; import { Box } from '../../layout/Box'; import { Stack } from '../../layout/Stack'; import { Button } from '../Button'; @@ -28,6 +28,7 @@ export const IMAGE_PREVIEW_INPUT_PROP_TYPES = { readonly: PropTypes.bool, disabled: PropTypes.bool, useAria: PropTypes.bool, + noPicker: PropTypes.bool, }; const ImagePreviewInput = ({ @@ -40,6 +41,7 @@ const ImagePreviewInput = ({ readonly, disabled, useAria, + noPicker, }) => { const [imagePreview, setImagePreview] = useState(previewURL); const [imageValue, setImageValue] = useState(value); @@ -79,14 +81,14 @@ const ImagePreviewInput = ({ }; const getControl = () => { - if (!control) + if (!control && !noPicker) return ( - ); - return React.cloneElement(control, { onClick: openFileBrowser }); + return !noPicker ? React.cloneElement(control, { onClick: openFileBrowser }) : null; }; useEffect(() => { @@ -102,23 +104,23 @@ const ImagePreviewInput = ({ {!imagePreview ? ( getControl() ) : ( - + {!readonly && !disabled ? ( - + diff --git a/packages/components/src/hooks/useHTMLToCanvas.js b/packages/components/src/hooks/useHTMLToCanvas.js index adb2aa402..e58858b94 100644 --- a/packages/components/src/hooks/useHTMLToCanvas.js +++ b/packages/components/src/hooks/useHTMLToCanvas.js @@ -1,15 +1,36 @@ import { useEffect, useState } from 'react'; import html2canvas from 'html2canvas'; -const useHTMLToCanvas = (ref) => { +const cache = {}; + +const useHTMLToCanvas = (ref, key) => { const [canvasImage, setCanvasImage] = useState(); const renderCanvas = async () => { - if (ref.current) { - const canvas = await html2canvas(ref.current); - const response = canvas.toDataURL('image/png'); - setCanvasImage(response); + if (cache[key]?.data) { + if (cache[key].data === 'pending') { + const eventHandler = () => { + setCanvasImage(cache[key].data); + window.removeEventListener(`renderQueue.${key}`, eventHandler); + }; + window.addEventListener(`renderQueue.${key}`, eventHandler); + return; + } + setCanvasImage(cache[key].data); + return; } + + cache[key] = { data: 'pending' }; + + const canvas = await html2canvas(ref.current); + const response = canvas.toDataURL('image/png'); + cache[key] = { data: response }; + + const event = new Event(`renderQueue.${key}`); + window.dispatchEvent(event); + + setCanvasImage(response); }; + useEffect(() => { renderCanvas(); }, [ref.current]); diff --git a/packages/components/src/layout/ContextContainer/ContextContainer.js b/packages/components/src/layout/ContextContainer/ContextContainer.js index a5ebcf6b3..c4036beef 100644 --- a/packages/components/src/layout/ContextContainer/ContextContainer.js +++ b/packages/components/src/layout/ContextContainer/ContextContainer.js @@ -8,7 +8,7 @@ import { Paragraph, Title } from '../../typography'; import { ContextContainerStyles } from './ContextContainer.styles'; export const CONTEXT_CONTAINER_PADDED_TYPES = [true, false, 'vertical', 'horizontal']; - +const FLEX_ALIGNS = ['flex-start', 'center', 'flex-end']; export const CONTEXT_CONTAINER_DEFAULT_PROPS = { title: '', description: '', @@ -23,6 +23,16 @@ export const CONTEXT_CONTAINER_PROP_TYPES = { padded: PropTypes.oneOf(CONTEXT_CONTAINER_PADDED_TYPES), divided: PropTypes.bool, spacing: PropTypes.number, + direction: PropTypes.oneOf(['row', 'column']), + fullHeight: PropTypes.bool, + alignItems: PropTypes.oneOf(FLEX_ALIGNS), + justifyContent: PropTypes.oneOf(FLEX_ALIGNS), + wrap: PropTypes.oneOf(['wrap', 'nowrap']), + alignContent: PropTypes.oneOf(FLEX_ALIGNS), + className: PropTypes.string, + style: PropTypes.object, + subtitle: PropTypes.any, + children: PropTypes.any, }; const ContextContainer = ({ @@ -60,7 +70,7 @@ const ContextContainer = ({ key={`d-${i}`} noFlex orientation={direction === 'row' ? 'vertical' : 'horizontal'} - /> + />, ); } }); @@ -72,7 +82,7 @@ const ContextContainer = ({ return ( {hasTitle && ( - {typeof title === 'string' ? - () - : (subtitle) - } + {typeof title === 'string' ? ( + <Title order={3} dangerouslySetInnerHTML={{ __html: title }} /> + ) : ( + subtitle + )} </Box> )} {hasSubtitle && ( <Box> - {typeof subtitle === 'string' ? - (<Title order={5} dangerouslySetInnerHTML={{ __html: subtitle }} />) - : (subtitle) - } + {typeof subtitle === 'string' ? ( + <Title order={5} dangerouslySetInnerHTML={{ __html: subtitle }} /> + ) : ( + subtitle + )} </Box> )} {hasDescription && ( diff --git a/packages/components/src/misc/CardEmptyCover/CardEmptyCover.js b/packages/components/src/misc/CardEmptyCover/CardEmptyCover.js index 60198f966..23cf00d07 100644 --- a/packages/components/src/misc/CardEmptyCover/CardEmptyCover.js +++ b/packages/components/src/misc/CardEmptyCover/CardEmptyCover.js @@ -21,7 +21,7 @@ import { AssetDocumentIcon } from '../FileIcon/AssetDocumentIcon'; const CardEmptyCover = memo(({ icon, fileType }) => { const pairColumnRef = useRef(null); - const { canvasImage } = useHTMLToCanvas(pairColumnRef); + const { canvasImage } = useHTMLToCanvas(pairColumnRef, fileType); const FileTypeIcon = [ { key: 'video', value: <AssetVideoIcon height={24} width={24} color={'#878D96'} /> }, { key: 'audio', value: <AssetAudioIcon height={24} width={24} color={'#878D96'} /> }, @@ -80,5 +80,4 @@ CardEmptyCover.propTypes = CARD_EMPTY_COVER_PROP_TYPES; CardEmptyCover.displayName = 'CardEmptyCover'; -export default CardEmptyCover; export { CardEmptyCover }; diff --git a/packages/components/src/tokens.compiled.js b/packages/components/src/tokens.compiled.js index 85d1e699a..8f8f58bf8 100644 --- a/packages/components/src/tokens.compiled.js +++ b/packages/components/src/tokens.compiled.js @@ -1153,7 +1153,7 @@ export default { "y": 0, "blur": 4, "spread": 0, - "color": "#ffffff26", + "color": "#ffffff", "type": "dropShadow" }, "type": "boxShadow" @@ -1234,8 +1234,17 @@ export default { "type": "color" }, "hover--reverse-transparent": { - "value": "#ffffff26", - "type": "color" + "value": "#ffffff", + "type": "color", + "$extensions": { + "studio.tokens": { + "modify": { + "type": "alpha", + "value": "0.15", + "space": "lch" + } + } + } }, "down": { "value": "#F1FFBD", @@ -4930,12 +4939,30 @@ export default { "color": { "primary": { "default": { - "value": "#4d535866", - "type": "color" + "value": "#4D5358", + "type": "color", + "$extensions": { + "studio.tokens": { + "modify": { + "type": "alpha", + "value": "0.4", + "space": "lch" + } + } + } }, "hover": { - "value": "#4d5358b3", - "type": "color" + "value": "#4D5358", + "type": "color", + "$extensions": { + "studio.tokens": { + "modify": { + "type": "alpha", + "value": "0.7", + "space": "lch" + } + } + } }, "down": { "value": "#4D5358",