function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/*
 * ui-components.jsx — базовый UI-kit дашборда: Checkbox, Select, DatePicker.
 *
 * Зачем: до этого каждый таб рисовал свои контролы вручную — нативные
 * <select> / <input type="date"> / <input type="checkbox"> с инлайновыми
 * стилями, которые разъезжались от файла к файлу. Нативный выпадающий
 * список и календарь Chrome рисует ОС — они игнорируют тему дашборда и
 * ломаются на тёмном фоне. Поэтому здесь кастомные поповеры.
 *
 * Экспорт: window.UI = { Checkbox, Select, DatePicker, Skeleton, TableSkeleton }.
 *
 * Загружается как <script type="text/babel" src="/ui-components.jsx"> ДО
 * остальных табов. Хуки берутся через префиксные алиасы (uiUseState и т.д.),
 * т.к. babel-standalone складывает все скрипты в общую область видимости и
 * голый `const { useState } = React` в двух файлах даёт редекларацию.
 *
 * Использует глобалы: React, ReactDOM.
 *
 * ── Контракт значений ─────────────────────────────────────────────────
 * Select.onChange(value) отдаёт value ровно того типа, что лежит в options.
 * Нативный <select> ВСЕГДА отдавал строку, поэтому при миграции call-site'ов
 * числовые id заворачиваются в String(...) — обработчики остаются нетронутыми.
 * DatePicker работает со строкой "YYYY-MM-DD" ("" = пусто) — тот же формат,
 * что отдавал <input type="date">.
 */

var uiUseState = React.useState;
var uiUseEffect = React.useEffect;
var uiUseLayoutEffect = React.useLayoutEffect;
var uiUseRef = React.useRef;
var uiUseMemo = React.useMemo;
var uiUseCallback = React.useCallback;

/* ── Размеры контролов ────────────────────────────────────────────────
   Соответствуют трём высотам, что реально встречались в проекте:
   ячейки таблиц (4px 8px / 12px), фильтр-бары (7px 12px / 13px),
   поля модалок (8px 12px / 13px). */
var UI_SIZES = {
  sm: {
    h: 26,
    fs: 12,
    px: 8,
    r: "var(--r-sm)"
  },
  md: {
    h: 32,
    fs: 13,
    px: 10,
    r: "var(--r-sm)"
  },
  lg: {
    h: 34,
    fs: 13,
    px: 12,
    r: "var(--r-md)"
  }
};

/* ══════════════════════════════════════════════════════════════════════
   Поповер: позиционирование
   ══════════════════════════════════════════════════════════════════════
   Поповеры рендерятся порталом в <body>, position:fixed. Иначе их режет
   overflow:auto у обёрток таблиц — а селекты живут прямо в строках. */
function uiPlacePopover(anchor, pop, _ref) {
  var matchWidth = _ref.matchWidth,
    minWidth = _ref.minWidth,
    align = _ref.align,
    gap = _ref.gap;
  var r = anchor.getBoundingClientRect();
  var vw = window.innerWidth;
  var vh = window.innerHeight;
  var ph = pop ? pop.offsetHeight : 280;
  var pw = matchWidth ? r.width : Math.max(minWidth || 0, pop ? pop.offsetWidth : r.width);
  var spaceBelow = vh - r.bottom - gap;
  var spaceAbove = r.top - gap;
  // Разворачиваем вверх, только если снизу реально не помещается И сверху лучше.
  var flip = spaceBelow < Math.min(ph, 200) && spaceAbove > spaceBelow;
  var top = flip ? Math.max(8, r.top - gap - ph) : r.bottom + gap;
  var maxHeight = Math.max(140, (flip ? spaceAbove : spaceBelow) - 8);
  var left = align === "right" ? r.right - pw : r.left;
  left = Math.min(Math.max(8, left), Math.max(8, vw - pw - 8));
  return _objectSpread({
    position: "fixed",
    top: Math.round(top),
    left: Math.round(left),
    maxHeight: Math.round(maxHeight)
  }, matchWidth ? {
    width: Math.round(r.width)
  } : {
    minWidth: Math.round(Math.max(minWidth || 0, r.width))
  });
}

/*
 * Открытый поповер, привязанный к якорю. Возвращает готовый style.
 * Позиция считается в useLayoutEffect (до отрисовки), поэтому первый кадр
 * уже корректный: пока pos === null, поповер спрятан за экран.
 */
function useUiPopover(open, anchorRef, popRef, opts) {
  var _ref2 = opts || {},
    _ref2$matchWidth = _ref2.matchWidth,
    matchWidth = _ref2$matchWidth === void 0 ? false : _ref2$matchWidth,
    _ref2$minWidth = _ref2.minWidth,
    minWidth = _ref2$minWidth === void 0 ? 0 : _ref2$minWidth,
    _ref2$align = _ref2.align,
    align = _ref2$align === void 0 ? "left" : _ref2$align,
    _ref2$gap = _ref2.gap,
    gap = _ref2$gap === void 0 ? 4 : _ref2$gap;
  var _uiUseState = uiUseState(null),
    _uiUseState2 = _slicedToArray(_uiUseState, 2),
    pos = _uiUseState2[0],
    setPos = _uiUseState2[1];
  uiUseLayoutEffect(function () {
    if (!open) {
      setPos(null);
      return;
    }
    var place = function place() {
      if (!anchorRef.current) return;
      setPos(uiPlacePopover(anchorRef.current, popRef.current, {
        matchWidth: matchWidth,
        minWidth: minWidth,
        align: align,
        gap: gap
      }));
    };
    place();
    // capture:true — ловим скролл любого внутреннего контейнера, не только окна.
    window.addEventListener("scroll", place, true);
    window.addEventListener("resize", place);
    return function () {
      window.removeEventListener("scroll", place, true);
      window.removeEventListener("resize", place);
    };
  }, [open, matchWidth, minWidth, align, gap]);
  return pos || {
    position: "fixed",
    top: -9999,
    left: -9999,
    visibility: "hidden"
  };
}

