@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 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,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: "&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
+ 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: D2UnsupportedField,
403
- orgUnit: D2UnsupportedField,
404
- file: D2UnsupportedField,
405
- image: D2UnsupportedField,
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