Add empty state node to role editor
This commit is contained in:
parent
303f400139
commit
a342aa3c31
|
@ -7,10 +7,13 @@
|
||||||
|
|
||||||
const { nodes, edges, createRole } = getContext("flow")
|
const { nodes, edges, createRole } = getContext("flow")
|
||||||
const flow = useSvelteFlow()
|
const flow = useSvelteFlow()
|
||||||
|
const autoFit = () =>
|
||||||
|
flow.fitView({ maxZoom: MaxAutoZoom, duration: ZoomDuration })
|
||||||
|
|
||||||
const addRole = async () => {
|
const addRole = async () => {
|
||||||
await createRole()
|
await createRole()
|
||||||
doAutoLayout()
|
doAutoLayout()
|
||||||
|
autoFit()
|
||||||
}
|
}
|
||||||
|
|
||||||
const doAutoLayout = () => {
|
const doAutoLayout = () => {
|
||||||
|
@ -33,13 +36,7 @@
|
||||||
on:click={() => flow.zoomOut({ duration: ZoomDuration })}
|
on:click={() => flow.zoomOut({ duration: ZoomDuration })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button secondary on:click={autoFit}>Zoom to fit</Button>
|
||||||
secondary
|
|
||||||
on:click={() =>
|
|
||||||
flow.fitView({ maxZoom: MaxAutoZoom, duration: ZoomDuration })}
|
|
||||||
>
|
|
||||||
Zoom to fit
|
|
||||||
</Button>
|
|
||||||
<Button secondary on:click={doAutoLayout}>Auto layout</Button>
|
<Button secondary on:click={doAutoLayout}>Auto layout</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="control bottom-right">
|
<div class="control bottom-right">
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<script>
|
||||||
|
import { NodeWidth, NodeHeight } from "./constants"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="node" style={`--width:${NodeWidth}px; --height:${NodeHeight}px;`}>
|
||||||
|
Add custom roles for more granular control over permissions
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.node {
|
||||||
|
border-radius: 4px;
|
||||||
|
width: var(--width);
|
||||||
|
height: var(--height);
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
font-size: 16px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--spectrum-global-color-gray-800);
|
||||||
|
text-shadow: 4px 4px 10px var(--background-color),
|
||||||
|
4px -4px 10px var(--background-color),
|
||||||
|
-4px 4px 10px var(--background-color),
|
||||||
|
-4px -4px 10px var(--background-color);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -10,6 +10,7 @@
|
||||||
} from "@xyflow/svelte"
|
} from "@xyflow/svelte"
|
||||||
import "@xyflow/svelte/dist/style.css"
|
import "@xyflow/svelte/dist/style.css"
|
||||||
import RoleNode from "./RoleNode.svelte"
|
import RoleNode from "./RoleNode.svelte"
|
||||||
|
import EmptyStateNode from "./EmptyStateNode.svelte"
|
||||||
import RoleEdge from "./RoleEdge.svelte"
|
import RoleEdge from "./RoleEdge.svelte"
|
||||||
import BracketEdge from "./BracketEdge.svelte"
|
import BracketEdge from "./BracketEdge.svelte"
|
||||||
import {
|
import {
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
getBasicPosition,
|
getBasicPosition,
|
||||||
rolesToLayout,
|
rolesToLayout,
|
||||||
nodeToRole,
|
nodeToRole,
|
||||||
|
getBounds,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
import { setContext, tick } from "svelte"
|
import { setContext, tick } from "svelte"
|
||||||
import Controls from "./Controls.svelte"
|
import Controls from "./Controls.svelte"
|
||||||
|
@ -38,9 +40,7 @@
|
||||||
})
|
})
|
||||||
|
|
||||||
// Derive the bounds of all custom role nodes
|
// Derive the bounds of all custom role nodes
|
||||||
const bounds = derivedMemo(nodes, $nodes => {
|
const bounds = derivedMemo(nodes, getBounds)
|
||||||
return getNodesBounds($nodes.filter(node => node.data.custom))
|
|
||||||
})
|
|
||||||
|
|
||||||
$: handleExternalRoleChanges($roles)
|
$: handleExternalRoleChanges($roles)
|
||||||
$: updateBuiltins($bounds)
|
$: updateBuiltins($bounds)
|
||||||
|
@ -159,7 +159,7 @@
|
||||||
{nodes}
|
{nodes}
|
||||||
{edges}
|
{edges}
|
||||||
snapGrid={[GridResolution, GridResolution]}
|
snapGrid={[GridResolution, GridResolution]}
|
||||||
nodeTypes={{ role: RoleNode }}
|
nodeTypes={{ role: RoleNode, empty: EmptyStateNode }}
|
||||||
edgeTypes={{ role: RoleEdge, bracket: BracketEdge }}
|
edgeTypes={{ role: RoleEdge, bracket: BracketEdge }}
|
||||||
proOptions={{ hideAttribution: true }}
|
proOptions={{ hideAttribution: true }}
|
||||||
fitViewOptions={{ maxZoom: MaxAutoZoom }}
|
fitViewOptions={{ maxZoom: MaxAutoZoom }}
|
||||||
|
|
|
@ -11,6 +11,22 @@ import { Roles } from "constants/backend"
|
||||||
import { roles } from "stores/builder"
|
import { roles } from "stores/builder"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
|
|
||||||
|
// Calculates the bounds of all custom nodes
|
||||||
|
export const getBounds = nodes => {
|
||||||
|
const customNodes = nodes.filter(node => node.data.custom)
|
||||||
|
|
||||||
|
// Empty state bounds which line up with bounds after adding first node
|
||||||
|
if (!customNodes.length) {
|
||||||
|
return {
|
||||||
|
x: 0,
|
||||||
|
y: 6.5 * GridResolution,
|
||||||
|
width: 12 * GridResolution,
|
||||||
|
height: 10 * GridResolution,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getNodesBounds(customNodes)
|
||||||
|
}
|
||||||
|
|
||||||
// Gets the position of the basic role
|
// Gets the position of the basic role
|
||||||
export const getBasicPosition = bounds => ({
|
export const getBasicPosition = bounds => ({
|
||||||
x: bounds.x - NodeHSpacing - NodeWidth,
|
x: bounds.x - NodeHSpacing - NodeWidth,
|
||||||
|
@ -33,6 +49,10 @@ const preProcessLayout = ({ nodes, edges }) => {
|
||||||
if (ignoredRoles.includes(node.id)) {
|
if (ignoredRoles.includes(node.id)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Filter out empty state
|
||||||
|
if (node.id === "empty") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
edges: edges.filter(edge => {
|
edges: edges.filter(edge => {
|
||||||
|
@ -82,7 +102,7 @@ export const dagreLayout = ({ nodes, edges }) => {
|
||||||
|
|
||||||
const postProcessLayout = ({ nodes, edges }) => {
|
const postProcessLayout = ({ nodes, edges }) => {
|
||||||
// Reposition basic and admin to bound the custom nodes
|
// Reposition basic and admin to bound the custom nodes
|
||||||
const bounds = getNodesBounds(nodes.filter(node => node.data.custom))
|
const bounds = getBounds(nodes)
|
||||||
nodes.find(x => x.id === Roles.BASIC).position = getBasicPosition(bounds)
|
nodes.find(x => x.id === Roles.BASIC).position = getBasicPosition(bounds)
|
||||||
nodes.find(x => x.id === Roles.ADMIN).position = getAdminPosition(bounds)
|
nodes.find(x => x.id === Roles.ADMIN).position = getAdminPosition(bounds)
|
||||||
|
|
||||||
|
@ -99,6 +119,28 @@ const postProcessLayout = ({ nodes, edges }) => {
|
||||||
target: Roles.BASIC,
|
target: Roles.BASIC,
|
||||||
type: "bracket",
|
type: "bracket",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add empty state node if required
|
||||||
|
if (!nodes.filter(node => node.data.custom).length) {
|
||||||
|
nodes.push({
|
||||||
|
id: "empty",
|
||||||
|
type: "empty",
|
||||||
|
position: {
|
||||||
|
x: bounds.x + bounds.width / 2 - NodeWidth / 2,
|
||||||
|
y: bounds.y + bounds.height / 2 - NodeHeight / 2,
|
||||||
|
},
|
||||||
|
data: {},
|
||||||
|
measured: {
|
||||||
|
width: NodeWidth,
|
||||||
|
height: NodeHeight,
|
||||||
|
},
|
||||||
|
deletable: false,
|
||||||
|
draggable: false,
|
||||||
|
connectable: false,
|
||||||
|
selectable: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return { nodes, edges }
|
return { nodes, edges }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue