refactor(livechat): Solve lint warnings (#39338)
Co-authored-by: Copilot <copilot@github.com>playwright-1.59.1
parent
5aff6f2436
commit
f9560cb167
@ -0,0 +1,19 @@ |
||||
import type { CSSProperties } from 'preact/compat'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
export type CharCounterProps = { |
||||
className?: string; |
||||
style?: CSSProperties; |
||||
textLength: number; |
||||
limitTextLength: number; |
||||
}; |
||||
|
||||
const CharCounter = ({ className, style = {}, textLength, limitTextLength }: CharCounterProps) => ( |
||||
<span className={createClassName(styles, 'footer__remainder', { highlight: textLength === limitTextLength }, [className])} style={style}> |
||||
{textLength} / {limitTextLength} |
||||
</span> |
||||
); |
||||
|
||||
export default CharCounter; |
||||
@ -0,0 +1,17 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
export type FooterProps = { |
||||
children: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
const Footer = ({ children, className, ...props }: FooterProps) => ( |
||||
<footer className={createClassName(styles, 'footer', {}, [className])} {...props}> |
||||
{children} |
||||
</footer> |
||||
); |
||||
|
||||
export default Footer; |
||||
@ -0,0 +1,17 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
export type FooterContentProps = { |
||||
children: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
const FooterContent = ({ children, className, ...props }: FooterContentProps) => ( |
||||
<div className={createClassName(styles, 'footer__content', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default FooterContent; |
||||
@ -0,0 +1,16 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import { MenuPopover } from '../Menu'; |
||||
import OptionsTrigger from './OptionsTrigger'; |
||||
|
||||
export type FooterOptionsProps = { |
||||
children: ComponentChildren; |
||||
}; |
||||
|
||||
const FooterOptions = ({ children }: FooterOptionsProps) => ( |
||||
<MenuPopover trigger={OptionsTrigger} overlayed> |
||||
{children} |
||||
</MenuPopover> |
||||
); |
||||
|
||||
export default FooterOptions; |
||||
@ -0,0 +1,23 @@ |
||||
import { type MouseEventHandler } from 'preact/compat'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
const handleMouseUp: MouseEventHandler<HTMLButtonElement> = ({ target }) => (target as HTMLButtonElement | null)?.blur(); |
||||
|
||||
type OptionsTriggerProps = { |
||||
pop: () => void; |
||||
}; |
||||
|
||||
const OptionsTrigger = ({ pop }: OptionsTriggerProps) => { |
||||
const { t } = useTranslation(); |
||||
|
||||
return ( |
||||
<button className={createClassName(styles, 'footer__options')} onClick={pop} onMouseUp={handleMouseUp}> |
||||
{t('options')} |
||||
</button> |
||||
); |
||||
}; |
||||
|
||||
export default OptionsTrigger; |
||||
@ -0,0 +1,25 @@ |
||||
import { RocketChatLogo } from '@rocket.chat/logo'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type PoweredByProps = { |
||||
className?: string; |
||||
}; |
||||
|
||||
export const PoweredBy = ({ className, ...props }: PoweredByProps) => { |
||||
const { t } = useTranslation(); |
||||
|
||||
return ( |
||||
<h3 data-qa='livechat-watermark' className={createClassName(styles, 'powered-by', {}, [className])} {...props}> |
||||
{t('powered_by_rocket_chat').split('Rocket.Chat')[0]} |
||||
<a className={createClassName(styles, 'powered-by__logo')} href='https://rocket.chat' target='_blank' rel='noopener noreferrer'> |
||||
<RocketChatLogo /> |
||||
</a> |
||||
{t('powered_by_rocket_chat').split('Rocket.Chat')[1]} |
||||
</h3> |
||||
); |
||||
}; |
||||
|
||||
export default PoweredBy; |
||||
@ -0,0 +1,6 @@ |
||||
export { default as Footer } from './Footer'; |
||||
export { default as FooterContent } from './FooterContent'; |
||||
export { default as PoweredBy } from './PoweredBy'; |
||||
export { default as OptionsTrigger } from './OptionsTrigger'; |
||||
export { default as FooterOptions } from './FooterOptions'; |
||||
export { default as CharCounter } from './CharCounter'; |
||||
@ -1,64 +0,0 @@ |
||||
import { RocketChatLogo } from '@rocket.chat/logo'; |
||||
import type { ComponentChildren } from 'preact'; |
||||
import type { JSXInternal } from 'preact/src/jsx'; |
||||
import { useTranslation, withTranslation } from 'react-i18next'; |
||||
|
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
import { PopoverMenu } from '../Menu'; |
||||
import styles from './styles.scss'; |
||||
|
||||
export const Footer = ({ children, className, ...props }: { children: ComponentChildren; className?: string }) => ( |
||||
<footer className={createClassName(styles, 'footer', {}, [className])} {...props}> |
||||
{children} |
||||
</footer> |
||||
); |
||||
|
||||
export const FooterContent = ({ children, className, ...props }: { children: ComponentChildren; className?: string }) => ( |
||||
<div className={createClassName(styles, 'footer__content', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export const PoweredBy = withTranslation()(({ className, t, ...props }: { className?: string; t: (translationKey: string) => string }) => ( |
||||
<h3 data-qa='livechat-watermark' className={createClassName(styles, 'powered-by', {}, [className])} {...props}> |
||||
{t('powered_by_rocket_chat').split('Rocket.Chat')[0]} |
||||
<a className={createClassName(styles, 'powered-by__logo')} href='https://rocket.chat' target='_blank' rel='noopener noreferrer'> |
||||
<RocketChatLogo /> |
||||
</a> |
||||
{t('powered_by_rocket_chat').split('Rocket.Chat')[1]} |
||||
</h3> |
||||
)); |
||||
|
||||
const handleMouseUp: JSXInternal.MouseEventHandler<HTMLButtonElement> = ({ target }: { target: EventTarget | null }) => |
||||
(target as HTMLButtonElement)?.blur(); |
||||
|
||||
const OptionsTrigger = ({ pop }: { pop: () => void }) => { |
||||
const { t } = useTranslation(); |
||||
return ( |
||||
<button className={createClassName(styles, 'footer__options')} onClick={pop} onMouseUp={handleMouseUp}> |
||||
{t('options')} |
||||
</button> |
||||
); |
||||
}; |
||||
|
||||
export const FooterOptions = ({ children }: { children: ComponentChildren }) => ( |
||||
<PopoverMenu trigger={OptionsTrigger} overlayed> |
||||
{children} |
||||
</PopoverMenu> |
||||
); |
||||
|
||||
export const CharCounter = ({ |
||||
className, |
||||
style = {}, |
||||
textLength, |
||||
limitTextLength, |
||||
}: { |
||||
className?: string; |
||||
style: JSXInternal.CSSProperties; |
||||
textLength: number; |
||||
limitTextLength: number; |
||||
}) => ( |
||||
<span className={createClassName(styles, 'footer__remainder', { highlight: textLength === limitTextLength }, [className])} style={style}> |
||||
{textLength} / {limitTextLength} |
||||
</span> |
||||
); |
||||
@ -0,0 +1,39 @@ |
||||
import type { ComponentChildren, Ref } from 'preact'; |
||||
import type { MouseEventHandler } from 'preact/compat'; |
||||
import type { JSXInternal } from 'preact/src/jsx'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import type { Theme } from '../../Theme'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderProps = { |
||||
children?: ComponentChildren; |
||||
theme?: Partial<Theme>; |
||||
className?: string; |
||||
post?: ComponentChildren; |
||||
large?: boolean; |
||||
style?: JSXInternal.CSSProperties; |
||||
ref?: Ref<HTMLElement>; |
||||
onClick?: MouseEventHandler<HTMLElement>; |
||||
}; |
||||
|
||||
const Header = ({ |
||||
children, |
||||
theme: { color: backgroundColor, fontColor: color } = {}, |
||||
className, |
||||
post, |
||||
large, |
||||
style, |
||||
...props |
||||
}: HeaderProps) => ( |
||||
<header |
||||
className={createClassName(styles, 'header', { large }, [className])} |
||||
style={style || backgroundColor || color ? { ...(style || {}), backgroundColor, color } : undefined} |
||||
{...props} |
||||
> |
||||
{children} |
||||
{post} |
||||
</header> |
||||
); |
||||
|
||||
export default Header; |
||||
@ -0,0 +1,18 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderActionProps = { |
||||
children?: ComponentChildren; |
||||
className?: string; |
||||
onClick?: () => void; |
||||
}; |
||||
|
||||
const HeaderAction = ({ children, className = undefined, ...props }: HeaderActionProps) => ( |
||||
<button className={createClassName(styles, 'header__action', {}, [className])} {...props}> |
||||
{children} |
||||
</button> |
||||
); |
||||
|
||||
export default HeaderAction; |
||||
@ -0,0 +1,17 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderActionsProps = { |
||||
children?: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
const HeaderActions = ({ children, className = undefined, ...props }: HeaderActionsProps) => ( |
||||
<nav className={createClassName(styles, 'header__actions', {}, [className])} {...props}> |
||||
{children} |
||||
</nav> |
||||
); |
||||
|
||||
export default HeaderActions; |
||||
@ -0,0 +1,17 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderContentProps = { |
||||
children?: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
const HeaderContent = ({ children, className = undefined, ...props }: HeaderContentProps) => ( |
||||
<div className={createClassName(styles, 'header__content', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default HeaderContent; |
||||
@ -0,0 +1,17 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderCustomFieldProps = { |
||||
children?: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
const HeaderCustomField = ({ children, className = undefined, ...props }: HeaderCustomFieldProps) => ( |
||||
<div className={createClassName(styles, 'header__custom-field', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default HeaderCustomField; |
||||
@ -0,0 +1,17 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderPictureProps = { |
||||
children?: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
export const HeaderPicture = ({ children, className = undefined, ...props }: HeaderPictureProps) => ( |
||||
<div className={createClassName(styles, 'header__picture', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default HeaderPicture; |
||||
@ -0,0 +1,17 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderPostProps = { |
||||
children?: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
export const HeaderPost = ({ children, className = undefined, ...props }: HeaderPostProps) => ( |
||||
<div className={createClassName(styles, 'header__post', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default HeaderPost; |
||||
@ -0,0 +1,28 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
import { toChildArray } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderSubTitleProps = { |
||||
children?: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
const HeaderSubTitle = ({ children, className = undefined, ...props }: HeaderSubTitleProps) => ( |
||||
<div |
||||
className={createClassName( |
||||
styles, |
||||
'header__subtitle', |
||||
{ |
||||
children: toChildArray(children).length > 0, |
||||
}, |
||||
[className], |
||||
)} |
||||
{...props} |
||||
> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default HeaderSubTitle; |
||||
@ -0,0 +1,17 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderTitleProps = { |
||||
children?: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
const HeaderTitle = ({ children, className = undefined, ...props }: HeaderTitleProps) => ( |
||||
<div className={createClassName(styles, 'header__title', {}, [className])} data-qa='header-title' {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default HeaderTitle; |
||||
@ -0,0 +1,9 @@ |
||||
export { default as Header } from './Header'; |
||||
export { default as HeaderPicture } from './HeaderPicture'; |
||||
export { default as HeaderContent } from './HeaderContent'; |
||||
export { default as HeaderTitle } from './HeaderTitle'; |
||||
export { default as HeaderSubTitle } from './HeaderSubTitle'; |
||||
export { default as HeaderActions } from './HeaderActions'; |
||||
export { default as HeaderAction } from './HeaderAction'; |
||||
export { default as HeaderPost } from './HeaderPost'; |
||||
export { default as HeaderCustomField } from './HeaderCustomField'; |
||||
@ -1,111 +0,0 @@ |
||||
import type { ComponentChildren, Ref } from 'preact'; |
||||
import { toChildArray } from 'preact'; |
||||
import type { JSXInternal } from 'preact/src/jsx'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import type { Theme } from '../../Theme'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type HeaderProps = { |
||||
children?: ComponentChildren; |
||||
theme?: Partial<Theme>; |
||||
className?: string; |
||||
post?: ComponentChildren; |
||||
large?: boolean; |
||||
style?: JSXInternal.CSSProperties; |
||||
ref?: Ref<HTMLElement>; |
||||
onClick?: JSXInternal.DOMAttributes<HTMLElement>['onClick']; |
||||
}; |
||||
|
||||
type HeaderComponentProps = { |
||||
children?: ComponentChildren; |
||||
className?: string; |
||||
}; |
||||
|
||||
export const Header = ({ |
||||
children, |
||||
theme: { color: backgroundColor, fontColor: color } = {}, |
||||
className, |
||||
post, |
||||
large, |
||||
style, |
||||
...props |
||||
}: HeaderProps) => ( |
||||
<header |
||||
className={createClassName(styles, 'header', { large }, [className])} |
||||
style={style || backgroundColor || color ? { ...(style || {}), backgroundColor, color } : undefined} |
||||
{...props} |
||||
> |
||||
{children} |
||||
{post} |
||||
</header> |
||||
); |
||||
|
||||
export const Picture = ({ children, className = undefined, ...props }: HeaderComponentProps) => ( |
||||
<div className={createClassName(styles, 'header__picture', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export const Content = ({ children, className = undefined, ...props }: HeaderComponentProps) => ( |
||||
<div className={createClassName(styles, 'header__content', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export const Title = ({ children, className = undefined, ...props }: HeaderComponentProps) => ( |
||||
<div className={createClassName(styles, 'header__title', {}, [className])} data-qa='header-title' {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export const SubTitle = ({ children, className = undefined, ...props }: HeaderComponentProps) => ( |
||||
<div |
||||
className={createClassName( |
||||
styles, |
||||
'header__subtitle', |
||||
{ |
||||
children: toChildArray(children).length > 0, |
||||
}, |
||||
[className], |
||||
)} |
||||
{...props} |
||||
> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export const Actions = ({ children, className = undefined, ...props }: HeaderComponentProps) => ( |
||||
<nav className={createClassName(styles, 'header__actions', {}, [className])} {...props}> |
||||
{children} |
||||
</nav> |
||||
); |
||||
|
||||
export const Action = ({ children, className = undefined, ...props }: HeaderComponentProps & { onClick?: () => void }) => ( |
||||
<button className={createClassName(styles, 'header__action', {}, [className])} {...props}> |
||||
{children} |
||||
</button> |
||||
); |
||||
|
||||
export const Post = ({ children, className = undefined, ...props }: HeaderComponentProps) => ( |
||||
<div className={createClassName(styles, 'header__post', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export const CustomField = ({ children, className = undefined, ...props }: HeaderComponentProps) => ( |
||||
<div className={createClassName(styles, 'header__custom-field', {}, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
Header.Picture = Picture; |
||||
Header.Content = Content; |
||||
Header.Title = Title; |
||||
Header.SubTitle = SubTitle; |
||||
Header.Actions = Actions; |
||||
Header.Action = Action; |
||||
Header.Post = Post; |
||||
Header.CustomField = CustomField; |
||||
|
||||
export default Header; |
||||
@ -0,0 +1,18 @@ |
||||
import type { HTMLAttributes } from 'preact/compat'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
export type MenuProps = { |
||||
hidden?: boolean; |
||||
placement?: string; |
||||
ref?: any; // FIXME: remove this
|
||||
} & Omit<HTMLAttributes<HTMLDivElement>, 'ref'>; |
||||
|
||||
const Menu = ({ children, hidden, placement = '', ...props }: MenuProps) => ( |
||||
<div className={createClassName(styles, 'menu', { hidden, placement })} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default Menu; |
||||
@ -0,0 +1,17 @@ |
||||
import type { HTMLAttributes } from 'preact/compat'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type GroupProps = { |
||||
title?: string; |
||||
} & HTMLAttributes<HTMLDivElement>; |
||||
|
||||
const MenuGroup = ({ children, title = '', ...props }: GroupProps) => ( |
||||
<div className={createClassName(styles, 'menu__group')} {...props}> |
||||
{title && <div className={createClassName(styles, 'menu__group-title')}>{title}</div>} |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default MenuGroup; |
||||
@ -0,0 +1,21 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
import type { HTMLAttributes } from 'preact/compat'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
type ItemProps = { |
||||
primary?: boolean; |
||||
danger?: boolean; |
||||
disabled?: boolean; |
||||
icon?: () => ComponentChildren; |
||||
} & HTMLAttributes<HTMLButtonElement>; |
||||
|
||||
const MenuItem = ({ children, primary = false, danger = false, disabled = false, icon = undefined, ...props }: ItemProps) => ( |
||||
<button className={createClassName(styles, 'menu__item', { primary, danger, disabled })} disabled={disabled} {...props}> |
||||
{icon && <div className={createClassName(styles, 'menu__item__icon')}>{icon()}</div>} |
||||
{children} |
||||
</button> |
||||
); |
||||
|
||||
export default MenuItem; |
||||
@ -0,0 +1,29 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import PopoverMenuWrapper from './PopoverMenuWrapper'; |
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
import { PopoverTrigger } from '../Popover'; |
||||
|
||||
type PopoverMenuProps = { |
||||
children?: ComponentChildren; |
||||
trigger: (contextValue: { pop: () => void }) => ComponentChildren; |
||||
overlayed?: boolean; |
||||
}; |
||||
|
||||
const MenuPopover = ({ children = null, trigger, overlayed }: PopoverMenuProps) => ( |
||||
<PopoverTrigger |
||||
overlayProps={{ |
||||
className: overlayed ? createClassName(styles, 'popover-menu__overlay') : null, |
||||
}} |
||||
> |
||||
{trigger} |
||||
{({ dismiss, triggerBounds, overlayBounds }) => ( |
||||
<PopoverMenuWrapper dismiss={dismiss} triggerBounds={triggerBounds} overlayBounds={overlayBounds}> |
||||
{children} |
||||
</PopoverMenuWrapper> |
||||
)} |
||||
</PopoverTrigger> |
||||
); |
||||
|
||||
export default MenuPopover; |
||||
@ -0,0 +1,4 @@ |
||||
export { default as Menu } from './Menu'; |
||||
export { default as MenuGroup } from './MenuGroup'; |
||||
export { default as MenuItem } from './MenuItem'; |
||||
export { default as MenuPopover } from './MenuPopover'; |
||||
@ -0,0 +1,30 @@ |
||||
import type { ComponentProps } from 'preact'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
|
||||
import { Button } from '../Button'; |
||||
import { ButtonGroup } from '../ButtonGroup'; |
||||
import ModalMessage from './MessageModal'; |
||||
import Modal from './Modal'; |
||||
|
||||
export type AlertModalProps = { |
||||
text: string; |
||||
buttonText?: string; |
||||
onConfirm: () => void; |
||||
} & Omit<ComponentProps<typeof Modal>, 'open' | 'onDismiss'>; |
||||
|
||||
const AlertModal = ({ text, buttonText, onConfirm, ...props }: AlertModalProps) => { |
||||
const { t } = useTranslation(); |
||||
|
||||
return ( |
||||
<Modal open animated dismissByOverlay={false} {...props}> |
||||
<ModalMessage>{text}</ModalMessage> |
||||
<ButtonGroup> |
||||
<Button secondary onClick={onConfirm}> |
||||
{buttonText || t('ok')} |
||||
</Button> |
||||
</ButtonGroup> |
||||
</Modal> |
||||
); |
||||
}; |
||||
|
||||
export default AlertModal; |
||||
@ -0,0 +1,35 @@ |
||||
import { type ComponentProps } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
|
||||
import { Button } from '../Button'; |
||||
import { ButtonGroup } from '../ButtonGroup'; |
||||
import ModalMessage from './MessageModal'; |
||||
import Modal from './Modal'; |
||||
|
||||
export type ConfirmationModalProps = { |
||||
text: string; |
||||
confirmButtonText?: string; |
||||
cancelButtonText?: string; |
||||
onConfirm: () => void; |
||||
onCancel: () => void; |
||||
} & Omit<ComponentProps<typeof Modal>, 'open' | 'onDismiss'>; |
||||
|
||||
const ConfirmationModal = ({ text, confirmButtonText, cancelButtonText, onConfirm, onCancel, ...props }: ConfirmationModalProps) => { |
||||
const { t } = useTranslation(); |
||||
|
||||
return ( |
||||
<Modal open animated dismissByOverlay={false} {...props}> |
||||
<ModalMessage>{text}</ModalMessage> |
||||
<ButtonGroup> |
||||
<Button outline secondary onClick={onCancel}> |
||||
{cancelButtonText || t('no')} |
||||
</Button> |
||||
<Button secondary danger onClick={onConfirm}> |
||||
{confirmButtonText || t('yes')} |
||||
</Button> |
||||
</ButtonGroup> |
||||
</Modal> |
||||
); |
||||
}; |
||||
|
||||
export default ConfirmationModal; |
||||
@ -0,0 +1,12 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
export type ModalMessageProps = { |
||||
children: ComponentChildren; |
||||
}; |
||||
|
||||
const ModalMessage = ({ children }: ModalMessageProps) => <div className={createClassName(styles, 'modal__message')}>{children}</div>; |
||||
|
||||
export default ModalMessage; |
||||
@ -0,0 +1,73 @@ |
||||
import { Component } from 'preact'; |
||||
import type { HTMLAttributes } from 'preact/compat'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
export type ModalProps = { |
||||
open: boolean; |
||||
animated?: boolean; |
||||
timeout?: number; |
||||
dismissByOverlay?: boolean; |
||||
onDismiss?: () => void; |
||||
} & Omit<HTMLAttributes<HTMLDivElement>, 'onDismiss'>; |
||||
|
||||
class Modal extends Component<ModalProps> { |
||||
static override defaultProps = { |
||||
dismissByOverlay: true, |
||||
}; |
||||
|
||||
mounted = false; |
||||
|
||||
handleKeyDown = ({ key }: KeyboardEvent) => { |
||||
if (key === 'Escape') { |
||||
this.triggerDismiss(); |
||||
} |
||||
}; |
||||
|
||||
handleTouchStart = () => { |
||||
const { dismissByOverlay } = this.props; |
||||
if (dismissByOverlay) this.triggerDismiss(); |
||||
}; |
||||
|
||||
handleMouseDown = () => { |
||||
const { dismissByOverlay } = this.props; |
||||
if (dismissByOverlay) this.triggerDismiss(); |
||||
}; |
||||
|
||||
triggerDismiss = () => { |
||||
const { onDismiss } = this.props; |
||||
if (this.mounted) onDismiss?.(); |
||||
}; |
||||
|
||||
override componentDidMount() { |
||||
this.mounted = true; |
||||
window.addEventListener('keydown', this.handleKeyDown, false); |
||||
const { timeout } = this.props; |
||||
if (timeout !== undefined && Number.isFinite(timeout) && timeout > 0) { |
||||
setTimeout(() => this.triggerDismiss(), timeout); |
||||
} |
||||
} |
||||
|
||||
override componentWillUnmount() { |
||||
this.mounted = false; |
||||
window.removeEventListener('keydown', this.handleKeyDown, false); |
||||
} |
||||
|
||||
render = ({ children, animated, open, ...props }: ModalProps) => |
||||
open ? ( |
||||
<div |
||||
data-qa-type='modal-overlay' |
||||
role='presentation' |
||||
className={createClassName(styles, 'modal__overlay')} |
||||
onTouchStart={this.handleTouchStart} |
||||
onMouseDown={this.handleMouseDown} |
||||
> |
||||
<div className={createClassName(styles, 'modal', { animated })} {...props}> |
||||
{children} |
||||
</div> |
||||
</div> |
||||
) : null; |
||||
} |
||||
|
||||
export default Modal; |
||||
@ -0,0 +1,33 @@ |
||||
import { type ComponentProps } from 'preact'; |
||||
|
||||
import AlertModal from './AlertModal'; |
||||
import ConfirmationModal from './ConfirmationModal'; |
||||
import store from '../../store'; |
||||
|
||||
export const ModalManager = { |
||||
confirm(props: Omit<ComponentProps<typeof ConfirmationModal>, 'onConfirm' | 'onCancel'>) { |
||||
return new Promise<{ success: boolean }>((resolve) => { |
||||
const handleButton = (success: boolean) => () => { |
||||
store.setState({ modal: null }); |
||||
resolve({ success }); |
||||
}; |
||||
|
||||
store.setState({ |
||||
modal: <ConfirmationModal {...props} onConfirm={handleButton(true)} onCancel={handleButton(false)} />, |
||||
}); |
||||
}); |
||||
}, |
||||
|
||||
alert(props: Omit<ComponentProps<typeof AlertModal>, 'onConfirm'>) { |
||||
return new Promise<{ success: boolean }>((resolve) => { |
||||
const handleButton = () => () => { |
||||
store.setState({ modal: null }); |
||||
resolve({ success: true }); |
||||
}; |
||||
|
||||
store.setState({ |
||||
modal: <AlertModal {...props} onConfirm={handleButton()} />, |
||||
}); |
||||
}); |
||||
}, |
||||
} as const; |
||||
@ -1,95 +0,0 @@ |
||||
import { Component } from 'preact'; |
||||
import { withTranslation } from 'react-i18next'; |
||||
|
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
import { Button } from '../Button'; |
||||
import { ButtonGroup } from '../ButtonGroup'; |
||||
import styles from './styles.scss'; |
||||
|
||||
export class Modal extends Component { |
||||
static defaultProps = { |
||||
dismissByOverlay: true, |
||||
}; |
||||
|
||||
handleKeyDown = ({ key }) => { |
||||
if (key === 'Escape') { |
||||
this.triggerDismiss(); |
||||
} |
||||
}; |
||||
|
||||
handleTouchStart = () => { |
||||
const { dismissByOverlay } = this.props; |
||||
dismissByOverlay && this.triggerDismiss(); |
||||
}; |
||||
|
||||
handleMouseDown = () => { |
||||
const { dismissByOverlay } = this.props; |
||||
dismissByOverlay && this.triggerDismiss(); |
||||
}; |
||||
|
||||
triggerDismiss = () => { |
||||
const { onDismiss } = this.props; |
||||
this.mounted && onDismiss && onDismiss(); |
||||
}; |
||||
|
||||
componentDidMount() { |
||||
this.mounted = true; |
||||
window.addEventListener('keydown', this.handleKeyDown, false); |
||||
const { timeout } = this.props; |
||||
if (Number.isFinite(timeout) && timeout > 0) { |
||||
setTimeout(() => this.triggerDismiss(), timeout); |
||||
} |
||||
} |
||||
|
||||
componentWillUnmount() { |
||||
this.mounted = false; |
||||
window.removeEventListener('keydown', this.handleKeyDown, false); |
||||
} |
||||
|
||||
render = ({ children, animated, open, ...props }) => |
||||
open ? ( |
||||
<div |
||||
data-qa-type='modal-overlay' |
||||
onTouchStart={this.handleTouchStart} |
||||
onMouseDown={this.handleMouseDown} |
||||
className={createClassName(styles, 'modal__overlay')} |
||||
> |
||||
<div className={createClassName(styles, 'modal', { animated })} {...props}> |
||||
{children} |
||||
</div> |
||||
</div> |
||||
) : null; |
||||
} |
||||
|
||||
export const ModalMessage = ({ children }) => <div className={createClassName(styles, 'modal__message')}>{children}</div>; |
||||
|
||||
export const ConfirmationModal = withTranslation()(({ text, confirmButtonText, cancelButtonText, onConfirm, onCancel, t, ...props }) => ( |
||||
<Modal open animated dismissByOverlay={false} {...props}> |
||||
<Modal.Message>{text}</Modal.Message> |
||||
<ButtonGroup> |
||||
<Button outline secondary onClick={onCancel}> |
||||
{cancelButtonText || t('no')} |
||||
</Button> |
||||
<Button secondaryDanger onClick={onConfirm}> |
||||
{confirmButtonText || t('yes')} |
||||
</Button> |
||||
</ButtonGroup> |
||||
</Modal> |
||||
)); |
||||
|
||||
export const AlertModal = withTranslation()(({ text, buttonText, onConfirm, t, ...props }) => ( |
||||
<Modal open animated dismissByOverlay={false} {...props}> |
||||
<Modal.Message>{text}</Modal.Message> |
||||
<ButtonGroup> |
||||
<Button secondary onClick={onConfirm}> |
||||
{buttonText || t('ok')} |
||||
</Button> |
||||
</ButtonGroup> |
||||
</Modal> |
||||
)); |
||||
|
||||
Modal.Message = ModalMessage; |
||||
Modal.Confirm = ConfirmationModal; |
||||
Modal.Alert = AlertModal; |
||||
|
||||
export default Modal; |
||||
@ -1,2 +0,0 @@ |
||||
export { default, Modal, ModalMessage, ConfirmationModal, AlertModal } from './component'; |
||||
export { default as ModalManager } from './manager'; |
||||
@ -0,0 +1,5 @@ |
||||
export { default as Modal } from './Modal'; |
||||
export { ModalManager } from './ModalManager'; |
||||
export { default as ModalMessage } from './MessageModal'; |
||||
export { default as AlertModal } from './AlertModal'; |
||||
export { default as ConfirmationModal } from './ConfirmationModal'; |
||||
@ -1,30 +0,0 @@ |
||||
import Modal from './component'; |
||||
import store from '../../store'; |
||||
|
||||
export default { |
||||
confirm(props = {}) { |
||||
return new Promise((resolve) => { |
||||
const handleButton = (success) => () => { |
||||
store.setState({ modal: null }); |
||||
resolve({ success }); |
||||
}; |
||||
|
||||
store.setState({ |
||||
modal: <Modal.Confirm {...props} onConfirm={handleButton(true)} onCancel={handleButton(false)} />, |
||||
}); |
||||
}); |
||||
}, |
||||
|
||||
alert(props = {}) { |
||||
return new Promise((resolve) => { |
||||
const handleButton = () => () => { |
||||
store.setState({ modal: null }); |
||||
resolve({ success: true }); |
||||
}; |
||||
|
||||
store.setState({ |
||||
modal: <Modal.Alert {...props} onConfirm={handleButton()} />, |
||||
}); |
||||
}); |
||||
}, |
||||
}; |
||||
@ -1,8 +1,9 @@ |
||||
import type { Meta, StoryFn } from '@storybook/preact'; |
||||
import type { ComponentProps } from 'preact'; |
||||
|
||||
import { PopoverContainer, PopoverTrigger } from '.'; |
||||
import { PopoverContainer } from '.'; |
||||
import { Button } from '../Button'; |
||||
import PopoverTrigger from './PopoverTrigger'; |
||||
|
||||
export default { |
||||
title: 'Components/Popover', |
||||
@ -0,0 +1,130 @@ |
||||
import { Component, type ComponentProps } from 'preact'; |
||||
|
||||
import { PopoverContext } from './PopoverContext'; |
||||
import PopoverOverlay from './PopoverOverlay'; |
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
import { normalizeDOMRect } from '../../helpers/normalizeDOMRect'; |
||||
|
||||
export type PopoverContainerProps = { |
||||
children?: any; |
||||
}; |
||||
|
||||
type PopoverContainerState = { |
||||
renderer: |
||||
| null |
||||
| ((options: { |
||||
dismiss: () => void; |
||||
overlayBounds?: { |
||||
left: number; |
||||
top: number; |
||||
right: number; |
||||
bottom: number; |
||||
} | null; |
||||
triggerBounds?: { |
||||
left: number; |
||||
top: number; |
||||
right: number; |
||||
bottom: number; |
||||
} | null; |
||||
}) => any); |
||||
overlayProps?: ComponentProps<typeof PopoverOverlay>; |
||||
overlayBounds?: { |
||||
left: number; |
||||
top: number; |
||||
right: number; |
||||
bottom: number; |
||||
} | null; |
||||
triggerBounds?: { |
||||
left: number; |
||||
top: number; |
||||
right: number; |
||||
bottom: number; |
||||
} | null; |
||||
}; |
||||
|
||||
class PopoverContainer extends Component<PopoverContainerProps, PopoverContainerState> { |
||||
override state: PopoverContainerState = { |
||||
renderer: null, |
||||
}; |
||||
|
||||
mounted = false; |
||||
|
||||
overlayRef: any = null; |
||||
|
||||
open = ( |
||||
renderer: (options: { |
||||
dismiss: () => void; |
||||
overlayBounds?: { left: number; top: number; right: number; bottom: number } | null; |
||||
triggerBounds?: { left: number; top: number; right: number; bottom: number } | null; |
||||
}) => any, |
||||
props: ComponentProps<typeof PopoverOverlay>, |
||||
{ currentTarget }: { currentTarget?: HTMLElement | null } = {}, |
||||
) => { |
||||
let overlayBounds; |
||||
let triggerBounds; |
||||
|
||||
if (this.overlayRef) { |
||||
overlayBounds = normalizeDOMRect(this.overlayRef.base.getBoundingClientRect()); |
||||
} |
||||
|
||||
if (currentTarget) { |
||||
triggerBounds = normalizeDOMRect(currentTarget.getBoundingClientRect()); |
||||
} |
||||
|
||||
this.setState({ renderer, ...props, overlayBounds, triggerBounds }); |
||||
}; |
||||
|
||||
dismiss = () => { |
||||
this.setState({ renderer: null, overlayBounds: null, triggerBounds: null }); |
||||
}; |
||||
|
||||
handleOverlayGesture = ({ currentTarget, target }: Event) => { |
||||
if (currentTarget !== target) { |
||||
return; |
||||
} |
||||
|
||||
this.dismiss(); |
||||
}; |
||||
|
||||
handleKeyDown = ({ key }: KeyboardEvent) => { |
||||
if (key !== 'Escape') { |
||||
return; |
||||
} |
||||
|
||||
this.dismiss(); |
||||
}; |
||||
|
||||
handleOverlayRef = (ref: any) => { |
||||
this.overlayRef = ref; |
||||
}; |
||||
|
||||
override componentDidMount() { |
||||
this.mounted = true; |
||||
window.addEventListener('keydown', this.handleKeyDown, false); |
||||
} |
||||
|
||||
override componentWillUnmount() { |
||||
this.mounted = false; |
||||
window.removeEventListener('keydown', this.handleKeyDown, false); |
||||
} |
||||
|
||||
render = ({ children }: PopoverContainerProps, { renderer, overlayProps, overlayBounds, triggerBounds }: PopoverContainerState) => ( |
||||
<PopoverContext.Provider value={{ open: this.open }}> |
||||
<div className={createClassName(styles, 'popover__container')}> |
||||
{children} |
||||
<PopoverOverlay |
||||
ref={this.handleOverlayRef} |
||||
onMouseDown={this.handleOverlayGesture} |
||||
onTouchStart={this.handleOverlayGesture} |
||||
visible={!!renderer} |
||||
{...overlayProps} |
||||
> |
||||
{renderer ? renderer({ dismiss: this.dismiss, overlayBounds, triggerBounds }) : null} |
||||
</PopoverOverlay> |
||||
</div> |
||||
</PopoverContext.Provider> |
||||
); |
||||
} |
||||
|
||||
export default PopoverContainer; |
||||
@ -0,0 +1,7 @@ |
||||
import { createContext } from 'preact'; |
||||
|
||||
export const PopoverContext = createContext<{ open: (renderer: any, props: any, options?: { currentTarget?: HTMLElement }) => void }>({ |
||||
open: () => { |
||||
// noop
|
||||
}, |
||||
}); |
||||
@ -0,0 +1,20 @@ |
||||
import type { ComponentChildren } from 'preact'; |
||||
import type { HTMLAttributes } from 'preact/compat'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
export type PopoverOverlayProps = { |
||||
children?: ComponentChildren; |
||||
visible?: boolean; |
||||
className?: string; |
||||
ref?: any; // FIXME: remove this
|
||||
} & HTMLAttributes<HTMLDivElement>; |
||||
|
||||
const PopoverOverlay = ({ children, className, visible, ...props }: PopoverOverlayProps) => ( |
||||
<div className={createClassName(styles, 'popover__overlay', { visible }, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default PopoverOverlay; |
||||
@ -0,0 +1,17 @@ |
||||
import { type ComponentChildren } from 'preact'; |
||||
|
||||
import { PopoverContext } from './PopoverContext'; |
||||
|
||||
export type PopoverTriggerProps = { |
||||
children: [ |
||||
trigger: (contextValue: { pop: () => void }) => ComponentChildren, |
||||
renderer: (popoverContext: { dismiss: () => void; triggerBounds: DOMRect; overlayBounds: DOMRect }) => ComponentChildren, |
||||
]; |
||||
overlayProps?: any; |
||||
}; |
||||
|
||||
const PopoverTrigger = ({ children, ...props }: PopoverTriggerProps) => ( |
||||
<PopoverContext.Consumer>{({ open }) => children[0]({ pop: open.bind(null, children[1], props) })}</PopoverContext.Consumer> |
||||
); |
||||
|
||||
export default PopoverTrigger; |
||||
@ -1,90 +0,0 @@ |
||||
import { Component, createContext } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
import { normalizeDOMRect } from '../../helpers/normalizeDOMRect'; |
||||
|
||||
const PopoverContext = createContext(); |
||||
|
||||
const PopoverOverlay = ({ children, className, visible, ...props }) => ( |
||||
<div className={createClassName(styles, 'popover__overlay', { visible }, [className])} {...props}> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export class PopoverContainer extends Component { |
||||
state = { |
||||
renderer: null, |
||||
}; |
||||
|
||||
open = (renderer, props, { currentTarget } = {}) => { |
||||
let overlayBounds; |
||||
let triggerBounds; |
||||
|
||||
if (this.overlayRef) { |
||||
overlayBounds = normalizeDOMRect(this.overlayRef.base.getBoundingClientRect()); |
||||
} |
||||
|
||||
if (currentTarget) { |
||||
triggerBounds = normalizeDOMRect(currentTarget.getBoundingClientRect()); |
||||
} |
||||
|
||||
this.setState({ renderer, ...props, overlayBounds, triggerBounds }); |
||||
}; |
||||
|
||||
dismiss = () => { |
||||
this.setState({ renderer: null, overlayBounds: null, triggerBounds: null }); |
||||
}; |
||||
|
||||
handleOverlayGesture = ({ currentTarget, target }) => { |
||||
if (currentTarget !== target) { |
||||
return; |
||||
} |
||||
|
||||
this.dismiss(); |
||||
}; |
||||
|
||||
handleKeyDown = ({ key }) => { |
||||
if (key !== 'Escape') { |
||||
return; |
||||
} |
||||
|
||||
this.dismiss(); |
||||
}; |
||||
|
||||
handleOverlayRef = (ref) => { |
||||
this.overlayRef = ref; |
||||
}; |
||||
|
||||
componentDidMount() { |
||||
this.mounted = true; |
||||
window.addEventListener('keydown', this.handleKeyDown, false); |
||||
} |
||||
|
||||
componentWillUnmount() { |
||||
this.mounted = false; |
||||
window.removeEventListener('keydown', this.handleKeyDown, false); |
||||
} |
||||
|
||||
render = ({ children }, { renderer, overlayProps, overlayBounds, triggerBounds }) => ( |
||||
<PopoverContext.Provider value={{ open: this.open }}> |
||||
<div className={createClassName(styles, 'popover__container')}> |
||||
{children} |
||||
<PopoverOverlay |
||||
ref={this.handleOverlayRef} |
||||
onMouseDown={this.handleOverlayGesture} |
||||
onTouchStart={this.handleOverlayGesture} |
||||
visible={!!renderer} |
||||
{...overlayProps} |
||||
> |
||||
{renderer ? renderer({ dismiss: this.dismiss, overlayBounds, triggerBounds }) : null} |
||||
</PopoverOverlay> |
||||
</div> |
||||
</PopoverContext.Provider> |
||||
); |
||||
} |
||||
|
||||
/** @type {function({ children: [function({ pop: function() }), function({ dismiss: any, triggerBounds?: any, overlayBounds?: any })], overlayProps?: any }): any} */ |
||||
export const PopoverTrigger = ({ children, ...props }) => ( |
||||
<PopoverContext.Consumer>{({ open }) => children[0]({ pop: open.bind(null, children[1], props) })}</PopoverContext.Consumer> |
||||
); |
||||
@ -0,0 +1,2 @@ |
||||
export { default as PopoverContainer } from './PopoverContainer'; |
||||
export { default as PopoverTrigger } from './PopoverTrigger'; |
||||
@ -0,0 +1,48 @@ |
||||
import { useEffect } from 'preact/hooks'; |
||||
|
||||
import type { ScreenTheme } from './ScreenProvider'; |
||||
import styles from './styles.scss'; |
||||
|
||||
export type CssVarProps = { |
||||
theme: ScreenTheme; |
||||
}; |
||||
|
||||
const CssVar = ({ theme }: CssVarProps) => { |
||||
useEffect(() => { |
||||
if (window.CSS && CSS.supports('color', 'var(--color)')) { |
||||
return; |
||||
} |
||||
let mounted = true; |
||||
void (async () => { |
||||
const { default: cssVars } = await import('css-vars-ponyfill'); |
||||
if (!mounted) { |
||||
return; |
||||
} |
||||
cssVars({ |
||||
variables: { |
||||
'--color': theme.color, |
||||
'--font-color': theme.fontColor, |
||||
'--icon-color': theme.iconColor, |
||||
}, |
||||
}); |
||||
})(); |
||||
return () => { |
||||
mounted = false; |
||||
}; |
||||
}, [theme]); |
||||
|
||||
return ( |
||||
<style>{` |
||||
.${styles.screen} { |
||||
${theme.color ? `--color: ${theme.color};` : ''} |
||||
${theme.fontColor ? `--font-color: ${theme.fontColor};` : ''} |
||||
${theme.iconColor ? `--icon-color: ${theme.iconColor};` : ''} |
||||
${theme.guestBubbleBackgroundColor ? `--sender-bubble-background-color: ${theme.guestBubbleBackgroundColor};` : ''} |
||||
${theme.agentBubbleBackgroundColor ? `--receiver-bubble-background-color: ${theme.agentBubbleBackgroundColor};` : ''} |
||||
${theme.background ? `--message-list-background: ${theme.background};` : ''} |
||||
} |
||||
`}</style>
|
||||
); |
||||
}; |
||||
|
||||
export default CssVar; |
||||
@ -0,0 +1,17 @@ |
||||
import { type ComponentChildren } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
export type ScreenContentProps = { |
||||
children?: ComponentChildren; |
||||
nopadding?: boolean; |
||||
triggered?: boolean; |
||||
full?: boolean; |
||||
}; |
||||
|
||||
const ScreenContent = ({ children, nopadding, triggered = false, full = false }: ScreenContentProps) => ( |
||||
<main className={createClassName(styles, 'screen__main', { nopadding, triggered, full })}>{children}</main> |
||||
); |
||||
|
||||
export default ScreenContent; |
||||
@ -0,0 +1,28 @@ |
||||
import { type ComponentChildren } from 'preact'; |
||||
import { useContext } from 'preact/hooks'; |
||||
|
||||
import { Footer, FooterContent, PoweredBy } from '../Footer'; |
||||
import { ScreenContext } from './ScreenProvider'; |
||||
|
||||
export type ScreenFooterProps = { |
||||
children?: ComponentChildren; |
||||
options?: ComponentChildren; |
||||
limit?: ComponentChildren; |
||||
}; |
||||
|
||||
const ScreenFooter = ({ children, options, limit }: ScreenFooterProps) => { |
||||
const { hideWatermark } = useContext(ScreenContext); |
||||
|
||||
return ( |
||||
<Footer> |
||||
{children && <FooterContent>{children}</FooterContent>} |
||||
<FooterContent> |
||||
{options} |
||||
{limit} |
||||
{!hideWatermark && <PoweredBy />} |
||||
</FooterContent> |
||||
</Footer> |
||||
); |
||||
}; |
||||
|
||||
export default ScreenFooter; |
||||
@ -0,0 +1,3 @@ |
||||
export { default as Screen } from './Screen'; |
||||
export { default as ScreenContent } from './ScreenContent'; |
||||
export { default as ScreenFooter } from './ScreenFooter'; |
||||
@ -0,0 +1,61 @@ |
||||
import type { HTMLAttributes } from 'preact/compat'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
export type Placement = 'left' | 'top' | 'right' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | null; |
||||
|
||||
const getPositioningStyle = ( |
||||
placement: Placement, |
||||
{ left, top, right, bottom }: { left: number; top: number; right: number; bottom: number }, |
||||
) => { |
||||
switch (placement) { |
||||
case 'left': |
||||
return { |
||||
left: `${left}px`, |
||||
top: `${(top + bottom) / 2}px`, |
||||
}; |
||||
|
||||
case 'top': |
||||
case 'top-left': |
||||
case 'top-right': |
||||
return { |
||||
left: `${(left + right) / 2}px`, |
||||
top: `${top}px`, |
||||
}; |
||||
|
||||
case 'right': |
||||
return { |
||||
left: `${right}px`, |
||||
top: `${(top + bottom) / 2}px`, |
||||
}; |
||||
|
||||
case 'bottom': |
||||
case 'bottom-left': |
||||
case 'bottom-right': |
||||
default: |
||||
return { |
||||
left: `${(left + right) / 2}px`, |
||||
top: `${bottom}px`, |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
export type TooltipProps = { |
||||
hidden?: boolean; |
||||
placement: Placement; |
||||
floating?: boolean; |
||||
triggerBounds: { left: number; top: number; right: number; bottom: number }; |
||||
} & Omit<HTMLAttributes<HTMLDivElement>, 'ref'>; |
||||
|
||||
const Tooltip = ({ children, hidden = false, placement, floating = false, triggerBounds, ...props }: TooltipProps) => ( |
||||
<div |
||||
className={createClassName(styles, 'tooltip', { hidden, placement, floating })} |
||||
style={floating ? getPositioningStyle(placement, triggerBounds) : {}} |
||||
{...props} |
||||
> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
export default Tooltip; |
||||
@ -0,0 +1,71 @@ |
||||
import { Component, type ComponentChildren } from 'preact'; |
||||
|
||||
import Tooltip, { type Placement } from './Tooltip'; |
||||
import { TooltipContext } from './TooltipContext'; |
||||
|
||||
export type TooltipContainerProps = { |
||||
children: any; |
||||
}; |
||||
|
||||
type TooltipContainerState = { |
||||
tooltip: any; |
||||
activeChild: number | null; |
||||
event: any; |
||||
placement: Placement; |
||||
content?: ComponentChildren; |
||||
}; |
||||
|
||||
class TooltipContainer extends Component<TooltipContainerProps, TooltipContainerState> { |
||||
override state: TooltipContainerState = { |
||||
tooltip: null, |
||||
activeChild: null, |
||||
event: null, |
||||
placement: null, |
||||
}; |
||||
|
||||
showTooltip = ( |
||||
event: any, |
||||
{ content, placement = 'bottom', childIndex }: { content: any; placement?: Placement; childIndex: number | null }, |
||||
) => { |
||||
const triggerBounds = event.target.getBoundingClientRect(); |
||||
this.setState({ |
||||
tooltip: ( |
||||
<Tooltip floating placement={placement} triggerBounds={triggerBounds}> |
||||
{content} |
||||
</Tooltip> |
||||
), |
||||
activeChild: childIndex, |
||||
event, |
||||
placement, |
||||
content, |
||||
}); |
||||
}; |
||||
|
||||
hideTooltip = () => { |
||||
this.setState({ tooltip: null }); |
||||
}; |
||||
|
||||
UNSAFE_componentWillReceiveProps(props: TooltipContainerProps) { |
||||
if (this.state.tooltip) { |
||||
const activeChildren = props?.children?.props?.children[this.state.activeChild ?? 0]; |
||||
if (activeChildren && activeChildren.props.content !== this.state.content) { |
||||
this.showTooltip(this.state.event, { |
||||
content: activeChildren.props.content, |
||||
placement: this.state.placement, |
||||
childIndex: this.state.activeChild, |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
render({ children }: TooltipContainerProps) { |
||||
return ( |
||||
<TooltipContext.Provider value={{ ...this.state, showTooltip: this.showTooltip, hideTooltip: this.hideTooltip }}> |
||||
{children} |
||||
<TooltipContext.Consumer>{({ tooltip }) => tooltip}</TooltipContext.Consumer> |
||||
</TooltipContext.Provider> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default TooltipContainer; |
||||
@ -0,0 +1,35 @@ |
||||
import { type ComponentChildren, createContext } from 'preact'; |
||||
|
||||
import type { Placement } from './Tooltip'; |
||||
|
||||
export const TooltipContext = createContext<{ |
||||
tooltip: any; |
||||
activeChild: number | null; |
||||
event: any; |
||||
placement: Placement; |
||||
content?: ComponentChildren; |
||||
showTooltip: ( |
||||
event: any, |
||||
{ |
||||
content, |
||||
placement, |
||||
childIndex, |
||||
}: { |
||||
content: any; |
||||
placement?: Placement; |
||||
childIndex: number | null; |
||||
}, |
||||
) => void; |
||||
hideTooltip: () => void; |
||||
}>({ |
||||
activeChild: null, |
||||
event: null, |
||||
placement: null, |
||||
showTooltip: () => { |
||||
// noop
|
||||
}, |
||||
hideTooltip: () => { |
||||
// noop
|
||||
}, |
||||
tooltip: null, |
||||
}); |
||||
@ -0,0 +1,30 @@ |
||||
import type { ComponentChildren, VNode } from 'preact'; |
||||
import { toChildArray, cloneElement } from 'preact'; |
||||
import type { FocusEvent, MouseEvent } from 'preact/compat'; |
||||
|
||||
import type { Placement } from './Tooltip'; |
||||
import { TooltipContext } from './TooltipContext'; |
||||
|
||||
export type TooltipTriggerProps = { |
||||
content: ComponentChildren; |
||||
placement?: Placement; |
||||
children: ComponentChildren; |
||||
}; |
||||
|
||||
const TooltipTrigger = ({ children, content, placement }: TooltipTriggerProps) => ( |
||||
<TooltipContext.Consumer> |
||||
{({ showTooltip, hideTooltip }) => |
||||
toChildArray(children).map((child, index) => |
||||
cloneElement(child as VNode, { |
||||
onMouseEnter: (event: MouseEvent<any>) => showTooltip(event, { content, placement, childIndex: index }), |
||||
onMouseLeave: () => hideTooltip(), |
||||
onFocusCapture: (event: FocusEvent<any>) => showTooltip(event, { content, placement, childIndex: index }), |
||||
onBlurCapture: () => hideTooltip(), |
||||
content, |
||||
}), |
||||
) |
||||
} |
||||
</TooltipContext.Consumer> |
||||
); |
||||
|
||||
export default TooltipTrigger; |
||||
@ -1,127 +0,0 @@ |
||||
import { cloneElement, Component, createContext, toChildArray } from 'preact'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../helpers/createClassName'; |
||||
|
||||
const getPositioningStyle = (placement, { left, top, right, bottom }) => { |
||||
switch (placement) { |
||||
case 'left': |
||||
return { |
||||
left: `${left}px`, |
||||
top: `${(top + bottom) / 2}px`, |
||||
}; |
||||
|
||||
case 'top': |
||||
case 'top-left': |
||||
case 'top-right': |
||||
return { |
||||
left: `${(left + right) / 2}px`, |
||||
top: `${top}px`, |
||||
}; |
||||
|
||||
case 'right': |
||||
return { |
||||
left: `${right}px`, |
||||
top: `${(top + bottom) / 2}px`, |
||||
}; |
||||
|
||||
case 'bottom': |
||||
case 'bottom-left': |
||||
case 'bottom-right': |
||||
default: |
||||
return { |
||||
left: `${(left + right) / 2}px`, |
||||
top: `${bottom}px`, |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
export const Tooltip = ({ children, hidden = false, placement, floating = false, triggerBounds, ...props }) => ( |
||||
<div |
||||
className={createClassName(styles, 'tooltip', { hidden, placement, floating })} |
||||
style={floating ? getPositioningStyle(placement, triggerBounds) : {}} |
||||
{...props} |
||||
> |
||||
{children} |
||||
</div> |
||||
); |
||||
|
||||
const TooltipContext = createContext(); |
||||
|
||||
export class TooltipContainer extends Component { |
||||
state = { |
||||
tooltip: null, |
||||
activeChild: null, |
||||
event: null, |
||||
placement: null, |
||||
}; |
||||
|
||||
showTooltip = (event, { content, placement = 'bottom', childIndex }) => { |
||||
const triggerBounds = event.target.getBoundingClientRect(); |
||||
this.setState({ |
||||
tooltip: ( |
||||
<Tooltip floating placement={placement} triggerBounds={triggerBounds}> |
||||
{content} |
||||
</Tooltip> |
||||
), |
||||
activeChild: childIndex, |
||||
event, |
||||
placement, |
||||
content, |
||||
}); |
||||
}; |
||||
|
||||
hideTooltip = () => { |
||||
this.setState({ tooltip: null }); |
||||
}; |
||||
|
||||
UNSAFE_componentWillReceiveProps(props) { |
||||
if (this.state.tooltip) { |
||||
const activeChildren = props?.children?.props?.children[this.state.activeChild]; |
||||
if (activeChildren && activeChildren.props.content !== this.state.content) { |
||||
this.showTooltip(this.state.event, { |
||||
content: activeChildren.props.content, |
||||
placement: this.state.placement, |
||||
childIndex: this.state.activeChild, |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
render({ children }) { |
||||
return ( |
||||
<TooltipContext.Provider value={{ ...this.state, showTooltip: this.showTooltip, hideTooltip: this.hideTooltip }}> |
||||
{children} |
||||
<TooltipContext.Consumer>{({ tooltip }) => tooltip}</TooltipContext.Consumer> |
||||
</TooltipContext.Provider> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export const TooltipTrigger = ({ children, content, placement = '' }) => ( |
||||
<TooltipContext.Consumer> |
||||
{({ showTooltip, hideTooltip }) => |
||||
toChildArray(children).map((child, index) => |
||||
cloneElement(child, { |
||||
onMouseEnter: (event) => showTooltip(event, { content, placement, childIndex: index }), |
||||
onMouseLeave: (event) => hideTooltip(event), |
||||
onFocusCapture: (event) => showTooltip(event, { content, placement, childIndex: index }), |
||||
onBlurCapture: (event) => hideTooltip(event), |
||||
content, |
||||
}), |
||||
) |
||||
} |
||||
</TooltipContext.Consumer> |
||||
); |
||||
|
||||
export const withTooltip = (component) => { |
||||
const TooltipConnection = ({ tooltip, ...props }) => <Tooltip.Trigger content={tooltip}>{component(props)}</Tooltip.Trigger>; |
||||
TooltipConnection.displayName = `withTooltip(${component.displayName})`; |
||||
|
||||
return TooltipConnection; |
||||
}; |
||||
|
||||
Tooltip.Container = TooltipContainer; |
||||
Tooltip.Trigger = TooltipTrigger; |
||||
|
||||
export default Tooltip; |
||||
@ -0,0 +1,3 @@ |
||||
export { default, default as Tooltip } from './Tooltip'; |
||||
export { default as TooltipContainer } from './TooltipContainer'; |
||||
export { default as TooltipTrigger } from './TooltipTrigger'; |
||||
@ -0,0 +1,35 @@ |
||||
import type * as uikit from '@rocket.chat/ui-kit'; |
||||
import type { ComponentChild } from 'preact'; |
||||
import type { TargetedEvent } from 'preact/compat'; |
||||
import { memo, useCallback } from 'preact/compat'; |
||||
|
||||
import { MenuGroup, MenuPopover } from '../../../Menu'; |
||||
import { usePerformAction } from '../Block'; |
||||
import OverflowOption from './OverflowOption'; |
||||
import OverflowTrigger from './OverflowTrigger'; |
||||
|
||||
type OverflowElementProps = uikit.OverflowElement & { |
||||
parser: uikit.SurfaceRenderer<ComponentChild>; |
||||
}; |
||||
|
||||
const OverflowElement = ({ actionId, confirm, options, parser }: OverflowElementProps) => { |
||||
const [performAction, performingAction] = usePerformAction(actionId); |
||||
|
||||
const handleClick = useCallback( |
||||
async (value: TargetedEvent<HTMLElement, MouseEvent>) => { |
||||
await performAction({ value }); |
||||
}, |
||||
[performAction], |
||||
); |
||||
|
||||
return ( |
||||
<MenuPopover trigger={({ pop }) => <OverflowTrigger loading={performingAction} onClick={pop} />}> |
||||
<MenuGroup> |
||||
{Array.isArray(options) && |
||||
options.map((option, i) => <OverflowOption key={i} {...option} confirm={confirm} parser={parser} onClick={handleClick} />)} |
||||
</MenuGroup> |
||||
</MenuPopover> |
||||
); |
||||
}; |
||||
|
||||
export default memo(OverflowElement); |
||||
@ -0,0 +1,41 @@ |
||||
import type * as uikit from '@rocket.chat/ui-kit'; |
||||
import type { ComponentChild } from 'preact'; |
||||
import type { TargetedEvent } from 'preact/compat'; |
||||
import { useCallback } from 'preact/compat'; |
||||
|
||||
import { MenuItem } from '../../../Menu'; |
||||
|
||||
type OverflowOptionProps = uikit.Option & { |
||||
confirm: boolean; |
||||
parser: uikit.SurfaceRenderer<ComponentChild>; |
||||
onClick: (value: string) => void; |
||||
}; |
||||
|
||||
const OverflowOption = ({ confirm, text, value, url, parser, onClick }: OverflowOptionProps) => { |
||||
const handleClick = useCallback( |
||||
async (event: TargetedEvent<HTMLElement, MouseEvent>) => { |
||||
event.preventDefault(); |
||||
|
||||
if (confirm) { |
||||
// TODO
|
||||
} |
||||
|
||||
if (url) { |
||||
const newTab = window.open(); |
||||
if (!newTab) { |
||||
throw new Error('Could not open new tab'); |
||||
} |
||||
newTab.opener = null; |
||||
newTab.location = url; |
||||
return; |
||||
} |
||||
|
||||
await onClick(value); |
||||
}, |
||||
[confirm, onClick, url, value], |
||||
); |
||||
|
||||
return <MenuItem onClick={handleClick}>{parser.renderTextObject(text, 0)}</MenuItem>; |
||||
}; |
||||
|
||||
export default OverflowOption; |
||||
@ -0,0 +1,33 @@ |
||||
import type { TargetedEvent } from 'preact/compat'; |
||||
import { useCallback } from 'preact/compat'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../../../helpers/createClassName'; |
||||
import KebabIcon from '../../../../icons/kebab.svg'; |
||||
import { Button } from '../../../Button'; |
||||
|
||||
type OverflowTriggerProps = { |
||||
loading: boolean; |
||||
onClick: () => void; |
||||
}; |
||||
|
||||
const OverflowTrigger = ({ loading, onClick }: OverflowTriggerProps) => { |
||||
const handleMouseUp = useCallback(({ currentTarget }: TargetedEvent<HTMLElement>) => { |
||||
currentTarget.blur(); |
||||
}, []); |
||||
|
||||
return ( |
||||
<Button |
||||
className={createClassName(styles, 'uikit-overflow__trigger')} |
||||
disabled={loading} |
||||
outline |
||||
secondary |
||||
onClick={onClick} |
||||
onMouseUp={handleMouseUp} |
||||
> |
||||
<KebabIcon width={20} height={20} /> |
||||
</Button> |
||||
); |
||||
}; |
||||
|
||||
export default OverflowTrigger; |
||||
@ -0,0 +1 @@ |
||||
export { default } from './OverflowElement'; |
||||
@ -1,94 +0,0 @@ |
||||
import type * as uikit from '@rocket.chat/ui-kit'; |
||||
import type { ComponentChild } from 'preact'; |
||||
import type { TargetedEvent } from 'preact/compat'; |
||||
import { memo, useCallback } from 'preact/compat'; |
||||
|
||||
import styles from './styles.scss'; |
||||
import { createClassName } from '../../../../helpers/createClassName'; |
||||
import KebabIcon from '../../../../icons/kebab.svg'; |
||||
import { Button } from '../../../Button'; |
||||
import Menu, { PopoverMenu } from '../../../Menu'; |
||||
import { usePerformAction } from '../Block'; |
||||
|
||||
type OverflowTriggerProps = { |
||||
loading: boolean; |
||||
onClick: () => void; |
||||
}; |
||||
|
||||
const OverflowTrigger = ({ loading, onClick }: OverflowTriggerProps) => { |
||||
const handleMouseUp = useCallback(({ currentTarget }: TargetedEvent<HTMLElement>) => { |
||||
currentTarget.blur(); |
||||
}, []); |
||||
|
||||
return ( |
||||
<Button |
||||
className={createClassName(styles, 'uikit-overflow__trigger')} |
||||
disabled={loading} |
||||
outline |
||||
secondary |
||||
onClick={onClick} |
||||
onMouseUp={handleMouseUp} |
||||
> |
||||
<KebabIcon width={20} height={20} /> |
||||
</Button> |
||||
); |
||||
}; |
||||
|
||||
type OverflowOptionProps = uikit.Option & { |
||||
confirm: boolean; |
||||
parser: uikit.SurfaceRenderer<ComponentChild>; |
||||
onClick: (value: string) => void; |
||||
}; |
||||
|
||||
const OverflowOption = ({ confirm, text, value, url, parser, onClick }: OverflowOptionProps) => { |
||||
const handleClick = useCallback( |
||||
async (event: TargetedEvent<HTMLElement, MouseEvent>) => { |
||||
event.preventDefault(); |
||||
|
||||
if (confirm) { |
||||
// TODO
|
||||
} |
||||
|
||||
if (url) { |
||||
const newTab = window.open(); |
||||
if (!newTab) { |
||||
throw new Error('Could not open new tab'); |
||||
} |
||||
newTab.opener = null; |
||||
newTab.location = url; |
||||
return; |
||||
} |
||||
|
||||
await onClick(value); |
||||
}, |
||||
[confirm, onClick, url, value], |
||||
); |
||||
|
||||
return <Menu.Item onClick={handleClick}>{parser.renderTextObject(text, 0)}</Menu.Item>; |
||||
}; |
||||
|
||||
type OverflowElementProps = uikit.OverflowElement & { |
||||
parser: uikit.SurfaceRenderer<ComponentChild>; |
||||
}; |
||||
|
||||
const OverflowElement = ({ actionId, confirm, options, parser }: OverflowElementProps) => { |
||||
const [performAction, performingAction] = usePerformAction(actionId); |
||||
|
||||
const handleClick = useCallback( |
||||
async (value: TargetedEvent<HTMLElement, MouseEvent>) => { |
||||
await performAction({ value }); |
||||
}, |
||||
[performAction], |
||||
); |
||||
|
||||
return ( |
||||
<PopoverMenu trigger={({ pop }) => <OverflowTrigger loading={performingAction} onClick={pop} />}> |
||||
<Menu.Group> |
||||
{Array.isArray(options) && |
||||
options.map((option, i) => <OverflowOption key={i} {...option} confirm={confirm} parser={parser} onClick={handleClick} />)} |
||||
</Menu.Group> |
||||
</PopoverMenu> |
||||
); |
||||
}; |
||||
|
||||
export default memo(OverflowElement); |
||||
@ -1,43 +0,0 @@ |
||||
import store from '../store'; |
||||
import { supportedLocales } from '../supportedLocales'; |
||||
|
||||
/** |
||||
* To normalize Language String and return language code |
||||
* @param {String} languageString |
||||
*/ |
||||
export const normalizeLanguageString = (languageString) => { |
||||
let [languageCode, countryCode] = languageString.split ? languageString.split(/[-_]/) : []; |
||||
if (!languageCode || languageCode.length !== 2) { |
||||
return 'en'; |
||||
} |
||||
languageCode = languageCode.toLowerCase(); |
||||
|
||||
if (!countryCode || countryCode.length !== 2) { |
||||
countryCode = null; |
||||
} else { |
||||
countryCode = countryCode.toUpperCase(); |
||||
} |
||||
|
||||
return countryCode ? `${languageCode}-${countryCode}` : languageCode; |
||||
}; |
||||
|
||||
/** |
||||
* To get browser Language of user |
||||
*/ |
||||
export const browserLanguage = () => navigator.userLanguage || navigator.language; |
||||
|
||||
/** |
||||
* This is configured langauge |
||||
*/ |
||||
export const configLanguage = () => { |
||||
const { config: { settings: { language } = {} } = {}, iframe: { language: iframeLanguage } = {} } = store.state; |
||||
return iframeLanguage || language; |
||||
}; |
||||
|
||||
export const getDateFnsLocale = () => { |
||||
let fullLanguage = configLanguage() || browserLanguage(); |
||||
fullLanguage = fullLanguage.toLowerCase(); |
||||
const [languageCode] = fullLanguage.split ? fullLanguage.split(/[-_]/) : []; |
||||
const locale = [fullLanguage, languageCode, 'en-US'].find((lng) => supportedLocales.indexOf(lng) > -1); |
||||
return import(`date-fns/locale/${locale}.js`).then((module) => module.default); |
||||
}; |
||||
@ -0,0 +1,46 @@ |
||||
import type { Locale } from 'date-fns'; |
||||
|
||||
import store from '../store'; |
||||
import { supportedLocales } from '../supportedLocales'; |
||||
|
||||
/** |
||||
* To normalize Language String and return language code |
||||
*/ |
||||
export const normalizeLanguageString = (languageString: string): string => { |
||||
let [languageCode, countryCode]: (string | undefined)[] = languageString.split?.(/[-_]/) ?? []; |
||||
if (languageCode?.length !== 2) { |
||||
return 'en'; |
||||
} |
||||
languageCode = languageCode.toLowerCase(); |
||||
|
||||
if (countryCode?.length !== 2) { |
||||
countryCode = undefined; |
||||
} else { |
||||
countryCode = countryCode.toUpperCase(); |
||||
} |
||||
|
||||
return countryCode ? `${languageCode}-${countryCode}` : languageCode; |
||||
}; |
||||
|
||||
/** |
||||
* To get browser Language of user |
||||
*/ |
||||
export const browserLanguage = (): string => navigator.language; |
||||
|
||||
/** |
||||
* This is configured langauge |
||||
*/ |
||||
export const configLanguage = (): string | undefined => { |
||||
const { iframe: { language: iframeLanguage } = {} } = store.state; |
||||
const language = (store.state.config?.settings as Record<string, unknown> | undefined)?.language as string | undefined; |
||||
return iframeLanguage || language; |
||||
}; |
||||
|
||||
export const getDateFnsLocale = async (): Promise<Locale> => { |
||||
let fullLanguage = configLanguage() || browserLanguage(); |
||||
fullLanguage = fullLanguage.toLowerCase(); |
||||
const [languageCode] = fullLanguage.split?.(/[-_]/) ?? []; |
||||
const locale = [fullLanguage, languageCode, 'en-US'].find((lng) => supportedLocales.indexOf(lng) > -1); |
||||
const { default: dateFnsLocale } = await import(`date-fns/locale/${locale}.js`); |
||||
return dateFnsLocale as Locale; |
||||
}; |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue