@stubber/form-fields 2.0.23 → 2.1.1
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 +5 -52
- package/dist/form-fields/Form.svelte.d.ts +1 -1
- package/dist/form-fields/sub/code-field.svelte +108 -65
- package/dist/form-fields/sub/field-builder-field.svelte +20 -11
- package/dist/form-fields/sub/grid-field.svelte +15 -5
- package/dist/form-fields/sub/grid-field.svelte.d.ts +1 -3
- package/dist/form-fields/sub/jsoneditor-field.svelte +9 -8
- package/dist/form-fields/sub/map-field.svelte +109 -17
- package/dist/form-fields/sub/map-field.svelte.d.ts +1 -0
- package/dist/form-fields/sub/qr-code-scanner-field.svelte +9 -11
- package/dist/form-fields/sub/smart-text-field.svelte +108 -67
- package/dist/utils/json-editor-sync.d.ts +2 -2
- package/dist/utils/json-editor-sync.js +1 -1
- package/package.json +3 -7
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
## How to use
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
git clone git@github.com:stubber/stubber-form-fields-pkg.git form-fields-pkg
|
|
6
|
+
git clone git@github.com:stubber/stubber-public-form-fields-pkg.git form-fields-pkg
|
|
7
7
|
cd form-fields-pkg
|
|
8
|
-
cp .
|
|
8
|
+
cp .env_staging .env
|
|
9
9
|
pnpm install
|
|
10
10
|
pnpm run dev
|
|
11
11
|
```
|
|
@@ -32,54 +32,7 @@ fix: fix button color
|
|
|
32
32
|
|
|
33
33
|
See default ruleset [here](https://github.com/semantic-release/commit-analyzer/blob/master/lib/default-release-rules.js)
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
# Quick Testing
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| AgGrid | x | |
|
|
40
|
-
| Arraybuilder | x | |
|
|
41
|
-
| Checkbox | x | |
|
|
42
|
-
| CheckboxAutocomplete | x | |
|
|
43
|
-
| Code | x | |
|
|
44
|
-
| Contactselector | x | |
|
|
45
|
-
| Currency | x | |
|
|
46
|
-
| Dataindication | x | |
|
|
47
|
-
| Date | x | |
|
|
48
|
-
| Datetime | x | |
|
|
49
|
-
| Email | x | |
|
|
50
|
-
| Fieldbuilder | x | |
|
|
51
|
-
| Fieldsbuilder | deprecated | |
|
|
52
|
-
| File | x | |
|
|
53
|
-
| Heading | x | |
|
|
54
|
-
| Hidden | x | |
|
|
55
|
-
| Hiddenlocation | x | |
|
|
56
|
-
| Html | x | |
|
|
57
|
-
| Jsoneditor | x | |
|
|
58
|
-
| Map | x | |
|
|
59
|
-
| Multicheckbox | x | |
|
|
60
|
-
| Multistep | x | |
|
|
61
|
-
| Note | x | |
|
|
62
|
-
| Number | x | |
|
|
63
|
-
| Objectbuilder | x | |
|
|
64
|
-
| Qrcodescanner | x | |
|
|
65
|
-
| Radio | x | |
|
|
66
|
-
| Renderfield | deprecated | |
|
|
67
|
-
| Screenrecorder | x | |
|
|
68
|
-
| Screenshot | x | |
|
|
69
|
-
| Scrollandreaddisplay | x | |
|
|
70
|
-
| Section | x | |
|
|
71
|
-
| Select | x | |
|
|
72
|
-
| Selectresource | x | |
|
|
73
|
-
| Signature | x | |
|
|
74
|
-
| Slider | x | |
|
|
75
|
-
| SmartText | x | |
|
|
76
|
-
| Telephone | x | |
|
|
77
|
-
| Text | x | |
|
|
78
|
-
| Voicenote | x | |
|
|
79
|
-
|
|
80
|
-
## TS Refactor
|
|
81
|
-
|
|
82
|
-
For now src/lib/fields2 exports a refactored and rewritten form fields implementation.
|
|
83
|
-
The existing implementation is still present for now, but will be removed in future.
|
|
84
|
-
|
|
85
|
-
This allows us to slowly switch editor/squared over to the new implementation without breaking everything at once.
|
|
37
|
+
A preview of all fields and some edge cases are available to test on the sveltekit app.
|
|
38
|
+
On staging, it's hosted at https://form-fields-pkg.staging.stubber.zone
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
|
-
import {
|
|
2
|
+
import type { Writable } from "svelte/store";
|
|
3
3
|
import type { UploadedFile } from "./fileserver";
|
|
4
4
|
import type { IFormDependencies, IInitialForm } from "./interfaces";
|
|
5
5
|
declare const __propDef: {
|
|
@@ -11,41 +11,37 @@ export const code_field_param_spec = {
|
|
|
11
11
|
};
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
|
-
<script>import {
|
|
15
|
-
acceptCompletion,
|
|
16
|
-
autocompletion,
|
|
17
|
-
CompletionContext,
|
|
18
|
-
completionKeymap,
|
|
19
|
-
startCompletion
|
|
20
|
-
} from "@codemirror/autocomplete";
|
|
21
|
-
import { javascript, javascriptLanguage } from "@codemirror/lang-javascript";
|
|
22
|
-
import { EditorState } from "@codemirror/state";
|
|
23
|
-
import { EditorView, keymap } from "@codemirror/view";
|
|
24
|
-
import { basicSetup } from "codemirror";
|
|
25
|
-
import { getContext } from "svelte";
|
|
14
|
+
<script>import { getContext } from "svelte";
|
|
26
15
|
import FieldLabel from "../FieldLabel.svelte";
|
|
27
16
|
import FieldMessage from "../FieldMessage.svelte";
|
|
28
17
|
export let fieldStore;
|
|
29
18
|
const code_fields_ctx_store = getContext("code_fields_ctx");
|
|
30
19
|
let value = $fieldStore.value || "";
|
|
31
20
|
let editor_view;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
21
|
+
let codemirror_loader;
|
|
22
|
+
function loadCodeMirror() {
|
|
23
|
+
if (!codemirror_loader) {
|
|
24
|
+
codemirror_loader = Promise.all([
|
|
25
|
+
import("@codemirror/autocomplete"),
|
|
26
|
+
import("@codemirror/lang-javascript"),
|
|
27
|
+
import("@codemirror/state"),
|
|
28
|
+
import("@codemirror/view"),
|
|
29
|
+
import("codemirror")
|
|
30
|
+
]).then(([autocomplete, javascript, state, view, codemirror]) => ({
|
|
31
|
+
acceptCompletion: autocomplete.acceptCompletion,
|
|
32
|
+
autocompletion: autocomplete.autocompletion,
|
|
33
|
+
completionKeymap: autocomplete.completionKeymap,
|
|
34
|
+
startCompletion: autocomplete.startCompletion,
|
|
35
|
+
javascript: javascript.javascript,
|
|
36
|
+
javascriptLanguage: javascript.javascriptLanguage,
|
|
37
|
+
EditorState: state.EditorState,
|
|
38
|
+
EditorView: view.EditorView,
|
|
39
|
+
keymap: view.keymap,
|
|
40
|
+
basicSetup: codemirror.basicSetup
|
|
41
|
+
}));
|
|
47
42
|
}
|
|
48
|
-
|
|
43
|
+
return codemirror_loader;
|
|
44
|
+
}
|
|
49
45
|
function keysFor(obj) {
|
|
50
46
|
return Object.keys(obj ?? {});
|
|
51
47
|
}
|
|
@@ -78,46 +74,92 @@ function globalCompletions(ctx) {
|
|
|
78
74
|
// keep suggestions stable while typing
|
|
79
75
|
};
|
|
80
76
|
}
|
|
81
|
-
const stubber_completions = javascriptLanguage.data.of({
|
|
82
|
-
autocomplete: globalCompletions
|
|
83
|
-
});
|
|
84
77
|
function bind_codemirror(el) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
78
|
+
let disposed = false;
|
|
79
|
+
let local_editor_view;
|
|
80
|
+
void loadCodeMirror().then(
|
|
81
|
+
({
|
|
82
|
+
acceptCompletion,
|
|
83
|
+
autocompletion,
|
|
84
|
+
completionKeymap,
|
|
85
|
+
startCompletion,
|
|
86
|
+
javascript,
|
|
87
|
+
javascriptLanguage,
|
|
88
|
+
EditorState,
|
|
89
|
+
EditorView,
|
|
90
|
+
keymap,
|
|
91
|
+
basicSetup
|
|
92
|
+
}) => {
|
|
93
|
+
if (disposed) return;
|
|
94
|
+
const custom_completion_keymap = completionKeymap.filter((binding) => binding.key != "Enter").concat([
|
|
95
|
+
{
|
|
96
|
+
key: "Enter",
|
|
97
|
+
run: () => {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
key: "Tab",
|
|
103
|
+
run: acceptCompletion
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
key: "Ctrl-Space",
|
|
107
|
+
run: startCompletion,
|
|
108
|
+
stopPropagation: true
|
|
109
|
+
}
|
|
110
|
+
]);
|
|
111
|
+
const stubber_completions = javascriptLanguage.data.of({
|
|
112
|
+
autocomplete: globalCompletions
|
|
113
|
+
});
|
|
114
|
+
const min_height_editor = EditorView.theme({
|
|
115
|
+
".cm-content, .cm-gutter": { minHeight: "200px" },
|
|
116
|
+
"&": { maxHeight: "300px" },
|
|
117
|
+
".cm-scroller": { overflow: "auto" }
|
|
118
|
+
});
|
|
119
|
+
const update_listener = EditorView.updateListener.of((update) => {
|
|
120
|
+
if (update.docChanged) {
|
|
121
|
+
const new_value = update.state.doc.toString();
|
|
122
|
+
if (new_value !== value) {
|
|
123
|
+
value = new_value;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
local_editor_view = new EditorView({
|
|
128
|
+
state: EditorState.create({
|
|
129
|
+
doc: value,
|
|
130
|
+
extensions: [
|
|
131
|
+
basicSetup,
|
|
132
|
+
javascript(),
|
|
133
|
+
stubber_completions,
|
|
134
|
+
autocompletion({ defaultKeymap: false }),
|
|
135
|
+
keymap.of(custom_completion_keymap),
|
|
136
|
+
min_height_editor,
|
|
137
|
+
update_listener
|
|
138
|
+
]
|
|
139
|
+
}),
|
|
140
|
+
parent: el
|
|
141
|
+
});
|
|
142
|
+
editor_view = local_editor_view;
|
|
111
143
|
}
|
|
112
|
-
|
|
113
|
-
|
|
144
|
+
);
|
|
145
|
+
return {
|
|
146
|
+
destroy() {
|
|
147
|
+
disposed = true;
|
|
148
|
+
local_editor_view?.destroy();
|
|
149
|
+
if (editor_view === local_editor_view) {
|
|
150
|
+
editor_view = void 0;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
114
155
|
$: sync_field_to_editor(value);
|
|
115
156
|
const sync_field_to_editor = (new_value) => {
|
|
116
|
-
|
|
117
|
-
|
|
157
|
+
const current_editor_view = editor_view;
|
|
158
|
+
if (current_editor_view && current_editor_view.state.doc.toString() !== new_value) {
|
|
159
|
+
current_editor_view.dispatch({
|
|
118
160
|
changes: {
|
|
119
161
|
from: 0,
|
|
120
|
-
to:
|
|
162
|
+
to: current_editor_view.state.doc.length,
|
|
121
163
|
insert: new_value
|
|
122
164
|
}
|
|
123
165
|
});
|
|
@@ -130,11 +172,12 @@ $: sync_field_to_value($fieldStore.value);
|
|
|
130
172
|
const sync_field_to_value = (new_value) => {
|
|
131
173
|
if (new_value !== value) {
|
|
132
174
|
value = new_value;
|
|
133
|
-
|
|
134
|
-
|
|
175
|
+
const current_editor_view = editor_view;
|
|
176
|
+
if (current_editor_view && current_editor_view.state.doc.toString() !== new_value) {
|
|
177
|
+
current_editor_view.dispatch({
|
|
135
178
|
changes: {
|
|
136
179
|
from: 0,
|
|
137
|
-
to:
|
|
180
|
+
to: current_editor_view.state.doc.length,
|
|
138
181
|
insert: new_value
|
|
139
182
|
}
|
|
140
183
|
});
|
|
@@ -84,7 +84,6 @@ import { max_files_param_spec } from "./screenrecorder-field.svelte";
|
|
|
84
84
|
import { scroll_and_read_display_field_param_spec } from "./scroll-and-read-display-field.svelte";
|
|
85
85
|
import { select_field_param_spec } from "./select-field.svelte";
|
|
86
86
|
import { select_resource_field_param_spec } from "./selectresource-field.svelte";
|
|
87
|
-
import { JSONEditor } from "svelte-jsoneditor";
|
|
88
87
|
import { handle_editor_change_setter, handle_store_change } from "../../utils/json-editor-sync";
|
|
89
88
|
export let fieldStore;
|
|
90
89
|
let value = cloneDeep($fieldStore.value) || {
|
|
@@ -472,22 +471,32 @@ let validations_open = false;
|
|
|
472
471
|
<Checkbox id="has_default" bind:checked={value.initvalue.has_default} />
|
|
473
472
|
<Label for="has_default">Has Default</Label>
|
|
474
473
|
</div>
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
474
|
+
{#await import("svelte-jsoneditor") then JSONEditorModule}
|
|
475
|
+
<JSONEditorModule.JSONEditor
|
|
476
|
+
bind:this={default_initvalue_editor}
|
|
477
|
+
onChange={(content) =>
|
|
478
|
+
handle_editor_change_setter(
|
|
479
|
+
content,
|
|
480
|
+
(new_val) => (value.initvalue.default = new_val)
|
|
481
|
+
)}
|
|
482
|
+
/>
|
|
483
|
+
{/await}
|
|
480
484
|
</div>
|
|
481
485
|
<div>
|
|
482
486
|
<div class="flex w-full items-center space-x-2">
|
|
483
487
|
<Checkbox id="has_override" bind:checked={value.initvalue.has_override} />
|
|
484
488
|
<Label for="has_override">Has Override</Label>
|
|
485
489
|
</div>
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
490
|
+
{#await import("svelte-jsoneditor") then JSONEditorModule}
|
|
491
|
+
<JSONEditorModule.JSONEditor
|
|
492
|
+
bind:this={override_initvalue_editor}
|
|
493
|
+
onChange={(content) =>
|
|
494
|
+
handle_editor_change_setter(
|
|
495
|
+
content,
|
|
496
|
+
(new_val) => (value.initvalue.override = new_val)
|
|
497
|
+
)}
|
|
498
|
+
/>
|
|
499
|
+
{/await}
|
|
491
500
|
</div>
|
|
492
501
|
</Collapsible.Content>
|
|
493
502
|
</Collapsible.Root>
|
|
@@ -15,10 +15,7 @@
|
|
|
15
15
|
};
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
|
-
<script>import {
|
|
19
|
-
import "ag-grid-community/styles/ag-grid.css";
|
|
20
|
-
import "ag-grid-community/styles/ag-theme-material.css";
|
|
21
|
-
import { onMount } from "svelte";
|
|
18
|
+
<script>import { onMount } from "svelte";
|
|
22
19
|
import FieldLabel from "../FieldLabel.svelte";
|
|
23
20
|
import FieldMessage from "../FieldMessage.svelte";
|
|
24
21
|
export let fieldStore;
|
|
@@ -45,12 +42,25 @@ const gridOptions = {
|
|
|
45
42
|
// Column Definitions: Defines & controls grid columns.
|
|
46
43
|
columnDefs: []
|
|
47
44
|
};
|
|
48
|
-
|
|
45
|
+
async function initializeGrid(cancelled) {
|
|
46
|
+
const [{ createGrid }] = await Promise.all([
|
|
47
|
+
import("ag-grid-community"),
|
|
48
|
+
import("ag-grid-community/styles/ag-grid.css"),
|
|
49
|
+
import("ag-grid-community/styles/ag-theme-material.css")
|
|
50
|
+
]);
|
|
51
|
+
if (cancelled()) return;
|
|
49
52
|
let columnDefs = $fieldStore.params?.columnDefs;
|
|
50
53
|
if (columnDefs) gridOptions.columnDefs = columnDefs;
|
|
51
54
|
let rowData = $fieldStore.value;
|
|
52
55
|
if (Array.isArray(rowData)) gridOptions.rowData = rowData;
|
|
53
56
|
createGrid(gridDiv, gridOptions);
|
|
57
|
+
}
|
|
58
|
+
onMount(() => {
|
|
59
|
+
let cancelled = false;
|
|
60
|
+
void initializeGrid(() => cancelled);
|
|
61
|
+
return () => {
|
|
62
|
+
cancelled = true;
|
|
63
|
+
};
|
|
54
64
|
});
|
|
55
65
|
</script>
|
|
56
66
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { ColDef } from "ag-grid-community";
|
|
2
3
|
import type { Writable } from "svelte/store";
|
|
3
4
|
import type { IBaseField, IBuiltField, IInitialForm } from "../interfaces";
|
|
4
5
|
interface IGridParams {
|
|
@@ -8,9 +9,6 @@ export declare const grid_field_param_spec: IInitialForm;
|
|
|
8
9
|
export interface IGridField extends IBaseField<IGridParams> {
|
|
9
10
|
fieldtype: "grid";
|
|
10
11
|
}
|
|
11
|
-
import { type ColDef } from "ag-grid-community";
|
|
12
|
-
import "ag-grid-community/styles/ag-grid.css";
|
|
13
|
-
import "ag-grid-community/styles/ag-theme-material.css";
|
|
14
12
|
declare const __propDef: {
|
|
15
13
|
props: {
|
|
16
14
|
fieldStore: Writable<IBuiltField<IGridParams>>;
|
|
@@ -16,7 +16,6 @@ export const jsoneditor_field_param_spec = {
|
|
|
16
16
|
handle_editor_change_setter,
|
|
17
17
|
handle_store_change
|
|
18
18
|
} from "../../utils/json-editor-sync";
|
|
19
|
-
import { JSONEditor } from "svelte-jsoneditor";
|
|
20
19
|
import FieldLabel from "../FieldLabel.svelte";
|
|
21
20
|
import FieldMessage from "../FieldMessage.svelte";
|
|
22
21
|
export let fieldStore;
|
|
@@ -27,11 +26,13 @@ let read_only = $fieldStore.params?.readOnly ?? false;
|
|
|
27
26
|
</script>
|
|
28
27
|
|
|
29
28
|
<FieldLabel {fieldStore} />
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
{#await import("svelte-jsoneditor") then JSONEditorModule}
|
|
30
|
+
<JSONEditorModule.JSONEditor
|
|
31
|
+
bind:this={jsoneditor}
|
|
32
|
+
content={initial_content}
|
|
33
|
+
readOnly={read_only}
|
|
34
|
+
onChange={(content) =>
|
|
35
|
+
handle_editor_change_setter(content, (new_val) => ($fieldStore.value = new_val))}
|
|
36
|
+
/>
|
|
37
|
+
{/await}
|
|
37
38
|
<FieldMessage {fieldStore} />
|
|
@@ -5,10 +5,24 @@ export const map_field_param_spec = {
|
|
|
5
5
|
spec: {
|
|
6
6
|
fields: {
|
|
7
7
|
hide_geolocate_button: {
|
|
8
|
-
fieldtype: "checkbox"
|
|
8
|
+
fieldtype: "checkbox",
|
|
9
|
+
without_value_details: true
|
|
9
10
|
},
|
|
10
11
|
auto_set_location: {
|
|
11
|
-
fieldtype: "checkbox"
|
|
12
|
+
fieldtype: "checkbox",
|
|
13
|
+
without_value_details: true
|
|
14
|
+
},
|
|
15
|
+
address_mode: {
|
|
16
|
+
fieldtype: "select",
|
|
17
|
+
without_value_details: true,
|
|
18
|
+
params: {
|
|
19
|
+
options: [
|
|
20
|
+
{ label: "None", value: "none" },
|
|
21
|
+
{ label: "Hidden", value: "hidden" },
|
|
22
|
+
{ label: "Readonly", value: "readonly" },
|
|
23
|
+
{ label: "Editable", value: "editable" }
|
|
24
|
+
]
|
|
25
|
+
}
|
|
12
26
|
}
|
|
13
27
|
}
|
|
14
28
|
}
|
|
@@ -17,10 +31,14 @@ export const map_field_param_spec = {
|
|
|
17
31
|
|
|
18
32
|
<script>import { controls, Geocoder, Map, Marker } from "@beyonk/svelte-mapbox";
|
|
19
33
|
import FieldLabel from "../FieldLabel.svelte";
|
|
34
|
+
import { mapValues, omit } from "lodash-es";
|
|
35
|
+
import { Input } from "@stubber/ui/input";
|
|
36
|
+
import { Label } from "@stubber/ui/label";
|
|
20
37
|
const { GeolocateControl } = controls;
|
|
21
38
|
export let fieldStore;
|
|
22
39
|
const initial_value = $fieldStore.value;
|
|
23
40
|
let params = $fieldStore.params;
|
|
41
|
+
let reverse_geocode_enabled = params?.address_mode && params?.address_mode !== "none";
|
|
24
42
|
let hide_geolocate_button = params?.hide_geolocate_button || false;
|
|
25
43
|
let auto_set_location = params?.auto_set_location || false;
|
|
26
44
|
let dependencies = $fieldStore?.formDependencies;
|
|
@@ -58,6 +76,39 @@ function setValueCoords(lat, lng, force = false) {
|
|
|
58
76
|
lng: lng ?? null,
|
|
59
77
|
latlng: `${lat ?? null},${lng ?? null}`
|
|
60
78
|
};
|
|
79
|
+
reverse_geocode(lat, lng);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function reverse_geocode(lat, lng) {
|
|
83
|
+
if (!mapboxAccessToken || !reverse_geocode_enabled) return;
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(
|
|
86
|
+
`https://api.mapbox.com/search/geocode/v6/reverse?longitude=${lng}&latitude=${lat}&access_token=${mapboxAccessToken}`
|
|
87
|
+
);
|
|
88
|
+
const data = await response.json();
|
|
89
|
+
if (data?.features?.length > 0) {
|
|
90
|
+
const feature = data?.features[0];
|
|
91
|
+
if (feature?.properties?.feature_type === "address") {
|
|
92
|
+
const full_address = feature?.properties?.full_address;
|
|
93
|
+
const cleaned_context = mapValues(
|
|
94
|
+
feature?.properties?.context,
|
|
95
|
+
(v) => omit(v, ["mapbox_id", "wikidata_id", "alternate"])
|
|
96
|
+
);
|
|
97
|
+
const address = {};
|
|
98
|
+
if (full_address) {
|
|
99
|
+
address.full_address = full_address;
|
|
100
|
+
}
|
|
101
|
+
if (cleaned_context) {
|
|
102
|
+
address["context"] = cleaned_context;
|
|
103
|
+
}
|
|
104
|
+
$fieldStore.value = {
|
|
105
|
+
...$fieldStore.value,
|
|
106
|
+
address
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error("reverse geocode error", error);
|
|
61
112
|
}
|
|
62
113
|
}
|
|
63
114
|
function resetValues() {
|
|
@@ -79,22 +130,34 @@ function handle_geolocate(o) {
|
|
|
79
130
|
const coords = o?.detail?.coords || {};
|
|
80
131
|
setValueCoords(coords.latitude, coords.longitude);
|
|
81
132
|
}
|
|
133
|
+
function handle_address_change(e) {
|
|
134
|
+
const new_address = e?.target?.value || "";
|
|
135
|
+
if (new_address && $fieldStore.value.address.full_address !== new_address) {
|
|
136
|
+
$fieldStore.value = {
|
|
137
|
+
...$fieldStore.value,
|
|
138
|
+
address: {
|
|
139
|
+
...$fieldStore.value.address,
|
|
140
|
+
full_address: new_address
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
82
145
|
</script>
|
|
83
146
|
|
|
84
147
|
<FieldLabel {fieldStore} />
|
|
85
148
|
{#if mapboxAccessToken}
|
|
86
|
-
<div class="flex flex-col
|
|
149
|
+
<div class="flex w-full flex-col">
|
|
87
150
|
<div>
|
|
88
151
|
<button
|
|
89
152
|
type="button"
|
|
90
153
|
on:click={resetValues}
|
|
91
|
-
class="text-sm text-surface-300 hover:
|
|
154
|
+
class="text-sm text-surface-300 hover:text-surface-500 hover:underline"
|
|
92
155
|
>
|
|
93
156
|
<i class="fa-regular fa-undo" /> Reset
|
|
94
157
|
</button>
|
|
95
158
|
</div>
|
|
96
159
|
<div class="relative mt-2 rounded-md">
|
|
97
|
-
<div class="w-full
|
|
160
|
+
<div class="h-72 w-full">
|
|
98
161
|
<Map
|
|
99
162
|
bind:center
|
|
100
163
|
zoom={14}
|
|
@@ -114,31 +177,60 @@ function handle_geolocate(o) {
|
|
|
114
177
|
<GeolocateControl position="top-right" on:geolocate={handle_geolocate} />
|
|
115
178
|
{/if}
|
|
116
179
|
{#if browserLocation}
|
|
117
|
-
<Marker
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
180
|
+
<Marker
|
|
181
|
+
lat={browserLocation.lat}
|
|
182
|
+
lng={browserLocation.lng}
|
|
183
|
+
markerOptions={{ anchor: "center" }}
|
|
184
|
+
>
|
|
185
|
+
<div class="relative flex flex-col items-center">
|
|
186
|
+
<span class="fa-stack fa-2x">
|
|
187
|
+
<i
|
|
188
|
+
class="fa-solid fa-circle fa-stack-2x text-[1.3em] leading-[inherit] text-white"
|
|
189
|
+
/>
|
|
190
|
+
<i class="fa-solid fa-user fa-stack-1x text-blue-600" />
|
|
191
|
+
</span>
|
|
124
192
|
</div>
|
|
125
193
|
</Marker>
|
|
126
194
|
{/if}
|
|
127
195
|
{#if $fieldStore.value?.lat && $fieldStore.value?.lng}
|
|
128
|
-
<Marker
|
|
129
|
-
|
|
196
|
+
<Marker
|
|
197
|
+
lat={$fieldStore.value.lat}
|
|
198
|
+
lng={$fieldStore.value.lng}
|
|
199
|
+
popup={false}
|
|
200
|
+
markerOptions={{ anchor: "bottom" }}
|
|
201
|
+
>
|
|
202
|
+
<div class="relative flex flex-col items-center">
|
|
130
203
|
<div
|
|
131
|
-
class="bg-
|
|
204
|
+
class="rounded-xl bg-white px-2 py-1 text-sm font-medium text-slate-800 shadow-lg ring-1 ring-black/20"
|
|
132
205
|
>
|
|
133
|
-
lat: {$fieldStore.value.lat?.toString()?.substring(0,
|
|
134
|
-
lng: {$fieldStore.value.lng?.toString()?.substring(0,
|
|
206
|
+
<div>lat: {$fieldStore.value.lat?.toString()?.substring(0, 7)}</div>
|
|
207
|
+
<div>lng: {$fieldStore.value.lng?.toString()?.substring(0, 7)}</div>
|
|
135
208
|
</div>
|
|
209
|
+
|
|
210
|
+
<span class="fa-stack fa-2x h-[1.5em]">
|
|
211
|
+
<i
|
|
212
|
+
class="fa-solid fa-location-pin fa-stack-2x text-[1.2em] leading-[inherit] text-white"
|
|
213
|
+
/>
|
|
214
|
+
<i class="fa-solid fa-location-dot fa-stack-1x text-green-600" />
|
|
215
|
+
</span>
|
|
136
216
|
</div>
|
|
137
217
|
</Marker>
|
|
138
218
|
{/if}
|
|
139
219
|
</Map>
|
|
140
220
|
</div>
|
|
141
221
|
</div>
|
|
222
|
+
{#if params?.address_mode === "editable"}
|
|
223
|
+
<Input
|
|
224
|
+
type="text"
|
|
225
|
+
class="mt-1"
|
|
226
|
+
value={$fieldStore.value?.address?.full_address || ".."}
|
|
227
|
+
on:change={handle_address_change}
|
|
228
|
+
/>
|
|
229
|
+
{:else if params?.address_mode === "readonly"}
|
|
230
|
+
<Label class="mt-1 block text-sm font-medium text-surface-700"
|
|
231
|
+
>{$fieldStore.value?.address?.full_address || ".."}</Label
|
|
232
|
+
>
|
|
233
|
+
{/if}
|
|
142
234
|
</div>
|
|
143
235
|
{/if}
|
|
144
236
|
<FieldMessage {fieldStore} />
|
|
@@ -4,6 +4,7 @@ import { type IBaseField, type IBuiltField, type IInitialForm } from "../interfa
|
|
|
4
4
|
export interface IMapFieldParams {
|
|
5
5
|
hide_geolocate_button?: boolean;
|
|
6
6
|
auto_set_location?: boolean;
|
|
7
|
+
address_mode?: "none" | "hidden" | "readonly" | "editable";
|
|
7
8
|
}
|
|
8
9
|
export interface IMapField extends IBaseField<IMapFieldParams> {
|
|
9
10
|
fieldtype: "map";
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
<script context="module">import {} from "../interfaces";
|
|
2
2
|
</script>
|
|
3
3
|
|
|
4
|
-
<script>import {
|
|
5
|
-
import { Input } from "@stubber/ui/input";
|
|
6
|
-
import { Button } from "@stubber/ui/button";
|
|
4
|
+
<script>import { Button } from "@stubber/ui/button";
|
|
7
5
|
import * as Dialog from "@stubber/ui/dialog";
|
|
8
6
|
import FieldLabel from "../FieldLabel.svelte";
|
|
9
7
|
import FieldMessage from "../FieldMessage.svelte";
|
|
10
|
-
import { Html5Qrcode } from "html5-qrcode";
|
|
11
8
|
export let fieldStore;
|
|
12
9
|
let qr_code_lib;
|
|
13
10
|
let is_cameras_available = false;
|
|
@@ -21,6 +18,7 @@ const open_scanner_modal = (is_open) => {
|
|
|
21
18
|
}
|
|
22
19
|
};
|
|
23
20
|
const setup_scanner = async () => {
|
|
21
|
+
const { Html5Qrcode } = await import("html5-qrcode");
|
|
24
22
|
let cameras = await Html5Qrcode.getCameras();
|
|
25
23
|
if (!cameras) {
|
|
26
24
|
is_cameras_available = false;
|
|
@@ -55,28 +53,28 @@ function confirm_scan() {
|
|
|
55
53
|
<Dialog.Content>
|
|
56
54
|
<Dialog.Header>
|
|
57
55
|
<Dialog.Title>Scan Code</Dialog.Title>
|
|
58
|
-
<div class="flex flex-col
|
|
59
|
-
<div id="reader" class="w-[250px] sm:w-[310px]
|
|
56
|
+
<div class="flex w-full flex-col gap-2">
|
|
57
|
+
<div id="reader" class="flex w-[250px] flex-col items-center sm:w-[310px]" />
|
|
60
58
|
{#if is_cameras_available}
|
|
61
59
|
{#if scan_success}
|
|
62
60
|
<div class="flex items-center space-x-2">
|
|
63
|
-
<i class="fas fa-circle text-success-400
|
|
61
|
+
<i class="fas fa-circle fa-2xs text-success-400" />
|
|
64
62
|
<p class="text-label text-surface-800">{scanned_text}</p>
|
|
65
63
|
</div>
|
|
66
64
|
{:else}
|
|
67
65
|
<div class="flex items-center space-x-2">
|
|
68
|
-
<i class="fas fa-circle text-surface-200
|
|
66
|
+
<i class="fas fa-circle fa-2xs text-surface-200" />
|
|
69
67
|
<p class="text-label text-surface-800">Searching for code ...</p>
|
|
70
68
|
</div>
|
|
71
69
|
{/if}
|
|
72
70
|
<div class="flex">
|
|
73
|
-
<div class="
|
|
71
|
+
<div class="ml-auto flex">
|
|
74
72
|
<Button disabled={!scan_success} on:click={confirm_scan}>Confirm</Button>
|
|
75
73
|
</div>
|
|
76
74
|
</div>
|
|
77
75
|
{:else}
|
|
78
|
-
<div class="
|
|
79
|
-
<p class="text-surface-800
|
|
76
|
+
<div class="flex aspect-square w-[250px] items-center justify-center sm:w-[310px]">
|
|
77
|
+
<p class="text-label text-surface-800">No camera found</p>
|
|
80
78
|
</div>
|
|
81
79
|
{/if}
|
|
82
80
|
</div>
|
|
@@ -57,29 +57,37 @@ export const smart_text_field_param_spec = {
|
|
|
57
57
|
};
|
|
58
58
|
</script>
|
|
59
59
|
|
|
60
|
-
<script>import {
|
|
61
|
-
acceptCompletion,
|
|
62
|
-
autocompletion,
|
|
63
|
-
CompletionContext,
|
|
64
|
-
completionKeymap
|
|
65
|
-
} from "@codemirror/autocomplete";
|
|
66
|
-
import { history } from "@codemirror/commands";
|
|
67
|
-
import { EditorState, StateField } from "@codemirror/state";
|
|
68
|
-
import {
|
|
69
|
-
Decoration,
|
|
70
|
-
EditorView,
|
|
71
|
-
keymap,
|
|
72
|
-
MatchDecorator,
|
|
73
|
-
placeholder,
|
|
74
|
-
ViewPlugin,
|
|
75
|
-
ViewUpdate
|
|
76
|
-
} from "@codemirror/view";
|
|
77
|
-
import { find } from "lodash-es";
|
|
60
|
+
<script>import { find } from "lodash-es";
|
|
78
61
|
import FieldLabel from "../FieldLabel.svelte";
|
|
79
62
|
import FieldMessage from "../FieldMessage.svelte";
|
|
80
63
|
import { getContext } from "svelte";
|
|
81
64
|
export let fieldStore;
|
|
82
65
|
let editor_view;
|
|
66
|
+
let codemirror_loader;
|
|
67
|
+
function loadCodeMirror() {
|
|
68
|
+
if (!codemirror_loader) {
|
|
69
|
+
codemirror_loader = Promise.all([
|
|
70
|
+
import("@codemirror/autocomplete"),
|
|
71
|
+
import("@codemirror/commands"),
|
|
72
|
+
import("@codemirror/state"),
|
|
73
|
+
import("@codemirror/view")
|
|
74
|
+
]).then(([autocomplete, commands, state, view]) => ({
|
|
75
|
+
acceptCompletion: autocomplete.acceptCompletion,
|
|
76
|
+
autocompletion: autocomplete.autocompletion,
|
|
77
|
+
completionKeymap: autocomplete.completionKeymap,
|
|
78
|
+
history: commands.history,
|
|
79
|
+
EditorState: state.EditorState,
|
|
80
|
+
StateField: state.StateField,
|
|
81
|
+
Decoration: view.Decoration,
|
|
82
|
+
EditorView: view.EditorView,
|
|
83
|
+
keymap: view.keymap,
|
|
84
|
+
MatchDecorator: view.MatchDecorator,
|
|
85
|
+
placeholder: view.placeholder,
|
|
86
|
+
ViewPlugin: view.ViewPlugin
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
return codemirror_loader;
|
|
90
|
+
}
|
|
83
91
|
$: validation_result = $fieldStore.validation_result;
|
|
84
92
|
$: type = validation_result?.type;
|
|
85
93
|
$: isValid = type !== "error";
|
|
@@ -88,58 +96,91 @@ const smart_text_fields_ctx_store = getContext("smart_text_fields_ctx");
|
|
|
88
96
|
$: parse_string = $fieldStore.params?.parse_string ?? true;
|
|
89
97
|
$: code_language = $fieldStore.params?.code_language || "handlebars";
|
|
90
98
|
const setup_editor = (element) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
99
|
+
let disposed = false;
|
|
100
|
+
let local_editor_view;
|
|
101
|
+
void loadCodeMirror().then(
|
|
102
|
+
({
|
|
103
|
+
acceptCompletion,
|
|
104
|
+
autocompletion,
|
|
105
|
+
completionKeymap,
|
|
106
|
+
history,
|
|
107
|
+
EditorState,
|
|
108
|
+
StateField,
|
|
109
|
+
Decoration,
|
|
110
|
+
EditorView,
|
|
111
|
+
keymap,
|
|
112
|
+
MatchDecorator,
|
|
113
|
+
placeholder,
|
|
114
|
+
ViewPlugin
|
|
115
|
+
}) => {
|
|
116
|
+
if (disposed) return;
|
|
117
|
+
const contentField = createContentField(StateField);
|
|
118
|
+
const tab_accept_completion = keymap.of(
|
|
119
|
+
completionKeymap.filter((binding) => binding.key != "Enter").concat([
|
|
120
|
+
{
|
|
121
|
+
key: "Enter",
|
|
122
|
+
run: () => {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
key: "Tab",
|
|
128
|
+
run: acceptCompletion
|
|
129
|
+
}
|
|
130
|
+
])
|
|
131
|
+
);
|
|
132
|
+
const extensions = [
|
|
133
|
+
history({ newGroupDelay: 150 }),
|
|
134
|
+
EditorView.lineWrapping,
|
|
135
|
+
contentField,
|
|
136
|
+
autocompletion({ override: [myCompletions], defaultKeymap: false }),
|
|
137
|
+
tab_accept_completion,
|
|
138
|
+
placeholder($fieldStore.label),
|
|
139
|
+
EditorView.contentAttributes.of({
|
|
140
|
+
spellcheck: "true"
|
|
141
|
+
})
|
|
142
|
+
];
|
|
143
|
+
if (code_language === "handlebars") {
|
|
144
|
+
extensions.push(fake_handlebars_lang(Decoration, MatchDecorator, ViewPlugin));
|
|
120
145
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
146
|
+
const str_value = $fieldStore?.value?.toString() ?? "";
|
|
147
|
+
local_editor_view = new EditorView({
|
|
148
|
+
state: EditorState.create({
|
|
149
|
+
doc: str_value,
|
|
150
|
+
extensions
|
|
151
|
+
}),
|
|
152
|
+
parent: element
|
|
153
|
+
});
|
|
154
|
+
editor_view = local_editor_view;
|
|
125
155
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (transaction.docChanged) {
|
|
134
|
-
let new_value = transaction.newDoc.toString();
|
|
135
|
-
if (new_value !== $fieldStore.value) {
|
|
136
|
-
$fieldStore.value = parse_string ? parse_string_value(new_value) : new_value;
|
|
156
|
+
);
|
|
157
|
+
return {
|
|
158
|
+
destroy() {
|
|
159
|
+
disposed = true;
|
|
160
|
+
local_editor_view?.destroy();
|
|
161
|
+
if (editor_view === local_editor_view) {
|
|
162
|
+
editor_view = void 0;
|
|
137
163
|
}
|
|
138
|
-
return new_value;
|
|
139
164
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
function createContentField(StateField) {
|
|
168
|
+
return StateField.define({
|
|
169
|
+
create() {
|
|
170
|
+
return "";
|
|
171
|
+
},
|
|
172
|
+
update(value, transaction) {
|
|
173
|
+
if (transaction.docChanged) {
|
|
174
|
+
let new_value = transaction.newDoc.toString();
|
|
175
|
+
if (new_value !== $fieldStore.value) {
|
|
176
|
+
$fieldStore.value = parse_string ? parse_string_value(new_value) : new_value;
|
|
177
|
+
}
|
|
178
|
+
return new_value;
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
143
184
|
$: update_editor_content($fieldStore.value);
|
|
144
185
|
function update_editor_content(new_value) {
|
|
145
186
|
if (editor_view) {
|
|
@@ -155,7 +196,7 @@ function update_editor_content(new_value) {
|
|
|
155
196
|
}
|
|
156
197
|
}
|
|
157
198
|
}
|
|
158
|
-
function fake_handlebars_lang() {
|
|
199
|
+
function fake_handlebars_lang(Decoration, MatchDecorator, ViewPlugin) {
|
|
159
200
|
const curly_decorator = new MatchDecorator({
|
|
160
201
|
regexp: /(\{\{[^}]*\}\})|(~~[^\s]*)/g,
|
|
161
202
|
decoration: Decoration.mark({ class: "curly-braces-highlight" })
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Content } from "svelte-jsoneditor";
|
|
2
2
|
import type { Writable } from "svelte/store";
|
|
3
3
|
export declare const determine_content: (value: any) => Content;
|
|
4
|
-
export declare const handle_store_change: (value: any, jsoneditor: JSONEditor) => void;
|
|
4
|
+
export declare const handle_store_change: (value: any, jsoneditor: import("svelte-jsoneditor").JSONEditor) => void;
|
|
5
5
|
export declare const handle_editor_change: (new_content: Content, store: Writable<any>) => void;
|
|
6
6
|
export declare const handle_editor_change_setter: (new_content: Content, setter: (value: any) => void) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stubber/form-fields",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "An automatic form builder based on field specifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"components",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"prettier-plugin-svelte": "^2.10.1",
|
|
50
50
|
"prettier-plugin-tailwindcss": "^0.4.1",
|
|
51
51
|
"publint": "^0.2.2",
|
|
52
|
+
"rollup-plugin-visualizer": "^7.0.1",
|
|
52
53
|
"socket.io-client": "^4.8.1",
|
|
53
54
|
"svelte-check": "^3.4.3",
|
|
54
55
|
"tailwindcss": "^3.3.3",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"@codemirror/lang-javascript": "^6.2.4",
|
|
64
65
|
"@codemirror/state": "^6.5.2",
|
|
65
66
|
"@codemirror/view": "^6.38.4",
|
|
66
|
-
"@stubber/ui": "^1.13.
|
|
67
|
+
"@stubber/ui": "^1.13.9",
|
|
67
68
|
"ag-grid-community": "^31.0.2",
|
|
68
69
|
"ag-grid-enterprise": "^31.0.2",
|
|
69
70
|
"codemirror": "^6.0.2",
|
|
@@ -84,11 +85,6 @@
|
|
|
84
85
|
"svelte-markdown": "^0.4.0",
|
|
85
86
|
"svelte-writable-derived": "^3.1.0"
|
|
86
87
|
},
|
|
87
|
-
"pnpm": {
|
|
88
|
-
"patchedDependencies": {
|
|
89
|
-
"@beyonk/svelte-mapbox@11.0.0": "patches/@beyonk__svelte-mapbox@11.0.0.patch"
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
88
|
"release": {
|
|
93
89
|
"branches": [
|
|
94
90
|
"staging"
|