Skip to content

Commit

Permalink
Add support for reordering models
Browse files Browse the repository at this point in the history
- Add support for reordering models
- Change from using model indices as ids to using their names
  • Loading branch information
albingroen committed Feb 28, 2023
1 parent 21751bb commit 7531d05
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 47 deletions.
2 changes: 1 addition & 1 deletion components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const Button = (
{...rest}
disabled={disabled}
className={classNames(
"font-medium rounded-md py-2 px-4 text-sm relative disabled:opacity-50",
"select-none font-medium rounded-md py-2 px-4 text-sm relative disabled:opacity-50",
"focus:outline-none focus-visible:ring-1",
{
primary: classNames(
Expand Down
2 changes: 1 addition & 1 deletion components/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ function renderItems(
}

return (
<Item asChild disabled={subItems.disabled}>
<Item asChild key={subItems.label} disabled={subItems.disabled}>
{renderItem(subItems, false, renderLink)}
</Item>
);
Expand Down
92 changes: 72 additions & 20 deletions components/Models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ import { PRISMA_DATABASES } from "../lib/prisma";
import { useEffect, useState } from "react";
import { useRouter } from "next/dist/client/router";
import { useSchemaContext } from "../lib/context";
import {
DragDropContext,
Draggable,
Droppable,
DropResult,
} from "react-beautiful-dnd";

export default function Models() {
const { schema, schemas, setSchema, setSchemas } = useSchemaContext();
Expand Down Expand Up @@ -76,22 +82,24 @@ export default function Models() {
};

const handleCreateModel = () => {
if (schema.models.some((model: Model) => model.name === "New")) {
const newModelName = "New";

if (schema.models.some((model: Model) => model.name === newModelName)) {
toast.error("A model called New exists");
} else {
const newSchema = {
...schema,
models: [
...schema.models,
{
name: "New",
name: newModelName,
fields: [ID_FIELD],
enums: [],
},
],
};
setSchema(newSchema);
push(`/schemas/${schema.name}/models/${newSchema.models.length - 1}`);
push(`/schemas/${schema.name}/models/${newModelName}`);
}
};

Expand Down Expand Up @@ -204,6 +212,24 @@ export default function Models() {

if (!schema) return null;

function handleDragModelsEnd(result: DropResult) {
const newModels = [...schema.models];

if (result.destination) {
newModels.splice(result.source.index, 1);

newModels.splice(
result.destination.index,
0,
schema.models[result.source.index]
);

updateSchema({
models: newModels,
});
}
}

return (
<>
<CommandPalette
Expand Down Expand Up @@ -355,23 +381,49 @@ export default function Models() {
</Stack>

{schema.models.length ? (
<ul className="w-full">
{schema.models.map((model, i) => {
const isActive = query.id === String(i);

return (
<li key={model.name}>
<SidebarItem
href={`/schemas/${schema.name}/models/${i}`}
icon={isActive ? CubeIconSolid : CubeIcon}
isActive={isActive}
>
{model.name}
</SidebarItem>
</li>
);
})}
</ul>
<DragDropContext onDragEnd={handleDragModelsEnd}>
<Droppable droppableId="models" direction="vertical">
{({ droppableProps, innerRef, placeholder }) => (
<ul className="w-full" {...droppableProps} ref={innerRef}>
{schema.models.map((model, i) => {
const isActive = query.id === model.name;

return (
<li key={model.name}>
<Draggable
index={i}
draggableId={model.name}
disableInteractiveElementBlocking
>
{(
{ draggableProps, innerRef, dragHandleProps },
{ isDragging }
) => (
<div
{...draggableProps}
{...dragHandleProps}
ref={innerRef}
>
<SidebarItem
href={`/schemas/${schema.name}/models/${model.name}`}
icon={isActive ? CubeIconSolid : CubeIcon}
isDragging={isDragging}
isActive={isActive}
>
{model.name}
</SidebarItem>
</div>
)}
</Draggable>
</li>
);
})}

{placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
) : null}
</Stack>

Expand Down
4 changes: 2 additions & 2 deletions components/Schemas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ export default function Schemas() {
</CommandPalette>

<Sidebar>
<Stack direction="vertical" className="p-5">
<p className="label-flat">My schemas</p>
<Stack direction="vertical" className="p-5" spacing="mini">
<p className="label">My schemas</p>

<Stack direction="vertical" spacing="huge">
<ul className="w-full">
Expand Down
13 changes: 10 additions & 3 deletions components/SidebarItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { classNames } from "react-cmdk";
interface SidebarItemProps {
children: ReactNode;
onClick?: () => void;
isDragging?: boolean;
isActive?: boolean;
target?: string;
icon: HeroIcon;
Expand All @@ -14,6 +15,7 @@ interface SidebarItemProps {
}

export default function SidebarItem({
isDragging,
icon: Icon,
children,
isActive,
Expand All @@ -26,9 +28,14 @@ export default function SidebarItem({
{...rest}
href={href}
className={classNames(
"hover:bg-gray-100 px-2 py-1.5 -mx-2 rounded-md flex items-center gap-2.5 group",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-black focus-visible:bg-gray-100",
"dark:hover:bg-neutral-800 dark:focus-visible:ring-white dark:focus-visible:bg-neutral-800"
"select-none px-2 py-1.5 -mx-2 rounded-md flex items-center gap-2.5 group",
isDragging
? "bg-gray-100 dark:bg-neutral-800"
: classNames(
"hover:bg-gray-100",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-black focus-visible:bg-gray-100",
"dark:hover:bg-neutral-800 dark:focus-visible:ring-white dark:focus-visible:bg-neutral-800"
)
)}
onClick={
onClick
Expand Down
42 changes: 24 additions & 18 deletions pages/schemas/[schemaId]/models/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,38 @@ import { Field, FieldType, Model } from "../../../../../lib/types";
import { TYPES } from "../../../../../lib/fields";
import { classNames } from "react-cmdk";
import { prismaTypesToIcons } from "../../../../../lib/icons";
import { useEffect, useState } from "react";
import { useState } from "react";
import { useRouter } from "next/dist/client/router";
import { useSchemaContext } from "../../../../../lib/context";

const Model = () => {
const { schema, setSchema } = useSchemaContext();
const { query, asPath, push } = useRouter();
const { query, push } = useRouter();
const { id } = query;

const model = schema.models?.[Number(id)];
const model = schema.models?.find((m) => m.name === id);

const [addingField, setAddingField] = useState<FieldType>();
const [editingField, setEditingField] = useState<string>();
const [name, setName] = useState<string>("");

if (!schema) return null;

const handleChangeName = (newName: string) => {
if (!model) return;

if (newName && newName !== model.name) {
if (schema.models.some((m: Model) => m.name === newName)) {
toast.error(`A model called ${newName} already exists`);
} else {
updateModel({ name: newName });
push(`/schemas/${schema.name}/models/${newName}`);
}
}
};

const updateModel = (values: any) => {
if (!model) return;

setSchema({
...schema,
models: schema.models.map((m: Model) =>
Expand All @@ -55,17 +61,9 @@ const Model = () => {
});
};

useEffect(() => {
if (model?.name) {
setName(model.name);
}
}, [model]);

if (!schema) return null;

return (
<>
{addingField && (
{addingField && model && (
<FieldComponent
defaultType={addingField ?? ("" as FieldType)}
onClose={() => setAddingField(undefined)}
Expand All @@ -80,7 +78,7 @@ const Model = () => {
/>
)}

{editingField && (
{editingField && model && (
<FieldComponent
onClose={() => {
setEditingField(undefined);
Expand All @@ -103,7 +101,7 @@ const Model = () => {

<Models />

{model && (
{model ? (
<Stack
spacing="large"
direction="vertical"
Expand All @@ -130,7 +128,7 @@ const Model = () => {
}}
contentEditable
>
{name}
{model.name}
</h2>

<Dropdown
Expand Down Expand Up @@ -160,7 +158,7 @@ const Model = () => {
],
});
push(
`/schemas/${schema.name}/models/${schema.models.length}`
`/schemas/${schema.name}/models/${duplicatedModelName}`
);
},
},
Expand Down Expand Up @@ -359,7 +357,7 @@ const Model = () => {
</DragDropContext>

<div className="flex-1 max-w-xs model-fields overflow-y-auto bg-gray-200 dark:bg-neutral-900 rounded-lg p-4 flex flex-col space-y-4">
<h2 className="font-medium text-xl">Add field</h2>
<h2 className="font-medium text-xl mt-0.5">Add field</h2>

<Stack direction="vertical">
{[
Expand Down Expand Up @@ -416,6 +414,14 @@ const Model = () => {
</div>
</div>
</Stack>
) : (
<Stack className="flex-1" justify="center" align="center">
<Stack>
<h3 className="text-gray-500 dark:text-neutral-400 fade-in">
Model not found
</h3>
</Stack>
</Stack>
)}
</>
);
Expand Down
4 changes: 2 additions & 2 deletions styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

@layer components {
.label {
@apply text-gray-500 dark:text-neutral-500 text-sm mb-2 inline-block;
@apply select-none text-gray-500 dark:text-neutral-500 text-sm mb-2 inline-block;
}

.label-flat {
@apply text-gray-500 dark:text-neutral-500 text-sm inline-block leading-none;
@apply select-none text-gray-500 dark:text-neutral-500 text-sm inline-block leading-none;
}

.input {
Expand Down

1 comment on commit 7531d05

@vercel
Copy link

@vercel vercel bot commented on 7531d05 Feb 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.