Skip to content
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

docs: change workflow diagram to list #8602

Merged
merged 1 commit into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client"

import React from "react"
import { WorkflowStepUi } from "types"
import { WorkflowDiagramStepNode } from "../../Common/Node"
import { WorkflowDiagramLine } from "../../Common/Line"

export type WorkflowDiagramCanvasDepthProps = {
cluster: WorkflowStepUi[]
next: WorkflowStepUi[]
}

export const WorkflowDiagramCanvasDepth = ({
cluster,
next,
}: WorkflowDiagramCanvasDepthProps) => {
return (
<div className="flex items-start">
<div className="flex flex-col justify-center gap-y-docs_0.5">
{cluster.map((step) => (
<WorkflowDiagramStepNode key={step.name} step={step} />
))}
</div>
<WorkflowDiagramLine step={next} />
</div>
)
}
206 changes: 206 additions & 0 deletions www/packages/docs-ui/src/components/WorkflowDiagram/Canvas/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
"use client"

import clsx from "clsx"
import {
useAnimationControls,
useDragControls,
useMotionValue,
motion,
} from "framer-motion"
import React, { useEffect, useRef, useState } from "react"
import { ArrowPathMini, MinusMini, PlusMini } from "@medusajs/icons"
import { DropdownMenu, Text } from "@medusajs/ui"
import { createNodeClusters, getNextCluster } from "../../../utils"
import { WorkflowDiagramCanvasDepth } from "./Depth"
import { WorkflowDiagramCommonProps } from "../../.."

type ZoomScale = 0.5 | 0.75 | 1

const defaultState = {
x: -1000,
y: -1020,
scale: 1,
}

const MAX_ZOOM = 1.5
const MIN_ZOOM = 0.5
const ZOOM_STEP = 0.25

