Skip to content

Commit

Permalink
Merge pull request #927 from andreiwebdev/playground-tabs-draggable
Browse files Browse the repository at this point in the history
Update - Make tabs in playground draggable
  • Loading branch information
aakrem authored Nov 27, 2023
2 parents 3e72d24 + c9ddbff commit 873b409
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 0 deletions.
51 changes: 51 additions & 0 deletions agenta-web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions agenta-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"dependencies": {
"@ant-design/colors": "^7.0.0",
"@ant-design/icons": "^5.0.1",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@heroicons/react": "^2.0.17",
"@mdx-js/loader": "^2.3.0",
"@mdx-js/react": "^2.3.0",
Expand Down
37 changes: 37 additions & 0 deletions agenta-web/src/components/DraggableTabNode/DraggableTabNode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react"
import {useSortable} from "@dnd-kit/sortable"
import {CSS} from "@dnd-kit/utilities"

// Defines the props for DraggableTabNode component.
interface DraggableTabNodeProps extends React.HTMLAttributes<HTMLDivElement> {
"data-node-key": string // Unique identifier for the draggable item.
}

// This component wraps each tab label to make it draggable.
const DraggableTabNode: React.FC<DraggableTabNodeProps> = ({
"data-node-key": key,
children,
...props
}) => {
// useSortable hook provides the necessary handlers and attributes for drag-and-drop.
const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id: key})

// CSS styles to apply transform and transition for the dragging effect.
const style: React.CSSProperties = {
...props.style,
transform: CSS.Transform.toString(transform ? {...transform, scaleX: 1} : null),
transition,
userSelect: "none",
cursor: "pointer", // Changes cursor to indicate draggability.
}

// Clones the children element to add refs and drag-and-drop related properties.
return React.cloneElement(children as React.ReactElement, {
ref: setNodeRef,
style,
...attributes,
...listeners,
})
}

export default DraggableTabNode
48 changes: 48 additions & 0 deletions agenta-web/src/components/Playground/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {useQueryParam} from "@/hooks/useQuery"
import AlertPopup from "../AlertPopup/AlertPopup"
import TestContextProvider from "./TestsetContextProvider"
import useBlockNavigation from "@/hooks/useBlockNavigation"
import type {DragEndEvent} from "@dnd-kit/core"
import {DndContext, PointerSensor, useSensor} from "@dnd-kit/core"
import {arrayMove, SortableContext, horizontalListSortingStrategy} from "@dnd-kit/sortable"
import DraggableTabNode from "../DraggableTabNode/DraggableTabNode"

const Playground: React.FC = () => {
const router = useRouter()
Expand All @@ -24,6 +28,7 @@ const Playground: React.FC = () => {
const [messageApi, contextHolder] = message.useMessage()
const [unsavedVariants, setUnsavedVariants] = useState<{[name: string]: boolean}>({})
const variantHelpers = useRef<{[name: string]: {save: Function; delete: Function}}>({})
const sensor = useSensor(PointerSensor, {activationConstraint: {distance: 50}}) // Initializes a PointerSensor with a specified activation distance.

const addTab = () => {
// Find the template variant
Expand Down Expand Up @@ -197,6 +202,32 @@ const Playground: React.FC = () => {
})
}

/**
* Handles the drag-and-drop event for tabs. It reorders the tabs in the `variants` array
* based on the drag result. The function checks if a tab is dropped over a different tab
* and updates the order accordingly.
*
* @param event The drag end event with active (dragged item) and over (drop target) properties.
*/
const onDragEnd = (event: DragEndEvent) => {
const {active, over} = event

if (over && active.id !== over.id) {
const activeId = active.id as string
const overId = over.id as string

setVariants((prev) => {
const activeIndex = prev.findIndex((variant) => variant.variantName === activeId)
const overIndex = prev.findIndex((variant) => variant.variantName === overId)

if (activeIndex !== -1 && overIndex !== -1) {
return arrayMove(prev, activeIndex, overIndex)
}
return prev
})
}
}

// Map the variants array to create the items array conforming to the Tab interface
const tabItems: PlaygroundTabsItem[] = variants.map((variant, index) => ({
key: variant.variantName,
Expand Down Expand Up @@ -234,6 +265,7 @@ const Playground: React.FC = () => {
/>
</div>
<Tabs
className="editable-card"
type="editable-card"
activeKey={activeKey}
onChange={setActiveKey}
Expand All @@ -245,6 +277,22 @@ const Playground: React.FC = () => {
}
}}
items={tabItems}
renderTabBar={(tabBarProps, DefaultTabBar) => (
<DndContext sensors={[sensor]} onDragEnd={onDragEnd}>
<SortableContext
items={tabItems.map((i) => i.key)}
strategy={horizontalListSortingStrategy}
>
<DefaultTabBar {...tabBarProps}>
{(node) => (
<DraggableTabNode {...node.props} key={node.key}>
{node}
</DraggableTabNode>
)}
</DefaultTabBar>
</SortableContext>
</DndContext>
)}
/>
</div>
</TestContextProvider>
Expand Down
5 changes: 5 additions & 0 deletions agenta-web/src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,8 @@ body {
min-height: 120px;
}
}

/* It removes antd tabs transition to fix lag when using type="editable-card" */
.editable-card.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab {
transition: none;
}

0 comments on commit 873b409

Please sign in to comment.