@stubber/form-fields 1.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 (151) hide show
  1. package/README.md +303 -0
  2. package/dist/Field.svelte +36 -0
  3. package/dist/Field.svelte.d.ts +31 -0
  4. package/dist/Form.svelte +34 -0
  5. package/dist/Form.svelte.d.ts +29 -0
  6. package/dist/NullFieldWrapper.svelte +6 -0
  7. package/dist/NullFieldWrapper.svelte.d.ts +25 -0
  8. package/dist/fields/component_parts/arraybuilder/FieldWrapper.svelte +69 -0
  9. package/dist/fields/component_parts/arraybuilder/FieldWrapper.svelte.d.ts +29 -0
  10. package/dist/fields/component_parts/fieldbuilder/FieldWrapper.svelte +8 -0
  11. package/dist/fields/component_parts/fieldbuilder/FieldWrapper.svelte.d.ts +25 -0
  12. package/dist/fields/components/AgGrid.svelte +53 -0
  13. package/dist/fields/components/AgGrid.svelte.d.ts +18 -0
  14. package/dist/fields/components/Arraybuilder.svelte +101 -0
  15. package/dist/fields/components/Arraybuilder.svelte.d.ts +25 -0
  16. package/dist/fields/components/Checkbox.svelte +100 -0
  17. package/dist/fields/components/Checkbox.svelte.d.ts +23 -0
  18. package/dist/fields/components/CheckboxAutocomplete.svelte +92 -0
  19. package/dist/fields/components/CheckboxAutocomplete.svelte.d.ts +23 -0
  20. package/dist/fields/components/Contactselector.svelte +348 -0
  21. package/dist/fields/components/Contactselector.svelte.d.ts +25 -0
  22. package/dist/fields/components/Currency.svelte +258 -0
  23. package/dist/fields/components/Currency.svelte.d.ts +23 -0
  24. package/dist/fields/components/Dataindication.svelte +35 -0
  25. package/dist/fields/components/Dataindication.svelte.d.ts +23 -0
  26. package/dist/fields/components/Date.svelte +94 -0
  27. package/dist/fields/components/Date.svelte.d.ts +23 -0
  28. package/dist/fields/components/Datetime.svelte +94 -0
  29. package/dist/fields/components/Datetime.svelte.d.ts +23 -0
  30. package/dist/fields/components/Email.svelte +124 -0
  31. package/dist/fields/components/Email.svelte.d.ts +23 -0
  32. package/dist/fields/components/Fieldbuilder.svelte +340 -0
  33. package/dist/fields/components/Fieldbuilder.svelte.d.ts +25 -0
  34. package/dist/fields/components/Fieldsbuilder.svelte +122 -0
  35. package/dist/fields/components/Fieldsbuilder.svelte.d.ts +25 -0
  36. package/dist/fields/components/File.svelte +230 -0
  37. package/dist/fields/components/File.svelte.d.ts +25 -0
  38. package/dist/fields/components/Heading.svelte +17 -0
  39. package/dist/fields/components/Heading.svelte.d.ts +23 -0
  40. package/dist/fields/components/Hidden.svelte +7 -0
  41. package/dist/fields/components/Hidden.svelte.d.ts +23 -0
  42. package/dist/fields/components/Hiddenlocation.svelte +28 -0
  43. package/dist/fields/components/Hiddenlocation.svelte.d.ts +23 -0
  44. package/dist/fields/components/Html.svelte +13 -0
  45. package/dist/fields/components/Html.svelte.d.ts +23 -0
  46. package/dist/fields/components/Jsoneditor.svelte +94 -0
  47. package/dist/fields/components/Jsoneditor.svelte.d.ts +23 -0
  48. package/dist/fields/components/Map.svelte +192 -0
  49. package/dist/fields/components/Map.svelte.d.ts +25 -0
  50. package/dist/fields/components/Multicheckbox.svelte +119 -0
  51. package/dist/fields/components/Multicheckbox.svelte.d.ts +23 -0
  52. package/dist/fields/components/Multistep.svelte +86 -0
  53. package/dist/fields/components/Multistep.svelte.d.ts +25 -0
  54. package/dist/fields/components/Note.svelte +92 -0
  55. package/dist/fields/components/Note.svelte.d.ts +23 -0
  56. package/dist/fields/components/Number.svelte +118 -0
  57. package/dist/fields/components/Number.svelte.d.ts +23 -0
  58. package/dist/fields/components/Objectbuilder.svelte +152 -0
  59. package/dist/fields/components/Objectbuilder.svelte.d.ts +25 -0
  60. package/dist/fields/components/Qrcodescanner.svelte +198 -0
  61. package/dist/fields/components/Qrcodescanner.svelte.d.ts +23 -0
  62. package/dist/fields/components/Radio.svelte +116 -0
  63. package/dist/fields/components/Radio.svelte.d.ts +23 -0
  64. package/dist/fields/components/Renderfield.svelte +58 -0
  65. package/dist/fields/components/Renderfield.svelte.d.ts +25 -0
  66. package/dist/fields/components/Screenrecorder.svelte +277 -0
  67. package/dist/fields/components/Screenrecorder.svelte.d.ts +25 -0
  68. package/dist/fields/components/Screenshot.svelte +270 -0
  69. package/dist/fields/components/Screenshot.svelte.d.ts +25 -0
  70. package/dist/fields/components/Scrollandreaddisplay.svelte +122 -0
  71. package/dist/fields/components/Scrollandreaddisplay.svelte.d.ts +23 -0
  72. package/dist/fields/components/Section.svelte +64 -0
  73. package/dist/fields/components/Section.svelte.d.ts +25 -0
  74. package/dist/fields/components/Select.svelte +229 -0
  75. package/dist/fields/components/Select.svelte.d.ts +23 -0
  76. package/dist/fields/components/Selectresource.svelte +291 -0
  77. package/dist/fields/components/Selectresource.svelte.d.ts +25 -0
  78. package/dist/fields/components/Signature.svelte +153 -0
  79. package/dist/fields/components/Signature.svelte.d.ts +25 -0
  80. package/dist/fields/components/Slider.svelte +101 -0
  81. package/dist/fields/components/Slider.svelte.d.ts +23 -0
  82. package/dist/fields/components/SmartText.svelte +330 -0
  83. package/dist/fields/components/SmartText.svelte.d.ts +23 -0
  84. package/dist/fields/components/Telephone.svelte +153 -0
  85. package/dist/fields/components/Telephone.svelte.d.ts +23 -0
  86. package/dist/fields/components/Text.svelte +106 -0
  87. package/dist/fields/components/Text.svelte.d.ts +23 -0
  88. package/dist/fields/components/Voicenote.svelte +268 -0
  89. package/dist/fields/components/Voicenote.svelte.d.ts +25 -0
  90. package/dist/fields/components/index.d.ts +81 -0
  91. package/dist/fields/components/index.js +82 -0
  92. package/dist/fields/definitions/_all.json +38 -0
  93. package/dist/fields/definitions/_valid_fieldtype.json +220 -0
  94. package/dist/fields/definitions/arraybuilder.json +39 -0
  95. package/dist/fields/definitions/checkbox.json +44 -0
  96. package/dist/fields/definitions/contactselector.json +15 -0
  97. package/dist/fields/definitions/currency.json +42 -0
  98. package/dist/fields/definitions/dataindication.json +16 -0
  99. package/dist/fields/definitions/date.json +16 -0
  100. package/dist/fields/definitions/datetime.json +15 -0
  101. package/dist/fields/definitions/email.json +16 -0
  102. package/dist/fields/definitions/fieldbuilder.json +64 -0
  103. package/dist/fields/definitions/fieldsbuilder.json +38 -0
  104. package/dist/fields/definitions/file.json +42 -0
  105. package/dist/fields/definitions/grid.json +47 -0
  106. package/dist/fields/definitions/heading.json +38 -0
  107. package/dist/fields/definitions/hidden.json +89 -0
  108. package/dist/fields/definitions/hiddenlocation.json +15 -0
  109. package/dist/fields/definitions/html.json +34 -0
  110. package/dist/fields/definitions/index.d.ts +86 -0
  111. package/dist/fields/definitions/index.js +96 -0
  112. package/dist/fields/definitions/jsoneditor.json +33 -0
  113. package/dist/fields/definitions/map.json +36 -0
  114. package/dist/fields/definitions/multicheckbox.json +47 -0
  115. package/dist/fields/definitions/multistep.json +35 -0
  116. package/dist/fields/definitions/note.json +16 -0
  117. package/dist/fields/definitions/number.json +42 -0
  118. package/dist/fields/definitions/objectbuilder.json +39 -0
  119. package/dist/fields/definitions/qrcodescanner.json +16 -0
  120. package/dist/fields/definitions/radio.json +47 -0
  121. package/dist/fields/definitions/renderfield.json +36 -0
  122. package/dist/fields/definitions/richtext.json +16 -0
  123. package/dist/fields/definitions/screenrecorder.json +42 -0
  124. package/dist/fields/definitions/screenshot.json +42 -0
  125. package/dist/fields/definitions/scrollandreaddisplay.json +49 -0
  126. package/dist/fields/definitions/section.json +50 -0
  127. package/dist/fields/definitions/select.json +47 -0
  128. package/dist/fields/definitions/selectresource.json +48 -0
  129. package/dist/fields/definitions/signature.json +16 -0
  130. package/dist/fields/definitions/slider.json +78 -0
  131. package/dist/fields/definitions/smart_text.json +101 -0
  132. package/dist/fields/definitions/telephone.json +16 -0
  133. package/dist/fields/definitions/text.json +35 -0
  134. package/dist/fields/definitions/voicenote.json +43 -0
  135. package/dist/index.d.ts +2 -0
  136. package/dist/index.js +3 -0
  137. package/dist/thirdparty/mapbox/GeoCoder.svelte +10 -0
  138. package/dist/thirdparty/mapbox/GeoCoder.svelte.d.ts +25 -0
  139. package/dist/thirdparty/mapbox/Map.svelte +30 -0
  140. package/dist/thirdparty/mapbox/Map.svelte.d.ts +20 -0
  141. package/dist/thirdparty/mapbox/MapMarker.svelte +13 -0
  142. package/dist/thirdparty/mapbox/MapMarker.svelte.d.ts +31 -0
  143. package/dist/utils/createField.d.ts +6 -0
  144. package/dist/utils/createField.js +33 -0
  145. package/dist/utils/createForm.d.ts +1 -0
  146. package/dist/utils/createForm.js +501 -0
  147. package/dist/utils/index.d.ts +18 -0
  148. package/dist/utils/index.js +126 -0
  149. package/dist/utils/syncing.d.ts +11 -0
  150. package/dist/utils/syncing.js +134 -0
  151. package/package.json +78 -0
