@stubber/form-fields 2.2.6 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +63 -25
  2. package/dist/form-fields/Autocomplete.svelte +261 -0
  3. package/dist/form-fields/Autocomplete.svelte.d.ts +43 -0
  4. package/dist/form-fields/CollapsibleSection.svelte +127 -0
  5. package/dist/form-fields/CollapsibleSection.svelte.d.ts +26 -0
  6. package/dist/form-fields/FieldLabel.svelte +18 -3
  7. package/dist/form-fields/FieldLabel.svelte.d.ts +1 -0
  8. package/dist/form-fields/FieldMessage.svelte +20 -9
  9. package/dist/form-fields/Form.svelte +19 -2
  10. package/dist/form-fields/Form.svelte.d.ts +2 -0
  11. package/dist/form-fields/UploadStatus.svelte +76 -0
  12. package/dist/form-fields/UploadStatus.svelte.d.ts +18 -0
  13. package/dist/form-fields/fields/_field-builder-reorder.svelte +35 -0
  14. package/dist/form-fields/fields/_field-builder-reorder.svelte.d.ts +22 -0
  15. package/dist/form-fields/{sub → fields}/array-builder-field.svelte +104 -38
  16. package/dist/form-fields/fields/checkbox-autocomplete.svelte +153 -0
  17. package/dist/form-fields/fields/checkbox-field.svelte +69 -0
  18. package/dist/form-fields/{sub → fields}/contact-selector-field.svelte +9 -6
  19. package/dist/form-fields/{sub → fields}/currency-field.svelte +108 -70
  20. package/dist/form-fields/fields/data-indication-field.svelte +39 -0
  21. package/dist/form-fields/fields/date-field.svelte +31 -0
  22. package/dist/form-fields/fields/date-time-field.svelte +31 -0
  23. package/dist/form-fields/{sub → fields}/email-field.svelte +25 -11
  24. package/dist/form-fields/fields/field-builder-field.svelte +568 -0
  25. package/dist/form-fields/fields/file-field.svelte +284 -0
  26. package/dist/form-fields/{sub → fields}/file-field.svelte.d.ts +2 -0
  27. package/dist/form-fields/fields/grid-field.svelte +152 -0
  28. package/dist/form-fields/{sub → fields}/heading-field.svelte +7 -4
  29. package/dist/form-fields/{sub → fields}/html-field.svelte +6 -4
  30. package/dist/form-fields/{sub → fields}/map-field.svelte +121 -78
  31. package/dist/form-fields/{sub → fields}/multi-checkbox-field.svelte +35 -20
  32. package/dist/form-fields/fields/multistep-field.svelte +135 -0
  33. package/dist/form-fields/fields/note-field.svelte +35 -0
  34. package/dist/form-fields/{sub → fields}/number-field.svelte +29 -14
  35. package/dist/form-fields/fields/object-builder-field.svelte +176 -0
  36. package/dist/form-fields/fields/qr-code-scanner-field.svelte +203 -0
  37. package/dist/form-fields/fields/radio-field.svelte +82 -0
  38. package/dist/form-fields/{sub → fields}/radio-field.svelte.d.ts +1 -1
  39. package/dist/form-fields/fields/screenrecorder-field.svelte +255 -0
  40. package/dist/form-fields/{sub → fields}/screenshot-field.svelte +101 -40
  41. package/dist/form-fields/{sub → fields}/scroll-and-read-display-field.svelte +43 -27
  42. package/dist/form-fields/fields/section-field.svelte +73 -0
  43. package/dist/form-fields/fields/select-field.svelte +104 -0
  44. package/dist/form-fields/{sub → fields}/signature-field.svelte +78 -22
  45. package/dist/form-fields/fields/slider-field.svelte +53 -0
  46. package/dist/form-fields/{sub → fields}/smart-text-field.svelte +62 -82
  47. package/dist/form-fields/fields/telephone-field.svelte +108 -0
  48. package/dist/form-fields/{sub → fields}/telephone-field.svelte.d.ts +1 -1
  49. package/dist/form-fields/{sub → fields}/text-field.svelte +28 -11
  50. package/dist/form-fields/{sub → fields}/voicenote-field.svelte +117 -46
  51. package/dist/form-fields/file_creation_options.d.ts +1 -1
  52. package/dist/form-fields/form-field.svelte +8 -1
  53. package/dist/form-fields/icons/ChevronDownIcon.svelte +21 -0
  54. package/dist/form-fields/icons/ChevronDownIcon.svelte.d.ts +20 -0
  55. package/dist/form-fields/icons/QrCodeScannerIcon.svelte +25 -0
  56. package/dist/form-fields/icons/QrCodeScannerIcon.svelte.d.ts +19 -0
  57. package/dist/form-fields/icons/ScreenRecordingIcon.svelte +21 -0
  58. package/dist/form-fields/icons/ScreenRecordingIcon.svelte.d.ts +19 -0
  59. package/dist/form-fields/icons/ScreenshotIcon.svelte +22 -0
  60. package/dist/form-fields/icons/ScreenshotIcon.svelte.d.ts +19 -0
  61. package/dist/form-fields/index.d.ts +5 -1
  62. package/dist/form-fields/index.js +3 -1
  63. package/dist/form-fields/interfaces.d.ts +63 -63
  64. package/dist/form-fields/interfaces.js +42 -39
  65. package/dist/form-fields/intl-tel-input.css +1 -0
  66. package/dist/form-fields/styles.d.ts +17 -0
  67. package/dist/form-fields/styles.js +4 -0
  68. package/package.json +2 -4
  69. package/dist/form-fields/sub/checkbox-autocomplete.svelte +0 -56
  70. package/dist/form-fields/sub/checkbox-field.svelte +0 -42
  71. package/dist/form-fields/sub/data-indication-field.svelte +0 -19
  72. package/dist/form-fields/sub/date-field.svelte +0 -31
  73. package/dist/form-fields/sub/date-time-field.svelte +0 -31
  74. package/dist/form-fields/sub/field-builder-field.svelte +0 -657
  75. package/dist/form-fields/sub/file-field.svelte +0 -159
  76. package/dist/form-fields/sub/grid-field.svelte +0 -69
  77. package/dist/form-fields/sub/multistep-field.svelte +0 -70
  78. package/dist/form-fields/sub/note-field.svelte +0 -18
  79. package/dist/form-fields/sub/object-builder-field.svelte +0 -134
  80. package/dist/form-fields/sub/qr-code-scanner-field.svelte +0 -84
  81. package/dist/form-fields/sub/radio-field.svelte +0 -69
  82. package/dist/form-fields/sub/screenrecorder-field.svelte +0 -178
  83. package/dist/form-fields/sub/section-field.svelte +0 -50
  84. package/dist/form-fields/sub/select-field.svelte +0 -150
  85. package/dist/form-fields/sub/slider-field.svelte +0 -28
  86. package/dist/form-fields/sub/telephone-field.svelte +0 -64
  87. /package/dist/form-fields/{sub → fields}/array-builder-field.svelte.d.ts +0 -0
  88. /package/dist/form-fields/{sub → fields}/checkbox-autocomplete.svelte.d.ts +0 -0
  89. /package/dist/form-fields/{sub → fields}/checkbox-field.svelte.d.ts +0 -0
  90. /package/dist/form-fields/{sub → fields}/code-field.svelte +0 -0
  91. /package/dist/form-fields/{sub → fields}/code-field.svelte.d.ts +0 -0
  92. /package/dist/form-fields/{sub → fields}/contact-selector-field.svelte.d.ts +0 -0
  93. /package/dist/form-fields/{sub → fields}/currency-field.svelte.d.ts +0 -0
  94. /package/dist/form-fields/{sub → fields}/data-indication-field.svelte.d.ts +0 -0
  95. /package/dist/form-fields/{sub → fields}/date-field.svelte.d.ts +0 -0
  96. /package/dist/form-fields/{sub → fields}/date-time-field.svelte.d.ts +0 -0
  97. /package/dist/form-fields/{sub → fields}/email-field.svelte.d.ts +0 -0
  98. /package/dist/form-fields/{sub → fields}/field-builder-field.svelte.d.ts +0 -0
  99. /package/dist/form-fields/{sub → fields}/grid-field.svelte.d.ts +0 -0
  100. /package/dist/form-fields/{sub → fields}/heading-field.svelte.d.ts +0 -0
  101. /package/dist/form-fields/{sub → fields}/hidden-field.svelte +0 -0
  102. /package/dist/form-fields/{sub → fields}/hidden-field.svelte.d.ts +0 -0
  103. /package/dist/form-fields/{sub → fields}/hidden-location-field.svelte +0 -0
  104. /package/dist/form-fields/{sub → fields}/hidden-location-field.svelte.d.ts +0 -0
  105. /package/dist/form-fields/{sub → fields}/html-field.svelte.d.ts +0 -0
  106. /package/dist/form-fields/{sub → fields}/jsoneditor-field.svelte +0 -0
  107. /package/dist/form-fields/{sub → fields}/jsoneditor-field.svelte.d.ts +0 -0
  108. /package/dist/form-fields/{sub → fields}/map-field.svelte.d.ts +0 -0
  109. /package/dist/form-fields/{sub → fields}/multi-checkbox-field.svelte.d.ts +0 -0
  110. /package/dist/form-fields/{sub → fields}/multistep-field.svelte.d.ts +0 -0
  111. /package/dist/form-fields/{sub → fields}/note-field.svelte.d.ts +0 -0
  112. /package/dist/form-fields/{sub → fields}/number-field.svelte.d.ts +0 -0
  113. /package/dist/form-fields/{sub → fields}/object-builder-field.svelte.d.ts +0 -0
  114. /package/dist/form-fields/{sub → fields}/qr-code-scanner-field.svelte.d.ts +0 -0
  115. /package/dist/form-fields/{sub → fields}/screenrecorder-field.svelte.d.ts +0 -0
  116. /package/dist/form-fields/{sub → fields}/screenshot-field.svelte.d.ts +0 -0
  117. /package/dist/form-fields/{sub → fields}/scroll-and-read-display-field.svelte.d.ts +0 -0
  118. /package/dist/form-fields/{sub → fields}/section-field.svelte.d.ts +0 -0
  119. /package/dist/form-fields/{sub → fields}/select-field.svelte.d.ts +0 -0
  120. /package/dist/form-fields/{sub → fields}/selectresource-field.svelte +0 -0
  121. /package/dist/form-fields/{sub → fields}/selectresource-field.svelte.d.ts +0 -0
  122. /package/dist/form-fields/{sub → fields}/signature-field.svelte.d.ts +0 -0
  123. /package/dist/form-fields/{sub → fields}/slider-field.svelte.d.ts +0 -0
  124. /package/dist/form-fields/{sub → fields}/smart-text-field.svelte.d.ts +0 -0
  125. /package/dist/form-fields/{sub → fields}/text-field.svelte.d.ts +0 -0
  126. /package/dist/form-fields/{sub → fields}/voicenote-field.svelte.d.ts +0 -0
