@ainsleydev/sveltekit-helper 0.1.0 → 0.1.1
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.
- package/dist/components/Grid/Column.svelte +29 -0
- package/dist/components/Grid/Column.svelte.d.ts +52 -0
- package/dist/components/Grid/Column.svelte.d.ts.map +1 -0
- package/dist/components/Grid/Container.svelte +48 -0
- package/dist/components/Grid/Container.svelte.d.ts +55 -0
- package/dist/components/Grid/Container.svelte.d.ts.map +1 -0
- package/dist/components/Grid/Row.svelte +45 -0
- package/dist/components/Grid/Row.svelte.d.ts +47 -0
- package/dist/components/Grid/Row.svelte.d.ts.map +1 -0
- package/dist/components/Grid/index.js +0 -2
- package/dist/components/payload/PayloadForm.svelte +303 -0
- package/dist/components/payload/PayloadForm.svelte.d.ts +27 -0
- package/dist/components/payload/PayloadForm.svelte.d.ts.map +1 -0
- package/dist/components/payload/PayloadMedia.svelte +136 -0
- package/dist/components/payload/PayloadMedia.svelte.d.ts +26 -0
- package/dist/components/payload/PayloadMedia.svelte.d.ts.map +1 -0
- package/dist/components/payload/index.js +0 -2
- package/dist/components/payload/types.js +1 -3
- package/dist/index.js +2 -3
- package/dist/utils/forms/helpers.js +3 -4
- package/dist/utils/forms/index.js +0 -2
- package/dist/utils/forms/schema.js +22 -19
- package/dist/utils/forms/schema.test.js +27 -41
- package/dist/utils/strings/index.js +3 -4
- package/dist/utils/strings/index.test.js +6 -8
- package/package.json +9 -9
- package/dist/components/Grid/index.js.map +0 -1
- package/dist/components/payload/index.js.map +0 -1
- package/dist/components/payload/types.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/utils/forms/helpers.js.map +0 -1
- package/dist/utils/forms/index.js.map +0 -1
- package/dist/utils/forms/schema.js.map +0 -1
- package/dist/utils/forms/schema.test.js.map +0 -1
- package/dist/utils/strings/index.js.map +0 -1
- package/dist/utils/strings/index.test.js.map +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
|
|
4
|
+
Responsive column component.
|
|
5
|
+
Provides base column structure with customisable gap.
|
|
6
|
+
Consumers should define their own grid classes (col-12, col-tab-6, etc.) in their global styles.
|
|
7
|
+
|
|
8
|
+
@example
|
|
9
|
+
```svelte
|
|
10
|
+
<Column class="col-12 col-tab-6 col-desk-4">
|
|
11
|
+
Content
|
|
12
|
+
</Column>
|
|
13
|
+
```
|
|
14
|
+
-->
|
|
15
|
+
<div class="col" {...$$restProps}>
|
|
16
|
+
<slot />
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<style>.col {
|
|
20
|
+
--col-gap: 1rem;
|
|
21
|
+
position: relative;
|
|
22
|
+
width: 100%;
|
|
23
|
+
padding-inline: var(--col-gap);
|
|
24
|
+
}
|
|
25
|
+
@media (max-width: 568px) {
|
|
26
|
+
.col {
|
|
27
|
+
--col-gap: 0.5rem;
|
|
28
|
+
}
|
|
29
|
+
}</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export default Column;
|
|
2
|
+
type Column = SvelteComponent<$$__sveltets_2_PropsWithChildren<{
|
|
3
|
+
[x: string]: any;
|
|
4
|
+
}, {
|
|
5
|
+
default: {};
|
|
6
|
+
}>, {
|
|
7
|
+
[evt: string]: CustomEvent<any>;
|
|
8
|
+
}, {
|
|
9
|
+
default: {};
|
|
10
|
+
}> & {
|
|
11
|
+
$$bindings?: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Responsive column component.
|
|
15
|
+
* Provides base column structure with customisable gap.
|
|
16
|
+
* Consumers should define their own grid classes (col-12, col-tab-6, etc.) in their global styles.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```svelte
|
|
20
|
+
* <Column class="col-12 col-tab-6 col-desk-4">
|
|
21
|
+
* Content
|
|
22
|
+
* </Column>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare const Column: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
|
|
26
|
+
[x: string]: any;
|
|
27
|
+
}, {
|
|
28
|
+
default: {};
|
|
29
|
+
}>, {
|
|
30
|
+
[evt: string]: CustomEvent<any>;
|
|
31
|
+
}, {
|
|
32
|
+
default: {};
|
|
33
|
+
}, {}, string>;
|
|
34
|
+
type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
|
|
35
|
+
default: any;
|
|
36
|
+
} ? Props extends Record<string, never> ? any : {
|
|
37
|
+
children?: any;
|
|
38
|
+
} : {});
|
|
39
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
40
|
+
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
41
|
+
$$bindings?: Bindings;
|
|
42
|
+
} & Exports;
|
|
43
|
+
(internal: unknown, props: Props & {
|
|
44
|
+
$$events?: Events;
|
|
45
|
+
$$slots?: Slots;
|
|
46
|
+
}): Exports & {
|
|
47
|
+
$set?: any;
|
|
48
|
+
$on?: any;
|
|
49
|
+
};
|
|
50
|
+
z_$$bindings?: Bindings;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=Column.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Column.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/Grid/Column.svelte.js"],"names":[],"mappings":";;;;;;;;;;;;AAyBA;;;;;;;;;;;GAWG;AACH;;;;;;;;eAAiI;sCArB3F,KAAK,EAAE,KAAK;;;;;6CALL,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,OAAO,OAAO,QAAQ;IAC3L,cAAc,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,WAAW,OAAO,SAAS,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
|
|
4
|
+
Centre content such as rows & columns horizontally with predefined max-width.
|
|
5
|
+
Uses CSS Grid to provide breakout and full-width layout options.
|
|
6
|
+
|
|
7
|
+
@example
|
|
8
|
+
```svelte
|
|
9
|
+
<Container>
|
|
10
|
+
<Row></Row>
|
|
11
|
+
</Container>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
@example
|
|
15
|
+
```svelte
|
|
16
|
+
<!-- Custom max width via CSS variable -->
|
|
17
|
+
<Container style="--container-max-width: 1400px">
|
|
18
|
+
<Row></Row>
|
|
19
|
+
</Container>
|
|
20
|
+
```
|
|
21
|
+
-->
|
|
22
|
+
<div class="container" {...$$restProps}>
|
|
23
|
+
<slot />
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<style>.container {
|
|
27
|
+
--container-padding: 1rem;
|
|
28
|
+
--container-max-width: 1328px;
|
|
29
|
+
--container-breakout-max-width: 1500px;
|
|
30
|
+
--container-breakout-size: calc(
|
|
31
|
+
(var(--container-breakout-max-width) - var(--container-max-width)) / 2
|
|
32
|
+
);
|
|
33
|
+
display: grid;
|
|
34
|
+
width: 100%;
|
|
35
|
+
position: relative;
|
|
36
|
+
grid-template-columns: [full-width-start] minmax(var(--container-padding), 1fr) [breakout-start] minmax(0, var(--container-breakout-size)) [content-start] min(100% - var(--container-padding) * 2, var(--container-max-width)) [content-end] minmax(0, var(--container-breakout-size)) [breakout-end] minmax(var(--container-padding), 1fr) [full-width-end];
|
|
37
|
+
}
|
|
38
|
+
.container :global(> *) {
|
|
39
|
+
grid-column: content;
|
|
40
|
+
}
|
|
41
|
+
.container :global(> .breakout) {
|
|
42
|
+
grid-column: breakout;
|
|
43
|
+
}
|
|
44
|
+
.container :global(> .full-width) {
|
|
45
|
+
display: grid;
|
|
46
|
+
grid-column: full-width;
|
|
47
|
+
grid-template-columns: inherit;
|
|
48
|
+
}</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export default Container;
|
|
2
|
+
type Container = SvelteComponent<$$__sveltets_2_PropsWithChildren<{
|
|
3
|
+
[x: string]: any;
|
|
4
|
+
}, {
|
|
5
|
+
default: {};
|
|
6
|
+
}>, {
|
|
7
|
+
[evt: string]: CustomEvent<any>;
|
|
8
|
+
}, {
|
|
9
|
+
default: {};
|
|
10
|
+
}> & {
|
|
11
|
+
$$bindings?: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Centre content such as rows & columns horizontally with predefined max-width.
|
|
15
|
+
* Uses CSS Grid to provide breakout and full-width layout options.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <Container>
|
|
20
|
+
* <Row></Row>
|
|
21
|
+
* </Container>
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```svelte
|
|
26
|
+
* <!-- Custom max width via CSS variable
|
|
27
|
+
*/
|
|
28
|
+
declare const Container: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
|
|
29
|
+
[x: string]: any;
|
|
30
|
+
}, {
|
|
31
|
+
default: {};
|
|
32
|
+
}>, {
|
|
33
|
+
[evt: string]: CustomEvent<any>;
|
|
34
|
+
}, {
|
|
35
|
+
default: {};
|
|
36
|
+
}, {}, string>;
|
|
37
|
+
type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
|
|
38
|
+
default: any;
|
|
39
|
+
} ? Props extends Record<string, never> ? any : {
|
|
40
|
+
children?: any;
|
|
41
|
+
} : {});
|
|
42
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
43
|
+
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
44
|
+
$$bindings?: Bindings;
|
|
45
|
+
} & Exports;
|
|
46
|
+
(internal: unknown, props: Props & {
|
|
47
|
+
$$events?: Events;
|
|
48
|
+
$$slots?: Slots;
|
|
49
|
+
}): Exports & {
|
|
50
|
+
$set?: any;
|
|
51
|
+
$on?: any;
|
|
52
|
+
};
|
|
53
|
+
z_$$bindings?: Bindings;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=Container.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Container.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/Grid/Container.svelte.js"],"names":[],"mappings":";;;;;;;;;;;;AA8BA;;;;;;;;;;;;;;GAcG;AACH;;;;;;;;eAAoI;sCAxB9F,KAAK,EAAE,KAAK;;;;;6CALL,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,OAAO,OAAO,QAAQ;IAC3L,cAAc,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,WAAW,OAAO,SAAS,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let noGaps = false;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<!--
|
|
6
|
+
@component
|
|
7
|
+
|
|
8
|
+
Flexbox row container for columns with gap management.
|
|
9
|
+
|
|
10
|
+
@example
|
|
11
|
+
```svelte
|
|
12
|
+
<Row>
|
|
13
|
+
<Column></Column>
|
|
14
|
+
</Row>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
@example
|
|
18
|
+
```svelte
|
|
19
|
+
<Row noGaps>
|
|
20
|
+
<Column></Column>
|
|
21
|
+
</Row>
|
|
22
|
+
```
|
|
23
|
+
-->
|
|
24
|
+
<div class="row" class:row--no-gaps={noGaps} {...$$restProps}>
|
|
25
|
+
<slot />
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<style>.row {
|
|
29
|
+
--row-gap: 1rem;
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-wrap: wrap;
|
|
32
|
+
margin-inline: calc(var(--row-gap) * -1);
|
|
33
|
+
}
|
|
34
|
+
.row--no-gaps {
|
|
35
|
+
margin-inline: 0;
|
|
36
|
+
}
|
|
37
|
+
.row--no-gaps :global(.col),
|
|
38
|
+
.row--no-gaps :global([class*="col-"]) {
|
|
39
|
+
padding-inline: 0;
|
|
40
|
+
}
|
|
41
|
+
@media (max-width: 568px) {
|
|
42
|
+
.row {
|
|
43
|
+
--row-gap: 0.5rem;
|
|
44
|
+
}
|
|
45
|
+
}</style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: Props & {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
|
|
15
|
+
default: any;
|
|
16
|
+
} ? Props extends Record<string, never> ? any : {
|
|
17
|
+
children?: any;
|
|
18
|
+
} : {});
|
|
19
|
+
/**
|
|
20
|
+
* Flexbox row container for columns with gap management.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```svelte
|
|
24
|
+
* <Row>
|
|
25
|
+
* <Column></Column>
|
|
26
|
+
* </Row>
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```svelte
|
|
31
|
+
* <Row noGaps>
|
|
32
|
+
* <Column></Column>
|
|
33
|
+
* </Row>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare const Row: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
|
|
37
|
+
[x: string]: any;
|
|
38
|
+
}, {
|
|
39
|
+
default: {};
|
|
40
|
+
}>, {
|
|
41
|
+
[evt: string]: CustomEvent<any>;
|
|
42
|
+
}, {
|
|
43
|
+
default: {};
|
|
44
|
+
}, {}, string>;
|
|
45
|
+
type Row = InstanceType<typeof Row>;
|
|
46
|
+
export default Row;
|
|
47
|
+
//# sourceMappingURL=Row.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Row.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/Grid/Row.svelte.ts"],"names":[],"mappings":"AAgBA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AACD,KAAK,gCAAgC,CAAC,KAAK,EAAE,KAAK,IAAI,KAAK,GACvD,CAAC,KAAK,SAAS;IAAE,OAAO,EAAE,GAAG,CAAA;CAAE,GACzB,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACnC,GAAG,GACH;IAAE,QAAQ,CAAC,EAAE,GAAG,CAAA;CAAE,GAClB,EAAE,CAAC,CAAC;AAId;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,GAAG;;;;;;;;cAA4G,CAAC;AACpG,KAAK,GAAG,GAAG,YAAY,CAAC,OAAO,GAAG,CAAC,CAAC;AACtC,eAAe,GAAG,CAAC"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { PayloadFormField } from '../../utils/forms/schema';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Payload form configuration
|
|
6
|
+
*/
|
|
7
|
+
export let form: {
|
|
8
|
+
id: number | string;
|
|
9
|
+
fields?: PayloadFormField[] | null;
|
|
10
|
+
confirmationMessage?: string | null;
|
|
11
|
+
submitButtonLabel?: string | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* API endpoint to submit form data to (defaults to '/api/forms')
|
|
16
|
+
*/
|
|
17
|
+
export let apiEndpoint = '/api/forms';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Custom submit handler (overrides default API submission)
|
|
21
|
+
*/
|
|
22
|
+
export let onSubmit: ((data: Record<string, unknown>) => Promise<void>) | undefined = undefined;
|
|
23
|
+
|
|
24
|
+
let formData = $state<Record<string, unknown>>({});
|
|
25
|
+
let errors: Record<string, string> = $state({});
|
|
26
|
+
let success = $state(false);
|
|
27
|
+
let error: string | null = $state(null);
|
|
28
|
+
let submitting = $state(false);
|
|
29
|
+
|
|
30
|
+
const typeMap = { text: 'text', email: 'email', number: 'number' };
|
|
31
|
+
|
|
32
|
+
async function handleSubmit(event: SubmitEvent) {
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
errors = {};
|
|
35
|
+
error = null;
|
|
36
|
+
submitting = true;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
if (onSubmit) {
|
|
40
|
+
await onSubmit(formData);
|
|
41
|
+
} else {
|
|
42
|
+
const response = await fetch(apiEndpoint, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify({
|
|
48
|
+
formID: form.id,
|
|
49
|
+
data: formData,
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const result = await response.json();
|
|
54
|
+
if (!result.success) {
|
|
55
|
+
error = result.error || 'Failed to submit form';
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
success = true;
|
|
61
|
+
} catch (err) {
|
|
62
|
+
error = err instanceof Error ? err.message : 'Failed to submit form';
|
|
63
|
+
success = false;
|
|
64
|
+
} finally {
|
|
65
|
+
submitting = false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<!--
|
|
71
|
+
@component
|
|
72
|
+
|
|
73
|
+
PayloadForm renders a form dynamically from Payload CMS form builder fields.
|
|
74
|
+
Uses basic HTML elements that can be styled via CSS variables or custom classes.
|
|
75
|
+
|
|
76
|
+
@example
|
|
77
|
+
```svelte
|
|
78
|
+
<PayloadForm
|
|
79
|
+
form={payloadFormData}
|
|
80
|
+
apiEndpoint="/api/forms"
|
|
81
|
+
/>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
@example
|
|
85
|
+
```svelte
|
|
86
|
+
<PayloadForm
|
|
87
|
+
form={payloadFormData}
|
|
88
|
+
onSubmit={async (data) => {
|
|
89
|
+
// Custom submission logic
|
|
90
|
+
await customAPI.submit(data)
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
```
|
|
94
|
+
-->
|
|
95
|
+
<form method="POST" onsubmit={handleSubmit} class="payload-form">
|
|
96
|
+
{#if form.fields?.length}
|
|
97
|
+
{#each form.fields as field, index (index)}
|
|
98
|
+
{@const required = field.required ?? false}
|
|
99
|
+
{@const name = field.name}
|
|
100
|
+
<div class="payload-form__group" class:payload-form__group--error={errors[name]}>
|
|
101
|
+
{#if field.blockType === 'text' || field.blockType === 'email' || field.blockType === 'number'}
|
|
102
|
+
<label for={field.id || name} class="payload-form__label">
|
|
103
|
+
{field.label}
|
|
104
|
+
{#if required}
|
|
105
|
+
<span class="payload-form__required">*</span>
|
|
106
|
+
{/if}
|
|
107
|
+
</label>
|
|
108
|
+
<input
|
|
109
|
+
id={field.id || name}
|
|
110
|
+
{name}
|
|
111
|
+
type={typeMap[field.blockType]}
|
|
112
|
+
placeholder={field.placeholder ?? ''}
|
|
113
|
+
bind:value={formData[name]}
|
|
114
|
+
disabled={success}
|
|
115
|
+
class="payload-form__input"
|
|
116
|
+
class:payload-form__input--error={errors[name]}
|
|
117
|
+
/>
|
|
118
|
+
{:else if field.blockType === 'textarea'}
|
|
119
|
+
<label for={field.id || name} class="payload-form__label">
|
|
120
|
+
{field.label}
|
|
121
|
+
{#if required}
|
|
122
|
+
<span class="payload-form__required">*</span>
|
|
123
|
+
{/if}
|
|
124
|
+
</label>
|
|
125
|
+
<textarea
|
|
126
|
+
id={field.id || name}
|
|
127
|
+
{name}
|
|
128
|
+
placeholder={field.placeholder ?? ''}
|
|
129
|
+
rows={8}
|
|
130
|
+
bind:value={formData[name]}
|
|
131
|
+
disabled={success}
|
|
132
|
+
class="payload-form__textarea"
|
|
133
|
+
class:payload-form__textarea--error={errors[name]}
|
|
134
|
+
/>
|
|
135
|
+
{:else if field.blockType === 'checkbox'}
|
|
136
|
+
<div class="payload-form__checkbox-wrapper">
|
|
137
|
+
<input
|
|
138
|
+
id={field.id || name}
|
|
139
|
+
type="checkbox"
|
|
140
|
+
{name}
|
|
141
|
+
bind:checked={formData[name]}
|
|
142
|
+
disabled={success}
|
|
143
|
+
class="payload-form__checkbox"
|
|
144
|
+
class:payload-form__checkbox--error={errors[name]}
|
|
145
|
+
/>
|
|
146
|
+
<label for={field.id || name} class="payload-form__label payload-form__label--checkbox">
|
|
147
|
+
{field.label}
|
|
148
|
+
{#if required}
|
|
149
|
+
<span class="payload-form__required">*</span>
|
|
150
|
+
{/if}
|
|
151
|
+
</label>
|
|
152
|
+
</div>
|
|
153
|
+
{/if}
|
|
154
|
+
|
|
155
|
+
{#if errors[name]}
|
|
156
|
+
<span class="payload-form__error">{errors[name]}</span>
|
|
157
|
+
{/if}
|
|
158
|
+
</div>
|
|
159
|
+
{/each}
|
|
160
|
+
{/if}
|
|
161
|
+
|
|
162
|
+
{#if success}
|
|
163
|
+
<div class="payload-form__success">
|
|
164
|
+
{form.confirmationMessage ?? 'Form submitted successfully!'}
|
|
165
|
+
</div>
|
|
166
|
+
{/if}
|
|
167
|
+
|
|
168
|
+
{#if error}
|
|
169
|
+
<div class="payload-form__alert">
|
|
170
|
+
{error}
|
|
171
|
+
</div>
|
|
172
|
+
{/if}
|
|
173
|
+
|
|
174
|
+
<button type="submit" disabled={success || submitting} class="payload-form__submit">
|
|
175
|
+
{#if submitting}
|
|
176
|
+
<span class="payload-form__loader"></span>
|
|
177
|
+
{/if}
|
|
178
|
+
{form.submitButtonLabel ?? 'Send'}
|
|
179
|
+
</button>
|
|
180
|
+
</form>
|
|
181
|
+
|
|
182
|
+
<style>.payload-form {
|
|
183
|
+
--form-gap: 1rem;
|
|
184
|
+
--form-input-padding: 0.75rem;
|
|
185
|
+
--form-input-border: 1px solid #d1d5db;
|
|
186
|
+
--form-input-border-radius: 0.375rem;
|
|
187
|
+
--form-input-bg: #ffffff;
|
|
188
|
+
--form-input-text: #111827;
|
|
189
|
+
--form-error-color: #ef4444;
|
|
190
|
+
--form-error-bg: #fee2e2;
|
|
191
|
+
--form-success-color: #10b981;
|
|
192
|
+
--form-success-bg: #d1fae5;
|
|
193
|
+
--form-button-bg: #3b82f6;
|
|
194
|
+
--form-button-text: #ffffff;
|
|
195
|
+
--form-button-hover-bg: #2563eb;
|
|
196
|
+
--form-button-disabled-bg: #9ca3af;
|
|
197
|
+
display: flex;
|
|
198
|
+
flex-direction: column;
|
|
199
|
+
gap: var(--form-gap);
|
|
200
|
+
}
|
|
201
|
+
.payload-form__group {
|
|
202
|
+
display: flex;
|
|
203
|
+
flex-direction: column;
|
|
204
|
+
gap: 0.5rem;
|
|
205
|
+
}
|
|
206
|
+
.payload-form__label {
|
|
207
|
+
font-size: 0.875rem;
|
|
208
|
+
font-weight: 500;
|
|
209
|
+
color: var(--form-input-text);
|
|
210
|
+
}
|
|
211
|
+
.payload-form__label--checkbox {
|
|
212
|
+
font-weight: 400;
|
|
213
|
+
cursor: pointer;
|
|
214
|
+
}
|
|
215
|
+
.payload-form__required {
|
|
216
|
+
color: var(--form-error-color);
|
|
217
|
+
margin-left: 0.25rem;
|
|
218
|
+
}
|
|
219
|
+
.payload-form__input, .payload-form__textarea {
|
|
220
|
+
padding: var(--form-input-padding);
|
|
221
|
+
border: var(--form-input-border);
|
|
222
|
+
border-radius: var(--form-input-border-radius);
|
|
223
|
+
background: var(--form-input-bg);
|
|
224
|
+
color: var(--form-input-text);
|
|
225
|
+
font-size: 1rem;
|
|
226
|
+
font-family: inherit;
|
|
227
|
+
}
|
|
228
|
+
.payload-form__input:focus, .payload-form__textarea:focus {
|
|
229
|
+
outline: 2px solid var(--form-button-bg);
|
|
230
|
+
outline-offset: 2px;
|
|
231
|
+
}
|
|
232
|
+
.payload-form__input--error, .payload-form__textarea--error {
|
|
233
|
+
border-color: var(--form-error-color);
|
|
234
|
+
}
|
|
235
|
+
.payload-form__textarea {
|
|
236
|
+
resize: vertical;
|
|
237
|
+
min-height: 8rem;
|
|
238
|
+
}
|
|
239
|
+
.payload-form__checkbox-wrapper {
|
|
240
|
+
display: flex;
|
|
241
|
+
align-items: flex-start;
|
|
242
|
+
gap: 0.5rem;
|
|
243
|
+
}
|
|
244
|
+
.payload-form__checkbox {
|
|
245
|
+
margin-top: 0.25rem;
|
|
246
|
+
width: 1rem;
|
|
247
|
+
height: 1rem;
|
|
248
|
+
cursor: pointer;
|
|
249
|
+
}
|
|
250
|
+
.payload-form__error {
|
|
251
|
+
font-size: 0.875rem;
|
|
252
|
+
color: var(--form-error-color);
|
|
253
|
+
}
|
|
254
|
+
.payload-form__success {
|
|
255
|
+
padding: 1rem;
|
|
256
|
+
background-color: var(--form-success-bg);
|
|
257
|
+
color: var(--form-success-color);
|
|
258
|
+
border-radius: var(--form-input-border-radius);
|
|
259
|
+
font-weight: 500;
|
|
260
|
+
}
|
|
261
|
+
.payload-form__alert {
|
|
262
|
+
padding: 1rem;
|
|
263
|
+
background-color: var(--form-error-bg);
|
|
264
|
+
color: var(--form-error-color);
|
|
265
|
+
border-radius: var(--form-input-border-radius);
|
|
266
|
+
font-weight: 500;
|
|
267
|
+
}
|
|
268
|
+
.payload-form__submit {
|
|
269
|
+
padding: var(--form-input-padding) 1.5rem;
|
|
270
|
+
background-color: var(--form-button-bg);
|
|
271
|
+
color: var(--form-button-text);
|
|
272
|
+
border: none;
|
|
273
|
+
border-radius: var(--form-input-border-radius);
|
|
274
|
+
font-size: 1rem;
|
|
275
|
+
font-weight: 500;
|
|
276
|
+
cursor: pointer;
|
|
277
|
+
display: flex;
|
|
278
|
+
align-items: center;
|
|
279
|
+
justify-content: center;
|
|
280
|
+
gap: 0.5rem;
|
|
281
|
+
transition: background-color 0.2s;
|
|
282
|
+
}
|
|
283
|
+
.payload-form__submit:hover:not(:disabled) {
|
|
284
|
+
background-color: var(--form-button-hover-bg);
|
|
285
|
+
}
|
|
286
|
+
.payload-form__submit:disabled {
|
|
287
|
+
background-color: var(--form-button-disabled-bg);
|
|
288
|
+
cursor: not-allowed;
|
|
289
|
+
}
|
|
290
|
+
.payload-form__loader {
|
|
291
|
+
width: 1rem;
|
|
292
|
+
height: 1rem;
|
|
293
|
+
border: 2px solid currentColor;
|
|
294
|
+
border-top-color: transparent;
|
|
295
|
+
border-radius: 50%;
|
|
296
|
+
animation: spin 0.6s linear infinite;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@keyframes spin {
|
|
300
|
+
to {
|
|
301
|
+
transform: rotate(360deg);
|
|
302
|
+
}
|
|
303
|
+
}</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PayloadForm renders a form dynamically from Payload CMS form builder fields.
|
|
3
|
+
* Uses basic HTML elements that can be styled via CSS variables or custom classes.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```svelte
|
|
7
|
+
* <PayloadForm
|
|
8
|
+
* form={payloadFormData}
|
|
9
|
+
* apiEndpoint="/api/forms"
|
|
10
|
+
* />
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```svelte
|
|
15
|
+
* <PayloadForm
|
|
16
|
+
* form={payloadFormData}
|
|
17
|
+
* onSubmit={async (data) => {
|
|
18
|
+
* // Custom submission logic
|
|
19
|
+
* await customAPI.submit(data)
|
|
20
|
+
* }}
|
|
21
|
+
* />
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare const PayloadForm: import("svelte").Component<Record<string, never>, {}, "">;
|
|
25
|
+
type PayloadForm = ReturnType<typeof PayloadForm>;
|
|
26
|
+
export default PayloadForm;
|
|
27
|
+
//# sourceMappingURL=PayloadForm.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PayloadForm.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/payload/PayloadForm.svelte.ts"],"names":[],"mappings":"AA4IA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,WAAW,2DAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Media, MediaSizes, PayloadMediaProps } from './types.js';
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
data,
|
|
6
|
+
loading = undefined,
|
|
7
|
+
className = '',
|
|
8
|
+
breakpointBuffer = 50,
|
|
9
|
+
maxWidth = undefined,
|
|
10
|
+
onload = () => null,
|
|
11
|
+
...restProps
|
|
12
|
+
}: PayloadMediaProps = $props();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Returns sources grouped by breakpoint/width, then sorted by mime priority within each group:
|
|
16
|
+
* For each width: avif → webp → jpeg/png → others
|
|
17
|
+
*/
|
|
18
|
+
const getSources = (sizesMap?: MediaSizes) => {
|
|
19
|
+
if (!sizesMap) return [];
|
|
20
|
+
|
|
21
|
+
const arr = Object.values(sizesMap)
|
|
22
|
+
.filter((s) => s?.url && s?.width)
|
|
23
|
+
.map((s) => ({
|
|
24
|
+
url: s.url as string,
|
|
25
|
+
width: s.width as number,
|
|
26
|
+
mimeType: s.mimeType ?? '',
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
const filtered = maxWidth ? arr.filter((s) => s.width <= maxWidth) : arr;
|
|
30
|
+
|
|
31
|
+
const mimePriority = (mime: string, url: string) => {
|
|
32
|
+
if (/avif/i.test(mime) || /\.avif$/i.test(url)) return 1;
|
|
33
|
+
if (/webp/i.test(mime) || /\.webp$/i.test(url)) return 2;
|
|
34
|
+
if (/jpe?g|png/i.test(mime) || /\.(jpe?g|png)$/i.test(url)) return 3;
|
|
35
|
+
return 4;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return filtered.sort((a, b) => {
|
|
39
|
+
if (a.width !== b.width) return a.width - b.width;
|
|
40
|
+
return mimePriority(a.mimeType, a.url) - mimePriority(b.mimeType, b.url);
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const isImage = $derived((data?.mimeType ?? '').startsWith('image'));
|
|
45
|
+
const isVideo = $derived((data?.mimeType ?? '').startsWith('video'));
|
|
46
|
+
const isSVG = $derived(!!(data?.filename && /\.svg$/i.test(data.filename)));
|
|
47
|
+
|
|
48
|
+
const sources = $derived(isImage && !isSVG ? getSources(data.sizes) : []);
|
|
49
|
+
const fallbackSource = $derived(sources[sources.length - 1]);
|
|
50
|
+
const imgAlt = $derived(data?.alt ?? '');
|
|
51
|
+
const imgLoading = $derived(loading ?? undefined);
|
|
52
|
+
const imgWidth = $derived(fallbackSource?.width ?? data?.width ?? undefined);
|
|
53
|
+
const imgHeight = $derived(
|
|
54
|
+
fallbackSource && data?.width && data?.height
|
|
55
|
+
? Math.round((fallbackSource.width / data.width) * data.height)
|
|
56
|
+
: data?.height ?? undefined,
|
|
57
|
+
);
|
|
58
|
+
const fallbackUrl = $derived(sources[sources.length - 1]?.url ?? data?.url ?? '');
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<!--
|
|
62
|
+
@component
|
|
63
|
+
|
|
64
|
+
PayloadMedia renders responsive images and videos from Payload CMS media fields.
|
|
65
|
+
Handles multiple image sizes with automatic AVIF/WebP format prioritisation.
|
|
66
|
+
|
|
67
|
+
@example
|
|
68
|
+
```svelte
|
|
69
|
+
<PayloadMedia
|
|
70
|
+
data={media}
|
|
71
|
+
loading="lazy"
|
|
72
|
+
maxWidth={1200}
|
|
73
|
+
/>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
@example
|
|
77
|
+
```svelte
|
|
78
|
+
<PayloadMedia
|
|
79
|
+
data={videoMedia}
|
|
80
|
+
className="custom-video"
|
|
81
|
+
/>
|
|
82
|
+
```
|
|
83
|
+
-->
|
|
84
|
+
{#if isImage}
|
|
85
|
+
{#if isSVG}
|
|
86
|
+
<img
|
|
87
|
+
src={data.url}
|
|
88
|
+
alt={imgAlt}
|
|
89
|
+
width={imgWidth}
|
|
90
|
+
height={imgHeight}
|
|
91
|
+
loading={imgLoading}
|
|
92
|
+
class={className}
|
|
93
|
+
{onload}
|
|
94
|
+
{...restProps}
|
|
95
|
+
/>
|
|
96
|
+
{:else}
|
|
97
|
+
<picture class={className} {...restProps}>
|
|
98
|
+
{#each sources as source, index (index)}
|
|
99
|
+
<source
|
|
100
|
+
media={`(max-width: ${source.width + breakpointBuffer}px)`}
|
|
101
|
+
srcset={source.url}
|
|
102
|
+
type={source.mimeType}
|
|
103
|
+
/>
|
|
104
|
+
{/each}
|
|
105
|
+
<img
|
|
106
|
+
src={fallbackUrl}
|
|
107
|
+
alt={imgAlt}
|
|
108
|
+
width={imgWidth}
|
|
109
|
+
height={imgHeight}
|
|
110
|
+
loading={imgLoading}
|
|
111
|
+
{onload}
|
|
112
|
+
/>
|
|
113
|
+
</picture>
|
|
114
|
+
{/if}
|
|
115
|
+
{:else if isVideo}
|
|
116
|
+
<video
|
|
117
|
+
controls
|
|
118
|
+
width={imgWidth}
|
|
119
|
+
height={imgHeight}
|
|
120
|
+
preload={loading === 'lazy' ? 'metadata' : 'auto'}
|
|
121
|
+
poster={data.thumbnailURL}
|
|
122
|
+
class={className}
|
|
123
|
+
{onload}
|
|
124
|
+
{...restProps}
|
|
125
|
+
>
|
|
126
|
+
<source src={data.url} type={data.mimeType} />
|
|
127
|
+
<track kind="captions" />
|
|
128
|
+
</video>
|
|
129
|
+
{/if}
|
|
130
|
+
|
|
131
|
+
<style>img,
|
|
132
|
+
video {
|
|
133
|
+
max-width: 100%;
|
|
134
|
+
height: auto;
|
|
135
|
+
display: block;
|
|
136
|
+
}</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PayloadMediaProps } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* PayloadMedia renders responsive images and videos from Payload CMS media fields.
|
|
4
|
+
* Handles multiple image sizes with automatic AVIF/WebP format prioritisation.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <PayloadMedia
|
|
9
|
+
* data={media}
|
|
10
|
+
* loading="lazy"
|
|
11
|
+
* maxWidth={1200}
|
|
12
|
+
* />
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```svelte
|
|
17
|
+
* <PayloadMedia
|
|
18
|
+
* data={videoMedia}
|
|
19
|
+
* className="custom-video"
|
|
20
|
+
* />
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
declare const PayloadMedia: import("svelte").Component<PayloadMediaProps, {}, "">;
|
|
24
|
+
type PayloadMedia = ReturnType<typeof PayloadMedia>;
|
|
25
|
+
export default PayloadMedia;
|
|
26
|
+
//# sourceMappingURL=PayloadMedia.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PayloadMedia.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/payload/PayloadMedia.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAqB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAqFvE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* SvelteKit utilities, components and helpers for ainsley.dev builds.
|
|
5
5
|
* Provides form utilities, grid components, and Payload CMS integrations.
|
|
6
|
-
*/
|
|
6
|
+
*/
|
|
7
|
+
// Re-export all exports from submodules
|
|
7
8
|
export * from './components/Grid/index.js';
|
|
8
9
|
export * from './components/payload/index.js';
|
|
9
10
|
export * from './utils/forms/index.js';
|
|
10
|
-
|
|
11
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
* // { email: 'Invalid email' }
|
|
18
18
|
* }
|
|
19
19
|
* ```
|
|
20
|
-
*/
|
|
20
|
+
*/
|
|
21
|
+
export const flattenZodErrors = (error) => {
|
|
21
22
|
const errors = {};
|
|
22
|
-
for (const issue of error.issues){
|
|
23
|
+
for (const issue of error.issues) {
|
|
23
24
|
const path = issue.path.join('.');
|
|
24
25
|
if (!errors[path]) {
|
|
25
26
|
errors[path] = issue.message;
|
|
@@ -27,5 +28,3 @@
|
|
|
27
28
|
}
|
|
28
29
|
return errors;
|
|
29
30
|
};
|
|
30
|
-
|
|
31
|
-
//# sourceMappingURL=helpers.js.map
|
|
@@ -25,29 +25,32 @@ import { z } from 'zod';
|
|
|
25
25
|
* // message: z.string().optional()
|
|
26
26
|
* // })
|
|
27
27
|
* ```
|
|
28
|
-
*/
|
|
29
|
-
|
|
28
|
+
*/
|
|
29
|
+
export const generateFormSchema = (fields) => {
|
|
30
|
+
if (!fields?.length)
|
|
31
|
+
return z.object({});
|
|
30
32
|
const shape = {};
|
|
31
|
-
for (const field of fields){
|
|
33
|
+
for (const field of fields) {
|
|
32
34
|
if (field.blockType === 'text' || field.blockType === 'textarea') {
|
|
33
|
-
shape[field.name] = field.required
|
|
34
|
-
message: `${field.label || field.name} is required`
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
message: 'Invalid email'
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
shape[field.name] = field.required
|
|
36
|
+
? z.string().min(1, { message: `${field.label || field.name} is required` })
|
|
37
|
+
: z.string().optional();
|
|
38
|
+
}
|
|
39
|
+
else if (field.blockType === 'email') {
|
|
40
|
+
shape[field.name] = field.required
|
|
41
|
+
? z.string().email({ message: 'Invalid email' })
|
|
42
|
+
: z.string().email({ message: 'Invalid email' }).optional();
|
|
43
|
+
}
|
|
44
|
+
else if (field.blockType === 'number') {
|
|
43
45
|
shape[field.name] = field.required ? z.number() : z.number().optional();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
}
|
|
47
|
+
else if (field.blockType === 'checkbox') {
|
|
48
|
+
shape[field.name] = field.required
|
|
49
|
+
? z.boolean().refine((val) => val === true, {
|
|
50
|
+
message: `${field.label || field.name} must be checked`,
|
|
51
|
+
})
|
|
52
|
+
: z.boolean().optional();
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
return z.object(shape);
|
|
51
56
|
};
|
|
52
|
-
|
|
53
|
-
//# sourceMappingURL=schema.js.map
|
|
@@ -1,115 +1,101 @@
|
|
|
1
1
|
import { describe, expect, test } from 'vitest';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { generateFormSchema } from './schema';
|
|
4
|
-
describe('generateFormSchema', ()=>{
|
|
5
|
-
test('returns empty object schema when no fields provided', ()=>{
|
|
4
|
+
describe('generateFormSchema', () => {
|
|
5
|
+
test('returns empty object schema when no fields provided', () => {
|
|
6
6
|
const schema = generateFormSchema(null);
|
|
7
7
|
expect(schema).toBeInstanceOf(z.ZodObject);
|
|
8
8
|
expect(schema.shape).toEqual({});
|
|
9
9
|
});
|
|
10
|
-
test('returns empty object schema for empty array', ()=>{
|
|
10
|
+
test('returns empty object schema for empty array', () => {
|
|
11
11
|
const schema = generateFormSchema([]);
|
|
12
12
|
expect(schema).toBeInstanceOf(z.ZodObject);
|
|
13
13
|
expect(schema.shape).toEqual({});
|
|
14
14
|
});
|
|
15
|
-
test('generates required text field schema', ()=>{
|
|
15
|
+
test('generates required text field schema', () => {
|
|
16
16
|
const fields = [
|
|
17
17
|
{
|
|
18
18
|
blockType: 'text',
|
|
19
19
|
name: 'firstName',
|
|
20
20
|
label: 'First Name',
|
|
21
|
-
required: true
|
|
22
|
-
}
|
|
21
|
+
required: true,
|
|
22
|
+
},
|
|
23
23
|
];
|
|
24
24
|
const schema = generateFormSchema(fields);
|
|
25
|
-
const result = schema.safeParse({
|
|
26
|
-
firstName: ''
|
|
27
|
-
});
|
|
25
|
+
const result = schema.safeParse({ firstName: '' });
|
|
28
26
|
expect(result.success).toBe(false);
|
|
29
|
-
const validResult = schema.safeParse({
|
|
30
|
-
firstName: 'John'
|
|
31
|
-
});
|
|
27
|
+
const validResult = schema.safeParse({ firstName: 'John' });
|
|
32
28
|
expect(validResult.success).toBe(true);
|
|
33
29
|
});
|
|
34
|
-
test('generates optional text field schema', ()=>{
|
|
30
|
+
test('generates optional text field schema', () => {
|
|
35
31
|
const fields = [
|
|
36
32
|
{
|
|
37
33
|
blockType: 'text',
|
|
38
34
|
name: 'middleName',
|
|
39
35
|
label: 'Middle Name',
|
|
40
|
-
required: false
|
|
41
|
-
}
|
|
36
|
+
required: false,
|
|
37
|
+
},
|
|
42
38
|
];
|
|
43
39
|
const schema = generateFormSchema(fields);
|
|
44
40
|
const result = schema.safeParse({});
|
|
45
41
|
expect(result.success).toBe(true);
|
|
46
42
|
});
|
|
47
|
-
test('generates required email field schema', ()=>{
|
|
43
|
+
test('generates required email field schema', () => {
|
|
48
44
|
const fields = [
|
|
49
45
|
{
|
|
50
46
|
blockType: 'email',
|
|
51
47
|
name: 'email',
|
|
52
48
|
label: 'Email',
|
|
53
|
-
required: true
|
|
54
|
-
}
|
|
49
|
+
required: true,
|
|
50
|
+
},
|
|
55
51
|
];
|
|
56
52
|
const schema = generateFormSchema(fields);
|
|
57
|
-
const invalidResult = schema.safeParse({
|
|
58
|
-
email: 'not-an-email'
|
|
59
|
-
});
|
|
53
|
+
const invalidResult = schema.safeParse({ email: 'not-an-email' });
|
|
60
54
|
expect(invalidResult.success).toBe(false);
|
|
61
|
-
const validResult = schema.safeParse({
|
|
62
|
-
email: 'test@example.com'
|
|
63
|
-
});
|
|
55
|
+
const validResult = schema.safeParse({ email: 'test@example.com' });
|
|
64
56
|
expect(validResult.success).toBe(true);
|
|
65
57
|
});
|
|
66
|
-
test('generates required checkbox field schema', ()=>{
|
|
58
|
+
test('generates required checkbox field schema', () => {
|
|
67
59
|
const fields = [
|
|
68
60
|
{
|
|
69
61
|
blockType: 'checkbox',
|
|
70
62
|
name: 'terms',
|
|
71
63
|
label: 'Accept Terms',
|
|
72
|
-
required: true
|
|
73
|
-
}
|
|
64
|
+
required: true,
|
|
65
|
+
},
|
|
74
66
|
];
|
|
75
67
|
const schema = generateFormSchema(fields);
|
|
76
|
-
const falseResult = schema.safeParse({
|
|
77
|
-
terms: false
|
|
78
|
-
});
|
|
68
|
+
const falseResult = schema.safeParse({ terms: false });
|
|
79
69
|
expect(falseResult.success).toBe(false);
|
|
80
|
-
const trueResult = schema.safeParse({
|
|
81
|
-
terms: true
|
|
82
|
-
});
|
|
70
|
+
const trueResult = schema.safeParse({ terms: true });
|
|
83
71
|
expect(trueResult.success).toBe(true);
|
|
84
72
|
});
|
|
85
|
-
test('generates schema with multiple fields', ()=>{
|
|
73
|
+
test('generates schema with multiple fields', () => {
|
|
86
74
|
const fields = [
|
|
87
75
|
{
|
|
88
76
|
blockType: 'text',
|
|
89
77
|
name: 'name',
|
|
90
78
|
label: 'Name',
|
|
91
|
-
required: true
|
|
79
|
+
required: true,
|
|
92
80
|
},
|
|
93
81
|
{
|
|
94
82
|
blockType: 'email',
|
|
95
83
|
name: 'email',
|
|
96
84
|
label: 'Email',
|
|
97
|
-
required: true
|
|
85
|
+
required: true,
|
|
98
86
|
},
|
|
99
87
|
{
|
|
100
88
|
blockType: 'textarea',
|
|
101
89
|
name: 'message',
|
|
102
90
|
label: 'Message',
|
|
103
|
-
required: false
|
|
104
|
-
}
|
|
91
|
+
required: false,
|
|
92
|
+
},
|
|
105
93
|
];
|
|
106
94
|
const schema = generateFormSchema(fields);
|
|
107
95
|
const result = schema.safeParse({
|
|
108
96
|
name: 'John',
|
|
109
|
-
email: 'john@example.com'
|
|
97
|
+
email: 'john@example.com',
|
|
110
98
|
});
|
|
111
99
|
expect(result.success).toBe(true);
|
|
112
100
|
});
|
|
113
101
|
});
|
|
114
|
-
|
|
115
|
-
//# sourceMappingURL=schema.test.js.map
|
|
@@ -9,12 +9,11 @@
|
|
|
9
9
|
* const id = generateRandomString(10)
|
|
10
10
|
* // Returns something like: "a3k9mxp2q1"
|
|
11
11
|
* ```
|
|
12
|
-
*/
|
|
12
|
+
*/
|
|
13
|
+
export const generateRandomString = (length) => {
|
|
13
14
|
let result = '';
|
|
14
|
-
while(result.length < length){
|
|
15
|
+
while (result.length < length) {
|
|
15
16
|
result += (Math.random() + 1).toString(36).substring(2);
|
|
16
17
|
}
|
|
17
18
|
return result.substring(0, length);
|
|
18
19
|
};
|
|
19
|
-
|
|
20
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
import { describe, expect, test } from 'vitest';
|
|
2
2
|
import { generateRandomString } from './index';
|
|
3
|
-
describe('generateRandomString', ()=>{
|
|
4
|
-
test('generates a string of the specified length', ()=>{
|
|
3
|
+
describe('generateRandomString', () => {
|
|
4
|
+
test('generates a string of the specified length', () => {
|
|
5
5
|
const length = 10;
|
|
6
6
|
const result = generateRandomString(length);
|
|
7
7
|
expect(result).toHaveLength(length);
|
|
8
8
|
});
|
|
9
|
-
test('generates alphanumeric characters only', ()=>{
|
|
9
|
+
test('generates alphanumeric characters only', () => {
|
|
10
10
|
const result = generateRandomString(100);
|
|
11
11
|
expect(result).toMatch(/^[a-z0-9]+$/);
|
|
12
12
|
});
|
|
13
|
-
test('generates different strings on multiple calls', ()=>{
|
|
13
|
+
test('generates different strings on multiple calls', () => {
|
|
14
14
|
const first = generateRandomString(20);
|
|
15
15
|
const second = generateRandomString(20);
|
|
16
16
|
expect(first).not.toBe(second);
|
|
17
17
|
});
|
|
18
|
-
test('handles length of 0', ()=>{
|
|
18
|
+
test('handles length of 0', () => {
|
|
19
19
|
const result = generateRandomString(0);
|
|
20
20
|
expect(result).toBe('');
|
|
21
21
|
});
|
|
22
|
-
test('handles length of 1', ()=>{
|
|
22
|
+
test('handles length of 1', () => {
|
|
23
23
|
const result = generateRandomString(1);
|
|
24
24
|
expect(result).toHaveLength(1);
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
|
-
|
|
28
|
-
//# sourceMappingURL=index.test.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainsleydev/sveltekit-helper",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "SvelteKit utilities, components and helpers for ainsley.dev builds",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -26,16 +26,18 @@
|
|
|
26
26
|
"exports": {
|
|
27
27
|
".": {
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
|
-
"
|
|
29
|
+
"svelte": "./dist/index.js",
|
|
30
30
|
"default": "./dist/index.js"
|
|
31
31
|
},
|
|
32
32
|
"./components/Grid": {
|
|
33
33
|
"types": "./dist/components/Grid/index.d.ts",
|
|
34
|
-
"
|
|
34
|
+
"svelte": "./dist/components/Grid/index.js",
|
|
35
|
+
"default": "./dist/components/Grid/index.js"
|
|
35
36
|
},
|
|
36
37
|
"./components/payload": {
|
|
37
38
|
"types": "./dist/components/payload/index.d.ts",
|
|
38
|
-
"
|
|
39
|
+
"svelte": "./dist/components/payload/index.js",
|
|
40
|
+
"default": "./dist/components/payload/index.js"
|
|
39
41
|
},
|
|
40
42
|
"./utils/forms": {
|
|
41
43
|
"types": "./dist/utils/forms/index.d.ts",
|
|
@@ -69,13 +71,13 @@
|
|
|
69
71
|
"devDependencies": {
|
|
70
72
|
"@sveltejs/kit": "^2.0.0",
|
|
71
73
|
"@sveltejs/package": "^2.0.0",
|
|
72
|
-
"@
|
|
73
|
-
"@swc/core": "^1.7.2",
|
|
74
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
74
75
|
"@types/node": "^24.10.1",
|
|
75
76
|
"payload": "3.63.0",
|
|
76
77
|
"rimraf": "6.1.0",
|
|
77
78
|
"svelte": "^5.0.0",
|
|
78
79
|
"typescript": "^5.5.4",
|
|
80
|
+
"vite": "^6.0.0",
|
|
79
81
|
"vitest": "^2.0.5",
|
|
80
82
|
"zod": "^3.24.3",
|
|
81
83
|
"@ainsleydev/eslint-config": "0.0.9"
|
|
@@ -85,9 +87,7 @@
|
|
|
85
87
|
"pnpm": ">=9"
|
|
86
88
|
},
|
|
87
89
|
"scripts": {
|
|
88
|
-
"build": "pnpm clean &&
|
|
89
|
-
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
|
|
90
|
-
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
|
90
|
+
"build": "pnpm clean && svelte-kit sync && svelte-package -i src",
|
|
91
91
|
"format": "biome check --write --unsafe .",
|
|
92
92
|
"lint": "biome lint --write .",
|
|
93
93
|
"clean": "rimraf {dist,*.tsbuildinfo}",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/Grid/index.ts"],"sourcesContent":["export { default as Container } from './Container.svelte';\nexport { default as Row } from './Row.svelte';\nexport { default as Column } from './Column.svelte';\n"],"names":["default","Container","Row","Column"],"mappings":"AAAA,SAASA,WAAWC,SAAS,QAAQ,qBAAqB;AAC1D,SAASD,WAAWE,GAAG,QAAQ,eAAe;AAC9C,SAASF,WAAWG,MAAM,QAAQ,kBAAkB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/payload/index.ts"],"sourcesContent":["export { default as PayloadForm } from './PayloadForm.svelte';\nexport { default as PayloadMedia } from './PayloadMedia.svelte';\nexport type { Media, MediaSizes, PayloadMediaProps } from './types.js';\n"],"names":["default","PayloadForm","PayloadMedia"],"mappings":"AAAA,SAASA,WAAWC,WAAW,QAAQ,uBAAuB;AAC9D,SAASD,WAAWE,YAAY,QAAQ,wBAAwB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/payload/types.ts"],"sourcesContent":["import type { HTMLAttributes } from 'svelte/elements';\n\nexport type MediaSizes = Record<\n\tstring,\n\t| Partial<{\n\t\t\turl?: string | null;\n\t\t\twidth?: number | null;\n\t\t\theight?: number | null;\n\t\t\tmimeType?: string | null;\n\t }>\n\t| undefined\n>;\n\nexport type PayloadMediaProps = HTMLAttributes<HTMLElement> & {\n\tdata: Media;\n\tloading?: 'lazy' | 'eager' | undefined;\n\tclassName?: string;\n\tbreakpointBuffer?: number;\n\tmaxWidth?: number | undefined;\n\tonload?: (event: Event) => void;\n};\n\nexport type Media = {\n\tid: number;\n\talt?: string;\n\tupdatedAt: string;\n\tcreatedAt: string;\n\tdeletedAt?: string | null;\n\turl?: string | null;\n\tthumbnailURL?: string | null;\n\tfilename?: string | null;\n\tmimeType?: string | null;\n\tfilesize?: number | null;\n\twidth?: number | null;\n\theight?: number | null;\n\tfocalX?: number | null;\n\tfocalY?: number | null;\n\tsizes?: MediaSizes;\n};\n"],"names":[],"mappings":"AAsBA,WAgBE"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @ainsleydev/sveltekit-helper\n *\n * SvelteKit utilities, components and helpers for ainsley.dev builds.\n * Provides form utilities, grid components, and Payload CMS integrations.\n */\n\n// Re-export all exports from submodules\nexport * from './components/Grid/index.js';\nexport * from './components/payload/index.js';\nexport * from './utils/forms/index.js';\n"],"names":[],"mappings":"AAAA;;;;;CAKC,GAED,wCAAwC;AACxC,cAAc,6BAA6B;AAC3C,cAAc,gCAAgC;AAC9C,cAAc,yBAAyB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/forms/helpers.ts"],"sourcesContent":["import type { z } from 'zod';\n\nexport type FormErrors = Record<string, string>;\n\n/**\n * Flattens Zod validation errors into a simple key-value object.\n * Takes only the first error message for each field.\n *\n * @param error - ZodError from validation failure.\n * @returns Object mapping field names to error messages.\n *\n * @example\n * ```typescript\n * import { flattenZodErrors } from '@ainsleydev/sveltekit-helper/utils/forms'\n *\n * const schema = z.object({ email: z.string().email() })\n * const result = schema.safeParse({ email: 'invalid' })\n *\n * if (!result.success) {\n * const errors = flattenZodErrors(result.error)\n * // { email: 'Invalid email' }\n * }\n * ```\n */\nexport const flattenZodErrors = (error: z.ZodError): FormErrors => {\n\tconst errors: FormErrors = {};\n\n\tfor (const issue of error.issues) {\n\t\tconst path = issue.path.join('.');\n\t\tif (!errors[path]) {\n\t\t\terrors[path] = issue.message;\n\t\t}\n\t}\n\n\treturn errors;\n};\n"],"names":["flattenZodErrors","error","errors","issue","issues","path","join","message"],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;CAmBC,GACD,OAAO,MAAMA,mBAAmB,CAACC;IAChC,MAAMC,SAAqB,CAAC;IAE5B,KAAK,MAAMC,SAASF,MAAMG,MAAM,CAAE;QACjC,MAAMC,OAAOF,MAAME,IAAI,CAACC,IAAI,CAAC;QAC7B,IAAI,CAACJ,MAAM,CAACG,KAAK,EAAE;YAClBH,MAAM,CAACG,KAAK,GAAGF,MAAMI,OAAO;QAC7B;IACD;IAEA,OAAOL;AACR,EAAE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/forms/index.ts"],"sourcesContent":["export { flattenZodErrors, type FormErrors } from './helpers.js';\nexport { generateFormSchema, type PayloadFormField } from './schema.js';\n"],"names":["flattenZodErrors","generateFormSchema"],"mappings":"AAAA,SAASA,gBAAgB,QAAyB,eAAe;AACjE,SAASC,kBAAkB,QAA+B,cAAc"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/forms/schema.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Represents a form field from Payload CMS form builder.\n * This is a simplified type that covers the most common field types.\n */\nexport type PayloadFormField =\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tplaceholder?: string | null;\n\t\t\tdefaultValue?: string | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'text';\n\t }\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tplaceholder?: string | null;\n\t\t\tdefaultValue?: string | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'email';\n\t }\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tplaceholder?: string | null;\n\t\t\tdefaultValue?: number | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'number';\n\t }\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tplaceholder?: string | null;\n\t\t\tdefaultValue?: string | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'textarea';\n\t }\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tdefaultValue?: boolean | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'checkbox';\n\t };\n\n/**\n * Generates a dynamic Zod schema from Payload CMS Form Fields.\n *\n * This utility automatically creates validation schemas for Payload form fields,\n * respecting required flags and field types.\n *\n * @param fields - Array of form fields from Payload CMS form builder.\n * @returns Zod object schema for validating the form.\n *\n * @example\n * ```typescript\n * import { generateFormSchema } from '@ainsleydev/sveltekit-helper/utils/forms'\n *\n * const fields = [\n * { blockType: 'text', name: 'name', label: 'Name', required: true },\n * { blockType: 'email', name: 'email', label: 'Email', required: true },\n * { blockType: 'textarea', name: 'message', label: 'Message', required: false }\n * ]\n *\n * const schema = generateFormSchema(fields)\n * // Returns z.object({\n * // name: z.string().min(1, { message: 'Name is required' }),\n * // email: z.string().email({ message: 'Invalid email' }),\n * // message: z.string().optional()\n * // })\n * ```\n */\nexport const generateFormSchema = (\n\tfields?: PayloadFormField[] | null,\n): z.ZodObject<Record<string, z.ZodTypeAny>> => {\n\tif (!fields?.length) return z.object({}) as z.ZodObject<Record<string, z.ZodTypeAny>>;\n\n\tconst shape: Record<string, z.ZodTypeAny> = {};\n\n\tfor (const field of fields) {\n\t\tif (field.blockType === 'text' || field.blockType === 'textarea') {\n\t\t\tshape[field.name] = field.required\n\t\t\t\t? z.string().min(1, { message: `${field.label || field.name} is required` })\n\t\t\t\t: z.string().optional();\n\t\t} else if (field.blockType === 'email') {\n\t\t\tshape[field.name] = field.required\n\t\t\t\t? z.string().email({ message: 'Invalid email' })\n\t\t\t\t: z.string().email({ message: 'Invalid email' }).optional();\n\t\t} else if (field.blockType === 'number') {\n\t\t\tshape[field.name] = field.required ? z.number() : z.number().optional();\n\t\t} else if (field.blockType === 'checkbox') {\n\t\t\tshape[field.name] = field.required\n\t\t\t\t? z.boolean().refine((val) => val === true, {\n\t\t\t\t\t\tmessage: `${field.label || field.name} must be checked`,\n\t\t\t\t\t})\n\t\t\t\t: z.boolean().optional();\n\t\t}\n\t}\n\n\treturn z.object(shape) as z.ZodObject<Record<string, z.ZodTypeAny>>;\n};\n"],"names":["z","generateFormSchema","fields","length","object","shape","field","blockType","name","required","string","min","message","label","optional","email","number","boolean","refine","val"],"mappings":"AAAA,SAASA,CAAC,QAAQ,MAAM;AAyDxB;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BC,GACD,OAAO,MAAMC,qBAAqB,CACjCC;IAEA,IAAI,CAACA,QAAQC,QAAQ,OAAOH,EAAEI,MAAM,CAAC,CAAC;IAEtC,MAAMC,QAAsC,CAAC;IAE7C,KAAK,MAAMC,SAASJ,OAAQ;QAC3B,IAAII,MAAMC,SAAS,KAAK,UAAUD,MAAMC,SAAS,KAAK,YAAY;YACjEF,KAAK,CAACC,MAAME,IAAI,CAAC,GAAGF,MAAMG,QAAQ,GAC/BT,EAAEU,MAAM,GAAGC,GAAG,CAAC,GAAG;gBAAEC,SAAS,GAAGN,MAAMO,KAAK,IAAIP,MAAME,IAAI,CAAC,YAAY,CAAC;YAAC,KACxER,EAAEU,MAAM,GAAGI,QAAQ;QACvB,OAAO,IAAIR,MAAMC,SAAS,KAAK,SAAS;YACvCF,KAAK,CAACC,MAAME,IAAI,CAAC,GAAGF,MAAMG,QAAQ,GAC/BT,EAAEU,MAAM,GAAGK,KAAK,CAAC;gBAAEH,SAAS;YAAgB,KAC5CZ,EAAEU,MAAM,GAAGK,KAAK,CAAC;gBAAEH,SAAS;YAAgB,GAAGE,QAAQ;QAC3D,OAAO,IAAIR,MAAMC,SAAS,KAAK,UAAU;YACxCF,KAAK,CAACC,MAAME,IAAI,CAAC,GAAGF,MAAMG,QAAQ,GAAGT,EAAEgB,MAAM,KAAKhB,EAAEgB,MAAM,GAAGF,QAAQ;QACtE,OAAO,IAAIR,MAAMC,SAAS,KAAK,YAAY;YAC1CF,KAAK,CAACC,MAAME,IAAI,CAAC,GAAGF,MAAMG,QAAQ,GAC/BT,EAAEiB,OAAO,GAAGC,MAAM,CAAC,CAACC,MAAQA,QAAQ,MAAM;gBAC1CP,SAAS,GAAGN,MAAMO,KAAK,IAAIP,MAAME,IAAI,CAAC,gBAAgB,CAAC;YACxD,KACCR,EAAEiB,OAAO,GAAGH,QAAQ;QACxB;IACD;IAEA,OAAOd,EAAEI,MAAM,CAACC;AACjB,EAAE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/forms/schema.test.ts"],"sourcesContent":["import { describe, expect, test } from 'vitest';\nimport { z } from 'zod';\n\nimport { generateFormSchema } from './schema';\n\ndescribe('generateFormSchema', () => {\n\ttest('returns empty object schema when no fields provided', () => {\n\t\tconst schema = generateFormSchema(null);\n\t\texpect(schema).toBeInstanceOf(z.ZodObject);\n\t\texpect(schema.shape).toEqual({});\n\t});\n\n\ttest('returns empty object schema for empty array', () => {\n\t\tconst schema = generateFormSchema([]);\n\t\texpect(schema).toBeInstanceOf(z.ZodObject);\n\t\texpect(schema.shape).toEqual({});\n\t});\n\n\ttest('generates required text field schema', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'text' as const,\n\t\t\t\tname: 'firstName',\n\t\t\t\tlabel: 'First Name',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst result = schema.safeParse({ firstName: '' });\n\t\texpect(result.success).toBe(false);\n\n\t\tconst validResult = schema.safeParse({ firstName: 'John' });\n\t\texpect(validResult.success).toBe(true);\n\t});\n\n\ttest('generates optional text field schema', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'text' as const,\n\t\t\t\tname: 'middleName',\n\t\t\t\tlabel: 'Middle Name',\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst result = schema.safeParse({});\n\t\texpect(result.success).toBe(true);\n\t});\n\n\ttest('generates required email field schema', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'email' as const,\n\t\t\t\tname: 'email',\n\t\t\t\tlabel: 'Email',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst invalidResult = schema.safeParse({ email: 'not-an-email' });\n\t\texpect(invalidResult.success).toBe(false);\n\n\t\tconst validResult = schema.safeParse({ email: 'test@example.com' });\n\t\texpect(validResult.success).toBe(true);\n\t});\n\n\ttest('generates required checkbox field schema', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'checkbox' as const,\n\t\t\t\tname: 'terms',\n\t\t\t\tlabel: 'Accept Terms',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst falseResult = schema.safeParse({ terms: false });\n\t\texpect(falseResult.success).toBe(false);\n\n\t\tconst trueResult = schema.safeParse({ terms: true });\n\t\texpect(trueResult.success).toBe(true);\n\t});\n\n\ttest('generates schema with multiple fields', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'text' as const,\n\t\t\t\tname: 'name',\n\t\t\t\tlabel: 'Name',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tblockType: 'email' as const,\n\t\t\t\tname: 'email',\n\t\t\t\tlabel: 'Email',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tblockType: 'textarea' as const,\n\t\t\t\tname: 'message',\n\t\t\t\tlabel: 'Message',\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst result = schema.safeParse({\n\t\t\tname: 'John',\n\t\t\temail: 'john@example.com',\n\t\t});\n\t\texpect(result.success).toBe(true);\n\t});\n});\n"],"names":["describe","expect","test","z","generateFormSchema","schema","toBeInstanceOf","ZodObject","shape","toEqual","fields","blockType","name","label","required","result","safeParse","firstName","success","toBe","validResult","invalidResult","email","falseResult","terms","trueResult"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,IAAI,QAAQ,SAAS;AAChD,SAASC,CAAC,QAAQ,MAAM;AAExB,SAASC,kBAAkB,QAAQ,WAAW;AAE9CJ,SAAS,sBAAsB;IAC9BE,KAAK,uDAAuD;QAC3D,MAAMG,SAASD,mBAAmB;QAClCH,OAAOI,QAAQC,cAAc,CAACH,EAAEI,SAAS;QACzCN,OAAOI,OAAOG,KAAK,EAAEC,OAAO,CAAC,CAAC;IAC/B;IAEAP,KAAK,+CAA+C;QACnD,MAAMG,SAASD,mBAAmB,EAAE;QACpCH,OAAOI,QAAQC,cAAc,CAACH,EAAEI,SAAS;QACzCN,OAAOI,OAAOG,KAAK,EAAEC,OAAO,CAAC,CAAC;IAC/B;IAEAP,KAAK,wCAAwC;QAC5C,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMK,SAASV,OAAOW,SAAS,CAAC;YAAEC,WAAW;QAAG;QAChDhB,OAAOc,OAAOG,OAAO,EAAEC,IAAI,CAAC;QAE5B,MAAMC,cAAcf,OAAOW,SAAS,CAAC;YAAEC,WAAW;QAAO;QACzDhB,OAAOmB,YAAYF,OAAO,EAAEC,IAAI,CAAC;IAClC;IAEAjB,KAAK,wCAAwC;QAC5C,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMK,SAASV,OAAOW,SAAS,CAAC,CAAC;QACjCf,OAAOc,OAAOG,OAAO,EAAEC,IAAI,CAAC;IAC7B;IAEAjB,KAAK,yCAAyC;QAC7C,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMW,gBAAgBhB,OAAOW,SAAS,CAAC;YAAEM,OAAO;QAAe;QAC/DrB,OAAOoB,cAAcH,OAAO,EAAEC,IAAI,CAAC;QAEnC,MAAMC,cAAcf,OAAOW,SAAS,CAAC;YAAEM,OAAO;QAAmB;QACjErB,OAAOmB,YAAYF,OAAO,EAAEC,IAAI,CAAC;IAClC;IAEAjB,KAAK,4CAA4C;QAChD,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMa,cAAclB,OAAOW,SAAS,CAAC;YAAEQ,OAAO;QAAM;QACpDvB,OAAOsB,YAAYL,OAAO,EAAEC,IAAI,CAAC;QAEjC,MAAMM,aAAapB,OAAOW,SAAS,CAAC;YAAEQ,OAAO;QAAK;QAClDvB,OAAOwB,WAAWP,OAAO,EAAEC,IAAI,CAAC;IACjC;IAEAjB,KAAK,yCAAyC;QAC7C,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;YACA;gBACCH,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;YACA;gBACCH,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMK,SAASV,OAAOW,SAAS,CAAC;YAC/BJ,MAAM;YACNU,OAAO;QACR;QACArB,OAAOc,OAAOG,OAAO,EAAEC,IAAI,CAAC;IAC7B;AACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/strings/index.ts"],"sourcesContent":["/**\n * Generates an alphanumeric random string.\n *\n * @param length - The length of the string to generate.\n * @returns The generated random string.\n *\n * @example\n * ```typescript\n * const id = generateRandomString(10)\n * // Returns something like: \"a3k9mxp2q1\"\n * ```\n */\nexport const generateRandomString = (length: number): string => {\n\tlet result = '';\n\twhile (result.length < length) {\n\t\tresult += (Math.random() + 1).toString(36).substring(2);\n\t}\n\treturn result.substring(0, length);\n};\n"],"names":["generateRandomString","length","result","Math","random","toString","substring"],"mappings":"AAAA;;;;;;;;;;;CAWC,GACD,OAAO,MAAMA,uBAAuB,CAACC;IACpC,IAAIC,SAAS;IACb,MAAOA,OAAOD,MAAM,GAAGA,OAAQ;QAC9BC,UAAU,AAACC,CAAAA,KAAKC,MAAM,KAAK,CAAA,EAAGC,QAAQ,CAAC,IAAIC,SAAS,CAAC;IACtD;IACA,OAAOJ,OAAOI,SAAS,CAAC,GAAGL;AAC5B,EAAE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/strings/index.test.ts"],"sourcesContent":["import { describe, expect, test } from 'vitest';\n\nimport { generateRandomString } from './index';\n\ndescribe('generateRandomString', () => {\n\ttest('generates a string of the specified length', () => {\n\t\tconst length = 10;\n\t\tconst result = generateRandomString(length);\n\t\texpect(result).toHaveLength(length);\n\t});\n\n\ttest('generates alphanumeric characters only', () => {\n\t\tconst result = generateRandomString(100);\n\t\texpect(result).toMatch(/^[a-z0-9]+$/);\n\t});\n\n\ttest('generates different strings on multiple calls', () => {\n\t\tconst first = generateRandomString(20);\n\t\tconst second = generateRandomString(20);\n\t\texpect(first).not.toBe(second);\n\t});\n\n\ttest('handles length of 0', () => {\n\t\tconst result = generateRandomString(0);\n\t\texpect(result).toBe('');\n\t});\n\n\ttest('handles length of 1', () => {\n\t\tconst result = generateRandomString(1);\n\t\texpect(result).toHaveLength(1);\n\t});\n});\n"],"names":["describe","expect","test","generateRandomString","length","result","toHaveLength","toMatch","first","second","not","toBe"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,IAAI,QAAQ,SAAS;AAEhD,SAASC,oBAAoB,QAAQ,UAAU;AAE/CH,SAAS,wBAAwB;IAChCE,KAAK,8CAA8C;QAClD,MAAME,SAAS;QACf,MAAMC,SAASF,qBAAqBC;QACpCH,OAAOI,QAAQC,YAAY,CAACF;IAC7B;IAEAF,KAAK,0CAA0C;QAC9C,MAAMG,SAASF,qBAAqB;QACpCF,OAAOI,QAAQE,OAAO,CAAC;IACxB;IAEAL,KAAK,iDAAiD;QACrD,MAAMM,QAAQL,qBAAqB;QACnC,MAAMM,SAASN,qBAAqB;QACpCF,OAAOO,OAAOE,GAAG,CAACC,IAAI,CAACF;IACxB;IAEAP,KAAK,uBAAuB;QAC3B,MAAMG,SAASF,qBAAqB;QACpCF,OAAOI,QAAQM,IAAI,CAAC;IACrB;IAEAT,KAAK,uBAAuB;QAC3B,MAAMG,SAASF,qBAAqB;QACpCF,OAAOI,QAAQC,YAAY,CAAC;IAC7B;AACD"}
|