/* Закрытие по клику вне якоря/поповера и по Escape. */
function useUiDismiss(open, close, refs) {
  uiUseEffect(function () {
    if (!open) return;
    var inside = function inside(t) {
      return refs.some(function (r) {
        return r.current && r.current.contains(t);
      });
    };
    var onDown = function onDown(e) {
      if (!inside(e.target)) close();
    };
    var onKey = function onKey(e) {
      if (e.key === "Escape") {
        e.stopPropagation();
        close();
      }
    };
    document.addEventListener("mousedown", onDown, true);
    document.addEventListener("keydown", onKey, true);
    return function () {
      document.removeEventListener("mousedown", onDown, true);
      document.removeEventListener("keydown", onKey, true);
    };
  }, [open, close]);
}
var UiPortal = function UiPortal(_ref3) {
  var children = _ref3.children;
  return ReactDOM.createPortal(children, document.body);
};

/*
 * События из портала всплывают по REACT-дереву, а не по DOM. Поповер лежит
 * в <body>, но его клики доходят до React-предков триггера — то есть до
 * бэкдропа модалки с onClick={onClose}. Без этой заглушки выбор опции в
 * селекте внутри модалки закрывал бы саму модалку.
 *
 * На закрытие по клику снаружи не влияет: useUiDismiss слушает document
 * нативными слушателями в фазе capture, а синтетический stopPropagation
 * их не трогает.
 */
var uiStopBubble = {
  onClick: function onClick(e) {
    return e.stopPropagation();
  },
  onMouseDown: function onMouseDown(e) {
    return e.stopPropagation();
  }
};

/* ══════════════════════════════════════════════════════════════════════
   Иконки
   ══════════════════════════════════════════════════════════════════════ */
var UiChevron = function UiChevron(_ref4) {
  var _ref4$size = _ref4.size,
    size = _ref4$size === void 0 ? 12 : _ref4$size;
  return /*#__PURE__*/React.createElement("svg", {
    className: "ui-chevron",
    width: size,
    height: size,
    viewBox: "0 0 16 16",
    fill: "none",
    "aria-hidden": "true"
  }, /*#__PURE__*/React.createElement("path", {
    d: "M4 6l4 4 4-4",
    stroke: "currentColor",
    strokeWidth: "1.6",
    strokeLinecap: "round",
    strokeLinejoin: "round"
  }));
};
var UiCalendarIcon = function UiCalendarIcon(_ref5) {
  var _ref5$size = _ref5.size,
    size = _ref5$size === void 0 ? 13 : _ref5$size;
  return /*#__PURE__*/React.createElement("svg", {
    width: size,
    height: size,
    viewBox: "0 0 16 16",
    fill: "none",
    "aria-hidden": "true"
  }, /*#__PURE__*/React.createElement("rect", {
    x: "2.25",
    y: "3.25",
    width: "11.5",
    height: "10.5",
    rx: "1.75",
    stroke: "currentColor",
    strokeWidth: "1.3"
  }), /*#__PURE__*/React.createElement("path", {
    d: "M2.5 6.5h11M5.5 2v2.5M10.5 2v2.5",
    stroke: "currentColor",
    strokeWidth: "1.3",
    strokeLinecap: "round"
  }));
};
var UiClearIcon = function UiClearIcon(_ref6) {
  var _ref6$size = _ref6.size,
    size = _ref6$size === void 0 ? 12 : _ref6$size;
  return /*#__PURE__*/React.createElement("svg", {
    width: size,
    height: size,
    viewBox: "0 0 16 16",
    fill: "none",
    "aria-hidden": "true"
  }, /*#__PURE__*/React.createElement("path", {
    d: "M4.5 4.5l7 7M11.5 4.5l-7 7",
    stroke: "currentColor",
    strokeWidth: "1.6",
    strokeLinecap: "round"
  }));
};

/* ══════════════════════════════════════════════════════════════════════
   Checkbox
   ══════════════════════════════════════════════════════════════════════
   <UI.Checkbox checked={bool} onChange={next => ...} label="..." />

   onChange отдаёт УЖЕ НОВОЕ булево значение, а не событие — это единственное
   отличие от нативного инпута, и оно снимает вечное e.target.checked.
   Нативный input остаётся под капотом (скрыт визуально, не display:none),
   поэтому работают Tab, Space и скринридеры. */
