diff --git a/.vscode/settings.json b/.vscode/settings.json index e3a04a028..ef64c2dc5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,10 @@ }, "typescript.tsdk": "node_modules/typescript/lib", "vitest.workspaceConfig": "vitest.workspace.ts", + "editor.quickSuggestions": { + "strings": "on" + }, + "tailwindCSS.rootFontSize": 14, "tailwindCSS.experimental.classRegex": [ ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], ["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] diff --git a/Changelog.md b/Changelog.md index ee4c8e588..53edc5ad4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,10 @@ - **Improved** UI responsiveness by using map instead of array for large data sets [#658](https://github.com/aws/graph-explorer/pull/658) +- **Improved** connection selection can now happen on any part of the connection + row [#657](https://github.com/aws/graph-explorer/pull/657) +- **Fixed** scrolling on search result details + [#657](https://github.com/aws/graph-explorer/pull/657) - **Improved** style for the sidebar buttons [#651](https://github.com/aws/graph-explorer/pull/651) - **Fixed** Docker image containing more files than necessary. diff --git a/packages/graph-explorer/package.json b/packages/graph-explorer/package.json index 7e089f1ab..9d9750687 100644 --- a/packages/graph-explorer/package.json +++ b/packages/graph-explorer/package.json @@ -47,6 +47,7 @@ "@react-stately/searchfield": "^3.5.7", "@tanstack/react-query": "^5.59.9", "@tanstack/react-query-devtools": "^5.59.9", + "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "color": "^4.2.3", "crypto-js": "^4.2.0", diff --git a/packages/graph-explorer/src/components/AdvancedList/AdvancedList.styles.tsx b/packages/graph-explorer/src/components/AdvancedList/AdvancedList.styles.tsx index 7e0a2f551..f18983d5b 100644 --- a/packages/graph-explorer/src/components/AdvancedList/AdvancedList.styles.tsx +++ b/packages/graph-explorer/src/components/AdvancedList/AdvancedList.styles.tsx @@ -28,13 +28,6 @@ const listStyles: ThemeStyleFn = ({ theme, isDarkTheme }) => { .advanced-list-search-wrapper { display: flex; align-items: center; - .advanced-list-search-input { - flex: 2; - } - - .advanced-list-category-select { - flex: 1; - } .select { min-width: 0; diff --git a/packages/graph-explorer/src/components/AdvancedList/AdvancedList.tsx b/packages/graph-explorer/src/components/AdvancedList/AdvancedList.tsx index 3ead8e75f..b77ba8da2 100644 --- a/packages/graph-explorer/src/components/AdvancedList/AdvancedList.tsx +++ b/packages/graph-explorer/src/components/AdvancedList/AdvancedList.tsx @@ -54,17 +54,17 @@ export type AdvancedListProps = { category?: string; onItemClick?: ( event: MouseEvent, - item: AdvancedListItemType, + item: AdvancedListItemType, index: number ) => void; onItemMouseOver?: ( event: MouseEvent, - item: AdvancedListItemType, + item: AdvancedListItemType, index: number ) => void; onItemMouseOut?: ( event: MouseEvent, - item: AdvancedListItemType, + item: AdvancedListItemType, index: number ) => void; onItemMouseEnter?: AdvancedListMouseEvent; @@ -83,7 +83,7 @@ export type AdvancedListProps = { hideCount?: boolean; hideEmptyState?: boolean; renderPopover?: ( - item: AdvancedListItemType, + item: AdvancedListItemType, itemRef: RefObject ) => ReactNode; hidePopover?: boolean; diff --git a/packages/graph-explorer/src/components/AdvancedList/internalComponents/AdvancedListWithGroups.tsx b/packages/graph-explorer/src/components/AdvancedList/internalComponents/AdvancedListWithGroups.tsx index e00893632..b436db773 100644 --- a/packages/graph-explorer/src/components/AdvancedList/internalComponents/AdvancedListWithGroups.tsx +++ b/packages/graph-explorer/src/components/AdvancedList/internalComponents/AdvancedListWithGroups.tsx @@ -1,6 +1,14 @@ import { cn } from "@/utils"; import groupBy from "lodash/groupBy"; -import type { DragEvent, MouseEvent, ReactNode, Ref, RefObject } from "react"; +import type { + DragEvent, + ForwardedRef, + MouseEvent, + PropsWithChildren, + ReactNode, + Ref, + RefObject, +} from "react"; import { forwardRef, memo, useEffect, useMemo, useState } from "react"; import type { VirtuosoHandle } from "react-virtuoso"; import { GroupedVirtuoso } from "react-virtuoso"; @@ -305,4 +313,8 @@ const AdvancedListWithGroups = ( ); }; -export default memo(forwardRef(AdvancedListWithGroups)); +export default memo(forwardRef(AdvancedListWithGroups)) as ( + props: PropsWithChildren> & { + ref?: ForwardedRef; + } +) => ReturnType; diff --git a/packages/graph-explorer/src/components/AdvancedList/internalComponents/SearchBar.tsx b/packages/graph-explorer/src/components/AdvancedList/internalComponents/SearchBar.tsx index 96604f67a..aaec2339c 100644 --- a/packages/graph-explorer/src/components/AdvancedList/internalComponents/SearchBar.tsx +++ b/packages/graph-explorer/src/components/AdvancedList/internalComponents/SearchBar.tsx @@ -24,7 +24,7 @@ const SearchBar = ({ return ( <> onTypeChange?.(value as string)} diff --git a/packages/graph-explorer/src/components/Card/Card.tsx b/packages/graph-explorer/src/components/Card/Card.tsx index 3b450a4f4..c743e3151 100644 --- a/packages/graph-explorer/src/components/Card/Card.tsx +++ b/packages/graph-explorer/src/components/Card/Card.tsx @@ -17,7 +17,7 @@ export interface CardProps onClick?: () => void; } -export const Card = ( +export function Card( { id, className, @@ -29,7 +29,7 @@ export const Card = ( ...restProps }: PropsWithChildren, ref: ForwardedRef -) => { +) { const styleWithTheme = useWithTheme(); return (
); -}; +} export default forwardRef>(Card); diff --git a/packages/graph-explorer/src/components/Carousel/Carousel.styles.ts b/packages/graph-explorer/src/components/Carousel/Carousel.styles.ts index 0f9689d31..1eb9fa64e 100644 --- a/packages/graph-explorer/src/components/Carousel/Carousel.styles.ts +++ b/packages/graph-explorer/src/components/Carousel/Carousel.styles.ts @@ -1,14 +1,6 @@ import { css } from "@emotion/css"; import { ActiveThemeType, fade, ProcessedTheme } from "@/core"; -export const defaultStyles = () => css` - display: flex; - .swiper { - height: 484px; - width: 320px; - } -`; - export const navArrowsStyles = ({ theme, }: ActiveThemeType) => css` diff --git a/packages/graph-explorer/src/components/Carousel/Carousel.tsx b/packages/graph-explorer/src/components/Carousel/Carousel.tsx index 5bea64ae4..a0122fe89 100644 --- a/packages/graph-explorer/src/components/Carousel/Carousel.tsx +++ b/packages/graph-explorer/src/components/Carousel/Carousel.tsx @@ -25,7 +25,7 @@ import { } from "swiper/types"; import { useWithTheme } from "@/core"; import { ChevronLeftIcon, ChevronRightIcon } from "@/components/icons"; -import { defaultStyles, navArrowsStyles } from "./Carousel.styles"; +import { navArrowsStyles } from "./Carousel.styles"; export interface CarouselProps { className?: string; @@ -38,7 +38,6 @@ export interface CarouselProps { prevArrow?: JSX.Element; slidesToShow?: number; swipe?: boolean; - variableWidth?: boolean; } const PrevArrow = forwardRef< @@ -99,7 +98,6 @@ export const Carousel = forwardRef< slidesToShow = 5, centerMode, pagination, - ...props }, ref ) => { @@ -110,19 +108,14 @@ export const Carousel = forwardRef< () => Children.map(children, (child, index) => { return ( - + {child} ); }), - [children, props.variableWidth] + [children] ); - const stylesWithTheme = useWithTheme(); - const [swiper, setSwiper] = useState(undefined); useImperativeHandle(ref, () => swiper, [swiper]); @@ -147,7 +140,7 @@ export const Carousel = forwardRef< } return ( -
+
{childrenComputed} diff --git a/packages/graph-explorer/src/components/Chip/Chip.styles.ts b/packages/graph-explorer/src/components/Chip/Chip.styles.ts deleted file mode 100644 index 7ae2955dd..000000000 --- a/packages/graph-explorer/src/components/Chip/Chip.styles.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { css } from "@emotion/css"; -import type { ThemeStyleFn } from "@/core"; -import { fade } from "@/core"; - -const heightMap = { - xs: "18px", - sm: "22px", - md: "26px", - lg: "30px", -}; - -const defaultStyles = - ( - variant: "info" | "success" | "error" | "warning", - background?: string, - color?: string, - size?: "xs" | "sm" | "md" | "lg" - ): ThemeStyleFn => - ({ theme }) => css` - display: inline-flex; - border-radius: 16px; - align-items: center; - padding: ${theme.spacing.base || "4px"}; - height: ${heightMap[size || "md"]}; - font-size: ${theme.typography.sizes?.[ - size === "md" ? "base" : size || "sm" - ]}; - color: ${color - ? color - : theme.chip?.variants?.[variant]?.text || theme.palette.common.white}; - background-color: ${background - ? background - : theme.chip?.variants?.[variant]?.background || - theme.palette[variant === "info" ? "primary" : variant].main}; - - &.chip-clickable { - cursor: pointer; - &:hover { - color: ${color - ? color - : theme.chip?.variants?.[variant]?.clickable?.hover?.text || - theme.palette.common.white}; - background-color: ${background - ? background - : theme.chip?.variants?.[variant]?.clickable?.hover?.background || - fade( - theme.palette[variant === "info" ? "primary" : variant].main, - 0.15 - )}; - } - } - .chip-label { - overflow: hidden; - text-overflow: ellipsis; - padding: 0 ${theme.spacing.base}; - font-weight: ${theme.typography.weight.base}; - white-space: nowrap; - } - - .icon-delete { - padding: 0; - color: ${fade(theme.palette.grey[600], 0.7)}; - - &:hover { - transition: 0.3s; - color: ${theme.palette.grey[600]}; - } - } - - > svg { - font-size: ${theme.spacing["5x"]}; - } - `; - -export default defaultStyles; diff --git a/packages/graph-explorer/src/components/Chip/Chip.tsx b/packages/graph-explorer/src/components/Chip/Chip.tsx index ff8a507f4..8320c14ee 100644 --- a/packages/graph-explorer/src/components/Chip/Chip.tsx +++ b/packages/graph-explorer/src/components/Chip/Chip.tsx @@ -1,75 +1,38 @@ import { cn } from "@/utils"; -import type { - ForwardedRef, - HTMLAttributes, - PropsWithChildren, - ReactNode, -} from "react"; +import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from "react"; import { forwardRef } from "react"; -import { useWithTheme } from "@/core"; -import IconButton from "../IconButton"; -import CloseIcon from "@/components/icons/CloseIcon"; -import defaultStyles from "./Chip.styles"; +import { cva, VariantProps } from "class-variance-authority"; -export interface ChipProps extends HTMLAttributes { - className?: string; - /* Takes precedence over variants and theme*/ - color?: string; - /* Takes precedence over variants and theme*/ - background?: string; - startAdornment?: ReactNode; - endAdornment?: ReactNode; - deleteIcon?: ReactNode; - onDelete?(): void; - size?: "xs" | "sm" | "md" | "lg"; - variant?: "info" | "success" | "error" | "warning"; -} +const chip = cva( + [ + "chip font-base inline-flex h-[22px] items-center select-none justify-center gap-1 overflow-hidden text-ellipsis whitespace-nowrap rounded-full px-2.5 text-sm text-white [&>svg]:size-4", + ], + { + variants: { + variant: { + info: "bg-info-main", + success: "bg-success-main", + error: "bg-error-main", + warning: "bg-warning-main", + }, + }, + defaultVariants: { + variant: "info", + }, + } +); + +export interface ChipProps + extends HTMLAttributes, + VariantProps {} export const Chip = ( - { - children, - className, - color, - background, - size = "sm", - variant = "info", - startAdornment, - endAdornment, - onDelete, - deleteIcon, - ...allProps - }: PropsWithChildren, + { children, className, variant, ...allProps }: PropsWithChildren, ref: ForwardedRef ) => { - const styleWithTheme = useWithTheme(); - return ( -
- {startAdornment} - {children} - {endAdornment} - {onDelete && ( - } - variant="text" - size="small" - /> - )} +
+ {children}
); }; diff --git a/packages/graph-explorer/src/components/Chip/index.ts b/packages/graph-explorer/src/components/Chip/index.ts index b6c6e9ebb..4729fe635 100644 --- a/packages/graph-explorer/src/components/Chip/index.ts +++ b/packages/graph-explorer/src/components/Chip/index.ts @@ -1,3 +1,2 @@ export { default } from "./Chip"; -export { default as useChipColor } from "./useChipColor"; export type { ChipProps } from "./Chip"; diff --git a/packages/graph-explorer/src/components/Chip/useChipColor.tsx b/packages/graph-explorer/src/components/Chip/useChipColor.tsx deleted file mode 100644 index 9bfd2e349..000000000 --- a/packages/graph-explorer/src/components/Chip/useChipColor.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useCallback } from "react"; -import { useTheme } from "@/core"; -import type { ChipProps } from "./Chip"; - -const isColorVariant = ( - color: string | undefined -): color is ChipProps["variant"] => { - if (!color) return false; - return ["error", "warning", "info", "success"].includes(color); -}; - -const useChipColor = ( - options: Record< - string, - { - label: string; - color?: string; - background?: string; - } - > -) => { - const [{ theme }] = useTheme(); - - const getChipColor = useCallback( - (value: string) => { - const option = options[value]; - - if (!option) return; - - const color = option.color; - - if (color && isColorVariant(color)) { - return { - variant: color, - }; - } - - return { - color: option?.color || theme.palette.text.primary, - background: option?.background || theme.palette.background.contrast, - }; - }, - [options, theme.palette.text.primary, theme.palette.background.contrast] - ); - - return { getChipColor }; -}; - -export default useChipColor; diff --git a/packages/graph-explorer/src/components/Graph/Graph.tsx b/packages/graph-explorer/src/components/Graph/Graph.tsx index a1536d536..933978e99 100755 --- a/packages/graph-explorer/src/components/Graph/Graph.tsx +++ b/packages/graph-explorer/src/components/Graph/Graph.tsx @@ -1,4 +1,3 @@ -import { css } from "@emotion/css"; import { cn } from "@/utils"; import cytoscape from "cytoscape"; import cyCanvas from "cytoscape-canvas"; @@ -48,23 +47,6 @@ cytoscape.use(d3Force); cytoscape.use(fcose); cyCanvas(cytoscape); -const defaultStyles = () => css` - width: 100%; - height: 100%; - position: relative; - overflow: hidden; - .graph-container { - width: 100%; - height: 100%; - position: absolute; - > div:first-child { - // Allow to render layers below the main nodes/edges layer - // E.g. animation layer should be at the bottom - z-index: 10 !important; - } - } -`; - const EMPTY_SET = new Set(); export type GraphRef = { cytoscape?: CytoscapeType; runLayout(): void }; @@ -398,8 +380,8 @@ export const Graph = ( const isEmpty = !nodes.length && !edges.length; const isLoading = loading; return ( -
-
+
+
{cy && children ? children(cy) : null} {isEmpty && !isLoading ? : null} {isLoading ? : null} diff --git a/packages/graph-explorer/src/components/Graph/internalComponents/EmptyState.tsx b/packages/graph-explorer/src/components/Graph/internalComponents/EmptyState.tsx index b5a8680b5..9dba6a7ff 100644 --- a/packages/graph-explorer/src/components/Graph/internalComponents/EmptyState.tsx +++ b/packages/graph-explorer/src/components/Graph/internalComponents/EmptyState.tsx @@ -1,41 +1,15 @@ -import { css } from "@emotion/css"; import { SearchIcon } from "@/components/icons"; import { PanelEmptyState } from "@/components/PanelEmptyState"; const EmptyState = () => { return ( -
+
} title="To get started, click into the search bar to browse graph data. Click + to add to Graph View." - size="md" />
); }; -function styles() { - return css` - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - color: #4b8fe2; - font-size: 1rem; - text-wrap: balance; - padding: 1rem; - user-select: none; - pointer-events: none; - .label { - padding-top: 16px; - } - background-color: #f0f4f9; - `; -} - export default EmptyState; diff --git a/packages/graph-explorer/src/components/IconButton/IconButton.styles.ts b/packages/graph-explorer/src/components/IconButton/IconButton.styles.ts index 778029071..2b8d95f26 100644 --- a/packages/graph-explorer/src/components/IconButton/IconButton.styles.ts +++ b/packages/graph-explorer/src/components/IconButton/IconButton.styles.ts @@ -340,64 +340,3 @@ export const defaultToggleButtonStyles = isSelected ? selectedStylesByVariantMap[variant] : "" ); }; - -export const defaultBadgeStyles: ThemeStyleFn = ({ theme }) => css` - &.badge { - position: absolute; - display: flex; - align-items: center; - justify-content: center; - min-width: 16px; - height: 16px; - border-radius: 8px; - padding: 0 ${theme.spacing.base}; - font-size: ${theme.typography.sizes.xs}; - color: ${theme.palette.secondary.contrastText}; - background: ${theme.palette.secondary.main}; - - &.variant-undetermined { - min-width: 8px; - height: 8px; - } - - &.placement-bottom-right { - bottom: -6px; - right: -6px; - - &.variant-undetermined { - bottom: 0; - right: 0; - } - } - - &.placement-bottom-left { - bottom: -6px; - left: -2px; - - &.variant-undetermined { - bottom: 0; - left: 0; - } - } - - &.placement-top-right { - top: -6px; - right: -6px; - - &.variant-undetermined { - top: 0; - right: 0; - } - } - - &.placement-top-left { - top: -6px; - left: -2px; - - &.variant-undetermined { - top: 0; - left: 0; - } - } - } -`; diff --git a/packages/graph-explorer/src/components/IconButton/IconButton.tsx b/packages/graph-explorer/src/components/IconButton/IconButton.tsx index df449a033..4b5aba866 100644 --- a/packages/graph-explorer/src/components/IconButton/IconButton.tsx +++ b/packages/graph-explorer/src/components/IconButton/IconButton.tsx @@ -6,10 +6,7 @@ import { forwardRef } from "react"; import { useWithTheme } from "@/core"; import type { TooltipProps } from "../Tooltip"; import Tooltip from "../Tooltip/Tooltip"; -import { - defaultBadgeStyles, - defaultIconButtonStyles, -} from "./IconButton.styles"; +import { defaultIconButtonStyles } from "./IconButton.styles"; export interface IconButtonProps extends Omit>, "elementType"> { @@ -23,23 +20,11 @@ export interface IconButtonProps as?: ElementType; tooltipText?: TooltipProps["text"]; tooltipPlacement?: TooltipProps["placement"]; - badge?: ReactNode; - badgeVariant?: "determined" | "undetermined"; - badgePlacement?: "top-left" | "top-right" | "bottom-left" | "bottom-right"; onClick?(e: MouseEvent): void; } export const IconButton = ( - { - tooltipText, - tooltipPlacement, - onClick, - badge, - color, - badgeVariant = "undetermined", - badgePlacement = "bottom-right", - ...props - }: IconButtonProps, + { tooltipText, tooltipPlacement, onClick, color, ...props }: IconButtonProps, ref: ForwardedRef ) => { const { buttonProps } = useButton( @@ -75,18 +60,6 @@ export const IconButton = ( > {icon} {props.children} - {Boolean(badge) && ( -
- {typeof badge !== "boolean" && badgeVariant === "determined" && badge} -
- )} ); diff --git a/packages/graph-explorer/src/components/ListItem/ListItem.tsx b/packages/graph-explorer/src/components/ListItem/ListItem.tsx index a11404395..2daebd300 100644 --- a/packages/graph-explorer/src/components/ListItem/ListItem.tsx +++ b/packages/graph-explorer/src/components/ListItem/ListItem.tsx @@ -87,7 +87,7 @@ export const ListItem = (
{children}
{secondary}
- {endAdornment &&
{endAdornment}
} + {endAdornment &&
{endAdornment}
}
); }; diff --git a/packages/graph-explorer/src/components/ModuleContainer/components/ModuleContainerHeader.tsx b/packages/graph-explorer/src/components/ModuleContainer/components/ModuleContainerHeader.tsx index ca5f91784..2c97e455e 100644 --- a/packages/graph-explorer/src/components/ModuleContainer/components/ModuleContainerHeader.tsx +++ b/packages/graph-explorer/src/components/ModuleContainer/components/ModuleContainerHeader.tsx @@ -9,7 +9,6 @@ import type { import { forwardRef, Fragment, useMemo, useState } from "react"; import getChildrenOfType from "@/utils/getChildrenOfType"; -import type { IconButtonProps } from "@/components/IconButton"; import IconButton from "@/components/IconButton"; import ChevronLeftIcon from "@/components/icons/ChevronLeftIcon"; import CloseIcon from "@/components/icons/CloseIcon"; @@ -33,8 +32,6 @@ export type Action = { active?: boolean; onlyPinnedVisible?: boolean; isDisabled?: boolean; - badge?: IconButtonProps["badge"]; - badgeVariant?: IconButtonProps["badgeVariant"]; collapsedItems?: ReactElement; }; @@ -187,9 +184,6 @@ const ModuleContainerHeader = ( color={action.color} icon={action.icon} onPress={() => onActionClick?.(action.value)} - badge={action.badge} - badgeVariant={action.badgeVariant} - badgePlacement="bottom-right" className="hover:text-primary-main focus-visible:text-primary-main focus-visible:bg-primary-main/20" />
diff --git a/packages/graph-explorer/src/components/ModuleContainer/components/ModuleContainerHeaderActions.tsx b/packages/graph-explorer/src/components/ModuleContainer/components/ModuleContainerHeaderActions.tsx index e07c06df5..1aaba5711 100644 --- a/packages/graph-explorer/src/components/ModuleContainer/components/ModuleContainerHeaderActions.tsx +++ b/packages/graph-explorer/src/components/ModuleContainer/components/ModuleContainerHeaderActions.tsx @@ -101,9 +101,6 @@ const ModuleContainerHeaderActions = ({ variant="text" icon={action.icon} onPress={() => onActionClick?.(action.value)} - badge={action.badge} - badgeVariant={action.badgeVariant} - badgePlacement="bottom-right" />
); diff --git a/packages/graph-explorer/src/components/PanelEmptyState/PanelEmptyState.styles.ts b/packages/graph-explorer/src/components/PanelEmptyState/PanelEmptyState.styles.ts deleted file mode 100644 index edc26e897..000000000 --- a/packages/graph-explorer/src/components/PanelEmptyState/PanelEmptyState.styles.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { css } from "@emotion/css"; -import type { ProcessedTheme, ThemeStyleFn } from "@/core"; -import { fade } from "@/core"; - -const getIndicatorSize = (size: "xs" | "sm" | "md" | "lg") => { - if (size === "md") { - return css` - width: 100px; - height: 100px; - font-size: 72.5px; - `; - } - - if (size === "lg") { - return css` - width: 150px; - height: 150px; - font-size: 100.5px; - `; - } - - if (size === "sm") { - return css` - width: 60px; - height: 60px; - font-size: 32px; - `; - } - - return css` - width: 40px; - height: 40px; - font-size: 18px; - `; -}; -const getTitleFontSize = (size: "xs" | "sm" | "md" | "lg") => { - const sizes = { - xs: "0.875rem", - sm: "0.875rem", - md: "1rem", - lg: "1.15rem", - }; - - return sizes[size]; -}; - -const getSubTitleFontSize = (size: "xs" | "sm" | "md" | "lg") => { - const sizes = { - xs: "0.75rem", - sm: "0.875rem", - md: "0.875rem", - lg: "1rem", - }; - - return sizes[size]; -}; -const getStyleByVariant = ( - variant: "info" | "waiting" | "warning" | "error", - theme: ProcessedTheme, - isDarkTheme?: boolean -) => { - const { palette, panelEmptyState } = theme; - const paletteVariant = variant === "waiting" ? "warning" : variant; - return css` - background: ${panelEmptyState?.panel?.indicator?.[variant]?.background || - `linear-gradient(180deg,${palette[paletteVariant].main} 48%,${palette[paletteVariant].light} 100%)`}; - box-shadow: ${isDarkTheme - ? "none" - : panelEmptyState?.panel?.indicator?.[variant]?.boxShadow || - `0px 0px 20px 2px ${fade(palette[paletteVariant].light, 0.7)}`}; - color: ${panelEmptyState?.panel?.indicator?.[variant]?.color || - palette.common.white}; - `; -}; - -const styles = - ( - variant: "info" | "waiting" | "warning" | "error", - size: "xs" | "sm" | "md" | "lg" - ): ThemeStyleFn => - ({ theme, isDarkTheme }) => css` - position: relative; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - z-index: 1; - - &.panel-empty-state-horizontal { - flex-direction: row; - .indicator-wrapper { - margin-bottom: 0; - margin-right: 12px; - } - .panel-empty-state-title { - margin-bottom: 8px; - } - .panel-empty-state-subtitle { - text-align: center; - } - } - - .indicator-wrapper { - margin-bottom: 16px; - } - .indicator { - ${getStyleByVariant(variant, theme, isDarkTheme)}; - ${getIndicatorSize(size)}; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - > svg { - width: 60%; - height: 60%; - } - } - - .panel-empty-state-container { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: ${theme.spacing["4x"]}; - } - - .panel-empty-state-text-container { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: ${theme.spacing.base}; - } - - .panel-empty-state-title { - margin: 0; - text-align: center; - color: ${theme.emptyState?.panel?.title?.color || - theme.palette.text.primary}; - font-size: ${theme.emptyState?.panel?.title?.fontSize || - getTitleFontSize(size)}; - font-weight: ${theme.emptyState?.panel?.title?.fontWeight || 500}; - } - - .panel-empty-state-subtitle { - margin: 0; - text-align: center; - color: ${theme.emptyState?.panel?.subtitle?.color || - theme.palette.text.secondary}; - font-size: ${theme.emptyState?.panel?.subtitle?.fontSize || - getSubTitleFontSize(size)}; - font-weight: ${theme.emptyState?.panel?.subtitle?.fontWeight || 400}; - } - `; - -export default styles; diff --git a/packages/graph-explorer/src/components/PanelEmptyState/PanelEmptyState.tsx b/packages/graph-explorer/src/components/PanelEmptyState/PanelEmptyState.tsx index a35b800ea..d61bbfaa6 100644 --- a/packages/graph-explorer/src/components/PanelEmptyState/PanelEmptyState.tsx +++ b/packages/graph-explorer/src/components/PanelEmptyState/PanelEmptyState.tsx @@ -1,21 +1,18 @@ -import { cn } from "@/utils"; import type { PropsWithChildren, ReactNode } from "react"; -import { useWithTheme } from "@/core"; import Button, { ButtonProps } from "../Button/Button"; -import styles from "./PanelEmptyState.styles"; +import { cva } from "class-variance-authority"; +import { cn } from "@/utils"; export type PanelEmptyStateProps = { - variant?: "info" | "waiting" | "warning" | "error"; + variant?: "info" | "error"; icon?: ReactNode; title?: ReactNode; subtitle?: ReactNode; className?: string; - size?: "xs" | "sm" | "md" | "lg"; onAction?: () => void; actionId?: string; actionLabel?: string; actionVariant?: ButtonProps["variant"]; - layout?: "horizontal" | "vertical"; }; const PanelEmptyState = ({ @@ -25,33 +22,52 @@ const PanelEmptyState = ({ title, subtitle, className, - size = "md", actionId, onAction, actionLabel, actionVariant, - layout = "vertical", }: PropsWithChildren) => { - const styleWithTheme = useWithTheme(); + const variantStyles = cva([], { + variants: { + variant: { + info: "bg-gradient-to-b from-primary-main to-primary-light shadow-[0_0_20px_2px_hsl(205,95%,71%,70%)]", + error: + "bg-gradient-to-b from-error-main to-error-light shadow-[0_0_20px_2px_rgba(255,144,119,0.7)]", + }, + }, + defaultVariants: { + variant: "info", + }, + }); + return (
{icon && ( -
-
{icon}
+
+
svg]:size-[60%]", + variantStyles({ variant }) + )} + > + {icon} +
)} -
-
- {title &&

{title}

} +
+
+ {title && ( +

{title}

+ )} {subtitle && ( -

{subtitle}

+

+ {subtitle} +

)}
{onAction && actionLabel && ( diff --git a/packages/graph-explorer/src/components/RemoteSvgIcon/RemoteSvgIcon.tsx b/packages/graph-explorer/src/components/RemoteSvgIcon/RemoteSvgIcon.tsx index 3490052de..5809eec3d 100644 --- a/packages/graph-explorer/src/components/RemoteSvgIcon/RemoteSvgIcon.tsx +++ b/packages/graph-explorer/src/components/RemoteSvgIcon/RemoteSvgIcon.tsx @@ -1,18 +1,12 @@ -import SVG from "react-inlinesvg"; +import { cn } from "@/utils"; +import SVG, { Props } from "react-inlinesvg"; -export interface RemoteSvgIconProps { - /** - * The url of the svg - */ - src: string; -} +export type RemoteSvgIconProps = Pick; /** * Fetches the remote SVG contents and renders it inline so that it honors any * css styling you've setup to affect the SVG (such as currentColor) */ -export const RemoteSvgIcon = ({ src }: RemoteSvgIconProps) => { - return ; -}; - -export default RemoteSvgIcon; +export default function RemoteSvgIcon({ src, className }: RemoteSvgIconProps) { + return ; +} diff --git a/packages/graph-explorer/src/components/Tabular/builders/index.ts b/packages/graph-explorer/src/components/Tabular/builders/index.ts index ad9020e96..9a670f4f4 100644 --- a/packages/graph-explorer/src/components/Tabular/builders/index.ts +++ b/packages/graph-explorer/src/components/Tabular/builders/index.ts @@ -1,5 +1,4 @@ export { default as makeBarChartCell } from "./makeBarChartCell"; -export { default as makeChipCell } from "./makeChipCell"; export { default as makeIconActionCell } from "./makeIconActionCell"; export { default as makeIconToggleCell } from "./makeIconToggleCell"; export { default as makeIconCell } from "./makeIconCell"; diff --git a/packages/graph-explorer/src/components/Tabular/builders/makeChipCell.tsx b/packages/graph-explorer/src/components/Tabular/builders/makeChipCell.tsx deleted file mode 100644 index 8d2546608..000000000 --- a/packages/graph-explorer/src/components/Tabular/builders/makeChipCell.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { css } from "@emotion/css"; -import type { ActiveThemeType, ProcessedTheme } from "@/core"; -import { useTheme } from "@/core"; - -import { Chip } from "@/components/Chip/Chip"; -import type { CellComponentProps } from "../useTabular"; - -const DEFAULT_COLOR = "#128ee5"; -const DEFAULT_BACKGROUND_COLOR = "rgba(18, 142, 229, 0.1)"; - -interface ChipStyleProps { - backgroundColor?: string; - borderRadius?: string; -} - -const styles = ({ backgroundColor, borderRadius }: ChipStyleProps) => css` - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - font-weight: 600; - - > div { - height: 24px; - min-width: 24px; - padding: 0; - justify-content: center; - align-items: center; - background-color: ${backgroundColor || DEFAULT_BACKGROUND_COLOR}; - border-radius: ${borderRadius}; - } -`; - -type ColorPropertyFunction = ( - value: unknown, - cellProps: CellComponentProps, - theme?: ActiveThemeType -) => string; - -type ChipCellProps = { - getBackgroundColor?: ColorPropertyFunction; - getColor?: ColorPropertyFunction; - getBorderRadius?: ColorPropertyFunction; -}; - -export const makeChipCell = - ({ - getBackgroundColor, - getColor, - getBorderRadius, - }: ChipCellProps) => - (props: CellComponentProps) => { - const [theme] = useTheme(); - return ( -
- - {props.value} - -
- ); - }; - -export default makeChipCell; diff --git a/packages/graph-explorer/src/components/VertexIcon.tsx b/packages/graph-explorer/src/components/VertexIcon.tsx index 0dff04b49..eadc5c813 100644 --- a/packages/graph-explorer/src/components/VertexIcon.tsx +++ b/packages/graph-explorer/src/components/VertexIcon.tsx @@ -1,25 +1,56 @@ +import { fade, VertexTypeConfig } from "@/core"; import RemoteSvgIcon from "./RemoteSvgIcon"; +import { cn } from "@/utils"; const VertexIcon = ({ iconUrl, iconImageType, + className, }: { iconUrl?: string; iconImageType?: string; + className?: string; }) => { if (!iconUrl) { return null; } if (iconImageType === "image/svg+xml") { - return ( -
- -
- ); + return ; } - return ; + return ; }; +export function VertexSymbol({ + vtConfig, + className, +}: { + vtConfig: VertexTypeConfig; + className?: string; +}) { + if (!vtConfig.iconUrl) { + return null; + } + + return ( +
+ +
+ ); +} + export default VertexIcon; diff --git a/packages/graph-explorer/src/components/Workspace/components/SidebarButton.tsx b/packages/graph-explorer/src/components/Workspace/components/SidebarButton.tsx index 16e5c0a26..c33178eed 100644 --- a/packages/graph-explorer/src/components/Workspace/components/SidebarButton.tsx +++ b/packages/graph-explorer/src/components/Workspace/components/SidebarButton.tsx @@ -39,7 +39,7 @@ function Badge({ ...props }: React.ComponentPropsWithoutRef<"div"> & { value?: boolean }) { return ( -
+
{children} {value ? ( -
+
diff --git a/packages/graph-explorer/src/modules/AvailableConnections/AvailableConnections.tsx b/packages/graph-explorer/src/modules/AvailableConnections/AvailableConnections.tsx index 64f59011d..c852cb4d2 100644 --- a/packages/graph-explorer/src/modules/AvailableConnections/AvailableConnections.tsx +++ b/packages/graph-explorer/src/modules/AvailableConnections/AvailableConnections.tsx @@ -4,8 +4,6 @@ import { useRecoilCallback, useRecoilValue } from "recoil"; import { v4 } from "uuid"; import { AddIcon, - AdvancedList, - AdvancedListItemType, Chip, DatabaseIcon, ModuleContainer, @@ -15,7 +13,7 @@ import { } from "@/components"; import { useNotification } from "@/components/NotificationProvider"; import Switch from "@/components/Switch"; -import { useWithTheme } from "@/core"; +import { RawConfiguration, useWithTheme } from "@/core"; import { activeConfigurationAtom, configurationAtom, @@ -27,6 +25,7 @@ import isValidConfigurationFile from "@/utils/isValidConfigurationFile"; import CreateConnection from "@/modules/CreateConnection"; import defaultStyles from "./AvailableConnections.styles"; import { fromFileToJson } from "@/utils/fileData"; +import { Virtuoso } from "react-virtuoso"; export type ConnectionDetailProps = { isSync: boolean; @@ -43,7 +42,6 @@ const AvailableConnections = ({ const activeConfig = useRecoilValue(activeConfigurationAtom); const configuration = useRecoilValue(configurationAtom); - const t = useTranslations(); const resetState = useResetState(); const onActiveConfigChange = useRecoilCallback( @@ -150,40 +148,6 @@ const AvailableConnections = ({ [isSync, onConfigImport] ); - const connectionItems = useMemo(() => { - const items: AdvancedListItemType[] = []; - configuration.forEach(config => { - items.push({ - id: config.id, - title: config.displayLabel || config.id, - subtitle: config.connection?.url, - icon: , - endAdornment: ( -
- - {t( - "available-connections.graph-type", - config.connection?.queryEngine || "gremlin" - )} - -
- onActiveConfigChange(config.id)} - isDisabled={isSync} - > - {activeConfig === config.id ? "Active" : "Inactive"} - -
- ), - }); - }); - - return items; - }, [activeConfig, configuration, isSync, onActiveConfigChange, t]); - return ( - - + ( +
+ onActiveConfigChange(config.id)} + /> +
+ )} /> onModalChange(false)} > @@ -211,4 +183,54 @@ const AvailableConnections = ({ ); }; +function ConfigRow({ + config, + isSelected, + isDisabled, + makeSelected, +}: { + config: RawConfiguration; + isSelected: boolean; + isDisabled: boolean; + makeSelected: () => void; +}) { + const t = useTranslations(); + + return ( +
!isDisabled && makeSelected()} + > + +
+
+ {config.displayLabel || config.id} +
+ {config.connection ? ( +
+ {config.connection.url} +
+ ) : null} +
+
+ + {t( + "available-connections.graph-type", + config.connection?.queryEngine || "gremlin" + )} + + + {isSelected ? "Active" : "Inactive"} + +
+
+ ); +} + export default AvailableConnections; diff --git a/packages/graph-explorer/src/modules/ConnectionDetail/ConnectionDetail.tsx b/packages/graph-explorer/src/modules/ConnectionDetail/ConnectionDetail.tsx index da0a5f3ab..1cc16ebb7 100644 --- a/packages/graph-explorer/src/modules/ConnectionDetail/ConnectionDetail.tsx +++ b/packages/graph-explorer/src/modules/ConnectionDetail/ConnectionDetail.tsx @@ -174,14 +174,10 @@ const ConnectionDetail = ({ isSync, onSyncChange }: ConnectionDetailProps) => {
{formatDate(lastSyncUpdate)}
)} {!lastSyncUpdate && !lastSyncFail && ( - - Not Synchronized - + Not Synchronized )} {lastSyncFail && ( - - Synchronization Failed - + Synchronization Failed )}
)} diff --git a/packages/graph-explorer/src/modules/GraphViewer/GraphViewer.tsx b/packages/graph-explorer/src/modules/GraphViewer/GraphViewer.tsx index e01368639..ccb3da46a 100644 --- a/packages/graph-explorer/src/modules/GraphViewer/GraphViewer.tsx +++ b/packages/graph-explorer/src/modules/GraphViewer/GraphViewer.tsx @@ -8,6 +8,7 @@ import { ModuleContainerHeader, RemoveFromCanvasIcon, ResetIcon, + VertexSymbol, ZoomInIcon, ZoomOutIcon, } from "@/components"; @@ -19,8 +20,6 @@ import IconButton from "@/components/IconButton"; import CloseIcon from "@/components/icons/CloseIcon"; import InfoIcon from "@/components/icons/InfoIcon"; import ScreenshotIcon from "@/components/icons/ScreenshotIcon"; -import ListItem from "@/components/ListItem"; -import RemoteSvgIcon from "@/components/RemoteSvgIcon"; import Select from "@/components/Select"; import { edgesHiddenIdsAtom, @@ -33,7 +32,6 @@ import { nodesSelectedIdsAtom, } from "@/core/StateProvider/nodes"; import useWithTheme from "@/core/ThemeProvider/useWithTheme"; -import fade from "@/core/ThemeProvider/utils/fade"; import { useEntities, useExpandNode } from "@/hooks"; import useTextTransform from "@/hooks/useTextTransform"; import defaultStyles from "./GraphViewerModule.styles"; @@ -279,15 +277,7 @@ export default function GraphViewer({ onActionClick={onHeaderActionClick} {...headerProps} /> -
+
void }) { const vtConfigs = useVertexTypeConfigs(); return ( - - } - onPress={onClose} - variant={"text"} - size={"small"} - /> - } - > - Legend - - {vtConfigs.map(vtConfig => { - return ( - - -
- ) - } - > - {vtConfig.displayLabel || textTransform(vtConfig.type)} - - ); - })} + +
+

Legend

+ } + onPress={onClose} + variant="text" + size="small" + /> +
+
    + {vtConfigs.map(vtConfig => { + return ( +
  • + + {vtConfig.displayLabel || textTransform(vtConfig.type)} +
  • + ); + })} +
); } diff --git a/packages/graph-explorer/src/modules/GraphViewer/GraphViewerModule.styles.ts b/packages/graph-explorer/src/modules/GraphViewer/GraphViewerModule.styles.ts index 63f7f5b65..272e90968 100644 --- a/packages/graph-explorer/src/modules/GraphViewer/GraphViewerModule.styles.ts +++ b/packages/graph-explorer/src/modules/GraphViewer/GraphViewerModule.styles.ts @@ -85,45 +85,6 @@ const defaultStyles: ThemeStyleFn = ({ theme }) => css` display: flex; } } - - .legend-container { - position: absolute; - overflow: auto; - height: calc(100% - ${theme.spacing["2x"]} - ${theme.spacing["2x"]}); - min-width: 200px; - max-width: 400px; - z-index: ${theme.zIndex.panes}; - bottom: ${theme.spacing["2x"]}; - right: ${theme.spacing["2x"]}; - row-gap: ${theme.spacing["2x"]}; - - .legend-title { - .content { - .primary { - font-weight: bold; - } - } - .end-adornment { - margin: 0; - } - } - .legend-item { - .icon { - display: flex; - align-items: center; - justify-content: center; - width: 24px; - height: 24px; - padding: ${theme.spacing.base}; - border-radius: 12px; - color: ${theme.palette.primary.main}; - background: ${fade(theme.palette.primary.main, 0.3)}; - } - .content { - min-height: 24px; - } - } - } } `; diff --git a/packages/graph-explorer/src/modules/KeywordSearch/KeywordSearch.tsx b/packages/graph-explorer/src/modules/KeywordSearch/KeywordSearch.tsx index 90c0591d4..410b03596 100644 --- a/packages/graph-explorer/src/modules/KeywordSearch/KeywordSearch.tsx +++ b/packages/graph-explorer/src/modules/KeywordSearch/KeywordSearch.tsx @@ -415,14 +415,7 @@ function SearchResults({ hideFooter /> {selection.state.size > 0 && ( - + {Array.from(selection.state).map(nodeId => { const node = searchResults.find(n => n.id === nodeId); diff --git a/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.styles.ts b/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.styles.ts index 4dc3e1794..d433d3b7d 100644 --- a/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.styles.ts +++ b/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.styles.ts @@ -18,11 +18,7 @@ const defaultStyles: ThemeStyleFn = ({ theme }) => css` justify-content: space-between; margin-top: ${theme.spacing["2x"]}; gap: ${theme.spacing["2x"]}; - .chip { - user-select: none; - min-width: 48px; - justify-content: center; - } + .vertex-type { display: flex; align-items: center; diff --git a/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.tsx b/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.tsx index 8a32c7a90..4ffd6b4fc 100644 --- a/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.tsx +++ b/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.tsx @@ -43,15 +43,16 @@ export default function NeighborsList({ vertex }: NeighborsListProps) {
{op.label}
-
+
- }> + + {neighborsInView} - + {vertex.neighborsCountByType[op.value]}
diff --git a/packages/graph-explorer/src/modules/common/VertexHeader.tsx b/packages/graph-explorer/src/modules/common/VertexHeader.tsx index 0e4650e65..ce7efd692 100644 --- a/packages/graph-explorer/src/modules/common/VertexHeader.tsx +++ b/packages/graph-explorer/src/modules/common/VertexHeader.tsx @@ -1,6 +1,5 @@ import { Vertex } from "@/types/entities"; -import { ComponentBaseProps, VertexIcon } from "@/components"; -import { fade } from "@/core"; +import { ComponentBaseProps, VertexSymbol } from "@/components"; import useDisplayNames from "@/hooks/useDisplayNames"; import useTextTransform from "@/hooks/useTextTransform"; import { @@ -23,18 +22,7 @@ export default function VertexHeader({ return (
{vtConfig.iconUrl && ( -
- -
+ )}
diff --git a/packages/graph-explorer/src/workspaces/GraphExplorer/GraphExplorer.tsx b/packages/graph-explorer/src/workspaces/GraphExplorer/GraphExplorer.tsx index 59234dc1f..6b668c39a 100644 --- a/packages/graph-explorer/src/workspaces/GraphExplorer/GraphExplorer.tsx +++ b/packages/graph-explorer/src/workspaces/GraphExplorer/GraphExplorer.tsx @@ -240,13 +240,7 @@ const GraphExplorer = () => {
)} {toggles.has("graph-viewer") && ( -
+