-
Notifications
You must be signed in to change notification settings - Fork 16
Rework visualizer to provide separate views of schema and relationships and update those views #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
82c5ee4
bd56d82
965b5d3
651f6c4
cc7c06e
9ecd906
c78dd9c
57c7883
f3268e0
fa41372
0a7f07f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,48 +1,20 @@ | ||
| import "react-reflex/styles.css"; | ||
|
|
||
| import { createStyles, darken, makeStyles, Theme } from "@material-ui/core/styles"; | ||
| import BubbleChartIcon from "@material-ui/icons/BubbleChart"; | ||
| import { useNavigate } from "@tanstack/react-router"; | ||
| import { Bubbles } from "lucide-react"; | ||
| import monaco from "monaco-editor"; | ||
|
|
||
| // TODO: rename | ||
| import TenantGraph from "@/components/visualizer/TenantGraph"; | ||
|
|
||
| import TabLabel from "../../playground-ui/TabLabel"; | ||
| import { DataStoreItem, DataStoreItemKind, DataStorePaths } from "../../services/datastore"; | ||
| import TenantGraph from "../../spicedb-common/components/graph/TenantGraph"; | ||
| import { TextRange } from "../../spicedb-common/include/protobuf-parser"; | ||
| import { DataStoreItem } from "../../services/datastore"; | ||
| import { ParseRelationshipError } from "../../spicedb-common/parsing"; | ||
| import { RelationTuple } from "../../spicedb-common/protodefs/core/v1/core_pb"; | ||
|
|
||
| import { PanelProps } from "./base/common"; | ||
|
|
||
| declare module "@tanstack/react-router" { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed the history state binding, so this went away. |
||
| interface HistoryState { | ||
| range?: TextRange; | ||
| } | ||
| } | ||
|
|
||
| const useStyles = makeStyles((theme: Theme) => | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Styling is tailwind now |
||
| createStyles({ | ||
| tenantGraphContainer: { | ||
| width: "100%", | ||
| height: "100%", | ||
| backgroundColor: theme.palette.background.default, | ||
| backgroundSize: "20px 20px", | ||
| backgroundImage: ` | ||
| linear-gradient(to right, ${darken( | ||
| theme.palette.background.default, | ||
| 0.1, | ||
| )} 1px, transparent 1px), | ||
| linear-gradient(to bottom, ${darken( | ||
| theme.palette.background.default, | ||
| 0.1, | ||
| )} 1px, transparent 1px) | ||
| `, | ||
| }, | ||
| }), | ||
| ); | ||
|
|
||
| export function VisualizerSummary() { | ||
| return <TabLabel icon={<BubbleChartIcon />} title="System Visualization" />; | ||
| return <TabLabel icon={<Bubbles />} title="System Visualization" />; | ||
| } | ||
|
|
||
| function isRelationship( | ||
|
|
@@ -52,49 +24,26 @@ function isRelationship( | |
| } | ||
|
|
||
| export function VisualizerPanel({ | ||
| location, | ||
| services, | ||
| dimensions, | ||
| editorPosition, | ||
| currentItem, | ||
| }: PanelProps & { | ||
| dimensions?: { width: number; height: number }; | ||
| editorPosition?: monaco.Position | undefined; | ||
| currentItem?: DataStoreItem | undefined; | ||
| editorPosition?: monaco.Position; | ||
| currentItem?: DataStoreItem; | ||
| }) { | ||
| const classes = useStyles(); | ||
| const navigate = useNavigate(); | ||
|
|
||
| const handleBrowseRequested = (range?: TextRange) => { | ||
| // TODO: make this functionality use querystrings instead of history state | ||
| navigate({ | ||
| to: DataStorePaths.Schema(), | ||
| state: { | ||
| range, | ||
| }, | ||
| }); | ||
| }; | ||
|
|
||
| const relationships = services.localParseService.state.relationships | ||
| .map((relFound) => relFound.parsed) | ||
| .filter(isRelationship); | ||
|
|
||
| return ( | ||
| <div className={classes.tenantGraphContainer} style={{ height: dimensions?.height ?? 0 }}> | ||
| <TenantGraph | ||
| key={location} | ||
| schema={services.localParseService.state.parsed} | ||
| relationships={relationships} | ||
| onBrowseRequested={handleBrowseRequested} | ||
| active={ | ||
| editorPosition | ||
| ? { | ||
| isSchema: currentItem?.kind === DataStoreItemKind.SCHEMA, | ||
| position: editorPosition, | ||
| } | ||
| : undefined | ||
| } | ||
| /> | ||
| <div | ||
| className="w-full h-full" | ||
| // NOTE: this style is to allow the reflexed pane to control the height of the container. | ||
| // Without this it doesn't render correctly, though there may be a different way | ||
| // to get the same behavior. | ||
| style={dimensions?.height ? { height: dimensions.height } : undefined} | ||
| > | ||
| <TenantGraph schema={services.localParseService.state.parsed} relationships={relationships} /> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,7 @@ import { | |
| RELATION_REGEX, | ||
| RESOURCE_ID_REGEX, | ||
| SUBJECT_ID_REGEX, | ||
| } from "../../parsing"; | ||
| } from "@/spicedb-common/parsing"; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved these components, so I updated their imports to be non-relative |
||
|
|
||
| /** | ||
| * DataKind defines the different kinds of data that can be found in columns. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { BaseEdge, EdgeLabelRenderer, getBezierPath, EdgeProps, Edge } from "@xyflow/react"; | ||
| import { ReactNode } from "react"; | ||
|
|
||
| export type CustomEdgeType = Edge<{ | ||
| type?: string; | ||
| relationName?: string; | ||
| relationNames?: string[]; | ||
| permissionNames?: string[]; | ||
| label?: ReactNode; // JSX label content | ||
| }>; | ||
|
|
||
| export default function CustomEdge({ | ||
| id, | ||
| sourceX, | ||
| sourceY, | ||
| targetX, | ||
| targetY, | ||
| sourcePosition, | ||
| targetPosition, | ||
| style, | ||
| markerEnd, | ||
| data, | ||
| }: EdgeProps<CustomEdgeType>) { | ||
| // Calculate bezier path and label position | ||
| const [edgePath, labelX, labelY] = getBezierPath({ | ||
| sourceX, | ||
| sourceY, | ||
| sourcePosition, | ||
| targetX, | ||
| targetY, | ||
| targetPosition, | ||
| }); | ||
|
|
||
| return ( | ||
| <> | ||
| {/* Render the edge line */} | ||
| <BaseEdge id={id} path={edgePath} markerEnd={markerEnd} style={style} /> | ||
|
|
||
| {/* Render complex label using EdgeLabelRenderer if label exists */} | ||
| {data?.label && ( | ||
| <EdgeLabelRenderer> | ||
| <div | ||
| // TODO: Make this use Tailwind | ||
| style={{ | ||
| position: "absolute", | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to make these use tailwind instead. |
||
| transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`, | ||
| background: "var(--card)", | ||
| color: "var(--card-foreground)", | ||
| padding: "4px 8px", | ||
| borderRadius: "4px", | ||
| border: "1px solid var(--border)", | ||
| fontSize: "10px", | ||
| lineHeight: "1.2", | ||
| pointerEvents: "all", | ||
| }} | ||
| className="nodrag nopan" | ||
| > | ||
| {data.label} | ||
| </div> | ||
| </EdgeLabelRenderer> | ||
| )} | ||
| </> | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting rid of the visjs include