fuma 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/api/client.d.ts +3 -0
  2. package/dist/api/client.js +24 -0
  3. package/dist/api/config.d.ts +46 -0
  4. package/dist/api/config.js +7 -0
  5. package/dist/api/index.d.ts +3 -0
  6. package/dist/api/index.js +3 -0
  7. package/dist/api/server.d.ts +11 -0
  8. package/dist/api/server.js +17 -0
  9. package/dist/{Meta.svelte → private/Meta.svelte} +4 -4
  10. package/dist/private/api.d.ts +47 -0
  11. package/dist/private/api.js +9 -0
  12. package/dist/private/model.d.ts +40 -0
  13. package/dist/private/model.js +17 -0
  14. package/dist/server/auth.js +1 -1
  15. package/dist/server/parseFormData.js +7 -1
  16. package/dist/server/parseQuery.d.ts +1 -10
  17. package/dist/server/parseQuery.js +4 -5
  18. package/dist/ui/drawer/Drawer.svelte +19 -11
  19. package/dist/ui/drawer/Drawer.svelte.d.ts +42 -7
  20. package/dist/ui/drawer/layers.d.ts +2 -1
  21. package/dist/ui/drawer/layers.js +5 -4
  22. package/dist/ui/form/Form.svelte +39 -24
  23. package/dist/ui/form/Form.svelte.d.ts +22 -14
  24. package/dist/ui/form/form.d.ts +20 -12
  25. package/dist/ui/form/form.js +34 -26
  26. package/dist/ui/form/formInput.d.ts +1 -1
  27. package/dist/ui/input/FormControl.svelte.d.ts +1 -1
  28. package/dist/ui/input/InputBoolean.svelte.d.ts +2 -2
  29. package/dist/ui/input/InputCheckboxs.svelte.d.ts +2 -2
  30. package/dist/ui/input/InputCheckboxsMenu.svelte +5 -5
  31. package/dist/ui/input/InputCheckboxsMenu.svelte.d.ts +2 -2
  32. package/dist/ui/input/InputCombo.svelte.d.ts +2 -2
  33. package/dist/ui/input/InputNumber.svelte.d.ts +1 -1
  34. package/dist/ui/input/InputPassword.svelte.d.ts +2 -2
  35. package/dist/ui/input/InputRadio.svelte.d.ts +2 -2
  36. package/dist/ui/input/InputRelation.svelte +7 -2
  37. package/dist/ui/input/InputRelations.svelte +7 -3
  38. package/dist/ui/input/InputText.svelte.d.ts +2 -2
  39. package/dist/ui/input/textRich/InputTextRich.svelte.d.ts +1 -1
  40. package/dist/ui/input/types.d.ts +2 -2
  41. package/dist/ui/login/Login.svelte +7 -3
  42. package/dist/ui/table/field.d.ts +1 -1
  43. package/dist/utils/constant.d.ts +1 -0
  44. package/dist/utils/constant.js +1 -0
  45. package/dist/utils/index.d.ts +1 -0
  46. package/dist/utils/index.js +1 -0
  47. package/dist/utils/jsonParse.d.ts +1 -1
  48. package/dist/utils/jsonParse.js +5 -3
  49. package/dist/validation/form.d.ts +6 -5
  50. package/dist/validation/form.js +8 -8
  51. package/dist/validation/zod.d.ts +77 -19
  52. package/dist/validation/zod.js +12 -14
  53. package/package.json +7 -2
  54. /package/dist/{Meta.svelte.d.ts → private/Meta.svelte.d.ts} +0 -0
  55. /package/dist/{server → private}/prisma.d.ts +0 -0
  56. /package/dist/{server → private}/prisma.js +0 -0