function Checkbox(_ref7) {
  var _ref7$checked = _ref7.checked,
    checked = _ref7$checked === void 0 ? false : _ref7$checked,
    _onChange = _ref7.onChange,
    label = _ref7.label,
    _ref7$indeterminate = _ref7.indeterminate,
    indeterminate = _ref7$indeterminate === void 0 ? false : _ref7$indeterminate,
    _ref7$disabled = _ref7.disabled,
    disabled = _ref7$disabled === void 0 ? false : _ref7$disabled,
    _ref7$size = _ref7.size,
    size = _ref7$size === void 0 ? "md" : _ref7$size,
    title = _ref7.title,
    name = _ref7.name,
    value = _ref7.value,
    style = _ref7.style,
    _ref7$className = _ref7.className,
    className = _ref7$className === void 0 ? "" : _ref7$className,
    labelStyle = _ref7.labelStyle,
    onClick = _ref7.onClick,
    _ref7$stopPropagation = _ref7.stopPropagation,
    stopPropagation = _ref7$stopPropagation === void 0 ? false : _ref7$stopPropagation,
    onMouseEnter = _ref7.onMouseEnter,
    onMouseLeave = _ref7.onMouseLeave;
  var ref = uiUseRef(null);
  var box = size === "sm" ? 14 : 16;

  // indeterminate выставляется только через DOM-свойство — атрибута нет.
  uiUseEffect(function () {
    if (ref.current) ref.current.indeterminate = !!indeterminate && !checked;
  }, [indeterminate, checked]);
  var handleClick = function handleClick(e) {
    if (stopPropagation) e.stopPropagation();
    if (onClick) onClick(e);
  };
  return /*#__PURE__*/React.createElement("label", {
    className: "ui-cb".concat(disabled ? " is-disabled" : "", " ").concat(className).trim(),
    title: title,
    style: style,
    onClick: handleClick
    /* Пробрасываются ради строк-списков, где вся строка — это чекбокс и
       подсветка ховера живёт на том же элементе (см. MultiTagPicker в kpi.js). */,
    onMouseEnter: onMouseEnter,
    onMouseLeave: onMouseLeave
  }, /*#__PURE__*/React.createElement("input", {
    ref: ref,
    type: "checkbox",
    className: "ui-cb-input",
    checked: !!checked,
    disabled: disabled,
    name: name,
    value: value,
    onChange: function onChange(e) {
      return _onChange && _onChange(e.target.checked, e);
    }
  }), /*#__PURE__*/React.createElement("span", {
    className: "ui-cb-box",
    style: {
      width: box,
      height: box
    },
    "aria-hidden": "true"
  }, /*#__PURE__*/React.createElement("svg", {
    viewBox: "0 0 16 16",
    fill: "none",
    className: "ui-cb-check"
  }, /*#__PURE__*/React.createElement("path", {
    d: "M3.5 8.5l3 3 6-6.5",
    stroke: "currentColor",
    strokeWidth: "2",
    strokeLinecap: "round",
    strokeLinejoin: "round"
  })), /*#__PURE__*/React.createElement("span", {
    className: "ui-cb-dash"
  })), label != null && label !== "" && /*#__PURE__*/React.createElement("span", {
    className: "ui-cb-label",
    style: _objectSpread({
      fontSize: UI_SIZES[size].fs
    }, labelStyle)
  }, label));
}

/* ══════════════════════════════════════════════════════════════════════
   Select
   ══════════════════════════════════════════════════════════════════════
   <UI.Select value={v} onChange={val => ...} options={[...]} />

   options: массив строк/чисел ИЛИ объектов { value, label, color?, hint?, disabled? }.
   onChange(value, option) — value ровно из options (см. «Контракт значений»).
   Сопоставление value с options нестрогое (String === String): числовой id
   в options и строковый в state — рабочая ситуация в этом проекте. */
function uiNormalizeOptions(options) {
  return (options || []).map(function (o) {
    return o != null && _typeof(o) === "object" ? o : {
      value: o,
      label: String(o)
    };
  });
}

/*
 * Режим multiple (добавлен 2026-07-21 под панель фильтров Partners).
 *
 * Включается новым необязательным пропом, поведение по умолчанию не меняется —
 * это принципиально: компонент используется в 64 местах, и любое изменение
 * дефолта задело бы их все.
 *
 * В режиме multiple:
 *   value    — массив значений вместо скаляра;
 *   onChange — получает НОВЫЙ массив (и опцию, по которой кликнули);
 *   клик по опции переключает её и НЕ закрывает поповер — иначе выбрать
 *     несколько было бы невозможно; закрытие остаётся по Escape, клику вне
 *     и Tab, как в одиночном режиме;
 *   renderTrigger — необязательный форматтер подписи; без него собирается
 *     «A, B +N», потому что перечислять все выбранные в узкой кнопке нельзя.
 */
