@nnkogift/dhis2-form-utils-dhis2-ui 0.1.0-alpha.0 → 0.1.0-alpha.2
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/index.cjs +558 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -1
- package/dist/index.d.ts +31 -1
- package/dist/index.js +541 -39
- package/dist/index.js.map +1 -1
- package/package.json +10 -4
package/dist/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { InputField,
|
|
3
|
-
import { jsx,
|
|
1
|
+
import { resolveFieldValidation, computeAgeFromDob, parseMultiTextValue, joinMultiTextValue, useOrgUnitPickerContext, useOrganisationUnitsQuery, useFieldControl, useSectionState, useFormFeedback, useFileResourceUpload } from '@nnkogift/dhis2-form-utils-hooks';
|
|
2
|
+
import { InputField, TextAreaField, CalendarInput, Field, Radio, CheckboxField, SingleSelectField, SingleSelectOption, MultiSelectField, MultiSelectOption, Button, OrganisationUnitTree, FileInputField, NoticeBox } from '@dhis2/ui';
|
|
3
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
import { useMemo, useState, useEffect, useRef } from 'react';
|
|
5
|
+
import { useConfig } from '@dhis2/app-runtime';
|
|
6
|
+
import { Map, NavigationControl, Marker } from 'maplibre-gl';
|
|
7
|
+
import { TerraDraw, TerraDrawPolygonMode, TerraDrawLineStringMode, TerraDrawPointMode } from 'terra-draw';
|
|
8
|
+
import { TerraDrawMapLibreGLAdapter } from 'terra-draw-maplibre-gl-adapter';
|
|
9
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
4
10
|
|
|
5
11
|
// src/fields/D2Field.tsx
|
|
6
12
|
function D2TextField({ control }) {
|
|
@@ -234,36 +240,54 @@ var BOOLEAN_OPTIONS = [
|
|
|
234
240
|
];
|
|
235
241
|
var RADIO_RENDER_HINTS = /* @__PURE__ */ new Set(["RADIO", "VERTICAL_RADIOBUTTONS", "HORIZONTAL_RADIOBUTTONS"]);
|
|
236
242
|
function D2BooleanField({ control }) {
|
|
237
|
-
const { fieldConfig, field, isMandatory, isDisabled } = control;
|
|
238
|
-
const { validationText, hasError, hasWarning } =
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
243
|
+
const { fieldConfig, field, isMandatory, isDisabled } = useMemo(() => control, [control]);
|
|
244
|
+
const { validationText, hasError, hasWarning } = useMemo(
|
|
245
|
+
() => resolveFieldValidation(control),
|
|
246
|
+
[control]
|
|
247
|
+
);
|
|
248
|
+
return /* @__PURE__ */ jsx(
|
|
249
|
+
Field,
|
|
250
|
+
{
|
|
251
|
+
label: fieldConfig.label,
|
|
252
|
+
helpText: fieldConfig.description,
|
|
253
|
+
warning: hasWarning,
|
|
254
|
+
error: hasError,
|
|
255
|
+
required: isMandatory,
|
|
256
|
+
disabled: isDisabled,
|
|
257
|
+
validationText,
|
|
258
|
+
children: /* @__PURE__ */ jsx(
|
|
259
|
+
"div",
|
|
260
|
+
{
|
|
261
|
+
style: {
|
|
262
|
+
display: "flex",
|
|
263
|
+
gap: 16
|
|
264
|
+
},
|
|
265
|
+
children: BOOLEAN_OPTIONS.map((option) => /* @__PURE__ */ jsx(
|
|
266
|
+
Radio,
|
|
267
|
+
{
|
|
268
|
+
name: field.name,
|
|
269
|
+
label: option.label,
|
|
270
|
+
value: option.value,
|
|
271
|
+
checked: field.value === option.value,
|
|
272
|
+
disabled: isDisabled,
|
|
273
|
+
onChange: ({ value }) => {
|
|
274
|
+
field.onChange(value ?? "");
|
|
275
|
+
},
|
|
276
|
+
onBlur: field.onBlur
|
|
277
|
+
},
|
|
278
|
+
option.label
|
|
279
|
+
))
|
|
280
|
+
}
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
);
|
|
263
284
|
}
|
|
264
285
|
function D2TrueOnlyField({ control }) {
|
|
265
|
-
const { fieldConfig, field, isMandatory, isDisabled } = control;
|
|
266
|
-
const { validationText, hasError, hasWarning } =
|
|
286
|
+
const { fieldConfig, field, isMandatory, isDisabled } = useMemo(() => control, [control]);
|
|
287
|
+
const { validationText, hasError, hasWarning } = useMemo(
|
|
288
|
+
() => resolveFieldValidation(control),
|
|
289
|
+
[control]
|
|
290
|
+
);
|
|
267
291
|
return /* @__PURE__ */ jsx(
|
|
268
292
|
CheckboxField,
|
|
269
293
|
{
|
|
@@ -333,11 +357,12 @@ function D2SelectField({ control }) {
|
|
|
333
357
|
function D2MultiSelectField({ control }) {
|
|
334
358
|
const { fieldConfig, field, isMandatory, isDisabled } = control;
|
|
335
359
|
const { validationText, hasError, hasWarning } = resolveFieldValidation(control);
|
|
336
|
-
const options = (
|
|
337
|
-
(option) => ({
|
|
360
|
+
const options = useMemo(
|
|
361
|
+
() => (control.visibleOptions ?? fieldConfig.optionSet?.options ?? []).map((option) => ({
|
|
338
362
|
label: option.label,
|
|
339
363
|
value: option.code
|
|
340
|
-
})
|
|
364
|
+
})),
|
|
365
|
+
[control.visibleOptions, fieldConfig.optionSet?.options]
|
|
341
366
|
);
|
|
342
367
|
const selected = parseMultiTextValue(field.value);
|
|
343
368
|
return /* @__PURE__ */ jsx(
|
|
@@ -359,6 +384,482 @@ function D2MultiSelectField({ control }) {
|
|
|
359
384
|
}
|
|
360
385
|
);
|
|
361
386
|
}
|
|
387
|
+
function D2OrgUnitField({ control }) {
|
|
388
|
+
const { fieldConfig, field, isMandatory, isDisabled } = control;
|
|
389
|
+
const { validationText, hasError, hasWarning } = resolveFieldValidation(control);
|
|
390
|
+
const pickerContext = useOrgUnitPickerContext();
|
|
391
|
+
const { organisationUnits, roots, loading, error } = useOrganisationUnitsQuery(
|
|
392
|
+
pickerContext?.roots
|
|
393
|
+
);
|
|
394
|
+
const value = field.value;
|
|
395
|
+
const selectedUnit = organisationUnits.find((ou) => ou.id === value);
|
|
396
|
+
const selectedLabel = selectedUnit?.displayName ?? value;
|
|
397
|
+
return /* @__PURE__ */ jsxs("fieldset", { children: [
|
|
398
|
+
/* @__PURE__ */ jsxs("legend", { children: [
|
|
399
|
+
fieldConfig.label,
|
|
400
|
+
isMandatory ? " *" : ""
|
|
401
|
+
] }),
|
|
402
|
+
fieldConfig.description ? /* @__PURE__ */ jsx("p", { children: fieldConfig.description }) : null,
|
|
403
|
+
selectedLabel ? /* @__PURE__ */ jsxs("p", { children: [
|
|
404
|
+
"Selected: ",
|
|
405
|
+
selectedLabel,
|
|
406
|
+
" ",
|
|
407
|
+
/* @__PURE__ */ jsx(
|
|
408
|
+
Button,
|
|
409
|
+
{
|
|
410
|
+
small: true,
|
|
411
|
+
disabled: isDisabled,
|
|
412
|
+
onClick: () => {
|
|
413
|
+
field.onChange("");
|
|
414
|
+
},
|
|
415
|
+
children: "Clear"
|
|
416
|
+
}
|
|
417
|
+
)
|
|
418
|
+
] }) : null,
|
|
419
|
+
roots.length > 0 ? /* @__PURE__ */ jsx(
|
|
420
|
+
OrganisationUnitTree,
|
|
421
|
+
{
|
|
422
|
+
roots,
|
|
423
|
+
singleSelection: true,
|
|
424
|
+
disableSelection: isDisabled,
|
|
425
|
+
initiallyExpanded: roots.map((id) => `/${id}`),
|
|
426
|
+
onChange: ({ id }) => {
|
|
427
|
+
field.onChange(id);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
) : loading ? /* @__PURE__ */ jsx("p", { children: "Loading organisation units\u2026" }) : error ? /* @__PURE__ */ jsx("p", { children: "Failed to load organisation units." }) : null,
|
|
431
|
+
validationText ? /* @__PURE__ */ jsx("p", { "data-error": hasError || void 0, "data-warning": hasWarning || void 0, children: validationText }) : null
|
|
432
|
+
] });
|
|
433
|
+
}
|
|
434
|
+
function useFileFieldUpload(onChange) {
|
|
435
|
+
const { upload, uploading, error } = useFileResourceUpload();
|
|
436
|
+
const [uploadError, setUploadError] = useState(void 0);
|
|
437
|
+
const handleFiles = (files) => {
|
|
438
|
+
const file = files.item(0);
|
|
439
|
+
if (!file) return;
|
|
440
|
+
setUploadError(void 0);
|
|
441
|
+
upload(file).then((result) => {
|
|
442
|
+
onChange(result.id);
|
|
443
|
+
}).catch((uploadFailure) => {
|
|
444
|
+
setUploadError(
|
|
445
|
+
uploadFailure instanceof Error ? uploadFailure.message : "Upload failed"
|
|
446
|
+
);
|
|
447
|
+
});
|
|
448
|
+
};
|
|
449
|
+
return { handleFiles, uploading, uploadError: uploadError ?? error?.message };
|
|
450
|
+
}
|
|
451
|
+
function D2FileField({ control }) {
|
|
452
|
+
const { fieldConfig, field, isMandatory, isDisabled } = control;
|
|
453
|
+
const { validationText, hasError, hasWarning } = resolveFieldValidation(control);
|
|
454
|
+
const { handleFiles, uploading, uploadError } = useFileFieldUpload(field.onChange);
|
|
455
|
+
return /* @__PURE__ */ jsx(
|
|
456
|
+
FileInputField,
|
|
457
|
+
{
|
|
458
|
+
name: field.name,
|
|
459
|
+
label: fieldConfig.label,
|
|
460
|
+
helpText: uploadError ?? fieldConfig.description ?? (uploading ? "Uploading\u2026" : "Max size depends on server configuration."),
|
|
461
|
+
required: isMandatory,
|
|
462
|
+
disabled: isDisabled || uploading,
|
|
463
|
+
warning: hasWarning,
|
|
464
|
+
error: hasError || Boolean(uploadError),
|
|
465
|
+
validationText,
|
|
466
|
+
buttonLabel: field.value ? "Replace file" : "Select file",
|
|
467
|
+
onChange: ({ files }) => {
|
|
468
|
+
handleFiles(files);
|
|
469
|
+
},
|
|
470
|
+
onBlur: field.onBlur
|
|
471
|
+
}
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
function D2ImageField({ control }) {
|
|
475
|
+
const { fieldConfig, field, isMandatory, isDisabled } = control;
|
|
476
|
+
const { validationText, hasError, hasWarning } = resolveFieldValidation(control);
|
|
477
|
+
const { handleFiles, uploading, uploadError } = useFileFieldUpload(field.onChange);
|
|
478
|
+
const { baseUrl } = useConfig();
|
|
479
|
+
const value = field.value;
|
|
480
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
481
|
+
/* @__PURE__ */ jsx(
|
|
482
|
+
FileInputField,
|
|
483
|
+
{
|
|
484
|
+
name: field.name,
|
|
485
|
+
label: fieldConfig.label,
|
|
486
|
+
accept: "image/*",
|
|
487
|
+
helpText: uploadError ?? fieldConfig.description ?? (uploading ? "Uploading\u2026" : "Max size depends on server configuration."),
|
|
488
|
+
required: isMandatory,
|
|
489
|
+
disabled: isDisabled || uploading,
|
|
490
|
+
warning: hasWarning,
|
|
491
|
+
error: hasError || Boolean(uploadError),
|
|
492
|
+
validationText,
|
|
493
|
+
buttonLabel: value ? "Replace image" : "Select image",
|
|
494
|
+
onChange: ({ files }) => {
|
|
495
|
+
handleFiles(files);
|
|
496
|
+
},
|
|
497
|
+
onBlur: field.onBlur
|
|
498
|
+
}
|
|
499
|
+
),
|
|
500
|
+
value ? /* @__PURE__ */ jsx(
|
|
501
|
+
"img",
|
|
502
|
+
{
|
|
503
|
+
src: `${baseUrl}/api/fileResources/${value}/data`,
|
|
504
|
+
alt: fieldConfig.label,
|
|
505
|
+
style: { maxWidth: "200px", maxHeight: "200px", marginTop: "8px" }
|
|
506
|
+
}
|
|
507
|
+
) : null
|
|
508
|
+
] });
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ../../packages/map/src/coordinateValue.ts
|
|
512
|
+
var LNG_MIN = -180;
|
|
513
|
+
var LNG_MAX = 180;
|
|
514
|
+
var LAT_MIN = -90;
|
|
515
|
+
var LAT_MAX = 90;
|
|
516
|
+
function parseCoordinateValue(value) {
|
|
517
|
+
if (!value) return null;
|
|
518
|
+
let parsed;
|
|
519
|
+
try {
|
|
520
|
+
parsed = JSON.parse(value);
|
|
521
|
+
} catch {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
if (!Array.isArray(parsed) || parsed.length !== 2) return null;
|
|
525
|
+
const lng = parsed[0];
|
|
526
|
+
const lat = parsed[1];
|
|
527
|
+
if (typeof lng !== "number" || typeof lat !== "number") return null;
|
|
528
|
+
if (!Number.isFinite(lng) || !Number.isFinite(lat)) return null;
|
|
529
|
+
if (lng < LNG_MIN || lng > LNG_MAX || lat < LAT_MIN || lat > LAT_MAX) return null;
|
|
530
|
+
return { lng, lat };
|
|
531
|
+
}
|
|
532
|
+
function joinCoordinateValue(lng, lat) {
|
|
533
|
+
return JSON.stringify([lng, lat]);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// ../../packages/map/src/geojsonValue.ts
|
|
537
|
+
var GEOMETRY_TYPES = /* @__PURE__ */ new Set([
|
|
538
|
+
"Point",
|
|
539
|
+
"MultiPoint",
|
|
540
|
+
"LineString",
|
|
541
|
+
"MultiLineString",
|
|
542
|
+
"Polygon",
|
|
543
|
+
"MultiPolygon",
|
|
544
|
+
"GeometryCollection"
|
|
545
|
+
]);
|
|
546
|
+
function isGeometryShape(value) {
|
|
547
|
+
if (typeof value !== "object" || value === null) return false;
|
|
548
|
+
const candidate = value;
|
|
549
|
+
if (typeof candidate.type !== "string" || !GEOMETRY_TYPES.has(candidate.type)) return false;
|
|
550
|
+
if (candidate.type === "GeometryCollection") return Array.isArray(candidate.geometries);
|
|
551
|
+
return Array.isArray(candidate.coordinates);
|
|
552
|
+
}
|
|
553
|
+
function parseGeojsonGeometry(value) {
|
|
554
|
+
if (!value) return null;
|
|
555
|
+
let parsed;
|
|
556
|
+
try {
|
|
557
|
+
parsed = JSON.parse(value);
|
|
558
|
+
} catch {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
return isGeometryShape(parsed) ? parsed : null;
|
|
562
|
+
}
|
|
563
|
+
function stringifyGeojsonGeometry(geometry) {
|
|
564
|
+
return JSON.stringify(geometry);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// ../../packages/map/src/defaultMapStyle.ts
|
|
568
|
+
var defaultMapStyle = {
|
|
569
|
+
version: 8,
|
|
570
|
+
sources: {
|
|
571
|
+
osm: {
|
|
572
|
+
type: "raster",
|
|
573
|
+
tiles: ["https://tile.openstreetmap.org/{z}/{x}/{y}.png"],
|
|
574
|
+
tileSize: 256,
|
|
575
|
+
attribution: "© OpenStreetMap contributors"
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
layers: [
|
|
579
|
+
{
|
|
580
|
+
id: "osm",
|
|
581
|
+
type: "raster",
|
|
582
|
+
source: "osm"
|
|
583
|
+
}
|
|
584
|
+
]
|
|
585
|
+
};
|
|
586
|
+
function useMapLibreMap({ style }) {
|
|
587
|
+
const containerRef = useRef(null);
|
|
588
|
+
const [map, setMap] = useState(null);
|
|
589
|
+
useEffect(() => {
|
|
590
|
+
if (!containerRef.current) return;
|
|
591
|
+
const instance = new Map({
|
|
592
|
+
container: containerRef.current,
|
|
593
|
+
style,
|
|
594
|
+
center: [0, 0],
|
|
595
|
+
zoom: 1
|
|
596
|
+
});
|
|
597
|
+
instance.addControl(new NavigationControl(), "top-right");
|
|
598
|
+
instance.once("load", () => {
|
|
599
|
+
setMap(instance);
|
|
600
|
+
});
|
|
601
|
+
return () => {
|
|
602
|
+
instance.remove();
|
|
603
|
+
setMap(null);
|
|
604
|
+
};
|
|
605
|
+
}, []);
|
|
606
|
+
return { containerRef, map };
|
|
607
|
+
}
|
|
608
|
+
function CoordinateMapPicker({
|
|
609
|
+
value,
|
|
610
|
+
onChange,
|
|
611
|
+
mapStyle,
|
|
612
|
+
disabled,
|
|
613
|
+
className
|
|
614
|
+
}) {
|
|
615
|
+
const { containerRef, map } = useMapLibreMap({ style: mapStyle ?? defaultMapStyle });
|
|
616
|
+
const markerRef = useRef(null);
|
|
617
|
+
const onChangeRef = useRef(onChange);
|
|
618
|
+
onChangeRef.current = onChange;
|
|
619
|
+
useEffect(() => {
|
|
620
|
+
if (!map) return;
|
|
621
|
+
const ensureMarker = (lng, lat) => {
|
|
622
|
+
if (markerRef.current) {
|
|
623
|
+
markerRef.current.setLngLat([lng, lat]);
|
|
624
|
+
return markerRef.current;
|
|
625
|
+
}
|
|
626
|
+
const marker = new Marker({ draggable: !disabled }).setLngLat([lng, lat]).addTo(map);
|
|
627
|
+
marker.on("dragend", () => {
|
|
628
|
+
const lngLat = marker.getLngLat();
|
|
629
|
+
onChangeRef.current(joinCoordinateValue(lngLat.lng, lngLat.lat));
|
|
630
|
+
});
|
|
631
|
+
markerRef.current = marker;
|
|
632
|
+
return marker;
|
|
633
|
+
};
|
|
634
|
+
const handleClick = (event) => {
|
|
635
|
+
if (disabled) return;
|
|
636
|
+
ensureMarker(event.lngLat.lng, event.lngLat.lat);
|
|
637
|
+
onChangeRef.current(joinCoordinateValue(event.lngLat.lng, event.lngLat.lat));
|
|
638
|
+
};
|
|
639
|
+
map.on("click", handleClick);
|
|
640
|
+
const parsed = parseCoordinateValue(value);
|
|
641
|
+
if (parsed) {
|
|
642
|
+
ensureMarker(parsed.lng, parsed.lat);
|
|
643
|
+
map.setCenter([parsed.lng, parsed.lat]);
|
|
644
|
+
}
|
|
645
|
+
return () => {
|
|
646
|
+
map.off("click", handleClick);
|
|
647
|
+
};
|
|
648
|
+
}, [map, disabled]);
|
|
649
|
+
useEffect(() => {
|
|
650
|
+
if (!markerRef.current) return;
|
|
651
|
+
const parsed = parseCoordinateValue(value);
|
|
652
|
+
if (parsed) {
|
|
653
|
+
markerRef.current.setLngLat([parsed.lng, parsed.lat]);
|
|
654
|
+
}
|
|
655
|
+
}, [value]);
|
|
656
|
+
useEffect(() => {
|
|
657
|
+
markerRef.current?.setDraggable(!disabled);
|
|
658
|
+
}, [disabled]);
|
|
659
|
+
return /* @__PURE__ */ jsx(
|
|
660
|
+
"div",
|
|
661
|
+
{
|
|
662
|
+
ref: containerRef,
|
|
663
|
+
className,
|
|
664
|
+
style: { width: "100%", height: "300px" },
|
|
665
|
+
"aria-hidden": "true"
|
|
666
|
+
}
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
var DEFAULT_MODES = ["point", "linestring", "polygon"];
|
|
670
|
+
function createDrawMode(mode) {
|
|
671
|
+
switch (mode) {
|
|
672
|
+
case "point":
|
|
673
|
+
return new TerraDrawPointMode();
|
|
674
|
+
case "linestring":
|
|
675
|
+
return new TerraDrawLineStringMode();
|
|
676
|
+
case "polygon":
|
|
677
|
+
return new TerraDrawPolygonMode();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function GeoJsonMapEditor({
|
|
681
|
+
value,
|
|
682
|
+
onChange,
|
|
683
|
+
mapStyle,
|
|
684
|
+
disabled,
|
|
685
|
+
className,
|
|
686
|
+
modes = DEFAULT_MODES
|
|
687
|
+
}) {
|
|
688
|
+
const { containerRef, map } = useMapLibreMap({ style: mapStyle ?? defaultMapStyle });
|
|
689
|
+
const drawRef = useRef(null);
|
|
690
|
+
const onChangeRef = useRef(onChange);
|
|
691
|
+
onChangeRef.current = onChange;
|
|
692
|
+
useEffect(() => {
|
|
693
|
+
if (!map) return;
|
|
694
|
+
const draw = new TerraDraw({
|
|
695
|
+
adapter: new TerraDrawMapLibreGLAdapter({ map }),
|
|
696
|
+
modes: modes.map(createDrawMode)
|
|
697
|
+
});
|
|
698
|
+
drawRef.current = draw;
|
|
699
|
+
draw.start();
|
|
700
|
+
const initial = parseGeojsonGeometry(value);
|
|
701
|
+
if (initial) {
|
|
702
|
+
draw.addFeatures([
|
|
703
|
+
{ type: "Feature", geometry: initial, properties: {} }
|
|
704
|
+
]);
|
|
705
|
+
}
|
|
706
|
+
const handleFinish = (id) => {
|
|
707
|
+
const feature = draw.getSnapshotFeature(id);
|
|
708
|
+
if (feature) {
|
|
709
|
+
onChangeRef.current(stringifyGeojsonGeometry(feature.geometry));
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
draw.on("finish", handleFinish);
|
|
713
|
+
return () => {
|
|
714
|
+
draw.off("finish", handleFinish);
|
|
715
|
+
try {
|
|
716
|
+
draw.stop();
|
|
717
|
+
} catch {
|
|
718
|
+
}
|
|
719
|
+
drawRef.current = null;
|
|
720
|
+
};
|
|
721
|
+
}, [map]);
|
|
722
|
+
const startDrawing = (mode) => {
|
|
723
|
+
const draw = drawRef.current;
|
|
724
|
+
if (!draw || disabled) return;
|
|
725
|
+
draw.clear();
|
|
726
|
+
draw.setMode(mode);
|
|
727
|
+
};
|
|
728
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
729
|
+
/* @__PURE__ */ jsx("div", { ref: containerRef, style: { width: "100%", height: "300px" }, "aria-hidden": "true" }),
|
|
730
|
+
!disabled ? /* @__PURE__ */ jsx("div", { role: "group", "aria-label": "Draw geometry", children: modes.map((mode) => /* @__PURE__ */ jsxs(
|
|
731
|
+
"button",
|
|
732
|
+
{
|
|
733
|
+
type: "button",
|
|
734
|
+
onClick: () => {
|
|
735
|
+
startDrawing(mode);
|
|
736
|
+
},
|
|
737
|
+
children: [
|
|
738
|
+
"Draw ",
|
|
739
|
+
mode
|
|
740
|
+
]
|
|
741
|
+
},
|
|
742
|
+
mode
|
|
743
|
+
)) }) : null
|
|
744
|
+
] });
|
|
745
|
+
}
|
|
746
|
+
function D2CoordinateField({ control }) {
|
|
747
|
+
const { fieldConfig, field, isMandatory, isDisabled } = control;
|
|
748
|
+
const { validationText, hasError, hasWarning } = resolveFieldValidation(control);
|
|
749
|
+
const value = field.value;
|
|
750
|
+
const [lngText, setLngText] = useState(() => parseCoordinateValue(value)?.lng.toString() ?? "");
|
|
751
|
+
const [latText, setLatText] = useState(() => parseCoordinateValue(value)?.lat.toString() ?? "");
|
|
752
|
+
useEffect(() => {
|
|
753
|
+
const parsed = parseCoordinateValue(value);
|
|
754
|
+
setLngText(parsed ? parsed.lng.toString() : "");
|
|
755
|
+
setLatText(parsed ? parsed.lat.toString() : "");
|
|
756
|
+
}, [value]);
|
|
757
|
+
const commit = (lngRaw, latRaw) => {
|
|
758
|
+
const lng = parseFloat(lngRaw);
|
|
759
|
+
const lat = parseFloat(latRaw);
|
|
760
|
+
if (Number.isFinite(lng) && Number.isFinite(lat)) {
|
|
761
|
+
field.onChange(joinCoordinateValue(lng, lat));
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
return /* @__PURE__ */ jsxs("fieldset", { children: [
|
|
765
|
+
/* @__PURE__ */ jsxs("legend", { children: [
|
|
766
|
+
fieldConfig.label,
|
|
767
|
+
isMandatory ? " *" : ""
|
|
768
|
+
] }),
|
|
769
|
+
fieldConfig.description ? /* @__PURE__ */ jsx("p", { children: fieldConfig.description }) : null,
|
|
770
|
+
/* @__PURE__ */ jsx(CoordinateMapPicker, { value, onChange: field.onChange, disabled: isDisabled }),
|
|
771
|
+
/* @__PURE__ */ jsx(
|
|
772
|
+
InputField,
|
|
773
|
+
{
|
|
774
|
+
name: `${field.name}-lng`,
|
|
775
|
+
type: "number",
|
|
776
|
+
label: "Longitude",
|
|
777
|
+
value: lngText,
|
|
778
|
+
disabled: isDisabled,
|
|
779
|
+
onChange: ({ value: next }) => {
|
|
780
|
+
const text = next ?? "";
|
|
781
|
+
setLngText(text);
|
|
782
|
+
commit(text, latText);
|
|
783
|
+
},
|
|
784
|
+
onBlur: field.onBlur
|
|
785
|
+
}
|
|
786
|
+
),
|
|
787
|
+
/* @__PURE__ */ jsx(
|
|
788
|
+
InputField,
|
|
789
|
+
{
|
|
790
|
+
name: `${field.name}-lat`,
|
|
791
|
+
type: "number",
|
|
792
|
+
label: "Latitude",
|
|
793
|
+
value: latText,
|
|
794
|
+
disabled: isDisabled,
|
|
795
|
+
onChange: ({ value: next }) => {
|
|
796
|
+
const text = next ?? "";
|
|
797
|
+
setLatText(text);
|
|
798
|
+
commit(lngText, text);
|
|
799
|
+
},
|
|
800
|
+
onBlur: field.onBlur
|
|
801
|
+
}
|
|
802
|
+
),
|
|
803
|
+
validationText ? /* @__PURE__ */ jsx("p", { "data-error": hasError || void 0, "data-warning": hasWarning || void 0, children: validationText }) : null
|
|
804
|
+
] });
|
|
805
|
+
}
|
|
806
|
+
function D2GeoJsonField({ control }) {
|
|
807
|
+
const { fieldConfig, field, isMandatory, isDisabled } = control;
|
|
808
|
+
const { validationText, hasError, hasWarning } = resolveFieldValidation(control);
|
|
809
|
+
const value = field.value;
|
|
810
|
+
const [textValue, setTextValue] = useState(value);
|
|
811
|
+
const [textError, setTextError] = useState(void 0);
|
|
812
|
+
return /* @__PURE__ */ jsxs("fieldset", { children: [
|
|
813
|
+
/* @__PURE__ */ jsxs("legend", { children: [
|
|
814
|
+
fieldConfig.label,
|
|
815
|
+
isMandatory ? " *" : ""
|
|
816
|
+
] }),
|
|
817
|
+
fieldConfig.description ? /* @__PURE__ */ jsx("p", { children: fieldConfig.description }) : null,
|
|
818
|
+
/* @__PURE__ */ jsx(
|
|
819
|
+
GeoJsonMapEditor,
|
|
820
|
+
{
|
|
821
|
+
value,
|
|
822
|
+
onChange: (next) => {
|
|
823
|
+
field.onChange(next);
|
|
824
|
+
const geometry = parseGeojsonGeometry(next);
|
|
825
|
+
setTextValue(geometry ? JSON.stringify(geometry, null, 2) : next);
|
|
826
|
+
setTextError(void 0);
|
|
827
|
+
},
|
|
828
|
+
disabled: isDisabled
|
|
829
|
+
}
|
|
830
|
+
),
|
|
831
|
+
/* @__PURE__ */ jsx(
|
|
832
|
+
TextAreaField,
|
|
833
|
+
{
|
|
834
|
+
name: `${field.name}-geometry`,
|
|
835
|
+
label: "Geometry (JSON)",
|
|
836
|
+
value: textValue,
|
|
837
|
+
disabled: isDisabled,
|
|
838
|
+
warning: Boolean(textError),
|
|
839
|
+
validationText: textError,
|
|
840
|
+
onChange: ({ value: next }) => {
|
|
841
|
+
setTextValue(next ?? "");
|
|
842
|
+
},
|
|
843
|
+
onBlur: () => {
|
|
844
|
+
if (!textValue) {
|
|
845
|
+
field.onChange("");
|
|
846
|
+
setTextError(void 0);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
const geometry = parseGeojsonGeometry(textValue);
|
|
850
|
+
if (geometry) {
|
|
851
|
+
field.onChange(stringifyGeojsonGeometry(geometry));
|
|
852
|
+
setTextError(void 0);
|
|
853
|
+
} else {
|
|
854
|
+
setTextError("Not a valid GeoJSON geometry");
|
|
855
|
+
}
|
|
856
|
+
field.onBlur();
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
),
|
|
860
|
+
validationText ? /* @__PURE__ */ jsx("p", { "data-error": hasError || void 0, "data-warning": hasWarning || void 0, children: validationText }) : null
|
|
861
|
+
] });
|
|
862
|
+
}
|
|
362
863
|
function D2UnsupportedField({ control }) {
|
|
363
864
|
const { fieldConfig, field, widgetKind, isMandatory } = control;
|
|
364
865
|
const { validationText, hasError, hasWarning } = resolveFieldValidation(control);
|
|
@@ -397,10 +898,11 @@ var WIDGET_BY_KIND = {
|
|
|
397
898
|
select: D2SelectField,
|
|
398
899
|
multiSelect: D2MultiSelectField,
|
|
399
900
|
datetime: D2DateTimeField,
|
|
400
|
-
coordinate:
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
901
|
+
coordinate: D2CoordinateField,
|
|
902
|
+
geojson: D2GeoJsonField,
|
|
903
|
+
orgUnit: D2OrgUnitField,
|
|
904
|
+
file: D2FileField,
|
|
905
|
+
image: D2ImageField,
|
|
404
906
|
unsupported: D2UnsupportedField
|
|
405
907
|
};
|
|
406
908
|
function D2FieldWidget({ control }) {
|
|
@@ -446,6 +948,6 @@ function FormFeedback() {
|
|
|
446
948
|
] });
|
|
447
949
|
}
|
|
448
950
|
|
|
449
|
-
export { D2Field, D2FieldWidget, FormFeedback, FormSection };
|
|
951
|
+
export { D2AgeField, D2BooleanField, D2CoordinateField, D2DateField, D2DateTimeField, D2EmailField, D2Field, D2FieldWidget, D2FileField, D2GeoJsonField, D2ImageField, D2IntegerField, D2LongTextField, D2MultiSelectField, D2NumberField, D2OrgUnitField, D2PercentageField, D2PhoneField, D2SelectField, D2TextField, D2TimeField, D2TrueOnlyField, D2UnsupportedField, FormFeedback, FormSection };
|
|
450
952
|
//# sourceMappingURL=index.js.map
|
|
451
953
|
//# sourceMappingURL=index.js.map
|