export const WorkflowDiagramCanvas = ({
workflow,
}: WorkflowDiagramCommonProps) => {
const [zoom, setZoom] = useState<number>(1)
const [isDragging, setIsDragging] = useState(false)

const scale = useMotionValue(defaultState.scale)
const x = useMotionValue(defaultState.x)
const y = useMotionValue(defaultState.y)

const controls = useAnimationControls()

const dragControls = useDragControls()
const dragConstraints = useRef<HTMLDivElement>(null)

const canZoomIn = zoom < MAX_ZOOM
const canZoomOut = zoom > MIN_ZOOM

useEffect(() => {
const unsubscribe = scale.on("change", (latest) => {
setZoom(latest as ZoomScale)
})

return () => {
unsubscribe()
}
}, [scale])

const clusters = createNodeClusters(workflow.steps)

function scaleXandY(
prevScale: number,
newScale: number,
x: number,
y: number
) {
const scaleRatio = newScale / prevScale
return {
x: x * scaleRatio,
y: y * scaleRatio,
}
}

const changeZoom = (newScale: number) => {
const { x: newX, y: newY } = scaleXandY(zoom, newScale, x.get(), y.get())

setZoom(newScale)
controls.set({ scale: newScale, x: newX, y: newY })
}

const zoomIn = () => {
const curr = scale.get()

if (curr < 1.5) {
const newScale = curr + ZOOM_STEP
changeZoom(newScale)
}
}

const zoomOut = () => {
const curr = scale.get()

if (curr > 0.5) {
const newScale = curr - ZOOM_STEP
changeZoom(newScale)
}
}

const resetCanvas = async () => await controls.start(defaultState)

return (
<div className="h-[200px] w-full rounded-docs_DEFAULT">
<div
ref={dragConstraints}
className="relative size-full rounded-docs_DEFAULT"
>
<div className="relative size-full overflow-hidden object-contain rounded-docs_DEFAULT shadow-elevation-card-rest">
<div>
<motion.div
onMouseDown={() => setIsDragging(true)}
onMouseUp={() => setIsDragging(false)}
drag
dragConstraints={dragConstraints}
dragElastic={0}
dragMomentum={false}
dragControls={dragControls}
initial={false}
animate={controls}
transition={{ duration: 0.25 }}
style={{
x,
y,
scale,
}}
className={clsx(
"bg-medusa-bg-subtle relative size-[500rem] origin-top-left items-start justify-start overflow-hidden",
"bg-[radial-gradient(var(--docs-border-base)_1.5px,transparent_0)] bg-[length:20px_20px] bg-repeat",
!isDragging && "cursor-grab",
isDragging && "cursor-grabbing"
)}
>
<main className="size-full">
<div className="absolute left-[1100px] top-[1100px] flex select-none items-start">
{Object.entries(clusters).map(([depth, cluster]) => {
const next = getNextCluster(clusters, Number(depth))

return (
<WorkflowDiagramCanvasDepth
cluster={cluster}
next={next}
key={depth}
/>
)
})}
</div>
</main>
</motion.div>
</div>
</div>
<div className="bg-medusa-bg-base shadow-borders-base text-medusa-fg-subtle absolute bottom-docs_1 left-docs_1.5 flex h-[28px] items-center overflow-hidden rounded-docs_sm">
<div className="flex items-center">
<button
onClick={zoomIn}
type="button"
disabled={!canZoomIn}
aria-label="Zoom in"
className="disabled:text-medusa-fg-disabled transition-fg hover:bg-medusa-bg-base-hover active:bg-medusa-bg-base-pressed focus-visible:bg-medusa-bg-base-pressed border-r p-docs_0.25 outline-none"
>
<PlusMini />
</button>
<div>
<DropdownMenu>
<DropdownMenu.Trigger className="disabled:text-medusa-fg-disabled transition-fg hover:bg-medusa-bg-base-hover active:bg-medusa-bg-base-pressed focus-visible:bg-medusa-bg-base-pressed flex w-[50px] items-center justify-center border-r p-docs_0.25 outline-none">
<Text
as="span"
size="xsmall"
leading="compact"
className="select-none tabular-nums"
>
{Math.round(zoom * 100)}%
</Text>
</DropdownMenu.Trigger>
<DropdownMenu.Content className="bg-medusa-bg-base">
{[50, 75, 100, 125, 150].map((value) => (
<DropdownMenu.Item
key={value}
onClick={() => changeZoom(value / 100)}
className="px-docs_0.5 py-[6px]"
>
{value}%
</DropdownMenu.Item>
))}
</DropdownMenu.Content>
</DropdownMenu>
</div>
<button
onClick={zoomOut}
type="button"
disabled={!canZoomOut}
aria-label="Zoom out"
className="disabled:text-medusa-fg-disabled transition-fg hover:bg-medusa-bg-base-hover active:bg-medusa-bg-base-pressed focus-visible:bg-medusa-bg-base-pressed border-r p-docs_0.25 outline-none"
>
<MinusMini />
</button>
</div>
<button
onClick={resetCanvas}
type="button"
aria-label="Reset canvas"
className="disabled:text-medusa-fg-disabled transition-fg hover:bg-medusa-bg-base-hover active:bg-medusa-bg-base-pressed focus-visible:bg-medusa-bg-base-pressed p-docs_0.25 outline-none"
>
<ArrowPathMini />
</button>
</div>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import React from "react"
import { WorkflowStepUi } from "types"
import { WorkflowDiagramStepNode } from "../Node"
import { WorkflowDiagramLine } from "../Line"
import { WorkflowDiagramStepNode } from "../../Common/Node"
import { WorkflowDiagramLine } from "../../Common/Line"

export type WorkflowDiagramDepthProps = {
cluster: WorkflowStepUi[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const WorkflowDiagramStepNode = ({ step }: WorkflowDiagramNodeProps) => {
>
<div
className={clsx(
"shadow-borders-base flex min-w-[120px] bg-medusa-bg-base",
"shadow-borders-base flex min-w-[120px] w-min bg-medusa-bg-base",
"items-center rounded-docs_sm py-docs_0.125 px-docs_0.5",
(step.type === "hook" || step.when) && "gap-x-docs_0.125"
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client"

import React from "react"
import { WorkflowStepUi } from "types"
import { WorkflowDiagramStepNode } from "../../Common/Node"
import { WorkflowDiagramLine } from "../../Common/Line"

export type WorkflowDiagramListDepthProps = {
cluster: WorkflowStepUi[]
next: WorkflowStepUi[]
}

export const WorkflowDiagramListDepth = ({
cluster,
}: WorkflowDiagramListDepthProps) => {
return (
<div className="flex items-start">
<WorkflowDiagramLine step={cluster} />
<div className="flex flex-col justify-center gap-y-docs_0.5">
{cluster.map((step) => (
<WorkflowDiagramStepNode key={step.name} step={step} />
))}
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client"

import React from "react"
import { createNodeClusters, getNextCluster } from "../../../utils"
import { WorkflowDiagramCommonProps } from "../../.."
import { WorkflowDiagramListDepth } from "./Depth"

export const WorkflowDiagramList = ({
workflow,
}: WorkflowDiagramCommonProps) => {
const clusters = createNodeClusters(workflow.steps)

return (
<div className="flex flex-col gap-docs_0.5">
{Object.entries(clusters).map(([depth, cluster]) => {
const next = getNextCluster(clusters, Number(depth))

return (
<WorkflowDiagramListDepth cluster={cluster} next={next} key={depth} />
)
})}
</div>
)
}
Loading
Loading