diff options
Diffstat (limited to 'packages/components/src')
3 files changed, 315 insertions, 362 deletions
diff --git a/packages/components/src/components/ColorPicker/ColorPicker.tsx b/packages/components/src/components/ColorPicker/ColorPicker.tsx index 51b820a37..632b470f0 100644 --- a/packages/components/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/components/src/components/ColorPicker/ColorPicker.tsx @@ -1,204 +1,156 @@ -import React, { useState } from 'react' -import { GithubPicker, ChromePicker, BlockPicker, SliderPicker, SketchPicker } from 'react-color' -import { IGlobalProps, Size, Type , getFormLabelSize } from '../../global' -import { Button } from '../Button' -import { IconButton } from '../IconButton' -import { Popup, PopupTrigger } from '../Popup' -import './ColorPicker.scss' -import { Dropdown, DropdownType } from '../Dropdown' +import React, { useRef, useState } from 'react'; +import { GithubPicker, ChromePicker, BlockPicker, SliderPicker, SketchPicker } from 'react-color'; +import { IGlobalProps, Size, Type, getFormLabelSize } from '../../global'; +import { Button } from '../Button'; +import { IconButton } from '../IconButton'; +import { Popup, PopupTrigger } from '../Popup'; +import './ColorPicker.scss'; +import { Dropdown, DropdownType } from '../Dropdown'; -export const ColorPickerArray= ["Classic", "Chrome", "GitHub", "Block", "Slider"] -export type ColorPickerType= typeof ColorPickerArray[number]; +export const ColorPickerArray = ['Classic', 'Chrome', 'GitHub', 'Block', 'Slider']; +export type ColorPickerType = (typeof ColorPickerArray)[number]; export interface IColorPickerProps extends IGlobalProps { - text?: string - icon?: JSX.Element | string - colorPickerType?: ColorPickerType - defaultPickerType?: ColorPickerType - selectedColor?: string - setSelectedColor: (color: any) => unknown - setFinalColor: (color:any) => unknown + text?: string; + icon?: JSX.Element | string; + colorPickerType?: ColorPickerType; + defaultPickerType?: ColorPickerType; + selectedColor?: string; + setSelectedColor: (color: any) => unknown; + setFinalColor: (color: any) => unknown; } export const ColorPicker = (props: IColorPickerProps) => { - const [selectedColorLoc, setSelectedColorLoc] = useState(); - const { defaultPickerType, text, colorPickerType, fillWidth, formLabelPlacement, size = Size.SMALL, type = Type.TERT, icon, selectedColor = selectedColorLoc, setSelectedColor = setSelectedColorLoc, setFinalColor = setSelectedColorLoc, tooltip, color='black', formLabel } = props - const [isOpen, setOpen] = useState<boolean>(false) - const [pickerSelectorOpen, setPickerSelectorOpen] = useState<boolean>(false); - const decimalToHexString = (number: number) => { - if (number < 0) { - number = 0xffffffff + number + 1; - } - return (number < 16 ? '0' : '') + number.toString(16).toUpperCase(); -} - const colorString = (color: any ) => { - return color.hex === 'transparent' ? color.hex: color.hex + (color.rgb.a ? decimalToHexString(Math.round(color.rgb.a * 255)) : 'ff'); - } - const onChange = (color: any) => { - setSelectedColor(colorString(color) as any); - } - const onChangeComplete = (color: any) => { - setFinalColor(colorString(color) as any); - } - const [picker, setPicker] = useState<string>(defaultPickerType ?? "Classic") + const [selectedColorLoc, setSelectedColorLoc] = useState(); + const { + defaultPickerType, + text, + colorPickerType, + fillWidth, + formLabelPlacement, + size = Size.SMALL, + type = Type.TERT, + icon, + selectedColor = selectedColorLoc, + setSelectedColor = setSelectedColorLoc, + setFinalColor = setSelectedColorLoc, + tooltip, + color = 'black', + formLabel, + } = props; + const [isOpen, setOpen] = useState<boolean>(false); + const [pickerSelectorOpen, setPickerSelectorOpen] = useState<boolean>(false); + const decimalToHexString = (number: number) => { + if (number < 0) { + number = 0xffffffff + number + 1; + } + return (number < 16 ? '0' : '') + number.toString(16).toUpperCase(); + }; + const colorString = (color: any) => { + return color.hex === 'transparent' ? color.hex : color.hex + (color.rgb.a ? decimalToHexString(Math.round(color.rgb.a * 255)) : 'ff'); + }; + const onChange = (color: any) => { + setSelectedColor(colorString(color) as any); + }; + const onChangeComplete = (color: any) => { + setFinalColor(colorString(color) as any); + }; + const [picker, setPicker] = useState<string>(defaultPickerType ?? 'Classic'); - const getToggle = () => { - if (icon && !text) { - return ( - <IconButton - active={isOpen} - tooltip={tooltip} - type={type} - color={color} - size={size} - icon={icon} - colorPicker={selectedColor} - fillWidth={fillWidth} - /> - ) - } else if (text) { - return ( - <Button - active={isOpen} - tooltip={tooltip} - size={size} - type={type} - color={color} - text={text} - icon={icon} - align={'flex-start'} - iconPlacement={'left'} - colorPicker={selectedColor} - fillWidth={fillWidth} - /> - ) - } else { - return ( - <IconButton - active={isOpen} - tooltip={tooltip} - type={type} - color={color} - size={size} - icon={icon} - colorPicker={selectedColor} - fillWidth={fillWidth} - /> - ) - } - } + const toggleRef = useRef<HTMLDivElement | null>(null); + const getToggle = () => ( + <div ref={toggleRef}> + {icon && !text ? ( + <IconButton active={isOpen} tooltip={tooltip} type={type} color={color} size={size} icon={icon} colorPicker={selectedColor} fillWidth={fillWidth} /> + ) : text ? ( + <Button active={isOpen} tooltip={tooltip} size={size} type={type} color={color} text={text} icon={icon} align={'flex-start'} iconPlacement={'left'} colorPicker={selectedColor} fillWidth={fillWidth} /> + ) : ( + <IconButton active={isOpen} tooltip={tooltip} type={type} color={color} size={size} icon={icon} colorPicker={selectedColor} fillWidth={fillWidth} /> + )} + </div> + ); - const getColorPicker = (pickerType: ColorPickerType):JSX.Element => { - const colorPalette = ["FFFFFF", "#F9F6F2", "#E2E2E2", "#D1D1D1", "#737576", "#4b4a4d", "#222021", - '#EB9694', '#FAD0C3', '#FEF3BD', '#C1E1C5', '#BEDADC', '#C4DEF6', '#BED3F3', '#D4C4FB', "transparent" - ] - switch(pickerType) { - case "Block": - return ( - <BlockPicker - color={selectedColor} - triangle={'hide'} - colors={colorPalette} - onChange={onChange} - onChangeComplete={onChangeComplete} - /> - ); - case "Classic": - return (<SketchPicker - onChange={onChange} - onChangeComplete={onChangeComplete} - presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']} - color={selectedColor} - />); - case "Chrome": - default: - return ( - <ChromePicker - color={selectedColor} - onChange={onChange} - onChangeComplete={onChangeComplete} - /> - ); - case "GitHub": - return ( - <GithubPicker - color={selectedColor} - colors={colorPalette} - triangle={'hide'} - onChange={onChange} - onChangeComplete={onChangeComplete} - /> - ); - case "Slider": - return ( - <div style={{width: 200, height: 50}}> - <SliderPicker - color={selectedColor} - onChange={onChange} - onChangeComplete={onChangeComplete} - /> - </div> - ); - } - } - const openChanged = (isOpen:boolean) => setPickerSelectorOpen(isOpen); + const getColorPicker = (pickerType: ColorPickerType): JSX.Element => { + const colorPalette = ['FFFFFF', '#F9F6F2', '#E2E2E2', '#D1D1D1', '#737576', '#4b4a4d', '#222021', '#EB9694', '#FAD0C3', '#FEF3BD', '#C1E1C5', '#BEDADC', '#C4DEF6', '#BED3F3', '#D4C4FB', 'transparent']; + switch (pickerType) { + case 'Classic': return ( + <SketchPicker + onChange={onChange} + onChangeComplete={onChangeComplete} + presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']} + color={selectedColor} + /> + ); + case 'Chrome': + default: return <ChromePicker color={selectedColor} onChange={onChange} onChangeComplete={onChangeComplete} />; + case 'GitHub': return <GithubPicker color={selectedColor} colors={colorPalette} triangle={'hide'} onChange={onChange} onChangeComplete={onChangeComplete} />; + case 'Block': return <BlockPicker color={selectedColor} triangle={'hide'} colors={colorPalette} onChange={onChange} onChangeComplete={onChangeComplete} />; + case 'Slider': return ( + <div style={{ width: 200, height: 50 }}> + <SliderPicker color={selectedColor} onChange={onChange} onChangeComplete={onChangeComplete} /> + </div> + ); + } // prettier-ignore + }; + const openChanged = (isOpen: boolean) => setPickerSelectorOpen(isOpen); - const getPopup = ():JSX.Element => { - if (colorPickerType){ - return getColorPicker(colorPickerType) - } else { - // Todo: this would be much easier if the selectedColor was a Color, not a string. - const newColor = (selectedColor === 'transparent' ? 'white': selectedColor?.startsWith("#") ? selectedColor.substring(0,7): - selectedColor?.startsWith('rgba') ? selectedColor?.replace(/,[0-9]*\)/,"1)") : selectedColor); - return <div style={{height: 'fit-content'}}> - <Dropdown - items={ - ColorPickerArray.map((item) => { - return { - text: item, - val: item, - } - }) - } - activeChanged={openChanged} - placement={'right'} - color={newColor} - type={Type.PRIM} - dropdownType={DropdownType.SELECT} - selectedVal={picker} - setSelectedVal={(val) => setPicker(val as string)} - fillWidth - /> - {getColorPicker(picker)} - </div> - } - } + const getPopup = (): JSX.Element => { + if (colorPickerType) { + return getColorPicker(colorPickerType); + } else { + // Todo: this would be much easier if the selectedColor was a Color, not a string. + const newColor = selectedColor === 'transparent' ? 'white' : selectedColor?.startsWith('#') ? selectedColor.substring(0, 7) : selectedColor?.startsWith('rgba') ? selectedColor?.replace(/,[0-9]*\)/, '1)') : selectedColor; + return ( + <div style={{ height: 'fit-content' }}> + <Dropdown + items={ColorPickerArray.map(item => { + return { + text: item, + val: item, + }; + })} + activeChanged={openChanged} + placement={'right'} + color={newColor} + type={Type.PRIM} + dropdownType={DropdownType.SELECT} + selectedVal={picker} + setSelectedVal={val => setPicker(val as string)} + fillWidth + /> + {getColorPicker(picker)} + </div> + ); + } + }; - const popupContainsPt = (x:number, y:number) => pickerSelectorOpen; + const popupContainsPt = (x: number, y: number) => { + const rect = toggleRef.current?.getBoundingClientRect(); + return rect && rect.left < x && rect.top < y && rect.right > x && rect.bottom > y; + }; - const colorPicker: JSX.Element = - ( - <Popup - toggle={getToggle()} - trigger={PopupTrigger.CLICK} - isOpen={isOpen} - setOpen={setOpen} - tooltip={tooltip} - size={size} - color={selectedColor} - popup={getPopup()} - popupContainsPt={popupContainsPt} // this should prohbably test to see if the click pt is actually within the picker selector list popup. - /> - ) + const colorPicker: JSX.Element = ( + <Popup + toggle={getToggle()} + trigger={PopupTrigger.CLICK} + isOpen={isOpen} + setOpen={setOpen} + tooltip={tooltip} + size={size} + color={selectedColor} + popup={getPopup()} + popupContainsPt={popupContainsPt} // this should prohbably test to see if the click pt is actually within the picker selector list popup. + /> + ); - return ( - formLabel ? - <div className={`form-wrapper ${formLabelPlacement}`} -style={{ width: fillWidth ? '100%' : undefined}}> - <div className={'formLabel'} style={{fontSize: getFormLabelSize(size)}}>{formLabel}</div> - {colorPicker} - </div> - : - colorPicker - ) -} + return formLabel ? ( + <div className={`form-wrapper ${formLabelPlacement}`} style={{ width: fillWidth ? '100%' : undefined }}> + <div className={'formLabel'} style={{ fontSize: getFormLabelSize(size) }}> + {formLabel} + </div> + {colorPicker} + </div> + ) : ( + colorPicker + ); +}; diff --git a/packages/components/src/components/IconButton/IconButton.tsx b/packages/components/src/components/IconButton/IconButton.tsx index 7d273e23f..954a0ae44 100644 --- a/packages/components/src/components/IconButton/IconButton.tsx +++ b/packages/components/src/components/IconButton/IconButton.tsx @@ -1,157 +1,153 @@ -import { Tooltip } from '@mui/material' -import React from 'react' -import { Colors, Size, Type, getFontSize, getHeight, isDark, getFormLabelSize } from '../../global' -import { IButtonProps } from '../Button' -import './IconButton.scss' +import { Tooltip } from '@mui/material'; +import React from 'react'; +import { Colors, Size, Type, getFontSize, getHeight, isDark, getFormLabelSize } from '../../global'; +import { IButtonProps } from '../Button'; +import './IconButton.scss'; export interface IIconButtonProps extends IButtonProps {} export const IconButton = (props: IButtonProps) => { - const { - active, - icon, - onClick, - onDoubleClick, - onPointerDown, - inactive, - type = Type.PRIM, - color = Colors.MEDIUM_BLUE, - background, - label, - height, - size = Size.SMALL, - style, - tooltip, - tooltipPlacement = 'top', - colorPicker, - formLabel, - formLabelPlacement, - hideLabel, - fillWidth - } = props + const { + active, + icon, + onClick, + onDoubleClick, + onPointerDown, + inactive, + type = Type.PRIM, + color = Colors.MEDIUM_BLUE, + background, + label, + height, + size = Size.SMALL, + style, + tooltip, + tooltipPlacement = 'top', + colorPicker, + formLabel, + formLabelPlacement, + hideLabel, + fillWidth, + } = props; - /** - * Pointer down - * @param e - */ - const handlePointerDown = (e: React.PointerEvent) => { - - if (!inactive && onPointerDown) { - e.stopPropagation(); - e.preventDefault(); - onPointerDown(e) - } - } + /** + * Pointer down + * @param e + */ + const handlePointerDown = (e: React.PointerEvent) => { + if (!inactive && onPointerDown) { + e.stopPropagation(); + e.preventDefault(); + onPointerDown(e); + } + }; - /** - * In the event that there is a single click - * @param e - */ - const handleClick = (e: React.MouseEvent) => { - if (!inactive && onClick) { - e.stopPropagation(); - e.preventDefault(); - onClick(e) - } - } + /** + * In the event that there is a single click + * @param e + */ + const handleClick = (e: React.MouseEvent) => { + if (!inactive && onClick) { + e.stopPropagation(); + e.preventDefault(); + onClick(e); + } + }; - /** - * Double click - * @param e - */ - const handleDoubleClick = (e: React.MouseEvent) => { - if (!inactive && onDoubleClick){ - e.stopPropagation(); - e.preventDefault(); - onDoubleClick(e) - } - } + /** + * Double click + * @param e + */ + const handleDoubleClick = (e: React.MouseEvent) => { + if (!inactive && onDoubleClick) { + e.stopPropagation(); + e.preventDefault(); + onDoubleClick(e); + } + }; - const getBorderColor = (): Colors | string | undefined => { - switch(type){ - case Type.PRIM: - return undefined; - case Type.SEC: - return color; - case Type.TERT: - if (colorPicker) return colorPicker; - if (active) return color; - else return color; - } - } + const getBorderColor = (): Colors | string | undefined => { + switch (type) { + case Type.PRIM: + return undefined; + case Type.SEC: + return color; + case Type.TERT: + if (colorPicker) return colorPicker; + if (active) return color; + else return color; + } + }; - const getColor = (): Colors | string | undefined => { - if (color && background) return color; - switch(type){ - case Type.PRIM: - return color; - case Type.SEC: - return color; - case Type.TERT: - if (colorPicker) { - if (isDark(colorPicker)) return Colors.WHITE; - else return Colors.BLACK + const getColor = (): Colors | string | undefined => { + if (color && background) return color; + switch (type) { + case Type.PRIM: + return color; + case Type.SEC: + return color; + case Type.TERT: + if (colorPicker) { + if (isDark(colorPicker)) return Colors.WHITE; + else return Colors.BLACK; + } + if (isDark(color)) return Colors.WHITE; + else return Colors.BLACK; } - if (isDark(color)) return Colors.WHITE; - else return Colors.BLACK - } - } + }; - const getBackground = (): Colors | string | undefined => { - if(background) return background; - switch(type){ - case Type.PRIM: - return color; - case Type.SEC: - return color; - case Type.TERT: - if (colorPicker) return colorPicker - else return color - } - } + const getBackground = (): Colors | string | undefined => { + if (background) return background; + switch (type) { + case Type.PRIM: + return color; + case Type.SEC: + return color; + case Type.TERT: + if (colorPicker) return colorPicker; + else return color; + } + }; - const defaultProperties: React.CSSProperties = { - height: getHeight(height, size), - width: fillWidth ? '100%' : getHeight(height, size), - minWidth: getHeight(height, size), - fontWeight: 500, - fontSize: getFontSize(size, true), - borderColor: getBorderColor(), - color: getColor() - } + const defaultProperties: React.CSSProperties = { + height: getHeight(height, size), + width: fillWidth ? '100%' : getHeight(height, size), + minWidth: getHeight(height, size), + fontWeight: 500, + fontSize: getFontSize(size, true), + borderColor: getBorderColor(), + color: getColor(), + }; - const backgroundProperties: React.CSSProperties = { - background: getBackground() - } + const backgroundProperties: React.CSSProperties = { + background: getBackground(), + }; - const iconButton: JSX.Element = ( - <Tooltip disableInteractive={true} arrow={true} placement={tooltipPlacement} title={tooltip}> - <div - className={`iconButton-container ${type} ${inactive && 'inactive'}`} - onClick={handleClick} - onDoubleClick={handleDoubleClick} - onPointerDown={handlePointerDown} - style={{...defaultProperties, ...style}} - tabIndex={-1} - > - <div className="iconButton-content"> - {icon} - {colorPicker && type !== (Type.TERT) && <div className={`color`} style={{background: colorPicker, outlineColor: defaultProperties.color}}/>} - {label && !hideLabel && <div className={'iconButton-label'} style={{color: defaultProperties.color}}>{label}</div>} - </div> - <div className={`background ${active && 'active'} ${inactive && 'inactive'}`} style={backgroundProperties}/> - </div> - </Tooltip> - ) + const iconButton: JSX.Element = ( + <Tooltip disableInteractive={true} arrow={true} placement={tooltipPlacement} title={tooltip}> + <div className={`iconButton-container ${type} ${inactive && 'inactive'}`} onClick={handleClick} onDoubleClick={handleDoubleClick} onPointerDown={handlePointerDown} style={{ ...defaultProperties, ...style }} tabIndex={-1}> + <div className="iconButton-content"> + {icon} + {colorPicker && type !== Type.TERT && <div className={`color`} style={{ background: colorPicker, outlineColor: defaultProperties.color }} />} + {label && !hideLabel && ( + <div className={'iconButton-label'} style={{ color: defaultProperties.color }}> + {label} + </div> + )} + </div> + <div className={`background ${active && 'active'} ${inactive && 'inactive'}`} style={backgroundProperties} /> + </div> + </Tooltip> + ); - return ( - formLabel ? - <div className={`form-wrapper ${formLabelPlacement}`} -style={{ width: fillWidth ? '100%' : undefined}}> - <div className={'formLabel'} style={{fontSize: getFormLabelSize(size)}}>{formLabel}</div> - {iconButton} - </div> - : - iconButton - ) -} + return formLabel ? ( + <div className={`form-wrapper ${formLabelPlacement}`} style={{ width: fillWidth ? '100%' : undefined }}> + <div className={'formLabel'} style={{ fontSize: getFormLabelSize(size) }}> + {formLabel} + </div> + {iconButton} + </div> + ) : ( + iconButton + ); +}; diff --git a/packages/components/src/components/Popup/Popup.tsx b/packages/components/src/components/Popup/Popup.tsx index f0920b723..5a58fee29 100644 --- a/packages/components/src/components/Popup/Popup.tsx +++ b/packages/components/src/components/Popup/Popup.tsx @@ -63,12 +63,19 @@ export const Popup = (props: IPopupProps) => { const triggerRef = useRef(null); const popperRef = useRef<HTMLDivElement | null>(null); + const toggleRef = useRef<HTMLDivElement | null>(null); let timeout = setTimeout(() => {}); const handlePointerAwayDown = (e: PointerEvent) => { const rect = popperRef.current?.getBoundingClientRect(); - if (rect && !(rect.left < e.clientX && rect.top < e.clientY && rect.right > e.clientX && rect.bottom > e.clientY) && !popupContainsPt?.(e.clientX, e.clientY)) { + const rect2 = toggleRef.current?.getBoundingClientRect(); + if ( + (!rect2 || !(rect2.left < e.clientX && rect2.top < e.clientY && rect2.right > e.clientX && rect2.bottom > e.clientY)) && + rect && + !(rect.left < e.clientX && rect.top < e.clientY && rect.right > e.clientX && rect.bottom > e.clientY) && + !popupContainsPt?.(e.clientX, e.clientY) + ) { e.preventDefault(); setOpen(false); e.stopPropagation(); @@ -79,9 +86,7 @@ export const Popup = (props: IPopupProps) => { if (isOpen) { window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true }); window.addEventListener('pointerdown', handlePointerAwayDown, { capture: true }); - return () => { - window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true }); - }; + return () => window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true }); } }, [isOpen, popupContainsPt]); @@ -104,31 +109,31 @@ export const Popup = (props: IPopupProps) => { timeout = setTimeout(() => setOpen(false), 1000); } }}> - {toggle ? ( - toggle - ) : ( - <Toggle - tooltip={tooltip} - size={size} - type={type} - color={color} - background={props.isToggle ? undefined : background} - toggleType={ToggleType.BUTTON} - icon={icon} - iconPlacement={iconPlacement} - text={text} - label={props.label} - toggleStatus={isOpen || props.toggleStatus} - onClick={() => { - if (trigger === PopupTrigger.CLICK) { - if (!props.isToggle || props.toggleStatus) { - setOpen(!isOpen); + {toggle ?? ( + <div ref={toggleRef}> + <Toggle + tooltip={tooltip} + size={size} + type={type} + color={color} + background={props.isToggle ? undefined : background} + toggleType={ToggleType.BUTTON} + icon={icon} + iconPlacement={iconPlacement} + text={text} + label={props.label} + toggleStatus={isOpen || props.toggleStatus} + onClick={() => { + if (trigger === PopupTrigger.CLICK) { + if (!props.isToggle || props.toggleStatus) { + setOpen(!isOpen); + } + props.toggleFunc?.(); } - props.toggleFunc?.(); - } - }} - fillWidth={fillWidth} - /> + }} + fillWidth={fillWidth} + /> + </div> )} </div> <Popper open={isOpen} style={{ zIndex: 20000 }} anchorEl={triggerRef.current} placement={placement} modifiers={[]}> |