@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.
- package/README.md +63 -25
- package/dist/form-fields/Autocomplete.svelte +261 -0
- package/dist/form-fields/Autocomplete.svelte.d.ts +43 -0
- package/dist/form-fields/CollapsibleSection.svelte +127 -0
- package/dist/form-fields/CollapsibleSection.svelte.d.ts +26 -0
- package/dist/form-fields/FieldLabel.svelte +18 -3
- package/dist/form-fields/FieldLabel.svelte.d.ts +1 -0
- package/dist/form-fields/FieldMessage.svelte +20 -9
- package/dist/form-fields/Form.svelte +19 -2
- package/dist/form-fields/Form.svelte.d.ts +2 -0
- package/dist/form-fields/UploadStatus.svelte +76 -0
- package/dist/form-fields/UploadStatus.svelte.d.ts +18 -0
- package/dist/form-fields/fields/_field-builder-reorder.svelte +35 -0
- package/dist/form-fields/fields/_field-builder-reorder.svelte.d.ts +22 -0
- package/dist/form-fields/{sub → fields}/array-builder-field.svelte +104 -38
- package/dist/form-fields/fields/checkbox-autocomplete.svelte +153 -0
- package/dist/form-fields/fields/checkbox-field.svelte +69 -0
- package/dist/form-fields/{sub → fields}/contact-selector-field.svelte +9 -6
- package/dist/form-fields/{sub → fields}/currency-field.svelte +108 -70
- package/dist/form-fields/fields/data-indication-field.svelte +39 -0
- package/dist/form-fields/fields/date-field.svelte +31 -0
- package/dist/form-fields/fields/date-time-field.svelte +31 -0
- package/dist/form-fields/{sub → fields}/email-field.svelte +25 -11
- package/dist/form-fields/fields/field-builder-field.svelte +568 -0
- package/dist/form-fields/fields/file-field.svelte +284 -0
- package/dist/form-fields/{sub → fields}/file-field.svelte.d.ts +2 -0
- package/dist/form-fields/fields/grid-field.svelte +152 -0
- package/dist/form-fields/{sub → fields}/heading-field.svelte +7 -4
- package/dist/form-fields/{sub → fields}/html-field.svelte +6 -4
- package/dist/form-fields/{sub → fields}/map-field.svelte +121 -78
- package/dist/form-fields/{sub → fields}/multi-checkbox-field.svelte +35 -20
- package/dist/form-fields/fields/multistep-field.svelte +135 -0
- package/dist/form-fields/fields/note-field.svelte +35 -0
- package/dist/form-fields/{sub → fields}/number-field.svelte +29 -14
- package/dist/form-fields/fields/object-builder-field.svelte +176 -0
- package/dist/form-fields/fields/qr-code-scanner-field.svelte +203 -0
- package/dist/form-fields/fields/radio-field.svelte +82 -0
- package/dist/form-fields/{sub → fields}/radio-field.svelte.d.ts +1 -1
- package/dist/form-fields/fields/screenrecorder-field.svelte +255 -0
- package/dist/form-fields/{sub → fields}/screenshot-field.svelte +101 -40
- package/dist/form-fields/{sub → fields}/scroll-and-read-display-field.svelte +43 -27
- package/dist/form-fields/fields/section-field.svelte +73 -0
- package/dist/form-fields/fields/select-field.svelte +104 -0
- package/dist/form-fields/{sub → fields}/signature-field.svelte +78 -22
- package/dist/form-fields/fields/slider-field.svelte +53 -0
- package/dist/form-fields/{sub → fields}/smart-text-field.svelte +62 -82
- package/dist/form-fields/fields/telephone-field.svelte +108 -0
- package/dist/form-fields/{sub → fields}/telephone-field.svelte.d.ts +1 -1
- package/dist/form-fields/{sub → fields}/text-field.svelte +28 -11
- package/dist/form-fields/{sub → fields}/voicenote-field.svelte +117 -46
- package/dist/form-fields/file_creation_options.d.ts +1 -1
- package/dist/form-fields/form-field.svelte +8 -1
- package/dist/form-fields/icons/ChevronDownIcon.svelte +21 -0
- package/dist/form-fields/icons/ChevronDownIcon.svelte.d.ts +20 -0
- package/dist/form-fields/icons/QrCodeScannerIcon.svelte +25 -0
- package/dist/form-fields/icons/QrCodeScannerIcon.svelte.d.ts +19 -0
- package/dist/form-fields/icons/ScreenRecordingIcon.svelte +21 -0
- package/dist/form-fields/icons/ScreenRecordingIcon.svelte.d.ts +19 -0
- package/dist/form-fields/icons/ScreenshotIcon.svelte +22 -0
- package/dist/form-fields/icons/ScreenshotIcon.svelte.d.ts +19 -0
- package/dist/form-fields/index.d.ts +5 -1
- package/dist/form-fields/index.js +3 -1
- package/dist/form-fields/interfaces.d.ts +63 -63
- package/dist/form-fields/interfaces.js +42 -39
- package/dist/form-fields/intl-tel-input.css +1 -0
- package/dist/form-fields/styles.d.ts +17 -0
- package/dist/form-fields/styles.js +4 -0
- package/package.json +2 -4
- package/dist/form-fields/sub/checkbox-autocomplete.svelte +0 -56
- package/dist/form-fields/sub/checkbox-field.svelte +0 -42
- package/dist/form-fields/sub/data-indication-field.svelte +0 -19
- package/dist/form-fields/sub/date-field.svelte +0 -31
- package/dist/form-fields/sub/date-time-field.svelte +0 -31
- package/dist/form-fields/sub/field-builder-field.svelte +0 -657
- package/dist/form-fields/sub/file-field.svelte +0 -159
- package/dist/form-fields/sub/grid-field.svelte +0 -69
- package/dist/form-fields/sub/multistep-field.svelte +0 -70
- package/dist/form-fields/sub/note-field.svelte +0 -18
- package/dist/form-fields/sub/object-builder-field.svelte +0 -134
- package/dist/form-fields/sub/qr-code-scanner-field.svelte +0 -84
- package/dist/form-fields/sub/radio-field.svelte +0 -69
- package/dist/form-fields/sub/screenrecorder-field.svelte +0 -178
- package/dist/form-fields/sub/section-field.svelte +0 -50
- package/dist/form-fields/sub/select-field.svelte +0 -150
- package/dist/form-fields/sub/slider-field.svelte +0 -28
- package/dist/form-fields/sub/telephone-field.svelte +0 -64
- /package/dist/form-fields/{sub → fields}/array-builder-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/checkbox-autocomplete.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/checkbox-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/code-field.svelte +0 -0
- /package/dist/form-fields/{sub → fields}/code-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/contact-selector-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/currency-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/data-indication-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/date-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/date-time-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/email-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/field-builder-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/grid-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/heading-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/hidden-field.svelte +0 -0
- /package/dist/form-fields/{sub → fields}/hidden-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/hidden-location-field.svelte +0 -0
- /package/dist/form-fields/{sub → fields}/hidden-location-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/html-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/jsoneditor-field.svelte +0 -0
- /package/dist/form-fields/{sub → fields}/jsoneditor-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/map-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/multi-checkbox-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/multistep-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/note-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/number-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/object-builder-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/qr-code-scanner-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/screenrecorder-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/screenshot-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/scroll-and-read-display-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/section-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/select-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/selectresource-field.svelte +0 -0
- /package/dist/form-fields/{sub → fields}/selectresource-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/signature-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/slider-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/smart-text-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/text-field.svelte.d.ts +0 -0
- /package/dist/form-fields/{sub → fields}/voicenote-field.svelte.d.ts +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<script context="module">export const select_field_param_spec = {
|
|
2
|
+
spec: {
|
|
3
|
+
fields: {
|
|
4
|
+
options: {
|
|
5
|
+
fieldtype: "arraybuilder",
|
|
6
|
+
params: {
|
|
7
|
+
new_entry_field: {
|
|
8
|
+
fieldtype: "section",
|
|
9
|
+
fields: {
|
|
10
|
+
label: {
|
|
11
|
+
fieldtype: "text"
|
|
12
|
+
},
|
|
13
|
+
value: {
|
|
14
|
+
fieldtype: "jsoneditor"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<script>import { isEqual } from "lodash-es";
|
|
26
|
+
import Autocomplete, {} from "../Autocomplete.svelte";
|
|
27
|
+
import FieldLabel from "../FieldLabel.svelte";
|
|
28
|
+
import FieldMessage from "../FieldMessage.svelte";
|
|
29
|
+
import { clear_value_details, set_value_details } from "../utils";
|
|
30
|
+
export let fieldStore;
|
|
31
|
+
export let load_options = void 0;
|
|
32
|
+
$: dropdownLabel = items.find((item) => isEqual(item.value, $fieldStore.value))?.label ?? $fieldStore.value?._default_label;
|
|
33
|
+
$: update_value_details_label(dropdownLabel);
|
|
34
|
+
const update_value_details_label = (label) => {
|
|
35
|
+
if (!label) {
|
|
36
|
+
clear_value_details($fieldStore, "label");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
set_value_details($fieldStore, "label", label);
|
|
40
|
+
};
|
|
41
|
+
const map_field_options = (options) => {
|
|
42
|
+
if (!options || !Array.isArray(options)) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
return options.filter(Boolean).map((option, index) => {
|
|
46
|
+
const key = `${option.label}${index}`;
|
|
47
|
+
return {
|
|
48
|
+
label: option.label,
|
|
49
|
+
value: option.value,
|
|
50
|
+
key
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
$: items = map_field_options($fieldStore.params?.options);
|
|
55
|
+
async function loadAutocompleteOptions(search) {
|
|
56
|
+
return map_field_options(await load_options(search));
|
|
57
|
+
}
|
|
58
|
+
function select_item(item) {
|
|
59
|
+
$fieldStore.value = item.value;
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<div class="select-field">
|
|
64
|
+
<FieldLabel {fieldStore} />
|
|
65
|
+
<Autocomplete
|
|
66
|
+
id={$fieldStore.id}
|
|
67
|
+
options={items}
|
|
68
|
+
value={$fieldStore.value}
|
|
69
|
+
placeholder={dropdownLabel ?? "Select..."}
|
|
70
|
+
loadOptions={load_options ? loadAutocompleteOptions : undefined}
|
|
71
|
+
invalid={$fieldStore.validation_result?.type === "error"}
|
|
72
|
+
ariaDescribedby="message-{$fieldStore.id}"
|
|
73
|
+
on:select={(event) => select_item(event.detail)}
|
|
74
|
+
/>
|
|
75
|
+
<div class="select-footer">
|
|
76
|
+
<div class="select-message">
|
|
77
|
+
<FieldMessage {fieldStore} />
|
|
78
|
+
</div>
|
|
79
|
+
<div class="select-actions">
|
|
80
|
+
<slot />
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<style>
|
|
86
|
+
.select-field {
|
|
87
|
+
width: 100%;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.select-footer {
|
|
91
|
+
display: flex;
|
|
92
|
+
width: 100%;
|
|
93
|
+
align-items: center;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.select-message {
|
|
97
|
+
min-width: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.select-actions {
|
|
101
|
+
flex: none;
|
|
102
|
+
margin-inline-start: auto;
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
@@ -9,29 +9,47 @@ export const signature_field_param_spec = {
|
|
|
9
9
|
};
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
|
-
<script>import
|
|
13
|
-
import { Button } from "@stubber/ui/button";
|
|
14
|
-
import SignaturePad from "signature_pad";
|
|
12
|
+
<script>import SignaturePad from "signature_pad";
|
|
15
13
|
import FieldLabel from "../FieldLabel.svelte";
|
|
16
14
|
import FieldMessage from "../FieldMessage.svelte";
|
|
17
|
-
import
|
|
15
|
+
import UploadStatus from "../UploadStatus.svelte";
|
|
16
|
+
import { getContext, onDestroy, onMount } from "svelte";
|
|
18
17
|
import { append_attachment, remove_attachment, uploadFiles } from "../fileserver";
|
|
19
18
|
import { debounce, snakeCase } from "lodash-es";
|
|
20
19
|
import { file_creation_options_field_spec } from "../file_creation_options";
|
|
20
|
+
import { STYLES_CONTEXT, classNames } from "../styles";
|
|
21
21
|
export let fieldStore;
|
|
22
|
-
|
|
22
|
+
const styles = getContext(STYLES_CONTEXT);
|
|
23
23
|
let pad;
|
|
24
24
|
let signaturePad;
|
|
25
|
+
let resizeObserver;
|
|
25
26
|
let file_creation_options = $fieldStore.params?.file_creation_options;
|
|
26
27
|
onMount(() => {
|
|
27
28
|
signaturePad = new SignaturePad(pad);
|
|
28
29
|
signaturePad.addEventListener("endStroke", handleStroke);
|
|
30
|
+
resizeObserver = new ResizeObserver(resizeCanvas);
|
|
31
|
+
resizeObserver.observe(pad);
|
|
32
|
+
resizeCanvas();
|
|
29
33
|
if ($fieldStore.value?.data) {
|
|
30
34
|
setTimeout(() => {
|
|
31
35
|
signaturePad.fromData($fieldStore.value?.data);
|
|
32
36
|
}, 0);
|
|
33
37
|
}
|
|
34
38
|
});
|
|
39
|
+
onDestroy(() => {
|
|
40
|
+
debounced_uploadFile.cancel();
|
|
41
|
+
resizeObserver?.disconnect();
|
|
42
|
+
signaturePad?.removeEventListener("endStroke", handleStroke);
|
|
43
|
+
signaturePad?.off();
|
|
44
|
+
});
|
|
45
|
+
function resizeCanvas() {
|
|
46
|
+
const data = signaturePad.toData();
|
|
47
|
+
const ratio = Math.max(window.devicePixelRatio || 1, 1);
|
|
48
|
+
pad.width = pad.offsetWidth * ratio;
|
|
49
|
+
pad.height = pad.offsetHeight * ratio;
|
|
50
|
+
pad.getContext("2d")?.scale(ratio, ratio);
|
|
51
|
+
if (data.length) signaturePad.fromData(data);
|
|
52
|
+
}
|
|
35
53
|
function clear() {
|
|
36
54
|
signaturePad.clear();
|
|
37
55
|
if ($fieldStore.value) {
|
|
@@ -51,7 +69,6 @@ function handleStroke() {
|
|
|
51
69
|
debounced_uploadFile();
|
|
52
70
|
}
|
|
53
71
|
let is_uploading = false;
|
|
54
|
-
$: canvasWidth = canvasContainer?.clientWidth;
|
|
55
72
|
async function uploadFile() {
|
|
56
73
|
const fileURI = signaturePad.toDataURL();
|
|
57
74
|
const fileBlob = await (await fetch(fileURI)).blob();
|
|
@@ -78,21 +95,60 @@ async function uploadFile() {
|
|
|
78
95
|
const debounced_uploadFile = debounce(uploadFile, 1e3);
|
|
79
96
|
</script>
|
|
80
97
|
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
<div class="signature-field">
|
|
99
|
+
<FieldLabel {fieldStore} />
|
|
100
|
+
<div class="signature-pad">
|
|
101
|
+
<canvas
|
|
102
|
+
bind:this={pad}
|
|
103
|
+
id={$fieldStore.id}
|
|
104
|
+
class="signature-canvas"
|
|
105
|
+
aria-describedby="message-{$fieldStore.id}"
|
|
106
|
+
aria-invalid={$fieldStore.validation_result?.type === "error" ? true : undefined}
|
|
107
|
+
/>
|
|
108
|
+
<div class="signature-actions">
|
|
109
|
+
<button
|
|
110
|
+
class={classNames("signature-clear", $styles.button)}
|
|
111
|
+
data-variant="destructive"
|
|
112
|
+
on:click={clear}
|
|
113
|
+
type="button">Clear</button
|
|
114
|
+
>
|
|
115
|
+
<span class="signature-status" hidden={!is_uploading} aria-live="polite">
|
|
116
|
+
<UploadStatus status="uploading" />
|
|
117
|
+
<span>Uploading signature…</span>
|
|
118
|
+
</span>
|
|
119
|
+
</div>
|
|
96
120
|
</div>
|
|
121
|
+
<FieldMessage {fieldStore} />
|
|
97
122
|
</div>
|
|
98
|
-
|
|
123
|
+
|
|
124
|
+
<style>
|
|
125
|
+
.signature-field,
|
|
126
|
+
.signature-pad,
|
|
127
|
+
.signature-canvas {
|
|
128
|
+
box-sizing: border-box;
|
|
129
|
+
width: 100%;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.signature-canvas {
|
|
133
|
+
display: block;
|
|
134
|
+
height: 12rem;
|
|
135
|
+
touch-action: none;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.signature-actions {
|
|
139
|
+
display: flex;
|
|
140
|
+
justify-content: space-between;
|
|
141
|
+
align-items: center;
|
|
142
|
+
margin-top: 0.5rem;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.signature-status {
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
gap: 0.5rem;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.signature-status[hidden] {
|
|
152
|
+
display: none;
|
|
153
|
+
}
|
|
154
|
+
</style>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script context="module"></script>
|
|
2
|
+
|
|
3
|
+
<script>import { getContext } from "svelte";
|
|
4
|
+
import FieldLabel from "../FieldLabel.svelte";
|
|
5
|
+
import FieldMessage from "../FieldMessage.svelte";
|
|
6
|
+
import { STYLES_CONTEXT, classNames } from "../styles";
|
|
7
|
+
export let fieldStore;
|
|
8
|
+
const styles = getContext(STYLES_CONTEXT);
|
|
9
|
+
$: min = $fieldStore.min ?? 0;
|
|
10
|
+
$: max = $fieldStore.max ?? 100;
|
|
11
|
+
$: step = $fieldStore.step ?? 1;
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div class="slider-field">
|
|
15
|
+
<FieldLabel {fieldStore} />
|
|
16
|
+
<div class="slider-control">
|
|
17
|
+
<div class="slider-values">
|
|
18
|
+
<span class="slider-boundary">{min}</span>
|
|
19
|
+
<output class="slider-value" for={$fieldStore.id}>
|
|
20
|
+
{$fieldStore.value ?? ""}
|
|
21
|
+
</output>
|
|
22
|
+
<span class="slider-boundary">{max}</span>
|
|
23
|
+
</div>
|
|
24
|
+
<input
|
|
25
|
+
class={classNames("slider-input", $styles.range)}
|
|
26
|
+
id={$fieldStore.id}
|
|
27
|
+
bind:value={$fieldStore.value}
|
|
28
|
+
{min}
|
|
29
|
+
{max}
|
|
30
|
+
{step}
|
|
31
|
+
type="range"
|
|
32
|
+
aria-invalid={$fieldStore.validation_result?.type === "error" ? true : undefined}
|
|
33
|
+
aria-describedby="message-{$fieldStore.id}"
|
|
34
|
+
/>
|
|
35
|
+
</div>
|
|
36
|
+
<FieldMessage {fieldStore} />
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
.slider-field,
|
|
41
|
+
.slider-control,
|
|
42
|
+
.slider-input {
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
width: 100%;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.slider-values {
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
justify-content: space-between;
|
|
51
|
+
width: 100%;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
@@ -58,9 +58,10 @@ export const smart_text_field_param_spec = {
|
|
|
58
58
|
</script>
|
|
59
59
|
|
|
60
60
|
<script>import { find } from "lodash-es";
|
|
61
|
+
import { getContext } from "svelte";
|
|
61
62
|
import FieldLabel from "../FieldLabel.svelte";
|
|
62
63
|
import FieldMessage from "../FieldMessage.svelte";
|
|
63
|
-
import {
|
|
64
|
+
import { STYLES_CONTEXT, classNames } from "../styles";
|
|
64
65
|
export let fieldStore;
|
|
65
66
|
let editor_view;
|
|
66
67
|
let codemirror_loader;
|
|
@@ -76,6 +77,7 @@ function loadCodeMirror() {
|
|
|
76
77
|
autocompletion: autocomplete.autocompletion,
|
|
77
78
|
completionKeymap: autocomplete.completionKeymap,
|
|
78
79
|
history: commands.history,
|
|
80
|
+
Compartment: state.Compartment,
|
|
79
81
|
EditorState: state.EditorState,
|
|
80
82
|
StateField: state.StateField,
|
|
81
83
|
Decoration: view.Decoration,
|
|
@@ -93,17 +95,22 @@ $: type = validation_result?.type;
|
|
|
93
95
|
$: isValid = type !== "error";
|
|
94
96
|
$: is_object = typeof $fieldStore.value === "object";
|
|
95
97
|
const smart_text_fields_ctx_store = getContext("smart_text_fields_ctx");
|
|
98
|
+
const styles = getContext(STYLES_CONTEXT);
|
|
96
99
|
$: parse_string = $fieldStore.params?.parse_string ?? true;
|
|
97
100
|
$: code_language = $fieldStore.params?.code_language || "handlebars";
|
|
98
|
-
const setup_editor = (element) => {
|
|
101
|
+
const setup_editor = (element, initial_appearance) => {
|
|
99
102
|
let disposed = false;
|
|
100
103
|
let local_editor_view;
|
|
104
|
+
let appearance = initial_appearance;
|
|
105
|
+
let appearance_compartment;
|
|
106
|
+
let appearance_extension;
|
|
101
107
|
void loadCodeMirror().then(
|
|
102
108
|
({
|
|
103
109
|
acceptCompletion,
|
|
104
110
|
autocompletion,
|
|
105
111
|
completionKeymap,
|
|
106
112
|
history,
|
|
113
|
+
Compartment,
|
|
107
114
|
EditorState,
|
|
108
115
|
StateField,
|
|
109
116
|
Decoration,
|
|
@@ -114,6 +121,11 @@ const setup_editor = (element) => {
|
|
|
114
121
|
ViewPlugin
|
|
115
122
|
}) => {
|
|
116
123
|
if (disposed) return;
|
|
124
|
+
appearance_compartment = new Compartment();
|
|
125
|
+
appearance_extension = () => EditorView.editorAttributes.of({
|
|
126
|
+
class: appearance.input,
|
|
127
|
+
"aria-invalid": appearance.invalid ? "true" : "false"
|
|
128
|
+
});
|
|
117
129
|
const contentField = createContentField(StateField);
|
|
118
130
|
const tab_accept_completion = keymap.of(
|
|
119
131
|
completionKeymap.filter((binding) => binding.key != "Enter").concat([
|
|
@@ -132,12 +144,19 @@ const setup_editor = (element) => {
|
|
|
132
144
|
const extensions = [
|
|
133
145
|
history({ newGroupDelay: 150 }),
|
|
134
146
|
EditorView.lineWrapping,
|
|
147
|
+
EditorView.theme({
|
|
148
|
+
"&": { minWidth: "0", width: "100%" },
|
|
149
|
+
".cm-scroller": { overflow: "auto" }
|
|
150
|
+
}),
|
|
151
|
+
appearance_compartment.of(appearance_extension()),
|
|
135
152
|
contentField,
|
|
136
153
|
autocompletion({ override: [myCompletions], defaultKeymap: false }),
|
|
137
154
|
tab_accept_completion,
|
|
138
155
|
placeholder($fieldStore.label),
|
|
139
156
|
EditorView.contentAttributes.of({
|
|
140
|
-
|
|
157
|
+
id: $fieldStore.id,
|
|
158
|
+
spellcheck: "true",
|
|
159
|
+
"aria-describedby": `message-${$fieldStore.id}`
|
|
141
160
|
})
|
|
142
161
|
];
|
|
143
162
|
if (code_language === "handlebars") {
|
|
@@ -155,6 +174,14 @@ const setup_editor = (element) => {
|
|
|
155
174
|
}
|
|
156
175
|
);
|
|
157
176
|
return {
|
|
177
|
+
update(next_appearance) {
|
|
178
|
+
appearance = next_appearance;
|
|
179
|
+
if (local_editor_view && appearance_compartment && appearance_extension) {
|
|
180
|
+
local_editor_view.dispatch({
|
|
181
|
+
effects: appearance_compartment.reconfigure(appearance_extension())
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
},
|
|
158
185
|
destroy() {
|
|
159
186
|
disposed = true;
|
|
160
187
|
local_editor_view?.destroy();
|
|
@@ -196,6 +223,17 @@ function update_editor_content(new_value) {
|
|
|
196
223
|
}
|
|
197
224
|
}
|
|
198
225
|
}
|
|
226
|
+
$: update_editor_attributes($fieldStore.id, !isValid);
|
|
227
|
+
function update_editor_attributes(id, invalid) {
|
|
228
|
+
if (!editor_view) return;
|
|
229
|
+
editor_view.contentDOM.id = id;
|
|
230
|
+
editor_view.contentDOM.setAttribute("aria-describedby", `message-${id}`);
|
|
231
|
+
if (invalid) {
|
|
232
|
+
editor_view.contentDOM.setAttribute("aria-invalid", "true");
|
|
233
|
+
} else {
|
|
234
|
+
editor_view.contentDOM.removeAttribute("aria-invalid");
|
|
235
|
+
}
|
|
236
|
+
}
|
|
199
237
|
function fake_handlebars_lang(Decoration, MatchDecorator, ViewPlugin) {
|
|
200
238
|
const curly_decorator = new MatchDecorator({
|
|
201
239
|
regexp: /(\{\{[^}]*\}\})|(~~[^\s]*)/g,
|
|
@@ -248,89 +286,31 @@ function parse_string_value(value) {
|
|
|
248
286
|
}
|
|
249
287
|
</script>
|
|
250
288
|
|
|
251
|
-
<
|
|
252
|
-
{
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
{
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
289
|
+
<div class="smart-text-field">
|
|
290
|
+
<FieldLabel {fieldStore} />
|
|
291
|
+
{#if !is_object}
|
|
292
|
+
<div
|
|
293
|
+
class="smart-text-editor"
|
|
294
|
+
use:setup_editor={{ input: classNames("smart-text-input", $styles.input), invalid: !isValid }}
|
|
295
|
+
/>
|
|
296
|
+
{:else}
|
|
297
|
+
<p class={classNames("smart-text-type-error", $styles.message)} data-variant="error">
|
|
298
|
+
The value of this field is not a string.
|
|
299
|
+
</p>
|
|
300
|
+
{/if}
|
|
301
|
+
<FieldMessage {fieldStore} />
|
|
302
|
+
</div>
|
|
261
303
|
|
|
262
304
|
<style>
|
|
263
|
-
.
|
|
264
|
-
|
|
305
|
+
.smart-text-field {
|
|
306
|
+
display: flex;
|
|
307
|
+
flex-direction: column;
|
|
265
308
|
min-width: 0;
|
|
266
|
-
|
|
267
|
-
border-width: 1px;
|
|
268
|
-
--tw-border-opacity: 1;
|
|
269
|
-
border-color: hsl(var(--input) / var(--tw-border-opacity, 1));
|
|
270
|
-
--tw-bg-opacity: 1;
|
|
271
|
-
background-color: hsl(var(--background) / var(--tw-bg-opacity, 1));
|
|
272
|
-
|
|
273
|
-
font-size: 0.875rem;
|
|
274
|
-
line-height: 1.25rem;
|
|
275
|
-
outline: 2px solid transparent;
|
|
276
|
-
outline-offset: 2px;
|
|
277
|
-
--tw-ring-offset-color: hsl(var(--background) / 1);
|
|
278
|
-
transition-property: color, box-shadow;
|
|
279
|
-
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
280
|
-
transition-duration: 150ms;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
@media (prefers-color-scheme: dark) {
|
|
284
|
-
.stubber-cm :global(.cm-editor) {
|
|
285
|
-
background-color: hsl(var(--input) / 0.3);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
.stubber-cm[aria-invalid="true"] :global(.cm-editor) {
|
|
290
|
-
--tw-border-opacity: 1;
|
|
291
|
-
border-color: hsl(var(--destructive) / var(--tw-border-opacity, 1));
|
|
292
|
-
--tw-ring-color: hsl(var(--destructive) / 0.5);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
.stubber-cm :global(.cm-editor.cm-focused) {
|
|
296
|
-
--tw-border-opacity: 1;
|
|
297
|
-
border-color: hsl(var(--ring) / var(--tw-border-opacity, 1));
|
|
298
|
-
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
|
|
299
|
-
var(--tw-ring-offset-color);
|
|
300
|
-
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width))
|
|
301
|
-
var(--tw-ring-color);
|
|
302
|
-
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
303
|
-
--tw-ring-color: hsl(var(--ring) / 0.5);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
.stubber-cm[aria-invalid="true"] :global(.cm-editor.cm-focused) {
|
|
307
|
-
--tw-ring-color: hsl(var(--destructive) / 0.5);
|
|
308
|
-
--tw-border-opacity: 1;
|
|
309
|
-
border-color: hsl(var(--destructive) / var(--tw-border-opacity, 1));
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
.stubber-cm :global(.cm-editor .cm-scroller) {
|
|
313
|
-
font-family: inherit;
|
|
314
|
-
line-height: inherit;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
.stubber-cm :global(.cm-editor .cm-content) {
|
|
318
|
-
padding: 0;
|
|
319
|
-
padding-top: 0.5rem;
|
|
320
|
-
padding-bottom: 0.5rem;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.stubber-cm :global(.cm-editor .cm-line) {
|
|
324
|
-
padding: 0;
|
|
325
|
-
padding-left: 0.75rem;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
.stubber-cm :global(.cm-editor .cm-placeholder) {
|
|
329
|
-
--tw-text-opacity: 1;
|
|
330
|
-
color: hsl(var(--muted-foreground) / var(--tw-text-opacity, 1));
|
|
309
|
+
width: 100%;
|
|
331
310
|
}
|
|
332
311
|
|
|
333
|
-
.
|
|
334
|
-
|
|
312
|
+
.smart-text-editor {
|
|
313
|
+
min-width: 0;
|
|
314
|
+
width: 100%;
|
|
335
315
|
}
|
|
336
316
|
</style>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<script context="module">import {} from "../interfaces";
|
|
2
|
+
</script>
|
|
3
|
+
|
|
4
|
+
<script>import { getContext } from "svelte";
|
|
5
|
+
import intlTelInput, {} from "intl-tel-input";
|
|
6
|
+
import "../intl-tel-input.css";
|
|
7
|
+
import FieldLabel from "../FieldLabel.svelte";
|
|
8
|
+
import FieldMessage from "../FieldMessage.svelte";
|
|
9
|
+
import { STYLES_CONTEXT, classNames } from "../styles";
|
|
10
|
+
import { set_value_details } from "../utils";
|
|
11
|
+
export let fieldStore;
|
|
12
|
+
const styles = getContext(STYLES_CONTEXT);
|
|
13
|
+
let iti = null;
|
|
14
|
+
function setupInput(input_element, initial_appearance) {
|
|
15
|
+
const handle_country_change = () => set_number_details();
|
|
16
|
+
input_element.addEventListener("countrychange", handle_country_change);
|
|
17
|
+
let appearance = initial_appearance;
|
|
18
|
+
const instance = intlTelInput(input_element, {
|
|
19
|
+
loadUtils: () => import("intl-tel-input/utils"),
|
|
20
|
+
initialCountryLookup: async () => {
|
|
21
|
+
const res = await fetch("https://ipapi.co/json");
|
|
22
|
+
const data = await res.json();
|
|
23
|
+
return data?.country_code || "us";
|
|
24
|
+
},
|
|
25
|
+
separateDialCode: false,
|
|
26
|
+
classNames: {
|
|
27
|
+
searchWrapper: appearance.search,
|
|
28
|
+
searchInput: appearance.searchInput
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
iti = instance;
|
|
32
|
+
const container = input_element.parentElement;
|
|
33
|
+
container?.style.setProperty("width", "100%");
|
|
34
|
+
void instance.promise.then(() => {
|
|
35
|
+
if (iti === instance) set_number_details();
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
update(next_appearance) {
|
|
39
|
+
const container2 = input_element.parentElement;
|
|
40
|
+
const search = container2?.querySelector(".iti__search-input-wrapper");
|
|
41
|
+
const search_input = container2?.querySelector(".iti__search-input");
|
|
42
|
+
replaceClasses(search, appearance.search, next_appearance.search);
|
|
43
|
+
replaceClasses(search_input, appearance.searchInput, next_appearance.searchInput);
|
|
44
|
+
appearance = next_appearance;
|
|
45
|
+
},
|
|
46
|
+
destroy() {
|
|
47
|
+
input_element.removeEventListener("countrychange", handle_country_change);
|
|
48
|
+
instance.destroy();
|
|
49
|
+
if (iti === instance) iti = null;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function replaceClasses(element, previous, next) {
|
|
54
|
+
if (!element) return;
|
|
55
|
+
element.classList.remove(...previous.split(/\s+/).filter(Boolean));
|
|
56
|
+
element.classList.add(...next.split(/\s+/).filter(Boolean));
|
|
57
|
+
}
|
|
58
|
+
let last_set_value = null;
|
|
59
|
+
function set_number_details(force = false) {
|
|
60
|
+
if (!iti) return;
|
|
61
|
+
if (!force && last_set_value === $fieldStore.value) return;
|
|
62
|
+
last_set_value = $fieldStore.value;
|
|
63
|
+
const formatted_number = iti.getNumber();
|
|
64
|
+
const is_valid = iti.isValidNumber();
|
|
65
|
+
const country_data = iti.getSelectedCountry();
|
|
66
|
+
if (is_valid && formatted_number !== $fieldStore.value) {
|
|
67
|
+
$fieldStore.value = formatted_number;
|
|
68
|
+
}
|
|
69
|
+
if ($fieldStore.value) {
|
|
70
|
+
set_value_details($fieldStore, "details", {
|
|
71
|
+
is_valid_number: is_valid,
|
|
72
|
+
selected_country: country_data
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<div class="telephone-field">
|
|
79
|
+
<FieldLabel {fieldStore} />
|
|
80
|
+
<input
|
|
81
|
+
use:setupInput={{
|
|
82
|
+
search: "telephone-search",
|
|
83
|
+
searchInput: classNames("telephone-search-input", $styles.input),
|
|
84
|
+
}}
|
|
85
|
+
bind:value={$fieldStore.value}
|
|
86
|
+
on:input={() => set_number_details()}
|
|
87
|
+
type="tel"
|
|
88
|
+
id={$fieldStore.id}
|
|
89
|
+
class={classNames("telephone-input", $styles.input)}
|
|
90
|
+
aria-invalid={$fieldStore.validation_result?.type == "error" ? true : undefined}
|
|
91
|
+
aria-describedby="message-{$fieldStore.id}"
|
|
92
|
+
/>
|
|
93
|
+
<FieldMessage {fieldStore} />
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<style>
|
|
97
|
+
.telephone-field,
|
|
98
|
+
.telephone-input {
|
|
99
|
+
box-sizing: border-box;
|
|
100
|
+
width: 100%;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.telephone-field :global(.telephone-search),
|
|
104
|
+
.telephone-field :global(.telephone-search-input) {
|
|
105
|
+
box-sizing: border-box;
|
|
106
|
+
width: 100%;
|
|
107
|
+
}
|
|
108
|
+
</style>
|
|
@@ -6,7 +6,7 @@ export interface ITelephoneFieldParams {
|
|
|
6
6
|
export interface ITelephoneField extends IBaseField<ITelephoneFieldParams> {
|
|
7
7
|
fieldtype: "telephone";
|
|
8
8
|
}
|
|
9
|
-
import "intl-tel-input
|
|
9
|
+
import "../intl-tel-input.css";
|
|
10
10
|
declare const __propDef: {
|
|
11
11
|
props: {
|
|
12
12
|
fieldStore: Writable<IBuiltField<ITelephoneFieldParams>>;
|
|
@@ -11,10 +11,12 @@ export const text_field_param_spec = {
|
|
|
11
11
|
};
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
|
-
<script>import {
|
|
14
|
+
<script>import { getContext } from "svelte";
|
|
15
15
|
import FieldLabel from "../FieldLabel.svelte";
|
|
16
16
|
import FieldMessage from "../FieldMessage.svelte";
|
|
17
|
+
import { STYLES_CONTEXT } from "../styles";
|
|
17
18
|
export let fieldStore;
|
|
19
|
+
const styles = getContext(STYLES_CONTEXT);
|
|
18
20
|
function parse_string_value(value) {
|
|
19
21
|
if (value === "true") return true;
|
|
20
22
|
if (value === "false") return false;
|
|
@@ -34,13 +36,28 @@ function on_input(e) {
|
|
|
34
36
|
}
|
|
35
37
|
</script>
|
|
36
38
|
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
<div class="text-field">
|
|
40
|
+
<FieldLabel {fieldStore} />
|
|
41
|
+
<input
|
|
42
|
+
class={$styles.input}
|
|
43
|
+
id={$fieldStore.id}
|
|
44
|
+
value={$fieldStore.value ?? ""}
|
|
45
|
+
placeholder={$fieldStore.label}
|
|
46
|
+
on:input={on_input}
|
|
47
|
+
aria-invalid={$fieldStore.validation_result?.type == "error" ? true : undefined}
|
|
48
|
+
aria-describedby={$fieldStore.validation_result ? `message-${$fieldStore.id}` : undefined}
|
|
49
|
+
/>
|
|
50
|
+
<FieldMessage {fieldStore} />
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<style>
|
|
54
|
+
.text-field {
|
|
55
|
+
width: 100%;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
input {
|
|
59
|
+
box-sizing: border-box;
|
|
60
|
+
display: block;
|
|
61
|
+
width: 100%;
|
|
62
|
+
}
|
|
63
|
+
</style>
|