@@ -0,0 +1,3 @@
1
+ import { type CreateAxiosDefaults } from 'axios';
2
+ import type { TypeMap, ApiConfig, ApiClient } from './config.js';
3
+ export declare function useApiClient<T extends TypeMap, C extends ApiConfig<T>>(prismaConfig: C, axiosConfig?: CreateAxiosDefaults): ApiClient<T, C>;
@@ -0,0 +1,24 @@
1
+ import axios, {} from 'axios';
2
+ import * as devalue from 'devalue';
3
+ export function useApiClient(prismaConfig, axiosConfig = {}) {
4
+ const _axios = axios.create({ baseURL: '/api', ...axiosConfig });
5
+ function useSearch(model) {
6
+ return async (search) => {
7
+ const config = { params: { search, model } };
8
+ const { data, headers } = await _axios.get('', config);
9
+ ensureJson(headers);
10
+ return devalue.unflatten(data);
11
+ };
12
+ }
13
+ const api = Object.keys(prismaConfig).reduce((acc, model) => ({
14
+ ...acc,
15
+ [model]: useSearch(model)
16
+ }), {});
17
+ return api;
18
+ }
19
+ function ensureJson(headers) {
20
+ const contentType = headers['content-type'];
21
+ if (contentType !== 'application/json') {
22
+ throw new Error(`Response Content-type is '${contentType}' instead 'application/json'`);
23
+ }
24
+ }
@@ -0,0 +1,46 @@
1
+ import type { OperationPayload, GetResult } from '@prisma/client/runtime/library.js';
2
+ export type Operation = 'findFirst' | 'findFirstOrThrow' | 'findUnique' | 'findUniqueOrThrow' | 'findMany' | 'create' | 'createMany' | 'update' | 'updateMany' | 'upsert' | 'delete' | 'deleteMany' | 'aggregate' | 'count' | 'groupBy';
3
+ export type TypeMap = {
4
+ model: {
5
+ [M: string]: {
6
+ payload: OperationPayload;
7
+ operations: {
8
+ [O in Operation]: {
9
+ args: {};
10
+ };
11
+ };
12
+ };
13
+ };
14
+ };
15
+ export type Result<T extends TypeMap, M extends keyof T['model'], Query extends {}, Payload extends T['model'][M]['payload'] = T['model'][M]['payload']> = GetResult<Payload, Query, 'findMany'>;
16
+ export type Query<T extends TypeMap, M extends keyof T['model'], O extends Operation = 'findMany'> = T['model'][M]['operations'][O]['args'];
17
+ export type QueryToResult<T extends TypeMap, M extends keyof T['model'], O extends Operation = 'findMany', Q extends Query<T, M, O> = {}> = (query: Q) => Promise<Result<T, M, Q>>;
18
+ export type ApiPrismaClient<T extends TypeMap> = {
19
+ [M in keyof T['model']]: {
20
+ [O in Operation]: QueryToResult<T, M, O>;
21
+ };
22
+ };
23
+ /**
24
+ * @example
25
+ * const config = {
26
+ * user: (contains) => ({ where: { username: { contains } } }),
27
+ * } satisfies ApiConfig<Prisma.TypeMap>
28
+ */
29
+ export type ApiConfig<T extends TypeMap> = {
30
+ [M in keyof T['model']]?: (search: string) => Query<T, M, 'findMany'>;
31
+ };
32
+ export type ApiClient<T extends TypeMap, C extends ApiConfig<T>> = {
33
+ [M in keyof C]: C[M] extends (...args: any) => infer Q ? (search: string) => Promise<Result<T, M & string, Q & {}>> : never;
34
+ };
35
+ export type ModelApiQuery = {
36
+ model: string;
37
+ search?: string;
38
+ take?: number;
39
+ skip?: number;
40
+ };
41
+ export declare const modelApiQuery: {
42
+ model: import("zod").ZodString;
43
+ search: import("zod").ZodDefault<import("zod").ZodString>;
44
+ take: import("zod").ZodDefault<import("zod").ZodNumber>;
45
+ skip: import("zod").ZodDefault<import("zod").ZodNumber>;
46
+ };
@@ -0,0 +1,7 @@
1
+ import { z } from '../validation/zod.js';
2
+ export const modelApiQuery = {
3
+ model: z.string(),
4
+ search: z.string().default(''),
5
+ take: z.number().default(10),
6
+ skip: z.number().default(0)
7
+ };
@@ -0,0 +1,3 @@
1
+ export * from './config.js';
2
+ export * from './client.js';
3
+ export * from './server.js';
@@ -0,0 +1,3 @@
1
+ export * from './config.js';
2
+ export * from './client.js';
3
+ export * from './server.js';
@@ -0,0 +1,11 @@
1
+ import { type RequestHandler } from '@sveltejs/kit';
2
+ import { type ApiConfig, type TypeMap } from './config.js';
3
+ interface BasicPrismaClient {
4
+ [K: string]: any;
5
+ $connect: any;
6
+ $transaction: any;
7
+ }
8
+ export declare function apiServer<T extends TypeMap>(config: ApiConfig<T>, client: BasicPrismaClient): {
9
+ GET: RequestHandler;
10
+ };
11
+ export {};
@@ -0,0 +1,17 @@
1
+ import { error } from '@sveltejs/kit';
2
+ import { json } from '../server/json.js';
3
+ import { parseQuery } from '../server/parseQuery.js';
4
+ import { modelApiQuery } from './config.js';
5
+ export function apiServer(config, client) {
6
+ return {
7
+ async GET(event) {
8
+ const { model, search, take, skip } = parseQuery(event.url, modelApiQuery);
9
+ if (!(model in config))
10
+ error(400, `Model "${model}" is not exposed in this API.`);
11
+ const baseQuery = { take, skip };
12
+ //@ts-ignore
13
+ const result = await client[model].findMany({ ...baseQuery, ...config[model](search) });
14
+ return json(result);
15
+ }
16
+ };
17
+ }
@@ -1,9 +1,9 @@
1
1
  <script>import {} from "svelte";
