UNPKG

@onesy/ui-react

Version:
373 lines 13.8 kB
import _extends from "@babel/runtime/helpers/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; const _excluded = ["tonal", "color", "label", "onSelect", "emojis", "categories", "selected", "size", "search", "tabs", "className"]; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } import React from 'react'; import { is, debounce, clamp, capitalize } from '@onesy/utils'; import { classNames, style as styleMethod, useOnesyTheme } from '@onesy/style-react'; import IconMaterialMood from '@onesy/icons-material-rounded-react/IconMaterialMoodW100'; import IconMaterialEmojiNature from '@onesy/icons-material-rounded-react/IconMaterialEmojiNatureW100'; import IconMaterialEmojiFoodBeverage from '@onesy/icons-material-rounded-react/IconMaterialEmojiFoodBeverageW100'; import IconMaterialHiking from '@onesy/icons-material-rounded-react/IconMaterialHikingW100'; import IconMaterialEmojiTransportation from '@onesy/icons-material-rounded-react/IconMaterialEmojiTransportationW100'; import IconMaterialEmojiObjects from '@onesy/icons-material-rounded-react/IconMaterialEmojiObjectsW100'; import IconMaterialEmojiSymbols from '@onesy/icons-material-rounded-react/IconMaterialEmojiSymbolsW100'; import IconMaterialEmojiFlags from '@onesy/icons-material-rounded-react/IconMaterialEmojiFlagsW100'; import MenuElement from '../Menu/Menu'; import LineElement from '../Line'; import SpyScrollElement from '../SpyScroll'; import ListElement from '../List'; import ListSubheaderElement from '../ListSubheader'; import TabsElement from '../Tabs'; import TextFieldElement from '../TextField'; import TabElement from '../Tab'; import TypeElement from '../Type'; import SurfaceElement from '../Surface'; import emojis_list from './emojis_list'; import { staticClassName } from '../utils'; const useStyle = styleMethod(theme => ({ root: {}, wrapper: { height: '70vh', width: '70vw', borderRadius: theme.methods.shape.radius.value(2, 'px'), boxShadow: 'rgba(0, 0, 0, 0.07) 0px 4px 32px 0px', overflow: 'hidden' }, size_small: { maxWidth: 276, maxHeight: 276 }, size_regular: { maxWidth: 340, maxHeight: 340 }, size_large: { maxWidth: 404, maxHeight: 404 }, main: { position: 'relative', height: 0, overflow: 'hidden auto' }, header: { padding: `${theme.methods.space.value(1.5, 'px')} ${theme.methods.space.value(1.5, 'px')} 0` }, categories: { height: '100%', userSelect: 'none' }, category: { paddingInlineStart: theme.methods.space.value(1.5, 'px') }, emoji: { position: 'relative', textAlign: 'center', cursor: 'pointer', transition: theme.methods.transitions.make('transform'), '&$emoji_selected': { '&::before': { content: '""', position: 'absolute', width: 'calc(100% + 4px)', height: 'auto', aspectRatio: '1/1', background: theme.palette.background.default.tertiary, inset: '0', borderRadius: theme.methods.shape.radius.value(40, 'px'), zIndex: '0', left: '50%', transform: 'translate(-50%)', top: '-1px' } }, '&:active': { transform: 'scale(0.94)' } }, emoji_size_small: { fontSize: '1.5rem', width: 24 }, emoji_size_regular: { fontSize: '2rem', width: 32 }, emoji_size_large: { fontSize: '2.5rem', width: 40 }, unicode: { position: 'relative', zIndex: 1 }, subheader: { backdropFilter: 'blur(2px)', background: 'transparent', '&.onesy-ListSubheader-root': { zIndex: 14 } }, tabs: { '&.onesy-Tabs-root': { background: 'transparent' }, '& .onesy-Tabs-tabs': { minWidth: '100%' } }, tab: { '&.onesy-Tab-root': { minWidth: 'unset' } }, tab_size_small: { '&.onesy-Tab-root': { padding: `${theme.methods.space.value(1, 'px')} ${theme.methods.space.value(0.75, 'px')}` } }, tab_size_regular: { '&.onesy-Tab-root': { padding: `${theme.methods.space.value(1, 'px')} ${theme.methods.space.value(1.25, 'px')}` } }, tab_size_large: { '&.onesy-Tab-root': { padding: `${theme.methods.space.value(1, 'px')} ${theme.methods.space.value(1.25, 'px')}` } } }), { name: 'onesy-Emojis' }); const Emojis = /*#__PURE__*/React.forwardRef((props_, ref) => { const theme = useOnesyTheme(); const l = theme.l; const props = React.useMemo(() => _objectSpread(_objectSpread(_objectSpread({}, theme?.ui?.elements?.all?.props?.default), theme?.ui?.elements?.onesyEmojis?.props?.default), props_), [props_]); const Line = React.useMemo(() => theme?.elements?.Line || LineElement, [theme]); const Menu = React.useMemo(() => theme?.elements?.Menu || MenuElement, [theme]); const List = React.useMemo(() => theme?.elements?.List || ListElement, [theme]); const ListSubheader = React.useMemo(() => theme?.elements?.ListSubheader || ListSubheaderElement, [theme]); const SpyScroll = React.useMemo(() => theme?.elements?.SpyScroll || SpyScrollElement, [theme]); const Tabs = React.useMemo(() => theme?.elements?.Tabs || TabsElement, [theme]); const TextField = React.useMemo(() => theme?.elements?.TextField || TextFieldElement, [theme]); const Tab = React.useMemo(() => theme?.elements?.Tab || TabElement, [theme]); const Type = React.useMemo(() => theme?.elements?.Type || TypeElement, [theme]); const Surface = React.useMemo(() => theme?.elements?.Surface || SurfaceElement, [theme]); const EMOJI_CATEGORIES = React.useMemo(() => { return [{ "name": l("Smileys and People"), "groups": ["Smiley", "Gesture", "Person", "Clothing"], "icon": /*#__PURE__*/React.createElement(IconMaterialMood, null) }, { "name": l("Animals and Nature"), "groups": ["Animal", "Nature"], "icon": /*#__PURE__*/React.createElement(IconMaterialEmojiNature, null) }, { "name": l("Food and Drink"), "groups": ["Food"], "icon": /*#__PURE__*/React.createElement(IconMaterialEmojiFoodBeverage, null) }, { "name": l("Activity"), "groups": ["Activity"], "icon": /*#__PURE__*/React.createElement(IconMaterialHiking, null) }, { "name": l("Travel and Places"), "groups": ["Travel"], "icon": /*#__PURE__*/React.createElement(IconMaterialEmojiTransportation, null) }, { "name": l("Objects"), "groups": ["Object"], "icon": /*#__PURE__*/React.createElement(IconMaterialEmojiObjects, null) }, { "name": l("Symbols"), "groups": ["Symbol"], "icon": /*#__PURE__*/React.createElement(IconMaterialEmojiSymbols, null) }, { "name": l("Flags and Countries"), "groups": ["Flag", "Country"], "icon": /*#__PURE__*/React.createElement(IconMaterialEmojiFlags, null) }]; }, []); const { tonal = true, color = 'themed', label: label_, onSelect: onSelect_, emojis = emojis_list, categories = EMOJI_CATEGORIES, selected, size = 'regular', search: search_ = true, tabs: tabs_ = true, className } = props, other = _objectWithoutProperties(props, _excluded); const { classes } = useStyle(); const [open, setOpen] = React.useState(false); const [openElement, setOpenElement] = React.useState(); const [search, setSearch] = React.useState(); const [tab, setTab] = React.useState(categories[0].name); const refs = { main: React.useRef(undefined) }; const onOpen = React.useCallback(() => { setOpen(true); setTimeout(() => { setOpenElement(refs.main.current || null); }, 140); }, []); const onClose = React.useCallback(() => { setOpen(false); setTimeout(() => { setOpenElement(null); setSearch(''); }, 140); }, []); const onSelect = React.useCallback(valueNew => { if (is('function', onSelect_)) onSelect_(valueNew); }, [onSelect_]); const emojisPerCategory = React.useMemo(() => { const value = {}; if (is('array', categories)) { categories.forEach(item => { const groups = {}; emojis.forEach(emoji => { const alias = (is('array', emoji.alias) ? emoji.alias : [emoji.alias]).join('').toLowerCase(); if (item.groups.includes(emoji.category) && (!search || alias.includes(search.toLowerCase()))) { if (!groups[emoji.category]) groups[emoji.category] = []; groups[emoji.category].unshift(emoji); } }); value[item.name] = []; item.groups.forEach(group => { if (!!groups[group]?.length) value[item.name].push(...groups[group]); }); }); Object.keys(value).forEach(item => { if (!value[item].length) delete value[item]; }); } return value; }, [search, emojis, categories]); const onChangeSearch = React.useCallback(debounce(valueNew => { setSearch(valueNew); }, 140), []); const onChangeTabs = React.useCallback(valueNew => { setTab(valueNew); }, []); const onActiveTab = React.useCallback(valueNew => { setTab(valueNew.replace(/-/g, ' ')); }, []); const onTabClick = React.useCallback(valueNew => { const elements = Array.from(refs.main.current?.children || []); if (!!elements.length) { const element = elements.find(item => item.dataset.onesyCategory === valueNew); if (element) { const top = element.offsetTop; const offset = -1; refs.main.current.scroll({ left: 0, top: clamp(top - offset, 0), behavior: 'smooth' }); } } }, []); const categoriesUsed = Object.keys(emojisPerCategory); const categoryToID = valueNew => valueNew.replace(/ /g, '-'); const categoriesUsedIDs = categoriesUsed.map(item => categoryToID(item)); const tabs = categories.filter(item => categoriesUsed.some(item_ => item.name === item_)).map(item => ({ name: item.icon, value: item.name })); const label = /*#__PURE__*/React.createElement(Line, { tonal: tonal, color: color, Component: Surface, className: classNames([classes.wrapper, classes[`size_${size}`]]) }, /*#__PURE__*/React.createElement(SpyScroll, { ids: categoriesUsedIDs, parent: openElement, onActive: onActiveTab }, /*#__PURE__*/React.createElement(Line, { gap: 0, align: "unset", justify: "unset", fullWidth: true, className: classes.categories }, (search_ || tabs_) && /*#__PURE__*/React.createElement(Line, { gap: 1, fullWidth: true, className: classes.header }, search_ && /*#__PURE__*/React.createElement(TextField, { name: l('Search'), version: "outlined", value: search || '', onChange: onChangeSearch, size: ['small', 'regular'].includes(size) ? 'small' : 'regular', fullWidth: true, clear: true }), tabs_ && !!categoriesUsed.length && /*#__PURE__*/React.createElement(Tabs, { valueDefault: tab, value: tab, onChange: onChangeTabs, size: "small", initialLineUpdateTimeout: 440, noDivider: true, className: classes.tabs }, tabs.map((item, index) => /*#__PURE__*/React.createElement(Tab, { key: index, value: item.value, onClick: () => onTabClick(item.value), "data-onesy-spy-scroll": categoryToID(item.value), className: classNames([classes.tab, classes[`tab_size_${size}`]]) }, /*#__PURE__*/React.cloneElement(item.name, { size: ['small', 'regular'].includes(size) ? 'small' : 'regular' }))))), /*#__PURE__*/React.createElement(Line, { ref: refs.main, gap: 0, flex: true, fullWidth: true, className: classes.main }, categoriesUsed.map((item, index) => /*#__PURE__*/React.createElement(List, { key: index, gap: 1, fullWidth: true, SurfaceProps: { id: categoryToID(item), 'data-onesy-category': item } }, /*#__PURE__*/React.createElement(ListSubheader, { className: classes.subheader }, /*#__PURE__*/React.createElement(Type, { version: size === 'large' ? 'b1' : size === 'regular' ? 'b2' : 'b3' }, item)), /*#__PURE__*/React.createElement(Line, { gap: 1, direction: "row", align: "center", wrap: "wrap", Component: "li", className: classes.category }, emojisPerCategory[item].map((emoji, index_) => /*#__PURE__*/React.createElement("span", { key: index_, onClick: () => onSelect(emoji), title: capitalize((is('array', emoji.alias) ? emoji.alias.join(', ') : emoji.alias).replace(/_/g, ' ')), className: classNames([classes.emoji, classes[`emoji_size_${size}`], selected?.includes(emoji.unicode) && classes.emoji_selected]) }, /*#__PURE__*/React.createElement("span", { className: classes.unicode }, emoji.unicode)))))))))); return /*#__PURE__*/React.createElement(Menu, _extends({ open: open, onOpen: onOpen, onClose: onClose, label: label, className: classNames([staticClassName('Emojis', theme) && ['onesy-Emojis-root', `onesy-Emojis-size-${size}`], className, classes.root]) }, other)); }); Emojis.displayName = 'onesy-Emojis'; export default Emojis;