import classnames from 'classnames/bind'; import React, { Children, ReactNode, useCallback, useState } from 'react'; import { KeyCode } from '../../helpers/key-code'; import { BoxDivProps, SilkeBox } from '../silke-box'; import { SilkeButton, SilkeButtonSet, SilkeButtonSetProps } from '../silke-button'; import ButtonContext from '../silke-button/context'; import { SilkeIcon, SilkeIcons } from '../silke-icon'; import { SilkeImage } from '../silke-image'; import { SilkeOverflowMenu, SilkeOverflowMenuItem } from '../silke-overflow-menu'; import { SilkeSkeleton } from '../silke-skeleton'; import { SilkeTagGreen } from '../silke-tag'; import { SilkeText, SilkeTitle } from '../silke-text'; import styles from './silke-tile.scss'; const cl = classnames.bind(styles); export type SilkeTileProps = { image?: string; icon?: SilkeIcons; autoFocus?: boolean; title?: React.ReactNode; titleKind?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; subTitle?: React.ReactNode; text?: string; tags?: string[]; loading?: boolean; /** * Width of tile * tiny = 256 * small = 512 * medium = 768 * large = 1024 */ width?: 'tiny' | 'small' | 'medium' | 'large'; selected?: boolean; menu?: SilkeOverflowMenuItem[]; expandable?: boolean | (() => Promise); expanded?: boolean; onExpand?: (expanding?: boolean) => void | Promise; expandOpenText?: string; expandCloseText?: string; expandLoadingText?: string; hideSelectedCheck?: boolean; children?: React.ReactNode | React.ReactNode[]; } & Omit; export function SilkeTile({ image, icon, title, titleKind = 'h5', subTitle, text, loading, children, className, width, selected, menu, expanded, expandable, onExpand, tags, expandOpenText = 'Show more', expandCloseText = 'Hide', expandLoadingText = 'Loading...', hideSelectedCheck = false, ...rest }: SilkeTileProps) { const [loadingExpand, setLoadingExpand] = useState(false); const [toggle, setToggle] = useState(!!expanded); let actions: ReactNode = null; const filteredChildren = Children.map(children, (c) => { if (React.isValidElement(c) && c.type === SilkeTileActions) { actions = c; return false; } return c; }); const handleToggle = useCallback(async () => { setToggle(!toggle); // Async expand if (onExpand && !toggle) { const res = onExpand(true); if (res) { setLoadingExpand(true); await res; setLoadingExpand(false); } } }, [toggle, onExpand]); return ( { if (e.key === KeyCode.enter && rest.onClick) { rest.onClick(e); } } : undefined } className={cl('tile', className, width, { clickable: Boolean(rest.onClick), selected, })} {...rest} > {loading && ( )} {image && } {(icon || title || subTitle) && ( {icon && } {title} {expandable && ( )} {menu && } {subTitle && {subTitle}} {tags && ( {tags.map((t, i) => ( ))} )} )} {expandable ? (
{toggle && ( {loadingExpand ? : filteredChildren} )}
) : ( {filteredChildren} )}
{selected && !hideSelectedCheck && ( )}
{actions}
); } type SilkeTileActions = Omit; export function SilkeTileActions({ children, className, ...rest }: SilkeTileActions) { const hasAtLeastTwoButtons = React.Children.toArray(children).filter(Boolean).length > 1; return hasAtLeastTwoButtons ? ( {children} ) : ( {children} ); }