@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
@@ -2,6 +2,7 @@ import { SvelteComponent } from "svelte";
2
2
  import type { Writable } from "svelte/store";
3
3
  import type { UploadedFile } from "./fileserver";
4
4
  import type { IFormDependencies, IInitialForm } from "./interfaces";
5
+ import { type FormFieldStyles } from "./styles";
5
6
  declare const __propDef: {
6
7
  props: {
7
8
  initial_form: IInitialForm;
@@ -10,6 +11,7 @@ declare const __propDef: {
10
11
  dependencies?: IFormDependencies | undefined;
11
12
  form_valid?: boolean;
12
13
  dynamic_field_type?: boolean;
14
+ styles?: FormFieldStyles | undefined;
13
15
  };
14
16
  events: {
15
17
  [evt: string]: CustomEvent<any>;
@@ -0,0 +1,76 @@
1
+ <script>export let status;
2
+ $: label = status === "uploaded" ? "Uploaded" : status === "failed" ? "Upload failed" : "Uploading";
3
+ </script>
4
+
5
+ <span class="upload-status upload-status-{status}" aria-label={label} role="status">
6
+ {#if status === "uploaded"}
7
+ <svg viewBox="0 0 24 24" aria-hidden="true">
8
+ <path d="m6.75 12.25 3.25 3.25 7.25-7.25" />
9
+ </svg>
10
+ {:else if status === "failed"}
11
+ <svg viewBox="0 0 24 24" aria-hidden="true">
12
+ <path d="M12 7.5v5.25M12 16.5h.01" />
13
+ </svg>
14
+ {:else}
15
+ <span class="upload-spinner" aria-hidden="true" />
16
+ {/if}
17
+ </span>
18
+
19
+ <style>
20
+ .upload-status {
21
+ display: inline-grid;
22
+ box-sizing: border-box;
23
+ width: var(--form-upload-status-size, 1.75rem);
24
+ height: var(--form-upload-status-size, 1.75rem);
25
+ flex: none;
26
+ place-items: center;
27
+ border-radius: 999px;
28
+ }
29
+
30
+ .upload-status-uploaded {
31
+ color: var(--form-upload-success-color, #15803d);
32
+ background: var(--form-upload-success-background, #dcfce7);
33
+ }
34
+
35
+ .upload-status-failed {
36
+ color: var(--form-upload-failure-color, #b91c1c);
37
+ background: var(--form-upload-failure-background, #fee2e2);
38
+ }
39
+
40
+ .upload-status-uploading {
41
+ color: var(--form-upload-progress-color, #2563eb);
42
+ background: var(--form-upload-progress-background, #dbeafe);
43
+ }
44
+
45
+ svg {
46
+ width: 1.2rem;
47
+ height: 1.2rem;
48
+ fill: none;
49
+ stroke: currentColor;
50
+ stroke-width: 2.5;
51
+ stroke-linecap: round;
52
+ stroke-linejoin: round;
53
+ }
54
+
55
+ .upload-spinner {
56
+ box-sizing: border-box;
57
+ width: 1rem;
58
+ height: 1rem;
59
+ border: 2px solid currentColor;
60
+ border-right-color: transparent;
61
+ border-radius: 999px;
62
+ animation: upload-spin 0.7s linear infinite;
63
+ }
64
+
65
+ @keyframes upload-spin {
66
+ to {
67
+ transform: rotate(360deg);
68
+ }
69
+ }
70
+
71
+ @media (prefers-reduced-motion: reduce) {
72
+ .upload-spinner {
73
+ animation-duration: 1.5s;
74
+ }
75
+ }
76
+ </style>
@@ -0,0 +1,18 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ status: "uploading" | "uploaded" | "failed";
5
+ };
6
+ events: {
7
+ [evt: string]: CustomEvent<any>;
8
+ };
9
+ slots: {};
10
+ exports?: {} | undefined;
11
+ bindings?: string | undefined;
12
+ };
13
+ export type UploadStatusProps = typeof __propDef.props;
14
+ export type UploadStatusEvents = typeof __propDef.events;
15
+ export type UploadStatusSlots = typeof __propDef.slots;
16
+ export default class UploadStatus extends SvelteComponent<UploadStatusProps, UploadStatusEvents, UploadStatusSlots> {
17
+ }
18
+ export {};
@@ -0,0 +1,35 @@
1
+ <script>import { getContext } from "svelte";
2
+ import { STYLES_CONTEXT } from "../styles";
3
+ export let item;
4
+ export let first;
5
+ export let last;
6
+ export let moveUp;
7
+ export let moveDown;
8
+ const styles = getContext(STYLES_CONTEXT);
9
+ </script>
10
+
11
+ <div class="field-builder-reorder">
12
+ <button
13
+ type="button"
14
+ class="field-builder-reorder-up {$styles.button ?? ''}"
15
+ data-size="icon"
16
+ disabled={first}
17
+ on:click={moveUp}
18
+ aria-label="Move {item} up">↑</button
19
+ >
20
+ <button
21
+ type="button"
22
+ class="field-builder-reorder-down {$styles.button ?? ''}"
23
+ data-size="icon"
24
+ disabled={last}
25
+ on:click={moveDown}
26
+ aria-label="Move {item} down">↓</button
27
+ >
28
+ </div>
29
+
30
+ <style>
31
+ .field-builder-reorder {
32
+ display: flex;
33
+ flex: none;
34
+ }
35
+ </style>
@@ -0,0 +1,22 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ item: string;
5
+ first: boolean;
6
+ last: boolean;
7
+ moveUp: () => void;
8
+ moveDown: () => void;
9
+ };
10
+ events: {
11
+ [evt: string]: CustomEvent<any>;
12
+ };
13
+ slots: {};
14
+ exports?: {} | undefined;
15
+ bindings?: string | undefined;
16
+ };
17
+ export type FieldBuilderReorderProps = typeof __propDef.props;
18
+ export type FieldBuilderReorderEvents = typeof __propDef.events;
19
+ export type FieldBuilderReorderSlots = typeof __propDef.slots;
20
+ export default class FieldBuilderReorder extends SvelteComponent<FieldBuilderReorderProps, FieldBuilderReorderEvents, FieldBuilderReorderSlots> {
21
+ }
22
+ export {};
@@ -10,16 +10,17 @@
10
10
  };
11
11
  </script>
12
12
 
13
- <script>import { Button } from "@stubber/ui/button";
14
- import * as Collapsible from "@stubber/ui/collapsible";
15
- import { Label } from "@stubber/ui/label";
13
+ <script>import { getContext } from "svelte";
16
14
  import { get as get_store_value } from "svelte/store";
17
15
  import FieldExprToggle from "../FieldExprToggle.svelte";
18
16
  import FieldMessage from "../FieldMessage.svelte";
19
17
  import FormField from "../form-field.svelte";
18
+ import CollapsibleSection from "../CollapsibleSection.svelte";
19
+ import { STYLES_CONTEXT } from "../styles";
20
20
  import { build_field } from "../utils";
21
21
  import { validate_field } from "../validations/validate_field";
22
22
  export let fieldStore;
23
+ const styles = getContext(STYLES_CONTEXT);
23
24
  const new_entry_field = $fieldStore?.params?.new_entry_field || {
24
25
  fieldtype: "text"
25
26
  };
@@ -95,45 +96,110 @@ const validate_sub_fields = async (field) => {
95
96
  };
96
97
  </script>
97
98
 
98
- <Collapsible.Root bind:open class="w-full">
99
- <Collapsible.Trigger asChild let:builder>
100
- <Button builders={[builder]} variant="ghost" class="w-full justify-between px-0">
101
- <Label class={section_has_errors ? "font-bold text-red-500" : ""}>{$fieldStore.label}</Label>
99
+ <div
100
+ class="array-builder-field"
101
+ class:array-builder-field-invalid={section_has_errors}
102
+ aria-invalid={section_has_errors ? true : undefined}
103
+ >
104
+ <CollapsibleSection
105
+ title={$fieldStore.label}
106
+ bind:open
107
+ contentId="content-{$fieldStore.id}"
108
+ labelledBy="label-{$fieldStore.id}"
109
+ >
110
+ <div slot="heading" class="array-builder-heading">
111
+ <span id="label-{$fieldStore.id}" class="array-builder-label">
112
+ {$fieldStore.label}
113
+ </span>
102
114
  <FieldMessage {fieldStore} />
103
-
104
- <div class="flex items-center">
105
- {#if open}
106
- <i class="fa fa-caret-down pr-1" />
107
- {:else}
108
- <i class="fa fa-caret-right pr-1" />
109
- {/if}
110
- <FieldExprToggle {fieldStore} />
111
- </div>
112
- </Button>
113
- </Collapsible.Trigger>
114
- <Collapsible.Content class="flex flex-col gap-y-1 pl-2">
115
+ </div>
116
+ <div slot="actions" class="array-builder-header-actions">
117
+ <FieldExprToggle {fieldStore} />
118
+ </div>
115
119
  {#each $fieldStore.sub_fields || [] as sub_field, index (get_store_value(sub_field).id)}
116
- <div class="flex w-full items-center space-x-2">
117
- <!-- up and down sort buttons -->
118
- <div class="flex flex-col items-center space-y-1">
119
- <Button variant="ghost" class="h-6 w-6 p-2" on:click={() => move_value_up(index)}>
120
- <i class="fa fa-chevron-up" />
121
- </Button>
122
- <Button variant="ghost" class="h-6 w-6 p-2" on:click={() => move_value_down(index)}>
123
- <i class="fa fa-chevron-down" />
124
- </Button>
120
+ <div class="array-builder-item">
121
+ <div class="array-builder-value">
122
+ <FormField fieldStore={sub_field} />
125
123
  </div>
126
- <FormField fieldStore={sub_field} />
127
- <Button variant="destructive" class="h-6 w-6 p-2" on:click={() => remove_value(index)}>
128
- <i class="fa fa-trash" />
129
- </Button>
124
+ <button
125
+ type="button"
126
+ class={$styles.button}
127
+ data-size="icon"
128
+ disabled={index === 0}
129
+ aria-label="Move {$fieldStore.label} item {index + 1} up"
130
+ on:click={() => move_value_up(index)}
131
+ >
132
+ <span aria-hidden="true">↑</span>
133
+ </button>
134
+ <button
135
+ type="button"
136
+ class={$styles.button}
137
+ data-size="icon"
138
+ disabled={index === array_value.length - 1}
139
+ aria-label="Move {$fieldStore.label} item {index + 1} down"
140
+ on:click={() => move_value_down(index)}
141
+ >
142
+ <span aria-hidden="true">↓</span>
143
+ </button>
144
+ <button
145
+ type="button"
146
+ class={$styles.button}
147
+ data-variant="destructive"
148
+ data-size="icon"
149
+ aria-label="Remove {$fieldStore.label} item {index + 1}"
150
+ on:click={() => remove_value(index)}
151
+ >
152
+ <span aria-hidden="true">×</span>
153
+ </button>
130
154
  </div>
131
155
  {/each}
132
- <div class="flex items-center justify-between">
133
- <Button size="icon" variant="outline" on:click={add_value}>
134
- <i class="fa fa-plus" />
135
- </Button>
156
+ <div class="array-builder-footer">
157
+ <button
158
+ type="button"
159
+ class={$styles.button}
160
+ data-variant="primary"
161
+ aria-label="Add {$fieldStore.label} item"
162
+ on:click={add_value}
163
+ >
164
+ <span aria-hidden="true">+</span>
165
+ <span>Add item</span>
166
+ </button>
136
167
  <FieldExprToggle {fieldStore} />
137
168
  </div>
138
- </Collapsible.Content>
139
- </Collapsible.Root>
169
+ </CollapsibleSection>
170
+ </div>
171
+
172
+ <style>
173
+ .array-builder-field,
174
+ .array-builder-item,
175
+ .array-builder-value {
176
+ box-sizing: border-box;
177
+ width: 100%;
178
+ min-width: 0;
179
+ }
180
+
181
+ .array-builder-header-actions,
182
+ .array-builder-reorder-actions,
183
+ .array-builder-footer {
184
+ display: flex;
185
+ align-items: center;
186
+ }
187
+
188
+ .array-builder-heading,
189
+ .array-builder-value {
190
+ flex: 1;
191
+ min-width: 0;
192
+ }
193
+
194
+ .array-builder-header-actions,
195
+ .array-builder-reorder-actions,
196
+ .array-builder-remove {
197
+ flex: none;
198
+ }
199
+
200
+ .array-builder-item {
201
+ display: flex;
202
+ align-items: center;
203
+ gap: 0.5rem;
204
+ }
205
+ </style>
@@ -0,0 +1,153 @@
1
+ <script context="module"></script>
2
+
3
+ <script>import { getContext } from "svelte";
4
+ import ChevronDownIcon from "../icons/ChevronDownIcon.svelte";
5
+ import { STYLES_CONTEXT, classNames } from "../styles";
6
+ import CheckboxField, {} from "./checkbox-field.svelte";
7
+ import SmartTextField, {} from "./smart-text-field.svelte";
8
+ import { isEqual } from "lodash-es";
9
+ export let fieldStore;
10
+ const styles = getContext(STYLES_CONTEXT);
11
+ $fieldStore.without_value_details = true;
12
+ let selected_option = $fieldStore.value === void 0 || typeof $fieldStore.value === "boolean" || isEqual($fieldStore.value, $fieldStore.params?.checkedvalue || true) ? "Checkbox" : "Smart Text";
13
+ const options = ["Checkbox", "Smart Text"];
14
+ let open = false;
15
+ function select_option(new_option) {
16
+ selected_option = new_option;
17
+ switch (selected_option) {
18
+ case "Checkbox":
19
+ $fieldStore.value = false;
20
+ break;
21
+ case "Smart Text":
22
+ $fieldStore.value = "";
23
+ break;
24
+ default:
25
+ break;
26
+ }
27
+ open = false;
28
+ }
29
+ function handle_focusout(event) {
30
+ const nextTarget = event.relatedTarget;
31
+ const currentTarget = event.currentTarget;
32
+ if (!(nextTarget instanceof Node) || !currentTarget.contains(nextTarget)) open = false;
33
+ }
34
+ function handle_keydown(event) {
35
+ if (event.key === "Escape" && open) {
36
+ event.preventDefault();
37
+ open = false;
38
+ }
39
+ }
40
+ function preserve_mouse_focus(node) {
41
+ const prevent_default = (event) => event.preventDefault();
42
+ node.addEventListener("mousedown", prevent_default);
43
+ return {
44
+ destroy: () => node.removeEventListener("mousedown", prevent_default)
45
+ };
46
+ }
47
+ </script>
48
+
49
+ <svelte:window on:keydown={handle_keydown} />
50
+
51
+ <div class="checkbox-autocomplete">
52
+ <div class="popover" on:focusout={handle_focusout}>
53
+ <button
54
+ type="button"
55
+ class={classNames("trigger", $styles.button)}
56
+ data-size="icon"
57
+ aria-label="Choose input type"
58
+ aria-haspopup="dialog"
59
+ aria-expanded={open}
60
+ aria-controls="checkbox-autocomplete-menu-{$fieldStore.id}"
61
+ on:click={() => (open = !open)}
62
+ >
63
+ <span class:indicator-open={open} class="indicator" aria-hidden="true">
64
+ <ChevronDownIcon />
65
+ </span>
66
+ </button>
67
+ {#if open}
68
+ <div
69
+ id="checkbox-autocomplete-menu-{$fieldStore.id}"
70
+ class={classNames("popup", $styles.popover)}
71
+ role="dialog"
72
+ aria-label="Choose input type"
73
+ use:preserve_mouse_focus
74
+ >
75
+ <div class="option-list" role="radiogroup">
76
+ {#each options as option}
77
+ <label
78
+ class={classNames("option", $styles.option)}
79
+ data-selected={selected_option === option ? true : undefined}
80
+ >
81
+ <input
82
+ class={$styles.choice}
83
+ type="radio"
84
+ name="checkbox-autocomplete-mode-{$fieldStore.id}"
85
+ value={option}
86
+ checked={selected_option === option}
87
+ on:change={() => select_option(option)}
88
+ />
89
+ <span>{option}</span>
90
+ </label>
91
+ {/each}
92
+ </div>
93
+ </div>
94
+ {/if}
95
+ </div>
96
+ {#if selected_option === "Smart Text"}
97
+ <div class="mode-field">
98
+ <SmartTextField {fieldStore} />
99
+ </div>
100
+ {:else}
101
+ <div class="mode-field">
102
+ <CheckboxField {fieldStore} />
103
+ </div>
104
+ {/if}
105
+ </div>
106
+
107
+ <style>
108
+ .checkbox-autocomplete {
109
+ display: flex;
110
+ align-items: flex-start;
111
+ min-width: 0;
112
+ width: 100%;
113
+ }
114
+
115
+ .popover {
116
+ position: relative;
117
+ flex: none;
118
+ }
119
+
120
+ .trigger {
121
+ display: inline-flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ }
125
+
126
+ .indicator {
127
+ display: inline-flex;
128
+ }
129
+
130
+ .popup {
131
+ position: absolute;
132
+ z-index: 20;
133
+ top: calc(100% + 0.25rem);
134
+ left: 0;
135
+ min-width: 10rem;
136
+ overflow: hidden;
137
+ }
138
+
139
+ .option-list {
140
+ display: flex;
141
+ flex-direction: column;
142
+ }
143
+
144
+ .option {
145
+ display: flex;
146
+ align-items: center;
147
+ }
148
+
149
+ .mode-field {
150
+ flex: 1;
151
+ min-width: 0;
152
+ }
153
+ </style>
@@ -0,0 +1,69 @@
1
+ <script context="module">export const checkbox_field_param_spec = {
2
+ spec: {
3
+ fields: {
4
+ checkedvalue: {
5
+ fieldtype: "jsoneditor",
6
+ initvalue: { has_default: true, default: true }
7
+ },
8
+ uncheckedvalue: {
9
+ fieldtype: "jsoneditor",
10
+ initvalue: { has_default: true, default: false }
11
+ }
12
+ }
13
+ }
14
+ };
15
+ </script>
16
+
17
+ <script>import { isEqual } from "lodash-es";
18
+ import { getContext } from "svelte";
19
+ import FieldLabel from "../FieldLabel.svelte";
20
+ import FieldMessage from "../FieldMessage.svelte";
21
+ import { STYLES_CONTEXT } from "../styles";
22
+ import { set_value_details } from "../utils";
23
+ export let fieldStore;
24
+ const styles = getContext(STYLES_CONTEXT);
25
+ set_value_details($fieldStore, "label", $fieldStore.label);
26
+ $: checked = isEqual($fieldStore.value, $fieldStore.params?.checkedvalue ?? true);
27
+ const on_change = (event) => {
28
+ const new_value = event.currentTarget.checked;
29
+ const checked_value = $fieldStore.params?.checkedvalue ?? true;
30
+ const unchecked_value = $fieldStore.params?.uncheckedvalue ?? false;
31
+ if (new_value) {
32
+ $fieldStore.value = checked_value;
33
+ } else {
34
+ $fieldStore.value = unchecked_value;
35
+ }
36
+ };
37
+ </script>
38
+
39
+ <div class="checkbox-field">
40
+ <div
41
+ class="checkbox-line"
42
+ aria-invalid={$fieldStore.validation_result?.type === "error" ? true : undefined}
43
+ >
44
+ <input
45
+ class={$styles.choice}
46
+ id={$fieldStore.id}
47
+ type="checkbox"
48
+ {checked}
49
+ aria-invalid={$fieldStore.validation_result?.type === "error" ? true : undefined}
50
+ aria-describedby="message-{$fieldStore.id}"
51
+ on:change={on_change}
52
+ />
53
+ <FieldLabel {fieldStore} with_margin={false} />
54
+ </div>
55
+ <FieldMessage {fieldStore} />
56
+ </div>
57
+
58
+ <style>
59
+ .checkbox-field {
60
+ display: flex;
61
+ flex-direction: column;
62
+ }
63
+
64
+ .checkbox-line {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: 0.5rem;
68
+ }
69
+ </style>
@@ -14,10 +14,12 @@
14
14
  };
15
15
  </script>
16
16
 
17
- <script>import { Button } from "@stubber/ui/button";
17
+ <script>import { getContext } from "svelte";
18
+ import { STYLES_CONTEXT, classNames } from "../styles";
18
19
  import SelectField, {
19
20
  } from "./select-field.svelte";
20
21
  export let fieldStore;
22
+ const styles = getContext(STYLES_CONTEXT);
21
23
  const params = $fieldStore.params || {};
22
24
  const dependencies = $fieldStore.formDependencies;
23
25
  const createWantToModal = dependencies?.createWantToModal;
@@ -69,12 +71,13 @@ const load_options = async (search_term) => {
69
71
  </script>
70
72
 
71
73
  <SelectField {fieldStore} {load_options}>
72
- <Button
74
+ <button
75
+ type="button"
76
+ class={$styles.button}
77
+ data-variant="primary"
73
78
  on:click={show_create_contact_modal}
74
- variant="ghost"
75
- class="ml-auto text-sm text-surface-500 hover:text-surface-700"
76
79
  >
77
- <i class="fa-regular fa-plus" />
80
+ <span aria-hidden="true">+</span>
78
81
  Create new contact
79
- </Button>
82
+ </button>
80
83
  </SelectField>