@stubber/form-fields 2.2.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +63 -25
  2. package/dist/form-fields/Autocomplete.svelte +261 -0
  3. package/dist/form-fields/Autocomplete.svelte.d.ts +43 -0
  4. package/dist/form-fields/CollapsibleSection.svelte +127 -0
  5. package/dist/form-fields/CollapsibleSection.svelte.d.ts +26 -0
  6. package/dist/form-fields/FieldLabel.svelte +18 -3
  7. package/dist/form-fields/FieldLabel.svelte.d.ts +1 -0
  8. package/dist/form-fields/FieldMessage.svelte +20 -9
  9. package/dist/form-fields/Form.svelte +19 -2
  10. package/dist/form-fields/Form.svelte.d.ts +2 -0
  11. package/dist/form-fields/UploadStatus.svelte +76 -0
  12. package/dist/form-fields/UploadStatus.svelte.d.ts +18 -0
  13. package/dist/form-fields/fields/_field-builder-reorder.svelte +35 -0
  14. package/dist/form-fields/fields/_field-builder-reorder.svelte.d.ts +22 -0
  15. package/dist/form-fields/{sub → fields}/array-builder-field.svelte +104 -38
  16. package/dist/form-fields/fields/checkbox-autocomplete.svelte +153 -0
  17. package/dist/form-fields/fields/checkbox-field.svelte +69 -0
  18. package/dist/form-fields/{sub → fields}/contact-selector-field.svelte +9 -6
  19. package/dist/form-fields/{sub → fields}/currency-field.svelte +108 -70
  20. package/dist/form-fields/fields/data-indication-field.svelte +39 -0
  21. package/dist/form-fields/fields/date-field.svelte +31 -0
  22. package/dist/form-fields/fields/date-time-field.svelte +31 -0
  23. package/dist/form-fields/{sub → fields}/email-field.svelte +25 -11
  24. package/dist/form-fields/fields/field-builder-field.svelte +568 -0
  25. package/dist/form-fields/fields/file-field.svelte +284 -0
  26. package/dist/form-fields/{sub → fields}/file-field.svelte.d.ts +2 -0
  27. package/dist/form-fields/fields/grid-field.svelte +152 -0
  28. package/dist/form-fields/{sub → fields}/heading-field.svelte +7 -4
  29. package/dist/form-fields/{sub → fields}/html-field.svelte +6 -4
  30. package/dist/form-fields/{sub → fields}/map-field.svelte +122 -78
  31. package/dist/form-fields/{sub → fields}/multi-checkbox-field.svelte +35 -20
  32. package/dist/form-fields/fields/multistep-field.svelte +135 -0
  33. package/dist/form-fields/fields/note-field.svelte +35 -0
  34. package/dist/form-fields/{sub → fields}/number-field.svelte +29 -14
  35. package/dist/form-fields/fields/object-builder-field.svelte +176 -0
  36. package/dist/form-fields/fields/qr-code-scanner-field.svelte +203 -0
  37. package/dist/form-fields/fields/radio-field.svelte +82 -0
  38. package/dist/form-fields/{sub → fields}/radio-field.svelte.d.ts +1 -1
  39. package/dist/form-fields/fields/screenrecorder-field.svelte +255 -0
  40. package/dist/form-fields/{sub → fields}/screenshot-field.svelte +101 -40
  41. package/dist/form-fields/{sub → fields}/scroll-and-read-display-field.svelte +43 -27
  42. package/dist/form-fields/fields/section-field.svelte +73 -0
  43. package/dist/form-fields/fields/select-field.svelte +104 -0
  44. package/dist/form-fields/{sub → fields}/signature-field.svelte +78 -22
  45. package/dist/form-fields/fields/slider-field.svelte +53 -0
  46. package/dist/form-fields/{sub → fields}/smart-text-field.svelte +62 -82
  47. package/dist/form-fields/fields/telephone-field.svelte +108 -0
  48. package/dist/form-fields/{sub → fields}/telephone-field.svelte.d.ts +1 -1
  49. package/dist/form-fields/{sub → fields}/text-field.svelte +28 -11
  50. package/dist/form-fields/{sub → fields}/voicenote-field.svelte +117 -46
  51. package/dist/form-fields/file_creation_options.d.ts +1 -1
  52. package/dist/form-fields/form-field.svelte +8 -1
  53. package/dist/form-fields/icons/ChevronDownIcon.svelte +21 -0
  54. package/dist/form-fields/icons/ChevronDownIcon.svelte.d.ts +20 -0
  55. package/dist/form-fields/icons/QrCodeScannerIcon.svelte +25 -0
  56. package/dist/form-fields/icons/QrCodeScannerIcon.svelte.d.ts +19 -0
  57. package/dist/form-fields/icons/ScreenRecordingIcon.svelte +21 -0
  58. package/dist/form-fields/icons/ScreenRecordingIcon.svelte.d.ts +19 -0
  59. package/dist/form-fields/icons/ScreenshotIcon.svelte +22 -0
  60. package/dist/form-fields/icons/ScreenshotIcon.svelte.d.ts +19 -0
  61. package/dist/form-fields/index.d.ts +5 -1
  62. package/dist/form-fields/index.js +3 -1
  63. package/dist/form-fields/interfaces.d.ts +63 -63
  64. package/dist/form-fields/interfaces.js +42 -39
  65. package/dist/form-fields/intl-tel-input.css +1 -0
  66. package/dist/form-fields/styles.d.ts +17 -0
  67. package/dist/form-fields/styles.js +4 -0
  68. package/package.json +2 -4
  69. package/dist/form-fields/sub/checkbox-autocomplete.svelte +0 -56
  70. package/dist/form-fields/sub/checkbox-field.svelte +0 -42
  71. package/dist/form-fields/sub/data-indication-field.svelte +0 -19
  72. package/dist/form-fields/sub/date-field.svelte +0 -31
  73. package/dist/form-fields/sub/date-time-field.svelte +0 -31
  74. package/dist/form-fields/sub/field-builder-field.svelte +0 -657
  75. package/dist/form-fields/sub/file-field.svelte +0 -159
  76. package/dist/form-fields/sub/grid-field.svelte +0 -69
  77. package/dist/form-fields/sub/multistep-field.svelte +0 -70
  78. package/dist/form-fields/sub/note-field.svelte +0 -18
  79. package/dist/form-fields/sub/object-builder-field.svelte +0 -134
  80. package/dist/form-fields/sub/qr-code-scanner-field.svelte +0 -84
  81. package/dist/form-fields/sub/radio-field.svelte +0 -69
  82. package/dist/form-fields/sub/screenrecorder-field.svelte +0 -178
  83. package/dist/form-fields/sub/section-field.svelte +0 -50
  84. package/dist/form-fields/sub/select-field.svelte +0 -150
  85. package/dist/form-fields/sub/slider-field.svelte +0 -28
  86. package/dist/form-fields/sub/telephone-field.svelte +0 -64
  87. /package/dist/form-fields/{sub → fields}/array-builder-field.svelte.d.ts +0 -0
  88. /package/dist/form-fields/{sub → fields}/checkbox-autocomplete.svelte.d.ts +0 -0
  89. /package/dist/form-fields/{sub → fields}/checkbox-field.svelte.d.ts +0 -0
  90. /package/dist/form-fields/{sub → fields}/code-field.svelte +0 -0
  91. /package/dist/form-fields/{sub → fields}/code-field.svelte.d.ts +0 -0
  92. /package/dist/form-fields/{sub → fields}/contact-selector-field.svelte.d.ts +0 -0
  93. /package/dist/form-fields/{sub → fields}/currency-field.svelte.d.ts +0 -0
  94. /package/dist/form-fields/{sub → fields}/data-indication-field.svelte.d.ts +0 -0
  95. /package/dist/form-fields/{sub → fields}/date-field.svelte.d.ts +0 -0
  96. /package/dist/form-fields/{sub → fields}/date-time-field.svelte.d.ts +0 -0
  97. /package/dist/form-fields/{sub → fields}/email-field.svelte.d.ts +0 -0
  98. /package/dist/form-fields/{sub → fields}/field-builder-field.svelte.d.ts +0 -0
  99. /package/dist/form-fields/{sub → fields}/grid-field.svelte.d.ts +0 -0
  100. /package/dist/form-fields/{sub → fields}/heading-field.svelte.d.ts +0 -0
  101. /package/dist/form-fields/{sub → fields}/hidden-field.svelte +0 -0
  102. /package/dist/form-fields/{sub → fields}/hidden-field.svelte.d.ts +0 -0
  103. /package/dist/form-fields/{sub → fields}/hidden-location-field.svelte +0 -0
  104. /package/dist/form-fields/{sub → fields}/hidden-location-field.svelte.d.ts +0 -0
  105. /package/dist/form-fields/{sub → fields}/html-field.svelte.d.ts +0 -0
  106. /package/dist/form-fields/{sub → fields}/jsoneditor-field.svelte +0 -0
  107. /package/dist/form-fields/{sub → fields}/jsoneditor-field.svelte.d.ts +0 -0
  108. /package/dist/form-fields/{sub → fields}/map-field.svelte.d.ts +0 -0
  109. /package/dist/form-fields/{sub → fields}/multi-checkbox-field.svelte.d.ts +0 -0
  110. /package/dist/form-fields/{sub → fields}/multistep-field.svelte.d.ts +0 -0
  111. /package/dist/form-fields/{sub → fields}/note-field.svelte.d.ts +0 -0
  112. /package/dist/form-fields/{sub → fields}/number-field.svelte.d.ts +0 -0
  113. /package/dist/form-fields/{sub → fields}/object-builder-field.svelte.d.ts +0 -0
  114. /package/dist/form-fields/{sub → fields}/qr-code-scanner-field.svelte.d.ts +0 -0
  115. /package/dist/form-fields/{sub → fields}/screenrecorder-field.svelte.d.ts +0 -0
  116. /package/dist/form-fields/{sub → fields}/screenshot-field.svelte.d.ts +0 -0
  117. /package/dist/form-fields/{sub → fields}/scroll-and-read-display-field.svelte.d.ts +0 -0
  118. /package/dist/form-fields/{sub → fields}/section-field.svelte.d.ts +0 -0
  119. /package/dist/form-fields/{sub → fields}/select-field.svelte.d.ts +0 -0
  120. /package/dist/form-fields/{sub → fields}/selectresource-field.svelte +0 -0
  121. /package/dist/form-fields/{sub → fields}/selectresource-field.svelte.d.ts +0 -0
  122. /package/dist/form-fields/{sub → fields}/signature-field.svelte.d.ts +0 -0
  123. /package/dist/form-fields/{sub → fields}/slider-field.svelte.d.ts +0 -0
  124. /package/dist/form-fields/{sub → fields}/smart-text-field.svelte.d.ts +0 -0
  125. /package/dist/form-fields/{sub → fields}/text-field.svelte.d.ts +0 -0
  126. /package/dist/form-fields/{sub → fields}/voicenote-field.svelte.d.ts +0 -0
