Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • graphpolaris/frontend-v2
  • rijkheere/frontend-v-2-reordering-paoh
2 results
Show changes
Commits on Source (6)
Showing
with 486 additions and 123 deletions
......@@ -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>
......
......@@ -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"
......
......@@ -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 ? (
......
import React from 'react';
import type { SVGProps } from 'react';
export function CarbonStringInteger(props: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
<path
fill="currentColor"
d="M26 12h-4v2h4v2h-3v2h3v2h-4v2h4a2.003 2.003 0 0 0 2-2v-6a2 2 0 0 0-2-2m-7 10h-6v-4a2 2 0 0 1 2-2h2v-2h-4v-2h4a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-2v2h4zM8 20v-8H6v1H4v2h2v5H4v2h6v-2z"
></path>
</svg>
);
}
export function CarbonStringText(props: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
<path
fill="currentColor"
d="M29 22h-5a2.003 2.003 0 0 1-2-2v-6a2 2 0 0 1 2-2h5v2h-5v6h5zM18 12h-4V8h-2v14h6a2.003 2.003 0 0 0 2-2v-6a2 2 0 0 0-2-2m-4 8v-6h4v6zm-6-8H3v2h5v2H4a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h6v-8a2 2 0 0 0-2-2m0 8H4v-2h4z"
></path>
</svg>
);
}
export function CarbonCalendar(props: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
<path
fill="currentColor"
d="M26 4h-4V2h-2v2h-8V2h-2v2H6c-1.1 0-2 .9-2 2v20c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2m0 22H6V12h20zm0-16H6V6h4v2h2V6h8v2h2V6h4z"
></path>
</svg>
);
}
export function CarbonBoolean(props: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
<path fill="currentColor" d="M23 23a7 7 0 1 1 7-7a7.01 7.01 0 0 1-7 7m0-12a5 5 0 1 0 5 5a5.006 5.006 0 0 0-5-5"></path>
<circle cx={9} cy={16} r={7} fill="currentColor"></circle>
</svg>
);
}
export function CarbonUndefined(props: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
<path fill="currentColor" d="M11 14h10v4H11z"></path>
<path
fill="currentColor"
d="M29.391 14.527L17.473 2.609A2.08 2.08 0 0 0 16 2c-.533 0-1.067.203-1.473.609L2.609 14.527C2.203 14.933 2 15.466 2 16s.203 1.067.609 1.473L14.526 29.39c.407.407.941.61 1.474.61s1.067-.203 1.473-.609L29.39 17.474c.407-.407.61-.94.61-1.474s-.203-1.067-.609-1.473M16 28.036L3.965 16L16 3.964L28.036 16z"
></path>
</svg>
);
}
import React from 'react';
import { Icon } from '@graphpolaris/shared/lib/components/icon';
import { Numbers, Close } from '@mui/icons-material';
import styles from './cardtooltipvis.module.scss';
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@graphpolaris/shared/lib/components/tooltip';
import {
CarbonStringInteger,
CarbonStringText,
CarbonCalendar,
CarbonBoolean,
CarbonUndefined,
} from '@graphpolaris/shared/lib/assets/carbonIcons/carbonIcons';
export type CardToolTipVisProps = {
type: string;
name: string;
data: Record<string, any>;
typeOfSchema?: string;
colorHeader: string;
connectedTo?: string;
connectedFrom?: string;
maxVisibleItems?: number;
numberOfElements?: number;
};
const formatNumber = (number: number) => {
return number.toLocaleString('de-DE'); // Format number with dots as thousand separators
};
export const VisualizationTooltip: React.FC<CardToolTipVisProps> = ({
type,
name,
data,
colorHeader,
maxVisibleItems = 5,
connectedFrom,
connectedTo,
typeOfSchema,
numberOfElements,
}) => {
const itemsToShow = Object.entries(data).slice(0, maxVisibleItems);
return (
<div className="border-1 border-sec-200 bg-white w-[12rem] -mx-2 -my-1">
<div className="flex m-0 justify-start items-stretch border-b border-sec-200 relative">
<div className="left-0 top-0 h-auto w-1.5" style={{ backgroundColor: colorHeader }}></div>
<div className="px-2.5 py-1 truncate flex">
<Tooltip>
<TooltipTrigger className={'flex max-w-full'}>
<span className="text-base font-semibold truncate">{name}</span>
</TooltipTrigger>
<TooltipContent side={'top'}>
<span>{name}</span>
</TooltipContent>
</Tooltip>
</div>
{/*
<div className="flex-shrink-0 ml-2">
<Button variantType="secondary" variant="ghost" size="xs" rounded={true} iconComponent={<Close />} onClick={() => {}} />
</div>
*/}
</div>
{type === 'schema' && numberOfElements && (
<div className="px-4 py-1 border-b border-sec-200">
<div className="flex flex-row gap-1 items-center justify-between">
<Icon component={<Numbers />} size={24} /> <span className="ml-auto text-right">{formatNumber(numberOfElements)}</span>
</div>
</div>
)}
{type === 'schema' && typeOfSchema === 'relationship' && (
<div className="px-4 py-1 border-b border-sec-200">
<div className="flex flex-row gap-3 items-center justify-between">
<span className="font-semibold">From</span>
<span className="ml-auto text-right">{connectedFrom}</span>
</div>
<div className="flex flex-row gap-1 items-center justify-between">
<span className="font-semibold">To</span>
<span className="ml-auto text-right">{connectedTo}</span>
</div>
</div>
)}
<TooltipProvider delayDuration={300}>
<div className={`px-3 py-1.5 ${data.length > maxVisibleItems ? 'max-h-20 overflow-y-auto' : ''}`}>
{data && Object.keys(data).length === 0 ? (
<div className="flex justify-center items-center h-full">
<span>No attributes</span>
</div>
) : (
Object.entries(data).map(([k, v]) => (
<Tooltip key={k}>
<div className="flex flex-row gap-1 items-center min-h-6">
<span className={`font-semibold truncate ${type === 'schema' ? 'w-[90%]' : 'min-w-[40%]'}`}>{k}</span>
<TooltipTrigger asChild>
<span className="ml-auto text-right truncate grow-1 flex items-center">
{type === 'schema' ? (
<Icon
className="ml-auto text-right flex-shrink-0"
component={
v === 'int' || v === 'float' ? (
<CarbonStringInteger />
) : v === 'string' ? (
<CarbonStringText />
) : v === 'boolean' ? (
<CarbonBoolean />
) : v === 'date' ? (
<CarbonCalendar />
) : v === 'undefined' ? (
<CarbonUndefined />
) : (
<CarbonUndefined />
)
}
color="hsl(var(--clr-sec--400))"
size={24}
/>
) : v !== undefined && (typeof v !== 'object' || Array.isArray(v)) && v != '' ? (
<span className="ml-auto text-right truncate">{typeof v === 'number' ? formatNumber(v) : v.toString()}</span>
) : (
<div className={`ml-auto mt-auto h-4 w-12 ${styles['diagonal-lines']}`}></div>
)}
</span>
</TooltipTrigger>
<TooltipContent side="right">
<div className="max-w-[18rem] break-all line-clamp-6">
{v !== undefined && (typeof v !== 'object' || Array.isArray(v)) && v != '' ? v : 'noData'}
</div>
</TooltipContent>
</div>
</Tooltip>
))
)}
</div>
</TooltipProvider>
</div>
);
};
.diagonal-lines {
border: 1px solid lightgray;
background:
repeating-linear-gradient(-45deg, transparent, transparent 6px, #eaeaea 6px, #eaeaea 8px),
/* Gray diagonal lines */ linear-gradient(to bottom, transparent, transparent); /* Vertical gradient */
}
import React, { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { VisualizationTooltip, CardToolTipVisProps } from '@graphpolaris/shared/lib/components/CardToolTipVis';
const metaCardToolTipVis: Meta<typeof VisualizationTooltip> = {
component: VisualizationTooltip,
title: 'Components/CardToolTipVis',
};
export default metaCardToolTipVis;
type Story = StoryObj<typeof VisualizationTooltip>;
export const SchemaNode: Story = {
render: (args) => {
return (
<div className="w-1/4 my-10 m-auto flex items-center justify-center">
<VisualizationTooltip {...args} />
</div>
);
},
args: {
type: 'schema',
typeOfSchema: 'node',
name: 'Person',
data: {
born: 'int',
name: 'string',
description: 'string',
},
colorHeader: '#fb7b04',
numberOfElements: 1000,
},
};
export const SchemaRelationship: Story = {
render: (args) => {
return (
<div className="w-1/4 my-10 m-auto flex items-center justify-center">
<VisualizationTooltip {...args} />
</div>
);
},
args: {
type: 'schema',
typeOfSchema: 'relationship',
name: 'Directed',
data: {
born: 'int',
name: 'string',
description: 'string',
imdb: 'string',
imdbVotes: 'int',
},
colorHeader: '#0676C1',
connectedTo: 'Person',
numberOfElements: 231230,
connectedFrom: 'Movie',
},
};
export const PopUpVis: Story = {
render: (args) => {
return (
<div className="w-1/4 my-10 m-auto flex items-center justify-center">
<VisualizationTooltip {...args} />
</div>
);
},
args: {
name: 'Person',
type: 'popupvis',
data: {
bio: 'From wikipedia was born in usa from a firefighter father',
name: 'Charlotte Henry',
born: {},
imdbRank: 21213,
imdbVotes: 1213,
poster: 'https://image.tmdb.org/t/p/w440_and_h660_face/kTKiREs37qd8GUlNI4Koiupwy6W.jpg',
tmdbId: '94105',
country: undefined,
labels: ['Actor', 'Person', 'Human'],
},
colorHeader: '#B69AEf',
},
};
export * from './VisualizationTooltip';
......@@ -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>
......
import { StoryObj, Meta } from '@storybook/react';
import { Icon } from '../icon';
import { ArrowBack, DeleteOutline, KeyboardArrowLeft, Settings } from '@mui/icons-material';
import { ArrowBack } from '@mui/icons-material';
import { CarbonStringInteger } from '@graphpolaris/shared/lib/assets/carbonIcons/carbonIcons';
const Component: Meta<typeof Icon> = {
title: 'Components/Icon',
component: Icon,
......@@ -21,8 +21,13 @@ const Component: Meta<typeof Icon> = {
export default Component;
type Story = StoryObj<typeof Component>;
export const BaseIcon: Story = (args: any) => {
export const MUIIcon: Story = (args: any) => {
return <Icon component={<ArrowBack />} size={24} {...args} />;
};
BaseIcon.args = {};
export const CarbonIcon: Story = (args: any) => {
return <Icon component={<CarbonStringInteger />} size={24} {...args} />;
};
MUIIcon.args = {};
CarbonIcon.args = {};
import React, { ReactElement } from 'react';
import React, { ReactElement, ReactNode } from 'react';
import { SVGProps } from 'react';
// Define Sizes and IconProps types
export type Sizes = 12 | 14 | 16 | 20 | 24 | 28 | 32 | 40;
export type IconProps = SVGProps<SVGSVGElement> & {
component: ReactElement<any>;
component: ReactNode | ReactElement<any>;
size?: Sizes;
color?: string;
};
// Icon component definition
export const Icon: React.FC<IconProps> = ({ component, size = 24, color, ...props }) => {
if (!component) {
console.error(`No icon found`);
console.error('No icon found');
return <div></div>;
}
return React.cloneElement(component, { style: { fontSize: size }, width: size, height: size, ...props });
const style = { fontSize: size, color };
// Check if component is a valid React element
if (React.isValidElement(component)) {
return React.cloneElement(component as ReactElement<any>, {
style: { ...style, ...(component as ReactElement<any>).props.style },
width: size,
height: size,
...props,
});
}
// Check if component is a function (assume it's a custom SVG component)
if (typeof component === 'function') {
// Render the custom SVG component directly
return (
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 32 32" style={style} {...props}>
{(component as () => ReactNode)()}
</svg>
);
}
// Default case: render null or fallback
console.error('Unsupported icon type');
return null;
};
......@@ -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>
......
......@@ -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>
);
};
......@@ -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';
......@@ -26,6 +26,7 @@ interface TooltipOptions {
onOpenChange?: (open: boolean) => void;
boundaryElement?: React.RefObject<HTMLElement> | null;
showArrow?: boolean;
interactive?: boolean;
}
export function useTooltip({
......@@ -34,12 +35,14 @@ export function useTooltip({
open: controlledOpen,
onOpenChange: setControlledOpen,
boundaryElement = null,
showArrow = false
showArrow = false,
interactive = true,
}: TooltipOptions = {}): {
open: boolean;
setOpen: (open: boolean) => void;
interactions: ReturnType<typeof useInteractions>;
data: ReturnType<typeof useFloating>;
interactive: boolean;
} {
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen);
......@@ -57,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 }));
}
......@@ -97,8 +100,9 @@ export function useTooltip({
setOpen,
interactions: interactions,
data: data,
interactive: interactive,
}),
[open, setOpen, interactions, data],
[open, setOpen, interactions, data, interactive],
);
}
......@@ -120,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,
......@@ -202,20 +203,22 @@ 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,
display: context.data.middlewareData.hide?.referenceHidden ? 'none' : 'block',
pointerEvents: context.interactive ? 'auto' : 'none',
}}
{...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>
);
......
......@@ -13,6 +13,7 @@ import {
wsSchemaRequest,
wsSchemaSubscription,
useQuerybuilderAttributesShown,
wsSchemaStatsRequest,
} from '@graphpolaris/shared/lib/data-access';
import { Broker, wsQuerySubscription, wsQueryTranslationSubscription } from '@graphpolaris/shared/lib/data-access/broker';
import { addInfo } from '@graphpolaris/shared/lib/data-access/store/configSlice';
......@@ -207,6 +208,7 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
// New active database
if (session.currentSaveState && session.currentSaveState !== nilUUID) {
wsSchemaRequest(session.currentSaveState);
wsSchemaStatsRequest(session.currentSaveState);
wsSelectState(session.currentSaveState);
loadSaveState(session.currentSaveState, session.saveStates);
}
......
// All database related API calls
import { SchemaFromBackend } from '../../schema';
import { SchemaFromBackend, SchemaStatsFromBackend } from '../../schema';
import { Broker } from './broker';
export function wsSchemaRequest(saveStateID: string) {
......@@ -13,6 +13,7 @@ export function wsSchemaRequest(saveStateID: string) {
},
});
}
type SchemaResponse = (data: SchemaFromBackend) => void;
export function wsSchemaSubscription(callback: SchemaResponse) {
const id = Broker.instance().subscribe(callback, 'schema_result');
......@@ -20,3 +21,22 @@ export function wsSchemaSubscription(callback: SchemaResponse) {
Broker.instance().unSubscribe('schema_result', id);
};
}
export function wsSchemaStatsRequest(saveStateID: string) {
Broker.instance().sendMessage({
key: 'schema',
subKey: 'getSchemaStats',
body: {
cached: false,
saveStateID: saveStateID,
},
});
}
type SchemaStatsResponse = (data: SchemaStatsFromBackend) => void;
export function wsSchemaStatsSubscription(callback: SchemaStatsResponse) {
const id = Broker.instance().subscribe(callback, 'schema_stats_result');
return () => {
Broker.instance().unSubscribe('schema_stats_result', id);
};
}
......@@ -18,7 +18,7 @@ export function InspectorPanel(props: { children?: React.ReactNode }) {
const { activeVisualizationIndex } = useVisualization();
const inspector = useMemo(() => {
if (selection) return <SelectionConfig />;
//if (selection) return <SelectionConfig />;
// if (!focus) return <ConnectionInspector />;
// if (activeVisualizationIndex !== -1) return <ConnectionInspector />;
return <VisualizationSettings />;
......@@ -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>
</>
......
......@@ -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>
......
......@@ -6,6 +6,18 @@ export type SchemaFromBackend = {
nodes: SchemaNode[];
};
export type SchemaStatsFromBackend = {
edgeStats: {
type: string;
count: number;
}[];
nodeStats: {
key: string;
labels: string[];
count: number;
}[];
};
export type SchemaAttributeTypes = 'string' | 'float' | 'int' | 'bool' | 'date' | 'time' | 'datetime' | 'duration';
export type DimensionType = 'categorical' | 'numerical' | 'temporal' | 'spatial';
......
......@@ -34,6 +34,9 @@ export type SchemaReactflowEntity = SchemaReactflowData & {
// handles: string[];
connectedRatio: number;
name: string;
x: number;
y: number;
reactFlowRef: any;
};
export type SchemaReactflowRelation = SchemaReactflowData & {
......@@ -42,6 +45,8 @@ export type SchemaReactflowRelation = SchemaReactflowData & {
collection: string;
fromRatio: number;
toRatio: number;
x: number;
y: number;
};
export type SchemaReactflowNodeWithFunctions = SchemaReactflowEntity & {
......