@nnkogift/dhis2-form-utils-dhis2-ui 0.1.0-alpha.0 → 0.1.0-alpha.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/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 } = dhis2FormUtilsHooks.resolveFieldValidation(control);
241
- const options = isMandatory ? [...BOOLEAN_OPTIONS] : [...BOOLEAN_OPTIONS, { label: "\u2014", value: "" }];
242
- return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
243
- /* @__PURE__ */ jsxRuntime.jsxs("legend", { children: [
244
- fieldConfig.label,
245
- isMandatory ? " *" : ""
246
- ] }),
247
- fieldConfig.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: fieldConfig.description }) : null,
248
- options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
249
- ui.Radio,
250
- {
251
- name: field.name,
252
- label: option.label,
253
- value: option.value,
254
- checked: field.value === option.value,
255
- disabled: isDisabled,
256
- onChange: ({ value }) => {
257
- field.onChange(value ?? "");
258
- },
259
- onBlur: field.onBlur
260
- },
261
- option.label
262
- )),
263
- validationText ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-error": hasError || void 0, "data-warning": hasWarning || void 0, children: validationText }) : null
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 } = dhis2FormUtilsHooks.resolveFieldValidation(control);
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 = (control.visibleOptions ?? fieldConfig.optionSet?.options ?? []).map(
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,484 @@ 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/dist/coordinateValue.js
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/dist/geojsonValue.js
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/dist/defaultMapStyle.js
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: "&copy; 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
+
611
+ // ../../packages/map/dist/CoordinateMapPicker.js
612
+ function CoordinateMapPicker({
613
+ value,
614
+ onChange,
615
+ mapStyle,
616
+ disabled,
617
+ className
618
+ }) {
619
+ const { containerRef, map } = useMapLibreMap({ style: mapStyle ?? defaultMapStyle });
620
+ const markerRef = react.useRef(null);
621
+ const onChangeRef = react.useRef(onChange);
622
+ onChangeRef.current = onChange;
623
+ react.useEffect(() => {
624
+ if (!map) return;
625
+ const ensureMarker = (lng, lat) => {
626
+ if (markerRef.current) {
627
+ markerRef.current.setLngLat([lng, lat]);
628
+ return markerRef.current;
629
+ }
630
+ const marker = new maplibreGl.Marker({ draggable: !disabled }).setLngLat([lng, lat]).addTo(map);
631
+ marker.on("dragend", () => {
632
+ const lngLat = marker.getLngLat();
633
+ onChangeRef.current(joinCoordinateValue(lngLat.lng, lngLat.lat));
634
+ });
635
+ markerRef.current = marker;
636
+ return marker;
637
+ };
638
+ const handleClick = (event) => {
639
+ if (disabled) return;
640
+ ensureMarker(event.lngLat.lng, event.lngLat.lat);
641
+ onChangeRef.current(joinCoordinateValue(event.lngLat.lng, event.lngLat.lat));
642
+ };
643
+ map.on("click", handleClick);
644
+ const parsed = parseCoordinateValue(value);
645
+ if (parsed) {
646
+ ensureMarker(parsed.lng, parsed.lat);
647
+ map.setCenter([parsed.lng, parsed.lat]);
648
+ }
649
+ return () => {
650
+ map.off("click", handleClick);
651
+ };
652
+ }, [map, disabled]);
653
+ react.useEffect(() => {
654
+ if (!markerRef.current) return;
655
+ const parsed = parseCoordinateValue(value);
656
+ if (parsed) {
657
+ markerRef.current.setLngLat([parsed.lng, parsed.lat]);
658
+ }
659
+ }, [value]);
660
+ react.useEffect(() => {
661
+ markerRef.current?.setDraggable(!disabled);
662
+ }, [disabled]);
663
+ return /* @__PURE__ */ jsxRuntime.jsx(
664
+ "div",
665
+ {
666
+ ref: containerRef,
667
+ className,
668
+ style: { width: "100%", height: "300px" },
669
+ "aria-hidden": "true"
670
+ }
671
+ );
672
+ }
673
+ var DEFAULT_MODES = ["point", "linestring", "polygon"];
674
+ function createDrawMode(mode) {
675
+ switch (mode) {
676
+ case "point":
677
+ return new terraDraw.TerraDrawPointMode();
678
+ case "linestring":
679
+ return new terraDraw.TerraDrawLineStringMode();
680
+ case "polygon":
681
+ return new terraDraw.TerraDrawPolygonMode();
682
+ }
683
+ }
684
+ function GeoJsonMapEditor({
685
+ value,
686
+ onChange,
687
+ mapStyle,
688
+ disabled,
689
+ className,
690
+ modes = DEFAULT_MODES
691
+ }) {
692
+ const { containerRef, map } = useMapLibreMap({ style: mapStyle ?? defaultMapStyle });
693
+ const drawRef = react.useRef(null);
694
+ const onChangeRef = react.useRef(onChange);
695
+ onChangeRef.current = onChange;
696
+ react.useEffect(() => {
697
+ if (!map) return;
698
+ const draw = new terraDraw.TerraDraw({
699
+ adapter: new terraDrawMaplibreGlAdapter.TerraDrawMapLibreGLAdapter({ map }),
700
+ modes: modes.map(createDrawMode)
701
+ });
702
+ drawRef.current = draw;
703
+ draw.start();
704
+ const initial = parseGeojsonGeometry(value);
705
+ if (initial) {
706
+ draw.addFeatures([
707
+ { type: "Feature", geometry: initial, properties: {} }
708
+ ]);
709
+ }
710
+ const handleFinish = (id) => {
711
+ const feature = draw.getSnapshotFeature(id);
712
+ if (feature) {
713
+ onChangeRef.current(stringifyGeojsonGeometry(feature.geometry));
714
+ }
715
+ };
716
+ draw.on("finish", handleFinish);
717
+ return () => {
718
+ draw.off("finish", handleFinish);
719
+ try {
720
+ draw.stop();
721
+ } catch {
722
+ }
723
+ drawRef.current = null;
724
+ };
725
+ }, [map]);
726
+ const startDrawing = (mode) => {
727
+ const draw = drawRef.current;
728
+ if (!draw || disabled) return;
729
+ draw.clear();
730
+ draw.setMode(mode);
731
+ };
732
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
733
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, style: { width: "100%", height: "300px" }, "aria-hidden": "true" }),
734
+ !disabled ? /* @__PURE__ */ jsxRuntime.jsx("div", { role: "group", "aria-label": "Draw geometry", children: modes.map((mode) => /* @__PURE__ */ jsxRuntime.jsxs(
735
+ "button",
736
+ {
737
+ type: "button",
738
+ onClick: () => {
739
+ startDrawing(mode);
740
+ },
741
+ children: [
742
+ "Draw ",
743
+ mode
744
+ ]
745
+ },
746
+ mode
747
+ )) }) : null
748
+ ] });
749
+ }
750
+ function D2CoordinateField({ control }) {
751
+ const { fieldConfig, field, isMandatory, isDisabled } = control;
752
+ const { validationText, hasError, hasWarning } = dhis2FormUtilsHooks.resolveFieldValidation(control);
753
+ const value = field.value;
754
+ const [lngText, setLngText] = react.useState(() => parseCoordinateValue(value)?.lng.toString() ?? "");
755
+ const [latText, setLatText] = react.useState(() => parseCoordinateValue(value)?.lat.toString() ?? "");
756
+ react.useEffect(() => {
757
+ const parsed = parseCoordinateValue(value);
758
+ setLngText(parsed ? parsed.lng.toString() : "");
759
+ setLatText(parsed ? parsed.lat.toString() : "");
760
+ }, [value]);
761
+ const commit = (lngRaw, latRaw) => {
762
+ const lng = parseFloat(lngRaw);
763
+ const lat = parseFloat(latRaw);
764
+ if (Number.isFinite(lng) && Number.isFinite(lat)) {
765
+ field.onChange(joinCoordinateValue(lng, lat));
766
+ }
767
+ };
768
+ return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
769
+ /* @__PURE__ */ jsxRuntime.jsxs("legend", { children: [
770
+ fieldConfig.label,
771
+ isMandatory ? " *" : ""
772
+ ] }),
773
+ fieldConfig.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: fieldConfig.description }) : null,
774
+ /* @__PURE__ */ jsxRuntime.jsx(CoordinateMapPicker, { value, onChange: field.onChange, disabled: isDisabled }),
775
+ /* @__PURE__ */ jsxRuntime.jsx(
776
+ ui.InputField,
777
+ {
778
+ name: `${field.name}-lng`,
779
+ type: "number",
780
+ label: "Longitude",
781
+ value: lngText,
782
+ disabled: isDisabled,
783
+ onChange: ({ value: next }) => {
784
+ const text = next ?? "";
785
+ setLngText(text);
786
+ commit(text, latText);
787
+ },
788
+ onBlur: field.onBlur
789
+ }
790
+ ),
791
+ /* @__PURE__ */ jsxRuntime.jsx(
792
+ ui.InputField,
793
+ {
794
+ name: `${field.name}-lat`,
795
+ type: "number",
796
+ label: "Latitude",
797
+ value: latText,
798
+ disabled: isDisabled,
799
+ onChange: ({ value: next }) => {
800
+ const text = next ?? "";
801
+ setLatText(text);
802
+ commit(lngText, text);
803
+ },
804
+ onBlur: field.onBlur
805
+ }
806
+ ),
807
+ validationText ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-error": hasError || void 0, "data-warning": hasWarning || void 0, children: validationText }) : null
808
+ ] });
809
+ }
810
+ function D2GeoJsonField({ control }) {
811
+ const { fieldConfig, field, isMandatory, isDisabled } = control;
812
+ const { validationText, hasError, hasWarning } = dhis2FormUtilsHooks.resolveFieldValidation(control);
813
+ const value = field.value;
814
+ const [textValue, setTextValue] = react.useState(value);
815
+ const [textError, setTextError] = react.useState(void 0);
816
+ return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
817
+ /* @__PURE__ */ jsxRuntime.jsxs("legend", { children: [
818
+ fieldConfig.label,
819
+ isMandatory ? " *" : ""
820
+ ] }),
821
+ fieldConfig.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: fieldConfig.description }) : null,
822
+ /* @__PURE__ */ jsxRuntime.jsx(
823
+ GeoJsonMapEditor,
824
+ {
825
+ value,
826
+ onChange: (next) => {
827
+ field.onChange(next);
828
+ const geometry = parseGeojsonGeometry(next);
829
+ setTextValue(geometry ? JSON.stringify(geometry, null, 2) : next);
830
+ setTextError(void 0);
831
+ },
832
+ disabled: isDisabled
833
+ }
834
+ ),
835
+ /* @__PURE__ */ jsxRuntime.jsx(
836
+ ui.TextAreaField,
837
+ {
838
+ name: `${field.name}-geometry`,
839
+ label: "Geometry (JSON)",
840
+ value: textValue,
841
+ disabled: isDisabled,
842
+ warning: Boolean(textError),
843
+ validationText: textError,
844
+ onChange: ({ value: next }) => {
845
+ setTextValue(next ?? "");
846
+ },
847
+ onBlur: () => {
848
+ if (!textValue) {
849
+ field.onChange("");
850
+ setTextError(void 0);
851
+ return;
852
+ }
853
+ const geometry = parseGeojsonGeometry(textValue);
854
+ if (geometry) {
855
+ field.onChange(stringifyGeojsonGeometry(geometry));
856
+ setTextError(void 0);
857
+ } else {
858
+ setTextError("Not a valid GeoJSON geometry");
859
+ }
860
+ field.onBlur();
861
+ }
862
+ }
863
+ ),
864
+ validationText ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-error": hasError || void 0, "data-warning": hasWarning || void 0, children: validationText }) : null
865
+ ] });
866
+ }
364
867
  function D2UnsupportedField({ control }) {
365
868
  const { fieldConfig, field, widgetKind, isMandatory } = control;
366
869
  const { validationText, hasError, hasWarning } = dhis2FormUtilsHooks.resolveFieldValidation(control);
@@ -399,10 +902,11 @@ var WIDGET_BY_KIND = {
399
902
  select: D2SelectField,
400
903
  multiSelect: D2MultiSelectField,
401
904
  datetime: D2DateTimeField,
402
- coordinate: D2UnsupportedField,
403
- orgUnit: D2UnsupportedField,
404
- file: D2UnsupportedField,
405
- image: D2UnsupportedField,
905
+ coordinate: D2CoordinateField,
906
+ geojson: D2GeoJsonField,
907
+ orgUnit: D2OrgUnitField,
908
+ file: D2FileField,
909
+ image: D2ImageField,
406
910
  unsupported: D2UnsupportedField
407
911
  };
408
912
  function D2FieldWidget({ control }) {
@@ -448,8 +952,29 @@ function FormFeedback() {
448
952
  ] });
