From 77a2cfd426a4c64157661875bf9a7510ad427f88 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Tue, 19 May 2026 11:59:25 +0700 Subject: [PATCH] =?UTF-8?q?web(ui):=20form=20patterns=20=E2=80=94=20FormFi?= =?UTF-8?q?eld,=20formsnap,=20directories=20pilot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../ui/patterns/confirm/confirm-dialog.svelte | 45 ++++++ .../patterns/confirm/confirm-state.svelte.ts | 41 +++++ .../patterns/data-table/app-data-table.svelte | 147 ++++++++++++++++++ web/src/lib/ui/patterns/data-table/types.ts | 8 + .../patterns/empty-state/empty-state.svelte | 29 ++++ .../ui/patterns/feedback/card-skeleton.svelte | 15 ++ .../patterns/feedback/table-skeleton.svelte | 27 ++++ web/src/lib/ui/patterns/form/app-input.svelte | 24 +++ .../lib/ui/patterns/form/app-textarea.svelte | 13 ++ .../lib/ui/patterns/form/form-field.svelte | 30 ++++ web/src/routes/+layout.svelte | 2 + web/src/routes/directories/+page.svelte | 36 ++--- 12 files changed, 397 insertions(+), 20 deletions(-) create mode 100644 web/src/lib/ui/patterns/confirm/confirm-dialog.svelte create mode 100644 web/src/lib/ui/patterns/confirm/confirm-state.svelte.ts create mode 100644 web/src/lib/ui/patterns/data-table/app-data-table.svelte create mode 100644 web/src/lib/ui/patterns/data-table/types.ts create mode 100644 web/src/lib/ui/patterns/empty-state/empty-state.svelte create mode 100644 web/src/lib/ui/patterns/feedback/card-skeleton.svelte create mode 100644 web/src/lib/ui/patterns/feedback/table-skeleton.svelte create mode 100644 web/src/lib/ui/patterns/form/app-input.svelte create mode 100644 web/src/lib/ui/patterns/form/app-textarea.svelte create mode 100644 web/src/lib/ui/patterns/form/form-field.svelte diff --git a/web/src/lib/ui/patterns/confirm/confirm-dialog.svelte b/web/src/lib/ui/patterns/confirm/confirm-dialog.svelte new file mode 100644 index 0000000..2570230 --- /dev/null +++ b/web/src/lib/ui/patterns/confirm/confirm-dialog.svelte @@ -0,0 +1,45 @@ + + + + {#if state} + + + {state.title} + {#if state.description} + {state.description} + {/if} + + + closeConfirm()}> + {state.cancelLabel ?? 'Отмена'} + + + {state.loading ? '…' : (state.confirmLabel ?? 'Подтвердить')} + + + + {/if} + diff --git a/web/src/lib/ui/patterns/confirm/confirm-state.svelte.ts b/web/src/lib/ui/patterns/confirm/confirm-state.svelte.ts new file mode 100644 index 0000000..d30bd20 --- /dev/null +++ b/web/src/lib/ui/patterns/confirm/confirm-state.svelte.ts @@ -0,0 +1,41 @@ +export type ConfirmOptions = { + title: string; + description?: string; + confirmLabel?: string; + cancelLabel?: string; + destructive?: boolean; + onConfirm: () => void | Promise; +}; + +export type ConfirmState = ConfirmOptions & { + open: boolean; + loading: boolean; +}; + +export const confirmState = $state<{ current: ConfirmState | null }>({ current: null }); + +export function confirm(options: ConfirmOptions) { + return new Promise((resolve) => { + confirmState.current = { + ...options, + open: true, + loading: false, + onConfirm: async () => { + if (!confirmState.current) return; + confirmState.current = { ...confirmState.current, loading: true }; + try { + await options.onConfirm(); + resolve(true); + } catch { + resolve(false); + } finally { + confirmState.current = null; + } + } + }; + }); +} + +export function closeConfirm() { + confirmState.current = null; +} diff --git a/web/src/lib/ui/patterns/data-table/app-data-table.svelte b/web/src/lib/ui/patterns/data-table/app-data-table.svelte new file mode 100644 index 0000000..1c000e3 --- /dev/null +++ b/web/src/lib/ui/patterns/data-table/app-data-table.svelte @@ -0,0 +1,147 @@ + + +
+ {#if toolbar} +
{@render toolbar()}
+ {/if} + + {#if error} + + Ошибка загрузки + {error} + + {/if} + +
+ + + + {#each columns as col (col.id)} + + {#if col.sortable} + + {:else} + {col.label} + {/if} + + {/each} + + + + {#if loading} + {#each Array(skeletonRows) as _, i (i)} + + {#each columns as col (col.id)} + + + + {/each} + + {/each} + {:else if sortedRows.length === 0} + + + + + + {:else} + {#each sortedRows as row (rowKey(row))} + + {#each columns as col (col.id)} + + {@render cell({ row, column: col })} + + {/each} + + {/each} + {/if} + + +
+
diff --git a/web/src/lib/ui/patterns/data-table/types.ts b/web/src/lib/ui/patterns/data-table/types.ts new file mode 100644 index 0000000..85b0c48 --- /dev/null +++ b/web/src/lib/ui/patterns/data-table/types.ts @@ -0,0 +1,8 @@ +export type DataTableColumn = { + id: string; + label: string; + sortable?: boolean; + sortValue?: (row: T) => string | number | null | undefined; + class?: string; + headerClass?: string; +}; diff --git a/web/src/lib/ui/patterns/empty-state/empty-state.svelte b/web/src/lib/ui/patterns/empty-state/empty-state.svelte new file mode 100644 index 0000000..a88ca8d --- /dev/null +++ b/web/src/lib/ui/patterns/empty-state/empty-state.svelte @@ -0,0 +1,29 @@ + + +
+ {#if Icon} + + {/if} +

{title}

+ {#if description} +

{description}

+ {/if} + {#if action} +
{@render action()}
+ {/if} +
diff --git a/web/src/lib/ui/patterns/feedback/card-skeleton.svelte b/web/src/lib/ui/patterns/feedback/card-skeleton.svelte new file mode 100644 index 0000000..a4844b8 --- /dev/null +++ b/web/src/lib/ui/patterns/feedback/card-skeleton.svelte @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/web/src/lib/ui/patterns/feedback/table-skeleton.svelte b/web/src/lib/ui/patterns/feedback/table-skeleton.svelte new file mode 100644 index 0000000..f2cc84f --- /dev/null +++ b/web/src/lib/ui/patterns/feedback/table-skeleton.svelte @@ -0,0 +1,27 @@ + + +
+ + + {#each Array(rows) as _, ri (ri)} + + {#each Array(columns) as _, ci (ci)} + + + + {/each} + + {/each} + + +
diff --git a/web/src/lib/ui/patterns/form/app-input.svelte b/web/src/lib/ui/patterns/form/app-input.svelte new file mode 100644 index 0000000..c6f6c47 --- /dev/null +++ b/web/src/lib/ui/patterns/form/app-input.svelte @@ -0,0 +1,24 @@ + + + diff --git a/web/src/lib/ui/patterns/form/app-textarea.svelte b/web/src/lib/ui/patterns/form/app-textarea.svelte new file mode 100644 index 0000000..391e58f --- /dev/null +++ b/web/src/lib/ui/patterns/form/app-textarea.svelte @@ -0,0 +1,13 @@ + + +