@stubber/form-fields 1.6.1 → 1.6.3
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/dist/fields2/sub/contact-selector-field.svelte +44 -46
- package/dist/fields2/sub/contact-selector-field.svelte.d.ts +2 -3
- package/dist/fields2/sub/currency-field.svelte +4 -4
- package/dist/fields2/sub/select-field.svelte +6 -6
- package/dist/fields2/sub/select-field.svelte.d.ts +1 -1
- package/dist/fields2/sub/selectresource-field.svelte +1 -4
- package/dist/fields2/sub/smart-text-field.svelte +61 -22
- package/package.json +3 -2
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
17
|
<script>import { Button } from "@stubber/ui/button";
|
|
18
|
-
import SelectField, {
|
|
18
|
+
import SelectField, {
|
|
19
|
+
} from "./select-field.svelte";
|
|
19
20
|
export let fieldStore;
|
|
20
21
|
const params = $fieldStore.params || {};
|
|
21
22
|
const dependencies = $fieldStore.formDependencies;
|
|
@@ -25,54 +26,51 @@ const show_create_contact_modal = () => {
|
|
|
25
26
|
const orguuid = dependencies?.stubber?.orguuid;
|
|
26
27
|
createWantToModal("_create_new_contact", stubref, orguuid);
|
|
27
28
|
};
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const details = {
|
|
39
|
-
resource_name: "contacts"
|
|
40
|
-
};
|
|
41
|
-
details.params = $fieldStore.params || {};
|
|
42
|
-
details.params.orguuid = orguuid;
|
|
43
|
-
details.params.stubref = stubref;
|
|
44
|
-
details.params.input = search_term;
|
|
45
|
-
if (details.params.limit === void 0) {
|
|
46
|
-
details.params.limit = 50;
|
|
47
|
-
}
|
|
48
|
-
console.log("Loading contacts with details:", details);
|
|
49
|
-
return new Promise((resolve, reject) => {
|
|
50
|
-
socket.emit("request", { type: "resource", details }, (res) => {
|
|
51
|
-
if (res.success) {
|
|
52
|
-
console.log("Contacts loaded:", res);
|
|
53
|
-
const contacts = res.payload?.contacts || [];
|
|
54
|
-
const result = contacts.map((contact) => {
|
|
55
|
-
let label = contact._default_label;
|
|
56
|
-
if (params.label && contact[params.label]) {
|
|
57
|
-
label = contact[params.label];
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
value: contact,
|
|
61
|
-
label
|
|
62
|
-
};
|
|
63
|
-
});
|
|
64
|
-
resolve(result);
|
|
65
|
-
} else {
|
|
66
|
-
console.error("Failed to load contacts:", res);
|
|
67
|
-
resolve([]);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
});
|
|
29
|
+
const load_options = async (search_term) => {
|
|
30
|
+
const socket = dependencies?.clienthub?.socket;
|
|
31
|
+
const orguuid = dependencies?.stubber?.orguuid;
|
|
32
|
+
const stubref = dependencies?.stubber?.stubref;
|
|
33
|
+
if (!socket || !orguuid || !stubref) {
|
|
34
|
+
console.error("ClientHub socket or stubber details not available.");
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
const details = {
|
|
38
|
+
resource_name: "contacts"
|
|
71
39
|
};
|
|
72
|
-
}
|
|
40
|
+
details.params = $fieldStore.params || {};
|
|
41
|
+
details.params.orguuid = orguuid;
|
|
42
|
+
details.params.stubref = stubref;
|
|
43
|
+
details.params.input = search_term;
|
|
44
|
+
if (details.params.limit === void 0) {
|
|
45
|
+
details.params.limit = 50;
|
|
46
|
+
}
|
|
47
|
+
console.log("Loading contacts with details:", details);
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
socket.emit("request", { type: "resource", details }, (res) => {
|
|
50
|
+
if (res.success) {
|
|
51
|
+
console.log("Contacts loaded:", res);
|
|
52
|
+
const contacts = res.payload?.contacts || [];
|
|
53
|
+
const result = contacts.map((contact) => {
|
|
54
|
+
let label = contact._default_label;
|
|
55
|
+
if (params.label && contact[params.label]) {
|
|
56
|
+
label = contact[params.label];
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
value: contact,
|
|
60
|
+
label
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
resolve(result);
|
|
64
|
+
} else {
|
|
65
|
+
console.error("Failed to load contacts:", res);
|
|
66
|
+
resolve([]);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
};
|
|
73
71
|
</script>
|
|
74
72
|
|
|
75
|
-
<SelectField {fieldStore}>
|
|
73
|
+
<SelectField {fieldStore} {load_options}>
|
|
76
74
|
<Button
|
|
77
75
|
on:click={show_create_contact_modal}
|
|
78
76
|
variant="ghost"
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
2
|
import type { Writable } from "svelte/store";
|
|
3
3
|
import type { IBaseField, IBuiltField, IInitialForm } from "../interfaces";
|
|
4
|
-
export interface IContactSelectorFieldParams {
|
|
4
|
+
export interface IContactSelectorFieldParams extends ISelectFieldParams {
|
|
5
5
|
label?: string;
|
|
6
6
|
limit?: number;
|
|
7
|
-
load_options?: (search_term: string) => Promise<ISelectFieldOption[]>;
|
|
8
7
|
}
|
|
9
8
|
export declare const contact_selector_field_param_spec: IInitialForm;
|
|
10
9
|
export interface IContactSelectorField extends IBaseField<IContactSelectorFieldParams> {
|
|
11
10
|
fieldtype: "contactselector";
|
|
12
11
|
}
|
|
13
|
-
import { type
|
|
12
|
+
import { type ISelectFieldParams } from "./select-field.svelte";
|
|
14
13
|
declare const __propDef: {
|
|
15
14
|
props: {
|
|
16
15
|
fieldStore: Writable<IBuiltField<IContactSelectorFieldParams>>;
|
|
@@ -130,7 +130,7 @@ $: selectedValue = currency_list.find((c) => c.value === current_details?.curren
|
|
|
130
130
|
|
|
131
131
|
<FieldLabel {fieldStore} />
|
|
132
132
|
<div
|
|
133
|
-
class="flex
|
|
133
|
+
class="flex h-9 w-full min-w-0 flex-row items-center gap-2 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"
|
|
134
134
|
>
|
|
135
135
|
<!-- currency indicator front left -->
|
|
136
136
|
<div class="pointer-events-none text-gray-500">
|
|
@@ -143,7 +143,7 @@ $: selectedValue = currency_list.find((c) => c.value === current_details?.curren
|
|
|
143
143
|
{/if}
|
|
144
144
|
</div>
|
|
145
145
|
<input
|
|
146
|
-
class="w-full focus-within:outline-none
|
|
146
|
+
class="w-full bg-transparent focus-within:outline-none"
|
|
147
147
|
use:inputRegexMask={currencyRegex}
|
|
148
148
|
value={initial_ui_value}
|
|
149
149
|
inputmode="decimal"
|
|
@@ -160,13 +160,13 @@ $: selectedValue = currency_list.find((c) => c.value === current_details?.curren
|
|
|
160
160
|
variant="outline"
|
|
161
161
|
role="combobox"
|
|
162
162
|
aria-expanded={open}
|
|
163
|
-
class="w-28 border-none bg-transparent px-1
|
|
163
|
+
class="w-28 justify-between border-none bg-transparent px-1"
|
|
164
164
|
>
|
|
165
165
|
{selectedValue}
|
|
166
166
|
<i class="fas fa-sort ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
167
167
|
</Button>
|
|
168
168
|
</Popover.Trigger>
|
|
169
|
-
<Popover.Content class="
|
|
169
|
+
<Popover.Content class="max-h-[50vh] overflow-y-scroll p-0">
|
|
170
170
|
<Command.Root>
|
|
171
171
|
<Command.Input placeholder="Search currencies..." />
|
|
172
172
|
<Command.Empty>No currency found.</Command.Empty>
|
|
@@ -33,6 +33,7 @@ import FieldLabel from "../FieldLabel.svelte";
|
|
|
33
33
|
import FieldMessage from "../FieldMessage.svelte";
|
|
34
34
|
import { set_value_details } from "../utils";
|
|
35
35
|
export let fieldStore;
|
|
36
|
+
export let load_options = void 0;
|
|
36
37
|
let open = false;
|
|
37
38
|
let loading = false;
|
|
38
39
|
let items = [];
|
|
@@ -70,19 +71,18 @@ function closeAndFocusTrigger(triggerId) {
|
|
|
70
71
|
let last_search = void 0;
|
|
71
72
|
const handle_search = async (search) => {
|
|
72
73
|
last_search = search;
|
|
73
|
-
if (
|
|
74
|
+
if (load_options) {
|
|
74
75
|
loading = true;
|
|
75
|
-
const remote_results = await
|
|
76
|
+
const remote_results = await load_options($state.search);
|
|
76
77
|
loading = false;
|
|
77
78
|
items = map_field_options(remote_results);
|
|
78
79
|
}
|
|
79
80
|
};
|
|
80
81
|
let debounced_handle_search = null;
|
|
81
|
-
if (
|
|
82
|
+
if (load_options) {
|
|
82
83
|
debounced_handle_search = debounce(handle_search, 300);
|
|
83
84
|
}
|
|
84
|
-
$: if (
|
|
85
|
-
console.log("doing debounced search", !!debounced_handle_search);
|
|
85
|
+
$: if (load_options && $state.search !== void 0 && $state.search !== last_search) {
|
|
86
86
|
if (debounced_handle_search) debounced_handle_search($state.search);
|
|
87
87
|
}
|
|
88
88
|
const handle_select = (item_key, trigger) => {
|
|
@@ -110,7 +110,7 @@ const handle_select = (item_key, trigger) => {
|
|
|
110
110
|
</Button>
|
|
111
111
|
</Popover.Trigger>
|
|
112
112
|
<Popover.Content sameWidth class="p-0 z-[100]">
|
|
113
|
-
<Command.Root shouldFilter={!
|
|
113
|
+
<Command.Root shouldFilter={!load_options} {state}>
|
|
114
114
|
<Command.Input class="border-none outline-none focus:ring-0" placeholder="Search..." />
|
|
115
115
|
<Command.List>
|
|
116
116
|
{#if loading}
|
|
@@ -7,7 +7,6 @@ export interface ISelectFieldOption {
|
|
|
7
7
|
}
|
|
8
8
|
export interface ISelectFieldParams {
|
|
9
9
|
options?: ISelectFieldOption[];
|
|
10
|
-
load_options?: (search_term: string) => Promise<ISelectFieldOption[]>;
|
|
11
10
|
}
|
|
12
11
|
export declare const select_field_param_spec: IInitialForm;
|
|
13
12
|
export interface ISelectField extends IBaseField<ISelectFieldParams> {
|
|
@@ -16,6 +15,7 @@ export interface ISelectField extends IBaseField<ISelectFieldParams> {
|
|
|
16
15
|
declare const __propDef: {
|
|
17
16
|
props: {
|
|
18
17
|
fieldStore: Writable<IBuiltField<ISelectFieldParams>>;
|
|
18
|
+
load_options?: ((search_term: string) => Promise<ISelectFieldOption[]>) | undefined;
|
|
19
19
|
};
|
|
20
20
|
events: {
|
|
21
21
|
[evt: string]: CustomEvent<any>;
|
|
@@ -121,12 +121,30 @@ const contentField = StateField.define({
|
|
|
121
121
|
},
|
|
122
122
|
update(value, transaction) {
|
|
123
123
|
if (transaction.docChanged) {
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
let new_value = transaction.newDoc.toString();
|
|
125
|
+
if (new_value !== $fieldStore.value) {
|
|
126
|
+
$fieldStore.value = parse_string ? parse_string_value(new_value) : new_value;
|
|
127
|
+
}
|
|
128
|
+
return new_value;
|
|
126
129
|
}
|
|
127
130
|
return value;
|
|
128
131
|
}
|
|
129
132
|
});
|
|
133
|
+
$: update_editor_content($fieldStore.value);
|
|
134
|
+
function update_editor_content(new_value) {
|
|
135
|
+
if (editor_view) {
|
|
136
|
+
const current_value = editor_view.state.doc.toString();
|
|
137
|
+
if (new_value !== current_value && typeof new_value === "string") {
|
|
138
|
+
editor_view.dispatch({
|
|
139
|
+
changes: {
|
|
140
|
+
from: 0,
|
|
141
|
+
to: current_value.length,
|
|
142
|
+
insert: new_value
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
130
148
|
function fake_handlebars_lang() {
|
|
131
149
|
const curly_decorator = new MatchDecorator({
|
|
132
150
|
regexp: /(\{\{[^}]*\}\})|(~~[^\s]*)/g,
|
|
@@ -180,43 +198,63 @@ function parse_string_value(value) {
|
|
|
180
198
|
|
|
181
199
|
<FieldLabel {fieldStore} />
|
|
182
200
|
{#if !is_object}
|
|
183
|
-
<div
|
|
184
|
-
use:setup_editor
|
|
185
|
-
class=" stubber-cm text-sm {isValid ? 'stubber-valid' : 'stubber-invalid'}"
|
|
186
|
-
/>
|
|
201
|
+
<div use:setup_editor class="stubber-cm" aria-invalid={!isValid} />
|
|
187
202
|
{:else}
|
|
188
203
|
<p class="text-surface-400">The value of this field is not a string.</p>
|
|
189
204
|
{/if}
|
|
190
205
|
<FieldMessage {fieldStore} />
|
|
191
206
|
|
|
207
|
+
<!-- below styles are basically this tailwind string:
|
|
208
|
+
flex h-9 w-full min-w-0 flex-row items-center gap-2 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 -->
|
|
209
|
+
|
|
192
210
|
<style>
|
|
193
211
|
.stubber-cm :global(.cm-editor) {
|
|
194
|
-
|
|
195
|
-
|
|
212
|
+
width: 100%;
|
|
213
|
+
min-width: 0;
|
|
196
214
|
border-radius: 0.375rem;
|
|
215
|
+
border-width: 1px;
|
|
197
216
|
--tw-border-opacity: 1;
|
|
198
217
|
border-color: hsl(var(--input) / var(--tw-border-opacity, 1));
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
width: 100%;
|
|
202
|
-
color: rgb(31 41 51);
|
|
218
|
+
--tw-bg-opacity: 1;
|
|
219
|
+
background-color: hsl(var(--background) / var(--tw-bg-opacity, 1));
|
|
203
220
|
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
font-size: 0.875rem;
|
|
222
|
+
line-height: 1.25rem;
|
|
223
|
+
outline: 2px solid transparent;
|
|
224
|
+
outline-offset: 2px;
|
|
225
|
+
--tw-ring-offset-color: hsl(var(--background) / 1);
|
|
226
|
+
transition-property: color, box-shadow;
|
|
227
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
228
|
+
transition-duration: 150ms;
|
|
206
229
|
}
|
|
207
230
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
231
|
+
@media (prefers-color-scheme: dark) {
|
|
232
|
+
.stubber-cm :global(.cm-editor) {
|
|
233
|
+
background-color: hsl(var(--input) / 0.3);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.stubber-cm[aria-invalid="true"] :global(.cm-editor) {
|
|
238
|
+
--tw-border-opacity: 1;
|
|
239
|
+
border-color: hsl(var(--destructive) / var(--tw-border-opacity, 1));
|
|
240
|
+
--tw-ring-color: hsl(var(--destructive) / 0.5);
|
|
212
241
|
}
|
|
213
242
|
|
|
214
|
-
.stubber-cm
|
|
215
|
-
border-
|
|
243
|
+
.stubber-cm :global(.cm-editor.cm-focused) {
|
|
244
|
+
--tw-border-opacity: 1;
|
|
245
|
+
border-color: hsl(var(--ring) / var(--tw-border-opacity, 1));
|
|
246
|
+
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
|
|
247
|
+
var(--tw-ring-offset-color);
|
|
248
|
+
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width))
|
|
249
|
+
var(--tw-ring-color);
|
|
250
|
+
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
251
|
+
--tw-ring-color: hsl(var(--ring) / 0.5);
|
|
216
252
|
}
|
|
217
253
|
|
|
218
|
-
.stubber-cm
|
|
219
|
-
|
|
254
|
+
.stubber-cm[aria-invalid="true"] :global(.cm-editor.cm-focused) {
|
|
255
|
+
--tw-ring-color: hsl(var(--destructive) / 0.5);
|
|
256
|
+
--tw-border-opacity: 1;
|
|
257
|
+
border-color: hsl(var(--destructive) / var(--tw-border-opacity, 1));
|
|
220
258
|
}
|
|
221
259
|
|
|
222
260
|
.stubber-cm :global(.cm-editor .cm-scroller) {
|
|
@@ -236,6 +274,7 @@ function parse_string_value(value) {
|
|
|
236
274
|
}
|
|
237
275
|
|
|
238
276
|
.stubber-cm :global(.cm-editor .cm-placeholder) {
|
|
277
|
+
--tw-text-opacity: 1;
|
|
239
278
|
color: hsl(var(--muted-foreground) / var(--tw-text-opacity, 1));
|
|
240
279
|
}
|
|
241
280
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stubber/form-fields",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"description": "An automatic form builder based on field specifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"components",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"postcss": "^8.4.28",
|
|
51
51
|
"prettier": "^2.8.0",
|
|
52
52
|
"prettier-plugin-svelte": "^2.10.1",
|
|
53
|
+
"prettier-plugin-tailwindcss": "^0.4.1",
|
|
53
54
|
"publint": "^0.2.2",
|
|
54
55
|
"socket.io-client": "^4.8.1",
|
|
55
56
|
"svelte-check": "^3.4.3",
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
"@codemirror/lang-javascript": "^6.2.4",
|
|
66
67
|
"@codemirror/state": "^6.5.2",
|
|
67
68
|
"@codemirror/view": "^6.38.4",
|
|
68
|
-
"@stubber/ui": "^1.13.
|
|
69
|
+
"@stubber/ui": "^1.13.6",
|
|
69
70
|
"ag-grid-community": "^31.0.2",
|
|
70
71
|
"ag-grid-enterprise": "^31.0.2",
|
|
71
72
|
"codemirror": "^6.0.2",
|