Skip to content

Commit

Permalink
feat: draggable window tabs (#34)
Browse files Browse the repository at this point in the history
* feat: draggable windows tab

* feat: improve drag and drop animation

* fix: drag overlay style when active

* fix: tab not clickable , dragged tab not active

* refactor: remove unused keyboard sensor

* refactor

* feat: add icon to the tab

---------

Co-authored-by: Visal .In <[email protected]>
  • Loading branch information
avevotsira and invisal authored Mar 20, 2024
1 parent 72ec87f commit d8b3679
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 52 deletions.
55 changes: 53 additions & 2 deletions 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
},
"dependencies": {
"@codemirror/lang-sql": "^6.5.5",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@lezer/common": "^1.2.1",
"@lezer/lr": "^1.4.0",
"@libsql/client": "^0.5.3",
Expand Down
11 changes: 10 additions & 1 deletion src/components/database-gui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { MessageChannelName } from "@/messages/const";
import { OpenTabsProps } from "@/messages/openTabs";
import QueryWindow from "@/components/tabs/query-tab";
import SchemaEditorTab from "@/components/tabs/schema-editor-tab";
import { LucideCode, LucideTable, LucideTableProperties } from "lucide-react";

export default function DatabaseGui() {
const DEFAULT_WIDTH = 300;
Expand All @@ -25,7 +26,12 @@ export default function DatabaseGui() {

const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const [tabs, setTabs] = useState<WindowTabItemProps[]>(() => [
{ title: "Query", key: "query", component: <QueryWindow /> },
{
title: "Query",
key: "query",
component: <QueryWindow />,
icon: LucideCode,
},
]);

useMessageListener<OpenTabsProps>(
Expand All @@ -45,6 +51,7 @@ export default function DatabaseGui() {
return [
...prev,
{
icon: LucideTable,
title: newTab.name,
key: newTab.key,
component: <TableDataContent tableName={newTab.tableName} />,
Expand All @@ -57,6 +64,7 @@ export default function DatabaseGui() {
return [
...prev,
{
icon: LucideCode,
title: newTab.name,
key: newTab.key,
component: <QueryWindow />,
Expand All @@ -74,6 +82,7 @@ export default function DatabaseGui() {
return [
...prev,
{
icon: LucideTableProperties,
title: newTab.name,
key: newTab.key,
component: <SchemaEditorTab tableName={newTab.tableName} />,
Expand Down
7 changes: 7 additions & 0 deletions src/components/sortable-tab.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.tab .close {
visibility: hidden;
}

.tab:hover .close {
visibility: visible;
}
76 changes: 76 additions & 0 deletions src/components/sortable-tab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { LucideIcon, LucideX } from "lucide-react";
import { useSortable } from "@dnd-kit/sortable";
import styles from "./sortable-tab.module.css";
import { WindowTabItemProps } from "./windows-tab";
import { cn } from "@/lib/utils";
import { forwardRef } from "react";
import { ButtonProps } from "./ui/button";

interface SortableTabProps {
tab: WindowTabItemProps;
selected: boolean;
onSelectChange: () => void;
onClose: () => void;
}

type WindowTabItemButtonProps = ButtonProps & {
selected?: boolean;
title: string;
icon: LucideIcon;
onClose?: () => void;
};

export const WindowTabItemButton = forwardRef<
HTMLButtonElement,
WindowTabItemButtonProps
>(function WindowTabItemButton(props: WindowTabItemButtonProps, ref) {
const { icon: Icon, selected, title, onClose, ...rest } = props;

const className = cn(
"h-9 flex items-center text-left text-xs font-semibold px-2 w-max-[150px]",
styles.tab,
selected
? "border-x border-t bg-background border-b-background rounded-t"
: "border-b border-t border-t-secondary border-x-secondary opacity-65 hover:opacity-100"
);

return (
<button className={className} ref={ref} {...rest}>
<Icon className="w-4 h-4 ml-2 grow-0 shrink-0" />
<div className="line-clamp-1 grow px-2">{title}</div>
<div
className={cn(
"rounded-full hover:bg-red-600 hover:text-white w-4 h-4 ml-2 flex justify-center items-center",
styles.close
)}
>
<LucideX
className={cn("w-3 h-3 grow-0 shrink-0", styles.close)}
onClick={onClose}
/>
</div>
</button>
);
});

export function SortableTab({
tab,
selected,
onSelectChange,
onClose,
}: SortableTabProps) {
const { attributes, listeners, setNodeRef } = useSortable({ id: tab.key });

return (
<WindowTabItemButton
ref={setNodeRef}
icon={tab.icon}
title={tab.title}
onClick={onSelectChange}
selected={selected}
onClose={onClose}
{...attributes}
{...listeners}
/>
);
}
12 changes: 6 additions & 6 deletions src/components/tabs/table-data-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ export default function TableDataWindow({ tableName }: TableDataContentProps) {
</AlertDialog>
)}
<div className="flex-shrink-0 flex-grow-0">
<div className="flex p-1 gap-1">
<div className="flex p-1 gap-1 pb-2">
<Button
variant={"outline"}
variant={"ghost"}
size={"sm"}
disabled={!changeNumber || isExecuting}
onClick={onCommit}
Expand All @@ -177,7 +177,7 @@ export default function TableDataWindow({ tableName }: TableDataContentProps) {
</Button>

<Button
variant={"outline"}
variant={"ghost"}
size={"sm"}
disabled={!changeNumber}
onClick={onDiscard}
Expand All @@ -189,16 +189,16 @@ export default function TableDataWindow({ tableName }: TableDataContentProps) {
<Separator orientation="vertical" />
</div>

<Button variant={"outline"} size={"sm"} onClick={onNewRow}>
<Button variant={"ghost"} size={"sm"} onClick={onNewRow}>
<LucidePlus className="w-4 h-4 text-green-600" />
</Button>

<Button variant={"outline"} size={"sm"} onClick={onRemoveRow}>
<Button variant={"ghost"} size={"sm"} onClick={onRemoveRow}>
<LucideDelete className="w-4 h-4 text-red-600" />
</Button>

<Button
variant={"outline"}
variant={"ghost"}
size={"sm"}
onClick={() => setRevision((prev) => prev + 1)}
disabled={loading}
Expand Down
Loading

0 comments on commit d8b3679

Please sign in to comment.