feat: update map anchor for station and desciprtion search
This commit is contained in:
@@ -364,8 +364,20 @@ const computeViewTransform = (
|
|||||||
return { scale, translation };
|
return { scale, translation };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAnchorFromOffset = (
|
||||||
|
offsetX: number,
|
||||||
|
offsetY: number
|
||||||
|
): { x: number; y: number } => {
|
||||||
|
const length = Math.hypot(offsetX, offsetY);
|
||||||
|
|
||||||
|
const nx = offsetX / length;
|
||||||
|
const ny = offsetY / length;
|
||||||
|
|
||||||
|
return { x: (1 - nx) / 2, y: (1 - ny) / 2 };
|
||||||
|
};
|
||||||
|
|
||||||
const backgroundColor = toColor(BACKGROUND_COLOR);
|
const backgroundColor = toColor(BACKGROUND_COLOR);
|
||||||
// Override route & station color to ED1C24
|
|
||||||
const pathColor = toColor(0xed1c24);
|
const pathColor = toColor(0xed1c24);
|
||||||
|
|
||||||
export const WebGLRouteMapPrototype = observer(() => {
|
export const WebGLRouteMapPrototype = observer(() => {
|
||||||
@@ -395,6 +407,7 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
const transformRef = useRef<Transform | null>(null);
|
const transformRef = useRef<Transform | null>(null);
|
||||||
const lastTransformRef = useRef<Transform | null>(null);
|
const lastTransformRef = useRef<Transform | null>(null);
|
||||||
const [transformState, setTransformState] = useState<Transform | null>(null);
|
const [transformState, setTransformState] = useState<Transform | null>(null);
|
||||||
|
|
||||||
const clampTransformScale = useCallback((transform: Transform): Transform => {
|
const clampTransformScale = useCallback((transform: Transform): Transform => {
|
||||||
const { min, max } = scaleLimitsRef.current;
|
const { min, max } = scaleLimitsRef.current;
|
||||||
const clampedScale = clamp(transform.scale, min, max);
|
const clampedScale = clamp(transform.scale, min, max);
|
||||||
@@ -1904,6 +1917,11 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
const labelY =
|
const labelY =
|
||||||
(rotatedY + offsetY) * camera.scale + camera.translation.y;
|
(rotatedY + offsetY) * camera.scale + camera.translation.y;
|
||||||
|
|
||||||
|
const anchor = getAnchorFromOffset(offsetX, offsetY);
|
||||||
|
const transformCss = `translate(${-anchor.x * 100}%, ${
|
||||||
|
-anchor.y * 100
|
||||||
|
}%)`;
|
||||||
|
|
||||||
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
||||||
const cssX = labelX / dpr;
|
const cssX = labelX / dpr;
|
||||||
const cssY = labelY / dpr;
|
const cssY = labelY / dpr;
|
||||||
@@ -1919,8 +1937,10 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
const fontSizePercent =
|
const fontSizePercent =
|
||||||
routeData?.font_size ?? originalRouteData?.font_size ?? 100;
|
routeData?.font_size ?? originalRouteData?.font_size ?? 100;
|
||||||
const fontScale = fontSizePercent / 100;
|
const fontScale = fontSizePercent / 100;
|
||||||
|
|
||||||
const primaryFontSize = 16 * fontScale;
|
const primaryFontSize = 16 * fontScale;
|
||||||
const secondaryFontSize = 13 * fontScale;
|
const secondaryFontSize = 13 * fontScale;
|
||||||
|
|
||||||
const secondaryMarginTop = 5 * fontScale;
|
const secondaryMarginTop = 5 * fontScale;
|
||||||
|
|
||||||
const backendAlign = station.align;
|
const backendAlign = station.align;
|
||||||
@@ -1972,7 +1992,7 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: cssX,
|
left: cssX,
|
||||||
top: cssY,
|
top: cssY,
|
||||||
transform: "translate(0, -50%)",
|
transform: transformCss,
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
fontFamily: "Roboto, sans-serif",
|
fontFamily: "Roboto, sans-serif",
|
||||||
textAlign: "left",
|
textAlign: "left",
|
||||||
|
|||||||
@@ -135,7 +135,10 @@ const LinkedStationsContentsInner = <
|
|||||||
|
|
||||||
const filteredAvailableItems = availableItems.filter((item) => {
|
const filteredAvailableItems = availableItems.filter((item) => {
|
||||||
if (!searchQuery.trim()) return true;
|
if (!searchQuery.trim()) return true;
|
||||||
return String(item.name).toLowerCase().includes(searchQuery.toLowerCase());
|
const query = searchQuery.toLowerCase();
|
||||||
|
const name = String(item.name || "").toLowerCase();
|
||||||
|
const description = String(item.description || "").toLowerCase();
|
||||||
|
return name.includes(query) || description.includes(query);
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -483,6 +486,7 @@ const LinkedStationsContentsInner = <
|
|||||||
<TextField
|
<TextField
|
||||||
{...params}
|
{...params}
|
||||||
label="Выберите остановку"
|
label="Выберите остановку"
|
||||||
|
placeholder="Введите название или описание остановки..."
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -490,16 +494,15 @@ const LinkedStationsContentsInner = <
|
|||||||
option.id === value?.id
|
option.id === value?.id
|
||||||
}
|
}
|
||||||
filterOptions={(options, { inputValue }) => {
|
filterOptions={(options, { inputValue }) => {
|
||||||
const searchWords = inputValue
|
if (!inputValue.trim()) return options;
|
||||||
.toLowerCase()
|
const query = inputValue.toLowerCase();
|
||||||
.split(" ")
|
|
||||||
.filter(Boolean);
|
|
||||||
return options.filter((option) => {
|
return options.filter((option) => {
|
||||||
const optionWords = String(option.name)
|
const name = String(option.name || "").toLowerCase();
|
||||||
.toLowerCase()
|
const description = String(
|
||||||
.split(" ");
|
option.description || ""
|
||||||
return searchWords.every((searchWord) =>
|
).toLowerCase();
|
||||||
optionWords.some((word) => word.startsWith(searchWord))
|
return (
|
||||||
|
name.includes(query) || description.includes(query)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@@ -534,7 +537,7 @@ const LinkedStationsContentsInner = <
|
|||||||
label="Поиск остановок"
|
label="Поиск остановок"
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
placeholder="Введите название остановки..."
|
placeholder="Введите название или описание остановки..."
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -550,11 +553,19 @@ const LinkedStationsContentsInner = <
|
|||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={String(item.name)}
|
label={
|
||||||
|
<div className="flex justify-between items-center w-full gap-10">
|
||||||
|
<p>{String(item.name)}</p>
|
||||||
|
<p className="text-xs text-gray-500 max-w-[300px] truncate text-ellipsis">
|
||||||
|
{String(item.description)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
sx={{
|
sx={{
|
||||||
margin: 0,
|
margin: 0,
|
||||||
"& .MuiFormControlLabel-label": {
|
"& .MuiFormControlLabel-label": {
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
|
width: "100%",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user