From 57ec30c9892891bf658625a0bfe16bd01ec13b12 Mon Sep 17 00:00:00 2001 From: Marcos Pieras <pieras.marcos@gmail.com> Date: Thu, 11 Jul 2024 12:16:41 +0000 Subject: [PATCH] fix: fixes to tooltip positioning and search bar layout --- .../dbConnectionSelector.tsx | 8 +- .../DatabaseManagement/forms/databaseForm.tsx | 2 +- .../DatabaseManagement/forms/settings.tsx | 2 +- .../lib/components/color-mode/index.tsx | 4 +- libs/shared/lib/components/info/index.tsx | 8 +- libs/shared/lib/components/inputs/index.tsx | 34 +++-- .../shared/lib/components/tooltip/Tooltip.tsx | 144 +++++++++--------- libs/shared/lib/inspector/InspectorPanel.tsx | 2 +- .../lib/querybuilder/panel/QueryBuilder.tsx | 18 +-- libs/shared/lib/schema/panel/Schema.tsx | 8 +- libs/shared/lib/sidebar/index.tsx | 4 +- libs/shared/lib/sidebar/search/NestedItem.tsx | 35 +++++ libs/shared/lib/sidebar/search/SearchBar.tsx | 108 ++++++++----- .../vis/components/VisualizationTabBar.tsx | 6 +- libs/shared/lib/vis/views/Recommender.tsx | 6 +- .../semanticsubstratesvis/Overview.mdx | 2 +- 16 files changed, 226 insertions(+), 165 deletions(-) create mode 100644 libs/shared/lib/sidebar/search/NestedItem.tsx diff --git a/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx index aefa156fe..a1feb5608 100644 --- a/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx +++ b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx @@ -172,7 +172,7 @@ export default function DatabaseSelector({}) { onMouseEnter={() => setHovered(save.id)} onMouseLeave={() => setHovered(null)} > - <Tooltip> + <Tooltip placement={'left'}> <TooltipTrigger> <div className={`h-[8px] w-[8px] rounded-full shrink-0 ${ @@ -180,7 +180,7 @@ export default function DatabaseSelector({}) { }`} /> </TooltipTrigger> - <TooltipContent side={'left'}> + <TooltipContent> <p> {session.testedSaveState[save.id] === DatabaseStatus.tested ? 'Database connection tested' @@ -209,7 +209,7 @@ export default function DatabaseSelector({}) { <TooltipTrigger> <Settings /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Change the connection details</p> </TooltipContent> </Tooltip> @@ -232,7 +232,7 @@ export default function DatabaseSelector({}) { <TooltipTrigger> <Delete /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Delete the database</p> </TooltipContent> </Tooltip> diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx index f7186f6fc..16da3fd07 100644 --- a/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx +++ b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx @@ -47,7 +47,7 @@ export const DatabaseForm = (props: { data: SaveStateI; onChange: (data: SaveSta }, [formData]); return ( - <div className="flex flex-col gap-2"> + <div className="flex flex-col gap-4 my-2"> <Input type="text" label="Name of database connection" diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx index fb0fcb6a9..259bf1193 100644 --- a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx +++ b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx @@ -120,7 +120,7 @@ export const SettingsForm = (props: { onClose(): void; open: 'add' | 'update'; s }} > <DialogContent className="lg:min-w-[50rem]"> - <div className="flex justify-between align-center"> + <div className="flex justify-between align-center m-2"> <h2 className="text-xl font-bold">{formTitle} Database Connection</h2> <div> {sampleDataPanel === true ? ( diff --git a/libs/shared/lib/components/color-mode/index.tsx b/libs/shared/lib/components/color-mode/index.tsx index 6b43428c1..c55ceb456 100644 --- a/libs/shared/lib/components/color-mode/index.tsx +++ b/libs/shared/lib/components/color-mode/index.tsx @@ -43,11 +43,11 @@ const ColorMode = () => { return ( <TooltipProvider delayDuration={0}> - <Tooltip> + <Tooltip placement={'right'}> <TooltipTrigger asChild> <Button variant="ghost" size="sm" iconComponent={iconComponent} onClick={toggleTheme} /> </TooltipTrigger> - <TooltipContent side={'right'}> + <TooltipContent> <p>{`Switch to ${theme === 'dark-mode' ? 'light' : 'dark'}-mode`}</p> </TooltipContent> </Tooltip> diff --git a/libs/shared/lib/components/info/index.tsx b/libs/shared/lib/components/info/index.tsx index 20b158aae..32c5d2ba3 100644 --- a/libs/shared/lib/components/info/index.tsx +++ b/libs/shared/lib/components/info/index.tsx @@ -5,16 +5,16 @@ import { InfoOutlined } from '@mui/icons-material'; type Props = { tooltip: string; - side?: 'top' | 'bottom' | 'left' | 'right'; + placement?: 'top' | 'bottom' | 'left' | 'right'; }; -export default function Info({ tooltip, side = 'left' }: Props) { +export default function Info({ tooltip, placement = 'left' }: Props) { return ( - <Tooltip> + <Tooltip placement={placement}> <TooltipTrigger> <Icon component={<InfoOutlined />} size={14} /> </TooltipTrigger> - <TooltipContent side={side}> + <TooltipContent> <p>{tooltip}</p> </TooltipContent> </Tooltip> diff --git a/libs/shared/lib/components/inputs/index.tsx b/libs/shared/lib/components/inputs/index.tsx index df18203eb..db355bb74 100644 --- a/libs/shared/lib/components/inputs/index.tsx +++ b/libs/shared/lib/components/inputs/index.tsx @@ -96,7 +96,7 @@ type DropdownProps = { disabled?: boolean; className?: string; value?: number | string; - options: number[] | string[] | {[key: string]: string}[]; + options: number[] | string[] | { [key: string]: string }[]; onChange?: (value: number | string) => void; }; @@ -153,7 +153,7 @@ export const SliderInput = ({ label, value, min, max, step, unit, showValue = tr /> </div> </TooltipTrigger> - {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + {tooltip && <TooltipContent>{tooltip}</TooltipContent>} </Tooltip> ); }; @@ -188,7 +188,7 @@ export const TextInput = ({ {label} </span> {required && isValid ? null : <span className="label-text-alt text-error">{errorText}</span>} - {info && <Info tooltip={info} side={'left'} />} + {info && <Info tooltip={info} placement={'left'} />} </label> )} <input @@ -212,7 +212,7 @@ export const TextInput = ({ disabled={disabled} /> </TooltipTrigger> - {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + {tooltip && <TooltipContent>{tooltip}</TooltipContent>} </Tooltip> ); }; @@ -248,7 +248,7 @@ export const NumberInput = ({ {label} </span> {required && isValid ? null : <span className="label-text-alt text-error">{errorText}</span>} - {info && <Info tooltip={info} side={'left'} />} + {info && <Info tooltip={info} placement={'left'} />} </label> <input type="number" @@ -272,7 +272,7 @@ export const NumberInput = ({ min={min} /> </TooltipTrigger> - {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + {tooltip && <TooltipContent>{tooltip}</TooltipContent>} </Tooltip> ); }; @@ -301,7 +301,7 @@ export const RadioInput = ({ label, value, options, onChange, tooltip }: RadioPr </label> ))} </TooltipTrigger> - {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + {tooltip && <TooltipContent>{tooltip}</TooltipContent>} </Tooltip> ); }; @@ -343,7 +343,7 @@ export const CheckboxInput = ({ label, value, options, onChange, tooltip }: Chec </label> ))} </TooltipTrigger> - {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + {tooltip && <TooltipContent>{tooltip}</TooltipContent>} </Tooltip> ); }; @@ -366,23 +366,25 @@ export const BooleanInput = ({ label, value, onChange, tooltip }: BooleanProps) /> </label> </TooltipTrigger> - {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + {tooltip && <TooltipContent>{tooltip}</TooltipContent>} </Tooltip> ); }; -function parsedValue(item: number | string | {[key: string]: string}, key: boolean = false) { +function parsedValue(item: number | string | { [key: string]: string }, key: boolean = false) { switch (typeof item) { - case 'number': return item.toString(); - case 'object': return key ? Object.keys(item)[0] : Object.values(item)[0]; + case 'number': + return item.toString(); + case 'object': + return key ? Object.keys(item)[0] : Object.values(item)[0]; } return item; } -function currentValue(value: string | number | undefined, options?: {[key: string]: string}[]) { +function currentValue(value: string | number | undefined, options?: { [key: string]: string }[]) { if (options != null && typeof options[0] == 'object') { - return parsedValue(options.find(x => x[value as string]) as {[key: string]: string}); + return parsedValue(options.find((x) => x[value as string]) as { [key: string]: string }); } return value; @@ -428,7 +430,7 @@ export const DropdownInput = ({ > <DropdownTrigger variant={buttonVariant} - title={overrideRender || currentValue(value, options as {[key: string]: string}[]) } + title={overrideRender || currentValue(value, options as { [key: string]: string }[])} size={size} className="cursor-pointer" disabled={disabled} @@ -458,7 +460,7 @@ export const DropdownInput = ({ )} </DropdownContainer> </TooltipTrigger> - {tooltip && <TooltipContent side={'top'}>{tooltip}</TooltipContent>} + {tooltip && <TooltipContent>{tooltip}</TooltipContent>} </Tooltip> ); }; diff --git a/libs/shared/lib/components/tooltip/Tooltip.tsx b/libs/shared/lib/components/tooltip/Tooltip.tsx index 0a0676f98..e6098a986 100644 --- a/libs/shared/lib/components/tooltip/Tooltip.tsx +++ b/libs/shared/lib/components/tooltip/Tooltip.tsx @@ -14,7 +14,7 @@ import { useInteractions, useMergeRefs, FloatingPortal, - FloatingArrow + FloatingArrow, } from '@floating-ui/react'; import type { Placement } from '@floating-ui/react'; import { FloatingDelayGroup } from '@floating-ui/react'; @@ -60,16 +60,16 @@ export function useTooltip({ flip({ crossAxis: placement.includes('-'), fallbackAxisSideDirection: 'start', - padding: 5 + padding: 5, }), shift({ padding: 5 }), ], - } + }; if (boundaryElement != null) { const boundary = boundaryElement?.current ?? undefined; - config.middleware.find(x => x.name == 'flip')!.options[0].boundary = boundary; - config.middleware.find(x => x.name == 'shift')!.options[0].boundary = boundary; + config.middleware.find((x) => x.name == 'flip')!.options[0].boundary = boundary; + config.middleware.find((x) => x.name == 'shift')!.options[0].boundary = boundary; config.middleware.push(hide({ boundary })); } @@ -124,72 +124,69 @@ export function Tooltip({ children, ...options }: { children: React.ReactNode } // 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>; + + return <TooltipContext.Provider value={tooltip}>{children}</TooltipContext.Provider>; } -export const TooltipTrigger = React.forwardRef<HTMLElement, React.HTMLProps<HTMLElement> & { asChild?: boolean, x?: number, y?: number }>(function TooltipTrigger( - { children, asChild = false, x = null, y = null, ...props }, - propRef, -) { - const context = useTooltipContext(); - const childrenRef = React.useMemo(() => { - if (children == null) { - return null; - } else { - return (children as any).ref; - } - }, [children]); - - const ref = useMergeRefs([context.data.refs.setReference, propRef, childrenRef]); - - React.useEffect(() => { - if (x && y && context.data.refs.reference.current != null) { - const element = context.data.refs.reference.current as HTMLElement; - element.style.position = 'absolute'; - const {x: offsetX, y: offsetY} = element.getBoundingClientRect(); - element.getBoundingClientRect = () => { - return { - width: 0, - height: 0, - x: offsetX, - y: offsetY, - top: y + offsetY, - left: x + offsetX, - right: x + offsetX, - bottom: y + offsetY, - } as DOMRect +export const TooltipTrigger = React.forwardRef<HTMLElement, React.HTMLProps<HTMLElement> & { asChild?: boolean; x?: number; y?: number }>( + function TooltipTrigger({ children, asChild = false, x = null, y = null, ...props }, propRef) { + const context = useTooltipContext(); + const childrenRef = React.useMemo(() => { + if (children == null) { + return null; + } else { + return (children as any).ref; } - context.data.update(); + }, [children]); + + const ref = useMergeRefs([context.data.refs.setReference, propRef, childrenRef]); + + React.useEffect(() => { + if (x && y && context.data.refs.reference.current != null) { + const element = context.data.refs.reference.current as HTMLElement; + element.style.position = 'absolute'; + const { x: offsetX, y: offsetY } = element.getBoundingClientRect(); + element.getBoundingClientRect = () => { + return { + width: 0, + height: 0, + x: offsetX, + y: offsetY, + top: y + offsetY, + left: x + offsetX, + right: x + offsetX, + bottom: y + offsetY, + } as DOMRect; + }; + context.data.update(); + } + }, [x, y]); + + // `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', + }), + ); } - }, [x, y]); - - // `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> - ); -}); + 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, @@ -206,7 +203,10 @@ export const TooltipContent = React.forwardRef< <FloatingPortal> <div ref={ref} - className={`z-50 max-w-64 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}` : ''}`} + className={`z-50 max-w-64 rounded bg-light px-2 py-2 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, @@ -215,12 +215,10 @@ export const TooltipContent = React.forwardRef< }} {...context.interactions.getFloatingProps(props)} > - { props.children } - { context.data.middlewareData.arrow ? <FloatingArrow - ref={(context.data.refs as any).arrow} - context={context.data.context} - style={{fill: 'white'}} - /> : null } + {props.children} + {context.data.middlewareData.arrow ? ( + <FloatingArrow ref={(context.data.refs as any).arrow} context={context.data.context} style={{ fill: 'white' }} /> + ) : null} </div> </FloatingPortal> ); diff --git a/libs/shared/lib/inspector/InspectorPanel.tsx b/libs/shared/lib/inspector/InspectorPanel.tsx index c5532a14c..84257549e 100644 --- a/libs/shared/lib/inspector/InspectorPanel.tsx +++ b/libs/shared/lib/inspector/InspectorPanel.tsx @@ -38,7 +38,7 @@ export function InspectorPanel(props: { children?: React.ReactNode }) { {focus && ( <> <span className="pb-0.5">{'>'}</span> - <Button variant="ghost" size="2xs" className="hover:underline p-0"> + <Button variant="ghost" size="2xs" className="hover:underline p-0 capitalize"> {focus.focusType} </Button> </> diff --git a/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx b/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx index a54e5e213..675f1ba4f 100644 --- a/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx +++ b/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx @@ -474,14 +474,14 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <div className="flex items-center"> <h1 className="text-xs font-semibold text-secondary-600 px-2 truncate">Query builder</h1> </div> - <div className="shrink-0 sticky right-0 px-0.5 ml-auto items-center flex"> + <div className="sticky right-0 px-0.5 ml-auto items-center flex truncate"> <ControlContainer> <TooltipProvider delayDuration={0}> <Tooltip> <TooltipTrigger> <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={fitView} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Fit to screen</p> </TooltipContent> </Tooltip> @@ -489,7 +489,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <TooltipTrigger> <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Delete />} onClick={() => clearAllNodes()} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Clear query panel</p> </TooltipContent> </Tooltip> @@ -505,7 +505,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { }} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Capture screen</p> </TooltipContent> </Tooltip> @@ -522,7 +522,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { }} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Layouts</p> </TooltipContent> </Tooltip> @@ -532,7 +532,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <TooltipTrigger> <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Settings />} className="query-settings" /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Query builder settings</p> </TooltipContent> </Tooltip> @@ -554,7 +554,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { }} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Rerun query</p> </TooltipContent> </Tooltip> @@ -572,7 +572,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { }} /> </TooltipTrigger> - <TooltipContent side={'top'} disabled={toggleSettings === 'logic'}> + <TooltipContent disabled={toggleSettings === 'logic'}> <p>Logic settings</p> </TooltipContent> </Tooltip> @@ -582,7 +582,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => { <TooltipTrigger> <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Lightbulb />} /> </TooltipTrigger> - <TooltipContent side={'top'} disabled={toggleSettings === 'ml'}> + <TooltipContent disabled={toggleSettings === 'ml'}> <p>Machine learning</p> </TooltipContent> </Tooltip> diff --git a/libs/shared/lib/schema/panel/Schema.tsx b/libs/shared/lib/schema/panel/Schema.tsx index 6bc23f973..ff7da823e 100644 --- a/libs/shared/lib/schema/panel/Schema.tsx +++ b/libs/shared/lib/schema/panel/Schema.tsx @@ -132,7 +132,7 @@ export const Schema = (props: Props) => { }} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Hide</p> </TooltipContent> </Tooltip> @@ -149,7 +149,7 @@ export const Schema = (props: Props) => { }} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Copy Schema to Clipboard</p> </TooltipContent> </Tooltip> @@ -157,7 +157,7 @@ export const Schema = (props: Props) => { <TooltipTrigger> <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<FitScreen />} onClick={() => {}} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Fit to screen</p> </TooltipContent> </Tooltip> @@ -167,7 +167,7 @@ export const Schema = (props: Props) => { <TooltipTrigger> <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Settings />} className="schema-settings" /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Schema settings</p> </TooltipContent> </Tooltip> diff --git a/libs/shared/lib/sidebar/index.tsx b/libs/shared/lib/sidebar/index.tsx index 6648d2195..9c8e66ed5 100644 --- a/libs/shared/lib/sidebar/index.tsx +++ b/libs/shared/lib/sidebar/index.tsx @@ -21,7 +21,7 @@ export function Sidebar({ onTab, tab }: { onTab: (tab: SideNavTab) => void; tab: <TooltipProvider delayDuration={100}> <div className="w-11 flex flex-col items-center"> {tabs.map((t) => ( - <Tooltip key={t.name}> + <Tooltip key={t.name} placement={'right'}> <TooltipTrigger asChild> <Button variantType="secondary" @@ -38,7 +38,7 @@ export function Sidebar({ onTab, tab }: { onTab: (tab: SideNavTab) => void; tab: className={tab === t.name ? 'bg-secondary-100' : ''} /> </TooltipTrigger> - <TooltipContent side={'right'}>{t.name}</TooltipContent> + <TooltipContent>{t.name}</TooltipContent> </Tooltip> ))} <div className="mt-auto mb-2"> diff --git a/libs/shared/lib/sidebar/search/NestedItem.tsx b/libs/shared/lib/sidebar/search/NestedItem.tsx new file mode 100644 index 000000000..5ba243d80 --- /dev/null +++ b/libs/shared/lib/sidebar/search/NestedItem.tsx @@ -0,0 +1,35 @@ +import React from 'react'; + +type NestedItemProps = { + keyName: string; + value: Record<string, any>; +}; + +export const NestedItem = ({ keyName, value }: NestedItemProps) => { + const numberOfMaxElementsShow = 9; + + return ( + <div> + <span className="font-bold">{keyName}:</span> + {Object.keys(value).map((keyInside, indexInside) => ( + <div key={`tooltipItem_${keyName}_${keyInside}`} className="mx-2"> + <span className="font-bold">{keyInside}:</span> + {typeof value[keyInside] === 'object' ? ( + <div> + {Object.keys(value[keyInside]) + .slice(0, numberOfMaxElementsShow) + .map((keyInside2) => ( + <div key={`tooltipItem_${keyName}_${keyInside}_${keyInside2}`} className="mx-2"> + <span className="font-bold">{keyInside2}:</span> + <span className="truncate"> {JSON.stringify(value[keyInside][keyInside2])}</span> + </div> + ))} + </div> + ) : ( + <span> {value[keyInside]}</span> + )} + </div> + ))} + </div> + ); +}; diff --git a/libs/shared/lib/sidebar/search/SearchBar.tsx b/libs/shared/lib/sidebar/search/SearchBar.tsx index c8bdae451..39a3bff10 100644 --- a/libs/shared/lib/sidebar/search/SearchBar.tsx +++ b/libs/shared/lib/sidebar/search/SearchBar.tsx @@ -19,6 +19,7 @@ import { import { filterData } from './similarity'; import { Button, Panel, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components'; import { Remove, Fullscreen } from '@mui/icons-material'; +import { NestedItem } from './NestedItem'; const SIMILARITY_THRESHOLD = 0.7; @@ -125,7 +126,7 @@ export function SearchBar(props: { onRemove?: () => void }) { }} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Hide</p> </TooltipContent> </Tooltip> @@ -133,15 +134,15 @@ export function SearchBar(props: { onRemove?: () => void }) { <TooltipTrigger asChild> <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={() => {}} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Mock icon</p> </TooltipContent> </Tooltip> </TooltipProvider> } > - <div className="flex flex-col w-full p-2"> - <div className="w-full"> + <div className="flex flex-col w-full h-full"> + <div className="w-full mb-2"> <input type="text" ref={inputRef} @@ -150,21 +151,21 @@ export function SearchBar(props: { onRemove?: () => void }) { id="input-group-search" className="block w-full p-2 ps-2 text-sm text-secondary-900 border border-secondary-300 rounded bg-secondary-50 focus:ring-blue-500 focus:border-blue-500 focus:ring-0" placeholder="Search database" - ></input> + /> </div> - <div> - {recentSearches.length !== 0 && ( - <div className="px-3 pb-3"> - <p className="text-sm">Recent searches</p> - {recentSearches.slice(0, 3).map((term) => ( - <p key={term} className="ml-1 text-sm text-secondary-500 cursor-pointer" onClick={() => setSearch(term)}> - {term} - </p> - ))} - </div> - )} + {recentSearches.length !== 0 && ( + <div className="px-3 pb-3"> + <p className="text-sm">Recent searches</p> + {recentSearches.slice(0, 3).map((term, index) => ( + <p key={`recent-${term}-${index}`} className="ml-1 text-sm text-secondary-500 cursor-pointer" onClick={() => setSearch(term)}> + {term} + </p> + ))} + </div> + )} + <div className="flex-grow overflow-y-auto"> {search !== '' && ( - <div className="z-10 w-full overflow-y-auto scroll h-full px-2 pb-2"> + <div className="z-10 w-full h-full overflow-y-auto px-2 pb-2"> {SEARCH_CATEGORIES.every((category) => results[category].nodes.length === 0 && results[category].edges.length === 0) ? ( <div className="ml-1 text-sm"> <p className="text-secondary-500">Found no matches...</p> @@ -173,38 +174,63 @@ export function SearchBar(props: { onRemove?: () => void }) { SEARCH_CATEGORIES.map((category, index) => { if (results[category].nodes.length > 0 || results[category].edges.length > 0) { return ( - <div key={index}> + <div key={`results_${index}`} className="mt-4"> <div className="flex justify-between p-2 text-lg"> <p className="font-bold text-sm">{category.charAt(0).toUpperCase() + category.slice(1)}</p> <p className="font-bold text-sm">{results[category].nodes.length + results[category].edges.length} results</p> </div> <div className="h-[1px] w-full bg-secondary-200"></div> - {Object.values(Object.values(results[category])) - .flat() - .map((item, index) => ( - <div - key={index} - className="flex flex-col hover:bg-secondary-300 px-2 py-1 cursor-pointer rounded ml-2" - title={JSON.stringify(item)} - onClick={() => { - CATEGORY_ACTIONS[category]( - { - nodes: results[category].nodes.includes(item) ? [item] : [], - edges: results[category].edges.includes(item) ? [item] : [], - }, - dispatch, - ); - }} - > - <div className="font-bold text-sm"> - {item?.key?.slice(0, 18) || item?.id?.slice(0, 18) || Object.values(item)?.[0]?.slice(0, 18)} + <TooltipProvider delayDuration={300}> + {Object.values(Object.values(results[category])) + .flat() + .map((item, idx) => ( + <div + key={`${category}-${item.id}-${idx}`} + className="flex flex-col hover:bg-secondary-200 px-2 py-1 cursor-pointer rounded ml-2" + onClick={() => { + CATEGORY_ACTIONS[category]( + { + nodes: results[category].nodes.includes(item) ? [item] : [], + edges: results[category].edges.includes(item) ? [item] : [], + }, + dispatch, + ); + }} + > + <div className="font-bold text-sm"> + {item?.key?.slice(0, 18) || item?.id?.slice(0, 18) || Object.values(item)?.[0]?.slice(0, 18)} + </div> + <Tooltip key={`tooltip_${category}-${item.id}-${idx}`} placement={'bottom'}> + <TooltipTrigger asChild> + <div className="font-light text-secondary-800 text-xs truncate">{JSON.stringify(item)}</div> + </TooltipTrigger> + <TooltipContent> + <div className="truncate"> + {Object.keys(item).map((key, idx) => ( + <div key={`tooltipItem_${idx}`} className="mx-2"> + {!(typeof item[key] === 'object' && item[key] !== null) ? ( + <div> + <span className="font-bold">{key}:</span> + <span className="truncate"> + {' '} + {key === 'similarity' ? Math.round(item[key] * 1000) / 1000 : item[key]} + </span> + </div> + ) : ( + <NestedItem keyName={key} value={item[key]} /> + )} + </div> + ))} + </div> + </TooltipContent> + </Tooltip> </div> - <div className="font-light text-secondary-800 text-xs">{JSON.stringify(item).substring(0, 40)}...</div> - </div> - ))} + ))} + </TooltipProvider> </div> ); - } else return <></>; + } + return null; }) )} </div> diff --git a/libs/shared/lib/vis/components/VisualizationTabBar.tsx b/libs/shared/lib/vis/components/VisualizationTabBar.tsx index c370b2265..b1b038411 100644 --- a/libs/shared/lib/vis/components/VisualizationTabBar.tsx +++ b/libs/shared/lib/vis/components/VisualizationTabBar.tsx @@ -71,7 +71,7 @@ export default function VisualizationTabBar(props: { fullSize: () => void }) { ); })} </Tabs> - <div className="items-center shrink-0 px-0.5"> + <div className="flex items-center shrink-0 px-0.5"> <TooltipProvider delayDuration={0}> <Tooltip> <TooltipTrigger> @@ -96,7 +96,7 @@ export default function VisualizationTabBar(props: { fullSize: () => void }) { </DropdownItemContainer> </DropdownContainer> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Add visualization</p> </TooltipContent> </Tooltip> @@ -109,7 +109,7 @@ export default function VisualizationTabBar(props: { fullSize: () => void }) { <TooltipTrigger asChild> <Button variantType="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={props.fullSize} /> </TooltipTrigger> - <TooltipContent side={'top'}> + <TooltipContent> <p>Full screen</p> </TooltipContent> </Tooltip> diff --git a/libs/shared/lib/vis/views/Recommender.tsx b/libs/shared/lib/vis/views/Recommender.tsx index a021353d0..1082b836a 100644 --- a/libs/shared/lib/vis/views/Recommender.tsx +++ b/libs/shared/lib/vis/views/Recommender.tsx @@ -10,7 +10,7 @@ export function Recommender() { return ( <div className="p-4"> <span className="text-md">Select a visualization</span> - <div className="grid grid-cols-3 gap-4"> + <div className="grid grid-cols-3 gap-4 my-2"> {Object.keys(Visualizations).map((name) => ( <div key={name} @@ -22,8 +22,8 @@ export function Recommender() { }} > <div className="flex items-center justify-between"> - <span className="text-sm font-semibold">{name}</span> - <Info tooltip="Here an explanation" side="top" /> + <span className="text-sm font-semibold truncate">{name}</span> + <Info tooltip="Here an explanation" placement="top" /> </div> {/* <image src={image} /> */} </div> diff --git a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/Overview.mdx b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/Overview.mdx index 2ae6f0b46..ad9c61362 100644 --- a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/Overview.mdx +++ b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/Overview.mdx @@ -1,6 +1,6 @@ import { Meta, Unstyled } from '@storybook/blocks'; -<Meta title="Visualizations/Implementation" /> +<Meta title="Visualizations/SemanticSubstrates" /> # Variables -- GitLab