@@ -30,12 +30,13 @@ export const map_field_param_spec = {
30
30
  </script>
31
31
 
32
32
  <script>import { controls, Geocoder, Map, Marker } from "@beyonk/svelte-mapbox";
33
+ import { getContext } from "svelte";
33
34
  import FieldLabel from "../FieldLabel.svelte";
34
35
  import { mapValues, omit } from "lodash-es";
35
- import { Input } from "@stubber/ui/input";
36
- import { Label } from "@stubber/ui/label";
36
+ import { STYLES_CONTEXT, classNames } from "../styles";
37
37
  const { GeolocateControl } = controls;
38
38
  export let fieldStore;
39
+ const styles = getContext(STYLES_CONTEXT);
39
40
  const initial_value = $fieldStore.value;
40
41
  let params = $fieldStore.params;
41
42
  let reverse_geocode_enabled = params?.address_mode && params?.address_mode !== "none";
@@ -123,6 +124,7 @@ function handle_geocoder_result(r) {
123
124
  const geo_center = r?.detail?.result?.center || [];
124
125
  if (geo_center.length === 2) {
125
126
  center = [geo_center[0], geo_center[1]];
127
+ setValueCoords(geo_center[1], geo_center[0]);
126
128
  mapComponent?.flyTo({ center });
127
129
  }
128
130
  }
@@ -132,7 +134,7 @@ function handle_geolocate(o) {
132
134
  }
133
135
  function handle_address_change(e) {
134
136
  const new_address = e?.target?.value || "";
135
- if (new_address && $fieldStore.value.address.full_address !== new_address) {
137
+ if (new_address && $fieldStore.value?.address?.full_address !== new_address) {
136
138
  $fieldStore.value = {
137
139
  ...$fieldStore.value,
138
140
  address: {
@@ -144,93 +146,135 @@ function handle_address_change(e) {
144
146
  }
145
147
  </script>
146
148
 
147
- <FieldLabel {fieldStore} />
148
- {#if mapboxAccessToken}
149
- <div class="flex w-full flex-col">
150
- <div>
149
+ <div class="map-field">
150
+ <FieldLabel {fieldStore} />
151
+ {#if mapboxAccessToken}
152
+ <div class="map-actions">
151
153
  <button
152
154
  type="button"
153
155
  on:click={resetValues}
154
- class="text-sm text-surface-300 hover:text-surface-500 hover:underline"
156
+ class={classNames("map-reset", $styles.button)}
157
+ data-variant="destructive">Reset</button
155
158
  >
156
- <i class="fa-regular fa-undo" /> Reset
157
- </button>
158
159
  </div>
159
- <div class="relative mt-2 rounded-md">
160
- <div class="h-72 w-full">
161
- <Map
162
- bind:center
163
- zoom={14}
164
- accessToken={mapboxAccessToken}
165
- style={mapboxStylesheetUrl}
166
- bind:this={mapComponent}
167
- on:click={handle_map_click}
168
- >
169
- <div class="absolute left-2 top-2">
170
- <Geocoder
171
- on:result={handle_geocoder_result}
172
- accessToken={mapboxAccessToken}
173
- {geocoder}
174
- />
175
- </div>
176
- {#if !hide_geolocate_button}
177
- <GeolocateControl position="top-right" on:geolocate={handle_geolocate} />
178
- {/if}
179
- {#if browserLocation}
180
- <Marker
181
- lat={browserLocation.lat}
182
- lng={browserLocation.lng}
183
- markerOptions={{ anchor: "center" }}
184
- >
185
- <div class="relative flex flex-col items-center">
186
- <span class="fa-stack fa-2x">
187
- <i
188
- class="fa-solid fa-circle fa-stack-2x text-[1.3em] leading-[inherit] text-white"
189
- />
190
- <i class="fa-solid fa-user fa-stack-1x text-blue-600" />
191
- </span>
160
+ <div
161
+ class="map-viewport"
162
+ id={params?.address_mode === "editable" ? undefined : $fieldStore.id}
163
+ aria-invalid={$fieldStore.validation_result?.type === "error" ? true : undefined}
164
+ aria-describedby="message-{$fieldStore.id}"
165
+ >
166
+ <Map
167
+ bind:center
168
+ zoom={14}
169
+ accessToken={mapboxAccessToken}
170
+ style={mapboxStylesheetUrl}
171
+ bind:this={mapComponent}
172
+ on:click={handle_map_click}
173
+ >
174
+ <div class="map-search">
175
+ <Geocoder on:result={handle_geocoder_result} accessToken={mapboxAccessToken} {geocoder} />
176
+ </div>
177
+ {#if !hide_geolocate_button}
178
+ <GeolocateControl position="top-right" on:geolocate={handle_geolocate} />
179
+ {/if}
180
+ {#if browserLocation}
181
+ <Marker
182
+ lat={browserLocation.lat}
183
+ lng={browserLocation.lng}
184
+ markerOptions={{ anchor: "center" }}
185
+ >
186
+ <span class="map-browser-marker" aria-hidden="true">
187
+ <svg class="map-browser-marker-icon" viewBox="0 0 24 24">
188
+ <circle cx="12" cy="12" r="10" fill="currentColor" />
189
+ <circle cx="12" cy="9" r="3" fill="white" />
190
+ <path d="M6.5 18c.8-3 2.6-4.5 5.5-4.5s4.7 1.5 5.5 4.5" fill="white" />
191
+ </svg>
192
+ </span>
193
+ </Marker>
194
+ {/if}
195
+ {#if $fieldStore.value?.lat && $fieldStore.value?.lng}
196
+ <Marker
197
+ lat={$fieldStore.value.lat}
198
+ lng={$fieldStore.value.lng}
199
+ popup={false}
200
+ markerOptions={{ anchor: "bottom" }}
201
+ >
202
+ <div class="map-location-marker">
203
+ <div class="map-coordinates">
204
+ <div>lat: {$fieldStore.value.lat?.toString()?.substring(0, 7)}</div>
205
+ <div>lng: {$fieldStore.value.lng?.toString()?.substring(0, 7)}</div>
192
206
  </div>
193
- </Marker>
194
- {/if}
195
- {#if $fieldStore.value?.lat && $fieldStore.value?.lng}
196
- <Marker
197
- lat={$fieldStore.value.lat}
198
- lng={$fieldStore.value.lng}
199
- popup={false}
200
- markerOptions={{ anchor: "bottom" }}
201
- >
202
- <div class="relative flex flex-col items-center">
203
- <div
204
- class="rounded-xl bg-white px-2 py-1 text-sm font-medium text-slate-800 shadow-lg ring-1 ring-black/20"
205
- >
206
- <div>lat: {$fieldStore.value.lat?.toString()?.substring(0, 7)}</div>
207
- <div>lng: {$fieldStore.value.lng?.toString()?.substring(0, 7)}</div>
208
- </div>
209
-
210
- <span class="fa-stack fa-2x h-[1.5em]">
211
- <i
212
- class="fa-solid fa-location-pin fa-stack-2x text-[1.2em] leading-[inherit] text-white"
207
+ <span aria-hidden="true">
208
+ <svg class="map-location-marker-icon" viewBox="0 0 24 32">
209
+ <path
210
+ d="M12 31s10-10.2 10-19A10 10 0 0 0 2 12c0 8.8 10 19 10 19Z"
211
+ fill="currentColor"
213
212
  />
214
- <i class="fa-solid fa-location-dot fa-stack-1x text-green-600" />
215
- </span>
216
- </div>
217
- </Marker>
218
- {/if}
219
- </Map>
220
- </div>
213
+ <circle cx="12" cy="12" r="4" fill="white" />
214
+ </svg>
215
+ </span>
216
+ </div>
217
+ </Marker>
218
+ {/if}
219
+ </Map>
221
220
  </div>
222
221
  {#if params?.address_mode === "editable"}
223
- <Input
222
+ <input
223
+ id={$fieldStore.id}
224
224
  type="text"
225
- class="mt-1"
225
+ class={classNames("map-address-input", $styles.input)}
226
226
  value={$fieldStore.value?.address?.full_address || ".."}
227
227
  on:change={handle_address_change}
228
+ aria-invalid={$fieldStore.validation_result?.type === "error" ? true : undefined}
229
+ aria-describedby="message-{$fieldStore.id}"
228
230
  />
229
231
  {:else if params?.address_mode === "readonly"}
230
- <Label class="mt-1 block text-sm font-medium text-surface-700"
231
- >{$fieldStore.value?.address?.full_address || ".."}</Label
232
- >
232
+ <p class="map-address">{$fieldStore.value?.address?.full_address || ".."}</p>
233
233
  {/if}
234
- </div>
235
- {/if}
236
- <FieldMessage {fieldStore} />
234
+ {/if}
235
+ <FieldMessage {fieldStore} />
236
+ </div>
237
+
238
+ <style>
239
+ .map-field,
240
+ .map-viewport,
241
+ :where(.map-address-input) {
242
+ box-sizing: border-box;
243
+ width: 100%;
244
+ min-width: 0;
245
+ }
246
+
247
+ .map-viewport {
248
+ position: relative;
249
+ height: 24rem;
250
+ overflow: hidden;
251
+ }
252
+
253
+ .map-search {
254
+ position: absolute;
255
+ z-index: 1;
256
+ inset-block-start: 0;
257
+ inset-inline-start: 0;
258
+ max-width: 100%;
259
+ }
260
+
261
+ .map-browser-marker-icon,
262
+ .map-location-marker-icon {
263
+ display: block;
264
+ width: 1.5rem;
265
+ }
266
+
267
+ .map-browser-marker-icon {
268
+ height: 1.5rem;
269
+ }
270
+
271
+ .map-location-marker {
272
+ display: flex;
273
+ flex-direction: column;
274
+ align-items: center;
275
+ }
276
+
277
+ .map-location-marker-icon {
278
+ height: 2rem;
279
+ }
280
+ </style>
@@ -25,12 +25,13 @@ export const multi_checkbox_field_param_spec = {
25
25
  };
26
26
  </script>
27
27
 
28
- <script>import { Checkbox } from "@stubber/ui/checkbox";
29
- import { Label } from "@stubber/ui/label";
30
- import { cloneDeep, startCase } from "lodash-es";
28
+ <script>import { cloneDeep, startCase } from "lodash-es";
29
+ import { getContext } from "svelte";
31
30
  import FieldLabel from "../FieldLabel.svelte";
32
31
  import FieldMessage from "../FieldMessage.svelte";
32
+ import { STYLES_CONTEXT, classNames } from "../styles";
33
33
  export let fieldStore;
34
+ const styles = getContext(STYLES_CONTEXT);
34
35
  let options = $fieldStore.params?.options || {};
35
36
  if ($fieldStore.value) {
36
37
  const newValue = {};
@@ -62,22 +63,36 @@ const handle_check_change = (key, item, checked) => {
62
63
  };
63
64
  $fieldStore.value = cloneDeep({ ...$fieldStore.value });
64
65
  };
66
+ const handle_change = (key, item, event) => {
67
+ handle_check_change(key, item, event.currentTarget.checked);
68
+ };
65
69
  </script>
66
70
 
67
- <FieldLabel {fieldStore} />
68
- {#each Object.entries(options) as [key, item] (key)}
69
- {@const checked = $fieldStore.value?.[key]?.checked ?? false}
70
- <div class="flex space-x-3 relative mt-2">
71
- <Checkbox
72
- id="{$fieldStore.id}_{key}"
73
- name={key}
74
- {checked}
75
- onCheckedChange={(checked) => handle_check_change(key, item, checked)}
76
- />
77
-
78
- <Label for="{$fieldStore.id}_{key}">
79
- {item.label || startCase(key)}
80
- </Label>
81
- </div>
82
- {/each}
83
- <FieldMessage {fieldStore} />
71
+ <div class="multi-checkbox-field">
72
+ <FieldLabel {fieldStore} />
73
+ <fieldset
74
+ class="multi-checkbox-options"
75
+ class:multi-checkbox-options-invalid={$fieldStore.validation_result?.type === "error"}
76
+ aria-label={$fieldStore.label}
77
+ aria-describedby="message-{$fieldStore.id}"
78
+ >
79
+ {#each Object.entries(options) as [key, item] (key)}
80
+ {@const checked = Boolean($fieldStore.value?.[key]?.checked)}
81
+ <div class="multi-checkbox-option" class:multi-checkbox-option-checked={checked}>
82
+ <input
83
+ class={classNames("multi-checkbox-input", $styles.choice)}
84
+ id="{$fieldStore.id}_{key}"
85
+ name={key}
86
+ type="checkbox"
87
+ {checked}
88
+ aria-invalid={$fieldStore.validation_result?.type === "error" ? true : undefined}
89
+ on:change={(event) => handle_change(key, item, event)}
90
+ />
91
+ <label class="multi-checkbox-label" for="{$fieldStore.id}_{key}">
92
+ {item.label || startCase(key)}
93
+ </label>
94
+ </div>
95
+ {/each}
96
+ </fieldset>
97
+ <FieldMessage {fieldStore} />
98
+ </div>
@@ -0,0 +1,135 @@
1
+ <script context="module">import { get } from "svelte/store";
2
+ import {} from "../interfaces";
3
+ </script>
4
+
5
+ <script>import { getContext, tick } from "svelte";
6
+ import FieldLabel from "../FieldLabel.svelte";
7
+ import FieldMessage from "../FieldMessage.svelte";
8
+ import FormField from "../form-field.svelte";
9
+ import { STYLES_CONTEXT, classNames } from "../styles";
10
+ export let fieldStore;
11
+ const styles = getContext(STYLES_CONTEXT);
12
+ $: fields = $fieldStore.sub_fields || [];
13
+ let allChildrenConditionsChecked = true;
14
+ let current_page_index = 0;
15
+ let stepButtons = [];
16
+ $: if (fields.length && current_page_index >= fields.length) {
17
+ current_page_index = fields.length - 1;
18
+ }
19
+ async function focus_page(index) {
20
+ current_page_index = index;
21
+ await tick();
22
+ stepButtons[index]?.focus();
23
+ }
24
+ function handle_step_keydown(event, index) {
25
+ let nextIndex;
26
+ if (event.key === "ArrowRight") nextIndex = (index + 1) % fields.length;
27
+ if (event.key === "ArrowLeft") nextIndex = (index - 1 + fields.length) % fields.length;
28
+ if (event.key === "Home") nextIndex = 0;
29
+ if (event.key === "End") nextIndex = fields.length - 1;
30
+ if (nextIndex !== void 0) {
31
+ event.preventDefault();
32
+ void focus_page(nextIndex);
33
+ }
34
+ }
35
+ </script>
36
+
37
+ <FieldLabel {fieldStore} />
38
+
39
+ {#if fields?.length}
40
+ <div
41
+ class="multistep-field"
42
+ class:multistep-field-hidden={!allChildrenConditionsChecked}
43
+ hidden={!allChildrenConditionsChecked}
44
+ >
45
+ <div class="multistep-steps" role="tablist" aria-label={`${$fieldStore.label} steps`}>
46
+ {#each fields as page, index (get(page).id)}
47
+ <button
48
+ bind:this={stepButtons[index]}
49
+ type="button"
50
+ id="tab-{$fieldStore.id}-{index}"
51
+ class={classNames("multistep-step", $styles.button)}
52
+ class:multistep-step-current={current_page_index === index}
53
+ role="tab"
54
+ aria-selected={current_page_index === index}
55
+ aria-controls="panel-{$fieldStore.id}-{index}"
56
+ tabindex={current_page_index === index ? 0 : -1}
57
+ on:click={() => (current_page_index = index)}
58
+ on:keydown={(event) => handle_step_keydown(event, index)}
59
+ >
60
+ <span
61
+ class="multistep-marker"
62
+ class:multistep-marker-current={current_page_index === index}
63
+ aria-hidden="true"
64
+ >
65
+ {index + 1}
66
+ </span>
67
+ <span class="multistep-step-label">{get(page).label}</span>
68
+ </button>
69
+ {/each}
70
+ </div>
71
+
72
+ <div class="multistep-panels">
73
+ {#each fields as sub_field, index (get(sub_field).id)}
74
+ <div
75
+ id="panel-{$fieldStore.id}-{index}"
76
+ class="multistep-panel"
77
+ class:multistep-panel-hidden={current_page_index !== index}
78
+ role="tabpanel"
79
+ aria-labelledby="tab-{$fieldStore.id}-{index}"
80
+ hidden={current_page_index !== index}
81
+ tabindex="0"
82
+ >
83
+ <FormField fieldStore={sub_field} />
84
+ </div>
85
+ {/each}
86
+ </div>
87
+
88
+ <div class="multistep-actions">
89
+ {#if current_page_index > 0}
90
+ <button
91
+ type="button"
92
+ class={classNames("multistep-back", $styles.button)}
93
+ on:click={() => (current_page_index = current_page_index - 1)}
94
+ >
95
+ Back
96
+ </button>
97
+ {/if}
98
+ {#if current_page_index < fields.length - 1}
99
+ <button
100
+ type="button"
101
+ class={classNames("multistep-next", $styles.button)}
102
+ data-variant="primary"
103
+ on:click={() => (current_page_index = current_page_index + 1)}
104
+ >
105
+ Next
106
+ </button>
107
+ {/if}
108
+ </div>
109
+ <FieldMessage {fieldStore} />
110
+ </div>
111
+ {/if}
112
+
113
+ <style>
114
+ .multistep-field,
115
+ .multistep-panels,
116
+ .multistep-panel {
117
+ box-sizing: border-box;
118
+ width: 100%;
119
+ min-width: 0;
120
+ }
121
+
122
+ .multistep-steps {
123
+ display: flex;
124
+ width: 100%;
125
+ overflow-x: auto;
126
+ }
127
+
128
+ .multistep-step {
129
+ flex: none;
130
+ }
131
+
132
+ .multistep-actions {
133
+ display: flex;
134
+ }
135
+ </style>
@@ -0,0 +1,35 @@
1
+ <script context="module">import {} from "../interfaces";
2
+ </script>
3
+
4
+ <script>import { getContext } from "svelte";
5
+ import FieldLabel from "../FieldLabel.svelte";
6
+ import FieldMessage from "../FieldMessage.svelte";
7
+ import { STYLES_CONTEXT, classNames } from "../styles";
8
+ export let fieldStore;
9
+ const styles = getContext(STYLES_CONTEXT);
10
+ </script>
11
+
12
+ <div class="note-field">
13
+ <FieldLabel {fieldStore} />
14
+ <textarea
15
+ class={classNames("note-input", $styles?.input)}
16
+ id={$fieldStore.id}
17
+ bind:value={$fieldStore.value}
18
+ placeholder={$fieldStore.label}
19
+ aria-invalid={$fieldStore.validation_result?.type === "error" ? true : undefined}
20
+ aria-describedby={$fieldStore.validation_result ? `message-${$fieldStore.id}` : undefined}
21
+ />
22
+ <FieldMessage {fieldStore} />
23
+ </div>
24
+
25
+ <style>
26
+ .note-field,
27
+ .note-input {
28
+ box-sizing: border-box;
29
+ width: 100%;
30
+ }
31
+
32
+ .note-input {
33
+ min-height: 100px;
34
+ }
35
+ </style>
@@ -36,10 +36,12 @@
36
36
  };
37
37
  </script>
38
38
 
39
- <script>import { Input } from "@stubber/ui/input";
39
+ <script>import { getContext } from "svelte";
40
40
  import FieldLabel from "../FieldLabel.svelte";
41
41
  import FieldMessage from "../FieldMessage.svelte";
42
+ import { STYLES_CONTEXT, classNames } from "../styles";
42
43
  export let fieldStore;
44
+ const styles = getContext(STYLES_CONTEXT);
43
45
  function onInput(e) {
44
46
  const input_element = e.target;
45
47
  let { value } = input_element;
@@ -62,16 +64,29 @@ function onBlur() {
62
64
  }
63
65
  </script>
64
66
 
65
- <FieldLabel {fieldStore} />
66
- <Input
67
- id={$fieldStore.id}
68
- value={$fieldStore.value}
69
- on:input={onInput}
70
- on:blur={onBlur}
71
- aria-invalid={$fieldStore.validation_result?.type == "error" ? true : undefined}
72
- type="number"
73
- step={$fieldStore.step || 0}
74
- max={$fieldStore.max}
75
- min={$fieldStore.min}
76
- />
77
- <FieldMessage {fieldStore} />
67
+ <div class="number-field">
68
+ <FieldLabel {fieldStore} />
69
+ <input
70
+ class={classNames("number-input", $styles.input)}
71
+ id={$fieldStore.id}
72
+ value={$fieldStore.value ?? ""}
73
+ placeholder={$fieldStore.label}
74
+ on:input={onInput}
75
+ on:blur={onBlur}
76
+ aria-invalid={$fieldStore.validation_result?.type == "error" ? true : undefined}
77
+ aria-describedby="message-{$fieldStore.id}"
78
+ type="number"
79
+ step={$fieldStore.step}
80
+ max={$fieldStore.max}
81
+ min={$fieldStore.min}
82
+ />
83
+ <FieldMessage {fieldStore} />
84
+ </div>
85
+
86
+ <style>
87
+ .number-field,
88
+ .number-input {
89
+ box-sizing: border-box;
90
+ width: 100%;
91
+ }
92
+ </style>