function Select(_ref8) {
  var value = _ref8.value,
    onChange = _ref8.onChange,
    options = _ref8.options,
    _ref8$placeholder = _ref8.placeholder,
    placeholder = _ref8$placeholder === void 0 ? "—" : _ref8$placeholder,
    searchable = _ref8.searchable,
    _ref8$clearable = _ref8.clearable,
    clearable = _ref8$clearable === void 0 ? false : _ref8$clearable,
    _ref8$disabled = _ref8.disabled,
    disabled = _ref8$disabled === void 0 ? false : _ref8$disabled,
    _ref8$size = _ref8.size,
    size = _ref8$size === void 0 ? "md" : _ref8$size,
    width = _ref8.width,
    minWidth = _ref8.minWidth,
    style = _ref8.style,
    _ref8$className = _ref8.className,
    className = _ref8$className === void 0 ? "" : _ref8$className,
    title = _ref8.title,
    _ref8$align = _ref8.align,
    align = _ref8$align === void 0 ? "left" : _ref8$align,
    onClick = _ref8.onClick,
    _ref8$stopPropagation = _ref8.stopPropagation,
    stopPropagation = _ref8$stopPropagation === void 0 ? false : _ref8$stopPropagation,
    id = _ref8.id,
    ariaLabel = _ref8.ariaLabel,
    _ref8$multiple = _ref8.multiple,
    multiple = _ref8$multiple === void 0 ? false : _ref8$multiple,
    renderTrigger = _ref8.renderTrigger,
    _ref8$maxTriggerItems = _ref8.maxTriggerItems,
    maxTriggerItems = _ref8$maxTriggerItems === void 0 ? 2 : _ref8$maxTriggerItems;
  var opts = uiUseMemo(function () {
    return uiNormalizeOptions(options);
  }, [options]);
  var _uiUseState3 = uiUseState(false),
    _uiUseState4 = _slicedToArray(_uiUseState3, 2),
    open = _uiUseState4[0],
    setOpen = _uiUseState4[1];
  var _uiUseState5 = uiUseState(""),
    _uiUseState6 = _slicedToArray(_uiUseState5, 2),
    query = _uiUseState6[0],
    setQuery = _uiUseState6[1];
  var _uiUseState7 = uiUseState(-1),
    _uiUseState8 = _slicedToArray(_uiUseState7, 2),
    active = _uiUseState8[0],
    setActive = _uiUseState8[1];
  var anchorRef = uiUseRef(null);
  var popRef = uiUseRef(null);
  var listRef = uiUseRef(null);
  var sz = UI_SIZES[size] || UI_SIZES.md;
  // Поиск включается сам, когда список перестаёт читаться глазами.
  var withSearch = searchable != null ? searchable : opts.length > 10;

  // Сопоставление нестрогое (String === String) в обоих режимах: в проекте
  // сплошь и рядом числовой id в options против строкового в state.
  var valueKeys = uiUseMemo(function () {
    if (!multiple) return null;
    var arr = Array.isArray(value) ? value : value == null || value === "" ? [] : [value];
    return new Set(arr.map(function (v) {
      return String(v);
    }));
  }, [multiple, value]);
  var selectedList = uiUseMemo(function () {
    return multiple ? opts.filter(function (o) {
      return valueKeys.has(String(o.value));
    }) : [];
  }, [multiple, opts, valueKeys]);
  var selected = uiUseMemo(function () {
    return multiple ? selectedList[0] || null : opts.find(function (o) {
      return String(o.value) === String(value == null ? "" : value);
    }) || null;
  }, [multiple, selectedList, opts, value]);

  // Пусто ли поле — от этого зависит класс is-placeholder и показ крестика.
  var isEmpty = multiple ? selectedList.length === 0 : !selected;

  // Подпись на кнопке. В multiple перечислять всё нельзя — кнопка узкая.
  var triggerLabel = uiUseMemo(function () {
    // renderTrigger работает в обоих режимах: чипам фильтров нужна подпись
    // вида «Source: All», где префикс виден на кнопке, но не в списке опций.
    if (!multiple) {
      if (!selected) return placeholder;
      return renderTrigger ? renderTrigger(selected) : selected.label;
    }
    if (selectedList.length === 0) return placeholder;
    if (renderTrigger) return renderTrigger(selectedList);
    var head = selectedList.slice(0, maxTriggerItems).map(function (o) {
      return o.label;
    }).join(", ");
    var rest = selectedList.length - maxTriggerItems;
    return rest > 0 ? "".concat(head, " +").concat(rest) : head;
  }, [multiple, selected, selectedList, placeholder, renderTrigger, maxTriggerItems]);
  var shown = uiUseMemo(function () {
    if (!query.trim()) return opts;
    var q = query.trim().toLowerCase();
    return opts.filter(function (o) {
      return String(o.label).toLowerCase().includes(q);
    });
  }, [opts, query]);
  var close = uiUseCallback(function () {
    setOpen(false);
    setQuery("");
    setActive(-1);
  }, []);
  useUiDismiss(open, close, [anchorRef, popRef]);
  var popStyle = useUiPopover(open, anchorRef, popRef, {
    minWidth: minWidth || 160,
    align: align,
    gap: 4
  });

  // При открытии подсвечиваем текущий выбор, чтобы стрелки шли от него.
  // uiUseLayoutEffect (не uiUseEffect) — важна синхронность: обычный passive
  // эффект коммитится отдельным тиком через MessageChannel-scheduler, и очень
  // быстрый ArrowDown сразу за Enter (программная навигация или быстрый ввод)
  // успевал отработать раньше, застав active ещё на -1 от предыдущего close(),
  // из-за чего подсвечивалась опция 0 вместо шага от текущего выбора.
  // Layout-эффект коммитится синхронно в той же фазе, что и рендер открытия.
  uiUseLayoutEffect(function () {
    if (!open) return;
    var i = shown.findIndex(function (o) {
      return selected && String(o.value) === String(selected.value);
    });
    setActive(i);
  }, [open]);

  // Держим подсвеченный пункт в зоне видимости при навигации стрелками.
  uiUseEffect(function () {
    if (!open || active < 0 || !listRef.current) return;
    var el = listRef.current.querySelector("[data-idx=\"".concat(active, "\"]"));
    if (el && el.scrollIntoView) el.scrollIntoView({
      block: "nearest"
    });
  }, [active, open]);
  var pick = function pick(o) {
    if (o.disabled) return;
    if (multiple) {
      // Поповер НЕ закрываем — иначе выбрать второй пункт было бы нельзя.
      // Порядок сохраняем по options, а не по кликам: так подпись на кнопке
      // не «прыгает» при снятии и повторной установке галочки.
      var next = opts.filter(function (x) {
        return String(x.value) === String(o.value) ? !valueKeys.has(String(x.value)) : valueKeys.has(String(x.value));
      }).map(function (x) {
        return x.value;
      });
      if (onChange) onChange(next, o);
      return;
    }
    close();
    if (onChange) onChange(o.value, o);
  };
  var step = function step(dir) {
    if (!shown.length) return;
    var i = active;
    for (var n = 0; n < shown.length; n++) {
      i = (i + dir + shown.length) % shown.length;
      if (!shown[i].disabled) break;
    }
    setActive(i);
  };
  var onKeyDown = function onKeyDown(e) {
    if (disabled) return;
    if (!open) {
      if (["Enter", " ", "ArrowDown", "ArrowUp"].includes(e.key)) {
        e.preventDefault();
        setOpen(true);
      }
      return;
    }
    if (e.key === "ArrowDown") {
      e.preventDefault();
      step(1);
    } else if (e.key === "ArrowUp") {
      e.preventDefault();
      step(-1);
    } else if (e.key === "Home") {
      e.preventDefault();
      setActive(shown.findIndex(function (o) {
        return !o.disabled;
      }));
    } else if (e.key === "End") {
      e.preventDefault();
      for (var i = shown.length - 1; i >= 0; i--) if (!shown[i].disabled) {
        setActive(i);
        break;
      }
    } else if (e.key === "Enter") {
      e.preventDefault();
      if (shown[active]) pick(shown[active]);
    } else if (e.key === "Tab") {
      close();
    }
  };
  var handleClick = function handleClick(e) {
    if (stopPropagation) e.stopPropagation();
    if (onClick) onClick(e);
    if (!disabled) setOpen(function (o) {
      return !o;
    });
  };
  var triggerStyle = _objectSpread(_objectSpread(_objectSpread({
    height: sz.h,
    fontSize: sz.fs,
    padding: "0 ".concat(sz.px, "px"),
    borderRadius: sz.r
  }, width != null ? {
    width: width
  } : null), minWidth != null ? {
    minWidth: minWidth
  } : null), style);
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("button", {
    type: "button",
    id: id,
    ref: anchorRef,
    className: "ui-ctl ui-sel".concat(open ? " is-open" : "").concat(isEmpty ? " is-placeholder" : "", " ").concat(className).trim(),
    style: triggerStyle,
    disabled: disabled,
    title: title,
    "aria-label": ariaLabel,
    "aria-haspopup": "listbox",
    "aria-expanded": open,
    onClick: handleClick,
    onKeyDown: onKeyDown
  }, /*#__PURE__*/React.createElement("span", {
    className: "ui-sel-value",
    style: !multiple && selected && selected.color ? {
      color: selected.color
    } : null
  }, triggerLabel), clearable && !isEmpty && !disabled && (multiple || String(selected.value) !== "") && /*#__PURE__*/React.createElement("span", {
    className: "ui-sel-clear",
    role: "button",
    "aria-label": "Clear",
    onClick: function onClick(e) {
      e.stopPropagation();
      if (onChange) onChange(multiple ? [] : "", null);
    }
  }, /*#__PURE__*/React.createElement(UiClearIcon, null)), /*#__PURE__*/React.createElement(UiChevron, null)), open && /*#__PURE__*/React.createElement(UiPortal, null, /*#__PURE__*/React.createElement("div", _objectSpread({
    ref: popRef,
    className: "ui-pop ui-sel-pop",
    style: popStyle,
    role: "listbox",
    "aria-multiselectable": multiple || undefined,
    onKeyDown: onKeyDown
  }, uiStopBubble), withSearch && /*#__PURE__*/React.createElement("div", {
    className: "ui-pop-search"
  }, /*#__PURE__*/React.createElement("input", {
    className: "ui-pop-search-input",
    autoFocus: true,
    value: query,
    placeholder: "Search\u2026",
    onChange: function onChange(e) {
      setQuery(e.target.value);
      setActive(0);
    },
    onKeyDown: onKeyDown
  })), /*#__PURE__*/React.createElement("div", {
    className: "ui-pop-list",
    ref: listRef
  }, shown.length === 0 && /*#__PURE__*/React.createElement("div", {
    className: "ui-pop-empty"
  }, "Nothing found"), shown.map(function (o, i) {
    var isSel = multiple ? valueKeys.has(String(o.value)) : selected && String(o.value) === String(selected.value);
    return /*#__PURE__*/React.createElement("div", {
      key: "".concat(o.value, "-").concat(i),
      "data-idx": i,
      role: "option",
      "aria-selected": !!isSel,
      className: "ui-opt".concat(isSel ? " is-selected" : "").concat(i === active ? " is-active" : "").concat(o.disabled ? " is-disabled" : ""),
      style: o.color ? {
        color: o.color
      } : null,
      onMouseEnter: function onMouseEnter() {
        return setActive(i);
      },
      onMouseDown: function onMouseDown(e) {
        return e.preventDefault();
      } /* не терять фокус до клика */,
      onClick: function onClick() {
        return pick(o);
      }
    }, /*#__PURE__*/React.createElement("span", {
      className: "ui-opt-label"
    }, o.label), o.hint != null && /*#__PURE__*/React.createElement("span", {
      className: "ui-opt-hint"
    }, o.hint), isSel && /*#__PURE__*/React.createElement("svg", {
      className: "ui-opt-check",
      viewBox: "0 0 16 16",
      fill: "none",
      "aria-hidden": "true"
    }, /*#__PURE__*/React.createElement("path", {
      d: "M3.5 8.5l3 3 6-6.5",
      stroke: "currentColor",
      strokeWidth: "1.8",
      strokeLinecap: "round",
      strokeLinejoin: "round"
    })));
  })))));
}

