diff --git a/ordinis-admin-ui/src/components/form/SchemaDrivenForm.tsx b/ordinis-admin-ui/src/components/form/SchemaDrivenForm.tsx
index 98abaf0..a1da121 100644
--- a/ordinis-admin-ui/src/components/form/SchemaDrivenForm.tsx
+++ b/ordinis-admin-ui/src/components/form/SchemaDrivenForm.tsx
@@ -3,6 +3,7 @@ import { useForm, Controller, type SubmitHandler } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import Ajv, { type ErrorObject } from 'ajv'
import addFormats from 'ajv-formats'
+import { CaretDownIcon, CircleNotchIcon } from '@phosphor-icons/react'
import {
Alert,
Badge,
@@ -747,13 +748,10 @@ const ReferenceSelectField = ({
// scroll и type-ahead search работают OOTB. SingleSelect popup
// через createPortal с position:fixed не делает auto-flip и уходит
// за нижний край когда триггер близко к низу modal'а.
- // Caret chevron как inline SVG в data-uri — match'ит SingleSelect
- // CaretDown иконку (color carbon/40, weight 'regular', size ~16).
- // appearance-none убирает OS default стрелку, иначе на macOS Safari
- // рендерится двойной chevron + native blue border.
- const caretSvg =
- "url(\"data:image/svg+xml;utf8,\")"
-
+ //
+ // Right adornment: spinner если идёт fetch options, иначе caret.
+ // pointer-events-none — иконка не блокирует click по нативному
+ // select (он принимает все клики, включая поверх стрелки).
return (
(
{label}
-
+
+
+
+ {isLoading ? (
+
+ ) : (
+
+ )}
+
+
{hint}{errorMsg}
diff --git a/ordinis-admin-ui/src/components/geo/AoiPickerDialog.tsx b/ordinis-admin-ui/src/components/geo/AoiPickerDialog.tsx
index d78a2d7..d20fb2f 100644
--- a/ordinis-admin-ui/src/components/geo/AoiPickerDialog.tsx
+++ b/ordinis-admin-ui/src/components/geo/AoiPickerDialog.tsx
@@ -115,11 +115,25 @@ export const AoiPickerDialog = ({ isOpen, onClose, onApply, initial }: Props) =>
mapRef.current = map
- // Forces map to recompute size: modal animations cause containerSize=0
- // первой кадр. После 100ms layout settled.
- const t = setTimeout(() => map.invalidateSize(), 100)
+ // Modal animation + flex layout = mapDiv может стартовать с 0×0 на
+ // первом кадре. Leaflet вычисляет tile grid от containerSize в момент
+ // create() — если 0, тайлы не запрашиваются и карта остаётся пустой.
+ //
+ // Решение: ResizeObserver на контейнер, invalidateSize при каждом
+ // resize. Это надёжно покрывает modal fade-in, dev tools toggle,
+ // window resize. Дополнительно invalidateSize на следующем
+ // animation frame — для случая когда RO не fire'ит (initial size
+ // уже окончательный).
+ const ro = new ResizeObserver(() => {
+ if (mapRef.current) mapRef.current.invalidateSize()
+ })
+ ro.observe(div)
+ const raf = requestAnimationFrame(() => {
+ if (mapRef.current) mapRef.current.invalidateSize()
+ })
return () => {
- clearTimeout(t)
+ ro.disconnect()
+ cancelAnimationFrame(raf)
}
}, [isOpen, initial])
@@ -162,7 +176,11 @@ export const AoiPickerDialog = ({ isOpen, onClose, onApply, initial }: Props) =>