@@ -1,178 +0,0 @@
1
- <script context="module">import {} from "../interfaces";
2
- </script>
3
-
4
- <script>import { Button } from "@stubber/ui/button";
5
- import { snakeCase } from "lodash-es";
6
- import FieldLabel from "../FieldLabel.svelte";
7
- import FieldMessage from "../FieldMessage.svelte";
8
- import {
9
- append_attachment,
10
- remove_attachment,
11
- uploadFiles
12
- } from "../fileserver";
13
- export let fieldStore;
14
- const file_name_prefix = snakeCase($fieldStore.label || "Screenshot");
15
- let fileList = [];
16
- let mediaRecorder = null;
17
- $: isRecording = mediaRecorder?.state === "recording";
18
- $: buttonLabel = isRecording ? "Stop Recording" : "Start Recording";
19
- let max_files_param = $fieldStore.params?.max_files;
20
- let file_creation_options = $fieldStore.params?.file_creation_options;
21
- $: max_files = isNaN(parseInt(max_files_param)) ? Infinity : parseInt(max_files_param);
22
- $: limit_remaining = max_files - fileList.length;
23
- const toggleRecording = () => {
24
- if (mediaRecorder?.state === "recording") {
25
- stopRecording();
26
- } else {
27
- startRecording();
28
- }
29
- };
30
- async function startRecording() {
31
- if (limit_remaining <= 0) {
32
- alert("You have reached the maximum number of files allowed");
33
- return;
34
- }
35
- if (!navigator.mediaDevices.getDisplayMedia) {
36
- alert("Screen capture is not supported in this browser.");
37
- return;
38
- }
39
- const stream = await navigator.mediaDevices.getDisplayMedia({
40
- audio: true,
41
- // @ts-ignore
42
- video: { mediaSource: "screen" }
43
- });
44
- const audiostream = await navigator.mediaDevices.getUserMedia({ audio: true });
45
- const combine = new MediaStream([...stream.getVideoTracks(), ...audiostream.getAudioTracks()]);
46
- mediaRecorder = new MediaRecorder(combine, { mimeType: "video/webm;codecs=vp9" });
47
- const media = [];
48
- mediaRecorder.ondataavailable = (e) => media.push(e.data);
49
- mediaRecorder.onstop = function() {
50
- let id = Math.random().toString(36).substring(7);
51
- let filename = `${$fieldStore.label}_${id}.webm`;
52
- let blob = new Blob(media, { type: "video/webm;codecs=vp9" });
53
- let file = new File([blob], filename, { type: "video/webm;codecs=vp9" });
54
- uploadFile(file, blob, filename);
55
- mediaRecorder = null;
56
- };
57
- mediaRecorder.start();
58
- }
59
- function stopRecording() {
60
- mediaRecorder?.stop();
61
- }
62
- async function uploadFile(file, blob, filename) {
63
- fileList = [
64
- ...fileList,
65
- {
66
- file,
67
- blob,
68
- filename,
69
- is_uploaded: false,
70
- is_failed: false
71
- }
72
- ];
73
- const upload_res = await uploadFiles(
74
- [file],
75
- $fieldStore.formDependencies,
76
- file_creation_options
77
- );
78
- const { uploaded_files = [], failed_files = [] } = upload_res || {};
79
- const newly_uploaded = uploaded_files.find((f) => f.filename === filename) || {};
80
- const is_uploaded = uploaded_files.length > 0;
81
- const is_failed = failed_files.length > 0;
82
- fileList = fileList.map((item) => {
83
- if (item.filename === filename) {
84
- return {
85
- ...item,
86
- ...newly_uploaded,
87
- is_uploaded,
88
- is_failed
89
- };
90
- }
91
- return item;
92
- });
93
- if (newly_uploaded.fileuuid) {
94
- append_attachment(newly_uploaded, $fieldStore.attachmentsStore);
95
- }
96
- }
97
- const update_file_list = (field_value) => {
98
- if (Array.isArray(field_value)) {
99
- fileList = field_value.map((f) => ({
100
- file: f.file,
101
- blob: f.blob,
102
- fileuuid: f.fileuuid,
103
- contentType: f.contentType,
104
- originalname: f.originalname,
105
- filename: f.filename,
106
- is_uploaded: f.is_uploaded || f.fileuuid ? true : false,
107
- is_failed: f.is_failed || false
108
- }));
109
- }
110
- };
111
- update_file_list($fieldStore.value);
112
- $: update_field_value(fileList);
113
- const update_field_value = (files) => {
114
- const uploaded_files = files.filter((f) => f.is_uploaded && f.fileuuid);
115
- $fieldStore.value = uploaded_files.map((f) => ({
116
- filename: f.filename,
117
- fileuuid: f.fileuuid,
118
- contentType: f.contentType,
119
- originalname: f.originalname
120
- }));
121
- };
122
- function removeFile(item) {
123
- fileList = fileList.filter((f) => f !== item);
124
- remove_attachment(item.fileuuid, $fieldStore.attachmentsStore);
125
- }
126
- </script>
127
-
128
- <FieldLabel {fieldStore} />
129
- <div class="flex flex-col items-start gap-2">
130
- <Button variant={isRecording ? "destructive" : "default"} on:click={toggleRecording}>
131
- {#if isRecording}
132
- <i class="fa-solid fa-video-slash" />
133
- {:else}
134
- <i class="fa-solid fa-video" />
135
- {/if}
136
- {buttonLabel}
137
- </Button>
138
- <div class="flex w-full flex-col gap-1">
139
- {#each fileList as item}
140
- <div class="flex w-full flex-row items-center gap-1">
141
- <div class="flex h-6 w-6 shrink-0 items-center justify-center">
142
- {#if item.is_uploaded}
143
- <i class="fa fa-check text-success-500" />
144
- {:else if item.is_failed}
145
- <i class="fa-regular fa-triangle-exclamation text-danger-400" />
146
- {:else}
147
- <i class="fa fa-pulse fa-spinner text-surface-500" />
148
- {/if}
149
- </div>
150
-
151
- {#if item?.blob}
152
- <div class="shrink p-2">
153
- <div class="relative max-w-[200px] overflow-hidden">
154
- <!-- <audio controls src={window.URL.createObjectURL(item.blob)} /> -->
155
- <video controls src={window.URL.createObjectURL(item.blob)} class="w-full">
156
- <track kind="captions" />
157
- </video>
158
- </div>
159
- </div>
160
- {:else}
161
- <div class="w-full shrink truncate rounded-sm border border-surface-200 py-1 pl-2">
162
- <p class="text-fluid-md text-surface-800">
163
- {item?.filename}
164
- </p>
165
- </div>
166
- {/if}
167
- <Button
168
- variant="destructive"
169
- class="h-6 w-6 shrink-0 p-0"
170
- on:click={() => removeFile(item)}
171
- >
172
- <i class="fa-solid fa-2xs fa-x" />
173
- </Button>
174
- </div>
175
- {/each}
176
- </div>
177
- </div>
178
- <FieldMessage {fieldStore} />
@@ -1,50 +0,0 @@
1
- <script context="module">import { get as get_store_value } from "svelte/store";
2
- </script>
3
-
4
- <script>import { Button } from "@stubber/ui/button";
5
- import * as Collapsible from "@stubber/ui/collapsible";
6
- import FieldMessage from "../FieldMessage.svelte";
7
- import FormField from "../form-field.svelte";
8
- import { Label } from "@stubber/ui/label";
9
- import FieldExprToggle from "../FieldExprToggle.svelte";
10
- import { validate_field } from "../validations/validate_field";
11
- export let fieldStore;
12
- let open = true;
13
- $: validate_sub_fields($fieldStore);
14
- let section_has_errors = false;
15
- const validate_sub_fields = async (field) => {
16
- section_has_errors = false;
17
- if (field.sub_fields && field.sub_fields.length > 0) {
18
- for (const sub_field of field.sub_fields || []) {
19
- const { key, errors } = await validate_field(field.formStore, sub_field);
20
- if (errors.length > 0) {
21
- section_has_errors = true;
22
- }
23
- }
24
- }
25
- };
26
- </script>
27
-
28
- <Collapsible.Root bind:open class="w-full">
29
- <Collapsible.Trigger asChild let:builder>
30
- <Button builders={[builder]} variant="ghost" class="w-full justify-between px-0">
31
- <Label class={section_has_errors ? "font-bold text-red-500" : ""}>{$fieldStore.label}</Label>
32
- <FieldMessage {fieldStore} />
33
-
34
- <div class="flex items-center">
35
- {#if open}
36
- <i class="fa fa-caret-down pr-1" />
37
- {:else}
38
- <i class="fa fa-caret-right pr-1" />
39
- {/if}
40
- <FieldExprToggle {fieldStore} />
41
- </div>
42
- </Button>
43
- </Collapsible.Trigger>
44
-
45
- <Collapsible.Content class="flex flex-col gap-y-2 pl-2">
46
- {#each $fieldStore.sub_fields || [] as sub_field (get_store_value(sub_field).id)}
47
- <FormField fieldStore={sub_field} />
48
- {/each}
49
- </Collapsible.Content>
50
- </Collapsible.Root>
@@ -1,150 +0,0 @@
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 { Button } from "@stubber/ui/button";
26
- import * as Command from "@stubber/ui/command";
27
- import * as Popover from "@stubber/ui/popover";
28
- import { createState } from "cmdk-sv";
29
- import { debounce, isEqual } from "lodash-es";
30
- import { onMount, tick } from "svelte";
31
- import FieldLabel from "../FieldLabel.svelte";
32
- import FieldMessage from "../FieldMessage.svelte";
33
- import { clear_value_details, set_value_details } from "../utils";
34
- export let fieldStore;
35
- export let load_options = void 0;
36
- let open = false;
37
- let loading = false;
38
- let items = [];
39
- const state = createState();
40
- $: dropdownLabel = items.find((i) => isEqual(i.value, $fieldStore.value))?.label ?? $fieldStore.value?._default_label ?? "Select...";
41
- $: update_value_details_label(dropdownLabel);
42
- const update_value_details_label = (label) => {
43
- if (!label || label === "Select...") {
44
- clear_value_details($fieldStore, "label");
45
- return;
46
- }
47
- set_value_details($fieldStore, "label", label);
48
- };
49
- const map_field_options = (options) => {
50
- if (!options || !Array.isArray(options)) {
51
- return [];
52
- }
53
- return options.filter(Boolean).map((option, index) => {
54
- const key = `${option.label}${index}`;
55
- return {
56
- label: option.label,
57
- value: option.value,
58
- key
59
- };
60
- });
61
- };
62
- const params = $fieldStore.params || {};
63
- items = map_field_options(params?.options);
64
- const get_selected_item_from_key = (key) => {
65
- return items.find((item) => item.key === key);
66
- };
67
- function closeAndFocusTrigger(triggerId) {
68
- open = false;
69
- tick().then(() => {
70
- document.getElementById(triggerId)?.focus();
71
- });
72
- }
73
- let last_search = void 0;
74
- const handle_search = async (search) => {
75
- last_search = search;
76
- if (load_options) {
77
- loading = true;
78
- const remote_results = await load_options($state.search);
79
- loading = false;
80
- items = map_field_options(remote_results);
81
- }
82
- };
83
- let debounced_handle_search = null;
84
- if (load_options) {
85
- debounced_handle_search = debounce(handle_search, 300);
86
- }
87
- $: if (load_options && $state.search !== void 0 && $state.search !== last_search) {
88
- if (debounced_handle_search) debounced_handle_search($state.search);
89
- }
90
- const handle_select = (item_key, trigger) => {
91
- const selected_item = get_selected_item_from_key(item_key);
92
- if (selected_item) {
93
- $fieldStore.value = selected_item.value;
94
- }
95
- closeAndFocusTrigger(trigger);
96
- };
97
- let triggerEl;
98
- let portalTarget;
99
- onMount(() => {
100
- const root = triggerEl?.getRootNode();
101
- portalTarget = root instanceof ShadowRoot ? null : document.body;
102
- });
103
- </script>
104
-
105
- <div class="invisible hidden" bind:this={triggerEl} />
106
-
107
- <FieldLabel {fieldStore} />
108
- <Popover.Root bind:open let:ids portal={portalTarget}>
109
- <Popover.Trigger asChild let:builder>
110
- <Button
111
- builders={[builder]}
112
- variant="outline"
113
- role="combobox"
114
- aria-expanded={open}
115
- class="flex h-10 w-full justify-between rounded-md border border-input bg-white bg-opacity-[15] px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid]:border-destructive aria-[invalid]:ring-destructive/50 aria-[invalid]:focus-visible:ring-destructive/50"
116
- style="box-shadow: inset 0px 16px 16px -16px rgba(0, 0, 0, 0.1333);"
117
- aria-invalid={$fieldStore.validation_result?.type == "error" ? true : undefined}
118
- aria-describedby="message-{$fieldStore.id}"
119
- >
120
- {dropdownLabel}
121
- <i class="fas fa-sort ml-2 h-4 w-4 shrink-0 opacity-50" />
122
- </Button>
123
- </Popover.Trigger>
124
- <Popover.Content sameWidth class="z-[100] p-0">
125
- <Command.Root shouldFilter={!load_options} {state}>
126
- <Command.Input class="border-none outline-none focus:ring-0" placeholder="Search..." />
127
- <Command.List>
128
- {#if loading}
129
- <Command.Loading class="py-6 text-center text-sm">Loading…</Command.Loading>
130
- {:else}
131
- <Command.Empty>No results found.</Command.Empty>
132
-
133
- {#each items as item}
134
- <Command.Item
135
- value={item.key}
136
- onSelect={(item_key) => handle_select(item_key, ids.trigger)}
137
- class={isEqual(item.value, $fieldStore.value) ? "bg-primary-100" : ""}
138
- >
139
- {item.label}
140
- </Command.Item>
141
- {/each}
142
- {/if}
143
- </Command.List>
144
- </Command.Root>
145
- </Popover.Content>
146
- </Popover.Root>
147
- <div class="flex items-center gap-2">
148
- <FieldMessage {fieldStore} />
149
- <slot />
150
- </div>
@@ -1,28 +0,0 @@
1
- <script context="module"></script>
2
-
3
- <script>import FieldLabel from "../FieldLabel.svelte";
4
- import FieldMessage from "../FieldMessage.svelte";
5
- export let fieldStore;
6
- $: min = $fieldStore.min ?? 0;
7
- $: max = $fieldStore.max ?? 100;
8
- $: step = $fieldStore.step ?? 1;
9
- </script>
10
-
11
- <FieldLabel {fieldStore} />
12
- <div class="relative">
13
- <div class="flex justify-between items-end text-sm text-surface-300">
14
- <div>{min}</div>
15
- <div class="text-md text-surface-700">{$fieldStore.value || ""}</div>
16
- <div>{max}</div>
17
- </div>
18
- <input
19
- class="block w-full text-field rounded-md border-0 mb-2 focus:outline-none"
20
- bind:value={$fieldStore.value}
21
- {min}
22
- {max}
23
- {step}
24
- type="range"
25
- aria-invalid={$fieldStore.validation_result?.type == "error" ? true : undefined}
26
- />
27
- </div>
28
- <FieldMessage {fieldStore} />
@@ -1,64 +0,0 @@
1
- <script context="module">import {} from "../interfaces";
2
- </script>
3
-
4
- <script>import intlTelInput, { Iti } from "intl-tel-input";
5
- import "intl-tel-input/styles";
6
- import FieldLabel from "../FieldLabel.svelte";
7
- import FieldMessage from "../FieldMessage.svelte";
8
- import { set_value_details } from "../utils";
9
- export let fieldStore;
10
- let iti = null;
11
- function setupInput(input_element) {
12
- setup_intl_tel_input(input_element);
13
- }
14
- async function setup_intl_tel_input(input_element) {
15
- iti = intlTelInput(input_element, {
16
- loadUtils: () => import("intl-tel-input/utils"),
17
- initialCountryLookup: async () => {
18
- const res = await fetch("https://ipapi.co/json");
19
- const data = await res.json();
20
- return data?.country_code || "us";
21
- },
22
- separateDialCode: false,
23
- containerClass: "w-full"
24
- });
25
- input_element.addEventListener("countrychange", () => set_number_details());
26
- await iti.promise;
27
- set_number_details();
28
- }
29
- let last_set_value = null;
30
- function set_number_details(force = false) {
31
- if (!iti) return;
32
- if (!force && last_set_value === $fieldStore.value) return;
33
- last_set_value = $fieldStore.value;
34
- const formatted_number = iti.getNumber();
35
- const is_valid = iti.isValidNumber();
36
- const country_data = iti.getSelectedCountry();
37
- if (is_valid && formatted_number !== $fieldStore.value) {
38
- $fieldStore.value = formatted_number;
39
- }
40
- if ($fieldStore.value) {
41
- set_value_details($fieldStore, "details", {
42
- is_valid_number: is_valid,
43
- selected_country: country_data
44
- });
45
- }
46
- }
47
- </script>
48
-
49
- <FieldLabel {fieldStore} />
50
- <div
51
- class="relative mt-2 flex w-full space-x-4 rounded-md bg-surface-100 focus:outline-primary-400"
52
- >
53
- <input
54
- use:setupInput
55
- bind:value={$fieldStore.value}
56
- on:input={() => set_number_details()}
57
- type="tel"
58
- id={$fieldStore.id}
59
- class="shadow-xs flex h-9 w-full min-w-0 rounded-md border border-input bg-background px-3 py-1 text-base outline-none ring-offset-background transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid]:border-destructive aria-[invalid]:ring-destructive/50 aria-[invalid]:focus-visible:ring-destructive/50 md:text-sm dark:bg-input/30"
60
- aria-invalid={$fieldStore.validation_result?.type == "error" ? true : undefined}
61
- aria-describedby="message-{$fieldStore.id}"
62
- />
63
- </div>
64
- <FieldMessage {fieldStore} />