packages/ketcher-react/src/script/ui/views/toolbars/LeftToolbar/LeftToolbar.tsx (173 lines of code) (raw):

/**************************************************************************** * Copyright 2021 EPAM Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ import { FC, MutableRefObject, useRef } from 'react'; import { IMAGE_KEY } from 'ketcher-core'; import { ToolbarGroupItem, ToolbarGroupItemCallProps, ToolbarGroupItemProps, } from '../ToolbarGroupItem'; import { ToolbarItem, ToolbarItemVariant } from '../toolbar.types'; import { arrowsOptions, bondCommon, bondQuery, bondSpecial, bondStereo, mappingOptions, rGroupOptions, selectOptions, shapeOptions, } from './leftToolbarOptions'; import { ArrowScroll } from '../ArrowScroll'; import { Bond } from './Bond'; import { RGroup } from './RGroup'; import { Shape } from './Shape'; import classes from './LeftToolbar.module.less'; import clsx from 'clsx'; import { useInView } from 'react-intersection-observer'; import { useResizeObserver } from '../../../../../hooks'; interface LeftToolbarProps extends Omit<ToolbarGroupItemProps, 'id' | 'options'> { className?: string; } type LeftToolbarCallProps = ToolbarGroupItemCallProps; type Props = LeftToolbarProps & LeftToolbarCallProps; const LeftToolbar = (props: Props) => { const { className, ...rest } = props; const { ref, height } = useResizeObserver<HTMLDivElement>(); const scrollRef = useRef() as MutableRefObject<HTMLDivElement>; const [startRef, startInView] = useInView({ threshold: 1 }); const [endRef, endInView] = useInView({ threshold: 1 }); const sizeRef = useRef() as MutableRefObject<HTMLDivElement>; type ItemProps = { id: ToolbarItemVariant; options?: ToolbarItem[]; dataTestId?: string; }; const Item = ({ id, options, dataTestId }: ItemProps) => ToolbarGroupItem({ id, options, dataTestId, ...rest }); const scrollUp = () => { scrollRef.current.scrollTop -= sizeRef.current.offsetHeight; }; const scrollDown = () => { scrollRef.current.scrollTop += sizeRef.current.offsetHeight; }; const status = rest.status; type GroupItem = ItemProps; const Group: FC<{ items?: GroupItem[]; className?: string }> = ({ items, className, }) => { const visibleItems: GroupItem[] = []; if (items) { items.forEach((item) => { let visible = true; if (status[item.id]?.hidden) { visible = false; } else if (item.options?.every((option) => status[option.id]?.hidden)) { visible = false; } if (visible) visibleItems.push(item); }); } return visibleItems.length ? ( <div className={clsx(classes.group, className)}> {visibleItems.map((item) => { switch (item.id) { case 'bond-common': return <Bond {...rest} height={height} key={item.id} />; case 'rgroup': return <RGroup {...rest} key={item.id} />; case 'shapes': return <Shape {...rest} key={item.id} />; case 'bonds': return ( <Item id={item.id} options={item.options} key={item.id} dataTestId="bonds" /> ); default: return <Item id={item.id} options={item.options} key={item.id} />; } })} </div> ) : null; }; return ( <div data-testid="left-toolbar" className={clsx(classes.root, className)} ref={ref} > <div className={classes.buttons} ref={scrollRef} data-testid="left-toolbar-buttons" > <div className={classes.listener} ref={startRef}> <Group className={classes.groupItem} items={[ { id: 'hand' }, { id: 'select', options: selectOptions }, { id: 'erase' }, ]} /> </div> <Group className={classes.groupItem} items={[ { id: 'bonds', options: [ ...bondCommon, ...bondQuery, ...bondSpecial, ...bondStereo, ], }, { id: 'chain' }, { id: 'enhanced-stereo' }, { id: 'charge-plus' }, { id: 'charge-minus' }, ]} /> <div className={classes.listener} ref={sizeRef}> <Group className={classes.groupItem} items={[{ id: 'sgroup' }, { id: 'rgroup', options: rGroupOptions }]} /> </div> <Group className={classes.groupItem} items={[ { id: 'reaction-plus' }, { id: 'arrows', options: arrowsOptions }, { id: 'reaction-mapping-tools', options: mappingOptions, }, ]} /> <div ref={endRef}> <Group className={classes.groupItem} items={[ { id: 'shapes', options: shapeOptions }, { id: 'text' }, { id: IMAGE_KEY }, ]} /> </div> </div> {height && scrollRef?.current?.scrollHeight > height && ( <ArrowScroll startInView={startInView} endInView={endInView} scrollUp={scrollUp} scrollDown={scrollDown} /> )} </div> ); }; export type { LeftToolbarProps, LeftToolbarCallProps }; export { LeftToolbar };