2
2
  import { slide } from "svelte/transition";
3
3
  import { mdiEyeOffOutline, mdiEyeOutline } from "@mdi/js";
4
- import { Card } from "./ui/card/index.js";
5
- import { Table } from "./ui/table/index.js";
6
- import { Icon } from "./ui/icon/index.js";
4
+ import { Card } from "../ui/card/index.js";
5
+ import { Table } from "../ui/table/index.js";
6
+ import { Icon } from "../ui/icon/index.js";
7
7
  export let name = "";
8
8
  export let description = "";
9
9
  export let component;
@@ -27,7 +27,7 @@ function updateComponentMeta(c) {
27
27
  }
28
28
  </script>
29
29
 
30
- <Card class="mx-auto mt-6 max-w-4xl">
30
+ <Card class="mx-auto mb-6 max-w-4xl">
31
31
  <div slot="title" class="flex items-center gap-4">
32
32
  <span class="grow">{name}</span>
33
33
  <button class="btn btn-square btn-sm" on:click={() => (isPropsVisible = !isPropsVisible)}>
@@ -0,0 +1,47 @@
1
+ import type { Prisma } from '@prisma/client';
2
+ export declare const apiConfig: {
3
+ Tag: (search: string) => {
4
+ where: {
5
+ name: {
6
+ contains: string;
7
+ };
8
+ };
9
+ };
10
+ Post: (search: string) => {
11
+ where: {
12
+ tags: {
13
+ some: {
14
+ name: {
15
+ contains: string;
16
+ };
17
+ };
18
+ };
19
+ };
20
+ include: {
21
+ tags: true;
22
+ };
23
+ };
24
+ };
25
+ export declare const api: import("../api/config.js").ApiClient<Prisma.TypeMap<import("@prisma/client/runtime/library").DefaultArgs>, {
26
+ Tag: (search: string) => {
27
+ where: {
28
+ name: {
29
+ contains: string;
30
+ };
31
+ };
32
+ };
33
+ Post: (search: string) => {
34
+ where: {
35
+ tags: {
36
+ some: {
37
+ name: {
38
+ contains: string;
39
+ };
40
+ };
41
+ };
42
+ };
43
+ include: {
44
+ tags: true;
45
+ };
46
+ };
47
+ }>;
@@ -0,0 +1,9 @@
1
+ import { useApiClient } from '../api/client.js';
2
+ export const apiConfig = {
3
+ Tag: (search) => ({ where: { name: { contains: search } } }),
4
+ Post: (search) => ({
5
+ where: { tags: { some: { name: { contains: search } } } },
6
+ include: { tags: true }
7
+ })
8
+ };
9
+ export const api = useApiClient(apiConfig);
@@ -0,0 +1,40 @@
1
+ export declare const modelPost: {
2
+ content: import("zod").ZodString;
3
+ aString: import("zod").ZodString;
4
+ aBoolean: import("zod").ZodBoolean;
5
+ aDate: import("zod").ZodDate;
6
+ aNumber: import("zod").ZodNumber;
7
+ tags: import("zod").ZodEffects<import("zod").ZodArray<import("zod").ZodObject<{
8
+ id: import("zod").ZodString;
9
+ }, "strip", import("zod").ZodTypeAny, {
10
+ id: string;
11
+ }, {
12
+ id: string;
13
+ }>, "many">, Partial<Record<"set" | "delete" | "connect" | "disconnect", {
14
+ id: string;
15
+ }[]>>, {
16
+ id: string;
17
+ }[]>;
18
+ };
19
+ export declare const modelPostUpdate: {
20
+ id: import("zod").ZodString;
21
+ tags: import("zod").ZodEffects<import("zod").ZodArray<import("zod").ZodObject<{
22
+ id: import("zod").ZodString;
23
+ }, "strip", import("zod").ZodTypeAny, {
24
+ id: string;
25
+ }, {
26
+ id: string;
27
+ }>, "many">, Partial<Record<"set" | "delete" | "connect" | "disconnect", {
28
+ id: string;
29
+ }[]>>, {
30
+ id: string;
31
+ }[]>;
32
+ content: import("zod").ZodString;
33
+ aString: import("zod").ZodString;
34
+ aBoolean: import("zod").ZodBoolean;
35
+ aDate: import("zod").ZodDate;
36
+ aNumber: import("zod").ZodNumber;
37
+ };
38
+ export declare const modelTag: {
39
+ name: import("zod").ZodString;
40
+ };
@@ -0,0 +1,17 @@
1
+ import { z } from '../validation/zod.js';
2
+ export const modelPost = {
3
+ content: z.string().min(10),
4
+ aString: z.string(),
5
+ aBoolean: z.boolean(),
6
+ aDate: z.date(),
7
+ aNumber: z.number(),
8
+ tags: z.relations.connect
9
+ };
10
+ export const modelPostUpdate = {
11
+ ...modelPost,
12
+ id: z.string(),
13
+ tags: z.relations.set
14
+ };
15
+ export const modelTag = {
16
+ name: z.string().min(2)
17
+ };
@@ -1,7 +1,7 @@
1
1
  import { Lucia } from 'lucia';
