From 165975d3694e6c2737fbf8f200bd0dbe9757a12f Mon Sep 17 00:00:00 2001 From: Leonardo <leomilho@gmail.com> Date: Sun, 9 Jun 2024 17:06:29 +0200 Subject: [PATCH] feat: floating ui refactor using floating ui for popups, dialog, dropdowns and tooltips --- .../dbConnectionSelector.tsx | 129 ++- .../DatabaseManagement/forms/databaseForm.tsx | 1 + .../DatabaseManagement/forms/settings.tsx | 149 ++-- apps/web/src/components/navbar/navbar.tsx | 31 +- apps/web/src/main.css | 5 - .../components/DesignGuides/styleGuide.mdx | 2 + libs/shared/lib/components/buttons/Button.tsx | 161 ++++ .../lib/components/buttons/button.stories.tsx | 8 +- libs/shared/lib/components/buttons/index.tsx | 162 +--- .../lib/components/buttons/overview.mdx | 1 + .../colorComponents/colorDropdown/index.tsx | 40 +- .../shared/lib/components/dropdowns/index.tsx | 99 +-- .../dropdowns/menudropdown.stories.tsx | 16 +- libs/shared/lib/components/forms/index.tsx | 11 +- libs/shared/lib/components/info/index.tsx | 20 +- libs/shared/lib/components/inputs/index.tsx | 453 +++++------ libs/shared/lib/components/layout/Dialog.tsx | 217 ++++- libs/shared/lib/components/layout/Popover.tsx | 244 ++++++ libs/shared/lib/components/pills/Pill.tsx | 17 +- libs/shared/lib/components/tabs/Tab.tsx | 22 +- .../shared/lib/components/tooltip/Tooltip.tsx | 165 ++++ libs/shared/lib/components/tooltip/index.tsx | 42 +- .../components/tooltip/tooltip.stories.tsx | 8 +- libs/shared/lib/inspector/InspectorPanel.tsx | 2 +- .../lib/querybuilder/panel/QueryBuilder.tsx | 121 +-- .../queryBuilderLogicPillsPanel.tsx | 33 +- .../panel/querysidepanel/queryMLDialog.tsx | 124 ++- .../logicpill/QueryLogicPill.tsx | 4 +- libs/shared/lib/schema/panel/Schema.tsx | 19 +- .../lib/schema/panel/SchemaSettings.tsx | 7 +- .../pills/nodes/entity/SchemaEntityPill.tsx | 1 + .../nodes/relation/SchemaRelationPill.tsx | 1 + libs/shared/lib/sidebar/index.tsx | 2 +- libs/shared/lib/sidebar/search/SearchBar.tsx | 4 +- libs/shared/lib/vis/components/bar.tsx | 70 +- .../config/ActiveVisualizationConfig.tsx | 2 +- .../vis/components/config/SelectionConfig.tsx | 2 +- libs/shared/lib/vis/views/noData.tsx | 2 +- .../vis/visualizations/paohvis/paohvis.tsx | 4 +- .../components/ConfigPanel.tsx | 2 +- .../vis/visualizations/tablevis/tablevis.tsx | 2 +- libs/shared/package.json | 3 +- libs/storybook/src/stories/Button.stories.ts | 44 - libs/storybook/src/stories/Button.tsx | 42 - libs/storybook/src/stories/button.css | 30 - pnpm-lock.yaml | 751 ++---------------- 46 files changed, 1510 insertions(+), 1765 deletions(-) create mode 100644 libs/shared/lib/components/buttons/Button.tsx create mode 100644 libs/shared/lib/components/layout/Popover.tsx create mode 100644 libs/shared/lib/components/tooltip/Tooltip.tsx delete mode 100644 libs/storybook/src/stories/Button.stories.ts delete mode 100644 libs/storybook/src/stories/Button.tsx delete mode 100644 libs/storybook/src/stories/button.css diff --git a/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx index 6ed8758c7..2775725cf 100644 --- a/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx +++ b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx @@ -5,7 +5,7 @@ import { deleteSaveState, selectSaveState } from '@graphpolaris/shared/lib/data- import { SettingsForm } from './forms/settings'; import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner'; import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice'; -import { DropdownButton, DropdownContainer, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns'; +import { DropdownTrigger, DropdownContainer, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns'; import { clearQB } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; import { clearSchema } from '@graphpolaris/shared/lib/data-access/store/schemaSlice'; import { DatabaseStatus, SaveStateI, nilUUID, wsDeleteState } from '@graphpolaris/shared/lib/data-access/broker'; @@ -17,7 +17,6 @@ export default function DatabaseSelector({}) { const session = useSessionCache(); const schemaGraph = useSchemaGraph(); const authCache = useAuthorizationCache(); - const dbSelectionMenuRef = React.useRef<HTMLDivElement>(null); const [hovered, setHovered] = useState<string | null>(null); const [connecting, setConnecting] = useState<boolean>(false); const [dbSelectionMenuOpen, setDbSelectionMenuOpen] = useState<boolean>(false); @@ -37,18 +36,6 @@ export default function DatabaseSelector({}) { } }, [session, settingsMenuOpen]); - useEffect(() => { - const handleClickOutside = ({ target }: MouseEvent) => { - if (dbSelectionMenuRef.current && !dbSelectionMenuRef.current.contains(target as Node)) { - setDbSelectionMenuOpen(false); - } - }; - document.addEventListener('click', handleClickOutside); - return () => { - document.removeEventListener('click', handleClickOutside); - }; - }, []); - useEffect(() => { setConnecting(false); }, [schemaGraph]); @@ -86,8 +73,24 @@ export default function DatabaseSelector({}) { }} /> )} - <DropdownContainer ref={dbSelectionMenuRef} className="w-[18rem]"> - <DropdownButton + <DropdownContainer + open={dbSelectionMenuOpen} + onOpenChange={(ret) => { + console.log('dbSelectionMenuOpen', dbSelectionMenuOpen, ret); + + if (!ret) { + if (session.saveStates && Object.keys(session.saveStates).length === 0) setSettingsMenuOpen('add'); + else setDbSelectionMenuOpen(!dbSelectionMenuOpen); + } else { + setDbSelectionMenuOpen(true); + } + }} + > + <DropdownTrigger + onClick={() => { + setDbSelectionMenuOpen(!dbSelectionMenuOpen); + }} + className="w-[18rem]" size="md" disabled={connecting || authCache.authorized === false || !!authCache.roomID} title={ @@ -126,14 +129,10 @@ export default function DatabaseSelector({}) { )} </div> } - onClick={() => { - if (session.saveStates && Object.keys(session.saveStates).length === 0) setSettingsMenuOpen('add'); - else setDbSelectionMenuOpen(!dbSelectionMenuOpen); - }} /> - {dbSelectionMenuOpen && session.saveStates !== undefined && ( - <DropdownItemContainer align="top-10 w-full z-30"> + {session.saveStates !== undefined && ( + <DropdownItemContainer> <li className="flex items-center p-2 hover:bg-secondary-50 cursor-pointer" onClick={(e) => { @@ -200,51 +199,49 @@ export default function DatabaseSelector({}) { {save.db.url} </p> </div> - {hovered === save.id && ( - <div className="flex items-center ml-2"> - <div - className="text-secondary-700 hover:text-secondary-400 transition-colors duration-300" - onClick={(e) => { - e.preventDefault(); - e.stopPropagation(); - setSettingsMenuOpen('update'); - setSelectedSaveState(save); - }} - > - <Tooltip> - <TooltipTrigger> - <Settings /> - </TooltipTrigger> - <TooltipContent side={'top'}> - <p>Change the connection details</p> - </TooltipContent> - </Tooltip> - </div> - <div - className="text-secondary-700 hover:text-secondary-400 transition-colors duration-300" - onClick={(e) => { - e.preventDefault(); - e.stopPropagation(); - setDbSelectionMenuOpen(false); - if (session.currentSaveState === save.id) { - dispatch(clearQB()); - dispatch(clearSchema()); - } - wsDeleteState(save.id); - dispatch(deleteSaveState(save.id)); - }} - > - <Tooltip> - <TooltipTrigger> - <Delete /> - </TooltipTrigger> - <TooltipContent side={'top'}> - <p>Delete the database</p> - </TooltipContent> - </Tooltip> - </div> + <div className={`flex items-center ml-2 ${hovered === save.id ? 'display' : 'invisible'}`}> + <div + className="text-secondary-700 hover:text-secondary-400 transition-colors duration-300" + onClick={(e) => { + e.preventDefault(); + e.stopPropagation(); + setSettingsMenuOpen('update'); + setSelectedSaveState(save); + }} + > + <Tooltip> + <TooltipTrigger> + <Settings /> + </TooltipTrigger> + <TooltipContent side={'top'}> + <p>Change the connection details</p> + </TooltipContent> + </Tooltip> </div> - )} + <div + className="text-secondary-700 hover:text-secondary-400 transition-colors duration-300" + onClick={(e) => { + e.preventDefault(); + e.stopPropagation(); + setDbSelectionMenuOpen(false); + if (session.currentSaveState === save.id) { + dispatch(clearQB()); + dispatch(clearSchema()); + } + wsDeleteState(save.id); + dispatch(deleteSaveState(save.id)); + }} + > + <Tooltip> + <TooltipTrigger> + <Delete /> + </TooltipTrigger> + <TooltipContent side={'top'}> + <p>Delete the database</p> + </TooltipContent> + </Tooltip> + </div> + </div> </li> ))} </DropdownItemContainer> diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx index db78c4740..142fb97a6 100644 --- a/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx +++ b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx @@ -80,6 +80,7 @@ export const DatabaseForm = (props: { data: SaveStateI; onChange: (data: SaveSta <div className="flex w-full gap-2"> <Input type="dropdown" + className="w-full" label="Database Type" required value={databaseNameMapping[formData.db.type]} diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx index bf84e5acf..1c761a6a2 100644 --- a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx +++ b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx @@ -9,7 +9,7 @@ import { nilUUID, } from '@graphpolaris/shared/lib/data-access'; import { ErrorOutline } from '@mui/icons-material'; -import { Dialog } from '@graphpolaris/shared/lib/components/layout'; +import { Dialog, DialogContent } from '@graphpolaris/shared/lib/components/layout'; import { Button } from '@graphpolaris/shared/lib/components/buttons'; import { useImmer } from 'use-immer'; import { addSaveState, testedSaveState } from '@graphpolaris/shared/lib/data-access/store/sessionSlice'; @@ -113,87 +113,96 @@ export const SettingsForm = (props: { onClose(): void; open: 'add' | 'update'; s } return ( - <Dialog open={!!props.open} onClose={props.onClose} className="lg:min-w-[50rem]"> - <div className="flex justify-between align-center"> - <h2 className="text-xl font-bold">{formTitle} Database Connection</h2> - <div> + <Dialog + open={!!props.open} + onOpenChange={(ret) => { + if (!ret) props.onClose; + }} + > + <DialogContent className="lg:min-w-[50rem]"> + <div className="flex justify-between align-center"> + <h2 className="text-xl font-bold">{formTitle} Database Connection</h2> + <div> + {sampleDataPanel === true ? ( + <Button variant="outline" label="Go back" onClick={() => setSampleDataPanel(false)} /> + ) : sampleDataPanel === false ? ( + <> + <h1 className="font-light text-xs">No data?</h1> + <p className="font-light text-sm cursor-pointer underline" onClick={() => setSampleDataPanel(true)}> + Try sample data + </p> + </> + ) : ( + '' + )} + </div> + </div> + + <> {sampleDataPanel === true ? ( - <Button variant="outline" label="Go back" onClick={() => setSampleDataPanel(false)} /> - ) : sampleDataPanel === false ? ( - <> - <h1 className="font-light text-xs">No data?</h1> - <p className="font-light text-sm cursor-pointer underline" onClick={() => setSampleDataPanel(true)}> - Try sample data - </p> - </> + <SampleDatabaseSelector + onClick={(data) => { + setHasError(false); + handleSubmit({ ...data, user_id: auth.userID || '' }); + }} + /> ) : ( - '' + <DatabaseForm + data={formData} + onChange={(data: SaveStateI, error: boolean) => { + setFormData({ ...data, id: formData.id }); + setHasError(error); + }} + /> )} - </div> - </div> - <> - {sampleDataPanel === true ? ( - <SampleDatabaseSelector - onClick={(data) => { - setHasError(false); - handleSubmit({ ...data, user_id: auth.userID || '' }); - }} - /> - ) : ( - <DatabaseForm - data={formData} - onChange={(data: SaveStateI, error: boolean) => { - setFormData({ ...data, id: formData.id }); - setHasError(error); - }} - /> - )} - - {!(connection.status === null) && ( - <div className={`flex flex-col justify-center items-center`}> - <div className="flex justify-center items-center"> - {connection.verified === false && <ErrorOutline className="text-secondary-400" />} - <p className="font-light text-sm text-secondary-400 ">{connection.status}</p> + {!(connection.status === null) && ( + <div className={`flex flex-col justify-center items-center`}> + <div className="flex justify-center items-center"> + {connection.verified === false && <ErrorOutline className="text-secondary-400" />} + <p className="font-light text-sm text-secondary-400 ">{connection.status}</p> + </div> + {connection.verified === null && <progress className="progress w-56"></progress>} </div> - {connection.verified === null && <progress className="progress w-56"></progress>} - </div> - )} + )} - <div className={`flex flex-row gap-3 card-actions w-full justify-stretch items-center ${sampleDataPanel === true && 'hidden'}`}> - <Button - type="primary" - className="flex-grow" - label={connection.updating ? formTitle.slice(0, -1) + 'ing...' : formTitle} - onClick={(event) => { - event.preventDefault(); - handleSubmit(); - }} - disabled={connection.updating || hasError} - /> - {props.open === 'update' && ( + <div + className={`pt-4 flex flex-row gap-3 card-actions w-full justify-stretch items-center ${sampleDataPanel === true && 'hidden'}`} + > <Button - type="secondary" + variantType="primary" className="flex-grow" - label={'Clone'} + label={connection.updating ? formTitle.slice(0, -1) + 'ing...' : formTitle} onClick={(event) => { - handleSubmit({ ...formData, name: formData.name + ' (copy)', id: nilUUID }, true); + event.preventDefault(); + handleSubmit(); }} disabled={connection.updating || hasError} /> - )} - <Button - variant="outline" - className="flex-grow" - label="Cancel" - disabled={props.disableCancel} - onClick={(event) => { - event.preventDefault(); - closeDialog(); - }} - /> - </div> - </> + {props.open === 'update' && ( + <Button + variantType="secondary" + className="flex-grow" + label={'Clone'} + onClick={(event) => { + handleSubmit({ ...formData, name: formData.name + ' (copy)', id: nilUUID }, true); + }} + disabled={connection.updating || hasError} + /> + )} + <Button + variant="outline" + className="flex-grow" + label="Cancel" + disabled={props.disableCancel} + onClick={(event) => { + event.preventDefault(); + closeDialog(); + }} + /> + </div> + </> + </DialogContent> </Dialog> ); }; diff --git a/apps/web/src/components/navbar/navbar.tsx b/apps/web/src/components/navbar/navbar.tsx index f9c975f77..d6c6923a7 100644 --- a/apps/web/src/components/navbar/navbar.tsx +++ b/apps/web/src/components/navbar/navbar.tsx @@ -15,6 +15,7 @@ import { useAuthorizationCache, useAuth } from '@graphpolaris/shared/lib/data-ac import DatabaseSelector from './DatabaseManagement/dbConnectionSelector'; import { DropdownItem, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns'; import GpLogo from './gp-logo'; +import { Popover, PopoverContent, PopoverTrigger } from '@graphpolaris/shared/lib/components/layout/Popover'; export const Navbar = () => { const dropdownRef = useRef<HTMLDivElement>(null); @@ -45,15 +46,17 @@ export const Navbar = () => { <div className="ml-auto"> <div className="w-fit" ref={dropdownRef}> - <div - className="relative inline-flex items-center justify-center w-8 h-8 overflow-hidden bg-secondary-500 rounded-full hover:bg-secondary-600 transition-colors duration-150 ease-in-out cursor-pointer" - onClick={() => setMenuOpen(!menuOpen)} - > - <span className="font-medium text-light">{authCache.username?.slice(0, 2).toUpperCase()}</span> - </div> + <Popover> + <PopoverTrigger> + <div + className="relative inline-flex items-center justify-center w-8 h-8 overflow-hidden bg-secondary-500 rounded-full hover:bg-secondary-600 transition-colors duration-150 ease-in-out cursor-pointer" + onClick={() => setMenuOpen(!menuOpen)} + > + <span className="font-medium text-light">{authCache.username?.slice(0, 2).toUpperCase()}</span> + </div> + </PopoverTrigger> - {menuOpen && ( - <DropdownItemContainer className="w-56 z-30" align="right-3"> + <PopoverContent className="w-56 z-30 bg-white rounded-sm border-[1px]"> <div className="p-2 text-sm border-b"> <h2 className="font-bold">user: {authCache.username}</h2> <h3 className="text-xs break-words">session: {authCache.sessionID}</h3> @@ -67,14 +70,6 @@ export const Navbar = () => { auth.newShareRoom(); }} /> - <DropdownItem - value="Advanced" - submenu={ - <> - <DropdownItem value="TBD" onClick={() => {}} /> - </> - } - /> <DropdownItem value="Settings" onClick={() => {}} /> <DropdownItem value="Log out" onClick={() => {}} /> </> @@ -92,8 +87,8 @@ export const Navbar = () => { <div className="p-2 border-t"> <h3 className="text-xs">Version: {buildInfo}</h3> </div> - </DropdownItemContainer> - )} + </PopoverContent> + </Popover> </div> </div> </nav> diff --git a/apps/web/src/main.css b/apps/web/src/main.css index b05311668..d4d0780e2 100644 --- a/apps/web/src/main.css +++ b/apps/web/src/main.css @@ -24,11 +24,6 @@ body { border: 1px solid hsl(var(--clr-sec--200)); } -.tooltip::before { - @apply z-50; - @apply content-[attr(data-tip)]; -} - /* TODO: Find out if this is being used before removing. .react-grid-layout { diff --git a/libs/shared/lib/components/DesignGuides/styleGuide.mdx b/libs/shared/lib/components/DesignGuides/styleGuide.mdx index f840a99e7..00c29bc5a 100644 --- a/libs/shared/lib/components/DesignGuides/styleGuide.mdx +++ b/libs/shared/lib/components/DesignGuides/styleGuide.mdx @@ -908,3 +908,5 @@ To add a new component, include the following files: ``` ``` + +variantType diff --git a/libs/shared/lib/components/buttons/Button.tsx b/libs/shared/lib/components/buttons/Button.tsx new file mode 100644 index 000000000..91aa5b33e --- /dev/null +++ b/libs/shared/lib/components/buttons/Button.tsx @@ -0,0 +1,161 @@ +import React, { ReactElement, useMemo } from 'react'; +import styles from './buttons.module.scss'; +import Icon, { Sizes } from '../icon'; +import { forwardRef } from 'react'; + +type ButtonProps = { + as?: 'button' | 'a' | 'div'; + variantType?: 'primary' | 'secondary' | 'danger'; + variant?: 'solid' | 'outline' | 'ghost'; + size?: '2xs' | 'xs' | 'sm' | 'md' | 'lg'; + label?: string; + rounded?: boolean; + disabled?: boolean; + block?: boolean; + onClick?: (e: any) => void; + href?: string; + iconComponent?: React.ReactElement; + iconPosition?: 'leading' | 'trailing'; + ariaLabel?: string; + children?: React.ReactNode; + className?: string; +}; + +export const Button = React.forwardRef<HTMLButtonElement | HTMLAnchorElement | HTMLDivElement, ButtonProps>( + ( + { + as = 'button', + variantType = 'secondary', + variant = 'solid', + label, + size = 'md', + rounded = false, + disabled = false, + onClick, + href, + block = false, + iconComponent, + iconPosition = 'leading', + ariaLabel, + className, + children, + ...props + }, + forwardedRef, + ) => { + let typeClass = useMemo(() => { + switch (variantType) { + case 'primary': + return styles['btn-primary']; + case 'secondary': + return styles['btn-secondary']; + case 'danger': + return styles['btn-danger']; + default: + return styles['btn-secondary']; + } + }, [variantType]); + + let variantClass = useMemo(() => { + switch (variant) { + case 'solid': + return styles['btn-solid']; + case 'outline': + return styles['btn-outline']; + case 'ghost': + return styles['btn-ghost']; + default: + return styles['btn-solid']; + } + }, [variant]); + + let sizeClass = useMemo(() => { + switch (size) { + case '2xs': + return styles['btn-2xs']; + case 'xs': + return styles['btn-xs']; + case 'sm': + return styles['btn-sm']; + case 'md': + return styles['btn-md']; + case 'lg': + return styles['btn-lg']; + default: + return styles['btn-md']; + } + }, [size]); + + const iconSize = useMemo(() => { + switch (size) { + case '2xs': + return 12; + case 'xs': + return 16; + case 'sm': + return 20; + case 'md': + return 24; + case 'lg': + return 28; + default: + return 24; + } + }, [size]); + + const blockClass = useMemo(() => (block ? styles['btn-block'] : ''), [block, styles]); + const roundedClass = useMemo(() => (rounded ? styles['btn-rounded'] : ''), [rounded, styles]); + + const icon = useMemo( + () => (iconComponent ? <Icon component={iconComponent} size={iconSize} className="ml-auto" /> : null), + [iconComponent, iconSize], + ); + + const iconOnlyClass = useMemo( + () => (iconComponent && !label && !children ? styles['btn-icon-only'] : ''), + [iconComponent, label, children], + ); + + const ButtonComponent = as; + + const isAnchor = as === 'a'; + + return ( + <ButtonComponent + className={`${styles.btn} ${typeClass} ${variantClass} ${sizeClass} ${blockClass} ${roundedClass} ${iconOnlyClass} ${className}`} + onClick={onClick} + disabled={disabled} + aria-label={ariaLabel} + href={isAnchor ? href : undefined} + ref={forwardedRef as React.RefObject<any>} + {...props} + > + {iconPosition === 'leading' && icon} + {label && <span>{label}</span>} + {children && <span>{children}</span>} + {iconPosition === 'trailing' && icon} + </ButtonComponent> + ); + }, +); + +type ButtonGroupProps = { + children: React.ReactNode; +}; + +export function ButtonGroup({ children }: ButtonGroupProps) { + return <div>{children}</div>; +} + +type ButtonItemProps = { + icon: React.ReactNode; + onClick: () => void; +}; + +export function ButtonGroupItem({ icon, onClick }: ButtonItemProps) { + return ( + <div onClick={onClick} className="rounded-sm bg-secondary-50 p-2"> + <span>{icon}</span> + </div> + ); +} diff --git a/libs/shared/lib/components/buttons/button.stories.tsx b/libs/shared/lib/components/buttons/button.stories.tsx index ba7b83568..69931feb8 100644 --- a/libs/shared/lib/components/buttons/button.stories.tsx +++ b/libs/shared/lib/components/buttons/button.stories.tsx @@ -48,7 +48,7 @@ type Story = StoryObj<typeof Button>; const BaseStory: Story = { args: { - type: 'primary', + variantType: 'primary', variant: 'solid', label: 'Click me', size: 'md', @@ -63,14 +63,14 @@ export const Primary = { ...BaseStory }; export const Secondary: Story = { args: { ...BaseStory.args, - type: 'secondary', + variantType: 'secondary', }, }; export const Danger: Story = { args: { ...BaseStory.args, - type: 'danger', + variantType: 'danger', }, }; @@ -97,7 +97,7 @@ export const Ghost: Story = { export const IconButton: Story = { args: { - type: 'primary', + variantType: 'primary', variant: 'outline', iconComponent: <ArrowBack />, rounded: true, diff --git a/libs/shared/lib/components/buttons/index.tsx b/libs/shared/lib/components/buttons/index.tsx index 38ea1a0c3..8b166a86e 100644 --- a/libs/shared/lib/components/buttons/index.tsx +++ b/libs/shared/lib/components/buttons/index.tsx @@ -1,161 +1 @@ -import React, { ReactElement, useMemo } from 'react'; -import styles from './buttons.module.scss'; -import Icon, { Sizes } from '../icon'; -import { forwardRef } from 'react'; - -type ButtonProps = { - as?: 'button' | 'a' | 'div'; - type?: 'primary' | 'secondary' | 'danger'; - variant?: 'solid' | 'outline' | 'ghost'; - size?: '2xs' | 'xs' | 'sm' | 'md' | 'lg'; - label?: string; - rounded?: boolean; - disabled?: boolean; - block?: boolean; - onClick?: (e: any) => void; - href?: string; - iconComponent?: React.ReactElement; - iconPosition?: 'leading' | 'trailing'; - ariaLabel?: string; - children?: React.ReactNode; - className?: string; -}; - -export const Button = React.forwardRef<HTMLButtonElement | HTMLAnchorElement | HTMLDivElement, ButtonProps>( - ( - { - as = 'button', - type = 'secondary', - variant = 'solid', - label, - size = 'md', - rounded = false, - disabled = false, - onClick, - href, - block = false, - iconComponent, - iconPosition = 'leading', - ariaLabel, - className, - children, - ...props - }, - forwardedRef, - ) => { - let typeClass = useMemo(() => { - switch (type) { - case 'primary': - return styles['btn-primary']; - case 'secondary': - return styles['btn-secondary']; - case 'danger': - return styles['btn-danger']; - default: - return styles['btn-secondary']; - } - }, [type]); - - let variantClass = useMemo(() => { - switch (variant) { - case 'solid': - return styles['btn-solid']; - case 'outline': - return styles['btn-outline']; - case 'ghost': - return styles['btn-ghost']; - default: - return styles['btn-solid']; - } - }, [variant]); - - let sizeClass = useMemo(() => { - switch (size) { - case '2xs': - return styles['btn-2xs']; - case 'xs': - return styles['btn-xs']; - case 'sm': - return styles['btn-sm']; - case 'md': - return styles['btn-md']; - case 'lg': - return styles['btn-lg']; - default: - return styles['btn-md']; - } - }, [size]); - - const iconSize = useMemo(() => { - switch (size) { - case '2xs': - return 12; - case 'xs': - return 16; - case 'sm': - return 20; - case 'md': - return 24; - case 'lg': - return 28; - default: - return 24; - } - }, [size]); - - const blockClass = useMemo(() => (block ? styles['btn-block'] : ''), [block, styles]); - const roundedClass = useMemo(() => (rounded ? styles['btn-rounded'] : ''), [rounded, styles]); - - const icon = useMemo( - () => (iconComponent ? <Icon component={iconComponent} size={iconSize} className="ml-auto" /> : null), - [iconComponent, iconSize], - ); - - const iconOnlyClass = useMemo( - () => (iconComponent && !label && !children ? styles['btn-icon-only'] : ''), - [iconComponent, label, children], - ); - - const ButtonComponent = as; - - const isAnchor = as === 'a'; - - return ( - <ButtonComponent - className={`${styles.btn} ${typeClass} ${variantClass} ${sizeClass} ${blockClass} ${roundedClass} ${iconOnlyClass} ${className}`} - onClick={onClick} - disabled={disabled} - aria-label={ariaLabel} - href={isAnchor ? href : undefined} - ref={forwardedRef as React.RefObject<any>} - {...props} - > - {iconPosition === 'leading' && icon} - {label && <span>{label}</span>} - {children && <span>{children}</span>} - {iconPosition === 'trailing' && icon} - </ButtonComponent> - ); - }, -); - -type ButtonGroupProps = { - children: React.ReactNode; -}; - -export function ButtonGroup({ children }: ButtonGroupProps) { - return <div>{children}</div>; -} - -type ButtonItemProps = { - icon: React.ReactNode; - onClick: () => void; -}; - -export function ButtonGroupItem({ icon, onClick }: ButtonItemProps) { - return ( - <div onClick={onClick} className="rounded-sm bg-secondary-50 p-2"> - <span>{icon}</span> - </div> - ); -} +export * from './Button'; diff --git a/libs/shared/lib/components/buttons/overview.mdx b/libs/shared/lib/components/buttons/overview.mdx index 2739cf127..810e45683 100644 --- a/libs/shared/lib/components/buttons/overview.mdx +++ b/libs/shared/lib/components/buttons/overview.mdx @@ -73,3 +73,4 @@ A button is used a lot in GP. <Button block type="primary" size="md" label="Click me" onClick={() => alert('Button clicked')} /> </div> </div> +variantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantTypevariantType diff --git a/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx index a14eaa6cf..4dcea6e51 100644 --- a/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx +++ b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { DropdownButton, DropdownContainer, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns'; +import { DropdownTrigger, DropdownContainer, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns'; import ColorLegend from '../colorLegend/index.js'; import { DimensionType } from '@graphpolaris/shared/lib/schema/index.js'; import { dataColors } from 'config'; @@ -48,11 +48,6 @@ export const DropdownColorLegend = ({ value, onChange, dimension, distribution } const [menuOpen, setMenuOpen] = useState<boolean>(false); const [selectedOption, setSelectedOption] = useState<any>('Select colormap'); - const handleButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => { - e.preventDefault(); - setMenuOpen(!menuOpen); - }; - const handleOptionClick = (option: string) => { setSelectedOption(option); setSelectedColorLegend(colorStructure[option]); @@ -61,8 +56,8 @@ export const DropdownColorLegend = ({ value, onChange, dimension, distribution } return ( <div className="w-200 h-200"> - <DropdownContainer className="w-full"> - <DropdownButton + <DropdownContainer> + <DropdownTrigger title={ <div className="flex items-center h-4"> {selectedColorLegend ? ( @@ -77,23 +72,20 @@ export const DropdownColorLegend = ({ value, onChange, dimension, distribution } )} </div> } - onClick={handleButtonClick} /> - {menuOpen && ( - <DropdownItemContainer align="top-10" className="w-60"> - {Object.keys(colorStructure).map((option: any, index) => ( - <li key={index} onClick={() => handleOptionClick(option)} className="cursor-pointer flex items-center ml-2 h-4 m-2"> - <ColorLegend - key={index.toString() + '_colorLegend'} - colors={colorStructure[option].colors} - data={colorStructure[option].data} - name={colorStructure[option].name} - showAxis={colorStructure[option].showAxis} - /> - </li> - ))} - </DropdownItemContainer> - )} + <DropdownItemContainer className="w-60"> + {Object.keys(colorStructure).map((option: any, index) => ( + <li key={index} onClick={() => handleOptionClick(option)} className="cursor-pointer flex items-center ml-2 h-4 m-2"> + <ColorLegend + key={index.toString() + '_colorLegend'} + colors={colorStructure[option].colors} + data={colorStructure[option].data} + name={colorStructure[option].name} + showAxis={colorStructure[option].showAxis} + /> + </li> + ))} + </DropdownItemContainer> </DropdownContainer> </div> ); diff --git a/libs/shared/lib/components/dropdowns/index.tsx b/libs/shared/lib/components/dropdowns/index.tsx index dbc2ce4b9..7a56352d3 100644 --- a/libs/shared/lib/components/dropdowns/index.tsx +++ b/libs/shared/lib/components/dropdowns/index.tsx @@ -2,31 +2,20 @@ import React, { useState, useEffect, useRef, ReactNode } from 'react'; import styles from './dropdowns.module.scss'; import Icon from '../icon'; import { ArrowDropDown } from '@mui/icons-material'; +import { PopoverContent, PopoverTrigger, Popover } from '../layout/Popover'; -type DropdownContainerProps = { - children: ReactNode; - className?: string; -}; - -export const DropdownContainer = React.forwardRef<HTMLDivElement, DropdownContainerProps>( - ({ children, className }, ref: React.ForwardedRef<HTMLDivElement>) => { - return ( - <div className={`relative inline-block text-left ${className && className}`} ref={ref}> - {children} - </div> - ); - }, -); +export const DropdownContainer = Popover; -type DropdownButtonProps = { +type DropdownTriggerProps = { title: string | ReactNode; - onClick: (e: React.MouseEvent<HTMLButtonElement>) => void; size?: 'xs' | 'sm' | 'md' | 'xl'; disabled?: boolean; variant?: 'primary' | 'ghost' | 'outline'; + className?: string; + onClick?: () => void; }; -export function DropdownButton({ title, onClick, size, disabled, variant }: DropdownButtonProps) { +export function DropdownTrigger({ title, size, disabled, variant, className, onClick }: DropdownTriggerProps) { const paddingClass = size === 'xs' ? 'py-0' : size === 'sm' ? 'px-1 py-1' : size === 'md' ? 'px-2 py-1' : 'px-4 py-2'; const textSizeClass = size === 'xs' ? 'text-xs' : size === 'sm' ? 'text-sm' : size === 'md' ? 'text-base' : 'text-lg'; @@ -37,38 +26,37 @@ export function DropdownButton({ title, onClick, size, disabled, variant }: Drop ? 'bg-transparent shadow-none' : 'border rounded bg-transparent'; return ( - <> - <button - className={`inline-flex w-full justify-between items-center gap-x-1.5 ${variantClass} ${textSizeClass} ${paddingClass} text-secondary-900 shadow-sm hover:bg-secondary-50 disabled:bg-secondary-100 disabled:cursor-not-allowed disabled:text-secondary-400 pl-1 truncate`} - onClick={onClick} - disabled={disabled} + <PopoverTrigger onClick={onClick}> + <div + className={`inline-flex w-full truncate justify-between items-center gap-x-1.5 ${variantClass} ${textSizeClass} ${paddingClass} text-secondary-900 shadow-sm hover:bg-secondary-50 disabled:bg-secondary-100 disabled:cursor-not-allowed disabled:text-secondary-400 pl-1 truncate${className ? ` ${className}` : ''}`} > <span className={`text-${size}`}>{title}</span> <Icon component={<ArrowDropDown />} size={16} /> - </button> - </> + </div> + </PopoverTrigger> ); } type DropdownItemContainerProps = { - align: string; + // align: string; className?: string; children: ReactNode; }; -export function DropdownItemContainer({ align, className, children }: DropdownItemContainerProps) { +export const DropdownItemContainer = React.forwardRef<HTMLDivElement, DropdownItemContainerProps>(({ children, className }, ref) => { return ( - <div - className={`${styles['dropdown-container']} ${align} ${className} bg-light`} + <PopoverContent + ref={ref} + className={`${styles['dropdown-container']} ${className} bg-light`} role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabIndex={-1} > <ul role="none">{children}</ul> - </div> + </PopoverContent> ); -} +}); type DropdownItemProps = { value: string; @@ -76,9 +64,10 @@ type DropdownItemProps = { className?: string; onClick?: (value: string) => void; submenu?: React.ReactNode; + selected?: boolean; }; -export function DropdownItem({ value, disabled, className, onClick, submenu }: DropdownItemProps) { +export function DropdownItem({ value, disabled, className, onClick, submenu, selected }: DropdownItemProps) { const itemRef = useRef(null); const submenuRef = useRef(null); const [isSubmenuOpen, setIsSubmenuOpen] = useState(false); @@ -86,7 +75,7 @@ export function DropdownItem({ value, disabled, className, onClick, submenu }: D return ( <li ref={itemRef} - className={`${styles['dropdown-item']} ${className && className} hover:bg-primary-100`} + className={`${styles['dropdown-item']} ${className && className} hover:bg-primary-100 ${selected ? 'bg-primary text-white hover:text-black' : ''}`} onClick={() => { !disabled && onClick && onClick(value); }} @@ -110,49 +99,3 @@ export const DropdownSubmenuContainer = React.forwardRef<HTMLDivElement, Dropdow </div> ); }); - -type DropdownProps = { - title: string; - options: Record<string, () => void>; - align: 'left-0' | 'right-0'; - closeOnClick?: boolean; - size?: 'xs' | 'sm' | 'md' | 'xl'; -}; - -export function MenuDropdown({ title, options, align = 'left-0', closeOnClick = true, size = 'sm' }: DropdownProps) { - const dropdownRef = useRef<HTMLDivElement>(null); - const [isDropdownOpen, setIsDropdownOpen] = useState<boolean>(false); - - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { - setIsDropdownOpen(false); - } - }; - if (isDropdownOpen) document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [isDropdownOpen]); - - return ( - <DropdownContainer ref={dropdownRef}> - <DropdownButton title={title} onClick={(e) => setIsDropdownOpen(!isDropdownOpen)} size={size} /> - {isDropdownOpen && ( - <DropdownItemContainer align={align}> - {options && - Object.keys(options).map((key, index) => ( - <DropdownItem - key={index} - value={key} - onClick={() => { - options[key](); - closeOnClick && setIsDropdownOpen(false); - }} - /> - ))} - </DropdownItemContainer> - )} - </DropdownContainer> - ); -} diff --git a/libs/shared/lib/components/dropdowns/menudropdown.stories.tsx b/libs/shared/lib/components/dropdowns/menudropdown.stories.tsx index 5f51fd829..8e7481d90 100644 --- a/libs/shared/lib/components/dropdowns/menudropdown.stories.tsx +++ b/libs/shared/lib/components/dropdowns/menudropdown.stories.tsx @@ -1,15 +1,15 @@ import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { MenuDropdown } from '.'; +// import { MenuDropdown } from '.'; -const Component: Meta<typeof MenuDropdown> = { - title: 'Components/Menu', - component: MenuDropdown, - decorators: [(Story) => <div className="m-5">{Story()}</div>], -}; +// const Component: Meta<typeof MenuDropdown> = { +// title: 'Components/Menu', +// component: MenuDropdown, +// decorators: [(Story) => <div className="m-5">{Story()}</div>], +// }; -export default Component; -type Story = StoryObj<typeof Component>; +// export default Component; +// type Story = StoryObj<typeof Component>; // export const Dropdown: Story = { // args: { diff --git a/libs/shared/lib/components/forms/index.tsx b/libs/shared/lib/components/forms/index.tsx index a4bb74327..366b045d1 100644 --- a/libs/shared/lib/components/forms/index.tsx +++ b/libs/shared/lib/components/forms/index.tsx @@ -2,6 +2,7 @@ import React, { PropsWithChildren } from 'react'; import { Button } from '../buttons'; import { Close } from '@mui/icons-material'; +/** @deprecated */ export const FormDiv = React.forwardRef<HTMLDivElement, PropsWithChildren<{ className?: string; hAnchor?: string; offset?: string }>>( (props, ref) => { const dialogRef = React.useRef<HTMLDivElement | null>(null); @@ -75,13 +76,13 @@ export const FormBody = ({ children, ...props }: PropsWithChildren<React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>>) => ( - <form className="card-body px-0 w-72 py-0" {...props}> + <form className="px-0 w-72 py-2" {...props}> {children} </form> ); export const FormTitle = ({ children, title, onClose }: PropsWithChildren<{ title: string; onClose?: () => void }>) => { return ( - <div className="card-title p-5 py-0 mt-2 flex w-full"> + <div className="font-semibold p-5 py-0 flex w-full"> <h2 className="w-full">{title}</h2> {onClose && <Button rounded variant="ghost" iconComponent={<Close />} onClick={() => onClose()} />} </div> @@ -94,7 +95,7 @@ export const FormActions = (props: { onClose?: () => void; onApply?: () => void {props.onClose && ( <div className="grid grid-cols-2 px-5 gap-2 mb-2"> <Button - type="secondary" + variantType="secondary" variant="outline" label="Cancel" onClick={(e) => { @@ -103,7 +104,7 @@ export const FormActions = (props: { onClose?: () => void; onApply?: () => void }} /> <Button - type="primary" + variantType="primary" label="Apply" onClick={() => { if (props.onApply) props.onApply(); @@ -114,7 +115,7 @@ export const FormActions = (props: { onClose?: () => void; onApply?: () => void )} {!props.onClose && ( <Button - type="primary" + variantType="primary" label="Apply" onClick={() => { if (props.onApply) props.onApply(); diff --git a/libs/shared/lib/components/info/index.tsx b/libs/shared/lib/components/info/index.tsx index d99e13ecb..6bb08d123 100644 --- a/libs/shared/lib/components/info/index.tsx +++ b/libs/shared/lib/components/info/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import Icon from '../icon'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../tooltip'; +import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip'; import { InfoOutlined } from '@mui/icons-material'; type Props = { @@ -10,15 +10,13 @@ type Props = { export default function Info({ tooltip, side = 'left' }: Props) { return ( - <TooltipProvider delayDuration={0}> - <Tooltip> - <TooltipTrigger> - <Icon component={<InfoOutlined />} size={14} /> - </TooltipTrigger> - <TooltipContent side={side}> - <p>{tooltip}</p> - </TooltipContent> - </Tooltip> - </TooltipProvider> + <Tooltip> + <TooltipTrigger> + <Icon component={<InfoOutlined />} size={14} /> + </TooltipTrigger> + <TooltipContent side={side}> + <p>{tooltip}</p> + </TooltipContent> + </Tooltip> ); } diff --git a/libs/shared/lib/components/inputs/index.tsx b/libs/shared/lib/components/inputs/index.tsx index 86d440b59..b2160102c 100644 --- a/libs/shared/lib/components/inputs/index.tsx +++ b/libs/shared/lib/components/inputs/index.tsx @@ -1,7 +1,9 @@ import React from 'react'; import styles from './inputs.module.scss'; -import { DropdownButton, DropdownContainer, DropdownItem, DropdownItemContainer } from '../dropdowns'; +import { DropdownTrigger, DropdownContainer, DropdownItem, DropdownItemContainer } from '../dropdowns'; import Info from '../info'; +import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip'; +import { Popover } from '../layout/Popover'; type SliderProps = { label: string; @@ -123,31 +125,36 @@ export const Input = (props: InputProps) => { export const SliderInput = ({ label, value, min, max, step, unit, showValue = true, onChange, tooltip }: SliderProps) => { return ( - <div data-tip={tooltip || null} className={styles['slider'] + (tooltip ? ' tooltip' : '')}> - <label className="label flex flex-row justify-between items-end"> - <span className="label-text">{label}</span> - {showValue ? ( - <div className="label-text"> - {value} - {unit} - </div> - ) : null} - </label> + <Tooltip> + <TooltipTrigger> + <div className={styles['slider']}> + <label className="label flex flex-row justify-between items-end"> + <span className="label-text">{label}</span> + {showValue ? ( + <div className="label-text"> + {value} + {unit} + </div> + ) : null} + </label> - <input - type="range" - min={min} - max={max} - step={step} - value={value} - onChange={(e) => { - if (onChange) { - onChange(parseFloat(e.target.value)); - } - }} - aria-labelledby="input-slider" - /> - </div> + <input + type="range" + min={min} + max={max} + step={step} + value={value} + onChange={(e) => { + if (onChange) { + onChange(parseFloat(e.target.value)); + } + }} + aria-labelledby="input-slider" + /> + </div> + </TooltipTrigger> + {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + </Tooltip> ); }; @@ -171,44 +178,42 @@ export const TextInput = ({ if (!tooltip && inline) tooltip = label; return ( - <div - data-tip={tooltip || null} - className={ - styles['input'] + ' form-control w-full' + (inline ? ' grid grid-cols-2 items-center gap-0.5' : '') + (tooltip ? ' tooltip' : '') - } - > - {label && ( - <label className="label p-0"> - <span - className={`text-sm font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`} - > - {label} - </span> - {required && isValid ? null : <span className="label-text-alt text-error">{errorText}</span>} - {info && <Info tooltip={info} side={'left'} />} - </label> - )} - <input - type={visible ? 'text' : 'password'} - placeholder={placeholder} - className={ - `${size} bg-light border border-secondary-300 placeholder-secondary-400 focus:outline-none block w-full focus:ring-1 ${ - isValid ? '' : 'input-error' - }` + (className ? ` ${className}` : '') - } - value={value.toString()} - onChange={(e) => { - if (required && validate) { - setIsValid(validate(e.target.value)); - } - if (onChange) { - onChange(e.target.value); + <Tooltip> + <TooltipTrigger className={styles['input'] + ' form-control w-full' + (inline ? ' grid grid-cols-2 items-center gap-0.5' : '')}> + {label && ( + <label className="label p-0"> + <span + className={`text-sm font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`} + > + {label} + </span> + {required && isValid ? null : <span className="label-text-alt text-error">{errorText}</span>} + {info && <Info tooltip={info} side={'left'} />} + </label> + )} + <input + type={visible ? 'text' : 'password'} + placeholder={placeholder} + className={ + `${size} bg-light border border-secondary-300 placeholder-secondary-400 focus:outline-none block w-full focus:ring-1 ${ + isValid ? '' : 'input-error' + }` + (className ? ` ${className}` : '') } - }} - required={required} - disabled={disabled} - /> - </div> + value={value.toString()} + onChange={(e) => { + if (required && validate) { + setIsValid(validate(e.target.value)); + } + if (onChange) { + onChange(e.target.value); + } + }} + required={required} + disabled={disabled} + /> + </TooltipTrigger> + {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + </Tooltip> ); }; @@ -234,128 +239,135 @@ export const NumberInput = ({ if (!tooltip && inline) tooltip = label; return ( - <div - data-tip={tooltip || null} - className={ - styles['input'] + ' form-control w-full' + (inline ? ' grid grid-cols-2 items-center gap-0.5' : '') + (tooltip ? ' tooltip' : '') - } - > - <label className="label p-0"> - <span - className={`text-sm text-left truncate font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`} - > - {label} - </span> - {required && isValid ? null : <span className="label-text-alt text-error">{errorText}</span>} - {info && <Info tooltip={info} side={'left'} />} - </label> - <input - type="number" - placeholder={placeholder} - className={`${size} bg-light border border-secondary-300 placeholder-secondary-400 focus:outline-none block w-full sm:text-sm focus:ring-1 ${ - isValid ? '' : 'input-error' - }`} - value={value.toString()} - onChange={(e) => { - if (required && validate) { - setIsValid(validate(e.target.value)); - } - if (onChange) { - onChange(Number(e.target.value)); - } - }} - required={required} - disabled={disabled} - onKeyDown={onKeyDown} - max={max} - min={min} - /> - </div> + <Tooltip> + <TooltipTrigger className={styles['input'] + ' form-control w-full' + (inline ? ' grid grid-cols-2 items-center gap-0.5' : '')}> + <label className="label p-0"> + <span + className={`text-sm text-left truncate font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`} + > + {label} + </span> + {required && isValid ? null : <span className="label-text-alt text-error">{errorText}</span>} + {info && <Info tooltip={info} side={'left'} />} + </label> + <input + type="number" + placeholder={placeholder} + className={`${size} bg-light border border-secondary-300 placeholder-secondary-400 focus:outline-none block w-full sm:text-sm focus:ring-1 ${ + isValid ? '' : 'input-error' + }`} + value={value.toString()} + onChange={(e) => { + if (required && validate) { + setIsValid(validate(e.target.value)); + } + if (onChange) { + onChange(Number(e.target.value)); + } + }} + required={required} + disabled={disabled} + onKeyDown={onKeyDown} + max={max} + min={min} + /> + </TooltipTrigger> + {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + </Tooltip> ); }; export const RadioInput = ({ label, value, options, onChange, tooltip }: RadioProps) => { return ( - <div data-tip={tooltip || null} className={tooltip ? 'tooltip' : ''}> - <label className="label p-0"> - <span className="label-text">{label}</span> - </label> - {options.map((option, index) => ( - <label key={index} className="label cursor-pointer w-fit gap-2 px-0 py-1"> - <input - type="radio" - name={option} - checked={value === option} - onChange={() => { - if (onChange) { - onChange(option); - } - }} - className="radio radio-xs radio-primary" - /> - <span className="label-text">{option}</span> + <Tooltip> + <TooltipTrigger> + <label className="label p-0"> + <span className="label-text">{label}</span> </label> - ))} - </div> + {options.map((option, index) => ( + <label key={index} className="label cursor-pointer w-fit gap-2 px-0 py-1"> + <input + type="radio" + name={option} + checked={value === option} + onChange={() => { + if (onChange) { + onChange(option); + } + }} + className="radio radio-xs radio-primary" + /> + <span className="label-text">{option}</span> + </label> + ))} + </TooltipTrigger> + {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + </Tooltip> ); }; export const CheckboxInput = ({ label, value, options, onChange, tooltip }: CheckboxProps) => { return ( - <div data-tip={tooltip || null} className={tooltip ? 'tooltip' : ''}> - {label && ( - <label className="label p-0"> - <span className="label-text">{label}</span> - </label> - )} - {options.map((option, index) => ( - <label key={index} className="label cursor-pointer w-fit gap-2 px-0 py-1"> + <Tooltip> + <TooltipTrigger> + {label && ( + <label className="label p-0"> + <span className="label-text">{label}</span> + </label> + )} + {options.map((option, index) => ( + <label key={index} className="label cursor-pointer w-fit gap-2 px-0 py-1"> + <input + type="checkbox" + name={option} + checked={Array.isArray(value) && value.includes(option)} + onClick={(event) => { + if (event.ctrlKey) { + const updatedValue = (event.target as any).checked ? [option] : value.filter((val) => val !== option); + if (onChange) { + onChange(updatedValue); + } + } else { + const updatedValue = (event.target as any).checked ? [...value, option] : value.filter((val) => val !== option); + if (onChange) { + onChange(updatedValue); + } + } + }} + onChange={() => { + // to remove warning + }} + className="checkbox checkbox-xs" + /> + <span className="label-text">{option}</span> + </label> + ))} + </TooltipTrigger> + {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + </Tooltip> + ); +}; + +export const BooleanInput = ({ label, value, onChange, tooltip }: BooleanProps) => { + return ( + <Tooltip> + <TooltipTrigger> + <label className={`label cursor-pointer w-fit gap-2 px-0 py-1`}> + <span className="text-sm">{label}</span> <input type="checkbox" - name={option} - checked={Array.isArray(value) && value.includes(option)} - onClick={(event) => { - if (event.ctrlKey) { - const updatedValue = (event.target as any).checked ? [option] : value.filter((val) => val !== option); - if (onChange) { - onChange(updatedValue); - } - } else { - const updatedValue = (event.target as any).checked ? [...value, option] : value.filter((val) => val !== option); - if (onChange) { - onChange(updatedValue); - } + checked={value} + onChange={(event) => { + if (onChange) { + onChange(event.target.checked); } }} - onChange={() => { - // to remove warning - }} className="checkbox checkbox-xs" /> - <span className="label-text">{option}</span> </label> - ))} - </div> - ); -}; - -export const BooleanInput = ({ label, value, onChange, tooltip }: BooleanProps) => { - return ( - <div data-tip={tooltip || null} className={tooltip ? 'tooltip' : ''}> - <label className={`label cursor-pointer w-fit gap-2 px-0 py-1`}> - <span className="text-sm">{label}</span> - <input - type="checkbox" - checked={value} - onChange={(event) => { - if (onChange) { - onChange(event.target.checked); - } - }} - className="checkbox checkbox-xs" - /> - </label> - </div> + </TooltipTrigger> + {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + </Tooltip> ); }; @@ -368,74 +380,67 @@ export const DropDownInput = ({ required = false, tooltip, size = 'sm', - inline = false, + inline = true, disabled = false, buttonVariant = 'primary', className = '', info, }: DropdownProps) => { - const dropdownRef = React.useRef<HTMLDivElement>(null); const [isDropdownOpen, setIsDropdownOpen] = React.useState<boolean>(false); if (!tooltip && inline) tooltip = label; - React.useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { - setIsDropdownOpen(false); - } - }; - if (isDropdownOpen) document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [isDropdownOpen]); return ( - <div - data-tip={tooltip || null} - className={className + ' w-full' + (inline ? ' grid grid-cols-2 items-center gap-0.5' : '') + (tooltip ? ' tooltip' : '')} - > - {label && ( - <label className="label p-0"> - <span - className={`text-sm text-left truncate font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`} - > - {label} - </span> - {info && <Info tooltip={info} />} - </label> - )} - <DropdownContainer className="w-full right-0 left-auto" ref={dropdownRef}> - <DropdownButton - variant={buttonVariant} - title={overrideRender || value} - size={size} - disabled={disabled} - onClick={(e) => { - e.stopPropagation(); - e.preventDefault(); - setIsDropdownOpen(!isDropdownOpen); - }} - /> - {isDropdownOpen && ( - <DropdownItemContainer align="left-0"> - {options && - options.map((item: any, index: number) => ( - <DropdownItem - key={index} - value={item.toString()} - onClick={() => { - const parsedValue = typeof item === 'number' ? item.toString() : item; - if (onChange) { - onChange(parsedValue); - } - setIsDropdownOpen(false); - }} - /> - ))} - </DropdownItemContainer> + <Tooltip> + <TooltipTrigger className={className + ' w-full' + (inline ? ' grid grid-cols-2 items-center gap-0.5' : '')}> + {label && ( + <label className="label p-0"> + <span + className={`text-sm text-left truncate font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`} + > + {label} + </span> + {info && <Info tooltip={info} />} + </label> )} - </DropdownContainer> - </div> + <DropdownContainer + open={isDropdownOpen} + onOpenChange={(ref) => { + setIsDropdownOpen(ref); + }} + > + <DropdownTrigger + variant={buttonVariant} + title={overrideRender || value} + size={size} + className="" + disabled={disabled} + onClick={() => { + setIsDropdownOpen(!isDropdownOpen); + }} + /> + {isDropdownOpen && ( + <DropdownItemContainer className="w-fit"> + {options && + options.map((item: any, index: number) => ( + <DropdownItem + key={index} + value={item.toString()} + selected={value === item} + onClick={() => { + const parsedValue = typeof item === 'number' ? item.toString() : item; + if (onChange) { + onChange(parsedValue); + } + setIsDropdownOpen(false); + }} + /> + ))} + </DropdownItemContainer> + )} + </DropdownContainer> + </TooltipTrigger> + {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + </Tooltip> ); }; diff --git a/libs/shared/lib/components/layout/Dialog.tsx b/libs/shared/lib/components/layout/Dialog.tsx index 75781e135..5166cc136 100644 --- a/libs/shared/lib/components/layout/Dialog.tsx +++ b/libs/shared/lib/components/layout/Dialog.tsx @@ -1,25 +1,210 @@ +import * as React from 'react'; + import { useEffect, useRef } from 'react'; +import { + useFloating, + useClick, + useDismiss, + useRole, + useInteractions, + useMergeRefs, + FloatingPortal, + FloatingFocusManager, + FloatingOverlay, + useId, +} from '@floating-ui/react'; +import { Button } from '../buttons'; + +interface DialogOptions { + initialOpen?: boolean; + open?: boolean; + onOpenChange?: (open: boolean) => void; +} -export type DialogProps = { - onClose: () => void; +export function useDialog({ initialOpen = false, open: controlledOpen, onOpenChange: setControlledOpen }: DialogOptions = {}): { open: boolean; - children?: React.ReactNode; - className?: string; + setOpen: (open: boolean) => void; + interactions: ReturnType<typeof useInteractions>; + data: ReturnType<typeof useFloating>; + floatingContext: ReturnType<typeof useFloating>['context']; + labelId: string | undefined; + descriptionId: string | undefined; + setLabelId: React.Dispatch<React.SetStateAction<string | undefined>>; + setDescriptionId: React.Dispatch<React.SetStateAction<string | undefined>>; +} { + const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen); + const [labelId, setLabelId] = React.useState<string | undefined>(); + const [descriptionId, setDescriptionId] = React.useState<string | undefined>(); + + const open = controlledOpen ?? uncontrolledOpen; + const setOpen = setControlledOpen ?? setUncontrolledOpen; + + const data = useFloating({ + open, + onOpenChange: setOpen, + }); + + const context = data.context; + + const click = useClick(context, { + enabled: controlledOpen == null, + }); + const dismiss = useDismiss(context, { outsidePressEvent: 'mousedown' }); + const role = useRole(context); + + const interactions = useInteractions([click, dismiss, role]); + + return React.useMemo( + () => ({ + open, + setOpen, + interactions: interactions, + data: data, + floatingContext: context, + labelId, + descriptionId, + setLabelId, + setDescriptionId, + }), + [open, setOpen, interactions, data, labelId, descriptionId], + ); +} + +type ContextType = ReturnType<typeof useDialog> & { + setLabelId: React.Dispatch<React.SetStateAction<string | undefined>>; + setDescriptionId: React.Dispatch<React.SetStateAction<string | undefined>>; +}; + +const DialogContext = React.createContext<ContextType | null>(null); + +export const useDialogContext = (): ContextType => { + const context = React.useContext(DialogContext); + + if (context == null) { + throw new Error('Dialog components must be wrapped in <Dialog />'); + } + + return context; }; -export const Dialog = (props: DialogProps) => { - const ref = useRef<HTMLDialogElement>(null); +export function Dialog({ + children, + ...options +}: { + children: React.ReactNode; +} & DialogOptions) { + const dialog = useDialog(options); + return <DialogContext.Provider value={dialog}>{children}</DialogContext.Provider>; +} + +interface DialogTriggerProps { + children: React.ReactNode; + asChild?: boolean; +} + +export const DialogTrigger = React.forwardRef<HTMLElement, React.HTMLProps<HTMLElement> & DialogTriggerProps>(function DialogTrigger( + { children, asChild = false, ...props }, + propRef, +) { + const context = useDialogContext(); + const childrenRef = (children as any).ref; + const ref = useMergeRefs([context.data.refs.setReference, propRef, childrenRef]); - useEffect(() => { - if (props.open) ref.current?.showModal(); - else ref.current?.close(); - }, [props.open]); + // `asChild` allows the user to pass any element as the anchor + if (asChild && React.isValidElement(children)) { + return React.cloneElement( + children, + context.interactions.getReferenceProps({ + ref, + ...props, + ...children.props, + 'data-state': context.open ? 'open' : 'closed', + }), + ); + } return ( - <dialog className={'fixed inset-0 z-10 overflow-y-auto rounded p-4 bg-light border'} ref={ref} onClose={() => props.onClose()}> - <form method="dialog" className={'flex flex-col gap-4 ' + (props?.className ? props?.className : '')}> - {props.children} - </form> - </dialog> + <button + ref={ref} + // The user can style the trigger based on the state + data-state={context.open ? 'open' : 'closed'} + {...context.interactions.getReferenceProps(props)} + > + {children} + </button> ); -}; +}); + +export const DialogContent = React.forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(function DialogContent(props, propRef) { + const context = useDialogContext(); + const ref = useMergeRefs([context.data.refs.setFloating, propRef]); + + if (!context.open) return null; + + return ( + <FloatingPortal> + <FloatingOverlay className="grid place-items-center" lockScroll style={{ backgroundColor: 'rgba(0, 0, 0, 0.4)' }}> + <FloatingFocusManager context={context.floatingContext}> + <div + ref={ref} + aria-labelledby={context.labelId} + aria-describedby={context.descriptionId} + {...context.interactions.getFloatingProps(props)} + className={`overflow-y-auto rounded p-4 bg-light border border-gray-300${props.className ? ` ${props.className}` : ''}`} + > + {props.children} + </div> + </FloatingFocusManager> + </FloatingOverlay> + </FloatingPortal> + ); +}); + +export const DialogHeading = React.forwardRef<HTMLHeadingElement, React.HTMLProps<HTMLHeadingElement>>(function DialogHeading( + { children, ...props }, + ref, +) { + const { setLabelId } = useDialogContext(); + const id = useId(); + + // Only sets `aria-labelledby` on the Dialog root element + // if this component is mounted inside it. + React.useLayoutEffect(() => { + setLabelId(id); + return () => setLabelId(undefined); + }, [id, setLabelId]); + + return ( + <h2 {...props} ref={ref} id={id}> + {children} + </h2> + ); +}); + +export const DialogDescription = React.forwardRef<HTMLParagraphElement, React.HTMLProps<HTMLParagraphElement>>(function DialogDescription( + { children, ...props }, + ref, +) { + const { setDescriptionId } = useDialogContext(); + const id = useId(); + + // Only sets `aria-describedby` on the Dialog root element + // if this component is mounted inside it. + React.useLayoutEffect(() => { + setDescriptionId(id); + return () => setDescriptionId(undefined); + }, [id, setDescriptionId]); + + return ( + <p {...props} ref={ref} id={id}> + {children} + </p> + ); +}); + +export const DialogClose = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>( + function DialogClose(props, ref) { + const { setOpen } = useDialogContext(); + return <Button variant="solid" className="w-full" {...props} ref={ref} onClick={() => setOpen(false)} />; + }, +); diff --git a/libs/shared/lib/components/layout/Popover.tsx b/libs/shared/lib/components/layout/Popover.tsx new file mode 100644 index 000000000..4a4cf7a8a --- /dev/null +++ b/libs/shared/lib/components/layout/Popover.tsx @@ -0,0 +1,244 @@ +import * as React from 'react'; +import { + useFloating, + autoUpdate, + offset, + flip, + shift, + useClick, + useDismiss, + useRole, + useInteractions, + useMergeRefs, + Placement, + FloatingPortal, + FloatingFocusManager, + useId, +} from '@floating-ui/react'; + +interface PopoverOptions { + initialOpen?: boolean; + placement?: Placement; + modal?: boolean; + open?: boolean; + onOpenChange?: (open: boolean) => void; +} + +export function usePopover({ + initialOpen = false, + placement = 'bottom', + modal, + open: controlledOpen, + onOpenChange: setControlledOpen, +}: PopoverOptions = {}): { + open: boolean; + setOpen: (open: boolean) => void; + interactions: ReturnType<typeof useInteractions>; + data: ReturnType<typeof useFloating>; + floatingContext: ReturnType<typeof useFloating>['context']; + labelId: string | undefined; + descriptionId: string | undefined; + setLabelId: React.Dispatch<React.SetStateAction<string | undefined>>; + setDescriptionId: React.Dispatch<React.SetStateAction<string | undefined>>; + modal: boolean; +} { + const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen); + const [labelId, setLabelId] = React.useState<string | undefined>(); + const [descriptionId, setDescriptionId] = React.useState<string | undefined>(); + + const open = controlledOpen ?? uncontrolledOpen; + const setOpen = setControlledOpen ?? setUncontrolledOpen; + + const data = useFloating({ + placement, + open, + onOpenChange: setOpen, + whileElementsMounted: autoUpdate, + middleware: [ + offset(5), + flip({ + crossAxis: placement.includes('-'), + fallbackAxisSideDirection: 'end', + padding: 5, + }), + shift({ padding: 5 }), + ], + }); + + const context = data.context; + + const click = useClick(context, { + enabled: controlledOpen == null, + }); + const dismiss = useDismiss(context); + const role = useRole(context); + + const interactions = useInteractions([click, dismiss, role]); + + return React.useMemo( + () => ({ + open, + setOpen, + interactions: interactions, + data: data, + floatingContext: context, + modal: modal || false, + labelId, + descriptionId, + setLabelId, + setDescriptionId, + }), + [open, setOpen, interactions, data, modal, labelId, descriptionId], + ); +} + +type ContextType = + | (ReturnType<typeof usePopover> & { + setLabelId: React.Dispatch<React.SetStateAction<string | undefined>>; + setDescriptionId: React.Dispatch<React.SetStateAction<string | undefined>>; + }) + | null; + +const PopoverContext = React.createContext<ContextType>(null); + +export const usePopoverContext = () => { + const context = React.useContext(PopoverContext); + + if (context == null) { + throw new Error('Popover components must be wrapped in <Popover />'); + } + + return context; +}; + +export function Popover({ + children, + modal = false, + ...restOptions +}: { + children: React.ReactNode; +} & PopoverOptions) { + // This can accept any props as options, e.g. `placement`, + // or other positioning options. + const popover = usePopover({ modal, ...restOptions }); + return <PopoverContext.Provider value={popover}>{children}</PopoverContext.Provider>; +} + +interface PopoverTriggerProps { + children: React.ReactNode; + asChild?: boolean; +} + +export const PopoverTrigger = React.forwardRef<HTMLElement, React.HTMLProps<HTMLElement> & PopoverTriggerProps>(function PopoverTrigger( + { children, asChild = false, ...props }, + propRef, +) { + const context = usePopoverContext(); + const childrenRef = (children as any).ref; + const ref = useMergeRefs([context.data.refs.setReference, propRef, childrenRef]); + + // `asChild` allows the user to pass any element as the anchor + if (asChild && React.isValidElement(children)) { + return React.cloneElement( + children, + context.interactions.getReferenceProps({ + ref, + ...props, + ...children.props, + 'data-state': context.open ? 'open' : 'closed', + }), + ); + } + + return ( + <button + ref={ref} + type="button" + // The user can style the trigger based on the state + data-state={context.open ? 'open' : 'closed'} + {...context.interactions.getReferenceProps(props)} + > + {children} + </button> + ); +}); + +export const PopoverContent = React.forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(function PopoverContent( + { style, className, ...props }, + propRef, +) { + const context = usePopoverContext(); + const ref = useMergeRefs([context.data.refs.setFloating, propRef]); + + if (!context.floatingContext.open) return null; + + return ( + <FloatingPortal> + <FloatingFocusManager context={context.floatingContext} modal={context.modal}> + <div + ref={ref} + style={{ ...context.data.floatingStyles, ...style }} + aria-labelledby={context.labelId} + aria-describedby={context.descriptionId} + {...context.interactions.getFloatingProps(props)} + className={`w-fit ${className || ''}`} + > + {props.children} + </div> + </FloatingFocusManager> + </FloatingPortal> + ); +}); + +export const PopoverHeading = React.forwardRef<HTMLHeadingElement, React.HTMLProps<HTMLHeadingElement>>( + function PopoverHeading(props, ref) { + const { setLabelId } = usePopoverContext(); + const id = useId(); + + // Only sets `aria-labelledby` on the Popover root element + // if this component is mounted inside it. + React.useLayoutEffect(() => { + setLabelId(id); + return () => setLabelId(undefined); + }, [id, setLabelId]); + + return ( + <h2 {...props} ref={ref} id={id}> + {props.children} + </h2> + ); + }, +); + +export const PopoverDescription = React.forwardRef<HTMLParagraphElement, React.HTMLProps<HTMLParagraphElement>>( + function PopoverDescription(props, ref) { + const { setDescriptionId } = usePopoverContext(); + const id = useId(); + + // Only sets `aria-describedby` on the Popover root element + // if this component is mounted inside it. + React.useLayoutEffect(() => { + setDescriptionId(id); + return () => setDescriptionId(undefined); + }, [id, setDescriptionId]); + + return <p {...props} ref={ref} id={id} />; + }, +); + +export const PopoverClose = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>( + function PopoverClose(props, ref) { + const { setOpen } = usePopoverContext(); + return ( + <button + type="button" + ref={ref} + {...props} + onClick={(event) => { + props.onClick?.(event); + setOpen(false); + }} + /> + ); + }, +); diff --git a/libs/shared/lib/components/pills/Pill.tsx b/libs/shared/lib/components/pills/Pill.tsx index aea075576..2cf653552 100644 --- a/libs/shared/lib/components/pills/Pill.tsx +++ b/libs/shared/lib/components/pills/Pill.tsx @@ -17,6 +17,7 @@ export type PillI = { handleLeft?: React.ReactNode; handleRight?: React.ReactNode; className?: string; + draggable?: boolean; }; export const Pill = React.memo((props: PillI) => { @@ -59,23 +60,27 @@ export const Pill = React.memo((props: PillI) => { const onDragStart = (event: React.DragEvent<HTMLDivElement>) => { const pill = event.target as HTMLDivElement; const dragImage = pill.cloneNode(true) as HTMLDivElement; - dragImage.style.transform = 'translate(0, 0)'; // Removes the background from drag image in Chrome + dragImage.style.transform = 'translate(0, 0)'; // Removes the background from drag image in Chrome document.body.appendChild(dragImage); const mouse_x = event.clientX - pill.getBoundingClientRect().left; const mouse_y = event.clientY - pill.getBoundingClientRect().top; event.dataTransfer.setDragImage(dragImage, mouse_x, mouse_y); - event.dataTransfer.setData('mouse_x', String(mouse_x)) - event.dataTransfer.setData('mouse_y', String(mouse_y)) - + event.dataTransfer.setData('mouse_x', String(mouse_x)); + event.dataTransfer.setData('mouse_y', String(mouse_y)); + setTimeout(() => { dragImage.remove(); }, 1); - } + }; return ( - <div draggable="true" onDragStart={onDragStart} className={(props.className ? props.className + ' ' : '') + 'flex flex-row flex-grow-0 text-xs text-justify'}> + <div + draggable={props.draggable || false} + onDragStart={!!props.draggable ? onDragStart : undefined} + className={(props.className ? props.className + ' ' : '') + 'flex flex-row flex-grow-0 text-xs text-justify'} + > <div className={'bg-secondary-200 ' + (corner !== 'square' ? 'rounded' : '')} style={{ diff --git a/libs/shared/lib/components/tabs/Tab.tsx b/libs/shared/lib/components/tabs/Tab.tsx index 855da1029..9b044371c 100644 --- a/libs/shared/lib/components/tabs/Tab.tsx +++ b/libs/shared/lib/components/tabs/Tab.tsx @@ -8,21 +8,21 @@ export const Tabs = (props: { children: React.ReactNode }) => { ); }; -export const Tab = ( - props: React.ButtonHTMLAttributes<HTMLDivElement> & { - active: boolean; - children: React.ReactNode; - text: string; - key?: string; - }, -) => { +export const Tab = ({ + activeTab, + text, + ...props +}: React.ButtonHTMLAttributes<HTMLDivElement> & { + activeTab: boolean; + children: React.ReactNode; + text: string; +}) => { return ( <div - key={props.key} - className={`flex items-center pl-2 pr-1 gap-1 cursor-pointer relative border-secondary-200 before:content-[''] before:absolute before:left-0 before:bottom-0 before:h-[2px] before:w-full ${props.active ? 'before:bg-primary-500' : 'before:bg-transparent hover:before:bg-secondary-300 hover:bg-secondary-200'}`} + className={`flex items-center pl-2 pr-1 gap-1 cursor-pointer relative border-secondary-200 before:content-[''] before:absolute before:left-0 before:bottom-0 before:h-[2px] before:w-full ${activeTab ? 'before:bg-primary-500' : 'before:bg-transparent hover:before:bg-secondary-300 hover:bg-secondary-200'}`} {...props} > - <p className={`text-xs text-secondary-500 font-semibold ${props.active && 'text-secondary-950'}`}>{props.text}</p> + <p className={`text-xs text-secondary-500 font-semibold ${activeTab && 'text-secondary-950'}`}>{text}</p> {props.children} </div> ); diff --git a/libs/shared/lib/components/tooltip/Tooltip.tsx b/libs/shared/lib/components/tooltip/Tooltip.tsx new file mode 100644 index 000000000..19ca660cf --- /dev/null +++ b/libs/shared/lib/components/tooltip/Tooltip.tsx @@ -0,0 +1,165 @@ +import * as React from 'react'; +import { + useFloating, + autoUpdate, + offset, + flip, + shift, + useHover, + useFocus, + useDismiss, + useRole, + useInteractions, + useMergeRefs, + FloatingPortal, +} from '@floating-ui/react'; +import type { Placement } from '@floating-ui/react'; +import { FloatingDelayGroup } from '@floating-ui/react'; + +interface TooltipOptions { + initialOpen?: boolean; + placement?: Placement; + open?: boolean; + onOpenChange?: (open: boolean) => void; +} + +export function useTooltip({ + initialOpen = false, + placement = 'top', + open: controlledOpen, + onOpenChange: setControlledOpen, +}: TooltipOptions = {}): { + open: boolean; + setOpen: (open: boolean) => void; + interactions: ReturnType<typeof useInteractions>; + data: ReturnType<typeof useFloating>; +} { + const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen); + + const open = controlledOpen ?? uncontrolledOpen; + const setOpen = setControlledOpen ?? setUncontrolledOpen; + + const data = useFloating({ + placement, + open, + onOpenChange: setOpen, + whileElementsMounted: autoUpdate, + middleware: [ + offset(5), + flip({ + crossAxis: placement.includes('-'), + fallbackAxisSideDirection: 'start', + padding: 5, + }), + shift({ padding: 5 }), + ], + }); + + const context = data.context; + + const hover = useHover(context, { + move: false, + enabled: controlledOpen == null, + }); + const focus = useFocus(context, { + enabled: controlledOpen == null, + }); + const dismiss = useDismiss(context); + const role = useRole(context, { role: 'tooltip' }); + + const interactions = useInteractions([hover, focus, dismiss, role]); + + return React.useMemo( + () => ({ + open, + setOpen, + interactions: interactions, + data: data, + }), + [open, setOpen, interactions, data], + ); +} + +type ContextType = ReturnType<typeof useTooltip> | null; + +const TooltipContext = React.createContext<ContextType>(null); + +export const useTooltipContext = () => { + const context = React.useContext(TooltipContext); + + if (context == null) { + throw new Error('Tooltip components must be wrapped in <Tooltip />'); + } + + return context; +}; + +export function Tooltip({ children, ...options }: { children: React.ReactNode } & TooltipOptions) { + // This can accept any props as options, e.g. `placement`, + // or other positioning options. + const tooltip = useTooltip(options); + return <TooltipContext.Provider value={tooltip}>{children}</TooltipContext.Provider>; +} + +export const TooltipTrigger = React.forwardRef<HTMLElement, React.HTMLProps<HTMLElement> & { asChild?: boolean }>(function TooltipTrigger( + { children, asChild = false, ...props }, + propRef, +) { + const context = useTooltipContext(); + const childrenRef = (children as any).ref; + const ref = useMergeRefs([context.data.refs.setReference, propRef, childrenRef]); + + // `asChild` allows the user to pass any element as the anchor + if (asChild && React.isValidElement(children)) { + return React.cloneElement( + children, + context.interactions.getReferenceProps({ + ref, + ...props, + ...children.props, + 'data-state': context.open ? 'open' : 'closed', + }), + ); + } + + return ( + <div + ref={ref} + // The user can style the trigger based on the state + data-state={context.open ? 'open' : 'closed'} + {...context.interactions.getReferenceProps(props)} + > + {children} + </div> + ); +}); + +export const TooltipContent = React.forwardRef< + HTMLDivElement, + React.HTMLProps<HTMLDivElement> & { + side?: string; + } +>(function TooltipContent({ style, className, ...props }, propRef) { + const context = useTooltipContext(); + const ref = useMergeRefs([context.data.refs.setFloating, propRef]); + + if (!context.open) return null; + + return ( + <FloatingPortal> + <div + ref={ref} + className={`z-50 max-w-64 overflow-hidden rounded bg-light px-2 py-1 shadow text-xs border border-secondary-200 text-dark animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2${className ? ` ${className}` : ''}`} + style={{ + ...context.data.floatingStyles, + ...style, + }} + {...context.interactions.getFloatingProps(props)} + /> + </FloatingPortal> + ); +}); + +export const TooltipProvider = (props: { delayDuration: number; children: React.ReactNode }) => { + return <FloatingDelayGroup delay={props.delayDuration}>{props.children}</FloatingDelayGroup>; +}; diff --git a/libs/shared/lib/components/tooltip/index.tsx b/libs/shared/lib/components/tooltip/index.tsx index 6c5a62d35..4024bdf57 100644 --- a/libs/shared/lib/components/tooltip/index.tsx +++ b/libs/shared/lib/components/tooltip/index.tsx @@ -1,7 +1,6 @@ // https://www.radix-ui.com/primitives/docs/components/tooltip -import React, { ReactNode } from 'react'; -import * as TooltipPrimitive from '@radix-ui/react-tooltip'; +export * from './Tooltip'; export interface BarTooltipProps { x: number; @@ -19,42 +18,3 @@ export const BarPlotTooltip = ({ x, y, children }: BarTooltipProps) => { </div> ); }; - -const TooltipProvider = TooltipPrimitive.Provider; -const TooltipTrigger = TooltipPrimitive.Trigger; - -export type TooltipProps = { - children: ReactNode; - disabled?: boolean; -}; - -const Tooltip: React.FC<TooltipProps> = ({ disabled, children }) => { - if (disabled) { - return null; - } - - return <TooltipPrimitive.Root>{children}</TooltipPrimitive.Root>; -}; - -const TooltipContent = React.forwardRef< - React.ElementRef<typeof TooltipPrimitive.Content>, - React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> & TooltipProps ->(({ className, sideOffset = 4, disabled = false, ...props }, ref) => { - if (disabled) { - return null; - } - - return ( - <TooltipPrimitive.Content - ref={ref} - sideOffset={sideOffset} - className={ - 'z-50 overflow-hidden rounded bg-light px-2 py-1 shadow text-xs border border-secondary-200 text-dark animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2' - } - {...props} - /> - ); -}); -TooltipContent.displayName = TooltipPrimitive.Content.displayName; - -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/libs/shared/lib/components/tooltip/tooltip.stories.tsx b/libs/shared/lib/components/tooltip/tooltip.stories.tsx index 1335dde4a..523a9a135 100644 --- a/libs/shared/lib/components/tooltip/tooltip.stories.tsx +++ b/libs/shared/lib/components/tooltip/tooltip.stories.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Meta } from '@storybook/react'; import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './index'; -import { delay } from 'lodash-es'; export default { title: 'Components/Tooltip', @@ -40,7 +39,8 @@ TooltipStory.args = { side: 'bottom', }; -export const TooltipPosition = TooltipStory.bind({}); -TooltipPosition.args = { - side: 'bottom', +export const TooltipPosition = { + args: { + side: 'bottom', + }, }; diff --git a/libs/shared/lib/inspector/InspectorPanel.tsx b/libs/shared/lib/inspector/InspectorPanel.tsx index 703af6cee..48c07f40b 100644 --- a/libs/shared/lib/inspector/InspectorPanel.tsx +++ b/libs/shared/lib/inspector/InspectorPanel.tsx @@ -56,7 +56,7 @@ export function InspectorPanel(props: { children?: React.ReactNode }) { {buildInfo === 'dev' && ( <div className="mt-auto p-2 bg-light"> <Button - type="primary" + variantType="primary" variant="outline" size="xs" label="Report an issue" diff --git a/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx b/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx index 858066c24..3f3ac4618 100644 --- a/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx +++ b/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx @@ -24,7 +24,7 @@ import ReactFlow, { isNode, useReactFlow, } from 'reactflow'; -import { Dialog } from '../../components/layout/Dialog'; +import { Dialog, DialogClose, DialogContent } from '../../components/layout/Dialog'; import { Button } from '../../components/buttons'; import { ControlContainer } from '../../components/controls'; import { addError } from '../../data-access/store/configSlice'; @@ -43,6 +43,7 @@ import { CameraAlt, Cached, Difference, ImportExport, Lightbulb, Settings, Fulls import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components/tooltip'; import { resultSetFocus } from '../../data-access/store/interactionSlice'; import { QueryBuilderDispatcherContext } from './QueryBuilderDispatcher'; +import { Popover, PopoverContent, PopoverTrigger } from '../../components/layout/Popover'; export type QueryBuilderProps = { onRunQuery?: () => void; @@ -449,8 +450,6 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { }} > <div ref={reactFlowWrapper} className="h-full w-full flex flex-col"> - <QueryMLDialog open={toggleSettings === 'ml'} onClose={() => setToggleSettings(undefined)} /> - <div className="sticky shrink-0 top-0 flex items-stretch justify-between h-7 bg-secondary-100 border-b border-secondary-200 max-w-full"> <div className="flex items-center"> <h1 className="text-xs font-semibold text-secondary-600 px-2 truncate">Query builder</h1> @@ -460,7 +459,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <TooltipProvider delayDuration={0}> <Tooltip> <TooltipTrigger asChild> - <Button type="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={fitView} /> + <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={fitView} /> </TooltipTrigger> <TooltipContent side={'top'}> <p>Fit to screen</p> @@ -468,7 +467,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { </Tooltip> <Tooltip> <TooltipTrigger asChild> - <Button type="secondary" variant="ghost" size="xs" iconComponent={<Delete />} onClick={() => clearAllNodes()} /> + <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Delete />} onClick={() => clearAllNodes()} /> </TooltipTrigger> <TooltipContent side={'top'}> <p>Clear query panel</p> @@ -477,7 +476,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <Tooltip> <TooltipTrigger asChild> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<CameraAlt />} @@ -493,7 +492,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <Tooltip> <TooltipTrigger asChild> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<ImportExport />} @@ -510,7 +509,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <Tooltip> <TooltipTrigger asChild> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<Settings />} @@ -529,7 +528,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <Tooltip> <TooltipTrigger asChild> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<Cached />} @@ -546,7 +545,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <Tooltip> <TooltipTrigger asChild> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<Difference />} @@ -561,24 +560,31 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <p>Logic settings</p> </TooltipContent> </Tooltip> - <Tooltip> - <TooltipTrigger asChild> - <Button - type="secondary" - variant="ghost" - size="xs" - iconComponent={<Lightbulb />} - onClick={(event) => { - event.stopPropagation(); - if (toggleSettings === 'ml') setToggleSettings(undefined); - else setToggleSettings('ml'); - }} - /> - </TooltipTrigger> - <TooltipContent side={'top'} disabled={toggleSettings === 'ml'}> - <p>Machine learning</p> - </TooltipContent> - </Tooltip> + <Popover> + <PopoverTrigger> + <Tooltip> + <TooltipTrigger> + <Button + variantType="secondary" + variant="ghost" + size="xs" + iconComponent={<Lightbulb />} + onClick={(event) => { + // event.stopPropagation(); + // if (toggleSettings === 'ml') setToggleSettings(undefined); + // else setToggleSettings('ml'); + }} + /> + </TooltipTrigger> + <TooltipContent side={'top'} disabled={toggleSettings === 'ml'}> + <p>Machine learning</p> + </TooltipContent> + </Tooltip> + </PopoverTrigger> + <PopoverContent> + <QueryMLDialog /> + </PopoverContent> + </Popover> </TooltipProvider> </ControlContainer> </div> @@ -586,40 +592,45 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <Dialog open={toggleSettings === 'logic'} - onClose={() => { - setToggleSettings(undefined); + onOpenChange={(ret) => { + if (!ret) setToggleSettings(undefined); }} > - <QueryBuilderLogicPillsPanel - onClick={(v) => { - connectingNodeId.current = null; - editLogicNode.current = undefined; - setToggleSettings(undefined); - }} - reactFlowWrapper={reactFlowWrapper.current} - title="Logic Pills usable by the node" - className="min-h-[75vh] max-h-[75vh]" - onDrag={() => {}} - connection={connectingNodeId?.current} - editNode={editLogicNode.current} - /> + <DialogContent> + <QueryBuilderLogicPillsPanel + onClick={(v) => { + connectingNodeId.current = null; + editLogicNode.current = undefined; + setToggleSettings(undefined); + }} + reactFlowWrapper={reactFlowWrapper.current} + title="Logic Pills usable by the node" + className="min-h-[75vh] max-h-[75vh]" + onDrag={() => {}} + connection={connectingNodeId?.current} + editNode={editLogicNode.current} + /> + <DialogClose>Cancel</DialogClose> + </DialogContent> </Dialog> <Dialog open={toggleSettings === 'relatedNodes'} - onClose={() => { - setToggleSettings(undefined); + onOpenChange={(ret) => { + if (!ret) setToggleSettings(undefined); }} > - <QueryBuilderRelatedNodesPanel - onFinished={() => { - connectingNodeId.current = null; - setToggleSettings(undefined); - }} - reactFlowWrapper={reactFlowWrapper.current} - title="Related nodes available to add to the query" - className="min-h-[75vh] max-h-[75vh]" - connection={connectingNodeId?.current} - /> + <DialogContent> + <QueryBuilderRelatedNodesPanel + onFinished={() => { + connectingNodeId.current = null; + setToggleSettings(undefined); + }} + reactFlowWrapper={reactFlowWrapper.current} + title="Related nodes available to add to the query" + className="min-h-[75vh] max-h-[75vh]" + connection={connectingNodeId?.current} + /> + </DialogContent> </Dialog> <svg height={0}> <defs> diff --git a/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderLogicPillsPanel.tsx b/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderLogicPillsPanel.tsx index 39426ce74..041acb8df 100644 --- a/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderLogicPillsPanel.tsx +++ b/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderLogicPillsPanel.tsx @@ -12,7 +12,7 @@ import { ConnectingNodeDataI } from '../utils/connectorDrop'; import { useQuerybuilderGraph } from '@graphpolaris/shared/lib/data-access'; import { toQuerybuilderGraphology, setQuerybuilderGraphology } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; import { useDispatch } from 'react-redux'; -import { Button } from '../../..'; +import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../..'; export const QueryBuilderLogicPillsPanel = (props: { reactFlowWrapper: HTMLDivElement | null; @@ -163,19 +163,24 @@ export const QueryBuilderLogicPillsPanel = (props: { <div className={props.className + ' card'}> {props.title && <h1 className="card-title mb-7">{props.title}</h1>} <div className="gap-1 flex"> - {dataOps.map((item, index) => ( - <div key={item.title} data-tip={item.description} className="tooltip tooltip-top m-0 p-0"> - <Button - iconComponent={item.icon} - size="sm" - variant={selectedOp === index ? 'solid' : 'outline'} - onClick={(e) => { - e.preventDefault(); - index === selectedOp ? setSelectedOp(-1) : setSelectedOp(index); - }} - ></Button> - </div> - ))} + <TooltipProvider delayDuration={50}> + {dataOps.map((item, index) => ( + <Tooltip key={item.title}> + <TooltipContent>{item.description}</TooltipContent> + <TooltipTrigger> + <Button + iconComponent={item.icon} + size="sm" + variant={selectedOp === index ? 'solid' : 'outline'} + onClick={(e) => { + e.preventDefault(); + index === selectedOp ? setSelectedOp(-1) : setSelectedOp(index); + }} + ></Button> + </TooltipTrigger> + </Tooltip> + ))} + </TooltipProvider> <div className="w-2" /> {dataTypes.map((item, index) => ( <div key={item.title} data-tip={item.description} className="tooltip tooltip-top m-0 p-0"> diff --git a/libs/shared/lib/querybuilder/panel/querysidepanel/queryMLDialog.tsx b/libs/shared/lib/querybuilder/panel/querysidepanel/queryMLDialog.tsx index ff724e1ca..213807e6b 100644 --- a/libs/shared/lib/querybuilder/panel/querysidepanel/queryMLDialog.tsx +++ b/libs/shared/lib/querybuilder/panel/querysidepanel/queryMLDialog.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { DialogProps } from '@graphpolaris/shared/lib/components/layout'; import { useAppDispatch, useML } from '@graphpolaris/shared/lib/data-access'; import { setCentralityEnabled, @@ -9,77 +8,72 @@ import { } from '@graphpolaris/shared/lib/data-access/store/mlSlice'; import { FormDiv, FormCard, FormBody, FormTitle, FormHBar } from '@graphpolaris/shared/lib/components/forms'; import { Input } from '@graphpolaris/shared/lib/components/inputs'; +import { PopoverTrigger } from '@graphpolaris/shared/lib/components/layout/Popover'; -type QueryMLDialogProps = DialogProps; - -export const QueryMLDialog = React.forwardRef<HTMLDivElement, QueryMLDialogProps>((props, ref) => { +export const QueryMLDialog = () => { const dispatch = useAppDispatch(); const ml = useML(); return ( - <> - {props.open && ( - <FormDiv ref={ref} className="" hAnchor="right"> - <FormCard> - <FormBody - onSubmit={(e) => { - e.preventDefault(); - }} - > - <FormTitle title="Machine Learning Options" onClose={props.onClose} /> - <FormHBar /> + <div> + <FormCard> + <FormBody + onSubmit={(e) => { + e.preventDefault(); + }} + > + <FormTitle title="Machine Learning Options" /> + <FormHBar /> - <div className="px-5"> - <Input - type="boolean" - label="Link Prediction" - value={ml.linkPrediction.enabled} - onChange={() => dispatch(setLinkPredictionEnabled(!ml.linkPrediction.enabled))} - /> - {ml.linkPrediction.enabled && ml.linkPrediction.result && <span># of predictions: {ml.linkPrediction.result.length}</span>} - {ml.linkPrediction.enabled && !ml.linkPrediction.result && <span>Loading...</span>} + <div className="px-5"> + <Input + type="boolean" + label="Link Prediction" + value={ml.linkPrediction.enabled} + onChange={() => dispatch(setLinkPredictionEnabled(!ml.linkPrediction.enabled))} + /> + {ml.linkPrediction.enabled && ml.linkPrediction.result && <span># of predictions: {ml.linkPrediction.result.length}</span>} + {ml.linkPrediction.enabled && !ml.linkPrediction.result && <span>Loading...</span>} - <Input - type="boolean" - label="Centrality" - value={ml.centrality.enabled} - onChange={() => dispatch(setCentralityEnabled(!ml.centrality.enabled))} - /> - {ml.centrality.enabled && Object.values(ml.centrality.result).length > 0 && ( - <span> - sum of centers: - {Object.values(ml.centrality.result) - .reduce((a, b) => b + a) - .toFixed(2)} - </span> - )} - {ml.centrality.enabled && Object.values(ml.centrality.result).length === 0 && <span>No Centers Found</span>} + <Input + type="boolean" + label="Centrality" + value={ml.centrality.enabled} + onChange={() => dispatch(setCentralityEnabled(!ml.centrality.enabled))} + /> + {ml.centrality.enabled && Object.values(ml.centrality.result).length > 0 && ( + <span> + sum of centers: + {Object.values(ml.centrality.result) + .reduce((a, b) => b + a) + .toFixed(2)} + </span> + )} + {ml.centrality.enabled && Object.values(ml.centrality.result).length === 0 && <span>No Centers Found</span>} - <Input - type="boolean" - label="Community detection" - value={ml.communityDetection.enabled} - onChange={() => dispatch(setCommunityDetectionEnabled(!ml.communityDetection.enabled))} - /> - {ml.communityDetection.enabled && ml.communityDetection.result && ( - <span># of communities: {ml.communityDetection.result.length}</span> - )} - {ml.communityDetection.enabled && !ml.communityDetection.result && <span>Loading...</span>} + <Input + type="boolean" + label="Community detection" + value={ml.communityDetection.enabled} + onChange={() => dispatch(setCommunityDetectionEnabled(!ml.communityDetection.enabled))} + /> + {ml.communityDetection.enabled && ml.communityDetection.result && ( + <span># of communities: {ml.communityDetection.result.length}</span> + )} + {ml.communityDetection.enabled && !ml.communityDetection.result && <span>Loading...</span>} - <Input - type="boolean" - label="Shortest path" - value={ml.shortestPath.enabled} - onChange={() => dispatch(setShortestPathEnabled(!ml.shortestPath.enabled))} - /> - {ml.shortestPath.enabled && ml.shortestPath.result?.length > 0 && <span># of hops: {ml.shortestPath.result.length}</span>} - {ml.shortestPath.enabled && !ml.shortestPath.srcNode && <span>Please select source node</span>} - {ml.shortestPath.enabled && ml.shortestPath.srcNode && !ml.shortestPath.trtNode && <span>Please select target node</span>} - </div> - </FormBody> - </FormCard> - </FormDiv> - )} - </> + <Input + type="boolean" + label="Shortest path" + value={ml.shortestPath.enabled} + onChange={() => dispatch(setShortestPathEnabled(!ml.shortestPath.enabled))} + /> + {ml.shortestPath.enabled && ml.shortestPath.result?.length > 0 && <span># of hops: {ml.shortestPath.result.length}</span>} + {ml.shortestPath.enabled && !ml.shortestPath.srcNode && <span>Please select source node</span>} + {ml.shortestPath.enabled && ml.shortestPath.srcNode && !ml.shortestPath.trtNode && <span>Please select target node</span>} + </div> + </FormBody> + </FormCard> + </div> ); -}); +}; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/QueryLogicPill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/QueryLogicPill.tsx index 2c2f75c77..09a9e534b 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/QueryLogicPill.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/QueryLogicPill.tsx @@ -6,7 +6,7 @@ import { GeneralDescription, InputNode, InputNodeTypeTypes } from '../../../mode import { styleHandleMap } from '../../utils'; import { setQuerybuilderGraphology, toQuerybuilderGraphology } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; import { LogicInput } from './LogicInput'; -import { Button, DropdownButton, Input, LogicPill } from '@graphpolaris/shared/lib/components'; +import { Button, DropdownTrigger, Input, LogicPill } from '@graphpolaris/shared/lib/components'; import { ArrowDropDown } from '@mui/icons-material'; import { QueryBuilderDispatcherContext } from '../../../panel/QueryBuilderDispatcher'; @@ -73,7 +73,7 @@ export function QueryLogicPill(node: SchemaReactflowLogicNode) { > <div className={`py-1 h-fit border-[1px] border-secondary-200 ${data.selected ? 'bg-secondary-400' : 'bg-secondary-100'}`}> {/* <div className="m-1 mx-2 text-left">{output.name}</div> */} - <DropdownButton + <DropdownTrigger title={output.name} variant="ghost" size="xs" diff --git a/libs/shared/lib/schema/panel/Schema.tsx b/libs/shared/lib/schema/panel/Schema.tsx index bb9a9c409..20d12776e 100644 --- a/libs/shared/lib/schema/panel/Schema.tsx +++ b/libs/shared/lib/schema/panel/Schema.tsx @@ -14,7 +14,8 @@ import { ContentCopy, FitScreen, Fullscreen, KeyboardArrowDown, KeyboardArrowRig import { AlgorithmToLayoutProvider, AllLayoutAlgorithms, LayoutFactory } from '../../graph-layout'; import { ConnectionLine, ConnectionDragLine } from '../../querybuilder'; import { schemaExpandRelation, schemaGraphology2Reactflow } from '../schema-utils'; -import { Panel, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components'; +import { Panel } from '../../components'; +import { Tooltip, TooltipContent, TooltipTrigger } from '../../components/tooltip/Tooltip'; import { resultSetFocus } from '../../data-access/store/interactionSlice'; import { useDispatch } from 'react-redux'; @@ -116,11 +117,11 @@ export const Schema = (props: Props) => { <Panel title="Schema" tooltips={ - <TooltipProvider delayDuration={10}> + <> <Tooltip> - <TooltipTrigger asChild> + <TooltipTrigger> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<Remove />} @@ -134,9 +135,9 @@ export const Schema = (props: Props) => { </TooltipContent> </Tooltip> <Tooltip> - <TooltipTrigger asChild> + <TooltipTrigger> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<ContentCopy />} @@ -151,14 +152,14 @@ export const Schema = (props: Props) => { </TooltipContent> </Tooltip> <Tooltip> - <TooltipTrigger asChild> - <Button type="secondary" variant="ghost" size="xs" iconComponent={<FitScreen />} onClick={() => {}} /> + <TooltipTrigger> + <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<FitScreen />} onClick={() => {}} /> </TooltipTrigger> <TooltipContent side={'top'}> <p>Fit to screen</p> </TooltipContent> </Tooltip> - </TooltipProvider> + </> } > <div className="schema-panel w-full h-full flex flex-col justify-between" ref={reactFlowRef}> diff --git a/libs/shared/lib/schema/panel/SchemaSettings.tsx b/libs/shared/lib/schema/panel/SchemaSettings.tsx index 270cb50d4..0d3c0fa55 100644 --- a/libs/shared/lib/schema/panel/SchemaSettings.tsx +++ b/libs/shared/lib/schema/panel/SchemaSettings.tsx @@ -1,11 +1,8 @@ -import { useEffect, useState } from 'react'; -import { Dialog, DialogProps } from '../../components/layout/Dialog'; import React from 'react'; import { useAppDispatch, useSchemaSettings } from '../../data-access'; -import { SchemaConnectionTypes, SchemaSettings, schemaConnectionTypeArray, setSchemaSettings } from '../../data-access/store/schemaSlice'; -import { FormActions, FormBody, FormCard, FormControl, FormHBar, FormTitle, FormDiv } from '../../components/forms'; -import { Layouts } from '../../graph-layout'; +import { SchemaConnectionTypes, schemaConnectionTypeArray, setSchemaSettings } from '../../data-access/store/schemaSlice'; import { Input } from '../../components/inputs'; +import { Layouts } from '../../graph-layout'; export const SchemaDialog = () => { const settings = useSchemaSettings(); diff --git a/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPill.tsx b/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPill.tsx index 3afed3f5e..d3bfe7ef2 100644 --- a/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPill.tsx +++ b/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPill.tsx @@ -59,6 +59,7 @@ export const SchemaEntityPill = React.memo(({ id, selected, data }: NodeProps<Sc draggable > <EntityPill + draggable title={id} withHandles="vertical" handleUp={ diff --git a/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationPill.tsx b/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationPill.tsx index c7a79c38a..86cdfcdb8 100644 --- a/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationPill.tsx +++ b/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationPill.tsx @@ -64,6 +64,7 @@ export const SchemaRelationPill = React.memo(({ id, selected, data, ...props }: draggable > <RelationPill + draggable title={data.collection} withHandles="vertical" handleUp={ diff --git a/libs/shared/lib/sidebar/index.tsx b/libs/shared/lib/sidebar/index.tsx index 8f92890cf..6648d2195 100644 --- a/libs/shared/lib/sidebar/index.tsx +++ b/libs/shared/lib/sidebar/index.tsx @@ -24,7 +24,7 @@ export function Sidebar({ onTab, tab }: { onTab: (tab: SideNavTab) => void; tab: <Tooltip key={t.name}> <TooltipTrigger asChild> <Button - type="secondary" + variantType="secondary" variant="ghost" size="sm" iconComponent={t.icon} diff --git a/libs/shared/lib/sidebar/search/SearchBar.tsx b/libs/shared/lib/sidebar/search/SearchBar.tsx index 04e24282f..c8bdae451 100644 --- a/libs/shared/lib/sidebar/search/SearchBar.tsx +++ b/libs/shared/lib/sidebar/search/SearchBar.tsx @@ -116,7 +116,7 @@ export function SearchBar(props: { onRemove?: () => void }) { <Tooltip> <TooltipTrigger asChild> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<Remove />} @@ -131,7 +131,7 @@ export function SearchBar(props: { onRemove?: () => void }) { </Tooltip> <Tooltip> <TooltipTrigger asChild> - <Button type="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={() => {}} /> + <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={() => {}} /> </TooltipTrigger> <TooltipContent side={'top'}> <p>Mock icon</p> diff --git a/libs/shared/lib/vis/components/bar.tsx b/libs/shared/lib/vis/components/bar.tsx index 76cca2479..fe25a8bc0 100644 --- a/libs/shared/lib/vis/components/bar.tsx +++ b/libs/shared/lib/vis/components/bar.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { Button, Icon } from '../../components'; +import { Button, DropdownTrigger, DropdownItemContainer, DropdownItem, Icon, DropdownContainer } from '../../components'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components/tooltip'; -import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import { Add, Close, Fullscreen } from '@mui/icons-material'; import { ControlContainer } from '../../components/controls'; import { Visualizations } from '../manager'; import { VisualizationManagerType } from '../manager'; import { Tabs, Tab } from '../../components/tabs'; +import { Popover, PopoverContent, PopoverTrigger } from '../../components/layout/Popover'; type Props = { manager: VisualizationManagerType; @@ -14,6 +14,8 @@ type Props = { }; export default function VisualizationBar({ manager, fullSize }: Props) { + const [open, setOpen] = React.useState(false); + const handleDragStart = (e: React.DragEvent<HTMLDivElement>, visId: string) => { e.dataTransfer.setData('text/plain', visId); }; @@ -35,35 +37,35 @@ export default function VisualizationBar({ manager, fullSize }: Props) { <h1 className="text-xs font-semibold text-secondary-600 px-2 truncate">Visualization</h1> </div> <div className="items-center shrink-0 px-0.5"> - <DropdownMenu.Root> - <DropdownMenu.Trigger> - <TooltipProvider delayDuration={0}> - <Tooltip> - <TooltipTrigger asChild> - <Button as={'a'} type="secondary" variant="ghost" size="xs" iconComponent={<Add />} onClick={() => {}} /> - </TooltipTrigger> - <TooltipContent side={'top'}> - <p>Add visualization</p> - </TooltipContent> - </Tooltip> - </TooltipProvider> - </DropdownMenu.Trigger> - <DropdownMenu.Portal> - <DropdownMenu.Content className="bg-light p-1 rounded border"> - {Object.keys(Visualizations).map((key) => ( - <DropdownMenu.Item - key={key} - className="text-sm px-2 py-1 rounded cursor-pointer hover:bg-secondary-200" - onClick={(e) => { - manager.changeActive(key); - }} - > - {key} - </DropdownMenu.Item> - ))} - </DropdownMenu.Content> - </DropdownMenu.Portal> - </DropdownMenu.Root> + <TooltipProvider delayDuration={0}> + <Tooltip> + <TooltipTrigger> + <Popover open={open} onOpenChange={setOpen}> + <PopoverTrigger onClick={() => setOpen((v) => !v)}> + <Button as={'a'} variantType="secondary" variant="ghost" size="xs" iconComponent={<Add />} onClick={() => {}} /> + </PopoverTrigger> + <PopoverContent> + <div className="bg-light p-1 rounded border"> + {Object.keys(Visualizations).map((key) => ( + <DropdownItem + value={key} + key={key} + className="text-sm px-2 py-1 rounded cursor-pointer hover:bg-secondary-200" + onClick={(e) => { + manager.changeActive(key); + setOpen(false); + }} + ></DropdownItem> + ))} + </div> + </PopoverContent> + </Popover> + </TooltipTrigger> + <TooltipContent side={'top'}> + <p>Add visualization</p> + </TooltipContent> + </Tooltip> + </TooltipProvider> </div> <Tabs> {manager.tabs.map((visId: string) => { @@ -71,7 +73,7 @@ export default function VisualizationBar({ manager, fullSize }: Props) { return ( <Tab key={visId} - active={isActive} + activeTab={isActive} text={visId} onClick={() => manager.changeActive(visId)} onDragStart={(e) => handleDragStart(e, visId)} @@ -80,7 +82,7 @@ export default function VisualizationBar({ manager, fullSize }: Props) { draggable > <Button - type="secondary" + variantType="secondary" variant="ghost" rounded size="2xs" @@ -99,7 +101,7 @@ export default function VisualizationBar({ manager, fullSize }: Props) { <TooltipProvider delayDuration={0}> <Tooltip> <TooltipTrigger asChild> - <Button type="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={fullSize} /> + <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={fullSize} /> </TooltipTrigger> <TooltipContent side={'top'}> <p>Full screen</p> diff --git a/libs/shared/lib/vis/components/config/ActiveVisualizationConfig.tsx b/libs/shared/lib/vis/components/config/ActiveVisualizationConfig.tsx index 88ed5de60..38fe19fa9 100644 --- a/libs/shared/lib/vis/components/config/ActiveVisualizationConfig.tsx +++ b/libs/shared/lib/vis/components/config/ActiveVisualizationConfig.tsx @@ -13,7 +13,7 @@ export const ActiveVisualizationConfig = ({ manager }: Props) => { <div className="flex justify-between items-center px-4 py-2"> <span className="text-xs font-bold">Visualization</span> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<Delete />} diff --git a/libs/shared/lib/vis/components/config/SelectionConfig.tsx b/libs/shared/lib/vis/components/config/SelectionConfig.tsx index 08dcadd77..496bad2c7 100644 --- a/libs/shared/lib/vis/components/config/SelectionConfig.tsx +++ b/libs/shared/lib/vis/components/config/SelectionConfig.tsx @@ -16,7 +16,7 @@ export const SelectionConfig = () => { <div className="flex justify-between items-center px-4 py-2"> <span className="text-xs font-bold">Selection</span> <Button - type="secondary" + variantType="secondary" variant="ghost" size="xs" iconComponent={<Delete />} diff --git a/libs/shared/lib/vis/views/noData.tsx b/libs/shared/lib/vis/views/noData.tsx index fafa7b810..dc63c70bc 100644 --- a/libs/shared/lib/vis/views/noData.tsx +++ b/libs/shared/lib/vis/views/noData.tsx @@ -15,7 +15,7 @@ export function NoData({ dataAvailable }: Props) { <div> <p>Query for data to visualize</p> <Button - type="primary" + variantType="primary" variant="outline" label="Learn how to query data" size="sm" diff --git a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx index 2b16589f2..9a848bd69 100644 --- a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx +++ b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx @@ -954,7 +954,7 @@ const PaohSettings = ({ </div> <Button className="w-full text-justify justify-start" - type="secondary" + variantType="secondary" variant="ghost" size="sm" onClick={toggleCollapseAttrRows} @@ -989,7 +989,7 @@ const PaohSettings = ({ <Button className="w-full text-justify justify-start" - type="secondary" + variantType="secondary" variant="ghost" size="sm" onClick={toggleCollapseAttrColumns} diff --git a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/components/ConfigPanel.tsx b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/components/ConfigPanel.tsx index 449218b01..3aadfdeee 100644 --- a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/components/ConfigPanel.tsx +++ b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/components/ConfigPanel.tsx @@ -229,7 +229,7 @@ const ConfigPanel: React.FC<ConfigPanelProps> = ({ data, onUpdateData }) => { <Button label="Make" - type="secondary" + variantType="secondary" variant="solid" disabled={!state.isButtonEnabled || (!state.orderNameXaxis && !state.orderNameYaxis)} onClick={onClickMakeButton} diff --git a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx index b1ed34e9b..d2070b3f0 100644 --- a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx +++ b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx @@ -134,7 +134,7 @@ const TableSettings = ({ <span className="text-sm">Attributes to display:</span> <Button className="w-full text-justify justify-start" - type="secondary" + variantType="secondary" variant="ghost" size="sm" onClick={toggleCollapseAttr} diff --git a/libs/shared/package.json b/libs/shared/package.json index 166ef8ea4..2559e751d 100644 --- a/libs/shared/package.json +++ b/libs/shared/package.json @@ -21,10 +21,9 @@ "@deck.gl/react": "^9.0.12", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", + "@floating-ui/react": "^0.26.16", "@mui/icons-material": "^5.15.13", "@pixi-essentials/cull": "^2.0.0", - "@radix-ui/react-dropdown-menu": "^2.0.6", - "@radix-ui/react-tooltip": "^1.0.7", "@reactflow/node-resizer": "^2.2.9", "@reduxjs/toolkit": "^2.2.1", "@tisoap/react-flow-smart-edge": "^3.0.0", diff --git a/libs/storybook/src/stories/Button.stories.ts b/libs/storybook/src/stories/Button.stories.ts deleted file mode 100644 index af4e0c310..000000000 --- a/libs/storybook/src/stories/Button.stories.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { Button } from './Button'; - -// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction -const meta = { - title: 'Example/Button', - component: Button, - tags: ['autodocs'], - argTypes: { - backgroundColor: { control: 'color' }, - }, -} satisfies Meta<typeof Button>; - -export default meta; -type Story = StoryObj<typeof meta>; - -// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args -export const Primary: Story = { - args: { - primary: true, - label: 'Button', - }, -}; - -export const Secondary: Story = { - args: { - label: 'Button', - }, -}; - -export const Large: Story = { - args: { - size: 'large', - label: 'Button', - }, -}; - -export const Small: Story = { - args: { - size: 'small', - label: 'Button', - }, -}; diff --git a/libs/storybook/src/stories/Button.tsx b/libs/storybook/src/stories/Button.tsx deleted file mode 100644 index 66f8fd32c..000000000 --- a/libs/storybook/src/stories/Button.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import './button.css'; - -interface ButtonProps { - /** - * Is this the principal call to action on the page? - */ - primary?: boolean; - /** - * What background color to use - */ - backgroundColor?: string; - /** - * How large should the button be? - */ - size?: 'small' | 'medium' | 'large'; - /** - * Button contents - */ - label: string; - /** - * Optional click handler - */ - onClick?: () => void; -} - -/** - * Primary UI component for user interaction - */ -export const Button = ({ primary = false, size = 'medium', backgroundColor, label, ...props }: ButtonProps) => { - const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; - return ( - <button - type="button" - className={['storybook-button', `storybook-button--${size}`, mode].join(' ')} - style={{ backgroundColor }} - {...props} - > - {label} - </button> - ); -}; diff --git a/libs/storybook/src/stories/button.css b/libs/storybook/src/stories/button.css deleted file mode 100644 index dc91dc763..000000000 --- a/libs/storybook/src/stories/button.css +++ /dev/null @@ -1,30 +0,0 @@ -.storybook-button { - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-weight: 700; - border: 0; - border-radius: 3em; - cursor: pointer; - display: inline-block; - line-height: 1; -} -.storybook-button--primary { - color: white; - background-color: #1ea7fd; -} -.storybook-button--secondary { - color: #333; - background-color: transparent; - box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; -} -.storybook-button--small { - font-size: 12px; - padding: 10px 16px; -} -.storybook-button--medium { - font-size: 14px; - padding: 11px 20px; -} -.storybook-button--large { - font-size: 16px; - padding: 12px 24px; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5a659daf..a88e3a1d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,7 +123,7 @@ importers: version: 0.5.1(@import-meta-env/cli@0.6.8)(dotenv@16.4.5) '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.2))(@types/node@20.11.27)(typescript@5.4.2))) + version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.11.27)(typescript@5.4.2))) '@testing-library/react': specifier: 14.2.1 version: 14.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -192,7 +192,7 @@ importers: devDependencies: '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.2))(@types/node@20.11.27)(typescript@5.4.2))) + version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.11.27)(typescript@5.4.2))) daisyui: specifier: ^4.7.3 version: 4.7.3(postcss@8.4.35) @@ -223,18 +223,15 @@ importers: '@emotion/styled': specifier: ^11.11.0 version: 11.11.0(@emotion/react@11.11.4(@types/react@18.2.65)(react@18.2.0))(@types/react@18.2.65)(react@18.2.0) + '@floating-ui/react': + specifier: ^0.26.16 + version: 0.26.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@mui/icons-material': specifier: ^5.15.13 version: 5.15.13(@mui/material@5.15.13(@emotion/react@11.11.4(@types/react@18.2.65)(react@18.2.0))(@emotion/styled@11.11.0(@emotion/react@11.11.4(@types/react@18.2.65)(react@18.2.0))(@types/react@18.2.65)(react@18.2.0))(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@types/react@18.2.65)(react@18.2.0) '@pixi-essentials/cull': specifier: ^2.0.0 version: 2.0.0(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/math@7.4.2) - '@radix-ui/react-dropdown-menu': - specifier: ^2.0.6 - version: 2.0.6(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-tooltip': - specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@reactflow/node-resizer': specifier: ^2.2.9 version: 2.2.9(@types/react@18.2.65)(immer@10.0.4)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -611,7 +608,7 @@ importers: version: 8.0.6(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/preset-scss': specifier: ^1.0.3 - version: 1.0.3(css-loader@6.10.0(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)))(sass-loader@14.1.1(sass@1.72.0)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)))(style-loader@3.3.4(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12))) + version: 1.0.3(css-loader@6.10.0(webpack@5.90.3(esbuild@0.19.12)))(sass-loader@14.1.1(sass@1.72.0)(webpack@5.90.3(esbuild@0.19.12)))(style-loader@3.3.4(webpack@5.90.3(esbuild@0.19.12))) '@storybook/react': specifier: ^8.0.6 version: 8.0.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.4.2) @@ -659,7 +656,7 @@ importers: version: 1.72.0 sass-loader: specifier: ^14.1.1 - version: 14.1.1(sass@1.72.0)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)) + version: 14.1.1(sass@1.72.0)(webpack@5.90.3(esbuild@0.19.12)) storybook: specifier: ^8.0.6 version: 8.0.6(@babel/preset-env@7.24.0(@babel/core@7.24.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -1812,8 +1809,14 @@ packages: '@floating-ui/dom@1.6.3': resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==} - '@floating-ui/react-dom@2.0.8': - resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==} + '@floating-ui/react-dom@2.1.0': + resolution: {integrity: sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.26.16': + resolution: {integrity: sha512-HEf43zxZNAI/E781QIVpYSF3K2VH4TTYZpqecjdsFkjsaU1EbaWcM++kw0HXFffj7gDUcBFevX8s0rQGQpxkow==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' @@ -2523,35 +2526,6 @@ packages: '@probe.gl/stats@4.0.9': resolution: {integrity: sha512-Q9Xt/sJUQaMsbjRKjOscv2t7wXIymTrOEJ4a3da4FTCn7bkKvcdxdyFAQySCrtPxE+YZ5I5lXpWPgv9BwmpE1g==} - '@radix-ui/primitive@1.0.1': - resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} - - '@radix-ui/react-arrow@1.0.3': - resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-collection@1.0.3': - resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-compose-refs@1.0.1': resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: @@ -2561,159 +2535,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.0.1': - resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-direction@1.0.1': - resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-dismissable-layer@1.0.5': - resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-dropdown-menu@2.0.6': - resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-focus-guards@1.0.1': - resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-focus-scope@1.0.4': - resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-id@1.0.1': - resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-menu@2.0.6': - resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-popper@1.1.3': - resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-portal@1.0.4': - resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-presence@1.0.1': - resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@1.0.3': - resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-roving-focus@1.0.4': - resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-slot@1.0.2': resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -2723,89 +2544,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-tooltip@1.0.7': - resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-use-callback-ref@1.0.1': - resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.0.1': - resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.0.3': - resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.0.1': - resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.0.1': - resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.0.1': - resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-visually-hidden@1.0.3': - resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/rect@1.0.1': - resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} - '@reactflow/background@11.3.9': resolution: {integrity: sha512-byj/G9pEC8tN0wT/ptcl/LkEP/BBfa33/SvBkqE4XwyofckqF87lKp573qGlisfnsijwAbpDlf81PuFL41So4Q==} peerDependencies: @@ -4144,10 +3882,6 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-hidden@1.2.3: - resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} - engines: {node: '>=10'} - aria-query@5.1.3: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} @@ -5028,9 +4762,6 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} - detect-node-es@1.1.0: - resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - detect-package-manager@2.0.1: resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==} engines: {node: '>=12'} @@ -5595,10 +5326,6 @@ packages: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} - get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} - get-npm-tarball-url@2.1.0: resolution: {integrity: sha512-ro+DiMu5DXgRBabqXupW38h7WPZ9+Ad8UjwhvsmmN8w1sU7ab0nzAXvVZ4kqYg57OrqomRtJvepX5/xvFKNtjA==} engines: {node: '>=12.17'} @@ -5959,9 +5686,6 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - invariant@2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - ip@2.0.1: resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==} @@ -7399,27 +7123,6 @@ packages: resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} engines: {node: '>=0.10.0'} - react-remove-scroll-bar@2.3.5: - resolution: {integrity: sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==} - engines: {node: '>=10'} - deprecated: please update to the following version as this contains a bug (https://github.com/theKashey/react-remove-scroll-bar/issues/57) - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - - react-remove-scroll@2.5.5: - resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - react-resizable@3.0.5: resolution: {integrity: sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==} peerDependencies: @@ -7443,16 +7146,6 @@ packages: peerDependencies: react: ^16.0.0 || ^17.0.0 || ^18.0.0 - react-style-singleton@2.2.1: - resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - react-test-renderer@18.2.0: resolution: {integrity: sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==} peerDependencies: @@ -8444,16 +8137,6 @@ packages: url@0.11.3: resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==} - use-callback-ref@1.3.1: - resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - use-composed-ref@1.3.0: resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} peerDependencies: @@ -8483,16 +8166,6 @@ packages: '@types/react': optional: true - use-sidecar@1.1.2: - resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - use-sync-external-store@1.2.0: resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: @@ -10180,12 +9853,20 @@ snapshots: '@floating-ui/core': 1.6.0 '@floating-ui/utils': 0.2.1 - '@floating-ui/react-dom@2.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@floating-ui/react-dom@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@floating-ui/dom': 1.6.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + '@floating-ui/react@0.26.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@floating-ui/react-dom': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@floating-ui/utils': 0.2.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tabbable: 6.2.0 + '@floating-ui/utils@0.2.1': {} '@gilbarbara/deep-equal@0.1.2': {} @@ -10618,7 +10299,7 @@ snapshots: '@mui/base@5.0.0-beta.39(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.0 - '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@floating-ui/react-dom': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@mui/types': 7.2.13(@types/react@18.2.65) '@mui/utils': 5.15.13(@types/react@18.2.65)(react@18.2.0) '@popperjs/core': 2.11.8 @@ -10975,33 +10656,6 @@ snapshots: '@probe.gl/stats@4.0.9': {} - '@radix-ui/primitive@1.0.1': - dependencies: - '@babel/runtime': 7.24.0 - - '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - '@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.65)(react@18.2.0)': dependencies: '@babel/runtime': 7.24.0 @@ -11009,172 +10663,6 @@ snapshots: optionalDependencies: '@types/react': 18.2.65 - '@radix-ui/react-context@1.0.1(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-direction@1.0.1(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-id@1.0.1(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - aria-hidden: 1.2.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.65)(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/rect': 1.0.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - '@radix-ui/react-slot@1.0.2(@types/react@18.2.65)(react@18.2.0)': dependencies: '@babel/runtime': 7.24.0 @@ -11183,87 +10671,6 @@ snapshots: optionalDependencies: '@types/react': 18.2.65 - '@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-use-rect@1.0.1(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/rect': 1.0.1 - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-use-size@1.0.1(@types/react@18.2.65)(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.65)(react@18.2.0) - react: 18.2.0 - optionalDependencies: - '@types/react': 18.2.65 - - '@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.65)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - '@types/react-dom': 18.2.22 - - '@radix-ui/rect@1.0.1': - dependencies: - '@babel/runtime': 7.24.0 - '@reactflow/background@11.3.9(@types/react@18.2.65)(immer@10.0.4)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@reactflow/core': 11.10.4(@types/react@18.2.65)(immer@10.0.4)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -11900,18 +11307,18 @@ snapshots: '@storybook/node-logger@8.0.6': {} - '@storybook/preset-scss@1.0.3(css-loader@6.10.0(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)))(sass-loader@14.1.1(sass@1.72.0)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)))(style-loader@3.3.4(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)))': - dependencies: - css-loader: 6.10.0(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)) - sass-loader: 14.1.1(sass@1.72.0)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)) - style-loader: 3.3.4(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)) - '@storybook/preset-scss@1.0.3(css-loader@6.10.0(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))))(sass-loader@14.1.1(sass@1.72.0)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))))(style-loader@3.3.4(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))))': dependencies: css-loader: 6.10.0(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))) sass-loader: 14.1.1(sass@1.72.0)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))) style-loader: 3.3.4(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))) + '@storybook/preset-scss@1.0.3(css-loader@6.10.0(webpack@5.90.3(esbuild@0.19.12)))(sass-loader@14.1.1(sass@1.72.0)(webpack@5.90.3(esbuild@0.19.12)))(style-loader@3.3.4(webpack@5.90.3(esbuild@0.19.12)))': + dependencies: + css-loader: 6.10.0(webpack@5.90.3(esbuild@0.19.12)) + sass-loader: 14.1.1(sass@1.72.0)(webpack@5.90.3(esbuild@0.19.12)) + style-loader: 3.3.4(webpack@5.90.3(esbuild@0.19.12)) + '@storybook/preview-api@8.0.6': dependencies: '@storybook/channels': 8.0.6 @@ -12101,7 +11508,7 @@ snapshots: '@swc/types@0.1.5': {} - '@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.2))(@types/node@20.11.27)(typescript@5.4.2)))': + '@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.11.27)(typescript@5.4.2)))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 @@ -13318,10 +12725,6 @@ snapshots: argparse@2.0.1: {} - aria-hidden@1.2.3: - dependencies: - tslib: 2.6.2 - aria-query@5.1.3: dependencies: deep-equal: 2.2.3 @@ -13904,7 +13307,7 @@ snapshots: utrie: 1.0.2 optional: true - css-loader@6.10.0(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)): + css-loader@6.10.0(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))): dependencies: icss-utils: 5.1.0(postcss@8.4.35) postcss: 8.4.35 @@ -13915,9 +13318,9 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.6.0 optionalDependencies: - webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12) + webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2)) - css-loader@6.10.0(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))): + css-loader@6.10.0(webpack@5.90.3(esbuild@0.19.12)): dependencies: icss-utils: 5.1.0(postcss@8.4.35) postcss: 8.4.35 @@ -13928,7 +13331,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.6.0 optionalDependencies: - webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2)) + webpack: 5.90.3(esbuild@0.19.12) css-selector-tokenizer@0.8.0: dependencies: @@ -14309,8 +13712,6 @@ snapshots: detect-indent@6.1.0: {} - detect-node-es@1.1.0: {} - detect-package-manager@2.0.1: dependencies: execa: 5.1.1 @@ -15176,8 +14577,6 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.1 - get-nonce@1.0.1: {} - get-npm-tarball-url@2.1.0: {} get-stream@6.0.1: {} @@ -15541,10 +14940,6 @@ snapshots: internmap@2.0.3: {} - invariant@2.2.4: - dependencies: - loose-envify: 1.4.0 - ip@2.0.1: {} ipaddr.js@1.9.1: {} @@ -16912,25 +16307,6 @@ snapshots: react-refresh@0.14.0: {} - react-remove-scroll-bar@2.3.5(@types/react@18.2.65)(react@18.2.0): - dependencies: - react: 18.2.0 - react-style-singleton: 2.2.1(@types/react@18.2.65)(react@18.2.0) - tslib: 2.6.2 - optionalDependencies: - '@types/react': 18.2.65 - - react-remove-scroll@2.5.5(@types/react@18.2.65)(react@18.2.0): - dependencies: - react: 18.2.0 - react-remove-scroll-bar: 2.3.5(@types/react@18.2.65)(react@18.2.0) - react-style-singleton: 2.2.1(@types/react@18.2.65)(react@18.2.0) - tslib: 2.6.2 - use-callback-ref: 1.3.1(@types/react@18.2.65)(react@18.2.0) - use-sidecar: 1.1.2(@types/react@18.2.65)(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.65 - react-resizable@3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: prop-types: 15.8.1 @@ -16957,15 +16333,6 @@ snapshots: react: 18.2.0 react-is: 18.2.0 - react-style-singleton@2.2.1(@types/react@18.2.65)(react@18.2.0): - dependencies: - get-nonce: 1.0.1 - invariant: 2.2.4 - react: 18.2.0 - tslib: 2.6.2 - optionalDependencies: - '@types/react': 18.2.65 - react-test-renderer@18.2.0(react@18.2.0): dependencies: react: 18.2.0 @@ -17260,19 +16627,19 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@14.1.1(sass@1.72.0)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)): + sass-loader@14.1.1(sass@1.72.0)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))): dependencies: neo-async: 2.6.2 optionalDependencies: sass: 1.72.0 - webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12) + webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2)) - sass-loader@14.1.1(sass@1.72.0)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))): + sass-loader@14.1.1(sass@1.72.0)(webpack@5.90.3(esbuild@0.19.12)): dependencies: neo-async: 2.6.2 optionalDependencies: sass: 1.72.0 - webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2)) + webpack: 5.90.3(esbuild@0.19.12) sass@1.72.0: dependencies: @@ -17567,14 +16934,14 @@ snapshots: strnum@1.0.5: {} - style-loader@3.3.4(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)): - dependencies: - webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12) - style-loader@3.3.4(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))): dependencies: webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2)) + style-loader@3.3.4(webpack@5.90.3(esbuild@0.19.12)): + dependencies: + webpack: 5.90.3(esbuild@0.19.12) + styled-components@6.1.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@emotion/is-prop-valid': 1.2.1 @@ -17706,28 +17073,27 @@ snapshots: type-fest: 0.16.0 unique-string: 2.0.0 - terser-webpack-plugin@5.3.10(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)): + terser-webpack-plugin@5.3.10(@swc/core@1.4.2(@swc/helpers@0.5.2))(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.29.2 - webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12) + webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2)) optionalDependencies: '@swc/core': 1.4.2(@swc/helpers@0.5.2) - esbuild: 0.19.12 - terser-webpack-plugin@5.3.10(@swc/core@1.4.2(@swc/helpers@0.5.2))(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))): + terser-webpack-plugin@5.3.10(esbuild@0.19.12)(webpack@5.90.3(esbuild@0.19.12)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.29.2 - webpack: 5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2)) + webpack: 5.90.3(esbuild@0.19.12) optionalDependencies: - '@swc/core': 1.4.2(@swc/helpers@0.5.2) + esbuild: 0.19.12 terser@5.29.2: dependencies: @@ -18062,13 +17428,6 @@ snapshots: punycode: 1.4.1 qs: 6.11.2 - use-callback-ref@1.3.1(@types/react@18.2.65)(react@18.2.0): - dependencies: - react: 18.2.0 - tslib: 2.6.2 - optionalDependencies: - '@types/react': 18.2.65 - use-composed-ref@1.3.0(react@18.2.0): dependencies: react: 18.2.0 @@ -18091,14 +17450,6 @@ snapshots: optionalDependencies: '@types/react': 18.2.65 - use-sidecar@1.1.2(@types/react@18.2.65)(react@18.2.0): - dependencies: - detect-node-es: 1.1.0 - react: 18.2.0 - tslib: 2.6.2 - optionalDependencies: - '@types/react': 18.2.65 - use-sync-external-store@1.2.0(react@18.2.0): dependencies: react: 18.2.0 @@ -18303,7 +17654,7 @@ snapshots: - esbuild - uglify-js - webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12): + webpack@5.90.3(esbuild@0.19.12): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.5 @@ -18326,7 +17677,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)(webpack@5.90.3(@swc/core@1.4.2(@swc/helpers@0.5.2))(esbuild@0.19.12)) + terser-webpack-plugin: 5.3.10(esbuild@0.19.12)(webpack@5.90.3(esbuild@0.19.12)) watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: -- GitLab