@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 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 .env_dev .env
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
- Conversion Progress:
35
+ # Quick Testing
36
36
 
37
- | Component | TS DONE | IS STRING |
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 { type Writable } from "svelte/store";
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
- const custom_completion_keymap = completionKeymap.filter((binding) => binding.key != "Enter").concat([
33
- {
34
- key: "Enter",
35
- run: () => {
36
- return false;
37
- }
38
- },
39
- {
40
- key: "Tab",
41
- run: acceptCompletion
42
- },
43
- {
44
- key: "Ctrl-Space",
45
- run: startCompletion,
46
- stopPropagation: true
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
- const min_height_editor = EditorView.theme({
86
- ".cm-content, .cm-gutter": { minHeight: "200px" },
87
- "&": { maxHeight: "300px" },
88
- ".cm-scroller": { overflow: "auto" }
89
- });
90
- editor_view = new EditorView({
91
- state: EditorState.create({
92
- doc: value,
93
- extensions: [
94
- basicSetup,
95
- javascript(),
96
- stubber_completions,
97
- autocompletion({ defaultKeymap: false }),
98
- keymap.of(custom_completion_keymap),
99
- min_height_editor,
100
- update_listener
101
- ]
102
- }),
103
- parent: el
104
- });
105
- }
106
- const update_listener = EditorView.updateListener.of((update) => {
107
- if (update.docChanged) {
108
- const new_value = update.state.doc.toString();
109
- if (new_value !== value) {
110
- value = new_value;
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
- if (editor_view && editor_view.state.doc.toString() !== new_value) {
117
- editor_view.dispatch({
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: editor_view.state.doc.length,
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
- if (editor_view?.state.doc.toString() !== new_value) {
134
- editor_view.dispatch({
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: editor_view.state.doc.length,
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
- <JSONEditor
476
- bind:this={default_initvalue_editor}
477
- onChange={(content) =>
478
- handle_editor_change_setter(content, (new_val) => (value.initvalue.default = new_val))}
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
- <JSONEditor
487
- bind:this={override_initvalue_editor}
488
- onChange={(content) =>
489
- handle_editor_change_setter(content, (new_val) => (value.initvalue.override = new_val))}
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 { createGrid, GridApi } from "ag-grid-community";
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
- onMount(() => {
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
- <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
- />
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 w-full">
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:underline hover:text-surface-500"
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 h-72">
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 lat={browserLocation.lat} lng={browserLocation.lng}>
118
- <div class="relative w-0">
119
- <div
120
- class="bg-surface-0 p-4 rounded-xl rounded-bl-none shadow-2xl border absolute bottom-0 whitespace-nowrap"
121
- >
122
- <i class="fa-solid fa-user text-4xl" />
123
- </div>
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 lat={$fieldStore.value.lat} lng={$fieldStore.value.lng} popup={false}>
129
- <div class="relative w-0">
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-surface-0 p-4 rounded-xl rounded-bl-none shadow-2xl border absolute bottom-0 whitespace-nowrap"
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, 6)} <br />
134
- lng: {$fieldStore.value.lng?.toString()?.substring(0, 6)}
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 { Textarea } from "@stubber/ui/textarea";
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 w-full gap-2">
59
- <div id="reader" class="w-[250px] sm:w-[310px] flex flex-col items-center" />
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 fa-2xs" />
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 fa-2xs" />
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="flex ml-auto">
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="w-[250px] sm:w-[310px] aspect-square flex items-center justify-center">
79
- <p class="text-surface-800 text-label">No camera found</p>
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
- const extensions = [
92
- history({ newGroupDelay: 150 }),
93
- EditorView.lineWrapping,
94
- contentField,
95
- autocompletion({ override: [myCompletions], defaultKeymap: false }),
96
- tab_accept_completion,
97
- placeholder($fieldStore.label),
98
- EditorView.contentAttributes.of({
99
- spellcheck: "true"
100
- })
101
- ];
102
- if (code_language === "handlebars") {
103
- extensions.push(fake_handlebars_lang());
104
- }
105
- const str_value = $fieldStore?.value?.toString() ?? "";
106
- editor_view = new EditorView({
107
- state: EditorState.create({
108
- doc: str_value,
109
- extensions
110
- }),
111
- parent: element
112
- });
113
- };
114
- const tab_accept_completion = keymap.of(
115
- completionKeymap.filter((binding) => binding.key != "Enter").concat([
116
- {
117
- key: "Enter",
118
- run: () => {
119
- return false;
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
- key: "Tab",
124
- run: acceptCompletion
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
- const contentField = StateField.define({
129
- create() {
130
- return "";
131
- },
132
- update(value, transaction) {
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
- return value;
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 { JSONEditor, type Content } from "svelte-jsoneditor";
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;
@@ -1,5 +1,5 @@
1
1
  import { cloneDeep, isEqual } from "lodash-es";
2
- import { JSONEditor } from "svelte-jsoneditor";
2
+ import {} from "svelte-jsoneditor";
3
3
  export const determine_content = (value) => {
4
4
  if (typeof value === "string") {
5
5
  return { text: value, json: undefined };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stubber/form-fields",
3
- "version": "2.0.23",
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.7",
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"