Simplify and rewrite some flow logic
This commit is contained in:
parent
0a109165f1
commit
c634cfdeac
|
@ -81,6 +81,7 @@
|
|||
text="Manage roles"
|
||||
selected={$isActive("./roles")}
|
||||
on:click={() => $goto("./roles")}
|
||||
selectedBy={$userSelectedResourceMap.roles}
|
||||
/>
|
||||
{#each enrichedDataSources.filter(ds => ds.show) as datasource}
|
||||
<DatasourceNavItem
|
||||
|
|
|
@ -1,32 +1,15 @@
|
|||
<script>
|
||||
import { Button, Helpers, ActionButton } from "@budibase/bbui"
|
||||
import { Button, ActionButton } from "@budibase/bbui"
|
||||
import { useSvelteFlow } from "@xyflow/svelte"
|
||||
import { getContext, tick } from "svelte"
|
||||
import { autoLayout, roleToNode } from "./layout"
|
||||
import { autoLayout } from "./layout"
|
||||
import { MaxAutoZoom, ZoomDuration } from "./constants"
|
||||
import { getSequentialName } from "helpers/duplicate"
|
||||
import { roles } from "stores/builder"
|
||||
import { Roles } from "constants/backend"
|
||||
|
||||
const { nodes, edges } = getContext("flow")
|
||||
const { nodes, edges, createRole } = getContext("flow")
|
||||
const flow = useSvelteFlow()
|
||||
|
||||
const addRole = async () => {
|
||||
const role = {
|
||||
name: Helpers.uuid(),
|
||||
uiMetadata: {
|
||||
displayName: getSequentialName($roles, "New role ", {
|
||||
getName: x => x.uiMetadata.displayName,
|
||||
}),
|
||||
color: "var(--spectrum-global-color-gray-700)",
|
||||
description: "Custom role",
|
||||
},
|
||||
permissionId: "write",
|
||||
inherits: Roles.BASIC,
|
||||
}
|
||||
const savedRole = await roles.save(role)
|
||||
nodes.update(state => [...state, roleToNode(savedRole)])
|
||||
await doAutoLayout()
|
||||
await createRole()
|
||||
}
|
||||
|
||||
const doAutoLayout = async () => {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
useSvelteFlow,
|
||||
} from "@xyflow/svelte"
|
||||
import { Icon, TooltipPosition } from "@budibase/bbui"
|
||||
import { onMount } from "svelte"
|
||||
import { onMount, getContext } from "svelte"
|
||||
import { roles } from "stores/builder"
|
||||
|
||||
export let sourceX
|
||||
|
@ -20,6 +20,7 @@
|
|||
export let target
|
||||
|
||||
const flow = useSvelteFlow()
|
||||
const { updateRole } = getContext("flow")
|
||||
|
||||
let edgeHovered = false
|
||||
let labelHovered = false
|
||||
|
@ -56,10 +57,11 @@
|
|||
edgeHovered = false
|
||||
}
|
||||
|
||||
const deleteEdge = () => {
|
||||
const deleteEdge = async () => {
|
||||
flow.deleteElements({
|
||||
edges: [{ id }],
|
||||
})
|
||||
await updateRole(target)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
|
|
@ -1,25 +1,157 @@
|
|||
<script>
|
||||
import { Heading } from "@budibase/bbui"
|
||||
import { Heading, Helpers } from "@budibase/bbui"
|
||||
import { writable } from "svelte/store"
|
||||
import { SvelteFlow, Background, BackgroundVariant } from "@xyflow/svelte"
|
||||
import {
|
||||
SvelteFlow,
|
||||
Background,
|
||||
BackgroundVariant,
|
||||
Position,
|
||||
} from "@xyflow/svelte"
|
||||
import "@xyflow/svelte/dist/style.css"
|
||||
import RoleNode from "./RoleNode.svelte"
|
||||
import RoleEdge from "./RoleEdge.svelte"
|
||||
import { rolesToNodes, autoLayout } from "./layout"
|
||||
import { onMount, setContext } from "svelte"
|
||||
import { autoLayout } from "./layout"
|
||||
import { setContext } from "svelte"
|
||||
import Controls from "./Controls.svelte"
|
||||
import { GridResolution, MaxAutoZoom } from "./constants"
|
||||
import { roles } from "stores/builder"
|
||||
import { Roles } from "constants/backend"
|
||||
import { getSequentialName } from "helpers/duplicate"
|
||||
|
||||
const nodes = writable([])
|
||||
const edges = writable([])
|
||||
const dragging = writable(false)
|
||||
|
||||
setContext("flow", { nodes, edges, dragging })
|
||||
// Ensure role changes are synced with nodes and edges
|
||||
$: handleExternalRoleChanges($roles)
|
||||
|
||||
onMount(() => {
|
||||
const layout = autoLayout(rolesToNodes())
|
||||
nodes.set(layout.nodes)
|
||||
edges.set(layout.edges)
|
||||
// Converts a role doc into a node structure
|
||||
const roleToNode = role => ({
|
||||
id: role._id,
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
type: "role",
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...role.uiMetadata,
|
||||
custom: !role._id.match(/[A-Z]+/),
|
||||
},
|
||||
})
|
||||
|
||||
// Converts a node structure back into a role doc
|
||||
const nodeToRole = node => {
|
||||
const role = $roles.find(x => x._id === node.id)
|
||||
const inherits = $edges.filter(x => x.target === node.id).map(x => x.source)
|
||||
console.log(inherits)
|
||||
return {
|
||||
...role,
|
||||
// inherits,
|
||||
uiMetadata: {
|
||||
displayName: node.data.displayName,
|
||||
color: node.data.color,
|
||||
description: node.data.description,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Builds a layout from an array of roles
|
||||
const rolesToLayout = roles => {
|
||||
let nodes = []
|
||||
let edges = []
|
||||
for (let role of roles.filter(role => role._id !== Roles.PUBLIC)) {
|
||||
// Add node for this role
|
||||
nodes.push(roleToNode(role))
|
||||
|
||||
// Add edges for this role
|
||||
let inherits = []
|
||||
if (role.inherits) {
|
||||
inherits = Array.isArray(role.inherits)
|
||||
? role.inherits
|
||||
: [role.inherits]
|
||||
}
|
||||
for (let sourceRole of inherits) {
|
||||
if (!roles.some(x => x._id === sourceRole)) {
|
||||
continue
|
||||
}
|
||||
edges.push({
|
||||
id: `${sourceRole}-${role._id}`,
|
||||
source: sourceRole,
|
||||
target: role._id,
|
||||
})
|
||||
}
|
||||
}
|
||||
return {
|
||||
nodes,
|
||||
edges,
|
||||
}
|
||||
}
|
||||
|
||||
// Updates nodes and edges based on external changes to roles
|
||||
const handleExternalRoleChanges = roles => {
|
||||
const currentNodes = $nodes
|
||||
const newLayout = autoLayout(rolesToLayout(roles))
|
||||
|
||||
// For roles we want to persist their current positions
|
||||
nodes.set(
|
||||
newLayout.nodes.map(node => {
|
||||
const position = currentNodes.find(x => x.id === node.id)?.position
|
||||
if (!position) {
|
||||
return node
|
||||
}
|
||||
return { ...node, position }
|
||||
})
|
||||
)
|
||||
|
||||
// Edges can always be updated
|
||||
edges.set(newLayout.edges)
|
||||
}
|
||||
|
||||
// Creates a new role
|
||||
const createRole = async () => {
|
||||
const role = {
|
||||
name: Helpers.uuid(),
|
||||
uiMetadata: {
|
||||
displayName: getSequentialName($roles, "New role ", {
|
||||
getName: x => x.uiMetadata.displayName,
|
||||
}),
|
||||
color: "var(--spectrum-global-color-gray-700)",
|
||||
description: "Custom role",
|
||||
},
|
||||
permissionId: "write",
|
||||
inherits: Roles.BASIC,
|
||||
}
|
||||
await roles.save(role)
|
||||
}
|
||||
|
||||
// Updates a role based on the latest flow data
|
||||
const updateRole = async roleId => {
|
||||
const node = $nodes.find(x => x.id === roleId)
|
||||
if (node) {
|
||||
const role = nodeToRole(node)
|
||||
await roles.save(role)
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes a role
|
||||
const deleteRole = async roleId => {
|
||||
const role = $roles.find(x => x._id === roleId)
|
||||
if (role) {
|
||||
roles.delete(role)
|
||||
}
|
||||
}
|
||||
|
||||
// Saves a new connection
|
||||
const onConnect = async connection => {
|
||||
await saveRole(connection.target)
|
||||
}
|
||||
|
||||
setContext("flow", {
|
||||
nodes,
|
||||
edges,
|
||||
dragging,
|
||||
createRole,
|
||||
updateRole,
|
||||
deleteRole,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -42,6 +174,7 @@
|
|||
defaultEdgeOptions={{ type: "role", animated: true, selectable: false }}
|
||||
onconnectstart={() => dragging.set(true)}
|
||||
onconnectend={() => dragging.set(false)}
|
||||
onconnect={onConnect}
|
||||
>
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
<Controls />
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
import { Roles } from "constants/backend"
|
||||
import { NodeWidth, NodeHeight, MaxAutoZoom, ZoomDuration } from "./constants"
|
||||
import { getContext, tick } from "svelte"
|
||||
import { autoLayout, nodeToRole } from "./layout"
|
||||
import { autoLayout } from "./layout"
|
||||
import { roles } from "stores/builder"
|
||||
|
||||
export let data
|
||||
export let id
|
||||
|
||||
const { nodes, edges, dragging } = getContext("flow")
|
||||
const { nodes, edges, dragging, updateRole, deleteRole } = getContext("flow")
|
||||
const flow = useSvelteFlow()
|
||||
|
||||
let anchor
|
||||
|
@ -49,12 +49,7 @@
|
|||
}
|
||||
|
||||
const deleteNode = async () => {
|
||||
flow.deleteElements({
|
||||
nodes: [{ id }],
|
||||
})
|
||||
await tick()
|
||||
doAutoLayout()
|
||||
await roles.delete(nodeToRole({ id, data }))
|
||||
await deleteRole(id)
|
||||
}
|
||||
|
||||
const openPopover = () => {
|
||||
|
@ -71,7 +66,7 @@
|
|||
color: tempColor,
|
||||
}
|
||||
flow.updateNodeData(id, newData)
|
||||
await roles.save(nodeToRole({ id, data: newData }))
|
||||
await updateRole(id)
|
||||
}
|
||||
|
||||
const doAutoLayout = () => {
|
||||
|
|
|
@ -1,79 +1,11 @@
|
|||
import dagre from "@dagrejs/dagre"
|
||||
import { NodeWidth, NodeHeight, GridResolution } from "./constants"
|
||||
import { Position } from "@xyflow/svelte"
|
||||
import { roles } from "stores/builder"
|
||||
import { Roles } from "constants/backend"
|
||||
import { get } from "svelte/store"
|
||||
import { Helpers } from "@budibase/bbui"
|
||||
|
||||
// Converts a role doc into a node structure
|
||||
export const roleToNode = role => ({
|
||||
id: role._id,
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
type: "role",
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...role.uiMetadata,
|
||||
custom: !role._id.match(/[A-Z]+/),
|
||||
},
|
||||
})
|
||||
|
||||
// Converts a node structure back into a role doc
|
||||
export const nodeToRole = node => {
|
||||
const role = get(roles).find(x => x._id === node.id)
|
||||
return {
|
||||
...role,
|
||||
uiMetadata: {
|
||||
displayName: node.data.displayName,
|
||||
color: node.data.color,
|
||||
description: node.data.description,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a flow compatible structure of nodes and edges from the current roles
|
||||
export const rolesToNodes = () => {
|
||||
const ignoredRoles = [Roles.PUBLIC]
|
||||
const $roles = get(roles)
|
||||
|
||||
let nodes = []
|
||||
let edges = []
|
||||
|
||||
for (let role of $roles) {
|
||||
if (ignoredRoles.includes(role._id)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Add node for this role
|
||||
nodes.push(roleToNode(role))
|
||||
|
||||
// Add edges for this role
|
||||
let inherits = []
|
||||
if (role.inherits) {
|
||||
inherits = Array.isArray(role.inherits) ? role.inherits : [role.inherits]
|
||||
}
|
||||
for (let sourceRole of inherits) {
|
||||
// Ensure source role exists
|
||||
if (!$roles.some(x => x._id === sourceRole)) {
|
||||
continue
|
||||
}
|
||||
edges.push({
|
||||
id: `${sourceRole}-${role._id}`,
|
||||
source: sourceRole,
|
||||
target: role._id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
nodes,
|
||||
edges,
|
||||
}
|
||||
}
|
||||
|
||||
// Updates positions of nodes and edges into a nice graph structure
|
||||
const dagreLayout = ({ nodes, edges }) => {
|
||||
export const dagreLayout = ({ nodes, edges }) => {
|
||||
const dagreGraph = new dagre.graphlib.Graph()
|
||||
dagreGraph.setDefaultEdgeLabel(() => ({}))
|
||||
dagreGraph.setGraph({
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<script>
|
||||
import RoleEditor from "components/backend/RoleEditor/RoleEditor.svelte"
|
||||
import { builderStore } from "stores/builder"
|
||||
|
||||
builderStore.selectResource("roles")
|
||||
</script>
|
||||
|
||||
<RoleEditor />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { derived, writable } from "svelte/store"
|
||||
import { derived, writable, get } from "svelte/store"
|
||||
import { API } from "api"
|
||||
import { RoleUtils } from "@budibase/frontend-core"
|
||||
|
||||
|
@ -56,6 +56,13 @@ export function createRolesStore() {
|
|||
}
|
||||
},
|
||||
replace: (roleId, role) => {
|
||||
// Remove role_ prefix
|
||||
if (roleId?.startsWith("role_")) {
|
||||
roleId = roleId.replace("role_", "")
|
||||
}
|
||||
if (role?._id.startsWith("role_")) {
|
||||
role._id = role._id.replace("role_", "")
|
||||
}
|
||||
console.log("replace", roleId, role)
|
||||
|
||||
// Handles external updates of roles
|
||||
|
|
|
@ -59,7 +59,7 @@ export const createBuilderWebsocket = appId => {
|
|||
|
||||
// Role events
|
||||
socket.onOther(BuilderSocketEvent.RoleChange, ({ id, role }) => {
|
||||
roles.replaceRole(id, role)
|
||||
roles.replace(id, role)
|
||||
})
|
||||
|
||||
// Design section events
|
||||
|
|
Loading…
Reference in New Issue