449
953
  }
450
954
 
955
+ exports.D2AgeField = D2AgeField;
956
+ exports.D2BooleanField = D2BooleanField;
957
+ exports.D2CoordinateField = D2CoordinateField;
958
+ exports.D2DateField = D2DateField;
959
+ exports.D2DateTimeField = D2DateTimeField;
960
+ exports.D2EmailField = D2EmailField;
451
961
  exports.D2Field = D2Field;
452
962
  exports.D2FieldWidget = D2FieldWidget;
963
+ exports.D2FileField = D2FileField;
964
+ exports.D2GeoJsonField = D2GeoJsonField;
965
+ exports.D2ImageField = D2ImageField;
966
+ exports.D2IntegerField = D2IntegerField;
967
+ exports.D2LongTextField = D2LongTextField;
968
+ exports.D2MultiSelectField = D2MultiSelectField;
969
+ exports.D2NumberField = D2NumberField;
970
+ exports.D2OrgUnitField = D2OrgUnitField;
971
+ exports.D2PercentageField = D2PercentageField;
972
+ exports.D2PhoneField = D2PhoneField;
973
+ exports.D2SelectField = D2SelectField;
974
+ exports.D2TextField = D2TextField;
975
+ exports.D2TimeField = D2TimeField;
976
+ exports.D2TrueOnlyField = D2TrueOnlyField;
977
+ exports.D2UnsupportedField = D2UnsupportedField;
453
978
  exports.FormFeedback = FormFeedback;
454
979
  exports.FormSection = FormSection;
455
980
  //# sourceMappingURL=index.cjs.map