feat: add markdown, lang atribute, button_text
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "white-nights",
|
||||
"private": true,
|
||||
"version": "1.0.7",
|
||||
"version": "1.0.8",
|
||||
"type": "module",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
|
||||
@@ -55,6 +55,7 @@ export type GetRouteResponse = {
|
||||
center_latitude: number;
|
||||
center_longitude: number;
|
||||
governor_appeal: number;
|
||||
button_text?: string;
|
||||
id: number;
|
||||
path: [number, number][];
|
||||
rotate: number;
|
||||
|
||||
@@ -20,6 +20,8 @@ function useThumbSync(scrollableRef: React.RefObject<HTMLDivElement | null>) {
|
||||
top: 0,
|
||||
hasScroll: false,
|
||||
});
|
||||
const [visible, setVisible] = useState(false);
|
||||
const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const rafRef = useRef<number | null>(null);
|
||||
|
||||
const update = useCallback(() => {
|
||||
@@ -32,7 +34,7 @@ function useThumbSync(scrollableRef: React.RefObject<HTMLDivElement | null>) {
|
||||
const th = ch;
|
||||
|
||||
if (sh <= ch) {
|
||||
setState({ height: th, top: 0, hasScroll: false });
|
||||
setState((prev) => ({ ...prev, hasScroll: false }));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,7 +70,24 @@ function useThumbSync(scrollableRef: React.RefObject<HTMLDivElement | null>) {
|
||||
};
|
||||
}, [update]);
|
||||
|
||||
return state;
|
||||
useEffect(() => {
|
||||
if (state.hasScroll) {
|
||||
if (hideTimerRef.current) {
|
||||
clearTimeout(hideTimerRef.current);
|
||||
hideTimerRef.current = null;
|
||||
}
|
||||
setVisible(true);
|
||||
} else {
|
||||
hideTimerRef.current = setTimeout(() => {
|
||||
setVisible(false);
|
||||
}, 200);
|
||||
}
|
||||
return () => {
|
||||
if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
|
||||
};
|
||||
}, [state.hasScroll]);
|
||||
|
||||
return { ...state, visible };
|
||||
}
|
||||
|
||||
export const TouchableLayout = forwardRef<HTMLDivElement, TouchableLayoutProps>(
|
||||
@@ -251,15 +270,19 @@ export const TouchableLayout = forwardRef<HTMLDivElement, TouchableLayoutProps>(
|
||||
<div ref={scrollableRef} className="scrollable">
|
||||
{children}
|
||||
</div>
|
||||
{thumb.hasScroll && (
|
||||
<div ref={trackRef} className="custom-scrollbar-track">
|
||||
<div
|
||||
ref={trackRef}
|
||||
className="custom-scrollbar-track"
|
||||
style={{ opacity: thumb.hasScroll ? 1 : 0 }}
|
||||
>
|
||||
{thumb.visible && (
|
||||
<div
|
||||
ref={thumbRef}
|
||||
className="custom-scrollbar-thumb"
|
||||
style={{ height: thumb.height, top: thumb.top }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,8 @@ import { useGeolocationStore } from "../../stores";
|
||||
import "../../styles/LeftWidget.css";
|
||||
import { apiStore } from "../../api/ApiStore/store";
|
||||
import { apiBaseURL } from "../../api/apiConfig";
|
||||
import { ReactMarkdownComponent } from "../ReactMarkdown";
|
||||
import { TouchableLayout } from "../TouchableLayout";
|
||||
|
||||
const LeftWidget = observer(
|
||||
({ selectedSightId, onClose, isVisible, sightTop }) => {
|
||||
@@ -15,8 +17,7 @@ const LeftWidget = observer(
|
||||
const [isImageLoaded, setIsImageLoaded] = useState(false);
|
||||
const [widgetHeight, setWidgetHeight] = useState(0);
|
||||
|
||||
const textRef = useRef(null);
|
||||
const activeTouch = useRef(null);
|
||||
const layoutRef = useRef(null);
|
||||
const widgetRef = useRef(null);
|
||||
|
||||
const store = useGeolocationStore();
|
||||
@@ -37,64 +38,10 @@ const LeftWidget = observer(
|
||||
}, [selectedSightData, isImageLoaded, isVisible, isLoading, error]);
|
||||
|
||||
useEffect(() => {
|
||||
const scrollContainer = textRef.current;
|
||||
if (!scrollContainer) return;
|
||||
|
||||
const handleTouchStart = (e) => {
|
||||
e.stopPropagation();
|
||||
if (e.touches.length === 1) {
|
||||
activeTouch.current = {
|
||||
identifier: e.touches[0].identifier,
|
||||
lastY: e.touches[0].clientY,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchMove = (e) => {
|
||||
e.preventDefault();
|
||||
if (activeTouch.current) {
|
||||
for (const touch of e.changedTouches) {
|
||||
if (touch.identifier === activeTouch.current.identifier) {
|
||||
const deltaY = touch.clientY - activeTouch.current.lastY;
|
||||
scrollContainer.scrollTop -= deltaY;
|
||||
activeTouch.current.lastY = touch.clientY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchEnd = (e) => {
|
||||
for (const touch of e.changedTouches) {
|
||||
if (
|
||||
activeTouch.current &&
|
||||
touch.identifier === activeTouch.current.identifier
|
||||
) {
|
||||
activeTouch.current = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scrollContainer.addEventListener("touchstart", handleTouchStart, {
|
||||
passive: true,
|
||||
});
|
||||
scrollContainer.addEventListener("touchmove", handleTouchMove, {
|
||||
passive: false,
|
||||
});
|
||||
scrollContainer.addEventListener("touchend", handleTouchEnd, {
|
||||
passive: true,
|
||||
});
|
||||
scrollContainer.addEventListener("touchcancel", handleTouchEnd, {
|
||||
passive: true,
|
||||
});
|
||||
|
||||
return () => {
|
||||
scrollContainer.removeEventListener("touchstart", handleTouchStart);
|
||||
scrollContainer.removeEventListener("touchmove", handleTouchMove);
|
||||
scrollContainer.removeEventListener("touchend", handleTouchEnd);
|
||||
scrollContainer.removeEventListener("touchcancel", handleTouchEnd);
|
||||
};
|
||||
if (layoutRef.current) {
|
||||
const scrollable = layoutRef.current.querySelector(".scrollable");
|
||||
if (scrollable) scrollable.scrollTop = 0;
|
||||
}
|
||||
}, [selectedSightData]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -238,9 +185,11 @@ const LeftWidget = observer(
|
||||
<div className="left-widget-address">
|
||||
{selectedSightData.address}
|
||||
</div>
|
||||
<div ref={textRef} className="left-widget-text">
|
||||
{selectedSightData.text}
|
||||
</div>
|
||||
<TouchableLayout ref={layoutRef} className="left-widget-text-scroll">
|
||||
<div className="left-widget-text">
|
||||
<ReactMarkdownComponent value={selectedSightData.text} />
|
||||
</div>
|
||||
</TouchableLayout>
|
||||
</div>
|
||||
</>
|
||||
) : (isVisible || selectedSightData) && !isLoading ? (
|
||||
|
||||
@@ -447,11 +447,13 @@ const SideMenu = observer(({ onMenuToggle }) => {
|
||||
}}
|
||||
className="appeal-button"
|
||||
>
|
||||
{selectedLanguage == "ru"
|
||||
? "Обращение губернатора"
|
||||
: selectedLanguage == "zh"
|
||||
? "州长致辞"
|
||||
: "Governor's appeal"}
|
||||
{route?.button_text
|
||||
? route.button_text
|
||||
: selectedLanguage == "ru"
|
||||
? "Обращение губернатора"
|
||||
: selectedLanguage == "zh"
|
||||
? "州长致辞"
|
||||
: "Governor's appeal"}
|
||||
</div>
|
||||
)}
|
||||
<div className="side-menu-buttons" style={{ marginTop: route?.governor_appeal > 0 ? '40px' : '260px' }}>
|
||||
|
||||
@@ -84,6 +84,12 @@ const SightItem = ({
|
||||
return () => window.removeEventListener("resize", checkWidth);
|
||||
}, [sightName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (localSelectedSightId !== sight.id) {
|
||||
setIsExpanded(false);
|
||||
}
|
||||
}, [localSelectedSightId, sight.id]);
|
||||
|
||||
const handleClick = (e) => {
|
||||
const newExpanded = !isExpanded;
|
||||
setIsExpanded(newExpanded);
|
||||
@@ -96,15 +102,19 @@ const SightItem = ({
|
||||
const stations = sightStationsCache.get(cacheKey) || [];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={
|
||||
localSelectedSightId === sight.id
|
||||
? "side-menu-sight-selected-wrapper"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<div
|
||||
ref={containerRef}
|
||||
id={`sight-${sight.id}`}
|
||||
onPointerDown={(e) => handlePointerDown(e, sight.id)}
|
||||
onPointerUp={(e) => handlePointerUp(e, sight.id, handleClick)}
|
||||
className={`side-menu-sight pointer ${
|
||||
localSelectedSightId === sight.id ? "selected" : ""
|
||||
}`}
|
||||
className="side-menu-sight pointer"
|
||||
>
|
||||
<span ref={textRef} className={shouldAnimate ? "marquee-text" : ""}>
|
||||
{sightName}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useRef, useEffect } from "react";
|
||||
import "../../styles/AppealWidget.css";
|
||||
import { TouchableLayout } from "../TouchableLayout";
|
||||
import { ReactMarkdownComponent } from "../ReactMarkdown";
|
||||
|
||||
function AppealWidget({
|
||||
widgetImgPath,
|
||||
@@ -38,7 +39,9 @@ function AppealWidget({
|
||||
ref={layoutRef}
|
||||
className="dynamic-widget-text-scroll"
|
||||
>
|
||||
<div className="dynamic-widget-text">{widgetText}</div>
|
||||
<div className="dynamic-widget-text">
|
||||
<ReactMarkdownComponent value={widgetText} />
|
||||
</div>
|
||||
</TouchableLayout>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -288,7 +288,7 @@ export const ThreeView: React.FC<ThreeViewProps> = ({
|
||||
{!isReady && (
|
||||
<div style={{
|
||||
position: "absolute", inset: 0,
|
||||
backgroundColor: BACKGROUND_COLOR,
|
||||
backgroundColor: `#${BACKGROUND_COLOR.toString(16).padStart(6, "0")}`,
|
||||
zIndex: 1,
|
||||
}} />
|
||||
)}
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
.side-menu-sights-block {
|
||||
height: calc(60%);
|
||||
overflow-y: scroll;
|
||||
margin-left: 20px;
|
||||
margin-left: 0;
|
||||
margin-top: 8px;
|
||||
margin-right: 5px;
|
||||
padding-right: 5px;
|
||||
touch-action: none; /* Отключаем стандартные действия */
|
||||
overscroll-behavior: contain; /* Предотвращаем прокрутку родительских элементов */
|
||||
}
|
||||
|
||||
@@ -66,8 +66,63 @@
|
||||
|
||||
|
||||
.dynamic-widget-text {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 190%;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
line-height: 135%;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.dynamic-widget-text .react-markdown-container {
|
||||
font-size: 16px;
|
||||
line-height: 135%;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.dynamic-widget-text .react-markdown-container p {
|
||||
font-size: 16px;
|
||||
line-height: 135%;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.dynamic-widget-text .react-markdown-container p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.dynamic-widget-text .react-markdown-container h1,
|
||||
.dynamic-widget-text .react-markdown-container h2,
|
||||
.dynamic-widget-text .react-markdown-container h3,
|
||||
.dynamic-widget-text .react-markdown-container h4,
|
||||
.dynamic-widget-text .react-markdown-container h5,
|
||||
.dynamic-widget-text .react-markdown-container h6 {
|
||||
font-size: 18px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dynamic-widget-text .react-markdown-container ul,
|
||||
.dynamic-widget-text .react-markdown-container ol {
|
||||
margin-bottom: 8px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.dynamic-widget-text .react-markdown-container li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.dynamic-widget-text .react-markdown-container blockquote {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 12px;
|
||||
border-left: 3px solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.dynamic-widget-text .react-markdown-container img {
|
||||
max-width: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.dynamic-widget-text .react-markdown-container a {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -67,17 +67,78 @@
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
.left-widget-text {
|
||||
.left-widget-text-scroll.scrollable-container {
|
||||
margin-top: 15px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.left-widget-text-scroll .scrollable-viewport {
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.left-widget-text {
|
||||
color: #fff;
|
||||
font-family: "Roboto";
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
line-height: 135%;
|
||||
max-height: 200px; /* Пример ограничения высоты */
|
||||
overflow-y: auto;
|
||||
touch-action: none;
|
||||
overscroll-behavior: contain;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.left-widget-text .react-markdown-container {
|
||||
font-size: 16px;
|
||||
line-height: 135%;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.left-widget-text .react-markdown-container p {
|
||||
font-size: 16px;
|
||||
line-height: 135%;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.left-widget-text .react-markdown-container p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.left-widget-text .react-markdown-container h1,
|
||||
.left-widget-text .react-markdown-container h2,
|
||||
.left-widget-text .react-markdown-container h3,
|
||||
.left-widget-text .react-markdown-container h4,
|
||||
.left-widget-text .react-markdown-container h5,
|
||||
.left-widget-text .react-markdown-container h6 {
|
||||
font-size: 18px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.left-widget-text .react-markdown-container ul,
|
||||
.left-widget-text .react-markdown-container ol {
|
||||
margin-bottom: 8px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.left-widget-text .react-markdown-container li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.left-widget-text .react-markdown-container blockquote {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 12px;
|
||||
border-left: 3px solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.left-widget-text .react-markdown-container img {
|
||||
max-width: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.left-widget-text .react-markdown-container a {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.left-widget-image {
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
@keyframes pulse-chevron {
|
||||
0% { transform: rotate(var(--r, 0deg)) translateY(0px) scale(1); }
|
||||
40% { transform: rotate(var(--r, 0deg)) translateY(-4px) scale(1.12); }
|
||||
60% { transform: rotate(var(--r, 0deg)) translateY(-5px) scale(1.14); }
|
||||
100% { transform: rotate(var(--r, 0deg)) translateY(0px) scale(1); }
|
||||
0% {
|
||||
transform: rotate(var(--r, 0deg)) translateY(0px) scale(1);
|
||||
}
|
||||
40% {
|
||||
transform: rotate(var(--r, 0deg)) translateY(-4px) scale(1.12);
|
||||
}
|
||||
60% {
|
||||
transform: rotate(var(--r, 0deg)) translateY(-5px) scale(1.14);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(var(--r, 0deg)) translateY(0px) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.chevron-svg {
|
||||
@@ -39,7 +47,7 @@
|
||||
rgba(255, 255, 255, 0) 8.71%,
|
||||
rgba(255, 255, 255, 0.16) 69.69%
|
||||
),
|
||||
var(--carrier-right, #806C59);
|
||||
var(--carrier-right, #806c59);
|
||||
color: white;
|
||||
|
||||
max-height: 68px;
|
||||
@@ -87,7 +95,7 @@
|
||||
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--carrier-right, #806C59) 80%,
|
||||
var(--carrier-right, #806c59) 80%,
|
||||
black
|
||||
);
|
||||
}
|
||||
@@ -220,7 +228,7 @@
|
||||
rgba(255, 255, 255, 0) 8.71%,
|
||||
rgba(255, 255, 255, 0.16) 69.69%
|
||||
),
|
||||
var(--carrier-right, #806C59);
|
||||
var(--carrier-right, #806c59);
|
||||
max-height: calc(100vh - 128px);
|
||||
}
|
||||
|
||||
@@ -340,7 +348,7 @@
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
transparent 35%,
|
||||
color-mix(in srgb, var(--carrier-right, #806C59) 80%, black) 50%,
|
||||
color-mix(in srgb, var(--carrier-right, #806c59) 80%, black) 50%,
|
||||
transparent 65%
|
||||
);
|
||||
border-radius: 3px;
|
||||
@@ -380,17 +388,26 @@
|
||||
|
||||
.sight-frame-menu-fade.left {
|
||||
left: 0;
|
||||
background: linear-gradient(to right, rgba(var(--carrier-right-menu-rgb, 179, 165, 152), 0.95), transparent);
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(var(--carrier-right-menu-rgb, 179, 165, 152), 0.95),
|
||||
transparent
|
||||
);
|
||||
border-radius: 0 0 0 10px;
|
||||
}
|
||||
|
||||
.sight-frame-menu-fade.right {
|
||||
right: 0;
|
||||
background: linear-gradient(to left, rgba(var(--carrier-right-menu-rgb, 179, 165, 152), 0.95), transparent);
|
||||
background: linear-gradient(
|
||||
to left,
|
||||
rgba(var(--carrier-right-menu-rgb, 179, 165, 152), 0.95),
|
||||
transparent
|
||||
);
|
||||
border-radius: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.sight-frame-menu {
|
||||
z-index: 10000;
|
||||
position: relative;
|
||||
padding: 7px 60px;
|
||||
width: 100%;
|
||||
@@ -825,7 +842,7 @@
|
||||
border-radius: 32px;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
background: var(--carrier-right, #806C59);
|
||||
background: var(--carrier-right, #806c59);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
font-size: 16px;
|
||||
margin-top: 120px;
|
||||
font-weight: 500;
|
||||
width: 220px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.side-menu-buttons {
|
||||
@@ -227,12 +229,10 @@
|
||||
.side-menu-sights-block {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
margin-left: 20px;
|
||||
margin-top: 8px;
|
||||
touch-action: none;
|
||||
overscroll-behavior: contain;
|
||||
width: auto;
|
||||
max-width: calc(100% - 20px);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
@@ -240,7 +240,6 @@
|
||||
|
||||
.side-menu-sight {
|
||||
padding-bottom: 2px;
|
||||
margin-right: 20px;
|
||||
margin-bottom: 6px;
|
||||
margin-top: 6px;
|
||||
border-bottom: 1px solid
|
||||
@@ -254,6 +253,12 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.side-menu-sight-selected-wrapper {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
margin-left: -20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.side-menu-sight > span {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
position: relative;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 3px;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.custom-scrollbar-thumb {
|
||||
@@ -67,6 +68,7 @@
|
||||
|
||||
.side-menu-sights-block .scrollable {
|
||||
height: 100%;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.list-of-sights-content .scrollable-viewport {
|
||||
|
||||
@@ -108,7 +108,7 @@ export const ArticleListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ export const CarrierListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ export const CityListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ export const CountryListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ export const MediaListPage = observer(() => {
|
||||
return (
|
||||
<>
|
||||
<div className="w-full">
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ export const RouteCreatePage = observer(() => {
|
||||
const [routeCoords, setRouteCoords] = useState("");
|
||||
const [govRouteNumber, setGovRouteNumber] = useState("");
|
||||
const [governorAppeal, setGovernorAppeal] = useState<string>("");
|
||||
const [buttonText, setButtonText] = useState("");
|
||||
const [direction, setDirection] = useState("backward");
|
||||
const [scaleMin, setScaleMin] = useState("10");
|
||||
const [scaleMax, setScaleMax] = useState("100");
|
||||
@@ -292,6 +293,10 @@ export const RouteCreatePage = observer(() => {
|
||||
newRoute.governor_appeal = governor_appeal;
|
||||
}
|
||||
|
||||
if (buttonText.trim()) {
|
||||
newRoute.button_text = buttonText.trim();
|
||||
}
|
||||
|
||||
const newId = await routeStore.createRoute(newRoute);
|
||||
toast.success("Маршрут успешно создан");
|
||||
navigate(`/route/${newId}/edit`);
|
||||
@@ -407,6 +412,18 @@ export const RouteCreatePage = observer(() => {
|
||||
onChange={(e) => setGovRouteNumber(e.target.value)}
|
||||
/>
|
||||
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||
Текст кнопки обращения
|
||||
</Typography>
|
||||
<TextField
|
||||
value={buttonText}
|
||||
onChange={(e) => setButtonText(e.target.value)}
|
||||
placeholder="Обращение губернатора"
|
||||
fullWidth
|
||||
size="small"
|
||||
helperText="Если пусто, будет использован текст по умолчанию"
|
||||
/>
|
||||
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||
Обращение к пассажирам
|
||||
</Typography>
|
||||
|
||||
@@ -566,6 +566,22 @@ export const RouteEditPage = observer(() => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||
Текст кнопки обращения
|
||||
</Typography>
|
||||
<TextField
|
||||
value={editRouteData.button_text || ""}
|
||||
onChange={(e) =>
|
||||
routeStore.setEditRouteData({
|
||||
button_text: e.target.value,
|
||||
})
|
||||
}
|
||||
placeholder="Обращение губернатора"
|
||||
fullWidth
|
||||
size="small"
|
||||
helperText="Если пусто, будет использован текст по умолчанию"
|
||||
/>
|
||||
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||
Обращение к пассажирам
|
||||
</Typography>
|
||||
|
||||
@@ -270,7 +270,7 @@ export const RouteListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface RouteData {
|
||||
icon_size?: number;
|
||||
font_size: number;
|
||||
governor_appeal: number;
|
||||
button_text?: string;
|
||||
id: number;
|
||||
path: [number, number][];
|
||||
rotate: number;
|
||||
|
||||
@@ -181,7 +181,7 @@ export const SightListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -316,7 +316,7 @@ export const SnapshotListPage = observer(() => {
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ export const StationListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ export const UserListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ export const VehicleListPage = observer(() => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{rows.length > 0 && (
|
||||
{(rows.length > 0 || searchQuery) && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ export type Route = {
|
||||
center_latitude: number;
|
||||
center_longitude: number;
|
||||
governor_appeal: number;
|
||||
button_text?: string;
|
||||
id: number;
|
||||
icon: string;
|
||||
path: number[][];
|
||||
@@ -143,6 +144,7 @@ class RouteStore {
|
||||
center_latitude: "",
|
||||
center_longitude: "",
|
||||
governor_appeal: 0,
|
||||
button_text: "" as string | undefined,
|
||||
id: 0,
|
||||
icon: "",
|
||||
path: [] as number[][],
|
||||
|
||||
Reference in New Issue
Block a user