2
2
  import { dev } from '$app/environment';
3
3
  import { PrismaAdapter } from '@lucia-auth/adapter-prisma';
4
- import { prisma } from './prisma.js';
4
+ import { prisma } from '../private/prisma.js';
5
5
  const adapter = new PrismaAdapter(prisma.session, prisma.user);
6
6
  export const lucia = new Lucia(adapter, {
7
7
  sessionCookie: {
@@ -1,3 +1,5 @@
1
+ import { USE_JSON_PARSER } from '../utils/constant.js';
2
+ import { jsonParse } from '../utils/jsonParse.js';
1
3
  import z from 'zod';
2
4
  export async function parseFormData(requestOrFormData, shapes, validation) {
3
5
  const formData = requestOrFormData instanceof Request ? await requestOrFormData.formData() : requestOrFormData;
@@ -24,7 +26,11 @@ export async function parseFormData(requestOrFormData, shapes, validation) {
24
26
  function flateToNeestedObject(flatObject) {
25
27
  const obj = {};
26
28
  Object.entries(flatObject).forEach(([key, value]) => {
27
- set(obj, key, value);
29
+ const useJsonParse = typeof value === 'string' && value.startsWith(USE_JSON_PARSER);
30
+ const _value = useJsonParse ? jsonParse(value.replace(USE_JSON_PARSER, ''), null) : value;
31
+ if (useJsonParse)
32
+ console.log({ value, _value });
33
+ set(obj, key, _value);
28
34
  });
29
35
  return obj;
30
36
  }
@@ -1,11 +1,2 @@
1
- /// <reference types="@sveltejs/kit" />
2
1
  import type { ZodRawShape } from 'zod';
3
- export declare function parseQuery<Type extends ZodRawShape>(url: URL, shape: Type): {
4
- err: import("@sveltejs/kit").ActionFailure<{
5
- issues: import("zod").ZodIssue[];
6
- }>;
7
- data?: undefined;
8
- } | {
9
- data: { [k_1 in keyof import("zod").objectUtil.addQuestionMarks<import("zod").baseObjectOutputType<Type>, { [k in keyof import("zod").baseObjectOutputType<Type>]: undefined extends import("zod").baseObjectOutputType<Type>[k] ? never : k; }[keyof Type]>]: import("zod").objectUtil.addQuestionMarks<import("zod").baseObjectOutputType<Type>, { [k_2 in keyof import("zod").baseObjectOutputType<Type>]: undefined extends import("zod").baseObjectOutputType<Type>[k_2] ? never : k_2; }[keyof Type]>[k_1]; };
10
- err?: undefined;
11
- };
2
+ export declare function parseQuery<Type extends ZodRawShape>(url: URL, shape: Type): { [k_1 in keyof import("zod").objectUtil.addQuestionMarks<import("zod").baseObjectOutputType<Type>, { [k in keyof import("zod").baseObjectOutputType<Type>]: undefined extends import("zod").baseObjectOutputType<Type>[k] ? never : k; }[keyof Type]>]: import("zod").objectUtil.addQuestionMarks<import("zod").baseObjectOutputType<Type>, { [k_2 in keyof import("zod").baseObjectOutputType<Type>]: undefined extends import("zod").baseObjectOutputType<Type>[k_2] ? never : k_2; }[keyof Type]>[k_1]; };
@@ -1,4 +1,4 @@
1
- import { fail } from '@sveltejs/kit';
1
+ import { error } from '@sveltejs/kit';
2
2
  import { z } from '../validation/zod.js';
3
3
  export function parseQuery(url, shape) {
4
4
  const queryRaw = {};
@@ -8,8 +8,7 @@ export function parseQuery(url, shape) {
8
8
  queryRaw[name] = param;
9
9
  });
10
10
  const parsed = z.object(shape).safeParse(queryRaw);
11
- if (parsed.success === false) {
12
- return { err: fail(400, { issues: parsed.error.issues }) };
13
- }
14
- return { data: parsed.data };
11
+ if (parsed.success === false)
12
+ error(400, { message: parsed.error.message });
13
+ return parsed.data;
15
14
  }
@@ -8,19 +8,22 @@ import { subscibeDrawerLayers } from "./layers.js";
8
8
  import { contextContainer } from "../context.js";
9
9
  export let title = "";
10
10
  export let key;
11
- export let value = "1";
12
11
  let klass = "";
13
12
  export { klass as class };
14
13
  export let maxWidth = "32rem";
15
14
  export let classHeader = "";
16
15
  export let classBody = "";
17
- export function open() {
18
- goto($urlParam.with({ [key]: value }), { replaceState: true, noScroll: true });
16
+ export function open(value = 1, options = {}) {
17
+ return goto($urlParam.with({ [key]: value }), {
18
+ ...options,
19
+ replaceState: true,
20
+ noScroll: true
21
+ });
19
22
  }
20
- export function close() {
21
- goto($urlParam.without(key), { replaceState: true, noScroll: true });
23
+ export function close(options = {}) {
24
+ return goto($urlParam.without(key), { ...options, replaceState: true, noScroll: true });
22
25
  }
23
- const { offset, destroy, isActive } = subscibeDrawerLayers(key, value);
26
+ const { offset, index, destroy, isActive } = subscibeDrawerLayers(key);
24
27
  onDestroy(destroy);
25
28
  contextContainer.set("drawer");
26
29
  </script>
@@ -28,15 +31,20 @@ contextContainer.set("drawer");
28
31
  {#if $isActive}
29
32
  <!-- svelte-ignore a11y-no-static-element-interactions -->
30
33
  <div
31
- on:click={close}
32
- on:keyup={close}
34
+ on:click={() => close()}
35
+ on:keyup={() => close()}
33
36
  transition:fade={{ duration: 200 }}
34
- class="fixed inset-0 z-10 bg-black/15 backdrop-blur-[1.5px] dark:bg-white/15"
37
+ style="z-index: {10 + $index};"
38
+ class="fixed inset-0 bg-black/15 backdrop-blur-[1.5px] dark:bg-white/15"
35
39
  />
36
40
 
37
41
  <aside
38
42
  transition:fly|local={{ x: 500, duration: 200, opacity: 1 }}
39
- style="max-width: min(100%, {maxWidth}); transform: translateX({-$offset * 4}rem);"
43
+ style="
44
+ z-index: {10 + $index};
45
+ max-width: min(100%, {maxWidth});
46
+ transform: translateX({-$offset * 4}rem);
47
+ "
40
48
  class="{klass}
41
49
  fixed bottom-0 right-0 top-0 z-10 flex
42
50
  w-full flex-col overflow-y-scroll bg-base-100
@@ -50,7 +58,7 @@ contextContainer.set("drawer");
50
58
  "
51
59
  >
52
60
  <h2 class="title">{title}</h2>
53
- <button on:click={close} class="btn btn-square btn-sm">
61
+ <button on:click={() => close()} class="btn btn-square btn-sm">
54
62
  <Icon path={mdiClose} title="annuler" />
55
63
  </button>
56
64
  </div>
@@ -3,21 +3,44 @@ declare const __propDef: {
3
3
  props: {
4
4
  title?: string | undefined;
5
5
  /** Key used in url query params */ key: string;
6
- /** Value need to match in url query params*/ value?: string | undefined;
7
6
  class?: string | undefined;
8
7
  maxWidth?: string | undefined;
9
8
  classHeader?: string | undefined;
10
9
  classBody?: string | undefined;
11
- open?: (() => void) | undefined;
12
- close?: (() => void) | undefined;
10
+ open?: ((value?: number, options?: {
11
+ replaceState?: boolean | undefined;
12
+ noScroll?: boolean | undefined;
13
+ keepFocus?: boolean | undefined;
14
+ invalidateAll?: boolean | undefined;
15
+ state?: App.PageState | undefined;
16
+ } | undefined) => Promise<void>) | undefined;
17
+ close?: ((options?: {
18
+ replaceState?: boolean | undefined;
19
+ noScroll?: boolean | undefined;
20
+ keepFocus?: boolean | undefined;
21
+ invalidateAll?: boolean | undefined;
22
+ state?: App.PageState | undefined;
23
+ } | undefined) => Promise<void>) | undefined;
13
24
  };
14
25
  events: {
15
26
  [evt: string]: CustomEvent<any>;
16
27
  };
17
28
  slots: {
18
29
  default: {
19
- open: () => void;
20
- close: () => void;
30
+ open: (value?: number, options?: {
31
+ replaceState?: boolean | undefined;
32
+ noScroll?: boolean | undefined;
33
+ keepFocus?: boolean | undefined;
34
+ invalidateAll?: boolean | undefined;
35
+ state?: App.PageState | undefined;
36
+ } | undefined) => Promise<void>;
37
+ close: (options?: {
38
+ replaceState?: boolean | undefined;
39
+ noScroll?: boolean | undefined;
40
+ keepFocus?: boolean | undefined;
41
+ invalidateAll?: boolean | undefined;
42
+ state?: App.PageState | undefined;
43
+ } | undefined) => Promise<void>;
21
44
  };
22
45
  };
23
46
  };
@@ -25,7 +48,19 @@ export type DrawerProps = typeof __propDef.props;
25
48
  export type DrawerEvents = typeof __propDef.events;
26
49
  export type DrawerSlots = typeof __propDef.slots;
27
50
  export default class Drawer extends SvelteComponent<DrawerProps, DrawerEvents, DrawerSlots> {
28
- get open(): () => void;
29
- get close(): () => void;
51
+ get open(): (value?: number, options?: {
52
+ replaceState?: boolean | undefined;
53
+ noScroll?: boolean | undefined;
54
+ keepFocus?: boolean | undefined;
55
+ invalidateAll?: boolean | undefined;
56
+ state?: App.PageState | undefined;
57
+ } | undefined) => Promise<void>;
58
+ get close(): (options?: {
59
+ replaceState?: boolean | undefined;
60
+ noScroll?: boolean | undefined;
61
+ keepFocus?: boolean | undefined;
62
+ invalidateAll?: boolean | undefined;
63
+ state?: App.PageState | undefined;
64
+ } | undefined) => Promise<void>;
30
65
  }
31
66
  export {};
@@ -1,7 +1,8 @@
1
1
  /// <reference types="svelte" />
2
2
  import { type Readable } from 'svelte/store';
3
- export declare function subscibeDrawerLayers(key: string, value: string): {
3
+ export declare function subscibeDrawerLayers(key: string): {
4
4
  isActive: Readable<boolean>;
5
+ index: Readable<number>;
5
6
  offset: Readable<number>;
6
7
  destroy(): void;
7
8
  };
@@ -1,4 +1,4 @@
1
- import { derived, get, writable } from 'svelte/store';
1
+ import { derived, writable } from 'svelte/store';
2
2
  import { page } from '$app/stores';
3
3
  import { browser } from '$app/environment';
4
4
  const layers = writable([]);
@@ -9,10 +9,10 @@ const layersOffset = derived(layers, ($layers) => {
9
9
  return { ...acc, [layer]: drawerOffset };
10
10
  }, {});
11
11
  });
12
- export function subscibeDrawerLayers(key, value) {
12
+ export function subscibeDrawerLayers(key) {
13
13
  const layerId = Math.random().toString().slice(2, 12);
14
14
  let isInitialized = false;
15
- const isActive = derived(page, ({ url }) => url.searchParams.get(key) === value, false);
15
+ const isActive = derived(page, ({ url }) => url.searchParams.has(key), false);
16
16
  const isActiveUnsubscribe = isActive.subscribe(($isActive) => {
17
17
  if ($isActive)
18
18
  addLayer();
@@ -41,7 +41,8 @@ export function subscibeDrawerLayers(key, value) {
41
41
  }
42
42
  return {
43
43
  isActive,
44
- offset: derived(layersOffset, (drawers) => drawers[layerId]),
44
+ index: derived(layers, ($layers) => $layers.indexOf(layerId)),
45
+ offset: derived(layersOffset, (offsets) => offsets[layerId]),
45
46
  destroy() {
46
47
  removeLayer();
47
48
  isActiveUnsubscribe();
@@ -2,7 +2,7 @@
2
2
 
3
3
  <script
4
4
 
5
- generics="Shape extends z.ZodRawShape, ReturnData extends Record<string, unknown> = FormData<Shape>"
5
+ generics="Shape extends z.ZodRawShape, Data extends FormDataInput<Shape> = FormDataInput<Shape>"
6
6
  >import { createEventDispatcher, onMount } from "svelte";
7
7
  import { fade } from "svelte/transition";
8
8
  import { page } from "$app/stores";
@@ -12,8 +12,9 @@ import {
12
12
  getFieldType,
13
13
  useHandleInput
14
14
  } from "./form.js";
15
+ import ButtonDelete from "../button/ButtonDelete.svelte";
15
16
  import { useForm } from "../../validation/form.js";
16
- import Input from "./FormInput.svelte";
17
+ import FormInput from "./FormInput.svelte";
17
18
  import FormSection from "./FormSection.svelte";
18
19
  let klass = "";
19
20
  export { klass as class };
@@ -22,32 +23,50 @@ export let classAction = "";
22
23
  export let model = void 0;
23
24
  export let fields = [];
24
25
  export let sections = [{}];
25
- export let data = initData(fields);
26
26
  export let action = "";
27
- export let actionDelete = "";
28
- export let actionPrefix = "";
27
+ export let actionCreate = "_create";
28
+ export let actionDelete = "_delete";
29
+ export let actionUpdate = "_update";
29
30
  export let options = {};
31
+ let dataInput = initData(fields);
32
+ export { dataInput as data };
33
+ let data = dataInput;
34
+ $:
35
+ $isDirty ? dataInput = data : data = dataInput;
30
36
  export function set(key, value) {
37
+ isDirty.set(true);
31
38
  data[key] = value;
32
39
  }
40
+ export function update(updater) {
41
+ isDirty.set(true);
42
+ data = updater(data);
43
+ }
33
44
  const dispatch = createEventDispatcher();
34
45
  const { enhance, setError } = useForm({
35
46
  ...options,
36
- onSuccess(action2, data2) {
37
- dispatch("success", { action: action2, data: data2 });
47
+ async onSuccess(url, data2) {
38
48
  if (options.onSuccess)
39
- options.onSuccess(action2, data2);
49
+ await options.onSuccess(url, data2);
50
+ dispatch("success", { action: url, data: data2 });
51
+ const actionPath = url.pathname + url.search;
52
+ if (actionPath.includes(action + actionDelete))
53
+ dispatch("deleted");
54
+ if (!data2)
55
+ return;
56
+ if (actionPath.includes(action + actionCreate))
57
+ dispatch("created", data2);
58
+ if (actionPath.includes(action + actionUpdate))
59
+ dispatch("updated", data2);
40
60
  }
41
61
  });
42
- const handleInput = model ? useHandleInput(model, { setError }) : () => {
43
- };
62
+ const { handleInput, isDirty } = useHandleInput({ model, setError });
44
63
  onMount(lookupValueFromParams);
45
64
  function lookupValueFromParams() {
46
65
  fields.flat().forEach(({ key }) => {
47
66
  if (data[key])
48
67
  return;
49
68
  const value = $page.url.searchParams.get(key);
50
- if (value)
69
+ if (value && key in data)
51
70
  data[key] = value;
52
71
  });
53
72
  }
@@ -65,12 +84,14 @@ const getBoolean = (bool) => (_data) => typeof bool === "boolean" || bool === vo
65
84
 
66
85
  <form
67
86
  method="post"
68
- action="{actionPrefix}{action}"
87
+ action="{action}{data.id ? actionUpdate : actionCreate}"
69
88
  enctype="multipart/form-data"
70
89
  class="{klass} flex flex-col gap-4"
71
90
  use:enhance
72
91
  on:input={handleInput}
73
92
  >
93
+ <slot />
94
+
74
95
  {#if data.id}
75
96
  <input type="hidden" name="id" value={data.id} />
76
97
  {/if}
@@ -88,7 +109,7 @@ const getBoolean = (bool) => (_data) => typeof bool === "boolean" || bool === vo
88
109
  style={`grid-column: span ${field.colSpan || 2};`}
89
110
  in:fade|local={{ duration: 200 }}
90
111
  >
91
- <Input
112
+ <FormInput
92
113
  key={field.key}
93
114
  type={inputType}
94
115
  bind:value={data[field.key]}
@@ -106,19 +127,13 @@ const getBoolean = (bool) => (_data) => typeof bool === "boolean" || bool === vo
106
127
  <div
107
128
  class="
108
129
  {classAction} {actionPadding}
109
- sticky bottom-0 col-span-full mt-2 flex gap-2 border-t py-4 backdrop-blur-sm
130
+ sticky bottom-0 col-span-full mt-2 flex flex-row-reverse gap-2 border-t py-4 backdrop-blur-sm
110
131
  "
111
132
  >
112
- {#if actionDelete}
113
- <button
114
- class="btn-ghos btn text-error"
115
- type="button"
116
- formaction="{actionPrefix}{actionDelete}"
117
- >
118
- Supprimer
119
- </button>
120
- {/if}
121
- <div class="grow" />
122
133
  <button class="btn btn-primary"> Valider </button>
134
+ <div class="grow" />
135
+ {#if data.id && actionDelete}
136
+ <ButtonDelete formaction="{action}{actionDelete}">Supprimer</ButtonDelete>
137
+ {/if}
123
138
  </div>
124
139
  </form>