@stubber/form-fields 2.2.6 → 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 +121 -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
@@ -0,0 +1,176 @@
1
+ <script context="module">import {} from "svelte/store";
2
+ import {} from "../interfaces";
3
+ export const object_builder_field_param_spec = {
4
+ spec: {
5
+ fields: {
6
+ value_field_spec: {
7
+ fieldtype: "fieldbuilder",
8
+ help: "Field specification for the object values",
9
+ params: {}
10
+ }
11
+ }
12
+ }
13
+ };
14
+ </script>
15
+
16
+ <script>import { cloneDeep } from "lodash-es";
17
+ import { getContext } from "svelte";
18
+ import FieldExprToggle from "../FieldExprToggle.svelte";
19
+ import FieldMessage from "../FieldMessage.svelte";
20
+ import FormField from "../form-field.svelte";
21
+ import CollapsibleSection from "../CollapsibleSection.svelte";
22
+ import { STYLES_CONTEXT, classNames } from "../styles";
23
+ import { build_field } from "../utils";
24
+ export let fieldStore;
25
+ const styles = getContext(STYLES_CONTEXT);
26
+ let value_field_spec = $fieldStore.params?.value_field_spec;
27
+ const handle_key_input = (old_key, e) => {
28
+ const target = e.currentTarget;
29
+ const new_key = make_key_unique(target.value);
30
+ if (new_key === old_key) return;
31
+ const prev_value = $fieldStore.value || {};
32
+ const new_value = {};
33
+ for (const [key, value] of Object.entries(prev_value)) {
34
+ if (key === old_key) {
35
+ new_value[new_key] = value;
36
+ } else {
37
+ new_value[key] = value;
38
+ }
39
+ }
40
+ item_open[new_key] = item_open[old_key] ?? true;
41
+ delete item_open[old_key];
42
+ item_open = { ...item_open };
43
+ $fieldStore.value = new_value;
44
+ };
45
+ const add_value = () => {
46
+ const unique_key = make_key_unique("");
47
+ const field_value = cloneDeep($fieldStore.value || {});
48
+ field_value[unique_key] = null;
49
+ $fieldStore.value = field_value;
50
+ };
51
+ const remove_value = (key) => {
52
+ const field_value = cloneDeep($fieldStore.value || {});
53
+ delete field_value[key];
54
+ delete item_open[key];
55
+ item_open = { ...item_open };
56
+ $fieldStore.value = field_value;
57
+ };
58
+ const make_key_unique = (key) => {
59
+ let unique_key = key;
60
+ let counter = 1;
61
+ const keys = Object.keys($fieldStore.value || {});
62
+ while (keys.includes(unique_key)) {
63
+ unique_key = `${key}_${counter}`;
64
+ counter++;
65
+ }
66
+ return unique_key;
67
+ };
68
+ let open = true;
69
+ let item_open = {};
70
+ function is_item_open(key) {
71
+ return item_open[key] ?? true;
72
+ }
73
+ function set_item_open(key, open2) {
74
+ item_open = { ...item_open, [key]: open2 };
75
+ }
76
+ </script>
77
+
78
+ <div
79
+ class="object-builder-field"
80
+ class:object-builder-field-invalid={$fieldStore.validation_result?.type === "error"}
81
+ >
82
+ <CollapsibleSection
83
+ title={$fieldStore.label}
84
+ bind:open
85
+ contentId="content-{$fieldStore.id}"
86
+ labelledBy="label-{$fieldStore.id}"
87
+ >
88
+ <div slot="heading" class="object-builder-heading">
89
+ <span id="label-{$fieldStore.id}" class={classNames("object-builder-label", $styles.label)}>
90
+ {$fieldStore.label}
91
+ </span>
92
+ <FieldMessage {fieldStore} />
93
+ </div>
94
+ <div slot="actions" class="object-builder-actions">
95
+ <FieldExprToggle {fieldStore} />
96
+ </div>
97
+ {#each Object.keys($fieldStore.value || {}) as key, index (index)}
98
+ <div class="object-builder-item">
99
+ <CollapsibleSection
100
+ title={key || "Item"}
101
+ open={is_item_open(key)}
102
+ onToggle={(next_open) => set_item_open(key, next_open)}
103
+ contentId="value-{$fieldStore.id}-{index}"
104
+ >
105
+ <button
106
+ slot="actions"
107
+ type="button"
108
+ class={classNames("object-builder-remove", $styles.button)}
109
+ data-variant="destructive"
110
+ data-size="icon"
111
+ on:click={() => remove_value(key)}
112
+ aria-label="Remove {key || 'item'}"
113
+ >
114
+ <span aria-hidden="true">×</span>
115
+ </button>
116
+ <input
117
+ class={classNames("object-builder-key-input", $styles.input)}
118
+ aria-label="Key for {key || 'item'}"
119
+ placeholder="Key"
120
+ value={key}
121
+ on:input={(e) => handle_key_input(key, e)}
122
+ />
123
+ {@const built_value_field = build_field(
124
+ $fieldStore.formStore,
125
+ $fieldStore.attachmentsStore,
126
+ $fieldStore.formDependencies,
127
+ key,
128
+ value_field_spec || { fieldtype: "text", title: "Value", hide_label: true },
129
+ $fieldStore.data_path
130
+ )}
131
+ <div class="object-builder-value">
132
+ <FormField fieldStore={built_value_field} />
133
+ </div>
134
+ </CollapsibleSection>
135
+ </div>
136
+ {/each}
137
+
138
+ <div class="object-builder-footer">
139
+ <button
140
+ type="button"
141
+ class={classNames("object-builder-add", $styles.button)}
142
+ data-variant="primary"
143
+ on:click={add_value}
144
+ >
145
+ <span aria-hidden="true">+</span>
146
+ <span>Add item</span>
147
+ </button>
148
+ </div>
149
+ </CollapsibleSection>
150
+ </div>
151
+
152
+ <style>
153
+ .object-builder-field,
154
+ .object-builder-item,
155
+ .object-builder-value {
156
+ box-sizing: border-box;
157
+ width: 100%;
158
+ min-width: 0;
159
+ }
160
+
161
+ .object-builder-heading,
162
+ .object-builder-key-input {
163
+ flex: 1;
164
+ min-width: 0;
165
+ }
166
+
167
+ .object-builder-actions,
168
+ .object-builder-remove {
169
+ flex: none;
170
+ }
171
+
172
+ .object-builder-actions {
173
+ display: flex;
174
+ align-items: center;
175
+ }
176
+ </style>
@@ -0,0 +1,203 @@
1
+ <script context="module">import {} from "../interfaces";
2
+ </script>
3
+
4
+ <script>import { getContext, onDestroy } from "svelte";
5
+ import FieldLabel from "../FieldLabel.svelte";
6
+ import FieldMessage from "../FieldMessage.svelte";
7
+ import QrCodeScannerIcon from "../icons/QrCodeScannerIcon.svelte";
8
+ import { STYLES_CONTEXT, classNames } from "../styles";
9
+ export let fieldStore;
10
+ const styles = getContext(STYLES_CONTEXT);
11
+ let dialog;
12
+ let qr_code_lib;
13
+ let scanner_status = "loading";
14
+ let scan_success = false;
15
+ let scanned_text = "";
16
+ let scanner_generation = 0;
17
+ $: reader_id = `${$fieldStore.id}-reader`;
18
+ onDestroy(() => {
19
+ scanner_generation += 1;
20
+ void stop_scanner();
21
+ });
22
+ async function stop_scanner() {
23
+ const scanner = qr_code_lib;
24
+ qr_code_lib = void 0;
25
+ if (!scanner) return;
26
+ try {
27
+ await scanner.stop();
28
+ } catch {
29
+ }
30
+ try {
31
+ scanner.clear();
32
+ } catch {
33
+ }
34
+ }
35
+ async function setup_scanner() {
36
+ const generation = ++scanner_generation;
37
+ scanner_status = "loading";
38
+ scan_success = false;
39
+ scanned_text = "";
40
+ try {
41
+ const { Html5Qrcode } = await import("html5-qrcode");
42
+ const cameras = await Html5Qrcode.getCameras();
43
+ if (generation !== scanner_generation || !dialog.open) return;
44
+ if (cameras.length === 0) {
45
+ scanner_status = "unavailable";
46
+ return;
47
+ }
48
+ const scanner = new Html5Qrcode(reader_id);
49
+ qr_code_lib = scanner;
50
+ await scanner.start(cameras[0].id, { fps: 2 }, on_scan_success, () => {
51
+ });
52
+ if (generation !== scanner_generation || !dialog.open) {
53
+ await stop_scanner();
54
+ return;
55
+ }
56
+ scanner_status = "scanning";
57
+ } catch {
58
+ scanner_status = "unavailable";
59
+ await stop_scanner();
60
+ }
61
+ }
62
+ function open_scanner() {
63
+ dialog.showModal();
64
+ void setup_scanner();
65
+ }
66
+ async function close_scanner() {
67
+ scanner_generation += 1;
68
+ dialog.close();
69
+ await stop_scanner();
70
+ }
71
+ function handle_cancel() {
72
+ scanner_generation += 1;
73
+ void stop_scanner();
74
+ }
75
+ function on_scan_success(decodedText) {
76
+ scanned_text = decodedText;
77
+ scan_success = true;
78
+ }
79
+ function confirm_scan() {
80
+ $fieldStore.value = scanned_text;
81
+ void close_scanner();
82
+ }
83
+ </script>
84
+
85
+ <div class="qr-code-scanner-field">
86
+ <FieldLabel {fieldStore} />
87
+ <div
88
+ class="qr-code-scanner-control"
89
+ class:qr-code-scanner-control-invalid={$fieldStore.validation_result?.type === "error"}
90
+ aria-invalid={$fieldStore.validation_result?.type === "error" ? true : undefined}
91
+ >
92
+ <button
93
+ id={$fieldStore.id}
94
+ type="button"
95
+ class={classNames("qr-code-scanner-trigger", $styles.button)}
96
+ data-variant="primary"
97
+ aria-haspopup="dialog"
98
+ aria-controls="dialog-{$fieldStore.id}"
99
+ aria-describedby="message-{$fieldStore.id}"
100
+ on:click={open_scanner}
101
+ >
102
+ <span class="qr-code-scanner-icon" aria-hidden="true">
103
+ <QrCodeScannerIcon />
104
+ </span>
105
+ Scan code
106
+ </button>
107
+
108
+ <dialog
109
+ bind:this={dialog}
110
+ id="dialog-{$fieldStore.id}"
111
+ class={classNames("qr-code-scanner-dialog", $styles.popover)}
112
+ aria-labelledby="dialog-title-{$fieldStore.id}"
113
+ on:cancel={handle_cancel}
114
+ >
115
+ <div class="qr-code-scanner-content">
116
+ <div class="qr-code-scanner-header">
117
+ <h2 id="dialog-title-{$fieldStore.id}" class="qr-code-scanner-title">Scan code</h2>
118
+ <button
119
+ type="button"
120
+ class={classNames("qr-code-scanner-close", $styles.button)}
121
+ data-size="icon"
122
+ aria-label="Close scanner"
123
+ on:click={() => void close_scanner()}
124
+ >
125
+ <span aria-hidden="true">×</span>
126
+ </button>
127
+ </div>
128
+
129
+ <div id={reader_id} class="qr-code-scanner-reader" />
130
+
131
+ {#if scanner_status === "unavailable"}
132
+ <p class="qr-code-scanner-unavailable" role="status">No camera found</p>
133
+ {:else}
134
+ <div
135
+ class="qr-code-scanner-status"
136
+ class:qr-code-scanner-status-success={scan_success}
137
+ role="status"
138
+ aria-live="polite"
139
+ >
140
+ <span class="qr-code-scanner-status-icon" aria-hidden="true">●</span>
141
+ <p>{scan_success ? scanned_text : "Searching for code…"}</p>
142
+ </div>
143
+ <div class="qr-code-scanner-actions">
144
+ <button
145
+ type="button"
146
+ class={classNames("qr-code-scanner-confirm", $styles.button)}
147
+ data-variant="primary"
148
+ disabled={!scan_success}
149
+ on:click={confirm_scan}>Confirm</button
150
+ >
151
+ </div>
152
+ {/if}
153
+ </div>
154
+ </dialog>
155
+ </div>
156
+ <FieldMessage {fieldStore} />
157
+ </div>
158
+
159
+ <style>
160
+ .qr-code-scanner-field,
161
+ .qr-code-scanner-control,
162
+ .qr-code-scanner-content,
163
+ .qr-code-scanner-reader {
164
+ box-sizing: border-box;
165
+ width: 100%;
166
+ min-width: 0;
167
+ }
168
+
169
+ :where(.qr-code-scanner-dialog) {
170
+ box-sizing: border-box;
171
+ max-width: calc(100vw - 2rem);
172
+ max-height: calc(100vh - 2rem);
173
+ overflow: auto;
174
+ }
175
+
176
+ .qr-code-scanner-header {
177
+ display: flex;
178
+ align-items: center;
179
+ min-width: 0;
180
+ }
181
+
182
+ .qr-code-scanner-title {
183
+ flex: 1;
184
+ min-width: 0;
185
+ }
186
+
187
+ :where(.qr-code-scanner-close) {
188
+ flex: none;
189
+ }
190
+
191
+ .qr-code-scanner-reader {
192
+ overflow: hidden;
193
+ }
194
+
195
+ .qr-code-scanner-reader :global(video),
196
+ .qr-code-scanner-reader :global(canvas) {
197
+ max-width: 100%;
198
+ }
199
+
200
+ .qr-code-scanner-status {
201
+ overflow-wrap: anywhere;
202
+ }
203
+ </style>
@@ -0,0 +1,82 @@
1
+ <script context="module">import {} from "../interfaces";
2
+ export const radio_field_param_spec = {
3
+ spec: {
4
+ fields: {
5
+ options: {
6
+ fieldtype: "arraybuilder",
7
+ params: {
8
+ new_entry_field: {
9
+ fieldtype: "section",
10
+ fields: {
11
+ label: {
12
+ fieldtype: "text"
13
+ },
14
+ value: {
15
+ fieldtype: "jsoneditor"
16
+ }
17
+ }
18
+ }
19
+ }
20
+ }
21
+ }
22
+ }
23
+ };
24
+ </script>
25
+
26
+ <script>import { isEqual } from "lodash-es";
27
+ import { getContext } from "svelte";
28
+ import FieldLabel from "../FieldLabel.svelte";
29
+ import FieldMessage from "../FieldMessage.svelte";
30
+ import { STYLES_CONTEXT, classNames } from "../styles";
31
+ import { set_value_details } from "../utils";
32
+ export let fieldStore;
33
+ const styles = getContext(STYLES_CONTEXT);
34
+ let options = $fieldStore.params?.options || [];
35
+ let mapped_options = options.map((option, index) => {
36
+ const key = `${option.label}${index}`;
37
+ return {
38
+ label: option.label || index,
39
+ value: option.value || option.label || index,
40
+ key
41
+ };
42
+ });
43
+ $: selected_option = mapped_options.find((o) => isEqual(o.value, $fieldStore.value));
44
+ $: update_value_details(selected_option);
45
+ const update_value_details = (newly_set_option) => {
46
+ set_value_details($fieldStore, "label", newly_set_option?.label);
47
+ };
48
+ const handle_radio_change = (option) => {
49
+ $fieldStore.value = option.value;
50
+ };
51
+ </script>
52
+
53
+ <div class="radio-field">
54
+ <FieldLabel {fieldStore} />
55
+ <fieldset
56
+ class="radio-options"
57
+ class:radio-options-invalid={$fieldStore.validation_result?.type === "error"}
58
+ aria-label={$fieldStore.label}
59
+ aria-describedby={$fieldStore.validation_result ? `message-${$fieldStore.id}` : undefined}
60
+ >
61
+ {#each mapped_options as option, index (option.key)}
62
+ <div
63
+ class="radio-option"
64
+ class:radio-option-selected={isEqual(option.value, $fieldStore.value)}
65
+ >
66
+ <input
67
+ class={classNames("radio-input", $styles.choice)}
68
+ type="radio"
69
+ id="{$fieldStore.id}-option-{index}"
70
+ name={$fieldStore.id}
71
+ value={option.key}
72
+ checked={isEqual(option.value, $fieldStore.value)}
73
+ on:change={() => handle_radio_change(option)}
74
+ />
75
+ <label class="radio-label" for="{$fieldStore.id}-option-{index}">
76
+ {option.label}
77
+ </label>
78
+ </div>
79
+ {/each}
80
+ </fieldset>
81
+ <FieldMessage {fieldStore} />
82
+ </div>
@@ -1,5 +1,5 @@
1
1
  import { SvelteComponent } from "svelte";
2
- import { type Writable } from "svelte/store";
2
+ import type { Writable } from "svelte/store";
3
3
  import { type IBaseField, type IBuiltField, type IInitialForm } from "../interfaces";
4
4
  export interface IRadioFieldParams {
5
5
  options: {