@joyautomation/salt 0.1.4 → 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.
@@ -11,6 +11,8 @@
11
11
  groups: propGroups,
12
12
  action,
13
13
  buttonText = 'Submit',
14
+ resetButtonText = 'Reset',
15
+ showReset = true,
14
16
  onsubmitstart,
15
17
  onsubmitend
16
18
  }: {
@@ -18,12 +20,15 @@
18
20
  groups?: FormGroup[]
19
21
  action: string
20
22
  buttonText?: string
23
+ resetButtonText?: string
24
+ showReset?: boolean
21
25
  onsubmitstart?: () => void
22
26
  onsubmitend?: () => void
23
27
  } = $props()
24
28
 
25
29
  let groups = $state(propGroups ?? [{ rows: propInputs ?? [] }])
26
30
  let submitting = $state(false)
31
+ let defaultValues: Map<string, string> = $state(new Map())
27
32
 
28
33
  $effect(() => {
29
34
  groups = propGroups ?? [{ rows: propInputs ?? [] }]
@@ -31,6 +36,19 @@
31
36
 
32
37
  const allInputs = $derived(groups.flatMap((g) => g.rows))
33
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
+ }
50
+ })
51
+
34
52
  const valid = $derived(
35
53
  allInputs.every((row) =>
36
54
  row.every((input) => {
@@ -40,6 +58,39 @@
40
58
  })
41
59
  )
42
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
+ }
43
94
  </script>
44
95
 
45
96
  <form
@@ -51,6 +102,7 @@
51
102
  onsubmitstart?.()
52
103
  return async ({ update }) => {
53
104
  submitting = false
105
+ updateDefaults()
54
106
  onsubmitend?.()
55
107
  update({ reset: false })
56
108
  }
@@ -71,11 +123,22 @@
71
123
  bind:value={input.value}
72
124
  inputs={allInputs}
73
125
  options={input.options || []}
126
+ changed={isChanged(input.id, input.value)}
74
127
  />
75
128
  {:else if input.type === 'checkbox'}
76
- <Switch {...input} bind:value={input.value} inputs={allInputs} />
129
+ <Switch
130
+ {...input}
131
+ bind:value={input.value}
132
+ inputs={allInputs}
133
+ changed={isChanged(input.id, input.value)}
134
+ />
77
135
  {:else}
78
- <Input {...input} bind:value={input.value} inputs={allInputs} />
136
+ <Input
137
+ {...input}
138
+ bind:value={input.value}
139
+ inputs={allInputs}
140
+ changed={isChanged(input.id, input.value)}
141
+ />
79
142
  {/if}
80
143
  </div>
81
144
  {/each}
@@ -83,17 +146,24 @@
83
146
  {/each}
84
147
  </fieldset>
85
148
  {/each}
86
- <button class="button--primary" disabled={!valid || submitting}>
87
- {#if submitting}
88
- <div class="form__spinner-container" transition:slide>
89
- <span class="form__spinner"></span>
90
- </div>
91
- {:else}
92
- <div transition:slide>
93
- {buttonText}
94
- </div>
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>
95
165
  {/if}
96
- </button>
166
+ </div>
97
167
  </form>
98
168
 
99
169
  <style>.form {
@@ -125,6 +195,25 @@
125
195
  flex-grow: 1;
126
196
  }
127
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
+
128
217
  .form__spinner-container {
129
218
  display: flex;
130
219
  justify-content: center;
@@ -4,6 +4,8 @@ type $$ComponentProps = {
4
4
  groups?: FormGroup[];
5
5
  action: string;
6
6
  buttonText?: string;
7
+ resetButtonText?: string;
8
+ showReset?: boolean;
7
9
  onsubmitstart?: () => void;
8
10
  onsubmitend?: () => void;
9
11
  };
@@ -12,8 +12,14 @@
12
12
  validations,
13
13
  inputs,
14
14
  touched = false,
15
+ changed = false,
15
16
  onblur = () => {}
16
- }: InputProps & { inputs?: FormInputs; touched?: boolean; onblur?: () => void } = $props()
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 class="input" class:input--invalid={touched && validationResult != null}>
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
- }: InputProps & { inputs?: FormInputs; options: { value: string; label: string }[] } = $props()
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 class="select-field" class:select-field--invalid={validationResult != null}>
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>
@@ -5,6 +5,7 @@ type $$ComponentProps = InputProps & {
5
5
  value: string;
6
6
  label: string;
7
7
  }[];
8
+ changed?: boolean;
8
9
  };
9
10
  declare const Select: import("svelte").Component<$$ComponentProps, {}, "value">;
10
11
  type Select = ReturnType<typeof Select>;
@@ -6,9 +6,8 @@
6
6
  name,
7
7
  label,
8
8
  value = $bindable('false'),
9
- validations,
10
- inputs
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>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joyautomation/salt",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "scripts": {
5
5
  "generate:icons": "node scripts/generate-icons.js",
6
6
  "dev": "vite dev",