diff --git a/CONTEXT.md b/CONTEXT.md index d4205429c..aa4155728 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -64,21 +64,21 @@ _Avoid_: Attribute (legacy code term being phased out) **Styles**: Display customizations per Vertex Type and Edge Type — shape, color, icon, line style, and display labels. Resolved through a **Styles Cascade** (highest precedence first): User Custom Styles → Shared Styles → App Default Styles. Persisted and merged with Schema-discovered metadata to produce the final rendering. The two specific instances are **Vertex Styles** and **Edge Styles**, each stored in IndexedDB as its own type-keyed Map per layer. Storage keys follow a `--styles` convention. -_Avoid_: User Preferences, preferences, user styling, user settings (the `*Preferences*` code names — `VertexPreferencesStorageModel`, `userPreferences.ts`, `vertexPreferencesAtom` — are the legacy term being phased out); "customization" as a standalone noun +_Avoid_: User Preferences, preferences, user styling, user settings; "customization" as a standalone noun **Styles Cascade**: -The precedence stack that resolves which style value a vertex or edge type displays. From highest to lowest priority: (3) **User Custom Styles** — per-type edits made in the style dialogs, (2) **Shared Styles** — loaded from a file via Settings, (1) **App Default Styles** — hardcoded in the codebase (`defaultVertexPreferences` / `defaultEdgePreferences`). The layers below User Custom Styles (1–2) collectively are **Default Styles** — what a per-type "Reset to Default" restores to (it clears the user's edit, revealing the Shared or App Default beneath). The verb is **customize**; the UI shorthand is **custom styles** / **shared styles**. +The precedence stack that resolves which style value a vertex or edge type displays. From highest to lowest priority: (3) **User Custom Styles** — per-type edits made in the style dialogs, (2) **Shared Styles** — loaded from a file via Settings, (1) **App Default Styles** — hardcoded in the codebase (`appDefaultVertexStyle` / `appDefaultEdgeStyle`). The layers below User Custom Styles (1–2) collectively are **Default Styles** — what a per-type "Reset to Default" restores to (it clears the user's edit, revealing the Shared or App Default beneath). The verb is **customize**; the UI shorthand is **custom styles** / **shared styles**. _Avoid_: Imported Default Styles, imported defaults (the layer is **Shared Styles**); Effective styles (no separate atom), style overrides **User Custom Styles**: -The highest-precedence layer in the Styles Cascade. Per-type edits a user makes in the vertex/edge style dialogs. Stored in `userVertexStylesAtom` (`Map`, key `"user-vertex-styles"`) and `userEdgeStylesAtom` (`Map`, key `"user-edge-styles"`). Clearing these ("Reset Custom Styles" in Settings) reveals Shared Styles beneath. +The highest-precedence layer in the Styles Cascade. Per-type edits a user makes in the vertex/edge style dialogs. Stored in `userVertexStylesAtom` (`Map`, key `"user-vertex-styles"`) and `userEdgeStylesAtom` (`Map`, key `"user-edge-styles"`). Clearing these ("Reset Custom Styles" in Settings) reveals Shared Styles beneath. **Shared Styles**: The second-highest-precedence layer in the Styles Cascade (below User Custom, above App Default). Loaded from a styling file via the Settings → Styles screen. Stored in `sharedVertexStylesAtom` (key `"shared-vertex-styles"`) and `sharedEdgeStylesAtom` (key `"shared-edge-styles"`). Non-destructive to User Custom Styles — loading writes only this layer. Clearing these ("Reset Shared Styles" in Settings) falls through to App Default Styles. Named for their purpose: a user **saves** their styles to a file and others **load** it to get the same look. _Avoid_: Imported Default Styles, imported defaults (renamed — "shared" names the purpose and reads better) **App Default Styles**: -The lowest-precedence layer in the Styles Cascade. Hardcoded in the codebase as `defaultVertexPreferences` and `defaultEdgePreferences`. Not persisted — always available as the final fallback. +The lowest-precedence layer in the Styles Cascade. Hardcoded in the codebase as `appDefaultVertexStyle` and `appDefaultEdgeStyle`. Not persisted — always available as the final fallback. **Schema Sync**: The process that queries the database to discover vertex types, edge types, and their attributes. Required before a user can explore a new Connection. @@ -137,13 +137,3 @@ _Avoid_: Save-status indicator > **Dev:** "Are **Styles** shared across **Connections**?" > **Domain expert:** "No — well, actually right now they're per **Vertex Type** and **Edge Type** globally, not scoped to a specific **Connection**. So if two databases happen to have the same type name, they'd share styling." - -## Flagged ambiguities - -- "Configuration" was used to mean both **Connection** and the bundled object (connection + schema + Styles) — resolved: **Connection** is canonical, "Configuration" is legacy. -- "User Preferences" / "preferences" was used for display customizations, but the code is mid-migration to "styles" (`userVertexStylesAtom` / `userEdgeStylesAtom`, `"user-vertex-styles"` / `"user-edge-styles"`) — resolved: **Styles** is canonical (with **Vertex Styles** / **Edge Styles** as instances); the `*Preferences*` code names are legacy being phased out. -- "Imported Default Styles" / "imported defaults" named the cascade's middle layer, and its file actions were "Import" / "Export" — resolved: the layer is **Shared Styles** ("shared" names the purpose and reads better than a bare adjective-noun), and the actions are **Load** / **Save** to match the house style used by the graph and configuration file features. "Default" now lives only in the cascade generally and the per-type "Reset to Default" button, not the layer name. Renamed end-to-end (atoms `shared*StylesAtom`, keys `"shared-*-styles"`) since the feature had not shipped — no migration. The core-styling plumbing keeps `import`-flavored names (`parseStylingFile`, `useApplyStylingImport`, `getStylingConflicts`) as an internal detail of the load action. -- A Connection's data now has three distinct shapes that look similar but must not be conflated, each with its own explicit type: the **Exported Connection File** (`ExportedConnectionFile`, on-disk wire format), the in-memory merged configuration (`MergedConfiguration` — connection plus a live `Schema` with `lastUpdate` as a `Date`), and the persisted storage shape (`RawConfiguration` — connection only; the schema is stored separately in `schemaAtom`, never embedded). `mergeConfiguration` assembles a `MergedConfiguration` from a stored `RawConfiguration` and its active schema. Remaining work: `configurationAtom` still keys by `RawConfiguration`, to be migrated to a connection record. -- "Node" means **Vertex** in code but is the preferred UI term for property graphs — resolved: use **Vertex** in code, "node" in UI copy. -- "Attribute" vs "Property" — resolved: **Property** is canonical, "attribute" is legacy code term being phased out. -- "Active connection" meant a single shared per-origin value, but the app consumed it as if it were per-tab — resolved: **Active Connection** is per-tab (sessionStorage), **Last Active Connection** is the shared persisted breadcrumb. The legacy `activeConfigurationAtom` / `active-configuration` key is reused as the breadcrumb. diff --git a/docs/adr/20260623-type-keyed-map-atoms-for-user-preferences.md b/docs/adr/20260623-type-keyed-map-atoms-for-user-preferences.md index 62153d171..dced1f621 100644 --- a/docs/adr/20260623-type-keyed-map-atoms-for-user-preferences.md +++ b/docs/adr/20260623-type-keyed-map-atoms-for-user-preferences.md @@ -2,6 +2,7 @@ - **Status:** Accepted - **Date:** 2026-06-23 +- **Updated:** 2026-07-06 — the styling vocabulary was renamed to the Styles convention (#1866). The decision below is unchanged; only the identifiers drifted. Read it with these substitutions: `VertexPreferencesStorageModel`→`VertexStyleStorage`, `EdgePreferencesStorageModel`→`EdgeStyleStorage`, `vertexPreferencesAtom`→`vertexStyleAtom`, `edgePreferencesAtom`→`edgeStyleAtom`, `LegacyUserStylingStorageModel`→`LegacyUserStylingStorage`, `userPreferences.ts`→`graphStyles.ts`. - **Related:** ADR `per-key-diff-merge-cross-tab-reconciliation` — the Map-keyed shape is the prerequisite for per-type merge. ADR `storage-layer-owns-persistence-failure` — the migration's "catch and degrade to defaults on failure" posture follows that ADR's established startup-failure stance. Issue #1864 (this change). Issues #1820 / #1831 (cross-tab merge, separate follow-up). ## Context diff --git a/docs/adr/20260624-styling-file-format.md b/docs/adr/20260624-styling-file-format.md index 7f23933be..19d8b3ff0 100644 --- a/docs/adr/20260624-styling-file-format.md +++ b/docs/adr/20260624-styling-file-format.md @@ -2,7 +2,8 @@ - **Status:** Accepted - **Date:** 2026-06-24 -- **Related:** ADR `shared-file-envelope` (the outer envelope this payload lives inside). ADR `type-keyed-map-atoms-for-user-preferences` (the Map storage shape that the shared layer mirrors). Issue #1866 (Preferences → Styles rename — deferred, not bundled here). +- **Updated:** 2026-07-06 — the Preferences → Styles rename (#1866) has since shipped. The decision below is unchanged; read `VertexPreferencesStorageModel`→`VertexStyleStorage` and `EdgePreferencesStorageModel`→`EdgeStyleStorage`. +- **Related:** ADR `shared-file-envelope` (the outer envelope this payload lives inside). ADR `type-keyed-map-atoms-for-user-preferences` (the Map storage shape that the shared layer mirrors). Issue #1866 (Preferences → Styles rename). ## Context diff --git a/docs/agents/testing.md b/docs/agents/testing.md index 32f85f6fb..c2f5ea022 100644 --- a/docs/agents/testing.md +++ b/docs/agents/testing.md @@ -75,4 +75,4 @@ Anything persisted to IndexedDB via localForage/Jotai may be reloaded in an olde Group them in a dedicated `describe("backward compatibility: ...")` with a comment block stating the old shape, why the tests exist, and a "do not delete without confirming migration" warning. Cast legacy fixtures with `as TypeName` to bypass compile-time checks. See `src/utils/parseConnectionFile.test.ts` for a worked example. -Persisted types that require this when modified: `SchemaStorageModel`, `PrefixTypeConfig`, `VertexTypeConfig`, `EdgeTypeConfig`, `RawConfiguration`, and the user-preference storage models. Triggers: removing/renaming a property, changing a property's type, adding a required property, or changing a property's semantics. +Persisted types that require this when modified: `SchemaStorageModel`, `PrefixTypeConfig`, `VertexTypeConfig`, `EdgeTypeConfig`, `RawConfiguration`, and the style storage models (`VertexStyleStorage`, `EdgeStyleStorage`). Triggers: removing/renaming a property, changing a property's type, adding a required property, or changing a property's semantics. diff --git a/packages/graph-explorer/src/components/Graph/Graph.model.ts b/packages/graph-explorer/src/components/Graph/Graph.model.ts index 8cce53f0c..efcc659fb 100644 --- a/packages/graph-explorer/src/components/Graph/Graph.model.ts +++ b/packages/graph-explorer/src/components/Graph/Graph.model.ts @@ -92,7 +92,7 @@ export type TextStyle = { wrap: "none" | "wrap" | "ellipsis"; }; -export type NodeStyle = { +export type RenderedNodeStyle = { background: string; backgroundFit?: "none" | "contain" | "cover"; backgroundWidth?: string; @@ -171,7 +171,7 @@ export type CurveStyle = | "segments" | "taxi"; -export type EdgeStyle = { +export type RenderedEdgeStyle = { color: string; curveStyle: CurveStyle; label?: string | ((edge: cytoscape.EdgeSingular) => string); diff --git a/packages/graph-explorer/src/components/Graph/styles/defaultEdgeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/defaultEdgeStyle.ts index 9fe97ef2f..227a13e55 100644 --- a/packages/graph-explorer/src/components/Graph/styles/defaultEdgeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/defaultEdgeStyle.ts @@ -1,6 +1,6 @@ -import type { EdgeStyle } from "../Graph.model"; +import type { RenderedEdgeStyle } from "../Graph.model"; -const defaultEdgeStyle: EdgeStyle = { +const defaultEdgeStyle: RenderedEdgeStyle = { visible: true, opacity: 1, color: "#f0f0f0", diff --git a/packages/graph-explorer/src/components/Graph/styles/defaultNodeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/defaultNodeStyle.ts index a6008a00c..c7527f98e 100644 --- a/packages/graph-explorer/src/components/Graph/styles/defaultNodeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/defaultNodeStyle.ts @@ -1,6 +1,6 @@ -import type { NodeStyle } from "../Graph.model"; +import type { RenderedNodeStyle } from "../Graph.model"; -const defaultNodeStyle: NodeStyle = { +const defaultNodeStyle: RenderedNodeStyle = { background: "#128EE5", backgroundOpacity: 0.4, borderColor: "#128EE5", diff --git a/packages/graph-explorer/src/components/Graph/styles/defaultSubwayEdgeStyles.ts b/packages/graph-explorer/src/components/Graph/styles/defaultSubwayEdgeStyles.ts index 83474caa8..565c5d933 100644 --- a/packages/graph-explorer/src/components/Graph/styles/defaultSubwayEdgeStyles.ts +++ b/packages/graph-explorer/src/components/Graph/styles/defaultSubwayEdgeStyles.ts @@ -1,8 +1,8 @@ -import type { EdgeStyle } from "../Graph.model"; +import type { RenderedEdgeStyle } from "../Graph.model"; import defaultEdgeStyle from "./defaultEdgeStyle"; -const defaultSubwayEdgeStyles: EdgeStyle = { +const defaultSubwayEdgeStyles: RenderedEdgeStyle = { ...defaultEdgeStyle, curveStyle: "taxi", taxiDirection: "rightward", diff --git a/packages/graph-explorer/src/components/Graph/styles/hiddenEdgeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/hiddenEdgeStyle.ts index 4c6e28e5d..05a3dc837 100644 --- a/packages/graph-explorer/src/components/Graph/styles/hiddenEdgeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/hiddenEdgeStyle.ts @@ -1,6 +1,6 @@ -import type { EdgeStyle } from "../Graph.model"; +import type { RenderedEdgeStyle } from "../Graph.model"; -const hiddenEdgeStyle: Partial = { +const hiddenEdgeStyle: Partial = { visible: false, }; diff --git a/packages/graph-explorer/src/components/Graph/styles/hiddenNodeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/hiddenNodeStyle.ts index 53f112790..d7ed0df06 100644 --- a/packages/graph-explorer/src/components/Graph/styles/hiddenNodeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/hiddenNodeStyle.ts @@ -1,6 +1,6 @@ -import type { NodeStyle } from "../Graph.model"; +import type { RenderedNodeStyle } from "../Graph.model"; -const hiddenNodeStyle: Partial = { +const hiddenNodeStyle: Partial = { visible: false, }; diff --git a/packages/graph-explorer/src/components/Graph/styles/outOfFocusEdgeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/outOfFocusEdgeStyle.ts index 36be7137a..57810b321 100644 --- a/packages/graph-explorer/src/components/Graph/styles/outOfFocusEdgeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/outOfFocusEdgeStyle.ts @@ -1,6 +1,6 @@ -import type { EdgeStyle } from "../Graph.model"; +import type { RenderedEdgeStyle } from "../Graph.model"; -const outOfFocusEdgeStyle: Partial = { +const outOfFocusEdgeStyle: Partial = { label: "", lineColor: "#a0a0a0", opacity: 0.2, diff --git a/packages/graph-explorer/src/components/Graph/styles/outOfFocusNodeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/outOfFocusNodeStyle.ts index 2aec67388..90ffe25a9 100644 --- a/packages/graph-explorer/src/components/Graph/styles/outOfFocusNodeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/outOfFocusNodeStyle.ts @@ -1,6 +1,6 @@ -import type { NodeStyle } from "../Graph.model"; +import type { RenderedNodeStyle } from "../Graph.model"; -const outOfFocusNodeStyle: Partial = { +const outOfFocusNodeStyle: Partial = { backgroundOpacity: 0.1, background: "#a0a0a0", borderOpacity: 0.2, diff --git a/packages/graph-explorer/src/components/Graph/styles/selectedEdgeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/selectedEdgeStyle.ts index 9a1327425..2a531dd0a 100644 --- a/packages/graph-explorer/src/components/Graph/styles/selectedEdgeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/selectedEdgeStyle.ts @@ -1,6 +1,6 @@ -import type { EdgeStyle } from "../Graph.model"; +import type { RenderedEdgeStyle } from "../Graph.model"; -const selectedEdgeStyle: Partial = { +const selectedEdgeStyle: Partial = { underlayColor: "#17457b", underlayOpacity: 0.4, underlayPadding: 4, diff --git a/packages/graph-explorer/src/components/Graph/styles/selectedNodeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/selectedNodeStyle.ts index 33376ba05..d75cd9b4e 100644 --- a/packages/graph-explorer/src/components/Graph/styles/selectedNodeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/selectedNodeStyle.ts @@ -1,6 +1,6 @@ -import type { NodeStyle } from "../Graph.model"; +import type { RenderedNodeStyle } from "../Graph.model"; -const selectedNodeStyle: Partial = { +const selectedNodeStyle: Partial = { underlayColor: "#17457b", underlayOpacity: 0.4, }; diff --git a/packages/graph-explorer/src/components/Graph/styles/selectedSubwayEdgeStyles.ts b/packages/graph-explorer/src/components/Graph/styles/selectedSubwayEdgeStyles.ts index 68912314f..eaa19320d 100644 --- a/packages/graph-explorer/src/components/Graph/styles/selectedSubwayEdgeStyles.ts +++ b/packages/graph-explorer/src/components/Graph/styles/selectedSubwayEdgeStyles.ts @@ -1,8 +1,8 @@ -import type { EdgeStyle } from "../Graph.model"; +import type { RenderedEdgeStyle } from "../Graph.model"; import selectedEdgeStyle from "./selectedEdgeStyle"; -const selectedSubwayEdgeStyles: Partial = { +const selectedSubwayEdgeStyles: Partial = { ...selectedEdgeStyle, curveStyle: "taxi", taxiDirection: "rightward", diff --git a/packages/graph-explorer/src/components/Graph/styles/toCyEdgeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/toCyEdgeStyle.ts index 9c64f50a5..6f32cc616 100644 --- a/packages/graph-explorer/src/components/Graph/styles/toCyEdgeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/toCyEdgeStyle.ts @@ -1,6 +1,8 @@ -import type { CyEdgeStyle, EdgeStyle } from "../Graph.model"; +import type { CyEdgeStyle, RenderedEdgeStyle } from "../Graph.model"; -const toCyEdgeStyle = (edgeStyle: Partial): Partial => { +const toCyEdgeStyle = ( + edgeStyle: Partial, +): Partial => { return { color: edgeStyle.text?.color, curveStyle: edgeStyle.curveStyle, diff --git a/packages/graph-explorer/src/components/Graph/styles/toCyNodeStyle.ts b/packages/graph-explorer/src/components/Graph/styles/toCyNodeStyle.ts index 6bdb36176..192cb229d 100644 --- a/packages/graph-explorer/src/components/Graph/styles/toCyNodeStyle.ts +++ b/packages/graph-explorer/src/components/Graph/styles/toCyNodeStyle.ts @@ -1,6 +1,8 @@ -import type { CyNodeStyle, NodeStyle } from "../Graph.model"; +import type { CyNodeStyle, RenderedNodeStyle } from "../Graph.model"; -const toCyNodeStyle = (nodeStyle: Partial): Partial => { +const toCyNodeStyle = ( + nodeStyle: Partial, +): Partial => { return { backgroundColor: nodeStyle.background, backgroundFit: nodeStyle.backgroundFit, diff --git a/packages/graph-explorer/src/components/VertexIcon.tsx b/packages/graph-explorer/src/components/VertexIcon.tsx index f3baf8049..6165d70f9 100644 --- a/packages/graph-explorer/src/components/VertexIcon.tsx +++ b/packages/graph-explorer/src/components/VertexIcon.tsx @@ -4,11 +4,7 @@ import DOMPurify from "dompurify"; import { DynamicIcon } from "lucide-react/dynamic"; import SVG from "react-inlinesvg"; -import { - useVertexPreferences, - type VertexPreferences, - type VertexType, -} from "@/core"; +import { useVertexStyle, type VertexStyle, type VertexType } from "@/core"; import { cn } from "@/utils"; import { getLucideName, isValidLucideIconName } from "@/utils/lucideIcons"; @@ -21,7 +17,7 @@ function sanitizeSvg(svg: string): string { } interface Props { - vertexStyle: VertexPreferences; + vertexStyle: VertexStyle; className?: string; alt?: string; } @@ -75,15 +71,15 @@ export function VertexIconByType({ vertexType: VertexType; className?: string; }) { - const vertexPreferences = useVertexPreferences(vertexType); - return ; + const vertexStyle = useVertexStyle(vertexType); + return ; } export function VertexSymbol({ vertexStyle, className, }: { - vertexStyle: VertexPreferences; + vertexStyle: VertexStyle; className?: string; }) { return ( @@ -108,8 +104,8 @@ export function VertexSymbolByType({ vertexType: VertexType; className?: string; }) { - const vertexPreferences = useVertexPreferences(vertexType); - return ; + const vertexStyle = useVertexStyle(vertexType); + return ; } export default VertexIcon; diff --git a/packages/graph-explorer/src/components/VertexRow.tsx b/packages/graph-explorer/src/components/VertexRow.tsx index 5a333ccdc..f6b7fbc48 100644 --- a/packages/graph-explorer/src/components/VertexRow.tsx +++ b/packages/graph-explorer/src/components/VertexRow.tsx @@ -1,6 +1,6 @@ import type { ComponentPropsWithoutRef } from "react"; -import { type DisplayVertex, useVertexPreferences } from "@/core"; +import { type DisplayVertex, useVertexStyle } from "@/core"; import { ASCII, cn, LABELS } from "@/utils"; import { SearchResultSubtitle, SearchResultTitle, VertexSymbol } from "."; @@ -14,7 +14,7 @@ export function VertexRow({ vertex: DisplayVertex; name?: string; } & ComponentPropsWithoutRef<"div">) { - const vertexPreferences = useVertexPreferences(vertex.primaryType); + const vertexStyle = useVertexStyle(vertex.primaryType); const resultName = name ? `${name}: ` : ""; const nameIsSameAsTypes = vertex.displayTypes === vertex.displayName; const isDefaultType = vertex.displayTypes === LABELS.MISSING_TYPE; @@ -29,7 +29,7 @@ export function VertexRow({ className={cn("flex flex-row items-center gap-3", className)} {...props} > - +
{title} {vertex.displayDescription} diff --git a/packages/graph-explorer/src/core/ConfigurationProvider/types.ts b/packages/graph-explorer/src/core/ConfigurationProvider/types.ts index ab6695260..0639fa5fa 100644 --- a/packages/graph-explorer/src/core/ConfigurationProvider/types.ts +++ b/packages/graph-explorer/src/core/ConfigurationProvider/types.ts @@ -3,9 +3,9 @@ import type { ConnectionConfig } from "@shared/types"; import { v4 } from "uuid"; import type { - EdgePreferencesStorageModel, - VertexPreferencesStorageModel, -} from "@/core/StateProvider/userPreferences"; + EdgeStyleStorage, + VertexStyleStorage, +} from "@/core/StateProvider/graphStyles"; import type { Branded } from "@/utils"; import type { IriNamespace, RdfPrefix } from "@/utils/rdf"; @@ -66,7 +66,7 @@ export type VertexTypeConfig = { * Total number of vertices of this type */ total?: number; -} & VertexPreferencesStorageModel; +} & VertexStyleStorage; export type EdgeTypeConfig = { /** @@ -89,7 +89,7 @@ export type EdgeTypeConfig = { * Total number of edges of this type */ total?: number; -} & EdgePreferencesStorageModel; +} & EdgeStyleStorage; export type PrefixTypeConfig = { prefix: RdfPrefix; diff --git a/packages/graph-explorer/src/core/StateProvider/configuration.test.ts b/packages/graph-explorer/src/core/StateProvider/configuration.test.ts index 873c572a7..f78f0c491 100644 --- a/packages/graph-explorer/src/core/StateProvider/configuration.test.ts +++ b/packages/graph-explorer/src/core/StateProvider/configuration.test.ts @@ -10,11 +10,11 @@ import { } from "@/core/entities"; import { RESERVED_TYPES_PROPERTY } from "@/utils"; import { - createRandomEdgePreferencesStorageModel, + createRandomEdgeStyleStorage, createRandomEdgeTypeConfig, createRandomRawConfiguration, createRandomSchema, - createRandomVertexPreferencesStorageModel, + createRandomVertexStyleStorage, createRandomVertexTypeConfig, } from "@/utils/testing"; @@ -23,11 +23,8 @@ import type { RawConfiguration, VertexTypeConfig, } from "../ConfigurationProvider"; +import type { EdgeStyleStorage, VertexStyleStorage } from "./graphStyles"; import type { SchemaStorageModel } from "./schema"; -import type { - EdgePreferencesStorageModel, - VertexPreferencesStorageModel, -} from "./userPreferences"; import { activeConfigSelector, @@ -42,14 +39,14 @@ import { } from "./configuration"; function toVertexStyles( - styles: VertexPreferencesStorageModel[] = [], -): Map { + styles: VertexStyleStorage[] = [], +): Map { return new Map(styles.map(style => [style.type, style])); } function toEdgeStyles( - styles: EdgePreferencesStorageModel[] = [], -): Map { + styles: EdgeStyleStorage[] = [], +): Map { return new Map(styles.map(style => [style.type, style])); } @@ -143,13 +140,13 @@ describe("mergedConfiguration", () => { const schema = createRandomSchema(); const vertexStyles = toVertexStyles( schema.vertices.map(v => ({ - ...createRandomVertexPreferencesStorageModel(), + ...createRandomVertexStyleStorage(), type: v.type, })), ); const edgeStyles = toEdgeStyles( schema.edges.map(v => ({ - ...createRandomEdgePreferencesStorageModel(), + ...createRandomEdgeStyleStorage(), type: v.type, })), ); diff --git a/packages/graph-explorer/src/core/StateProvider/configuration.ts b/packages/graph-explorer/src/core/StateProvider/configuration.ts index 8974000af..1ba4a3ed8 100644 --- a/packages/graph-explorer/src/core/StateProvider/configuration.ts +++ b/packages/graph-explorer/src/core/StateProvider/configuration.ts @@ -19,13 +19,13 @@ import { } from "@/core"; import { RESERVED_TYPES_PROPERTY } from "@/utils/constants"; -import { activeSchemaSelector, type SchemaStorageModel } from "./schema"; import { - defaultEdgePreferences, - defaultVertexPreferences, - type EdgePreferencesStorageModel, - type VertexPreferencesStorageModel, -} from "./userPreferences"; + appDefaultEdgeStyle, + appDefaultVertexStyle, + type EdgeStyleStorage, + type VertexStyleStorage, +} from "./graphStyles"; +import { activeSchemaSelector, type SchemaStorageModel } from "./schema"; /** Gets the currently active config. */ export const activeConfigSelector = atom(get => { @@ -67,8 +67,8 @@ export const mergedConfigurationSelector = atom(get => { export function mergeConfiguration( currentSchema: SchemaStorageModel | null | undefined, currentConfig: RawConfiguration, - vertexStyles: ReadonlyMap, - edgeStyles: ReadonlyMap, + vertexStyles: ReadonlyMap, + edgeStyles: ReadonlyMap, ): MergedConfiguration { const mergedVertices = (currentSchema?.vertices ?? []) .map(schemaVertex => @@ -116,7 +116,7 @@ export type NormalizedConnection = ReturnType; const mergeVertex = ( schemaVertex: VertexTypeConfig, - preferences?: VertexPreferencesStorageModel, + style?: VertexStyleStorage, ): VertexTypeConfig => { // Ignore the displayLabel from the schema const patchedSchema = patchToRemoveDisplayLabel(schemaVertex); @@ -126,14 +126,14 @@ const mergeVertex = ( ...getDefaultVertexTypeConfig(schemaVertex.type), // Automatic schema override ...patchedSchema, - // User preferences override - ...preferences, + // User style override + ...style, }; }; const mergeEdge = ( schemaEdge: EdgeTypeConfig, - preferences?: EdgePreferencesStorageModel, + style?: EdgeStyleStorage, ): EdgeTypeConfig => { // Ignore the displayLabel from the schema const patchedSchema = patchToRemoveDisplayLabel(schemaEdge); @@ -143,8 +143,8 @@ const mergeEdge = ( ...getDefaultEdgeTypeConfig(schemaEdge.type), // Automatic schema override ...patchedSchema, - // User preferences override - ...preferences, + // User style override + ...style, }; if (config.displayNameAttribute === "type") { @@ -179,7 +179,7 @@ export const edgeTypesSelector = atom(get => { export const defaultVertexTypeConfig = { attributes: [], - ...defaultVertexPreferences, + ...appDefaultVertexStyle, } satisfies Omit; export function getDefaultVertexTypeConfig( @@ -193,7 +193,7 @@ export function getDefaultVertexTypeConfig( export const defaultEdgeTypeConfig = { attributes: [], - ...defaultEdgePreferences, + ...appDefaultEdgeStyle, } satisfies Omit; export function getDefaultEdgeTypeConfig(edgeType: EdgeType): EdgeTypeConfig { diff --git a/packages/graph-explorer/src/core/StateProvider/displayAttribute.ts b/packages/graph-explorer/src/core/StateProvider/displayAttribute.ts index 297d4d5ba..2bd6864cb 100644 --- a/packages/graph-explorer/src/core/StateProvider/displayAttribute.ts +++ b/packages/graph-explorer/src/core/StateProvider/displayAttribute.ts @@ -8,7 +8,7 @@ import { sortAttributeByName } from "./sortAttributeByName"; /** Represents an attribute's display information after all transformations have been applied. */ export type DisplayAttribute = ReturnType; -/** Maps a `Vertex` or `Edge` instance's attributes to a list of `DisplayAttribute` instances using the schema and any user preferences. */ +/** Maps a `Vertex` or `Edge` instance's attributes to a list of `DisplayAttribute` instances using the schema and any user styles. */ export function getSortedDisplayAttributes( entity: Vertex | Edge, textTransform: TextTransformer, diff --git a/packages/graph-explorer/src/core/StateProvider/displayEdge.test.ts b/packages/graph-explorer/src/core/StateProvider/displayEdge.test.ts index 3973768ef..73e17ee93 100644 --- a/packages/graph-explorer/src/core/StateProvider/displayEdge.test.ts +++ b/packages/graph-explorer/src/core/StateProvider/displayEdge.test.ts @@ -19,7 +19,7 @@ import { import { formatDate } from "@/utils"; import { createRandomEdge, - createRandomEdgePreferencesStorageModel, + createRandomEdgeStyleStorage, createRandomEdgeTypeConfig, createRandomRawConfiguration, createRandomSchema, @@ -120,7 +120,7 @@ describe("useDisplayEdgeFromEdge", () => { expect(result.current.displayTypes).toStrictEqual(edge.type); }); - it("should use display label from user preferences", () => { + it("should use display label from user styles", () => { const dbState = new DbState(); const edge = createRandomEdge(); @@ -129,7 +129,7 @@ describe("useDisplayEdgeFromEdge", () => { etConfig.displayLabel = createRandomName("schema"); dbState.activeSchema.edges.push(etConfig); - const edgePrefs = createRandomEdgePreferencesStorageModel(); + const edgePrefs = createRandomEdgeStyleStorage(); edgePrefs.type = edge.type; edgePrefs.displayLabel = createRandomName("prefs"); dbState.edgeStyles.set(edgePrefs.type, edgePrefs); diff --git a/packages/graph-explorer/src/core/StateProvider/displayEdge.ts b/packages/graph-explorer/src/core/StateProvider/displayEdge.ts index 336d8b8e2..e77376505 100644 --- a/packages/graph-explorer/src/core/StateProvider/displayEdge.ts +++ b/packages/graph-explorer/src/core/StateProvider/displayEdge.ts @@ -6,7 +6,7 @@ import { displayEdgeTypeConfigSelector, type Edge, type EdgeId, - edgePreferenceByTypeAtom, + edgeStyleByTypeAtom, edgesAtom, edgeSelector, edgesSelectedIdsAtom, @@ -71,7 +71,7 @@ const displayEdgeSelector = atomFamily((edge: Edge) => const isSparql = queryEngine === "sparql"; // One type config used for shape, color, icon, etc. - const preferences = get(edgePreferenceByTypeAtom(edge.type)); + const style = get(edgeStyleByTypeAtom(edge.type)); // List all edge types for displaying const edgeTypes = [edge.type]; @@ -102,7 +102,7 @@ const displayEdgeSelector = atomFamily((edge: Edge) => } const displayName = getDisplayAttributeValueByName( - preferences.displayNameAttribute, + style.displayNameAttribute, ); const displayEdge: DisplayEdge = { diff --git a/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.test.ts b/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.test.ts index ccd76fb3e..e14445ade 100644 --- a/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.test.ts +++ b/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.test.ts @@ -6,9 +6,9 @@ import type { TextTransformer } from "@/hooks"; import { createEdgeType, createVertexType } from "@/core"; import { LABELS, SEARCH_TOKENS } from "@/utils"; import { - createRandomEdgePreferences, + createRandomEdgeStyle, createRandomEdgeTypeConfig, - createRandomVertexPreferences, + createRandomVertexStyle, createRandomVertexTypeConfig, DbState, renderHookWithState, @@ -60,7 +60,7 @@ describe("useDisplayVertexTypeConfig", () => { expect(result.current.displayLabel).toBe(vtConfig.type); }); - it("should use display label from user preferences", () => { + it("should use display label from user style", () => { const dbState = new DbState(); const vtConfig = createRandomVertexTypeConfig(); vtConfig.displayLabel = createRandomName("displayLabel"); @@ -83,11 +83,11 @@ describe("mapToDisplayVertexTypeConfig", () => { { name: "firstName", dataType: "String" }, { name: "age", dataType: "Number" }, ]; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, identityTextTransform, ); @@ -114,11 +114,11 @@ describe("mapToDisplayVertexTypeConfig", () => { { name: "apple", dataType: "String" }, { name: "middle", dataType: "String" }, ]; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, identityTextTransform, ); @@ -137,11 +137,11 @@ describe("mapToDisplayVertexTypeConfig", () => { { name: "apple", dataType: "String" }, { name: "middle", dataType: "String" }, ]; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, identityTextTransform, ); @@ -161,11 +161,11 @@ describe("mapToDisplayVertexTypeConfig", () => { { name: "ccc", dataType: "String" }, { name: RDFS_LABEL_URI, dataType: "String" }, ]; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, identityTextTransform, ); @@ -184,11 +184,11 @@ describe("mapToDisplayVertexTypeConfig", () => { { name: "name", dataType: "String" }, { name: "email", dataType: "String" }, ]; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, identityTextTransform, ); @@ -215,11 +215,11 @@ describe("mapToDisplayVertexTypeConfig", () => { { name: "active", dataType: "Boolean" }, { name: "created", dataType: "Date" }, ]; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, identityTextTransform, ); @@ -233,11 +233,11 @@ describe("mapToDisplayVertexTypeConfig", () => { { name: "active", dataType: "Boolean" }, { name: "created", dataType: "Date" }, ]; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, identityTextTransform, ); @@ -250,11 +250,11 @@ describe("mapToDisplayVertexTypeConfig", () => { { name: "unknown" }, { name: "name", dataType: "String" }, ]; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, identityTextTransform, ); @@ -277,11 +277,11 @@ describe("mapToDisplayVertexTypeConfig", () => { it("should handle empty attributes array", () => { const vtConfig = createRandomVertexTypeConfig(); vtConfig.attributes = []; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, identityTextTransform, ); @@ -295,7 +295,7 @@ describe("mapToDisplayVertexTypeConfig", () => { { name: "lastName", dataType: "String" }, { name: "email", dataType: "String" }, ]; - const preferences = createRandomVertexPreferences(); + const style = createRandomVertexStyle(); // Custom transformer that uppercases const uppercaseTransform: TextTransformer = (text: string) => @@ -303,7 +303,7 @@ describe("mapToDisplayVertexTypeConfig", () => { const result = mapToDisplayVertexTypeConfig( vtConfig, - preferences, + style, uppercaseTransform, ); @@ -323,11 +323,11 @@ describe("mapToDisplayEdgeTypeConfig", () => { { name: RDFS_LABEL_URI, dataType: "String" }, { name: "created", dataType: "Date" }, ]; - const preferences = createRandomEdgePreferences(); + const style = createRandomEdgeStyle(); const result = mapToDisplayEdgeTypeConfig( etConfig, - preferences, + style, identityTextTransform, ); @@ -362,7 +362,7 @@ describe("useDisplayEdgeTypeConfig", () => { expect(result.current.displayLabel).toBe(etConfig.type); }); - it("should use display label from the user preferences", () => { + it("should use display label from the user style", () => { const dbState = new DbState(); const etConfig = createRandomEdgeTypeConfig(); etConfig.displayLabel = createRandomName("displayLabel"); diff --git a/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.ts b/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.ts index da1bd1c5a..ee62ec113 100644 --- a/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.ts +++ b/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.ts @@ -6,13 +6,13 @@ import { useCallback } from "react"; import { activeSchemaAtom, type AttributeConfig, - edgePreferenceByTypeAtom, - type EdgePreferences, + edgeStyleByTypeAtom, + type EdgeStyle, type EdgeType, type EdgeTypeConfig, edgeTypeConfigSelector, - vertexPreferenceByTypeAtom, - type VertexPreferences, + vertexStyleByTypeAtom, + type VertexStyle, type VertexType, type VertexTypeConfig, vertexTypeConfigSelector, @@ -114,9 +114,9 @@ export function useSearchableAttributes(type: string) { export const displayVertexTypeConfigSelector = atomFamily((type: VertexType) => atom(get => { const textTransform = get(textTransformSelector); - const preferences = get(vertexPreferenceByTypeAtom(type)); + const style = get(vertexStyleByTypeAtom(type)); const typeConfig = get(vertexTypeConfigSelector(type)); - return mapToDisplayVertexTypeConfig(typeConfig, preferences, textTransform); + return mapToDisplayVertexTypeConfig(typeConfig, style, textTransform); }), ); @@ -135,9 +135,9 @@ export const displayVertexTypeConfigsSelector = atom(get => { export const displayEdgeTypeConfigSelector = atomFamily((type: EdgeType) => atom(get => { const textTransform = get(textTransformSelector); - const preferences = get(edgePreferenceByTypeAtom(type)); + const style = get(edgeStyleByTypeAtom(type)); const typeConfig = get(edgeTypeConfigSelector(type)); - return mapToDisplayEdgeTypeConfig(typeConfig, preferences, textTransform); + return mapToDisplayEdgeTypeConfig(typeConfig, style, textTransform); }), ); @@ -154,13 +154,11 @@ export const displayEdgeTypeConfigsSelector = atom(get => { export function mapToDisplayVertexTypeConfig( typeConfig: VertexTypeConfig, - preferences: VertexPreferences, + style: VertexStyle, textTransform: TextTransformer, ): DisplayVertexTypeConfig { const displayLabel = - preferences.displayLabel || - textTransform(typeConfig.type) || - LABELS.MISSING_TYPE; + style.displayLabel || textTransform(typeConfig.type) || LABELS.MISSING_TYPE; const attributes: DisplayConfigAttribute[] = typeConfig.attributes .map(attr => ({ @@ -181,13 +179,11 @@ export function mapToDisplayVertexTypeConfig( export function mapToDisplayEdgeTypeConfig( typeConfig: EdgeTypeConfig, - preferences: EdgePreferences, + style: EdgeStyle, textTransform: TextTransformer, ): DisplayEdgeTypeConfig { const displayLabel = - preferences.displayLabel || - textTransform(typeConfig.type) || - LABELS.MISSING_TYPE; + style.displayLabel || textTransform(typeConfig.type) || LABELS.MISSING_TYPE; const attributes: DisplayConfigAttribute[] = typeConfig.attributes .map(attr => ({ diff --git a/packages/graph-explorer/src/core/StateProvider/displayVertex.test.ts b/packages/graph-explorer/src/core/StateProvider/displayVertex.test.ts index 46d9e13c5..7a0373dd6 100644 --- a/packages/graph-explorer/src/core/StateProvider/displayVertex.test.ts +++ b/packages/graph-explorer/src/core/StateProvider/displayVertex.test.ts @@ -26,7 +26,7 @@ import { createRandomSchema, createRandomVertex, createRandomVertexId, - createRandomVertexPreferencesStorageModel, + createRandomVertexStyleStorage, createRandomVertexTypeConfig, DbState, renderHookWithJotai, @@ -67,7 +67,7 @@ describe("useDisplayVertexFromVertex", () => { expect(act(vertex).displayTypes).toStrictEqual(LABELS.MISSING_TYPE); }); - it("should use the display label from user preferences", () => { + it("should use the display label from user styles", () => { const dbState = new DbState(); const vertex = createRandomVertex(); @@ -77,8 +77,8 @@ describe("useDisplayVertexFromVertex", () => { vtConfig.displayLabel = createRandomName("schema"); dbState.activeSchema.vertices.push(vtConfig); - // User vertex preferences - const userPrefs = createRandomVertexPreferencesStorageModel(); + // User vertex styles + const userPrefs = createRandomVertexStyleStorage(); userPrefs.type = vertex.type; userPrefs.displayLabel = createRandomName("userPrefs"); dbState.vertexStyles.set(userPrefs.type, userPrefs); diff --git a/packages/graph-explorer/src/core/StateProvider/displayVertex.ts b/packages/graph-explorer/src/core/StateProvider/displayVertex.ts index 21b0e2c84..5ca4560eb 100644 --- a/packages/graph-explorer/src/core/StateProvider/displayVertex.ts +++ b/packages/graph-explorer/src/core/StateProvider/displayVertex.ts @@ -12,7 +12,7 @@ import { useVertex, type Vertex, type VertexId, - vertexPreferenceByTypeAtom, + vertexStyleByTypeAtom, type VertexType, } from "@/core"; import { textTransformSelector } from "@/hooks"; @@ -44,12 +44,12 @@ export function useDisplayVerticesInCanvas() { return useAtomValue(displayVerticesInCanvasSelector); } -/** Maps a `Vertex` instance to a `DisplayVertex` instance using the schema and any user preferences. */ +/** Maps a `Vertex` instance to a `DisplayVertex` instance using the schema and any user styles. */ export function useDisplayVertexFromVertex(vertex: Vertex) { return useAtomValue(displayVertexSelector(vertex)); } -/** Maps the `Vertex` instances to a `DisplayVertex` instances using the schema and any user preferences. */ +/** Maps the `Vertex` instances to a `DisplayVertex` instances using the schema and any user styles. */ export function useDisplayVerticesFromVertices(vertices: Vertex[]) { return useAtomValue(displayVerticesSelector(vertices)); } @@ -85,8 +85,7 @@ const displayVertexSelector = atomFamily((vertex: Vertex) => const displayTypes = vertexTypes .map( type => - get(vertexPreferenceByTypeAtom(type)).displayLabel ?? - textTransform(type), + get(vertexStyleByTypeAtom(type)).displayLabel ?? textTransform(type), ) .join(", "); @@ -109,12 +108,12 @@ const displayVertexSelector = atomFamily((vertex: Vertex) => return LABELS.MISSING_VALUE; } - const vertexPreferences = get(vertexPreferenceByTypeAtom(vertex.type)); + const vertexStyle = get(vertexStyleByTypeAtom(vertex.type)); const displayName = getDisplayAttributeValueByName( - vertexPreferences.displayNameAttribute, + vertexStyle.displayNameAttribute, ); const displayDescription = getDisplayAttributeValueByName( - vertexPreferences.longDisplayNameAttribute, + vertexStyle.longDisplayNameAttribute, ); const result: DisplayVertex = { diff --git a/packages/graph-explorer/src/core/StateProvider/userPreferences.test.ts b/packages/graph-explorer/src/core/StateProvider/graphStyles.test.ts similarity index 93% rename from packages/graph-explorer/src/core/StateProvider/userPreferences.test.ts rename to packages/graph-explorer/src/core/StateProvider/graphStyles.test.ts index 3197dff95..0957a2df8 100644 --- a/packages/graph-explorer/src/core/StateProvider/userPreferences.test.ts +++ b/packages/graph-explorer/src/core/StateProvider/graphStyles.test.ts @@ -6,26 +6,26 @@ import { createEdgeType, createVertexType } from "@/core"; import { DbState, renderHookWithState } from "@/utils/testing"; import { - defaultEdgePreferences, - defaultVertexPreferences, - edgePreferencesAtom, - type EdgePreferencesStorageModel, + appDefaultEdgeStyle, + appDefaultVertexStyle, + edgeStyleAtom, + type EdgeStyleStorage, useEdgeStyling, useVertexStyling, - vertexPreferencesAtom, - type VertexPreferencesStorageModel, -} from "./userPreferences"; + vertexStyleAtom, + type VertexStyleStorage, +} from "./graphStyles"; -function createExpectedVertex(existing: VertexPreferencesStorageModel) { +function createExpectedVertex(existing: VertexStyleStorage) { return { - ...defaultVertexPreferences, + ...appDefaultVertexStyle, ...existing, }; } -function createExpectedEdge(existing: EdgePreferencesStorageModel) { +function createExpectedEdge(existing: EdgeStyleStorage) { return { - ...defaultEdgePreferences, + ...appDefaultEdgeStyle, ...existing, }; } @@ -399,14 +399,14 @@ describe("useDeferredAtom integration", () => { }); }); -describe("vertexPreferencesAtom", () => { - it("should return stored preferences for a known type", () => { +describe("vertexStyleAtom", () => { + it("should return stored styles for a known type", () => { const dbState = new DbState(); const vertexType = createVertexType("Person"); dbState.addVertexStyle(vertexType, { color: "#ff0000" }); const { result } = renderHookWithState( - () => useAtomValue(vertexPreferencesAtom), + () => useAtomValue(vertexStyleAtom), dbState, ); @@ -420,7 +420,7 @@ describe("vertexPreferencesAtom", () => { const vertexType = createVertexType("Unknown"); const { result } = renderHookWithState( - () => useAtomValue(vertexPreferencesAtom), + () => useAtomValue(vertexStyleAtom), dbState, ); @@ -430,14 +430,14 @@ describe("vertexPreferencesAtom", () => { }); }); -describe("edgePreferencesAtom", () => { - it("should return stored preferences for a known type", () => { +describe("edgeStyleAtom", () => { + it("should return stored styles for a known type", () => { const dbState = new DbState(); const edgeType = createEdgeType("KNOWS"); dbState.addEdgeStyle(edgeType, { lineColor: "#00ff00" }); const { result } = renderHookWithState( - () => useAtomValue(edgePreferencesAtom), + () => useAtomValue(edgeStyleAtom), dbState, ); @@ -451,7 +451,7 @@ describe("edgePreferencesAtom", () => { const edgeType = createEdgeType("Unknown"); const { result } = renderHookWithState( - () => useAtomValue(edgePreferencesAtom), + () => useAtomValue(edgeStyleAtom), dbState, ); diff --git a/packages/graph-explorer/src/core/StateProvider/userPreferences.ts b/packages/graph-explorer/src/core/StateProvider/graphStyles.ts similarity index 54% rename from packages/graph-explorer/src/core/StateProvider/userPreferences.ts rename to packages/graph-explorer/src/core/StateProvider/graphStyles.ts index f272e6074..3ce752246 100644 --- a/packages/graph-explorer/src/core/StateProvider/userPreferences.ts +++ b/packages/graph-explorer/src/core/StateProvider/graphStyles.ts @@ -63,78 +63,81 @@ export const ARROW_STYLES = [ ] as const; export type ArrowStyle = (typeof ARROW_STYLES)[number]; -/** The user preferences to be used for the specified vertex type as the type used for storing in local storage. */ -export type VertexPreferencesStorageModel = { - type: VertexType; - /** - * Color overwrite for vertex - */ - color?: string; - /** - * Label overwrite for vertex - */ +/** + * The visual appearance of a vertex — the fields that make sense for both a + * per-type style and a type-less global default. Every field is required: this + * is the resolved baseline shape a rendered vertex always has. + */ +export type VertexVisualStyle = { + /** Color overwrite for vertex */ + color: string; + /** Icon overwrite for vertex */ + iconUrl: string; + /** Icon overwrite for vertex */ + iconImageType: string; + shape: ShapeStyle; + backgroundOpacity: number; + borderWidth: number; + borderColor: string; + borderStyle: LineStyle; +}; + +/** + * The type-specific fields of a vertex style — a display label and the + * attributes used to derive labels. These only make sense per vertex type, so + * they are excluded from the type-less global default. + */ +export type VertexTypeStyle = { + /** Label overwrite for vertex */ displayLabel?: string; - /** - * Icon overwrite for vertex - */ - iconUrl?: string; - /** - * Icon overwrite for vertex - */ - iconImageType?: string; - /** - * Vertex attribute to be used as label - */ - displayNameAttribute?: string; - /** - * Vertex attribute to be used as description - */ - longDisplayNameAttribute?: string; - shape?: ShapeStyle; - backgroundOpacity?: number; - borderWidth?: number; - borderColor?: string; - borderStyle?: LineStyle; + /** Vertex attribute to be used as label */ + displayNameAttribute: string; + /** Vertex attribute to be used as description */ + longDisplayNameAttribute: string; +}; + +/** The visual appearance of an edge, shared by per-type styles and defaults. */ +export type EdgeVisualStyle = { + labelColor: string; + labelBackgroundOpacity: number; + labelBorderColor: string; + labelBorderStyle: LineStyle; + labelBorderWidth: number; + lineColor: string; + lineThickness: number; + lineStyle: LineStyle; + sourceArrowStyle: ArrowStyle; + targetArrowStyle: ArrowStyle; }; -/** The user preferences to be used for the specified edge type as the type used for storing in local storage. */ -export type EdgePreferencesStorageModel = { - type: EdgeType; +/** The type-specific fields of an edge style. */ +export type EdgeTypeStyle = { displayLabel?: string; - displayNameAttribute?: string; - labelColor?: string; - labelBackgroundOpacity?: number; - labelBorderColor?: string; - labelBorderStyle?: LineStyle; - labelBorderWidth?: number; - lineColor?: string; - lineThickness?: number; - lineStyle?: LineStyle; - sourceArrowStyle?: ArrowStyle; - targetArrowStyle?: ArrowStyle; + displayNameAttribute: string; }; -/** The user preferences to be used for the specified vertex type as an immutable object. */ -export type VertexPreferences = Simplify< - Readonly< - Pick & - Required> - > +/** The style for the specified vertex type as stored in local storage. */ +export type VertexStyleStorage = Simplify< + Partial & { type: VertexType } +>; + +/** The style for the specified edge type as stored in local storage. */ +export type EdgeStyleStorage = Simplify< + Partial & { type: EdgeType } >; -/** The user preferences to be used for the specified edge type as an immutable object. */ -export type EdgePreferences = Simplify< - Readonly< - Pick & - Required> - > +/** The resolved style for the specified vertex type as an immutable object. */ +export type VertexStyle = Simplify< + Readonly +>; + +/** The resolved style for the specified edge type as an immutable object. */ +export type EdgeStyle = Simplify< + Readonly >; /** The default values to use when no user provided value is given. */ -export const defaultVertexPreferences: Omit< - VertexPreferences, - "type" | "displayLabel" -> = { +export const appDefaultVertexStyle = { displayNameAttribute: RESERVED_ID_PROPERTY, longDisplayNameAttribute: RESERVED_TYPES_PROPERTY, iconUrl: DEFAULT_ICON_URL, @@ -145,13 +148,10 @@ export const defaultVertexPreferences: Omit< borderWidth: 0, borderColor: "#128EE5", borderStyle: "solid", -}; +} as const satisfies Omit; /** The default values to use when no user provided value is given. */ -export const defaultEdgePreferences: Omit< - EdgePreferences, - "type" | "displayLabel" -> = { +export const appDefaultEdgeStyle = { displayNameAttribute: RESERVED_TYPES_PROPERTY, labelColor: "#17457b", labelBackgroundOpacity: 0.7, @@ -163,7 +163,7 @@ export const defaultEdgePreferences: Omit< lineStyle: "solid", sourceArrowStyle: "none", targetArrowStyle: "triangle", -}; +} as const satisfies Omit; /** * @deprecated Legacy IndexedDB shape stored under the `"user-styling"` key. @@ -171,16 +171,16 @@ export const defaultEdgePreferences: Omit< * the startup migration in `migrateUserStyling.ts`. Do not use this type in * new code. */ -export type LegacyUserStylingStorageModel = { - vertices?: Array; - edges?: Array; +export type LegacyUserStylingStorage = { + vertices?: Array; + edges?: Array; }; /** * The styles cascade — the single place vertex/edge appearance is assembled. * * Three layers, lowest precedence first: - * 1. App defaults — {@link defaultVertexPreferences} / {@link defaultEdgePreferences}. + * 1. App defaults — {@link appDefaultVertexStyle} / {@link appDefaultEdgeStyle}. * 2. Shared styles — `sharedVertexStylesAtom` / `sharedEdgeStylesAtom` * (storage keys `shared-vertex-styles` / `shared-edge-styles`), loaded * from a styling file on the Settings → Styles page. @@ -188,21 +188,21 @@ export type LegacyUserStylingStorageModel = { * (storage keys `user-vertex-styles` / `user-edge-styles`), edits made in the * style dialogs. * - * Higher layers override lower ones per field (see {@link createVertexPreference} - * / {@link createEdgePreference}). Related modules: `core/styling` (the styling + * Higher layers override lower ones per field (see {@link resolveVertexStyle} + * / {@link resolveEdgeStyle}). Related modules: `core/styling` (the styling * import parser and import/export hooks) and `core/fileEnvelope` (the file * wrapper). The `icon`↔`iconUrl` rename happens at the file-format seam in the * styling parser, not here. */ -/** Vertex preferences indexed by type for O(1) lookup with cascade fallback. +/** Vertex styles indexed by type for O(1) lookup with cascade fallback. * Cascade: user > shared > app defaults. */ -export const vertexPreferencesAtom = atom(get => { +export const vertexStyleAtom = atom(get => { const userStyles = get(userVertexStylesAtom); const sharedStyles = get(sharedVertexStylesAtom); return { get(type: VertexType) { - return createVertexPreference( + return resolveVertexStyle( type, sharedStyles.get(type), userStyles.get(type), @@ -211,14 +211,14 @@ export const vertexPreferencesAtom = atom(get => { }; }); -/** Edge preferences indexed by type for O(1) lookup with cascade fallback. +/** Edge styles indexed by type for O(1) lookup with cascade fallback. * Cascade: user > shared > app defaults. */ -export const edgePreferencesAtom = atom(get => { +export const edgeStyleAtom = atom(get => { const userStyles = get(userEdgeStylesAtom); const sharedStyles = get(sharedEdgeStylesAtom); return { get(type: EdgeType) { - return createEdgePreference( + return resolveEdgeStyle( type, sharedStyles.get(type), userStyles.get(type), @@ -228,82 +228,82 @@ export const edgePreferencesAtom = atom(get => { }); /** Combines the cascade layers: app defaults < shared < user. */ -export function createVertexPreference( +export function resolveVertexStyle( type: VertexType, - shared?: VertexPreferencesStorageModel, - user?: VertexPreferencesStorageModel, -): VertexPreferences { + shared?: VertexStyleStorage, + user?: VertexStyleStorage, +): VertexStyle { return { type, - ...defaultVertexPreferences, + ...appDefaultVertexStyle, ...shared, ...user, } as const; } /** Combines the cascade layers: app defaults < shared < user. */ -export function createEdgePreference( +export function resolveEdgeStyle( type: EdgeType, - shared?: EdgePreferencesStorageModel, - user?: EdgePreferencesStorageModel, -): EdgePreferences { + shared?: EdgeStyleStorage, + user?: EdgeStyleStorage, +): EdgeStyle { return { type, - ...defaultEdgePreferences, + ...appDefaultEdgeStyle, ...shared, ...user, } as const; } -/** Returns an array of vertex preferences based on the known vertex types in the schema. +/** Returns an array of vertex styles based on the known vertex types in the schema. * Always includes an entry for `LABELS.MISSING_TYPE` so that blank nodes (which are * assigned that synthetic type at runtime) receive icon styling on the canvas. */ -export function useAllVertexPreferences(): VertexPreferences[] { - const prefs = useAtomValue(vertexPreferencesAtom); +export function useAllVertexStyles(): VertexStyle[] { + const styles = useAtomValue(vertexStyleAtom); const { vertices: allSchemas } = useActiveSchema(); - const schemaPrefs = allSchemas.map(({ type }) => prefs.get(type)); + const schemaStyles = allSchemas.map(({ type }) => styles.get(type)); const missingType = LABELS.MISSING_TYPE as VertexType; - const alreadyIncluded = schemaPrefs.some(p => p.type === missingType); + const alreadyIncluded = schemaStyles.some(s => s.type === missingType); if (alreadyIncluded) { - return schemaPrefs; + return schemaStyles; } - return [...schemaPrefs, prefs.get(missingType)]; + return [...schemaStyles, styles.get(missingType)]; } -/** Returns an array of edge preferences based on the known edge types in the schema. */ -export function useAllEdgePreferences(): EdgePreferences[] { - const prefs = useAtomValue(edgePreferencesAtom); +/** Returns an array of edge styles based on the known edge types in the schema. */ +export function useAllEdgeStyles(): EdgeStyle[] { + const styles = useAtomValue(edgeStyleAtom); const { edges: allSchemas } = useActiveSchema(); - return allSchemas.map(({ type }) => prefs.get(type)); + return allSchemas.map(({ type }) => styles.get(type)); } -/** Returns the user preferences for the specified vertex type. */ -export function useVertexPreferences(type: VertexType): VertexPreferences { - return useDeferredValue(useAtomValue(vertexPreferenceByTypeAtom(type))); +/** Returns the resolved style for the specified vertex type. */ +export function useVertexStyle(type: VertexType): VertexStyle { + return useDeferredValue(useAtomValue(vertexStyleByTypeAtom(type))); } -/** Returns the user preferences for the specified edge type. */ -export function useEdgePreferences(type: EdgeType): EdgePreferences { - return useDeferredValue(useAtomValue(edgePreferenceByTypeAtom(type))); +/** Returns the resolved style for the specified edge type. */ +export function useEdgeStyle(type: EdgeType): EdgeStyle { + return useDeferredValue(useAtomValue(edgeStyleByTypeAtom(type))); } /** - * Returns the user preferences for the specified vertex type. + * Returns the resolved style for the specified vertex type. */ -export const vertexPreferenceByTypeAtom = atomFamily((type: VertexType) => - atom(get => get(vertexPreferencesAtom).get(type)), +export const vertexStyleByTypeAtom = atomFamily((type: VertexType) => + atom(get => get(vertexStyleAtom).get(type)), ); /** - * Returns the user preferences for the specified edge type. + * Returns the resolved style for the specified edge type. */ -export const edgePreferenceByTypeAtom = atomFamily((type: EdgeType) => - atom(get => get(edgePreferencesAtom).get(type)), +export const edgeStyleByTypeAtom = atomFamily((type: EdgeType) => + atom(get => get(edgeStyleAtom).get(type)), ); -type UpdatedVertexStyle = Partial>; +type UpdatedVertexStyle = Partial>; /** * Provides the necessary functions for managing vertex styles. @@ -313,7 +313,7 @@ type UpdatedVertexStyle = Partial>; */ export function useVertexStyling(type: VertexType) { const setVertexStyles = useSetAtom(userVertexStylesAtom); - const vertexStyle = useVertexPreferences(type); + const vertexStyle = useVertexStyle(type); const setVertexStyle = (updatedStyle: UpdatedVertexStyle) => setVertexStyles(prev => @@ -334,7 +334,7 @@ export function useVertexStyling(type: VertexType) { }; } -type UpdatedEdgeStyle = Partial>; +type UpdatedEdgeStyle = Partial>; /** * Provides the necessary functions for managing edge styles. @@ -344,7 +344,7 @@ type UpdatedEdgeStyle = Partial>; */ export function useEdgeStyling(type: EdgeType) { const setEdgeStyles = useSetAtom(userEdgeStylesAtom); - const edgeStyle = useEdgePreferences(type); + const edgeStyle = useEdgeStyle(type); const setEdgeStyle = (updatedStyle: UpdatedEdgeStyle) => setEdgeStyles(prev => diff --git a/packages/graph-explorer/src/core/StateProvider/index.ts b/packages/graph-explorer/src/core/StateProvider/index.ts index 37b324990..a215ca8c7 100644 --- a/packages/graph-explorer/src/core/StateProvider/index.ts +++ b/packages/graph-explorer/src/core/StateProvider/index.ts @@ -10,7 +10,7 @@ export * from "./featureFlags"; export * from "./neighbors"; export * from "./nodes"; export * from "./renderedEntities"; -export * from "./userPreferences"; +export * from "./graphStyles"; export * from "./schema"; export * from "./storageAtoms"; export * from "./graphSession"; diff --git a/packages/graph-explorer/src/core/StateProvider/migrateUserStyling.test.ts b/packages/graph-explorer/src/core/StateProvider/migrateUserStyling.test.ts index df94ca0e4..906803367 100644 --- a/packages/graph-explorer/src/core/StateProvider/migrateUserStyling.test.ts +++ b/packages/graph-explorer/src/core/StateProvider/migrateUserStyling.test.ts @@ -5,10 +5,10 @@ import { createEdgeType, createVertexType } from "@/core/entities"; import { logger } from "@/utils"; import type { - EdgePreferencesStorageModel, - LegacyUserStylingStorageModel, - VertexPreferencesStorageModel, -} from "./userPreferences"; + EdgeStyleStorage, + LegacyUserStylingStorage, + VertexStyleStorage, +} from "./graphStyles"; import { migrateUserStylingIfNeeded, @@ -20,8 +20,8 @@ import { persistenceStatusStore } from "./persistence"; * BACKWARD COMPATIBILITY — PERSISTED DATA * * Older versions stored all styling under a single `"user-styling"` key as a - * `LegacyUserStylingStorageModel` object: `{ vertices: VertexPreferencesStorageModel[], edges: - * EdgePreferencesStorageModel[] }`. Styling is now stored as two type-keyed maps + * `LegacyUserStylingStorage` object: `{ vertices: VertexStyleStorage[], edges: + * EdgeStyleStorage[] }`. Styling is now stored as two type-keyed maps * under `"user-vertex-styles"` and `"user-edge-styles"` — the user-defined layer of the * planned `--styles` set. This migration converts the old shape * to the new one on startup. @@ -34,15 +34,15 @@ import { persistenceStatusStore } from "./persistence"; */ describe("migrateUserStylingIfNeeded", () => { it("converts old user-styling arrays into type-keyed maps", async () => { - const vertexStyle: VertexPreferencesStorageModel = { + const vertexStyle: VertexStyleStorage = { type: createVertexType("Person"), color: "#ff0000", }; - const edgeStyle: EdgePreferencesStorageModel = { + const edgeStyle: EdgeStyleStorage = { type: createEdgeType("knows"), lineColor: "#00ff00", }; - await localForage.setItem("user-styling", { + await localForage.setItem("user-styling", { vertices: [vertexStyle], edges: [edgeStyle], }); @@ -58,14 +58,11 @@ describe("migrateUserStylingIfNeeded", () => { }); it("leaves the old user-styling key untouched as a rollback escape hatch", async () => { - const old: LegacyUserStylingStorageModel = { + const old: LegacyUserStylingStorage = { vertices: [{ type: createVertexType("Person") }], edges: [{ type: createEdgeType("knows") }], }; - await localForage.setItem( - "user-styling", - old, - ); + await localForage.setItem("user-styling", old); await migrateUserStylingIfNeeded(); @@ -88,7 +85,7 @@ describe("migrateUserStylingIfNeeded", () => { ]); await localForage.setItem("user-vertex-styles", existingVertexStyles); await localForage.setItem("user-edge-styles", existingEdgeStyles); - await localForage.setItem("user-styling", { + await localForage.setItem("user-styling", { vertices: [{ type: createVertexType("Person") }], edges: [{ type: createEdgeType("knows") }], }); @@ -105,16 +102,16 @@ describe("migrateUserStylingIfNeeded", () => { }); it("completes a partial write when only vertex-styles exists", async () => { - const vertexStyle: VertexPreferencesStorageModel = { + const vertexStyle: VertexStyleStorage = { type: createVertexType("Person"), color: "#ff0000", }; - const edgeStyle: EdgePreferencesStorageModel = { + const edgeStyle: EdgeStyleStorage = { type: createEdgeType("knows"), lineColor: "#00ff00", }; // Simulate a crash after vertex-styles was written but before edge-styles was written. - await localForage.setItem("user-styling", { + await localForage.setItem("user-styling", { vertices: [vertexStyle], edges: [edgeStyle], }); @@ -136,16 +133,16 @@ describe("migrateUserStylingIfNeeded", () => { }); it("completes a partial write when only edge-styles exists", async () => { - const vertexStyle: VertexPreferencesStorageModel = { + const vertexStyle: VertexStyleStorage = { type: createVertexType("Person"), color: "#ff0000", }; - const edgeStyle: EdgePreferencesStorageModel = { + const edgeStyle: EdgeStyleStorage = { type: createEdgeType("knows"), lineColor: "#00ff00", }; // Simulate a crash after edge-styles was written but before vertex-styles was written. - await localForage.setItem("user-styling", { + await localForage.setItem("user-styling", { vertices: [vertexStyle], edges: [edgeStyle], }); @@ -170,15 +167,15 @@ describe("migrateUserStylingIfNeeded", () => { // The legacy snapshot is never updated post-migration, so a key that // survives a partial loss may hold newer edits the snapshot doesn't know // about. Recovery must fill the missing key without clobbering the edited one. - const legacyVertexStyle: VertexPreferencesStorageModel = { + const legacyVertexStyle: VertexStyleStorage = { type: createVertexType("Person"), color: "#ff0000", }; - const edgeStyle: EdgePreferencesStorageModel = { + const edgeStyle: EdgeStyleStorage = { type: createEdgeType("knows"), lineColor: "#00ff00", }; - await localForage.setItem("user-styling", { + await localForage.setItem("user-styling", { vertices: [legacyVertexStyle], edges: [edgeStyle], }); @@ -202,15 +199,15 @@ describe("migrateUserStylingIfNeeded", () => { }); it("collapses duplicate types to the last entry", async () => { - const first: VertexPreferencesStorageModel = { + const first: VertexStyleStorage = { type: createVertexType("Person"), color: "#111111", }; - const second: VertexPreferencesStorageModel = { + const second: VertexStyleStorage = { type: createVertexType("Person"), color: "#222222", }; - await localForage.setItem("user-styling", { + await localForage.setItem("user-styling", { vertices: [first, second], }); @@ -222,7 +219,7 @@ describe("migrateUserStylingIfNeeded", () => { }); it("logs each storage key it migrates", async () => { - await localForage.setItem("user-styling", { + await localForage.setItem("user-styling", { vertices: [{ type: createVertexType("Person") }], edges: [{ type: createEdgeType("knows") }], }); @@ -238,11 +235,11 @@ describe("migrateUserStylingIfNeeded", () => { }); it("only logs the key it actually migrates during partial-write recovery", async () => { - const edgeStyle: EdgePreferencesStorageModel = { + const edgeStyle: EdgeStyleStorage = { type: createEdgeType("knows"), lineColor: "#00ff00", }; - await localForage.setItem("user-styling", { + await localForage.setItem("user-styling", { vertices: [{ type: createVertexType("Person") }], edges: [edgeStyle], }); diff --git a/packages/graph-explorer/src/core/StateProvider/migrateUserStyling.ts b/packages/graph-explorer/src/core/StateProvider/migrateUserStyling.ts index b0850acd5..3f7d02914 100644 --- a/packages/graph-explorer/src/core/StateProvider/migrateUserStyling.ts +++ b/packages/graph-explorer/src/core/StateProvider/migrateUserStyling.ts @@ -4,7 +4,7 @@ import { logger } from "@/utils"; import { createErrorDetails } from "@/utils/createErrorDetails"; import type { EdgeType, VertexType } from "../entities"; -import type { LegacyUserStylingStorageModel } from "./userPreferences"; +import type { LegacyUserStylingStorage } from "./graphStyles"; import { persistenceStatusStore } from "./persistence"; import { classifyStorageError } from "./persistence/classifyStorageError"; @@ -68,7 +68,7 @@ export async function migrateUserStylingIfNeeded() { } const old = - await localForage.getItem("user-styling"); + await localForage.getItem("user-styling"); if (!old) { return; } diff --git a/packages/graph-explorer/src/core/StateProvider/storageAtoms.ts b/packages/graph-explorer/src/core/StateProvider/storageAtoms.ts index a2c24fbd7..f987a21e2 100644 --- a/packages/graph-explorer/src/core/StateProvider/storageAtoms.ts +++ b/packages/graph-explorer/src/core/StateProvider/storageAtoms.ts @@ -4,11 +4,8 @@ import type { } from "../ConfigurationProvider"; import type { EdgeType, VertexType } from "../entities"; import type { GraphSessionStorageModel } from "./graphSession/storage"; +import type { EdgeStyleStorage, VertexStyleStorage } from "./graphStyles"; import type { SchemaStorageModel } from "./schema"; -import type { - EdgePreferencesStorageModel, - VertexPreferencesStorageModel, -} from "./userPreferences"; import { createActiveConfigurationAtom } from "./activeConnectionStorage"; import { atomWithLocalForage, reconcileMapByKey } from "./atomWithLocalForage"; @@ -93,13 +90,13 @@ const [ */ atomWithLocalForage( "user-vertex-styles", - new Map(), + new Map(), reconcileMapByKey, ), /** User-defined edge style overrides, keyed by type. */ atomWithLocalForage( "user-edge-styles", - new Map(), + new Map(), reconcileMapByKey, ), /** @@ -109,13 +106,13 @@ const [ */ atomWithLocalForage( "shared-vertex-styles", - new Map(), + new Map(), reconcileMapByKey, ), /** Shared edge styles, loaded from a styling file. */ atomWithLocalForage( "shared-edge-styles", - new Map(), + new Map(), reconcileMapByKey, ), atomWithLocalForage("graph-view-layout", defaultGraphViewLayout), diff --git a/packages/graph-explorer/src/core/styling/roundTrip.test.ts b/packages/graph-explorer/src/core/styling/roundTrip.test.ts index c69a918d1..a2e46372b 100644 --- a/packages/graph-explorer/src/core/styling/roundTrip.test.ts +++ b/packages/graph-explorer/src/core/styling/roundTrip.test.ts @@ -3,9 +3,9 @@ import { describe, expect, test, vi } from "vitest"; import type { VertexType, EdgeType } from "@/core/entities"; import type { - VertexPreferencesStorageModel, - EdgePreferencesStorageModel, -} from "@/core/StateProvider/userPreferences"; + VertexStyleStorage, + EdgeStyleStorage, +} from "@/core/StateProvider/graphStyles"; import { getAppStore } from "@/core"; import { createEdgeType, createVertexType } from "@/core/entities"; @@ -35,7 +35,7 @@ describe("round-trip: export then import", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("airport"), { @@ -57,7 +57,7 @@ describe("round-trip: export then import", () => { ); store.set( userEdgeStylesAtom, - new Map([ + new Map([ [ createEdgeType("route"), { @@ -131,7 +131,7 @@ describe("round-trip: export then import", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("http://data.nobelprize.org/terms/LaureateAward"), { @@ -226,7 +226,7 @@ describe("round-trip: export then import", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("CustomNode"), { @@ -269,7 +269,7 @@ describe("round-trip: export then import", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("PngNode"), { @@ -332,7 +332,7 @@ describe("round-trip: export then import", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("BmpNode"), { @@ -378,7 +378,7 @@ describe("round-trip: export then import", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("HttpsNode"), { @@ -425,7 +425,7 @@ describe("round-trip: export then import", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("LucideType"), { diff --git a/packages/graph-explorer/src/core/styling/stylingParser.ts b/packages/graph-explorer/src/core/styling/stylingParser.ts index 432351262..b38e2348d 100644 --- a/packages/graph-explorer/src/core/styling/stylingParser.ts +++ b/packages/graph-explorer/src/core/styling/stylingParser.ts @@ -8,11 +8,11 @@ import { createEdgeType, createVertexType } from "@/core/entities"; import { FileEnvelopeError } from "@/core/fileEnvelope"; import { ARROW_STYLES, - type EdgePreferencesStorageModel, + type EdgeStyleStorage, LINE_STYLES, SHAPE_STYLES, - type VertexPreferencesStorageModel, -} from "@/core/StateProvider/userPreferences"; + type VertexStyleStorage, +} from "@/core/StateProvider/graphStyles"; import { typedEntries } from "@/utils"; // --- Format identity --- @@ -62,8 +62,8 @@ export type EntryImportIssue = { export type ImportIssue = GeneralImportIssue | EntryImportIssue; export type StylingParseResult = { - vertexStyles: Map; - edgeStyles: Map; + vertexStyles: Map; + edgeStyles: Map; }; /** @@ -140,7 +140,7 @@ const vertexEntrySchema = z borderStyle: z.enum(LINE_STYLES).optional(), }) .transform( - ({ icon, ...rest }): Omit => + ({ icon, ...rest }): Omit => icon !== undefined ? { ...rest, iconUrl: icon } : rest, ); @@ -178,7 +178,7 @@ export type StylingExportPayload = { }; export function toVertexFileEntry( - model: VertexPreferencesStorageModel, + model: VertexStyleStorage, ): VertexStyleFileEntry { const { type: _type, iconUrl, ...rest } = model; // The file format uses `icon`; storage uses `iconUrl`. Every other field maps @@ -186,9 +186,7 @@ export function toVertexFileEntry( return iconUrl !== undefined ? { ...rest, icon: iconUrl } : rest; } -export function toEdgeFileEntry( - model: EdgePreferencesStorageModel, -): EdgeStyleFileEntry { +export function toEdgeFileEntry(model: EdgeStyleStorage): EdgeStyleFileEntry { const { type: _type, ...rest } = model; return rest; } diff --git a/packages/graph-explorer/src/core/styling/useStylingImportExport.test.ts b/packages/graph-explorer/src/core/styling/useStylingImportExport.test.ts index 3c6be981c..14dff0b2a 100644 --- a/packages/graph-explorer/src/core/styling/useStylingImportExport.test.ts +++ b/packages/graph-explorer/src/core/styling/useStylingImportExport.test.ts @@ -3,9 +3,9 @@ import { describe, expect, test, vi } from "vitest"; import type { VertexType, EdgeType } from "@/core/entities"; import type { - VertexPreferencesStorageModel, - EdgePreferencesStorageModel, -} from "@/core/StateProvider/userPreferences"; + VertexStyleStorage, + EdgeStyleStorage, +} from "@/core/StateProvider/graphStyles"; import { getAppStore } from "@/core"; import { createEdgeType, createVertexType } from "@/core/entities"; @@ -69,7 +69,7 @@ describe("styling import", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("Person"), { type: createVertexType("Person"), color: "#111" }, @@ -95,7 +95,7 @@ describe("styling import", () => { const store = getAppStore(); store.set( sharedVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("OldType"), { type: createVertexType("OldType"), color: "#old" }, @@ -119,16 +119,13 @@ describe("styling import", () => { }); test("getStylingConflicts identifies overlapping keys", () => { - const sharedVertexStyles = new Map< - VertexType, - VertexPreferencesStorageModel - >([ + const sharedVertexStyles = new Map([ [ createVertexType("Person"), { type: createVertexType("Person"), color: "#existing" }, ], ]); - const sharedEdgeStyles = new Map([ + const sharedEdgeStyles = new Map([ [ createEdgeType("knows"), { type: createEdgeType("knows"), lineColor: "#old" }, @@ -136,7 +133,7 @@ describe("styling import", () => { ]); const parsed = { - vertexStyles: new Map([ + vertexStyles: new Map([ [ createVertexType("Person"), { type: createVertexType("Person"), color: "#new" }, @@ -146,7 +143,7 @@ describe("styling import", () => { { type: createVertexType("Airport"), color: "#fresh" }, ], ]), - edgeStyles: new Map([ + edgeStyles: new Map([ [ createEdgeType("knows"), { type: createEdgeType("knows"), lineColor: "#new" }, @@ -167,19 +164,19 @@ describe("styling import", () => { test("getStylingConflicts returns empty when no overlap", () => { const parsed = { - vertexStyles: new Map([ + vertexStyles: new Map([ [ createVertexType("Brand New"), { type: createVertexType("Brand New"), color: "#aaa" }, ], ]), - edgeStyles: new Map(), + edgeStyles: new Map(), }; const conflicts = getStylingConflicts( parsed, - new Map(), - new Map(), + new Map(), + new Map(), ); expect(conflicts).toStrictEqual({ vertices: [], edges: [] }); }); @@ -240,7 +237,7 @@ describe("useExportStylingFile", () => { const store = getAppStore(); store.set( sharedVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("Person"), { @@ -253,7 +250,7 @@ describe("useExportStylingFile", () => { ); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("Person"), { type: createVertexType("Person"), color: "#user" }, @@ -262,7 +259,7 @@ describe("useExportStylingFile", () => { ); store.set( sharedEdgeStylesAtom, - new Map([ + new Map([ [ createEdgeType("route"), { type: createEdgeType("route"), lineColor: "#edge-shared" }, @@ -286,7 +283,7 @@ describe("useExportStylingFile", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("City"), { type: createVertexType("City"), color: "#city" }, @@ -305,7 +302,7 @@ describe("useExportStylingFile", () => { const store = getAppStore(); store.set( sharedEdgeStylesAtom, - new Map([ + new Map([ [ createEdgeType("likes"), { type: createEdgeType("likes"), lineStyle: "dashed" }, @@ -331,7 +328,7 @@ describe("useExportStylingFile", () => { const store = getAppStore(); store.set( userVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("Airport"), { diff --git a/packages/graph-explorer/src/core/styling/useStylingImportExport.ts b/packages/graph-explorer/src/core/styling/useStylingImportExport.ts index 680ddff7c..820cc8a23 100644 --- a/packages/graph-explorer/src/core/styling/useStylingImportExport.ts +++ b/packages/graph-explorer/src/core/styling/useStylingImportExport.ts @@ -2,9 +2,9 @@ import { useAtomValue, useSetAtom } from "jotai"; import type { EdgeType, VertexType } from "@/core/entities"; import type { - EdgePreferencesStorageModel, - VertexPreferencesStorageModel, -} from "@/core/StateProvider/userPreferences"; + EdgeStyleStorage, + VertexStyleStorage, +} from "@/core/StateProvider/graphStyles"; import { parseFileEnvelope } from "@/core/fileEnvelope"; import { @@ -57,8 +57,8 @@ export async function parseStylingFile( */ export function getStylingConflicts( parsed: StylingParseResult, - sharedVertexStyles: Map, - sharedEdgeStyles: Map, + sharedVertexStyles: Map, + sharedEdgeStyles: Map, ): ImportConflicts { const vertices: string[] = []; const edges: string[] = []; diff --git a/packages/graph-explorer/src/modules/ConnectionDetail/ConnectionData.tsx b/packages/graph-explorer/src/modules/ConnectionDetail/ConnectionData.tsx index 154567691..3a156dcfc 100644 --- a/packages/graph-explorer/src/modules/ConnectionDetail/ConnectionData.tsx +++ b/packages/graph-explorer/src/modules/ConnectionDetail/ConnectionData.tsx @@ -17,7 +17,7 @@ import { import { type DisplayVertexTypeConfig, useDisplayVertexTypeConfigs, - useVertexPreferences, + useVertexStyle, } from "@/core"; import useTranslations from "@/hooks/useTranslations"; import { cn } from "@/utils"; @@ -103,16 +103,13 @@ function VertexTypeList({ function Row({ config }: { config: DisplayVertexTypeConfig }) { const t = useTranslations(); - const vertexPreferences = useVertexPreferences(config.type); + const vertexStyle = useVertexStyle(config.type); const unit = config.attributes.length === 1 ? t("property") : t("properties"); return (
- + {config.displayLabel} diff --git a/packages/graph-explorer/src/modules/EdgesStyling/EdgeStyleDialog.tsx b/packages/graph-explorer/src/modules/EdgesStyling/EdgeStyleDialog.tsx index 1be478523..ab84d24aa 100644 --- a/packages/graph-explorer/src/modules/EdgesStyling/EdgeStyleDialog.tsx +++ b/packages/graph-explorer/src/modules/EdgesStyling/EdgeStyleDialog.tsx @@ -34,7 +34,7 @@ import { type ArrowStyle, type LineStyle, useEdgeStyling, -} from "@/core/StateProvider/userPreferences"; +} from "@/core/StateProvider/graphStyles"; import useTranslations from "@/hooks/useTranslations"; import { parseNumberSafely, RESERVED_TYPES_PROPERTY } from "@/utils"; diff --git a/packages/graph-explorer/src/modules/EdgesStyling/SingleEdgeStyling.tsx b/packages/graph-explorer/src/modules/EdgesStyling/SingleEdgeStyling.tsx index 486b5f3b2..bb33c25d6 100644 --- a/packages/graph-explorer/src/modules/EdgesStyling/SingleEdgeStyling.tsx +++ b/packages/graph-explorer/src/modules/EdgesStyling/SingleEdgeStyling.tsx @@ -2,7 +2,7 @@ import { type ComponentPropsWithRef, useEffect, useState } from "react"; import { Button, FormItem, InputField, Label, StylingIcon } from "@/components"; import { type EdgeType, useDisplayEdgeTypeConfig } from "@/core"; -import { useEdgeStyling } from "@/core/StateProvider/userPreferences"; +import { useEdgeStyling } from "@/core/StateProvider/graphStyles"; import { useDebounceValue, usePrevious } from "@/hooks"; import { LABELS } from "@/utils"; diff --git a/packages/graph-explorer/src/modules/EntityDetails/EdgeDetail.tsx b/packages/graph-explorer/src/modules/EntityDetails/EdgeDetail.tsx index 9f1a6d862..a80ca7671 100644 --- a/packages/graph-explorer/src/modules/EntityDetails/EdgeDetail.tsx +++ b/packages/graph-explorer/src/modules/EntityDetails/EdgeDetail.tsx @@ -23,7 +23,7 @@ import { type DisplayEdge, type LineStyle, useDisplayVertex, - useEdgePreferences, + useEdgeStyle, } from "@/core"; import { useTranslations } from "@/hooks"; import { @@ -42,7 +42,7 @@ const EdgeDetail = ({ edge }: EdgeDetailProps) => { const sourceVertex = useDisplayVertex(edge.sourceId); const targetVertex = useDisplayVertex(edge.targetId); - const style = useEdgePreferences(edge.type); + const style = useEdgeStyle(edge.type); const allAttributes: DisplayAttribute[] = [ ...(edge.hasUniqueId diff --git a/packages/graph-explorer/src/modules/GraphViewer/useBackgroundImageMap.test.ts b/packages/graph-explorer/src/modules/GraphViewer/useBackgroundImageMap.test.ts index c5f7c10dd..c681f1f97 100644 --- a/packages/graph-explorer/src/modules/GraphViewer/useBackgroundImageMap.test.ts +++ b/packages/graph-explorer/src/modules/GraphViewer/useBackgroundImageMap.test.ts @@ -2,9 +2,9 @@ import { waitFor } from "@testing-library/react"; import { beforeEach, describe, expect, it, type Mock, vi } from "vitest"; -import { defaultVertexPreferences } from "@/core"; +import { appDefaultVertexStyle } from "@/core"; import { - createRandomVertexPreferences, + createRandomVertexStyle, DbState, renderHookWithState, } from "@/utils/testing"; @@ -35,7 +35,7 @@ describe("useBackgroundImageMap", () => { }); it("should generate background image map for single vertex config", async () => { - const vertexConfig = createRandomVertexPreferences(); + const vertexConfig = createRandomVertexStyle(); const expectedImage = "data:image/svg+xml;utf8,person"; mockRenderNode.mockResolvedValue(expectedImage); @@ -55,8 +55,8 @@ describe("useBackgroundImageMap", () => { }); it("should generate background image map for multiple vertex configs", async () => { - const config1 = createRandomVertexPreferences(); - const config2 = createRandomVertexPreferences(); + const config1 = createRandomVertexStyle(); + const config2 = createRandomVertexStyle(); const image1 = "data:image/svg+xml;utf8,first"; const image2 = "data:image/svg+xml;utf8,second"; @@ -79,8 +79,8 @@ describe("useBackgroundImageMap", () => { }); it("should filter out failed renders from the map", async () => { - const config1 = createRandomVertexPreferences(); - const config2 = createRandomVertexPreferences(); + const config1 = createRandomVertexStyle(); + const config2 = createRandomVertexStyle(); const successImage = "data:image/svg+xml;utf8,success"; @@ -101,7 +101,7 @@ describe("useBackgroundImageMap", () => { }); it("should handle renderNode returning undefined", async () => { - const vertexConfig = createRandomVertexPreferences(); + const vertexConfig = createRandomVertexStyle(); mockRenderNode.mockResolvedValue(undefined); const { result } = renderHookWithState( @@ -116,7 +116,7 @@ describe("useBackgroundImageMap", () => { }); it("should handle renderNode throwing an error", async () => { - const vertexConfig = createRandomVertexPreferences(); + const vertexConfig = createRandomVertexStyle(); mockRenderNode.mockRejectedValue(new Error("Render failed")); const { result } = renderHookWithState( @@ -131,8 +131,8 @@ describe("useBackgroundImageMap", () => { }); it("should update map when vertex configs change", async () => { - const initialConfig = createRandomVertexPreferences(); - const updatedConfig = createRandomVertexPreferences(); + const initialConfig = createRandomVertexStyle(); + const updatedConfig = createRandomVertexStyle(); const initialImage = "data:image/svg+xml;utf8,initial"; const updatedImage = "data:image/svg+xml;utf8,updated"; @@ -165,9 +165,9 @@ describe("useBackgroundImageMap", () => { it("should handle mixed success and failure renders", async () => { const configs = [ - createRandomVertexPreferences(), - createRandomVertexPreferences(), - createRandomVertexPreferences(), + createRandomVertexStyle(), + createRandomVertexStyle(), + createRandomVertexStyle(), ]; const image1 = "data:image/svg+xml;utf8,first"; @@ -192,7 +192,7 @@ describe("useBackgroundImageMap", () => { }); it("should use correct query key for caching", async () => { - const vertexConfig = createRandomVertexPreferences(); + const vertexConfig = createRandomVertexStyle(); mockRenderNode.mockResolvedValue("data:image/svg+xml;utf8,"); renderHookWithState(() => useBackgroundImageMap([vertexConfig]), dbState); @@ -210,8 +210,8 @@ describe("useBackgroundImageMap", () => { }); it("should handle vertex configs with different image types", async () => { - const svgConfig = createRandomVertexPreferences(); - const pngConfig = createRandomVertexPreferences(); + const svgConfig = createRandomVertexStyle(); + const pngConfig = createRandomVertexStyle(); const svgImage = "data:image/svg+xml;utf8,svg"; const pngImage = "https://example.com/image.png"; @@ -232,11 +232,11 @@ describe("useBackgroundImageMap", () => { }); it("should handle vertex configs with no icon URL", async () => { - const configWithIcon = createRandomVertexPreferences(); + const configWithIcon = createRandomVertexStyle(); configWithIcon.iconUrl = "https://example.com/icon.svg"; - const configWithoutIcon = createRandomVertexPreferences(); - configWithoutIcon.iconUrl = defaultVertexPreferences.iconUrl; + const configWithoutIcon = createRandomVertexStyle(); + configWithoutIcon.iconUrl = appDefaultVertexStyle.iconUrl; const successImage = "data:image/svg+xml;utf8,success"; diff --git a/packages/graph-explorer/src/modules/GraphViewer/useBackgroundImageMap.ts b/packages/graph-explorer/src/modules/GraphViewer/useBackgroundImageMap.ts index b0cf17996..c92757b4c 100644 --- a/packages/graph-explorer/src/modules/GraphViewer/useBackgroundImageMap.ts +++ b/packages/graph-explorer/src/modules/GraphViewer/useBackgroundImageMap.ts @@ -1,6 +1,6 @@ import { useQueries } from "@tanstack/react-query"; -import type { VertexPreferences } from "@/core"; +import type { VertexStyle } from "@/core"; import { renderNode } from "./renderNode"; @@ -13,7 +13,7 @@ import { renderNode } from "./renderNode"; * @returns A Map where keys are vertex type names and values are their * corresponding background image strings */ -export function useBackgroundImageMap(vtConfigs: VertexPreferences[]) { +export function useBackgroundImageMap(vtConfigs: VertexStyle[]) { return useQueries({ queries: vtConfigs.map(vtConfig => ({ queryKey: ["vertexIcon", vtConfig], diff --git a/packages/graph-explorer/src/modules/GraphViewer/useGraphStyles.ts b/packages/graph-explorer/src/modules/GraphViewer/useGraphStyles.ts index 68b33e57e..0333616c9 100644 --- a/packages/graph-explorer/src/modules/GraphViewer/useGraphStyles.ts +++ b/packages/graph-explorer/src/modules/GraphViewer/useGraphStyles.ts @@ -4,10 +4,10 @@ import { useDeferredValue } from "react"; import type { GraphProps } from "@/components/Graph"; import { - type EdgePreferences, - useAllEdgePreferences, - useAllVertexPreferences, - type VertexPreferences, + type EdgeStyle, + useAllEdgeStyles, + useAllVertexStyles, + type VertexStyle, } from "@/core"; import { useBackgroundImageMap } from "./useBackgroundImageMap"; @@ -19,8 +19,8 @@ const LINE_PATTERN = { }; export default function useGraphStyles() { - const vtConfigs = useAllVertexPreferences(); - const etConfigs = useAllEdgePreferences(); + const vtConfigs = useAllVertexStyles(); + const etConfigs = useAllEdgeStyles(); const deferredVtConfigs = useDeferredValue(vtConfigs); const deferredEtConfigs = useDeferredValue(etConfigs); @@ -35,8 +35,8 @@ export default function useGraphStyles() { } function createGraphStyles( - deferredVtConfigs: VertexPreferences[], - deferredEtConfigs: EdgePreferences[], + deferredVtConfigs: VertexStyle[], + deferredEtConfigs: EdgeStyle[], backgroundImageMap: Map, ): GraphProps["styles"] { const styles: GraphProps["styles"] = {}; diff --git a/packages/graph-explorer/src/modules/NodesStyling/NodeStyleDialog.tsx b/packages/graph-explorer/src/modules/NodesStyling/NodeStyleDialog.tsx index b37d4c3f8..76eb72472 100644 --- a/packages/graph-explorer/src/modules/NodesStyling/NodeStyleDialog.tsx +++ b/packages/graph-explorer/src/modules/NodesStyling/NodeStyleDialog.tsx @@ -34,7 +34,7 @@ import { type LineStyle, type ShapeStyle, useVertexStyling, -} from "@/core/StateProvider/userPreferences"; +} from "@/core/StateProvider/graphStyles"; import { isAllowedIconValue } from "@/core/styling"; import useTranslations from "@/hooks/useTranslations"; import { parseNumberSafely } from "@/utils"; diff --git a/packages/graph-explorer/src/modules/NodesStyling/SingleNodeStyling.tsx b/packages/graph-explorer/src/modules/NodesStyling/SingleNodeStyling.tsx index a84c9eb44..16d0487d8 100644 --- a/packages/graph-explorer/src/modules/NodesStyling/SingleNodeStyling.tsx +++ b/packages/graph-explorer/src/modules/NodesStyling/SingleNodeStyling.tsx @@ -2,7 +2,7 @@ import { type ComponentPropsWithRef, useEffect, useState } from "react"; import { Button, FormItem, InputField, Label, StylingIcon } from "@/components"; import { useDisplayVertexTypeConfig, type VertexType } from "@/core"; -import { useVertexStyling } from "@/core/StateProvider/userPreferences"; +import { useVertexStyling } from "@/core/StateProvider/graphStyles"; import { useDebounceValue, usePrevious } from "@/hooks"; import { LABELS } from "@/utils/constants"; diff --git a/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/Details.test.tsx b/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/Details.test.tsx index 043801e7f..5b2d7ce9c 100644 --- a/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/Details.test.tsx +++ b/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/Details.test.tsx @@ -41,7 +41,7 @@ vi.mock("@/core", async () => { displayLabel: vertexType, attributes: [], }), - useVertexPreferences: () => ({ color: "#000000" }), + useVertexStyle: () => ({ color: "#000000" }), }; }); diff --git a/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/Details.tsx b/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/Details.tsx index 4e201326e..d32045b45 100644 --- a/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/Details.tsx +++ b/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/Details.tsx @@ -21,7 +21,7 @@ import { type EdgeType, useDisplayEdgeTypeConfig, useDisplayVertexTypeConfig, - useVertexPreferences, + useVertexStyle, type VertexType, } from "@/core"; import { useTranslations } from "@/hooks"; @@ -282,7 +282,7 @@ function VertexTypeText({ selected: boolean; onClick?: () => void; }) { - const style = useVertexPreferences(vertexType); + const style = useVertexStyle(vertexType); const { displayLabel } = useDisplayVertexTypeConfig(vertexType); return ( diff --git a/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.tsx b/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.tsx index 5a41eaef9..0b7de1738 100644 --- a/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.tsx +++ b/packages/graph-explorer/src/modules/common/NeighborsList/NeighborsList.tsx @@ -13,7 +13,7 @@ import { createVertexType, useNeighbors, useNeighborByType as useNeighborsByType, - useVertexPreferences, + useVertexStyle, type VertexId, } from "@/core"; import { useQueryEngine } from "@/core/connector"; @@ -70,12 +70,12 @@ function NeighborTypeRow({ op: NeighborOption; }) { const neighbors = useNeighborsByType(vertexId, createVertexType(op.value)); - const vertexPreferences = useVertexPreferences(op.config.type); + const vertexStyle = useVertexStyle(op.config.type); return ( <> - + {op.label} diff --git a/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx b/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx index 209af0393..82181aa5e 100644 --- a/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx +++ b/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx @@ -54,7 +54,7 @@ import { type VertexType, } from "@/core"; import { useVertexTypeConfig } from "@/core/ConfigurationProvider/useConfiguration"; -import { useVertexStyling } from "@/core/StateProvider/userPreferences"; +import { useVertexStyling } from "@/core/StateProvider/graphStyles"; import { useAddVertexToGraph, useHasVertexBeenAddedToGraph } from "@/hooks"; import useTranslations from "@/hooks/useTranslations"; import { @@ -254,15 +254,15 @@ function DisplayNameAndDescriptionOptions({ return options; })(); - const { setVertexStyle: setPreferences } = useVertexStyling(vertexType); + const { setVertexStyle } = useVertexStyling(vertexType); const onDisplayNameChange = (field: "name" | "longName") => (value: string | string[]) => { if (field === "name") { - setPreferences({ displayNameAttribute: value as string }); + setVertexStyle({ displayNameAttribute: value as string }); } if (field === "longName") { - setPreferences({ longDisplayNameAttribute: value as string }); + setVertexStyle({ longDisplayNameAttribute: value as string }); } }; diff --git a/packages/graph-explorer/src/routes/Settings/LoadStylesButton.test.tsx b/packages/graph-explorer/src/routes/Settings/LoadStylesButton.test.tsx index d99d03f15..23ab3de28 100644 --- a/packages/graph-explorer/src/routes/Settings/LoadStylesButton.test.tsx +++ b/packages/graph-explorer/src/routes/Settings/LoadStylesButton.test.tsx @@ -4,7 +4,7 @@ import userEvent from "@testing-library/user-event"; import { Suspense } from "react"; import { describe, expect, test, vi } from "vitest"; -import type { VertexPreferencesStorageModel } from "@/core/StateProvider/userPreferences"; +import type { VertexStyleStorage } from "@/core/StateProvider/graphStyles"; import { TooltipProvider } from "@/components"; import { type AppStore, getAppStore } from "@/core"; @@ -100,7 +100,7 @@ describe("LoadStylesButton", () => { const store = renderButton(store => store.set( sharedVertexStylesAtom, - new Map([ + new Map([ [ createVertexType("Person"), { type: createVertexType("Person"), color: "#old" }, diff --git a/packages/graph-explorer/src/utils/testing/DbState.ts b/packages/graph-explorer/src/utils/testing/DbState.ts index 54531b275..09e4578f4 100644 --- a/packages/graph-explorer/src/utils/testing/DbState.ts +++ b/packages/graph-explorer/src/utils/testing/DbState.ts @@ -7,7 +7,7 @@ import { configurationAtom, type Edge, type EdgeId, - type EdgePreferencesStorageModel, + type EdgeStyleStorage, edgesAtom, edgesFilteredIdsAtom, userEdgeStylesAtom, @@ -26,7 +26,7 @@ import { toNodeMap, type Vertex, type VertexId, - type VertexPreferencesStorageModel, + type VertexStyleStorage, userVertexStylesAtom, type VertexType, } from "@/core"; @@ -49,8 +49,8 @@ import { export class DbState { #activeSchema: SchemaStorageModel | null; activeConfig: RawConfiguration; - vertexStyles: Map; - edgeStyles: Map; + vertexStyles: Map; + edgeStyles: Map; explorer: Explorer; @@ -160,8 +160,8 @@ export class DbState { */ addVertexStyle( vertexType: VertexType, - style: Omit, - ): VertexPreferencesStorageModel { + style: Omit, + ): VertexStyleStorage { const composedStyle = { ...style, type: vertexType }; this.vertexStyles.set(vertexType, composedStyle); return composedStyle; @@ -175,8 +175,8 @@ export class DbState { */ addEdgeStyle( edgeType: EdgeType, - style: Omit, - ): EdgePreferencesStorageModel { + style: Omit, + ): EdgeStyleStorage { const composedStyle = { ...style, type: edgeType }; this.edgeStyles.set(edgeType, composedStyle); return composedStyle; diff --git a/packages/graph-explorer/src/utils/testing/persistence.test.ts b/packages/graph-explorer/src/utils/testing/persistence.test.ts index 421e0c34e..6965a8cd9 100644 --- a/packages/graph-explorer/src/utils/testing/persistence.test.ts +++ b/packages/graph-explorer/src/utils/testing/persistence.test.ts @@ -6,11 +6,11 @@ import type { } from "@/core/ConfigurationProvider"; import type { EdgeType, VertexType } from "@/core/entities"; import type { GraphSessionStorageModel } from "@/core/StateProvider/graphSession/storage"; -import type { SchemaStorageModel } from "@/core/StateProvider/schema"; import type { - EdgePreferencesStorageModel, - VertexPreferencesStorageModel, -} from "@/core/StateProvider/userPreferences"; + EdgeStyleStorage, + VertexStyleStorage, +} from "@/core/StateProvider/graphStyles"; +import type { SchemaStorageModel } from "@/core/StateProvider/schema"; import { reconcileMapByKey } from "@/core/StateProvider/atomWithLocalForage"; @@ -25,8 +25,8 @@ import { createRandomVertexType, } from "./randomData"; -type VertexStyles = Map; -type EdgeStyles = Map; +type VertexStyles = Map; +type EdgeStyles = Map; describe("persistence test helpers", () => { test("a tab reads back the value it persisted", async () => { @@ -136,7 +136,7 @@ describe("cross-tab user styling reconciliation", () => { const typeX = createRandomVertexType(); const typeY = createRandomVertexType(); - const styleForType = (type: VertexType): VertexPreferencesStorageModel => ({ + const styleForType = (type: VertexType): VertexStyleStorage => ({ type, color: "#ff0000", }); @@ -172,7 +172,7 @@ describe("cross-tab user styling reconciliation", () => { const typeX = createRandomEdgeType(); const typeY = createRandomEdgeType(); - const styleForType = (type: EdgeType): EdgePreferencesStorageModel => ({ + const styleForType = (type: EdgeType): EdgeStyleStorage => ({ type, lineColor: "#ff0000", }); diff --git a/packages/graph-explorer/src/utils/testing/randomData.ts b/packages/graph-explorer/src/utils/testing/randomData.ts index 0cd0f35b4..1622ceddd 100644 --- a/packages/graph-explorer/src/utils/testing/randomData.ts +++ b/packages/graph-explorer/src/utils/testing/randomData.ts @@ -34,17 +34,15 @@ import { type ConnectionWithId, createEdge, createEdgeId, - createEdgePreference, createEdgeType, createNewConfigurationId, createVertex, createVertexId, - createVertexPreference, createVertexType, type EdgeConnection, type EdgeId, - type EdgePreferences, - type EdgePreferencesStorageModel, + type EdgeStyle, + type EdgeStyleStorage, type EdgeType, type EdgeTypeConfig, type Entities, @@ -54,11 +52,13 @@ import { type LineStyle, type PrefixTypeConfig, type RawConfiguration, + resolveEdgeStyle, + resolveVertexStyle, type SchemaStorageModel, type Vertex, type VertexId, - type VertexPreferences, - type VertexPreferencesStorageModel, + type VertexStyle, + type VertexStyleStorage, type VertexType, type VertexTypeConfig, } from "@/core"; @@ -657,7 +657,7 @@ export function createRandomAwsRegion(): string { return pickRandomElement(["us-west-1", "us-west-2", "us-east-1"]); } -export function createRandomVertexPreferencesStorageModel(): VertexPreferencesStorageModel { +export function createRandomVertexStyleStorage(): VertexStyleStorage { const color = randomlyUndefined(createRandomColor()); const borderColor = randomlyUndefined(createRandomColor()); const iconUrl = randomlyUndefined(createRandomUrlString()); @@ -679,7 +679,7 @@ export function createRandomVertexPreferencesStorageModel(): VertexPreferencesSt }; } -export function createRandomEdgePreferencesStorageModel(): EdgePreferencesStorageModel { +export function createRandomEdgeStyleStorage(): EdgeStyleStorage { const displayLabel = randomlyUndefined(createRandomName("DisplayLabel")); const displayNameAttribute = randomlyUndefined( createRandomName("DisplayNameAttribute"), @@ -701,36 +701,33 @@ export function createRandomEdgePreferencesStorageModel(): EdgePreferencesStorag export function createRandomVertexStyles(): Map< VertexType, - VertexPreferencesStorageModel + VertexStyleStorage > { return new Map( - createArray(3, createRandomVertexPreferencesStorageModel).map(style => [ + createArray(3, createRandomVertexStyleStorage).map(style => [ style.type, style, ]), ); } -export function createRandomEdgeStyles(): Map< - EdgeType, - EdgePreferencesStorageModel -> { +export function createRandomEdgeStyles(): Map { return new Map( - createArray(3, createRandomEdgePreferencesStorageModel).map(style => [ + createArray(3, createRandomEdgeStyleStorage).map(style => [ style.type, style, ]), ); } -export function createRandomVertexPreferences(): Writable { - const stored = createRandomVertexPreferencesStorageModel(); - return createVertexPreference(stored.type, stored); +export function createRandomVertexStyle(): Writable { + const stored = createRandomVertexStyleStorage(); + return resolveVertexStyle(stored.type, stored); } -export function createRandomEdgePreferences(): Writable { - const stored = createRandomEdgePreferencesStorageModel(); - return createEdgePreference(stored.type, stored); +export function createRandomEdgeStyle(): Writable { + const stored = createRandomEdgeStyleStorage(); + return resolveEdgeStyle(stored.type, stored); } export function createRandomFeatureFlags(): FeatureFlags {