@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,284 @@
1
+ <script context="module">import {
2
+ } from "../interfaces";
3
+ export const file_field_param_spec = {
4
+ spec: {
5
+ fields: {
6
+ max_files: {
7
+ fieldtype: "number",
8
+ __order: 0
9
+ },
10
+ file_creation_options: file_creation_options_field_spec
11
+ }
12
+ }
13
+ };
14
+ </script>
15
+
16
+ <script>import { isEmpty, isEqual, isUndefined } from "lodash-es";
17
+ import { getContext } from "svelte";
18
+ import FieldLabel from "../FieldLabel.svelte";
19
+ import FieldMessage from "../FieldMessage.svelte";
20
+ import UploadStatus from "../UploadStatus.svelte";
21
+ import { append_attachment, remove_attachment, uploadFiles } from "../fileserver";
22
+ import { file_creation_options_field_spec } from "../file_creation_options";
23
+ import { STYLES_CONTEXT, classNames } from "../styles";
24
+ export let fieldStore;
25
+ const styles = getContext(STYLES_CONTEXT);
26
+ $: validation_result = $fieldStore.validation_result;
27
+ $: validation_type = validation_result?.type;
28
+ async function handle_files_select(acceptedFiles) {
29
+ if (acceptedFiles.length === 0) return;
30
+ if (limit_remaining < acceptedFiles.length) {
31
+ alert("You have reached the maximum number of files allowed");
32
+ return;
33
+ }
34
+ selected_files = [
35
+ ...selected_files,
36
+ ...acceptedFiles.map((f) => ({
37
+ filename: f.name,
38
+ f_id: Math.random().toString(36).substring(7)
39
+ }))
40
+ ];
41
+ const upload_res = await uploadFiles(
42
+ acceptedFiles,
43
+ $fieldStore.formDependencies,
44
+ file_creation_options
45
+ );
46
+ const { uploaded_files: newly_uploaded, failed_files: newly_failed } = upload_res || {
47
+ uploaded_files: [],
48
+ failed_files: []
49
+ };
50
+ const newly_uploaded_with_f_id = newly_uploaded.map((f) => ({
51
+ ...f,
52
+ f_id: Math.random().toString(36).substring(7)
53
+ }));
54
+ for (const f of newly_uploaded) {
55
+ append_attachment(f, $fieldStore.attachmentsStore);
56
+ }
57
+ uploaded_files = [...uploaded_files, ...newly_uploaded_with_f_id];
58
+ failed_files = [
59
+ ...failed_files,
60
+ ...newly_failed.map((f) => ({
61
+ filename: f.name,
62
+ f_id: Math.random().toString(36).substring(7)
63
+ }))
64
+ ];
65
+ selected_files = [];
66
+ }
67
+ function handle_input(event) {
68
+ const input = event.currentTarget;
69
+ void handle_files_select(Array.from(input.files ?? []));
70
+ input.value = "";
71
+ }
72
+ let drag_depth = 0;
73
+ let is_dragging = false;
74
+ function handle_dragenter(event) {
75
+ if (limit_remaining <= 0 || !event.dataTransfer?.types.includes("Files")) return;
76
+ drag_depth += 1;
77
+ is_dragging = true;
78
+ }
79
+ function handle_dragleave() {
80
+ drag_depth = Math.max(0, drag_depth - 1);
81
+ if (drag_depth === 0) is_dragging = false;
82
+ }
83
+ function handle_drop(event) {
84
+ drag_depth = 0;
85
+ is_dragging = false;
86
+ if (limit_remaining <= 0) return;
87
+ void handle_files_select(Array.from(event.dataTransfer?.files ?? []));
88
+ }
89
+ let uploaded_files = Array.isArray($fieldStore.value) ? $fieldStore.value : [];
90
+ let failed_files = [];
91
+ let selected_files = [];
92
+ $: update_fieldStore(uploaded_files);
93
+ function update_fieldStore(uploaded_files2) {
94
+ if (isEmpty(uploaded_files2) && isUndefined($fieldStore.value)) return;
95
+ $fieldStore.value = uploaded_files2;
96
+ }
97
+ $: update_uploaded_files($fieldStore.value);
98
+ function update_uploaded_files(value) {
99
+ if (isEmpty(value)) {
100
+ uploaded_files = [];
101
+ } else if (Array.isArray(value) && !isEqual(value, uploaded_files)) {
102
+ uploaded_files = value;
103
+ }
104
+ }
105
+ $: all_files = [
106
+ ...uploaded_files.map((f) => ({ ...f, is_uploaded: true, is_failed: false })),
107
+ ...failed_files.map((f) => ({ ...f, is_failed: true, is_uploaded: false })),
108
+ ...selected_files.map((f) => ({ ...f, is_uploaded: false, is_failed: false }))
109
+ ];
110
+ let max_files = isNaN(Number($fieldStore.params?.max_files)) ? Infinity : Number($fieldStore.params?.max_files);
111
+ $: limit_remaining = max_files - selected_files.length - uploaded_files.length;
112
+ let file_creation_options = $fieldStore.params?.file_creation_options;
113
+ function remove_file(item) {
114
+ uploaded_files = uploaded_files?.filter((f) => f.f_id !== item.f_id);
115
+ failed_files = failed_files?.filter((f) => f.f_id !== item.f_id);
116
+ selected_files = selected_files?.filter((f) => f.f_id !== item.f_id);
117
+ if ("fileuuid" in item) {
118
+ remove_attachment(item.fileuuid, $fieldStore.attachmentsStore);
119
+ }
120
+ }
121
+ </script>
122
+
123
+ <div class="file-field">
124
+ <FieldLabel {fieldStore} />
125
+ <div class="file-control" class:file-control-invalid={validation_type === "error"}>
126
+ <label
127
+ class={classNames("file-dropzone", $styles.dropzone)}
128
+ class:file-dropzone-active={is_dragging}
129
+ class:file-dropzone-disabled={limit_remaining <= 0}
130
+ data-dragging={is_dragging ? true : undefined}
131
+ data-disabled={limit_remaining <= 0 ? true : undefined}
132
+ data-invalid={validation_type === "error" ? true : undefined}
133
+ on:dragenter={handle_dragenter}
134
+ on:dragover|preventDefault
135
+ on:dragleave={handle_dragleave}
136
+ on:drop|preventDefault={handle_drop}
137
+ >
138
+ <input
139
+ id={$fieldStore.id}
140
+ class="file-input"
141
+ type="file"
142
+ multiple
143
+ disabled={limit_remaining <= 0}
144
+ aria-invalid={validation_type === "error" ? true : undefined}
145
+ aria-describedby="message-{$fieldStore.id}"
146
+ on:change={handle_input}
147
+ />
148
+ <span class="file-prompt">Drop files here</span>
149
+ <span class="file-hint">or click to browse</span>
150
+ {#if limit_remaining <= 0}
151
+ <span class="file-limit">You have reached the maximum number of files allowed</span>
152
+ {/if}
153
+ </label>
154
+
155
+ <ul class="file-list" aria-live="polite">
156
+ {#each all_files as item (item.f_id)}
157
+ <li class={classNames("file-item", $styles.uploadItem)}>
158
+ <div class="file-status">
159
+ <UploadStatus
160
+ status={item.is_uploaded ? "uploaded" : item.is_failed ? "failed" : "uploading"}
161
+ />
162
+ </div>
163
+ <span class="file-filename">{item.filename}</span>
164
+ <button
165
+ type="button"
166
+ class={classNames("file-remove", $styles.button)}
167
+ data-variant="destructive"
168
+ data-size="icon"
169
+ aria-label="Remove {item.filename}"
170
+ on:click={() => remove_file(item)}
171
+ >
172
+ ×
173
+ </button>
174
+ </li>
175
+ {/each}
176
+ </ul>
177
+ </div>
178
+ <FieldMessage {fieldStore} />
179
+ </div>
180
+
181
+ <style>
182
+ .file-field,
183
+ .file-control,
184
+ :where(.file-dropzone),
185
+ .file-input,
186
+ .file-list {
187
+ box-sizing: border-box;
188
+ width: 100%;
189
+ min-width: 0;
190
+ }
191
+
192
+ :where(.file-dropzone) {
193
+ display: flex;
194
+ flex-direction: column;
195
+ align-items: center;
196
+ justify-content: center;
197
+ gap: 0.5rem;
198
+ min-height: 6rem;
199
+ cursor: pointer;
200
+ text-align: center;
201
+ }
202
+
203
+ .file-control,
204
+ .file-list {
205
+ display: flex;
206
+ flex-direction: column;
207
+ gap: 0.5rem;
208
+ }
209
+
210
+ :where(.file-item) {
211
+ display: flex;
212
+ align-items: center;
213
+ gap: 0.5rem;
214
+ min-width: 0;
215
+ padding: var(--form-upload-item-padding, 0.625rem 0.75rem);
216
+ border: var(--form-upload-item-border, 1px solid #e2e8f0);
217
+ border-radius: var(--form-upload-item-radius, 0.5rem);
218
+ background: var(--form-upload-item-background, #fff);
219
+ }
220
+
221
+ .file-input {
222
+ position: absolute;
223
+ width: 1px;
224
+ height: 1px;
225
+ padding: 0;
226
+ margin: -1px;
227
+ overflow: hidden;
228
+ clip: rect(0, 0, 0, 0);
229
+ white-space: nowrap;
230
+ border: 0;
231
+ }
232
+
233
+ .file-list {
234
+ padding: 0;
235
+ margin: 0;
236
+ list-style: none;
237
+ }
238
+
239
+ .file-prompt {
240
+ font-weight: 600;
241
+ }
242
+
243
+ .file-hint,
244
+ .file-limit {
245
+ font-size: 0.875em;
246
+ }
247
+
248
+ .file-dropzone-disabled {
249
+ cursor: not-allowed;
250
+ }
251
+
252
+ .file-filename {
253
+ flex: 1;
254
+ min-width: 0;
255
+ margin: 0;
256
+ overflow-wrap: anywhere;
257
+ }
258
+
259
+ .file-status,
260
+ :where(.file-remove) {
261
+ flex: none;
262
+ }
263
+
264
+ .file-status {
265
+ display: flex;
266
+ align-items: center;
267
+ gap: 0.5rem;
268
+ }
269
+
270
+ .file-status-text {
271
+ font-size: 0.875em;
272
+ font-weight: 600;
273
+ }
274
+
275
+ @media (max-width: 30rem) {
276
+ .file-status-text {
277
+ position: absolute;
278
+ width: 1px;
279
+ height: 1px;
280
+ overflow: hidden;
281
+ clip: rect(0, 0, 0, 0);
282
+ }
283
+ }
284
+ </style>
@@ -14,6 +14,8 @@ declare const __propDef: {
14
14
  fieldStore: Writable<IBuiltField<IFileFieldParams>>;
15
15
  };
16
16
  events: {
17
+ dragover: DragEvent;
18
+ } & {
17
19
  [evt: string]: CustomEvent<any>;
18
20
  };
19
21
  slots: {};
@@ -0,0 +1,152 @@
1
+ <script context="module">export const grid_field_param_spec = {
2
+ spec: {
3
+ fields: {
4
+ columnDefs: {
5
+ fieldtype: "arraybuilder",
6
+ params: {
7
+ new_entry_field: {
8
+ fieldtype: "jsoneditor"
9
+ }
10
+ },
11
+ help: "See https://www.ag-grid.com/javascript-data-grid/column-definitions/"
12
+ }
13
+ }
14
+ }
15
+ };
16
+ </script>
17
+
18
+ <script>import { getContext } from "svelte";
19
+ import FieldLabel from "../FieldLabel.svelte";
20
+ import FieldMessage from "../FieldMessage.svelte";
21
+ import { STYLES_CONTEXT, classNames } from "../styles";
22
+ export let fieldStore;
23
+ const styles = getContext(STYLES_CONTEXT);
24
+ $: columns = ($fieldStore.params?.columnDefs ?? []).filter(
25
+ (column) => typeof column.field === "string"
26
+ );
27
+ $: rows = Array.isArray($fieldStore.value) ? $fieldStore.value : [];
28
+ function get_value(row, field) {
29
+ return field.split(".").reduce((value, key) => value?.[key], row);
30
+ }
31
+ function set_value(row, field, value) {
32
+ const nextRow = { ...row };
33
+ const keys = field.split(".");
34
+ let target = nextRow;
35
+ let source = row;
36
+ keys.slice(0, -1).forEach((key) => {
37
+ target[key] = { ...source?.[key] ?? {} };
38
+ target = target[key];
39
+ source = source?.[key];
40
+ });
41
+ target[keys[keys.length - 1]] = value;
42
+ return nextRow;
43
+ }
44
+ function input_type(column, value) {
45
+ if (column.cellDataType === "boolean" || typeof value === "boolean") return "checkbox";
46
+ if (column.cellDataType === "number" || column.type === "number" || typeof value === "number") {
47
+ return "number";
48
+ }
49
+ return "text";
50
+ }
51
+ function update_cell(rowIndex, column, event) {
52
+ const input = event.currentTarget;
53
+ const currentValue = get_value(rows[rowIndex], column.field);
54
+ let value = input.value;
55
+ if (input.type === "checkbox") value = input.checked;
56
+ if (input.type === "number") value = input.value === "" ? "" : input.valueAsNumber;
57
+ if (typeof column.valueParser === "function") {
58
+ value = column.valueParser({
59
+ newValue: value,
60
+ oldValue: currentValue,
61
+ data: rows[rowIndex],
62
+ colDef: column
63
+ });
64
+ }
65
+ $fieldStore.value = rows.map(
66
+ (row, index) => index === rowIndex ? set_value(row, column.field, value) : row
67
+ );
68
+ }
69
+ function display_value(row, column) {
70
+ const value = get_value(row, column.field);
71
+ if (typeof column.valueFormatter === "function") {
72
+ return column.valueFormatter({ value, data: row, colDef: column });
73
+ }
74
+ if (typeof value === "boolean") return value ? "Yes" : "No";
75
+ return value ?? "";
76
+ }
77
+ </script>
78
+
79
+ <div class="grid-field" class:grid-field-invalid={$fieldStore.validation_result?.type === "error"}>
80
+ <FieldLabel {fieldStore} />
81
+ <div class="grid-viewport">
82
+ <table id={$fieldStore.id} class="grid-table" aria-describedby={`message-${$fieldStore.id}`}>
83
+ <thead class="grid-head">
84
+ <tr class="grid-row">
85
+ {#each columns as column (column.field)}
86
+ <th class="grid-header" scope="col">{column.headerName ?? column.field}</th>
87
+ {/each}
88
+ </tr>
89
+ </thead>
90
+ <tbody class="grid-body">
91
+ {#each rows as row, rowIndex}
92
+ <tr class="grid-row">
93
+ {#each columns as column (column.field)}
94
+ <td class="grid-cell">
95
+ {#if column.editable === true}
96
+ <input
97
+ class={classNames(
98
+ "grid-input",
99
+ input_type(column, get_value(row, column.field)) === "checkbox"
100
+ ? $styles.choice
101
+ : $styles.input
102
+ )}
103
+ type={input_type(column, get_value(row, column.field))}
104
+ value={input_type(column, get_value(row, column.field)) === "checkbox"
105
+ ? undefined
106
+ : get_value(row, column.field) ?? ""}
107
+ checked={input_type(column, get_value(row, column.field)) === "checkbox"
108
+ ? Boolean(get_value(row, column.field))
109
+ : undefined}
110
+ aria-label={`${column.headerName ?? column.field}, row ${rowIndex + 1}`}
111
+ aria-invalid={$fieldStore.validation_result?.type === "error"
112
+ ? true
113
+ : undefined}
114
+ aria-describedby={`message-${$fieldStore.id}`}
115
+ on:input={(event) => update_cell(rowIndex, column, event)}
116
+ on:change={(event) => update_cell(rowIndex, column, event)}
117
+ />
118
+ {:else}
119
+ {display_value(row, column)}
120
+ {/if}
121
+ </td>
122
+ {/each}
123
+ </tr>
124
+ {:else}
125
+ <tr class="grid-row">
126
+ <td class="grid-empty" colspan={Math.max(columns.length, 1)}>No rows.</td>
127
+ </tr>
128
+ {/each}
129
+ </tbody>
130
+ </table>
131
+ </div>
132
+ <FieldMessage {fieldStore} />
133
+ </div>
134
+
135
+ <style>
136
+ .grid-field,
137
+ .grid-viewport,
138
+ .grid-table {
139
+ box-sizing: border-box;
140
+ width: 100%;
141
+ min-width: 0;
142
+ }
143
+
144
+ .grid-viewport {
145
+ overflow-x: auto;
146
+ }
147
+
148
+ :where(.grid-input) {
149
+ box-sizing: border-box;
150
+ width: 100%;
151
+ }
152
+ </style>
@@ -13,16 +13,19 @@ export const heading_field_param_spec = {
13
13
  };
14
14
  </script>
15
15
 
16
- <script>export let fieldStore;
16
+ <script>import { getContext } from "svelte";
17
+ import { STYLES_CONTEXT } from "../styles";
18
+ export let fieldStore;
19
+ const styles = getContext(STYLES_CONTEXT);
17
20
  $: heading_text = $fieldStore?.params?.heading_text;
18
21
  $: subheading_text = $fieldStore?.params?.subheading_text;
19
22
  </script>
20
23
 
21
- <div class="flex flex-col w-full mb-2">
24
+ <div class="heading-field">
22
25
  {#if heading_text}
23
- <h1 class="text-xl text-black tracking-tight font-semibold mt-4">{heading_text}</h1>
26
+ <h1 class="heading-title">{heading_text}</h1>
24
27
  {/if}
25
28
  {#if subheading_text}
26
- <h2 class="text-sm text-surface-500 whitespace-pre-wrap">{subheading_text}</h2>
29
+ <h2 class="heading-subtitle">{subheading_text}</h2>
27
30
  {/if}
28
31
  </div>
@@ -10,12 +10,14 @@ export const html_field_param_spec = {
10
10
  };
11
11
  </script>
12
12
 
13
- <script>export let fieldStore;
14
- let params = $fieldStore.params;
15
- $: html = params?.html;
13
+ <script>import { getContext } from "svelte";
14
+ import { STYLES_CONTEXT } from "../styles";
15
+ export let fieldStore;
16
+ const styles = getContext(STYLES_CONTEXT);
17
+ $: html = $fieldStore.params?.html;
16
18
  </script>
17
19
 
18
- <div class="w-full">
20
+ <div class="html-field">
19
21
  {#if html}
20
22
  {@html html}
21
23
  {/if}