mirror of
https://github.com/LukeHagar/toasty.git
synced 2025-12-06 04:21:49 +00:00
146 lines
4.2 KiB
Svelte
146 lines
4.2 KiB
Svelte
<script lang="ts">
|
|
import { schemaStore, addSchema, addEndpoint, selectItem, type SchemaItem, type APIEndpoint } from '$lib/stores/schema';
|
|
import SchemaNode from './SchemaNode.svelte';
|
|
import EndpointNode from './EndpointNode.svelte';
|
|
import { Button } from '$lib/components/ui/button';
|
|
import { Plus } from 'lucide-svelte';
|
|
|
|
let canvasElement: HTMLDivElement;
|
|
let isDragOver = $state(false);
|
|
|
|
function handleDragOver(event: DragEvent) {
|
|
event.preventDefault();
|
|
isDragOver = true;
|
|
}
|
|
|
|
function handleDragLeave(event: DragEvent) {
|
|
event.preventDefault();
|
|
isDragOver = false;
|
|
}
|
|
|
|
function handleDrop(event: DragEvent) {
|
|
event.preventDefault();
|
|
isDragOver = false;
|
|
|
|
const rect = canvasElement.getBoundingClientRect();
|
|
const x = event.clientX - rect.left;
|
|
const y = event.clientY - rect.top;
|
|
|
|
// Get drag data
|
|
const schemaType = event.dataTransfer?.getData('schema-type');
|
|
const schemaFormat = event.dataTransfer?.getData('schema-format');
|
|
const apiMethod = event.dataTransfer?.getData('api-method');
|
|
const apiComponent = event.dataTransfer?.getData('api-component');
|
|
|
|
if (schemaType) {
|
|
// Create new schema item
|
|
const newSchema: Omit<SchemaItem, 'id'> = {
|
|
type: schemaType as any,
|
|
name: `new_${schemaType}`,
|
|
required: false,
|
|
description: `A ${schemaType} field`,
|
|
format: schemaFormat || undefined,
|
|
children: schemaType === 'object' || schemaType === 'array' ? [] : undefined,
|
|
x,
|
|
y
|
|
};
|
|
addSchema(newSchema);
|
|
} else if (apiMethod) {
|
|
// Create new endpoint
|
|
const newEndpoint: Omit<APIEndpoint, 'id'> = {
|
|
method: apiMethod as any,
|
|
path: `/${apiMethod.toLowerCase()}/resource`,
|
|
summary: `${apiMethod} operation`,
|
|
description: `${apiMethod} endpoint description`,
|
|
x,
|
|
y
|
|
};
|
|
addEndpoint(newEndpoint);
|
|
}
|
|
}
|
|
|
|
function handleItemClick(item: SchemaItem | APIEndpoint) {
|
|
selectItem(item);
|
|
}
|
|
|
|
function handleKeyDown(event: KeyboardEvent) {
|
|
if (event.key === 'Enter') {
|
|
// Trigger the button click event on Enter key press
|
|
const button = event.target as HTMLButtonElement;
|
|
button.click();
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<!-- Created interactive drag and drop canvas -->
|
|
<div
|
|
bind:this={canvasElement}
|
|
class="relative min-h-96 border-2 border-dashed rounded-lg transition-colors {isDragOver ? 'border-primary bg-primary/5' : 'border-border bg-muted/20'}"
|
|
role="button"
|
|
aria-label="Drop Components Here"
|
|
ondragover={handleDragOver}
|
|
ondragleave={handleDragLeave}
|
|
ondrop={handleDrop}
|
|
tabindex="0"
|
|
>
|
|
{#if $schemaStore.schemas.length === 0 && $schemaStore.endpoints.length === 0}
|
|
<div class="absolute inset-0 flex items-center justify-center">
|
|
<div class="text-center">
|
|
<div class="w-16 h-16 mx-auto mb-4 rounded-full bg-primary/10 flex items-center justify-center">
|
|
<Plus class="w-8 h-8 text-primary" />
|
|
</div>
|
|
<h3 class="text-lg font-semibold text-foreground mb-2">Drop Components Here</h3>
|
|
<p class="text-muted-foreground mb-4">Drag schema types and API components from the sidebar to build your OpenAPI specification</p>
|
|
<Button onkeydown={handleKeyDown} tabindex="0">
|
|
<Plus class="w-4 h-4 mr-2" />
|
|
Add Component
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
{:else}
|
|
<!-- Render dropped schemas -->
|
|
{#each $schemaStore.schemas as schema}
|
|
<div
|
|
class="absolute cursor-pointer"
|
|
style="left: {schema.x}px; top: {schema.y}px;"
|
|
role="button"
|
|
aria-label={`Click to select ${schema.name}`}
|
|
onclick={() => handleItemClick(schema)}
|
|
onkeydown={(event) => {
|
|
if (event.key === 'Enter') {
|
|
handleItemClick(schema);
|
|
}
|
|
}}
|
|
tabindex="0"
|
|
>
|
|
<SchemaNode
|
|
{...schema}
|
|
selected={$schemaStore.selectedItem?.id === schema.id}
|
|
/>
|
|
</div>
|
|
{/each}
|
|
|
|
<!-- Render dropped endpoints -->
|
|
{#each $schemaStore.endpoints as endpoint}
|
|
<div
|
|
class="absolute cursor-pointer"
|
|
style="left: {endpoint.x}px; top: {endpoint.y}px;"
|
|
role="button"
|
|
aria-label={`Click to select ${endpoint.path}`}
|
|
onclick={() => handleItemClick(endpoint)}
|
|
onkeydown={(event) => {
|
|
if (event.key === 'Enter') {
|
|
handleItemClick(endpoint);
|
|
}
|
|
}}
|
|
tabindex="0"
|
|
>
|
|
<EndpointNode
|
|
{...endpoint}
|
|
selected={$schemaStore.selectedItem?.id === endpoint.id}
|
|
/>
|
|
</div>
|
|
{/each}
|
|
{/if}
|
|
</div>
|