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