@@ -0,0 +1,291 @@
1
+ <script>
2
+ import { onMount, onDestroy } from "svelte";
3
+ import _ from "lodash-es";
4
+ import * as utils from "../../utils/index.js";
5
+ import { deepEqual } from "fast-equals";
6
+ import { writable } from "svelte/store";
7
+ import { syncStoreToStore } from "../../utils/syncing";
8
+
9
+ export let form;
10
+ export let field;
11
+
12
+ const internal = writable();
13
+
14
+ let filter_text = "";
15
+ let is_focused = false;
16
+ let input;
17
+
18
+ let dependencies = form.dependencies;
19
+ let clienthub = dependencies?.clienthub;
20
+ let stubber = dependencies?.stubber;
21
+ let socket = clienthub?.socket;
22
+ let stubref = stubber?.stubref;
23
+ let orguuid = stubber?.orguuid;
24
+
25
+ let clickOutside = utils.clickOutside;
26
+
27
+ $: state_key = $field.state?.state_key;
28
+ $: label = $field.spec?.title;
29
+ $: hide_label = $field.spec?.hide_label;
30
+ $: resource_name = $field.spec?.params?.resource_name;
31
+
32
+ $: isValid = !$field.state?.validation || $field.state?.validation?.valid;
33
+ $: validationMessage = $field.state?.validation?.message;
34
+
35
+ onMount(() => {
36
+ // set field values that aren't set yet
37
+ let f = _.cloneDeep($field);
38
+ let initial_state_internal = {
39
+ selected_item: null,
40
+ raw_items: [],
41
+ focused_index: -1,
42
+ };
43
+ let initial_data = {
44
+ base: f?.data?.base,
45
+ base_label: f?.data?.base_label,
46
+ };
47
+ _.set(f, "data", initial_data);
48
+ _.set(f, "state.internal", initial_state_internal);
49
+ if (!deepEqual(f, $field)) $field = f;
50
+
51
+ syncStoreToStore(
52
+ field,
53
+ internal,
54
+ (a, b) => {
55
+ let _clone = _.cloneDeep(a.state?.internal) || {};
56
+
57
+ // get parts from data
58
+ if (a?.data?.base) {
59
+ _.set(_clone, "selected_item._value", a?.data?.base);
60
+ _.set(_clone, "selected_item._label", a?.data?.base_label);
61
+ }
62
+ filter_text = a?.data?.base_label ?? "";
63
+
64
+ // set field state if changed
65
+ if (!deepEqual(a?.state?.internal, _clone)) {
66
+ $field.state.internal = _clone;
67
+ }
68
+ return _clone;
69
+ },
70
+ (a, b) => {
71
+ let _clone = _.cloneDeep(a) || {};
72
+ // update the state
73
+ _.set(_clone, "state.internal", _.cloneDeep(b));
74
+ // update the data
75
+ _.set(_clone, "data.base", b?.selected_item?._value);
76
+ _.set(_clone, "data.base_label", b?.selected_item?._label ?? "");
77
+ return _clone;
78
+ }
79
+ );
80
+ });
81
+
82
+ $: items = _.isArray($internal?.raw_items)
83
+ ? $internal?.raw_items.map((item) => {
84
+ let _label = item[$field.spec?.params?.label || $field.spec.label] ?? item._default_label;
85
+ let _value = item;
86
+ return { _label, _value };
87
+ })
88
+ : [];
89
+
90
+ // $: console.log("items updated", items);
91
+
92
+ let debounceLoad = utils.debounce(loadResults, 200);
93
+ $: debounceLoad(filter_text);
94
+ async function loadResults(ft) {
95
+ let comparison = _.cloneDeep($internal);
96
+ if (!clienthub) return;
97
+ let details = {
98
+ resource_name,
99
+ };
100
+
101
+ details.params = structuredClone($field.spec.params) || {};
102
+ // add required/dynamic params (orguuid, stubref, input, and limit)
103
+ details.params.orguuid = orguuid;
104
+ details.params.stubref = stubref;
105
+ details.params.input = ft;
106
+ if (details.params.limit === undefined) {
107
+ details.params.limit = 50;
108
+ }
109
+
110
+ // emit join page event
111
+ socket.emit(
112
+ "request",
113
+ {
114
+ type: "resource",
115
+ details,
116
+ },
117
+ (res) => {
118
+ // TODO : handle failure
119
+ if (res.success) {
120
+ // console.log("selectresource success", res);
121
+ comparison.raw_items = res.payload[resource_name];
122
+
123
+ if (!deepEqual(comparison, $internal)) {
124
+ $internal = _.cloneDeep(comparison);
125
+ }
126
+ }
127
+ }
128
+ );
129
+ }
130
+
131
+ let teleportedNode = null;
132
+ onDestroy(() => {
133
+ if (teleportedNode) {
134
+ teleportedNode.remove();
135
+ teleportedNode = null;
136
+ }
137
+ });
138
+
139
+ // used to show the dropdown panel in the body instead of inside the component
140
+ // fixes an issue where the dropdown panel would be cut off by the parent container
141
+ function teleport(node) {
142
+ // Get the original position and width
143
+ let rect = node.getBoundingClientRect();
144
+ let originalWidth = node.offsetWidth;
145
+ let originalHeight = node.offsetHeight;
146
+
147
+ // Teleport to the body
148
+ let teleportContainer = document.body;
149
+ teleportContainer.appendChild(node);
150
+
151
+ teleportedNode = node;
152
+
153
+ // Apply the original width and position to the teleported element
154
+ node.style.width = originalWidth + "px";
155
+ node.style.height = originalHeight + "px";
156
+ node.style.position = "absolute";
157
+ node.style.top = rect.top + window.scrollY + "px";
158
+ node.style.left = rect.left + "px";
159
+
160
+ // set z-index to 1000
161
+ node.style.zIndex = 1000;
162
+ }
163
+
164
+ const setSelected = (item) => {
165
+ filter_text = item._label;
166
+ let comparison = _.cloneDeep($internal);
167
+ comparison.focused_index = -1;
168
+ comparison.selected_item = item;
169
+
170
+ if (!deepEqual(comparison, $internal)) {
171
+ $internal = _.cloneDeep(comparison);
172
+ }
173
+
174
+ is_focused = false;
175
+ input.blur();
176
+ };
177
+
178
+ function handleDeselect() {
179
+ let comparison = _.cloneDeep($internal);
180
+ comparison.selected_item = null;
181
+
182
+ if (!deepEqual(comparison, $internal)) {
183
+ $internal = _.cloneDeep(comparison);
184
+ }
185
+ filter_text = "";
186
+ }
187
+ </script>
188
+
189
+ {#if $internal}
190
+ <div
191
+ use:clickOutside={() => {
192
+ if (is_focused) {
193
+ is_focused = false;
194
+ filter_text = $field?.data?.base_label ?? "";
195
+ }
196
+ }}
197
+ class="relative flex flex-col w-full text-surface-900"
198
+ >
199
+ <label for="input_{state_key}" class="block text-label {hide_label ? 'hidden' : ''}">
200
+ {label}
201
+ </label>
202
+ <div class="relative flex mt-2 rounded-md">
203
+ <input
204
+ on:keydown={(e) => {
205
+ switch (e.key) {
206
+ case "Enter":
207
+ if ($internal.focused_index >= 0 && $internal.focused_index < items.length) {
208
+ setSelected(items[$internal.focused_index]);
209
+ }
210
+ e.preventDefault();
211
+ break;
212
+ case "ArrowDown":
213
+ $internal.focused_index = ($internal.focused_index + 1) % items.length;
214
+ e.preventDefault();
215
+ break;
216
+ case "ArrowUp":
217
+ $internal.focused_index = ($internal.focused_index - 1 + items.length) % items.length;
218
+ e.preventDefault();
219
+ break;
220
+ }
221
+ }}
222
+ on:focus={() => {
223
+ if (!is_focused) {
224
+ is_focused = true;
225
+ filter_text = "";
226
+ }
227
+ }}
228
+ bind:this={input}
229
+ type="select"
230
+ id="input_mask{state_key}"
231
+ placeholder={label}
232
+ class="block w-full rounded-md border-0 py-1.5 pl-3 pr-9 text-field text-surface-900 ring-1 ring-inset {!isValid
233
+ ? 'ring-danger-500'
234
+ : 'ring-surface-300 focus:ring-primary-400'} focus:outline-none placeholder:text-surface-400 focus:ring-2 focus:ring-inset"
235
+ bind:value={filter_text}
236
+ autocomplete="off"
237
+ />
238
+ {#if !$internal.selected_item}
239
+ <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
240
+ <i class="text-surface-300 fa-regular fa-arrows-up-down fa-fw" />
241
+ </div>
242
+ {/if}
243
+ {#if $internal.selected_item}
244
+ <button
245
+ on:click={handleDeselect}
246
+ class="absolute inset-y-0 right-0 flex items-center pr-2 text-surface-300 hover:text-danger-500"
247
+ >
248
+ <i class="fa-regular fa-x fa-fw" />
249
+ </button>
250
+ {/if}
251
+ </div>
252
+ {#if is_focused}
253
+ <div use:teleport class="z-10 w-full absolute inset-y-[70px]">
254
+ {#if items?.length > 0}
255
+ <ul
256
+ class="block max-h-[200px] overflow-y-auto w-full bg-white shadow-lg rounded-md ring-1 ring-surface-300"
257
+ >
258
+ {#each items as item, index (item?._value?._key)}
259
+ <li
260
+ class="group {$internal.focused_index === index
261
+ ? 'bg-primary-400'
262
+ : ''} hover:bg-primary-400 focus-within:bg-primary-400 text-field cursor-default"
263
+ >
264
+ <button
265
+ on:click|preventDefault={() => setSelected(item)}
266
+ class="selectitem text-surface-900 group-hover:text-white focus:text-white focus:outline-none px-4 py-1.5 w-full flex items-center"
267
+ >
268
+ {item._label}
269
+ {#if item?._value?._key == $internal?.selected_item?._value?._key}
270
+ <i class="text-primary-400 ml-auto fa-regular fa-check" />
271
+ {/if}
272
+ </button>
273
+ </li>
274
+ {/each}
275
+ </ul>
276
+ {:else}
277
+ <div
278
+ class="h-[100px] flex items-center justify-center w-full bg-white shadow-lg rounded-md ring-1 ring-surface-300"
279
+ >
280
+ <span class="text-paragraph text-surface-400">Start typing to search...</span>
281
+ </div>
282
+ {/if}
283
+ </div>
284
+ {/if}
285
+ {#if validationMessage}
286
+ <p class="text-label {!isValid ? `text-danger-500` : `text-success-500`}">
287
+ {validationMessage}
288
+ </p>
289
+ {/if}
290
+ </div>
291
+ {/if}
@@ -0,0 +1,25 @@
1
+ /** @typedef {typeof __propDef.props} SelectresourceProps */
2
+ /** @typedef {typeof __propDef.events} SelectresourceEvents */
3
+ /** @typedef {typeof __propDef.slots} SelectresourceSlots */
4
+ export default class Selectresource extends SvelteComponentTyped<{
5
+ form: any;
6
+ field: any;
7
+ }, {
8
+ [evt: string]: CustomEvent<any>;
9
+ }, {}> {
10
+ }
11
+ export type SelectresourceProps = typeof __propDef.props;
12
+ export type SelectresourceEvents = typeof __propDef.events;
13
+ export type SelectresourceSlots = typeof __propDef.slots;
14
+ import { SvelteComponentTyped } from "svelte";
15
+ declare const __propDef: {
16
+ props: {
17
+ form: any;
18
+ field: any;
19
+ };
20
+ events: {
21
+ [evt: string]: CustomEvent<any>;
22
+ };
23
+ slots: {};
24
+ };
25
+ export {};
@@ -0,0 +1,153 @@
1
+ <script>
2
+ import SignaturePad from "signature_pad";
3
+ import _ from "lodash-es";
4
+ import { onMount } from "svelte";
5
+ import { deepEqual } from "fast-equals";
6
+ import { writable } from "svelte/store";
7
+ import { syncStoreToStore } from "../../utils/syncing";
8
+
9
+ export let form;
10
+ export let field;
11
+
12
+ const internal = writable({});
13
+
14
+ $: state_key = $field.state?.state_key;
15
+ $: label = $field.spec?.title;
16
+ $: hide_label = $field.spec?.hide_label;
17
+
18
+ $: isValid = !$field.state?.validation || $field.state?.validation?.valid;
19
+ $: validationMessage = $field.state?.validation?.message;
20
+
21
+ let canvasContainer;
22
+ let pad;
23
+ let signaturePad;
24
+
25
+ onMount(() => {
26
+ // set field values that aren't set yet
27
+ let f = _.cloneDeep($field);
28
+
29
+ let initial_value = f?.data?.base ?? {
30
+ data: [],
31
+ file: undefined,
32
+ };
33
+ let initial_data = {
34
+ ...f?.data,
35
+ base: initial_value,
36
+ };
37
+ let initial_state_internal = {
38
+ value: initial_value,
39
+ };
40
+
41
+ _.set(f, "data", initial_data);
42
+ _.set(f, "state.internal", initial_state_internal);
43
+ if (!deepEqual(f, $field)) $field = f;
44
+
45
+ //init signature pad
46
+ signaturePad = new SignaturePad(pad);
47
+ signaturePad.addEventListener("endStroke", handleStroke);
48
+ setTimeout(() => {
49
+ let d = _.isArray($field.data?.base) ? $field.data?.base : [];
50
+ signaturePad.fromData(d);
51
+ }, 0);
52
+
53
+ syncStoreToStore(
54
+ field,
55
+ internal,
56
+ (a, b) => {
57
+ let _clone = _.cloneDeep(a.state?.internal) || {};
58
+
59
+ // get parts from data
60
+ _clone.value = a?.data?.base;
61
+ _clone.file = a?.data?.base?.file;
62
+
63
+ // set field state if changed
64
+ if (!deepEqual(a?.state?.internal, _clone)) {
65
+ $field.state.internal = _clone;
66
+ //update signature pad
67
+ let d = _.isArray(_clone.value) ? _clone.value : [];
68
+ signaturePad.fromData(d);
69
+ }
70
+ return _clone;
71
+ },
72
+ (a, b) => {
73
+ let _clone = _.cloneDeep(a) || {};
74
+ // update the state
75
+ _.set(_clone, "state.internal", _.cloneDeep(b));
76
+ // update the data
77
+ _.set(_clone, "data.base", b?.value);
78
+ return _clone;
79
+ }
80
+ );
81
+ });
82
+
83
+ async function uploadFile() {
84
+ const fileURI = signaturePad.toDataURL();
85
+ const fileBlob = await (await fetch(fileURI)).blob();
86
+ const filesForm = new FormData();
87
+ filesForm.append(label, fileBlob);
88
+ let upload_res = await form.uploadFiles(null, filesForm);
89
+ let { uploaded_files } = upload_res;
90
+ if (uploaded_files?.length) {
91
+ $internal.value.file = uploaded_files[0];
92
+ uploaded_files.forEach((a) => {
93
+ form.appendAttachment(a);
94
+ });
95
+ } else {
96
+ console.warn("Failed to upload file");
97
+ }
98
+
99
+ return true;
100
+ }
101
+
102
+ function clear() {
103
+ signaturePad.clear();
104
+ $field.data.base = {
105
+ data: null,
106
+ };
107
+ }
108
+
109
+ function handleStroke() {
110
+ const data = signaturePad.toData();
111
+ let comparison = _.cloneDeep($internal);
112
+ comparison.value.data = _.cloneDeep(data);
113
+ comparison.upload = true;
114
+
115
+ if (!deepEqual(comparison, $internal)) {
116
+ $internal = comparison;
117
+ uploadFile();
118
+ }
119
+ }
120
+
121
+ $: canvasWidth = canvasContainer?.clientWidth;
122
+ </script>
123
+
124
+ {#if $internal}
125
+ <div class="flex flex-col w-full text-surface-900">
126
+ <label for="input_{state_key}" class="block text-label {hide_label ? 'hidden' : ''}">
127
+ {label}
128
+ </label>
129
+ <div bind:this={canvasContainer}>
130
+ <canvas
131
+ bind:this={pad}
132
+ id="signature-pad"
133
+ class="signature-pad mt-2 rounded-md border"
134
+ width={canvasWidth}
135
+ height={200}
136
+ />
137
+ <div class="mt-0 text-sm">
138
+ <button
139
+ on:click={clear}
140
+ type="button"
141
+ class="text-surface-500 text-sm hover:text-surface-700"
142
+ >
143
+ Clear
144
+ </button>
145
+ </div>
146
+ </div>
147
+ {#if validationMessage}
148
+ <p class="text-label {!isValid ? `text-danger-500` : `text-success-500`}">
149
+ {validationMessage}
150
+ </p>
151
+ {/if}
152
+ </div>
153
+ {/if}
@@ -0,0 +1,25 @@
1
+ /** @typedef {typeof __propDef.props} SignatureProps */
2
+ /** @typedef {typeof __propDef.events} SignatureEvents */
3
+ /** @typedef {typeof __propDef.slots} SignatureSlots */
4
+ export default class Signature extends SvelteComponentTyped<{
5
+ form: any;
6
+ field: any;
7
+ }, {
8
+ [evt: string]: CustomEvent<any>;
9
+ }, {}> {
10
+ }
11
+ export type SignatureProps = typeof __propDef.props;
12
+ export type SignatureEvents = typeof __propDef.events;
13
+ export type SignatureSlots = typeof __propDef.slots;
14
+ import { SvelteComponentTyped } from "svelte";
15
+ declare const __propDef: {
16
+ props: {
17
+ form: any;
18
+ field: any;
19
+ };
20
+ events: {
21
+ [evt: string]: CustomEvent<any>;
22
+ };
23
+ slots: {};
24
+ };
25
+ export {};
@@ -0,0 +1,101 @@
1
+ <script>
2
+ import { syncStoreToStore } from "../../utils/syncing";
3
+ import { deepEqual } from "fast-equals";
4
+ import _ from "lodash-es";
5
+ import { onMount } from "svelte";
6
+ import { writable } from "svelte/store";
7
+
8
+ export let field;
9
+
10
+ const internal = writable();
11
+
12
+ $: state_key = $field.state?.state_key;
13
+ $: label = $field.spec?.title;
14
+ $: hide_label = $field.spec?.hide_label;
15
+ $: isValid = !$field.state?.validation || $field.state?.validation?.valid;
16
+ $: validationMessage = $field.state?.validation?.message;
17
+ $: min = $field.spec?.params?.min ?? 0;
18
+ $: max = $field.spec?.params?.max ?? 100;
19
+ $: step = $field.spec?.params?.step ?? 1;
20
+
21
+ onMount(() => {
22
+ // set field values that aren't set yet
23
+ let f = _.cloneDeep($field);
24
+ let initial_value = f?.data?.base ?? "";
25
+ let initial_data = {
26
+ ...f?.data,
27
+ base: initial_value,
28
+ };
29
+ let initial_state_internal = {
30
+ ...f?.state?.internal,
31
+ raw: initial_value,
32
+ };
33
+ _.set(f, "data", initial_data);
34
+ _.set(f, "state.internal", initial_state_internal);
35
+ if (!deepEqual(f, $field)) $field = f;
36
+
37
+ syncStoreToStore(
38
+ field,
39
+ internal,
40
+ (a, b) => {
41
+ let _clone = _.cloneDeep(a.state?.internal) || {};
42
+
43
+ // get parts from data
44
+ _clone.raw = a?.data?.base;
45
+
46
+ // set field state if changed
47
+ if (!deepEqual(a?.state?.internal, _clone)) {
48
+ $field.state.internal = _clone;
49
+ }
50
+
51
+ return _clone;
52
+ },
53
+ (a, b) => {
54
+ let _clone = _.cloneDeep(a) || {};
55
+ // update the state
56
+ _.set(_clone, "state.internal", _.cloneDeep(b));
57
+ // update the data
58
+ _.set(_clone, "data.base", b?.raw);
59
+ return _clone;
60
+ },
61
+ null,
62
+ 300
63
+ );
64
+ });
65
+ </script>
66
+
67
+ {#if $internal}
68
+ <div class="flex flex-col w-full text-surface-900">
69
+ <label for="input_{state_key}" class="block text-label {hide_label ? 'hidden' : ''}">
70
+ {label}
71
+ </label>
72
+ <div class="relative">
73
+ <div class="flex justify-between items-end text-sm text-surface-300">
74
+ <div>{min}</div>
75
+ <div class="text-md text-surface-700">{$internal.raw}</div>
76
+ <div>{max}</div>
77
+ </div>
78
+ <input
79
+ on:keydown={(e) => {
80
+ if (e.key === "Enter") {
81
+ e.preventDefault();
82
+ }
83
+ }}
84
+ {min}
85
+ {max}
86
+ {step}
87
+ type="range"
88
+ id="input_{state_key}"
89
+ placeholder={label}
90
+ class="block w-full text-field rounded-md border-0 mb-2 focus:outline-none"
91
+ name={state_key}
92
+ bind:value={$internal.raw}
93
+ />
94
+ </div>
95
+ {#if validationMessage}
96
+ <p class="text-label {!isValid ? `text-danger-500` : `text-success-500`}">
97
+ {validationMessage}
98
+ </p>
99
+ {/if}
100
+ </div>
101
+ {/if}
@@ -0,0 +1,23 @@
1
+ /** @typedef {typeof __propDef.props} SliderProps */
2
+ /** @typedef {typeof __propDef.events} SliderEvents */
3
+ /** @typedef {typeof __propDef.slots} SliderSlots */
4
+ export default class Slider extends SvelteComponentTyped<{
5
+ field: any;
6
+ }, {
7
+ [evt: string]: CustomEvent<any>;
8
+ }, {}> {
9
+ }
10
+ export type SliderProps = typeof __propDef.props;
11
+ export type SliderEvents = typeof __propDef.events;
12
+ export type SliderSlots = typeof __propDef.slots;
13
+ import { SvelteComponentTyped } from "svelte";
14
+ declare const __propDef: {
15
+ props: {
16
+ field: any;
17
+ };
18
+ events: {
19
+ [evt: string]: CustomEvent<any>;
20
+ };
21
+ slots: {};
22
+ };
23
+ export {};