@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.
- package/README.md +303 -0
- package/dist/Field.svelte +36 -0
- package/dist/Field.svelte.d.ts +31 -0
- package/dist/Form.svelte +34 -0
- package/dist/Form.svelte.d.ts +29 -0
- package/dist/NullFieldWrapper.svelte +6 -0
- package/dist/NullFieldWrapper.svelte.d.ts +25 -0
- package/dist/fields/component_parts/arraybuilder/FieldWrapper.svelte +69 -0
- package/dist/fields/component_parts/arraybuilder/FieldWrapper.svelte.d.ts +29 -0
- package/dist/fields/component_parts/fieldbuilder/FieldWrapper.svelte +8 -0
- package/dist/fields/component_parts/fieldbuilder/FieldWrapper.svelte.d.ts +25 -0
- package/dist/fields/components/AgGrid.svelte +53 -0
- package/dist/fields/components/AgGrid.svelte.d.ts +18 -0
- package/dist/fields/components/Arraybuilder.svelte +101 -0
- package/dist/fields/components/Arraybuilder.svelte.d.ts +25 -0
- package/dist/fields/components/Checkbox.svelte +100 -0
- package/dist/fields/components/Checkbox.svelte.d.ts +23 -0
- package/dist/fields/components/CheckboxAutocomplete.svelte +92 -0
- package/dist/fields/components/CheckboxAutocomplete.svelte.d.ts +23 -0
- package/dist/fields/components/Contactselector.svelte +348 -0
- package/dist/fields/components/Contactselector.svelte.d.ts +25 -0
- package/dist/fields/components/Currency.svelte +258 -0
- package/dist/fields/components/Currency.svelte.d.ts +23 -0
- package/dist/fields/components/Dataindication.svelte +35 -0
- package/dist/fields/components/Dataindication.svelte.d.ts +23 -0
- package/dist/fields/components/Date.svelte +94 -0
- package/dist/fields/components/Date.svelte.d.ts +23 -0
- package/dist/fields/components/Datetime.svelte +94 -0
- package/dist/fields/components/Datetime.svelte.d.ts +23 -0
- package/dist/fields/components/Email.svelte +124 -0
- package/dist/fields/components/Email.svelte.d.ts +23 -0
- package/dist/fields/components/Fieldbuilder.svelte +340 -0
- package/dist/fields/components/Fieldbuilder.svelte.d.ts +25 -0
- package/dist/fields/components/Fieldsbuilder.svelte +122 -0
- package/dist/fields/components/Fieldsbuilder.svelte.d.ts +25 -0
- package/dist/fields/components/File.svelte +230 -0
- package/dist/fields/components/File.svelte.d.ts +25 -0
- package/dist/fields/components/Heading.svelte +17 -0
- package/dist/fields/components/Heading.svelte.d.ts +23 -0
- package/dist/fields/components/Hidden.svelte +7 -0
- package/dist/fields/components/Hidden.svelte.d.ts +23 -0
- package/dist/fields/components/Hiddenlocation.svelte +28 -0
- package/dist/fields/components/Hiddenlocation.svelte.d.ts +23 -0
- package/dist/fields/components/Html.svelte +13 -0
- package/dist/fields/components/Html.svelte.d.ts +23 -0
- package/dist/fields/components/Jsoneditor.svelte +94 -0
- package/dist/fields/components/Jsoneditor.svelte.d.ts +23 -0
- package/dist/fields/components/Map.svelte +192 -0
- package/dist/fields/components/Map.svelte.d.ts +25 -0
- package/dist/fields/components/Multicheckbox.svelte +119 -0
- package/dist/fields/components/Multicheckbox.svelte.d.ts +23 -0
- package/dist/fields/components/Multistep.svelte +86 -0
- package/dist/fields/components/Multistep.svelte.d.ts +25 -0
- package/dist/fields/components/Note.svelte +92 -0
- package/dist/fields/components/Note.svelte.d.ts +23 -0
- package/dist/fields/components/Number.svelte +118 -0
- package/dist/fields/components/Number.svelte.d.ts +23 -0
- package/dist/fields/components/Objectbuilder.svelte +152 -0
- package/dist/fields/components/Objectbuilder.svelte.d.ts +25 -0
- package/dist/fields/components/Qrcodescanner.svelte +198 -0
- package/dist/fields/components/Qrcodescanner.svelte.d.ts +23 -0
- package/dist/fields/components/Radio.svelte +116 -0
- package/dist/fields/components/Radio.svelte.d.ts +23 -0
- package/dist/fields/components/Renderfield.svelte +58 -0
- package/dist/fields/components/Renderfield.svelte.d.ts +25 -0
- package/dist/fields/components/Screenrecorder.svelte +277 -0
- package/dist/fields/components/Screenrecorder.svelte.d.ts +25 -0
- package/dist/fields/components/Screenshot.svelte +270 -0
- package/dist/fields/components/Screenshot.svelte.d.ts +25 -0
- package/dist/fields/components/Scrollandreaddisplay.svelte +122 -0
- package/dist/fields/components/Scrollandreaddisplay.svelte.d.ts +23 -0
- package/dist/fields/components/Section.svelte +64 -0
- package/dist/fields/components/Section.svelte.d.ts +25 -0
- package/dist/fields/components/Select.svelte +229 -0
- package/dist/fields/components/Select.svelte.d.ts +23 -0
- package/dist/fields/components/Selectresource.svelte +291 -0
- package/dist/fields/components/Selectresource.svelte.d.ts +25 -0
- package/dist/fields/components/Signature.svelte +153 -0
- package/dist/fields/components/Signature.svelte.d.ts +25 -0
- package/dist/fields/components/Slider.svelte +101 -0
- package/dist/fields/components/Slider.svelte.d.ts +23 -0
- package/dist/fields/components/SmartText.svelte +330 -0
- package/dist/fields/components/SmartText.svelte.d.ts +23 -0
- package/dist/fields/components/Telephone.svelte +153 -0
- package/dist/fields/components/Telephone.svelte.d.ts +23 -0
- package/dist/fields/components/Text.svelte +106 -0
- package/dist/fields/components/Text.svelte.d.ts +23 -0
- package/dist/fields/components/Voicenote.svelte +268 -0
- package/dist/fields/components/Voicenote.svelte.d.ts +25 -0
- package/dist/fields/components/index.d.ts +81 -0
- package/dist/fields/components/index.js +82 -0
- package/dist/fields/definitions/_all.json +38 -0
- package/dist/fields/definitions/_valid_fieldtype.json +220 -0
- package/dist/fields/definitions/arraybuilder.json +39 -0
- package/dist/fields/definitions/checkbox.json +44 -0
- package/dist/fields/definitions/contactselector.json +15 -0
- package/dist/fields/definitions/currency.json +42 -0
- package/dist/fields/definitions/dataindication.json +16 -0
- package/dist/fields/definitions/date.json +16 -0
- package/dist/fields/definitions/datetime.json +15 -0
- package/dist/fields/definitions/email.json +16 -0
- package/dist/fields/definitions/fieldbuilder.json +64 -0
- package/dist/fields/definitions/fieldsbuilder.json +38 -0
- package/dist/fields/definitions/file.json +42 -0
- package/dist/fields/definitions/grid.json +47 -0
- package/dist/fields/definitions/heading.json +38 -0
- package/dist/fields/definitions/hidden.json +89 -0
- package/dist/fields/definitions/hiddenlocation.json +15 -0
- package/dist/fields/definitions/html.json +34 -0
- package/dist/fields/definitions/index.d.ts +86 -0
- package/dist/fields/definitions/index.js +96 -0
- package/dist/fields/definitions/jsoneditor.json +33 -0
- package/dist/fields/definitions/map.json +36 -0
- package/dist/fields/definitions/multicheckbox.json +47 -0
- package/dist/fields/definitions/multistep.json +35 -0
- package/dist/fields/definitions/note.json +16 -0
- package/dist/fields/definitions/number.json +42 -0
- package/dist/fields/definitions/objectbuilder.json +39 -0
- package/dist/fields/definitions/qrcodescanner.json +16 -0
- package/dist/fields/definitions/radio.json +47 -0
- package/dist/fields/definitions/renderfield.json +36 -0
- package/dist/fields/definitions/richtext.json +16 -0
- package/dist/fields/definitions/screenrecorder.json +42 -0
- package/dist/fields/definitions/screenshot.json +42 -0
- package/dist/fields/definitions/scrollandreaddisplay.json +49 -0
- package/dist/fields/definitions/section.json +50 -0
- package/dist/fields/definitions/select.json +47 -0
- package/dist/fields/definitions/selectresource.json +48 -0
- package/dist/fields/definitions/signature.json +16 -0
- package/dist/fields/definitions/slider.json +78 -0
- package/dist/fields/definitions/smart_text.json +101 -0
- package/dist/fields/definitions/telephone.json +16 -0
- package/dist/fields/definitions/text.json +35 -0
- package/dist/fields/definitions/voicenote.json +43 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/thirdparty/mapbox/GeoCoder.svelte +10 -0
- package/dist/thirdparty/mapbox/GeoCoder.svelte.d.ts +25 -0
- package/dist/thirdparty/mapbox/Map.svelte +30 -0
- package/dist/thirdparty/mapbox/Map.svelte.d.ts +20 -0
- package/dist/thirdparty/mapbox/MapMarker.svelte +13 -0
- package/dist/thirdparty/mapbox/MapMarker.svelte.d.ts +31 -0
- package/dist/utils/createField.d.ts +6 -0
- package/dist/utils/createField.js +33 -0
- package/dist/utils/createForm.d.ts +1 -0
- package/dist/utils/createForm.js +501 -0
- package/dist/utils/index.d.ts +18 -0
- package/dist/utils/index.js +126 -0
- package/dist/utils/syncing.d.ts +11 -0
- package/dist/utils/syncing.js +134 -0
- 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 {};
|