/* ══════════════════════════════════════════════════════════════════════
   DatePicker
   ══════════════════════════════════════════════════════════════════════
   <UI.DatePicker value="YYYY-MM-DD" onChange={str => ...} />

   Значение — строка "YYYY-MM-DD" ("" = пусто), как у <input type="date">,
   поэтому все фильтры и тела запросов остаются без изменений.
   Все вычисления в ЛОКАЛЬНОМ времени: Date#toISOString сдвинул бы дату
   на день для UTC+N после полудня. */
var UI_MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var UI_WD = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
function uiIso(d) {
  var p = function p(n) {
    return String(n).padStart(2, "0");
  };
  return "".concat(d.getFullYear(), "-").concat(p(d.getMonth() + 1), "-").concat(p(d.getDate()));
}
function uiParseIso(s) {
  if (!s || typeof s !== "string") return null;
  var m = /^(\d{4})-(\d{2})-(\d{2})/.exec(s);
  if (!m) return null;
  var d = new Date(+m[1], +m[2] - 1, +m[3]);
  return isNaN(d.getTime()) ? null : d;
}

/* Понедельник — первый день недели. */
function uiMonthGrid(year, month) {
  var first = new Date(year, month, 1);
  var lead = (first.getDay() + 6) % 7;
  var start = new Date(year, month, 1 - lead);
  return Array.from({
    length: 42
  }, function (_, i) {
    return new Date(start.getFullYear(), start.getMonth(), start.getDate() + i);
  });
}
function DatePicker(_ref9) {
  var _ref9$value = _ref9.value,
    value = _ref9$value === void 0 ? "" : _ref9$value,
    onChange = _ref9.onChange,
    _ref9$placeholder = _ref9.placeholder,
    placeholder = _ref9$placeholder === void 0 ? "—" : _ref9$placeholder,
    min = _ref9.min,
    max = _ref9.max,
    _ref9$clearable = _ref9.clearable,
    clearable = _ref9$clearable === void 0 ? true : _ref9$clearable,
    _ref9$disabled = _ref9.disabled,
    disabled = _ref9$disabled === void 0 ? false : _ref9$disabled,
    _ref9$size = _ref9.size,
    size = _ref9$size === void 0 ? "md" : _ref9$size,
    width = _ref9.width,
    style = _ref9.style,
    _ref9$className = _ref9.className,
    className = _ref9$className === void 0 ? "" : _ref9$className,
    title = _ref9.title,
    _ref9$align = _ref9.align,
    align = _ref9$align === void 0 ? "left" : _ref9$align,
    onClick = _ref9.onClick,
    _ref9$stopPropagation = _ref9.stopPropagation,
    stopPropagation = _ref9$stopPropagation === void 0 ? false : _ref9$stopPropagation,
    id = _ref9.id,
    ariaLabel = _ref9.ariaLabel;
  var _uiUseState9 = uiUseState(false),
    _uiUseState0 = _slicedToArray(_uiUseState9, 2),
    open = _uiUseState0[0],
    setOpen = _uiUseState0[1];
  var anchorRef = uiUseRef(null);
  var popRef = uiUseRef(null);
  var sz = UI_SIZES[size] || UI_SIZES.md;
  var selected = uiUseMemo(function () {
    return uiParseIso(value);
  }, [value]);
  var today = uiUseMemo(function () {
    var d = new Date();
    return new Date(d.getFullYear(), d.getMonth(), d.getDate());
  }, []);

  // Месяц, показанный в календаре: от выбранной даты, иначе от сегодня.
  var _uiUseState1 = uiUseState(function () {
      var b = selected || today;
      return {
        y: b.getFullYear(),
        m: b.getMonth()
      };
    }),
    _uiUseState10 = _slicedToArray(_uiUseState1, 2),
    view = _uiUseState10[0],
    setView = _uiUseState10[1];

  // Открыли — вернулись на месяц текущего значения (а не туда, где листали прошлый раз).
  uiUseEffect(function () {
    if (!open) return;
    var b = uiParseIso(value) || today;
    setView({
      y: b.getFullYear(),
      m: b.getMonth()
    });
  }, [open]);
  var close = uiUseCallback(function () {
    return setOpen(false);
  }, []);
  useUiDismiss(open, close, [anchorRef, popRef]);
  var popStyle = useUiPopover(open, anchorRef, popRef, {
    minWidth: 248,
    align: align,
    gap: 4
  });
  var minD = uiUseMemo(function () {
    return uiParseIso(min);
  }, [min]);
  var maxD = uiUseMemo(function () {
    return uiParseIso(max);
  }, [max]);
  var outOfRange = function outOfRange(d) {
    return minD && d < minD || maxD && d > maxD;
  };
  var days = uiUseMemo(function () {
    return uiMonthGrid(view.y, view.m);
  }, [view.y, view.m]);
  var shift = function shift(dm, dy) {
    return setView(function (v) {
      var d = new Date(v.y + (dy || 0), v.m + (dm || 0), 1);
      return {
        y: d.getFullYear(),
        m: d.getMonth()
      };
    });
  };
  var pick = function pick(d) {
    if (outOfRange(d)) return;
    close();
    if (onChange) onChange(uiIso(d));
  };
  var handleClick = function handleClick(e) {
    if (stopPropagation) e.stopPropagation();
    if (onClick) onClick(e);
    if (!disabled) setOpen(function (o) {
      return !o;
    });
  };
  var onKeyDown = function onKeyDown(e) {
    if (disabled) return;
    if (!open) {
      if (["Enter", " ", "ArrowDown"].includes(e.key)) {
        e.preventDefault();
        setOpen(true);
      }
      return;
    }
    if (e.key === "PageUp") {
      e.preventDefault();
      shift(e.shiftKey ? 0 : -1, e.shiftKey ? -1 : 0);
    } else if (e.key === "PageDown") {
      e.preventDefault();
      shift(e.shiftKey ? 0 : 1, e.shiftKey ? 1 : 0);
    } else if (e.key === "Tab") {
      close();
    }
  };
  var triggerStyle = _objectSpread(_objectSpread({
    height: sz.h,
    fontSize: sz.fs,
    padding: "0 ".concat(sz.px, "px"),
    borderRadius: sz.r
  }, width != null ? {
    width: width
  } : null), style);
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("button", {
    type: "button",
    id: id,
    ref: anchorRef,
    className: "ui-ctl ui-date".concat(open ? " is-open" : "").concat(!selected ? " is-placeholder" : "", " ").concat(className).trim(),
    style: triggerStyle,
    disabled: disabled,
    title: title,
    "aria-label": ariaLabel,
    "aria-haspopup": "dialog",
    "aria-expanded": open,
    onClick: handleClick,
    onKeyDown: onKeyDown
  }, /*#__PURE__*/React.createElement("span", {
    className: "ui-date-icon"
  }, /*#__PURE__*/React.createElement(UiCalendarIcon, {
    size: sz.fs
  })), /*#__PURE__*/React.createElement("span", {
    className: "ui-date-value"
  }, selected ? value : placeholder), clearable && selected && !disabled && /*#__PURE__*/React.createElement("span", {
    className: "ui-sel-clear",
    role: "button",
    "aria-label": "Clear",
    onClick: function onClick(e) {
      e.stopPropagation();
      if (onChange) onChange("");
    }
  }, /*#__PURE__*/React.createElement(UiClearIcon, null))), open && /*#__PURE__*/React.createElement(UiPortal, null, /*#__PURE__*/React.createElement("div", _objectSpread({
    ref: popRef,
    className: "ui-pop ui-cal",
    style: popStyle,
    role: "dialog",
    "aria-label": "Choose date",
    onKeyDown: onKeyDown
  }, uiStopBubble), /*#__PURE__*/React.createElement("div", {
    className: "ui-cal-head"
  }, /*#__PURE__*/React.createElement("button", {
    type: "button",
    className: "ui-cal-nav",
    title: "Previous year",
    onClick: function onClick() {
      return shift(0, -1);
    }
  }, "\xAB"), /*#__PURE__*/React.createElement("button", {
    type: "button",
    className: "ui-cal-nav",
    title: "Previous month",
    onClick: function onClick() {
      return shift(-1, 0);
    }
  }, "\u2039"), /*#__PURE__*/React.createElement("div", {
    className: "ui-cal-title"
  }, UI_MONTHS[view.m], " ", view.y), /*#__PURE__*/React.createElement("button", {
    type: "button",
    className: "ui-cal-nav",
    title: "Next month",
    onClick: function onClick() {
      return shift(1, 0);
    }
  }, "\u203A"), /*#__PURE__*/React.createElement("button", {
    type: "button",
    className: "ui-cal-nav",
    title: "Next year",
    onClick: function onClick() {
      return shift(0, 1);
    }
  }, "\xBB")), /*#__PURE__*/React.createElement("div", {
    className: "ui-cal-wd"
  }, UI_WD.map(function (w) {
    return /*#__PURE__*/React.createElement("span", {
      key: w
    }, w);
  })), /*#__PURE__*/React.createElement("div", {
    className: "ui-cal-grid"
  }, days.map(function (d) {
    var iso = uiIso(d);
    var isOther = d.getMonth() !== view.m;
    var isSel = selected && iso === uiIso(selected);
    var isToday = iso === uiIso(today);
    var off = outOfRange(d);
    return /*#__PURE__*/React.createElement("button", {
      key: iso,
      type: "button",
      disabled: off,
      className: "ui-cal-day".concat(isOther ? " is-other" : "").concat(isSel ? " is-selected" : "").concat(isToday ? " is-today" : ""),
      onClick: function onClick() {
        return pick(d);
      }
    }, d.getDate());
  })), /*#__PURE__*/React.createElement("div", {
    className: "ui-cal-foot"
  }, /*#__PURE__*/React.createElement("button", {
    type: "button",
    className: "ui-cal-action",
    disabled: outOfRange(today),
    onClick: function onClick() {
      return pick(today);
    }
  }, "Today"), clearable && /*#__PURE__*/React.createElement("button", {
    type: "button",
    className: "ui-cal-action",
    onClick: function onClick() {
      close();
      if (onChange) onChange("");
    }
  }, "Clear")))));
}

/* ══════════════════════════════════════════════════════════════════════
   Skeleton / TableSkeleton
   ══════════════════════════════════════════════════════════════════════
   <UI.Skeleton width={80} height={14} />
   Базовый прямоугольник-заглушка. aria-hidden — он декоративный, реальный
   статус загрузки озвучивается отдельно (см. .ui-sr-only в TableSkeleton).

   <UI.TableSkeleton columns={COLUMNS} rows={perPage} />
   Таблица-заглушка ФИКСИРОВАННОЙ высоты, подменяющая настоящую целиком
   (тот же .table-wrap, тот же <thead> с реальными подписями колонок).
   `rows` обязан быть числом строк, которые реально отрисует таб (обычно
   текущий perPage) — а не произвольной константой вроде 3–5, иначе высота
   скелетона разойдётся с высотой настоящей таблицы, и подмена одного на
   другое даст ровно тот прыжок вёрстки, который весь этот компонент должен
   устранить. Ширины столбцов колонки берут либо из columns[i].skeletonWidth,
   либо (по умолчанию) — узкие для align:"right" (числа/деньги) и заметно
   более широкие для текстовых, по детерминированному циклу без Math.random,
   чтобы скриншот-тесты и визуальные снапшоты были воспроизводимы. */
function Skeleton(_ref0) {
  var _ref0$width = _ref0.width,
    width = _ref0$width === void 0 ? "100%" : _ref0$width,
    _ref0$height = _ref0.height,
    height = _ref0$height === void 0 ? 14 : _ref0$height,
    radius = _ref0.radius,
    style = _ref0.style,
    _ref0$className = _ref0.className,
    className = _ref0$className === void 0 ? "" : _ref0$className;
  return /*#__PURE__*/React.createElement("span", {
    "aria-hidden": "true",
    className: "ui-skel ".concat(className).trim(),
    style: _objectSpread({
      width: width,
      height: height,
      borderRadius: radius != null ? radius : "var(--r-sm)"
    }, style)
  });
}
var UI_SKEL_WIDTH_CYCLE = [88, 72, 60, 80, 66]; // %, циклически по (row+col) — стабильно между рендерами

