Skip to content

Commit 44d809b

Browse files
committed
Added emptyIfZero option to numberProxy and intProxy.
1 parent 95c2603 commit 44d809b

File tree

5 files changed

+92
-23
lines changed

5 files changed

+92
-23
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- A boolean schema field didn't accept a boolean `false` value when posted, it was coerced as `true`.
1313
- A SuperDebug truncated string showed only the whole string length, not the truncated.
1414

15+
### Added
16+
17+
- Added `emptyIfZero` option to `numberProxy` and `intProxy`.
18+
1519
## [1.4.0] - 2023-07-20
1620

1721
### Fixed

src/lib/client/proxies.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,41 +30,45 @@ type DefaultOptions = {
3030
| 'iso';
3131
delimiter?: '.' | ',';
3232
empty?: 'null' | 'undefined';
33+
emptyIfZero?: boolean;
3334
};
3435

3536
const defaultOptions: DefaultOptions = {
3637
trueStringValue: 'true',
37-
dateFormat: 'iso'
38+
dateFormat: 'iso',
39+
emptyIfZero: true
3840
};
3941

40-
export function intProxy<
42+
///// Proxy functions ///////////////////////////////////////////////
43+
44+
export function booleanProxy<
4145
T extends Record<string, unknown>,
4246
Path extends FormPath<T>
4347
>(
4448
form: Writable<T>,
4549
path: Path,
46-
options: Pick<DefaultOptions, 'empty'> = {}
50+
options: Pick<DefaultOptions, 'trueStringValue'> = {
51+
trueStringValue: 'true'
52+
}
4753
) {
48-
return _stringProxy(form, path, 'int', {
54+
return _stringProxy(form, path, 'boolean', {
4955
...defaultOptions,
5056
...options
51-
}) as FormPathType<T, Path> extends number ? Writable<string> : never;
57+
}) as FormPathType<T, Path> extends boolean ? Writable<string> : never;
5258
}
5359

54-
export function booleanProxy<
60+
export function intProxy<
5561
T extends Record<string, unknown>,
5662
Path extends FormPath<T>
5763
>(
5864
form: Writable<T>,
5965
path: Path,
60-
options: Pick<DefaultOptions, 'trueStringValue'> = {
61-
trueStringValue: 'true'
62-
}
66+
options: Pick<DefaultOptions, 'empty' | 'emptyIfZero'> = {}
6367
) {
64-
return _stringProxy(form, path, 'boolean', {
68+
return _stringProxy(form, path, 'int', {
6569
...defaultOptions,
6670
...options
67-
}) as FormPathType<T, Path> extends boolean ? Writable<string> : never;
71+
}) as FormPathType<T, Path> extends number ? Writable<string> : never;
6872
}
6973

7074
export function numberProxy<
@@ -73,7 +77,7 @@ export function numberProxy<
7377
>(
7478
form: Writable<T>,
7579
path: Path,
76-
options: Pick<DefaultOptions, 'empty' | 'delimiter'> = {}
80+
options: Pick<DefaultOptions, 'empty' | 'emptyIfZero' | 'delimiter'> = {}
7781
) {
7882
return _stringProxy(form, path, 'number', {
7983
...defaultOptions,
@@ -117,6 +121,8 @@ export function stringProxy<
117121
}) as FormPathType<T, Path> extends string ? Writable<string> : never;
118122
}
119123

124+
///// Implementation ////////////////////////////////////////////////
125+
120126
/**
121127
* Creates a string store that will pass its value to a field in the form.
122128
* @param form The form
@@ -134,8 +140,13 @@ function _stringProxy<
134140
options: DefaultOptions
135141
): Writable<string> {
136142
function toValue(value: unknown) {
137-
if (!value && options.empty !== undefined)
143+
if (
144+
!value &&
145+
options.empty !== undefined &&
146+
(value !== 0 || options.emptyIfZero)
147+
) {
138148
return options.empty === 'null' ? null : undefined;
149+
}
139150

140151
if (typeof value === 'number') {
141152
value = value.toString();
@@ -157,8 +168,12 @@ function _stringProxy<
157168
if (type == 'number') num = parseFloat(numberToConvert);
158169
else num = parseInt(numberToConvert, 10);
159170

160-
if (options.empty !== undefined && (isNaN(num) || num == 0))
171+
if (
172+
options.empty !== undefined &&
173+
((num === 0 && options.emptyIfZero) || isNaN(num))
174+
) {
161175
return options.empty == 'null' ? null : undefined;
176+
}
162177

163178
return num;
164179
}

src/routes/Navigation.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
<a href="/reset">Reset</a> |
1414
<a href="/url">URL</a> |
1515
<a href="/tainted">Tainted</a> |
16-
<a href="/proxies">Proxies</a>
16+
<a href="/proxies">Proxies</a> |
17+
<a href="/tests/datetime-local">Proxies 2</a>
1718
<br />
1819
<a href="/properly-nested">Properly nested</a> |
1920
<a href="/files">Files</a> |

src/routes/tests/datetime-local/+page.svelte

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,73 @@
3131
empty: 'undefined'
3232
});
3333
34-
const num = numberProxy(form, 'number', {
34+
const emptyUndef = intProxy(form, 'emptyUndef', {
3535
empty: 'undefined'
3636
});
37+
38+
const emptyZero = numberProxy(form, 'emptyZero', {
39+
empty: 'undefined',
40+
emptyIfZero: false
41+
});
3742
</script>
3843

3944
<SuperDebug data={{ $form, $tainted }} />
4045

4146
<form method="POST" use:enhance>
4247
<label>
43-
Number: <input name="number" type="number" bind:value={$num} />
44-
{#if $errors.number}<span class="invalid">{$errors.number}</span>{/if}
48+
Empty if undefined and zero: <input
49+
name="emptyUndef"
50+
type="number"
51+
bind:value={$emptyUndef}
52+
/>
53+
<span class="value">
54+
{$emptyUndef === '' ? 'undefined' : $emptyUndef}
55+
</span>
56+
{#if $errors.emptyUndef}
57+
<span class="invalid">{$errors.emptyUndef}</span>
58+
{/if}
4559
</label>
60+
4661
<label>
47-
Name: <input name="date" type="datetime-local" bind:value={$date} />
48-
{#if $errors.date}<span class="invalid">{$errors.date}</span>{/if}
62+
Empty if undefined and not zero: <input
63+
name="emptyZero"
64+
type="number"
65+
bind:value={$emptyZero}
66+
/>
67+
<span class="value">
68+
{$emptyZero === '' ? 'undefined' : $emptyZero}
69+
</span>
70+
{#if $errors.emptyZero}
71+
<span class="invalid">{$errors.emptyZero}</span>
72+
{/if}
4973
</label>
74+
5075
<label>
51-
Number2: <input
76+
Normal number: <input
5277
name="number2"
5378
type="number"
5479
bind:value={$form.number2}
5580
/>
81+
<span class="value">
82+
{$form.number2}
83+
</span>
5684
{#if $errors.number2}<span class="invalid">{$errors.number2}</span>{/if}
5785
</label>
86+
87+
<label>
88+
Date between 2021-2023: <input
89+
name="date"
90+
type="datetime-local"
91+
bind:value={$date}
92+
/>
93+
<span class="value">
94+
{$date === ''
95+
? 'invalid-date'
96+
: new Date($date).toISOString().slice(0, 10)}
97+
</span>
98+
{#if $errors.date}<span class="invalid">{$errors.date}</span>{/if}
99+
</label>
100+
58101
<div>
59102
<button>Submit</button>
60103
</div>
@@ -71,5 +114,10 @@
71114
.invalid {
72115
color: crimson;
73116
}
117+
118+
.value {
119+
font-weight: normal;
120+
color: mediumseagreen;
121+
}
74122
}
75123
</style>

src/routes/tests/datetime-local/schema.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { z } from 'zod';
22

33
export const schema = z.object({
4-
number: z.number().optional(),
5-
number2: z.number().optional(),
4+
emptyUndef: z.number().optional(),
5+
number2: z.number().nullable(),
6+
emptyZero: z.number().optional(),
67
date: z
78
.date()
89
.min(new Date('2021-01-01'))

0 commit comments

Comments
 (0)