Compare commits
No commits in common. "565d5c906be47129b104c603a43d3c7cb9507d6e" and "ec03a4328338a71b9818231fb5e8e1033549e348" have entirely different histories.
565d5c906b
...
ec03a43283
1 changed files with 26 additions and 288 deletions
|
|
@ -11,16 +11,7 @@
|
||||||
// the CRM dropdown still has no backend → kept visually but disabled. Hover/active
|
// the CRM dropdown still has no backend → kept visually but disabled. Hover/active
|
||||||
// + @keyframes live in a pp-prefixed local <style>.
|
// + @keyframes live in a pp-prefixed local <style>.
|
||||||
|
|
||||||
import {
|
import { useRef, useState, type CSSProperties } from "react";
|
||||||
useEffect,
|
|
||||||
useId,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
type CSSProperties,
|
|
||||||
type Dispatch,
|
|
||||||
type KeyboardEvent as ReactKeyboardEvent,
|
|
||||||
type SetStateAction,
|
|
||||||
} from "react";
|
|
||||||
import { tokens } from "./tokens";
|
import { tokens } from "./tokens";
|
||||||
import { dropdownOptions } from "./fixtures";
|
import { dropdownOptions } from "./fixtures";
|
||||||
import {
|
import {
|
||||||
|
|
@ -56,69 +47,6 @@ interface DdProps {
|
||||||
triggerColor?: string;
|
triggerColor?: string;
|
||||||
caretColor?: string;
|
caretColor?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
ariaLabel?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shared combobox keydown logic for the HUD dropdowns (the <Dd> widget + the
|
|
||||||
// inline РАДИУС copy). aria-activedescendant pattern: focus STAYS on the trigger
|
|
||||||
// button; arrows move a virtual `activeIdx` highlight, Enter/Space commit it,
|
|
||||||
// Escape/Tab close. Navigation keys preventDefault to stop page scroll.
|
|
||||||
function comboKeyDown(
|
|
||||||
e: ReactKeyboardEvent<HTMLButtonElement>,
|
|
||||||
open: boolean,
|
|
||||||
options: string[],
|
|
||||||
activeIdx: number,
|
|
||||||
setActiveIdx: Dispatch<SetStateAction<number>>,
|
|
||||||
onToggle: () => void,
|
|
||||||
onSelect: (v: string) => void,
|
|
||||||
) {
|
|
||||||
if (!open) {
|
|
||||||
if (
|
|
||||||
e.key === "ArrowDown" ||
|
|
||||||
e.key === "ArrowUp" ||
|
|
||||||
e.key === "Enter" ||
|
|
||||||
e.key === " "
|
|
||||||
) {
|
|
||||||
e.preventDefault();
|
|
||||||
onToggle();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (e.key) {
|
|
||||||
case "ArrowDown":
|
|
||||||
e.preventDefault();
|
|
||||||
setActiveIdx((i) => Math.min(options.length - 1, i + 1));
|
|
||||||
break;
|
|
||||||
case "ArrowUp":
|
|
||||||
e.preventDefault();
|
|
||||||
setActiveIdx((i) => Math.max(0, i - 1));
|
|
||||||
break;
|
|
||||||
case "Home":
|
|
||||||
e.preventDefault();
|
|
||||||
setActiveIdx(0);
|
|
||||||
break;
|
|
||||||
case "End":
|
|
||||||
e.preventDefault();
|
|
||||||
setActiveIdx(options.length - 1);
|
|
||||||
break;
|
|
||||||
case "Enter":
|
|
||||||
case " ":
|
|
||||||
e.preventDefault();
|
|
||||||
if (activeIdx >= 0 && activeIdx < options.length) {
|
|
||||||
onSelect(options[activeIdx]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Escape":
|
|
||||||
e.preventDefault();
|
|
||||||
onToggle();
|
|
||||||
break;
|
|
||||||
case "Tab":
|
|
||||||
// do NOT preventDefault — let focus move naturally; just close the list
|
|
||||||
onToggle();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Dd({
|
function Dd({
|
||||||
|
|
@ -137,23 +65,7 @@ function Dd({
|
||||||
triggerColor = tokens.ink,
|
triggerColor = tokens.ink,
|
||||||
caretColor = tokens.muted2,
|
caretColor = tokens.muted2,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
ariaLabel,
|
|
||||||
}: DdProps) {
|
}: DdProps) {
|
||||||
const listId = useId();
|
|
||||||
const optId = (i: number) => `${listId}-opt-${i}`;
|
|
||||||
const [activeIdx, setActiveIdx] = useState(-1);
|
|
||||||
|
|
||||||
// On open, point the virtual highlight at the current selection (fallback to
|
|
||||||
// the first option); reset when closed. Pure UI sync — not a data effect.
|
|
||||||
useEffect(() => {
|
|
||||||
if (open) {
|
|
||||||
const idx = options.indexOf(value);
|
|
||||||
setActiveIdx(idx >= 0 ? idx : 0);
|
|
||||||
} else {
|
|
||||||
setActiveIdx(-1);
|
|
||||||
}
|
|
||||||
}, [open, value, options]);
|
|
||||||
|
|
||||||
const panelStyle: CSSProperties = {
|
const panelStyle: CSSProperties = {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: 0,
|
left: 0,
|
||||||
|
|
@ -176,8 +88,7 @@ function Dd({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ position: "relative" }}>
|
<div style={{ position: "relative" }}>
|
||||||
<button
|
<div
|
||||||
type="button"
|
|
||||||
className={
|
className={
|
||||||
disabled
|
disabled
|
||||||
? "pp-dd-trigger-disabled"
|
? "pp-dd-trigger-disabled"
|
||||||
|
|
@ -185,31 +96,7 @@ function Dd({
|
||||||
? "pp-dd-trigger-dashed"
|
? "pp-dd-trigger-dashed"
|
||||||
: "pp-dd-trigger"
|
: "pp-dd-trigger"
|
||||||
}
|
}
|
||||||
onClick={!disabled ? onToggle : undefined}
|
onClick={onToggle}
|
||||||
onKeyDown={
|
|
||||||
disabled
|
|
||||||
? undefined
|
|
||||||
: (e) =>
|
|
||||||
comboKeyDown(
|
|
||||||
e,
|
|
||||||
open,
|
|
||||||
options,
|
|
||||||
activeIdx,
|
|
||||||
setActiveIdx,
|
|
||||||
onToggle,
|
|
||||||
onSelect,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
role="combobox"
|
|
||||||
aria-haspopup="listbox"
|
|
||||||
aria-expanded={open}
|
|
||||||
aria-controls={listId}
|
|
||||||
aria-activedescendant={
|
|
||||||
open && activeIdx >= 0 ? optId(activeIdx) : undefined
|
|
||||||
}
|
|
||||||
aria-label={ariaLabel}
|
|
||||||
aria-disabled={disabled || undefined}
|
|
||||||
disabled={disabled}
|
|
||||||
style={{
|
style={{
|
||||||
height: triggerHeight,
|
height: triggerHeight,
|
||||||
background: triggerBg,
|
background: triggerBg,
|
||||||
|
|
@ -222,8 +109,6 @@ function Dd({
|
||||||
fontSize: triggerFontSize,
|
fontSize: triggerFontSize,
|
||||||
color: triggerColor,
|
color: triggerColor,
|
||||||
cursor: disabled ? "default" : "pointer",
|
cursor: disabled ? "default" : "pointer",
|
||||||
width: "100%",
|
|
||||||
textAlign: "left",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
|
|
@ -238,24 +123,14 @@ function Dd({
|
||||||
▼
|
▼
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</div>
|
||||||
{open && (
|
{open && (
|
||||||
<div
|
<div style={panelStyle}>
|
||||||
style={panelStyle}
|
{options.map((o) => (
|
||||||
role="listbox"
|
|
||||||
id={listId}
|
|
||||||
aria-label={ariaLabel}
|
|
||||||
tabIndex={-1}
|
|
||||||
>
|
|
||||||
{options.map((o, i) => (
|
|
||||||
<div
|
<div
|
||||||
key={o}
|
key={o}
|
||||||
className="pp-dd-opt"
|
className="pp-dd-opt"
|
||||||
role="option"
|
|
||||||
id={optId(i)}
|
|
||||||
aria-selected={o === value}
|
|
||||||
onClick={() => onSelect(o)}
|
onClick={() => onSelect(o)}
|
||||||
onMouseEnter={() => setActiveIdx(i)}
|
|
||||||
style={{
|
style={{
|
||||||
padding: "7px 10px",
|
padding: "7px 10px",
|
||||||
fontSize: optionFontSize,
|
fontSize: optionFontSize,
|
||||||
|
|
@ -264,7 +139,6 @@ function Dd({
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
color: o === value ? tokens.accent : tokens.ink,
|
color: o === value ? tokens.accent : tokens.ink,
|
||||||
fontWeight: o === value ? 600 : 400,
|
fontWeight: o === value ? 600 : 400,
|
||||||
background: i === activeIdx ? "rgba(46,139,255,.1)" : undefined,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{o}
|
{o}
|
||||||
|
|
@ -479,9 +353,6 @@ export default function ParamsPanel({
|
||||||
markers = [],
|
markers = [],
|
||||||
}: ParamsPanelProps) {
|
}: ParamsPanelProps) {
|
||||||
const [openDd, setOpenDd] = useState<DdKey>(null);
|
const [openDd, setOpenDd] = useState<DdKey>(null);
|
||||||
// РАДИУС combobox a11y state (aria-activedescendant highlight + listbox id).
|
|
||||||
const radiusListId = useId();
|
|
||||||
const [radiusActive, setRadiusActive] = useState(-1);
|
|
||||||
// Map zoom — applied as transform: scale() on the map content layer (not the
|
// Map zoom — applied as transform: scale() on the map content layer (not the
|
||||||
// whole panel). Default 1 (identity → pixel-identical), step .25, clamp 1–2.5
|
// whole panel). Default 1 (identity → pixel-identical), step .25, clamp 1–2.5
|
||||||
// (no zoom-out below 1: there is no real basemap behind the blueprint SVG, so
|
// (no zoom-out below 1: there is no real basemap behind the blueprint SVG, so
|
||||||
|
|
@ -535,9 +406,6 @@ export default function ParamsPanel({
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
// БАЛКОН radiogroup focus targets (roving tabindex).
|
|
||||||
const balNoRef = useRef<HTMLButtonElement>(null);
|
|
||||||
const balYesRef = useRef<HTMLButtonElement>(null);
|
|
||||||
const suggest = useGeocodeSuggest(addressQuery);
|
const suggest = useGeocodeSuggest(addressQuery);
|
||||||
|
|
||||||
const handleAddressChange = (v: string) => {
|
const handleAddressChange = (v: string) => {
|
||||||
|
|
@ -568,34 +436,6 @@ export default function ParamsPanel({
|
||||||
setOpenDd(null);
|
setOpenDd(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Keep the РАДИУС virtual highlight pointed at the open selection (UI sync,
|
|
||||||
// not a data effect) — mirrors the <Dd> internal effect.
|
|
||||||
useEffect(() => {
|
|
||||||
if (openDd === "radius") {
|
|
||||||
const idx = RADIUS_OPTIONS.indexOf(radius);
|
|
||||||
setRadiusActive(idx >= 0 ? idx : 0);
|
|
||||||
} else {
|
|
||||||
setRadiusActive(-1);
|
|
||||||
}
|
|
||||||
}, [openDd, radius]);
|
|
||||||
|
|
||||||
// БАЛКОН radiogroup keyboard: arrows pick + move focus; Space/Enter commit the
|
|
||||||
// focused option.
|
|
||||||
const handleBalconyKey = (e: ReactKeyboardEvent<HTMLButtonElement>) => {
|
|
||||||
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
|
||||||
e.preventDefault();
|
|
||||||
setBalcony(true);
|
|
||||||
balYesRef.current?.focus();
|
|
||||||
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
|
||||||
e.preventDefault();
|
|
||||||
setBalcony(false);
|
|
||||||
balNoRef.current?.focus();
|
|
||||||
} else if (e.key === " " || e.key === "Enter") {
|
|
||||||
e.preventDefault();
|
|
||||||
setBalcony(e.currentTarget === balYesRef.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
const trimmedAddress = address.trim();
|
const trimmedAddress = address.trim();
|
||||||
const areaNum = Number(area.replace(",", "."));
|
const areaNum = Number(area.replace(",", "."));
|
||||||
|
|
@ -653,14 +493,8 @@ export default function ParamsPanel({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
// No Tailwind preflight here → reset UA <button> chrome explicitly so the
|
|
||||||
// radio pills keep the HUD font/size (UA buttons don't inherit font-family).
|
|
||||||
// `inherit` picks up the artboard's Manrope, matching the prior <div> + the
|
|
||||||
// map +/- buttons (strict parity, one approach).
|
|
||||||
fontFamily: "inherit",
|
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
letterSpacing: 1,
|
letterSpacing: 1,
|
||||||
padding: 0,
|
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
transition: "all .15s",
|
transition: "all .15s",
|
||||||
};
|
};
|
||||||
|
|
@ -867,9 +701,7 @@ export default function ParamsPanel({
|
||||||
gap: 5,
|
gap: 5,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<div
|
||||||
type="button"
|
|
||||||
aria-label="Приблизить карту"
|
|
||||||
style={{
|
style={{
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
|
|
@ -883,17 +715,12 @@ export default function ParamsPanel({
|
||||||
transition: "all .15s",
|
transition: "all .15s",
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
color: tokens.muted,
|
color: tokens.muted,
|
||||||
padding: 0,
|
|
||||||
fontFamily: "inherit",
|
|
||||||
}}
|
}}
|
||||||
onClick={() => setZoom((z) => Math.min(2.5, z + 0.25))}
|
onClick={() => setZoom((z) => Math.min(2.5, z + 0.25))}
|
||||||
>
|
>
|
||||||
+
|
+
|
||||||
</button>
|
</div>
|
||||||
<button
|
<div
|
||||||
type="button"
|
|
||||||
aria-label="Отдалить карту"
|
|
||||||
disabled={zoom <= 1}
|
|
||||||
style={{
|
style={{
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
|
|
@ -908,15 +735,13 @@ export default function ParamsPanel({
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
color: tokens.muted,
|
color: tokens.muted,
|
||||||
opacity: zoom <= 1 ? 0.4 : 1,
|
opacity: zoom <= 1 ? 0.4 : 1,
|
||||||
padding: 0,
|
|
||||||
fontFamily: "inherit",
|
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (zoom > 1) setZoom((z) => Math.max(1, z - 0.25));
|
if (zoom > 1) setZoom((z) => Math.max(1, z - 0.25));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
−
|
−
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* map corner bracket */}
|
{/* map corner bracket */}
|
||||||
|
|
@ -950,31 +775,9 @@ export default function ParamsPanel({
|
||||||
submit). Trigger markup unchanged; now toggles a Dd-style option panel
|
submit). Trigger markup unchanged; now toggles a Dd-style option panel
|
||||||
and uses the shared pp-dd-trigger hover cue. */}
|
and uses the shared pp-dd-trigger hover cue. */}
|
||||||
<div style={{ position: "relative" }}>
|
<div style={{ position: "relative" }}>
|
||||||
<button
|
<div
|
||||||
type="button"
|
|
||||||
className="pp-dd-trigger"
|
className="pp-dd-trigger"
|
||||||
onClick={() => toggle("radius")}
|
onClick={() => toggle("radius")}
|
||||||
onKeyDown={(e) =>
|
|
||||||
comboKeyDown(
|
|
||||||
e,
|
|
||||||
openDd === "radius",
|
|
||||||
RADIUS_OPTIONS,
|
|
||||||
radiusActive,
|
|
||||||
setRadiusActive,
|
|
||||||
() => toggle("radius"),
|
|
||||||
pick(setRadius),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
role="combobox"
|
|
||||||
aria-haspopup="listbox"
|
|
||||||
aria-expanded={openDd === "radius"}
|
|
||||||
aria-controls={radiusListId}
|
|
||||||
aria-activedescendant={
|
|
||||||
openDd === "radius" && radiusActive >= 0
|
|
||||||
? `${radiusListId}-opt-${radiusActive}`
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
aria-label="Радиус анализа"
|
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
|
@ -984,30 +787,19 @@ export default function ParamsPanel({
|
||||||
padding: "6px 10px",
|
padding: "6px 10px",
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontFamily: tokens.font.mono,
|
fontFamily: tokens.font.mono,
|
||||||
color: "inherit",
|
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span>{radius}</span>
|
<span>{radius}</span>
|
||||||
<span style={{ color: tokens.muted2, fontSize: 9 }}>▼</span>
|
<span style={{ color: tokens.muted2, fontSize: 9 }}>▼</span>
|
||||||
</button>
|
</div>
|
||||||
{openDd === "radius" && (
|
{openDd === "radius" && (
|
||||||
<div
|
<div style={radiusPanel}>
|
||||||
style={radiusPanel}
|
{RADIUS_OPTIONS.map((o) => (
|
||||||
role="listbox"
|
|
||||||
id={radiusListId}
|
|
||||||
aria-label="Радиус анализа"
|
|
||||||
tabIndex={-1}
|
|
||||||
>
|
|
||||||
{RADIUS_OPTIONS.map((o, i) => (
|
|
||||||
<div
|
<div
|
||||||
key={o}
|
key={o}
|
||||||
className="pp-dd-opt"
|
className="pp-dd-opt"
|
||||||
role="option"
|
|
||||||
id={`${radiusListId}-opt-${i}`}
|
|
||||||
aria-selected={o === radius}
|
|
||||||
onClick={() => pick(setRadius)(o)}
|
onClick={() => pick(setRadius)(o)}
|
||||||
onMouseEnter={() => setRadiusActive(i)}
|
|
||||||
style={{
|
style={{
|
||||||
padding: "7px 10px",
|
padding: "7px 10px",
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|
@ -1016,8 +808,6 @@ export default function ParamsPanel({
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
color: o === radius ? tokens.accent : tokens.ink,
|
color: o === radius ? tokens.accent : tokens.ink,
|
||||||
fontWeight: o === radius ? 600 : 400,
|
fontWeight: o === radius ? 600 : 400,
|
||||||
background:
|
|
||||||
i === radiusActive ? "rgba(46,139,255,.1)" : undefined,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{o}
|
{o}
|
||||||
|
|
@ -1073,26 +863,16 @@ export default function ParamsPanel({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<div style={hintLabel}>АДРЕС (YANDEX / NOMINATIM)</div>
|
||||||
htmlFor="pp-address"
|
|
||||||
style={{ ...hintLabel, display: "block" }}
|
|
||||||
>
|
|
||||||
АДРЕС (YANDEX / NOMINATIM)
|
|
||||||
</label>
|
|
||||||
<div style={{ position: "relative" }}>
|
<div style={{ position: "relative" }}>
|
||||||
{/* Address autocomplete (geocode suggest, ЕКБ viewbox). Typed text
|
{/* Address autocomplete (geocode suggest, ЕКБ viewbox). Typed text
|
||||||
is debounced into `addressQuery`; the dropdown below mirrors the
|
is debounced into `addressQuery`; the dropdown below mirrors the
|
||||||
HUD <Dd> panel styling. Picking an item fills the full address
|
HUD <Dd> panel styling. Picking an item fills the full address
|
||||||
and captures lat/lon for the estimate payload. */}
|
and captures lat/lon for the estimate payload. */}
|
||||||
<input
|
<input
|
||||||
id="pp-address"
|
|
||||||
className="pp-input"
|
className="pp-input"
|
||||||
type="text"
|
type="text"
|
||||||
value={address}
|
value={address}
|
||||||
aria-invalid={fieldErrors.address ? true : undefined}
|
|
||||||
aria-describedby={
|
|
||||||
fieldErrors.address ? "pp-address-err" : undefined
|
|
||||||
}
|
|
||||||
onChange={(e) => handleAddressChange(e.target.value)}
|
onChange={(e) => handleAddressChange(e.target.value)}
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
if (address.trim().length >= 3) setSuggestOpen(true);
|
if (address.trim().length >= 3) setSuggestOpen(true);
|
||||||
|
|
@ -1157,11 +937,7 @@ export default function ParamsPanel({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{fieldErrors.address && (
|
{fieldErrors.address && (
|
||||||
<div
|
<div style={{ ...errorText, marginTop: 4 }} role="alert">
|
||||||
id="pp-address-err"
|
|
||||||
style={{ ...errorText, marginTop: 4 }}
|
|
||||||
role="alert"
|
|
||||||
>
|
|
||||||
{fieldErrors.address}
|
{fieldErrors.address}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -1170,17 +946,12 @@ export default function ParamsPanel({
|
||||||
|
|
||||||
<div style={fieldRow}>
|
<div style={fieldRow}>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="pp-area" style={{ ...hintLabel, display: "block" }}>
|
<div style={hintLabel}>ПЛОЩАДЬ, М²</div>
|
||||||
ПЛОЩАДЬ, М²
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
id="pp-area"
|
|
||||||
className="pp-input"
|
className="pp-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputMode="decimal"
|
inputMode="decimal"
|
||||||
value={area}
|
value={area}
|
||||||
aria-invalid={fieldErrors.area ? true : undefined}
|
|
||||||
aria-describedby={fieldErrors.area ? "pp-area-err" : undefined}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setArea(e.target.value.replace(/[^\d.,]/g, ""));
|
setArea(e.target.value.replace(/[^\d.,]/g, ""));
|
||||||
if (fieldErrors.area)
|
if (fieldErrors.area)
|
||||||
|
|
@ -1197,11 +968,7 @@ export default function ParamsPanel({
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{fieldErrors.area && (
|
{fieldErrors.area && (
|
||||||
<div
|
<div style={{ ...errorText, marginTop: 4 }} role="alert">
|
||||||
id="pp-area-err"
|
|
||||||
style={{ ...errorText, marginTop: 4 }}
|
|
||||||
role="alert"
|
|
||||||
>
|
|
||||||
{fieldErrors.area}
|
{fieldErrors.area}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -1217,7 +984,6 @@ export default function ParamsPanel({
|
||||||
mono
|
mono
|
||||||
triggerFontSize={14}
|
triggerFontSize={14}
|
||||||
optionFontSize={13}
|
optionFontSize={13}
|
||||||
ariaLabel="Комнат"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1225,13 +991,10 @@ export default function ParamsPanel({
|
||||||
<div style={fieldRow}>
|
<div style={fieldRow}>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
<label htmlFor="pp-floor" style={hintLabel}>
|
<span style={hintLabel}>ЭТАЖ</span>
|
||||||
ЭТАЖ
|
|
||||||
</label>
|
|
||||||
<span style={optHint}>если знаете</span>
|
<span style={optHint}>если знаете</span>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
id="pp-floor"
|
|
||||||
className="pp-input"
|
className="pp-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
|
|
@ -1246,13 +1009,10 @@ export default function ParamsPanel({
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
<label htmlFor="pp-total-floors" style={hintLabel}>
|
<span style={hintLabel}>ВСЕГО ЭТАЖЕЙ</span>
|
||||||
ВСЕГО ЭТАЖЕЙ
|
|
||||||
</label>
|
|
||||||
<span style={optHint}>если знаете</span>
|
<span style={optHint}>если знаете</span>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
id="pp-total-floors"
|
|
||||||
className="pp-input"
|
className="pp-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
|
|
@ -1283,13 +1043,10 @@ export default function ParamsPanel({
|
||||||
<div style={fieldRow}>
|
<div style={fieldRow}>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
<label htmlFor="pp-year" style={hintLabel}>
|
<span style={hintLabel}>ГОД ПОСТРОЙКИ</span>
|
||||||
ГОД ПОСТРОЙКИ
|
|
||||||
</label>
|
|
||||||
<span style={optHint}>опц.</span>
|
<span style={optHint}>опц.</span>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
id="pp-year"
|
|
||||||
className="pp-input"
|
className="pp-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
|
|
@ -1316,7 +1073,6 @@ export default function ParamsPanel({
|
||||||
mono={false}
|
mono={false}
|
||||||
triggerFontSize={13}
|
triggerFontSize={13}
|
||||||
optionFontSize={12}
|
optionFontSize={12}
|
||||||
ariaLabel="Тип дома"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1336,40 +1092,23 @@ export default function ParamsPanel({
|
||||||
mono={false}
|
mono={false}
|
||||||
triggerFontSize={13}
|
triggerFontSize={13}
|
||||||
optionFontSize={12}
|
optionFontSize={12}
|
||||||
ariaLabel="Состояние ремонта"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div style={hintLabel}>БАЛКОН / ЛОДЖИЯ</div>
|
<div style={hintLabel}>БАЛКОН / ЛОДЖИЯ</div>
|
||||||
|
<div style={{ height: 40, display: "flex", gap: 6 }}>
|
||||||
<div
|
<div
|
||||||
role="radiogroup"
|
|
||||||
aria-label="Балкон или лоджия"
|
|
||||||
style={{ height: 40, display: "flex", gap: 6 }}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
role="radio"
|
|
||||||
aria-checked={balcony === false}
|
|
||||||
tabIndex={balcony === false || balcony === null ? 0 : -1}
|
|
||||||
ref={balNoRef}
|
|
||||||
onClick={() => setBalcony(false)}
|
onClick={() => setBalcony(false)}
|
||||||
onKeyDown={handleBalconyKey}
|
|
||||||
style={{ ...balBase, ...(balcony === false ? balOn : balOff) }}
|
style={{ ...balBase, ...(balcony === false ? balOn : balOff) }}
|
||||||
>
|
>
|
||||||
НЕТ
|
НЕТ
|
||||||
</button>
|
</div>
|
||||||
<button
|
<div
|
||||||
type="button"
|
|
||||||
role="radio"
|
|
||||||
aria-checked={balcony === true}
|
|
||||||
tabIndex={balcony === true ? 0 : -1}
|
|
||||||
ref={balYesRef}
|
|
||||||
onClick={() => setBalcony(true)}
|
onClick={() => setBalcony(true)}
|
||||||
onKeyDown={handleBalconyKey}
|
|
||||||
style={{ ...balBase, ...(balcony === true ? balOn : balOff) }}
|
style={{ ...balBase, ...(balcony === true ? balOn : balOff) }}
|
||||||
>
|
>
|
||||||
ДА
|
ДА
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1397,7 +1136,6 @@ export default function ParamsPanel({
|
||||||
triggerColor={tokens.hint}
|
triggerColor={tokens.hint}
|
||||||
caretColor={tokens.muted4}
|
caretColor={tokens.muted4}
|
||||||
disabled
|
disabled
|
||||||
ariaLabel="CRM — для менеджера"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue