@joyautomation/salt 0.1.3 → 0.1.5
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/forms/Form.svelte +153 -30
- package/dist/components/forms/Form.svelte.d.ts +5 -2
- package/dist/components/forms/Input.svelte +16 -2
- package/dist/components/forms/Input.svelte.d.ts +1 -0
- package/dist/components/forms/Select.svelte +16 -3
- package/dist/components/forms/Select.svelte.d.ts +1 -0
- package/dist/components/forms/Switch.svelte +8 -4
- package/dist/components/forms/Switch.svelte.d.ts +1 -0
- package/dist/components/forms/index.d.ts +1 -1
- package/dist/components/forms/types.d.ts +4 -0
- package/package.json +1 -1
|
@@ -3,39 +3,94 @@
|
|
|
3
3
|
import Input from './Input.svelte'
|
|
4
4
|
import Select from './Select.svelte'
|
|
5
5
|
import Switch from './Switch.svelte'
|
|
6
|
-
import type { FormInputs } from './types.js'
|
|
6
|
+
import type { FormGroup, FormInputs } from './types.js'
|
|
7
7
|
import { slide } from 'svelte/transition'
|
|
8
8
|
|
|
9
9
|
const {
|
|
10
10
|
inputs: propInputs,
|
|
11
|
+
groups: propGroups,
|
|
11
12
|
action,
|
|
12
13
|
buttonText = 'Submit',
|
|
14
|
+
resetButtonText = 'Reset',
|
|
15
|
+
showReset = true,
|
|
13
16
|
onsubmitstart,
|
|
14
17
|
onsubmitend
|
|
15
18
|
}: {
|
|
16
|
-
inputs
|
|
19
|
+
inputs?: FormInputs
|
|
20
|
+
groups?: FormGroup[]
|
|
17
21
|
action: string
|
|
18
22
|
buttonText?: string
|
|
23
|
+
resetButtonText?: string
|
|
24
|
+
showReset?: boolean
|
|
19
25
|
onsubmitstart?: () => void
|
|
20
26
|
onsubmitend?: () => void
|
|
21
27
|
} = $props()
|
|
22
28
|
|
|
23
|
-
let
|
|
29
|
+
let groups = $state(propGroups ?? [{ rows: propInputs ?? [] }])
|
|
24
30
|
let submitting = $state(false)
|
|
31
|
+
let defaultValues: Map<string, string> = $state(new Map())
|
|
25
32
|
|
|
26
33
|
$effect(() => {
|
|
27
|
-
|
|
34
|
+
groups = propGroups ?? [{ rows: propInputs ?? [] }]
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const allInputs = $derived(groups.flatMap((g) => g.rows))
|
|
38
|
+
|
|
39
|
+
// Capture defaults on first render
|
|
40
|
+
$effect(() => {
|
|
41
|
+
if (defaultValues.size === 0) {
|
|
42
|
+
const defaults = new Map<string, string>()
|
|
43
|
+
for (const row of allInputs) {
|
|
44
|
+
for (const input of row) {
|
|
45
|
+
defaults.set(input.id, input.value)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
defaultValues = defaults
|
|
49
|
+
}
|
|
28
50
|
})
|
|
29
51
|
|
|
30
52
|
const valid = $derived(
|
|
31
|
-
|
|
53
|
+
allInputs.every((row) =>
|
|
32
54
|
row.every((input) => {
|
|
33
55
|
return input.validations.every(([validation]) => {
|
|
34
|
-
return !validation(input.value,
|
|
56
|
+
return !validation(input.value, allInputs)
|
|
35
57
|
})
|
|
36
58
|
})
|
|
37
59
|
)
|
|
38
60
|
)
|
|
61
|
+
|
|
62
|
+
const hasChanges = $derived(
|
|
63
|
+
allInputs.some((row) =>
|
|
64
|
+
row.some((input) => {
|
|
65
|
+
return defaultValues.get(input.id) !== input.value
|
|
66
|
+
})
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
function isChanged(inputId: string, value: string): boolean {
|
|
71
|
+
return defaultValues.has(inputId) && defaultValues.get(inputId) !== value
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function reset() {
|
|
75
|
+
for (const row of allInputs) {
|
|
76
|
+
for (const input of row) {
|
|
77
|
+
const def = defaultValues.get(input.id)
|
|
78
|
+
if (def !== undefined) {
|
|
79
|
+
input.value = def
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function updateDefaults() {
|
|
86
|
+
const defaults = new Map<string, string>()
|
|
87
|
+
for (const row of allInputs) {
|
|
88
|
+
for (const input of row) {
|
|
89
|
+
defaults.set(input.id, input.value)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
defaultValues = defaults
|
|
93
|
+
}
|
|
39
94
|
</script>
|
|
40
95
|
|
|
41
96
|
<form
|
|
@@ -47,50 +102,118 @@
|
|
|
47
102
|
onsubmitstart?.()
|
|
48
103
|
return async ({ update }) => {
|
|
49
104
|
submitting = false
|
|
105
|
+
updateDefaults()
|
|
50
106
|
onsubmitend?.()
|
|
51
107
|
update({ reset: false })
|
|
52
108
|
}
|
|
53
109
|
}}
|
|
54
110
|
>
|
|
55
|
-
{#each
|
|
56
|
-
<
|
|
57
|
-
{#
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
111
|
+
{#each groups as group}
|
|
112
|
+
<fieldset class="form__group">
|
|
113
|
+
{#if group.heading}
|
|
114
|
+
<legend class="form__group-heading">{group.heading}</legend>
|
|
115
|
+
{/if}
|
|
116
|
+
{#each group.rows as row}
|
|
117
|
+
<div class="form__row">
|
|
118
|
+
{#each row as input}
|
|
119
|
+
<div>
|
|
120
|
+
{#if input.type === 'select'}
|
|
121
|
+
<Select
|
|
122
|
+
{...input}
|
|
123
|
+
bind:value={input.value}
|
|
124
|
+
inputs={allInputs}
|
|
125
|
+
options={input.options || []}
|
|
126
|
+
changed={isChanged(input.id, input.value)}
|
|
127
|
+
/>
|
|
128
|
+
{:else if input.type === 'checkbox'}
|
|
129
|
+
<Switch
|
|
130
|
+
{...input}
|
|
131
|
+
bind:value={input.value}
|
|
132
|
+
inputs={allInputs}
|
|
133
|
+
changed={isChanged(input.id, input.value)}
|
|
134
|
+
/>
|
|
135
|
+
{:else}
|
|
136
|
+
<Input
|
|
137
|
+
{...input}
|
|
138
|
+
bind:value={input.value}
|
|
139
|
+
inputs={allInputs}
|
|
140
|
+
changed={isChanged(input.id, input.value)}
|
|
141
|
+
/>
|
|
142
|
+
{/if}
|
|
143
|
+
</div>
|
|
144
|
+
{/each}
|
|
66
145
|
</div>
|
|
67
146
|
{/each}
|
|
68
|
-
</
|
|
147
|
+
</fieldset>
|
|
69
148
|
{/each}
|
|
70
|
-
<
|
|
71
|
-
{
|
|
72
|
-
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
149
|
+
<div class="form__actions">
|
|
150
|
+
<button class="button--primary" disabled={!valid || submitting}>
|
|
151
|
+
{#if submitting}
|
|
152
|
+
<div class="form__spinner-container" transition:slide>
|
|
153
|
+
<span class="form__spinner"></span>
|
|
154
|
+
</div>
|
|
155
|
+
{:else}
|
|
156
|
+
<div transition:slide>
|
|
157
|
+
{buttonText}
|
|
158
|
+
</div>
|
|
159
|
+
{/if}
|
|
160
|
+
</button>
|
|
161
|
+
{#if showReset && hasChanges}
|
|
162
|
+
<button type="button" class="button--secondary form__reset" onclick={reset} transition:slide>
|
|
163
|
+
{resetButtonText}
|
|
164
|
+
</button>
|
|
79
165
|
{/if}
|
|
80
|
-
</
|
|
166
|
+
</div>
|
|
81
167
|
</form>
|
|
82
168
|
|
|
83
169
|
<style>.form {
|
|
84
170
|
display: flex;
|
|
85
171
|
flex-direction: column;
|
|
172
|
+
gap: calc(var(--spacing-unit) * 4);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.form__group {
|
|
176
|
+
border: none;
|
|
177
|
+
padding: 0;
|
|
178
|
+
margin: 0;
|
|
179
|
+
display: flex;
|
|
180
|
+
flex-direction: column;
|
|
181
|
+
gap: calc(var(--spacing-unit) * 1);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.form__group-heading {
|
|
185
|
+
font-size: var(--text-base);
|
|
186
|
+
font-weight: 600;
|
|
187
|
+
color: var(--theme-text);
|
|
188
|
+
padding: 0;
|
|
86
189
|
}
|
|
87
|
-
|
|
190
|
+
|
|
191
|
+
.form__row {
|
|
88
192
|
display: flex;
|
|
89
193
|
}
|
|
90
|
-
.
|
|
194
|
+
.form__row > * {
|
|
91
195
|
flex-grow: 1;
|
|
92
196
|
}
|
|
93
197
|
|
|
198
|
+
.form__actions {
|
|
199
|
+
display: flex;
|
|
200
|
+
align-items: center;
|
|
201
|
+
gap: calc(var(--spacing-unit) * 2);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.form__reset {
|
|
205
|
+
background: transparent;
|
|
206
|
+
border: 1px solid var(--theme-neutral-400);
|
|
207
|
+
color: var(--theme-text);
|
|
208
|
+
padding: calc(var(--spacing-unit) * 1.5) calc(var(--spacing-unit) * 3);
|
|
209
|
+
border-radius: var(--rounded-sm);
|
|
210
|
+
cursor: pointer;
|
|
211
|
+
font-size: var(--text-sm);
|
|
212
|
+
}
|
|
213
|
+
.form__reset:hover {
|
|
214
|
+
background: var(--theme-neutral-200);
|
|
215
|
+
}
|
|
216
|
+
|
|
94
217
|
.form__spinner-container {
|
|
95
218
|
display: flex;
|
|
96
219
|
justify-content: center;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type { FormInputs } from './types.js';
|
|
1
|
+
import type { FormGroup, FormInputs } from './types.js';
|
|
2
2
|
type $$ComponentProps = {
|
|
3
|
-
inputs
|
|
3
|
+
inputs?: FormInputs;
|
|
4
|
+
groups?: FormGroup[];
|
|
4
5
|
action: string;
|
|
5
6
|
buttonText?: string;
|
|
7
|
+
resetButtonText?: string;
|
|
8
|
+
showReset?: boolean;
|
|
6
9
|
onsubmitstart?: () => void;
|
|
7
10
|
onsubmitend?: () => void;
|
|
8
11
|
};
|
|
@@ -12,8 +12,14 @@
|
|
|
12
12
|
validations,
|
|
13
13
|
inputs,
|
|
14
14
|
touched = false,
|
|
15
|
+
changed = false,
|
|
15
16
|
onblur = () => {}
|
|
16
|
-
}: InputProps & {
|
|
17
|
+
}: InputProps & {
|
|
18
|
+
inputs?: FormInputs
|
|
19
|
+
touched?: boolean
|
|
20
|
+
changed?: boolean
|
|
21
|
+
onblur?: () => void
|
|
22
|
+
} = $props()
|
|
17
23
|
const validationResult = $derived(
|
|
18
24
|
validations.find(([validation]) => {
|
|
19
25
|
return validation(value, inputs ?? [])
|
|
@@ -21,7 +27,11 @@
|
|
|
21
27
|
)
|
|
22
28
|
</script>
|
|
23
29
|
|
|
24
|
-
<div
|
|
30
|
+
<div
|
|
31
|
+
class="input"
|
|
32
|
+
class:input--invalid={touched && validationResult != null}
|
|
33
|
+
class:input--changed={changed}
|
|
34
|
+
>
|
|
25
35
|
{#if label != null}
|
|
26
36
|
<label for={id}>{label}</label>
|
|
27
37
|
{/if}
|
|
@@ -72,6 +82,10 @@
|
|
|
72
82
|
font-family: inherit;
|
|
73
83
|
}
|
|
74
84
|
|
|
85
|
+
.input--changed > input, .input--changed > textarea {
|
|
86
|
+
border-left: solid 3px var(--theme-primary);
|
|
87
|
+
}
|
|
88
|
+
|
|
75
89
|
.input--invalid > input, .input--invalid > textarea {
|
|
76
90
|
border: solid 1px var(--theme-error-400, var(--red-400));
|
|
77
91
|
}</style>
|
|
@@ -2,6 +2,7 @@ import type { FormInputs, InputProps } from './types.js';
|
|
|
2
2
|
type $$ComponentProps = InputProps & {
|
|
3
3
|
inputs?: FormInputs;
|
|
4
4
|
touched?: boolean;
|
|
5
|
+
changed?: boolean;
|
|
5
6
|
onblur?: () => void;
|
|
6
7
|
};
|
|
7
8
|
declare const Input: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
@@ -9,8 +9,13 @@
|
|
|
9
9
|
value = $bindable(''),
|
|
10
10
|
validations,
|
|
11
11
|
inputs,
|
|
12
|
-
options
|
|
13
|
-
|
|
12
|
+
options,
|
|
13
|
+
changed = false
|
|
14
|
+
}: InputProps & {
|
|
15
|
+
inputs?: FormInputs
|
|
16
|
+
options: { value: string; label: string }[]
|
|
17
|
+
changed?: boolean
|
|
18
|
+
} = $props()
|
|
14
19
|
const validationResult = $derived(
|
|
15
20
|
validations.find(([validation]) => {
|
|
16
21
|
return validation(value, inputs ?? [])
|
|
@@ -18,7 +23,11 @@
|
|
|
18
23
|
)
|
|
19
24
|
</script>
|
|
20
25
|
|
|
21
|
-
<div
|
|
26
|
+
<div
|
|
27
|
+
class="select-field"
|
|
28
|
+
class:select-field--invalid={validationResult != null}
|
|
29
|
+
class:select-field--changed={changed}
|
|
30
|
+
>
|
|
22
31
|
{#if label != null}
|
|
23
32
|
<label for={id}>{label}</label>
|
|
24
33
|
{/if}
|
|
@@ -64,6 +73,10 @@
|
|
|
64
73
|
border-color: var(--theme-primary);
|
|
65
74
|
}
|
|
66
75
|
|
|
76
|
+
.select-field--changed > select {
|
|
77
|
+
border-left: solid 3px var(--theme-primary);
|
|
78
|
+
}
|
|
79
|
+
|
|
67
80
|
.select-field--invalid > select {
|
|
68
81
|
border: solid 1px var(--theme-error-400, var(--red-400));
|
|
69
82
|
}</style>
|
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
name,
|
|
7
7
|
label,
|
|
8
8
|
value = $bindable('false'),
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}: InputProps & { inputs?: FormInputs } = $props()
|
|
9
|
+
changed = false
|
|
10
|
+
}: InputProps & { inputs?: FormInputs; changed?: boolean } = $props()
|
|
12
11
|
|
|
13
12
|
const checked = $derived(value === 'true')
|
|
14
13
|
|
|
@@ -24,7 +23,7 @@
|
|
|
24
23
|
}
|
|
25
24
|
</script>
|
|
26
25
|
|
|
27
|
-
<div class="switch-field">
|
|
26
|
+
<div class="switch-field" class:switch-field--changed={changed}>
|
|
28
27
|
{#if label != null}
|
|
29
28
|
<label for={id}>{label}</label>
|
|
30
29
|
{/if}
|
|
@@ -57,6 +56,11 @@
|
|
|
57
56
|
text-transform: capitalize;
|
|
58
57
|
}
|
|
59
58
|
|
|
59
|
+
.switch-field--changed {
|
|
60
|
+
padding-left: calc(var(--spacing-unit) * 1.5);
|
|
61
|
+
border-left: solid 3px var(--theme-primary);
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
.switch {
|
|
61
65
|
position: relative;
|
|
62
66
|
display: inline-block;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { FormInputs, InputProps } from './types.js';
|
|
2
2
|
type $$ComponentProps = InputProps & {
|
|
3
3
|
inputs?: FormInputs;
|
|
4
|
+
changed?: boolean;
|
|
4
5
|
};
|
|
5
6
|
declare const Switch: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
6
7
|
type Switch = ReturnType<typeof Switch>;
|
|
@@ -3,4 +3,4 @@ export { default as Input } from './Input.svelte';
|
|
|
3
3
|
export { default as Select } from './Select.svelte';
|
|
4
4
|
export { default as Switch } from './Switch.svelte';
|
|
5
5
|
export { default as SearchableSelect } from './SearchableSelect.svelte';
|
|
6
|
-
export type { InputProps, FormInputs, FormInputsPartial } from './types.js';
|
|
6
|
+
export type { InputProps, FormInputs, FormInputsPartial, FormGroup } from './types.js';
|