function uiSkelColWidth(col, rowIndex, colIndex) {
  if (col.skeletonWidth != null) return col.skeletonWidth;
  if (col.align === "right") return "50%";
  return UI_SKEL_WIDTH_CYCLE[(rowIndex + colIndex) % UI_SKEL_WIDTH_CYCLE.length] + "%";
}
function TableSkeleton(_ref1) {
  var columns = _ref1.columns,
    _ref1$rows = _ref1.rows,
    rows = _ref1$rows === void 0 ? 5 : _ref1$rows,
    _ref1$className = _ref1.className,
    className = _ref1$className === void 0 ? "" : _ref1$className;
  var cols = columns || [];
  return /*#__PURE__*/React.createElement("div", {
    className: "table-wrap ".concat(className).trim(),
    "aria-busy": "true"
  }, /*#__PURE__*/React.createElement("span", {
    className: "ui-sr-only",
    role: "status"
  }, "Loading\u2026"), /*#__PURE__*/React.createElement("table", null, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, cols.map(function (c) {
    return /*#__PURE__*/React.createElement("th", {
      key: c.key,
      className: c.align === "right" ? "r" : ""
    }, c.label);
  }))), /*#__PURE__*/React.createElement("tbody", null, Array.from({
    length: rows
  }).map(function (_, i) {
    return /*#__PURE__*/React.createElement("tr", {
      key: i
    }, cols.map(function (c, ci) {
      return /*#__PURE__*/React.createElement("td", {
        key: c.key,
        className: c.align === "right" ? "r" : ""
      }, !c.skeletonHide && /*#__PURE__*/React.createElement(Skeleton, {
        width: uiSkelColWidth(c, i, ci),
        height: 13
      }));
    }));
  }))));
}
window.UI = {
  Checkbox: Checkbox,
  Select: Select,
  DatePicker: DatePicker,
  Skeleton: Skeleton,
  TableSkeleton: TableSkeleton
};