import React, { useCallback, useContext, useMemo, useState } from 'react'; import styled from 'styled-components'; import { colors } from '../../config.json'; import log from '../../shared/logging'; import { useMounted } from '../lib/utilityHooks'; import { StyledButtonContent, StyledHiddenSide, StyledLabel, StyledLeft, StyledRight, StyledVisibleSide, transparentButton, } from './AppButtonStyles'; import { measurements } from './common-styles'; import ImageView from './ImageView'; interface ILabelProps { textOffset?: number; children?: React.ReactNode; } export function Label(props: ILabelProps) { return {props.children}; } interface IIconProps { source: string; width?: number; height?: number; } export function Icon(props: IIconProps) { return ; } export interface IProps extends React.HTMLAttributes { children?: React.ReactNode; className?: string; disabled?: boolean; onClick?: () => void; textOffset?: number; } type ChildrenGroups = { left: React.ReactNode[]; label: React.ReactNode; right: React.ReactNode[] }; const BaseButton = React.memo(function BaseButtonT(props: IProps) { const { children, textOffset, ...otherProps } = props; const groupedChildren = useMemo(() => { return React.Children.toArray(children).reduce( (groups: ChildrenGroups, child) => { if (groups.label === undefined && typeof child === 'string') { return { ...groups, label: }; } else if (React.isValidElement(child) && child.type === Label) { return { ...groups, label: React.cloneElement(child as React.ReactElement, { textOffset }), }; } else if (groups.label === undefined) { return { ...groups, left: [...groups.left, child] }; } else { return { ...groups, right: [...groups.right, child] }; } }, { left: [], label: undefined, right: [] }, ); }, [children, textOffset]); return ( {groupedChildren.left} {groupedChildren.right} {groupedChildren.label ?? ); }); function SimpleButtonT(props: React.ButtonHTMLAttributes) { const blockingContext = useContext(BlockingContext); return ( ); } export const SimpleButton = React.memo(SimpleButtonT); const StyledSimpleButton = styled(SimpleButton)({ display: 'flex', cursor: 'default', borderRadius: 4, border: 'none', padding: 0, '&&:disabled': { opacity: 0.5, }, }); interface IBlockingContext { disabled?: boolean; onClick?: () => Promise; } const BlockingContext = React.createContext({}); interface IBlockingProps { children?: React.ReactNode; onClick: () => Promise; disabled?: boolean; } export function BlockingButton(props: IBlockingProps) { const isMounted = useMounted(); const [isBlocked, setIsBlocked] = useState(false); const onClick = useCallback(async () => { setIsBlocked(true); try { await props.onClick(); } catch (error) { log.error(`onClick() failed - ${error}`); } if (isMounted()) { setIsBlocked(false); } }, [props.onClick]); const contextValue = useMemo( () => ({ disabled: isBlocked || props.disabled, onClick, }), [isBlocked, props.disabled, onClick], ); return {props.children}; } export const RedButton = styled(BaseButton)({ backgroundColor: colors.red, '&&:not(:disabled):hover': { backgroundColor: colors.red95, }, }); export const GreenButton = styled(BaseButton)({ backgroundColor: colors.green, '&&:not(:disabled):hover': { backgroundColor: colors.green90, }, }); export const BlueButton = styled(BaseButton)({ backgroundColor: colors.blue80, '&&:not(:disabled):hover': { backgroundColor: colors.blue60, }, }); export const TransparentButton = styled(BaseButton)(transparentButton, { backgroundColor: colors.white20, '&&:not(:disabled):hover': { backgroundColor: colors.white40, }, }); export const RedTransparentButton = styled(BaseButton)(transparentButton, { backgroundColor: colors.red60, '&&:not(:disabled):hover': { backgroundColor: colors.red80, }, }); const StyledButtonWrapper = styled.div({ display: 'flex', flexDirection: 'column', flex: 0, '&&:not(:last-child)': { marginBottom: measurements.buttonVerticalMargin, }, }); interface IButtonGroupProps { children: React.ReactNode | React.ReactNode[]; } export function ButtonGroup(props: IButtonGroupProps) { return ( <> {React.Children.map(props.children, (button, index) => ( {button} ))} ); }