feat: hide search input when list is empty and add animated city selector highlight
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -108,7 +108,9 @@ export const ArticleListPage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<DataGrid
|
<DataGrid
|
||||||
|
|||||||
@@ -162,7 +162,9 @@ export const CarrierListPage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
|
|||||||
@@ -165,7 +165,9 @@ export const CityListPage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={filteredRows}
|
rows={filteredRows}
|
||||||
|
|||||||
@@ -117,7 +117,9 @@ export const CountryListPage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
|
|||||||
@@ -113,7 +113,9 @@ export const MediaListPage = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
{canWriteMedia && ids.length > 0 && (
|
{canWriteMedia && ids.length > 0 && (
|
||||||
<div className="flex justify-end mb-5 duration-300">
|
<div className="flex justify-end mb-5 duration-300">
|
||||||
|
|||||||
@@ -271,7 +271,9 @@ export const RouteListPage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
|
|||||||
@@ -182,7 +182,9 @@ export const SightListPage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
|
|||||||
@@ -299,7 +299,9 @@ export const SnapshotListPage = observer(() => {
|
|||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
|
|||||||
@@ -226,7 +226,9 @@ export const StationListPage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
|
|||||||
@@ -147,7 +147,9 @@ export const UserListPage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
|
|||||||
@@ -174,7 +174,9 @@ export const VehicleListPage = observer(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
{rows.length > 0 && (
|
||||||
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
)}
|
||||||
|
|
||||||
{canWriteVehicles && ids.length > 0 && (
|
{canWriteVehicles && ids.length > 0 && (
|
||||||
<div className="flex justify-end mb-5 duration-300">
|
<div className="flex justify-end mb-5 duration-300">
|
||||||
|
|||||||
@@ -6,11 +6,21 @@ import {
|
|||||||
SelectChangeEvent,
|
SelectChangeEvent,
|
||||||
Typography,
|
Typography,
|
||||||
Box,
|
Box,
|
||||||
|
keyframes,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { authStore, cityStore, selectedCityStore, snapshotStore, type City } from "@shared";
|
import { authStore, cityStore, selectedCityStore, snapshotStore, type City } from "@shared";
|
||||||
import { MapPin } from "lucide-react";
|
import { MapPin } from "lucide-react";
|
||||||
|
|
||||||
|
const borderSpin = keyframes`
|
||||||
|
0% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 200% 50%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const CitySelector: React.FC = observer(() => {
|
export const CitySelector: React.FC = observer(() => {
|
||||||
const { selectedCity, setSelectedCity, isLocked } = selectedCityStore;
|
const { selectedCity, setSelectedCity, isLocked } = selectedCityStore;
|
||||||
const canLoadAllCities = authStore.isAdmin && authStore.canRead("cities");
|
const canLoadAllCities = authStore.isAdmin && authStore.canRead("cities");
|
||||||
@@ -43,6 +53,8 @@ export const CitySelector: React.FC = observer(() => {
|
|||||||
})()
|
})()
|
||||||
: baseCities;
|
: baseCities;
|
||||||
|
|
||||||
|
const noCitySelected = !selectedCity?.id;
|
||||||
|
|
||||||
const handleCityChange = (event: SelectChangeEvent<string>) => {
|
const handleCityChange = (event: SelectChangeEvent<string>) => {
|
||||||
const cityId = event.target.value;
|
const cityId = event.target.value;
|
||||||
if (cityId === "") {
|
if (cityId === "") {
|
||||||
@@ -58,49 +70,76 @@ export const CitySelector: React.FC = observer(() => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selectElement = (
|
||||||
|
<Select
|
||||||
|
value={selectedCity?.id?.toString() || ""}
|
||||||
|
onChange={handleCityChange}
|
||||||
|
displayEmpty
|
||||||
|
disabled={isLocked}
|
||||||
|
sx={{
|
||||||
|
height: "40px",
|
||||||
|
color: "white",
|
||||||
|
borderRadius: "4px",
|
||||||
|
...(noCitySelected && !isLocked
|
||||||
|
? {
|
||||||
|
backgroundColor: "#48989f",
|
||||||
|
"& .MuiOutlinedInput-notchedOutline": { border: "none" },
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
"& .MuiOutlinedInput-notchedOutline": {
|
||||||
|
borderColor: isLocked
|
||||||
|
? "rgba(255, 255, 255, 0.1)"
|
||||||
|
: "rgba(255, 255, 255, 0.3)",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
"&.Mui-disabled": {
|
||||||
|
color: "rgba(255, 255, 255, 0.5)",
|
||||||
|
WebkitTextFillColor: "rgba(255, 255, 255, 0.5)",
|
||||||
|
},
|
||||||
|
"&:hover .MuiOutlinedInput-notchedOutline": {
|
||||||
|
borderColor: isLocked
|
||||||
|
? "rgba(255, 255, 255, 0.1)"
|
||||||
|
: "rgba(255, 255, 255, 0.5)",
|
||||||
|
},
|
||||||
|
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
||||||
|
borderColor: "white",
|
||||||
|
},
|
||||||
|
"& .MuiSvgIcon-root": {
|
||||||
|
color: isLocked ? "rgba(255, 255, 255, 0.3)" : "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value="">
|
||||||
|
<Typography variant="body2">Выберите город</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
{currentCities.map((city) => (
|
||||||
|
<MenuItem key={city.id} value={city.id?.toString()}>
|
||||||
|
<Typography variant="body2">{city.name}</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="flex items-center gap-2">
|
<Box className="flex items-center gap-2">
|
||||||
<MapPin size={16} className={isLocked ? "text-gray-400" : "text-white"} />
|
<MapPin size={16} className={isLocked ? "text-gray-400" : "text-white"} />
|
||||||
<FormControl size="medium" sx={{ minWidth: 120 }}>
|
<FormControl size="medium" sx={{ minWidth: 120 }}>
|
||||||
<Select
|
{noCitySelected && !isLocked ? (
|
||||||
value={selectedCity?.id?.toString() || ""}
|
<Box
|
||||||
onChange={handleCityChange}
|
sx={{
|
||||||
displayEmpty
|
position: "relative",
|
||||||
disabled={isLocked}
|
borderRadius: "4px",
|
||||||
sx={{
|
padding: "2px",
|
||||||
height: "40px",
|
background: "linear-gradient(90deg, rgba(255,255,255,0.1), rgba(255,255,255,0.7), rgba(255,255,255,0.1), rgba(255,255,255,0.7), rgba(255,255,255,0.1))",
|
||||||
color: "white",
|
backgroundSize: "200% 100%",
|
||||||
"&.Mui-disabled": {
|
animation: `${borderSpin} 2.5s linear infinite`,
|
||||||
color: "rgba(255, 255, 255, 0.5)",
|
}}
|
||||||
WebkitTextFillColor: "rgba(255, 255, 255, 0.5)",
|
>
|
||||||
},
|
{selectElement}
|
||||||
"& .MuiOutlinedInput-notchedOutline": {
|
</Box>
|
||||||
borderColor: isLocked
|
) : (
|
||||||
? "rgba(255, 255, 255, 0.1)"
|
selectElement
|
||||||
: "rgba(255, 255, 255, 0.3)",
|
)}
|
||||||
},
|
|
||||||
"&:hover .MuiOutlinedInput-notchedOutline": {
|
|
||||||
borderColor: isLocked
|
|
||||||
? "rgba(255, 255, 255, 0.1)"
|
|
||||||
: "rgba(255, 255, 255, 0.5)",
|
|
||||||
},
|
|
||||||
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
|
||||||
borderColor: "white",
|
|
||||||
},
|
|
||||||
"& .MuiSvgIcon-root": {
|
|
||||||
color: isLocked ? "rgba(255, 255, 255, 0.3)" : "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem value="">
|
|
||||||
<Typography variant="body2">Выберите город</Typography>
|
|
||||||
</MenuItem>
|
|
||||||
{currentCities.map((city) => (
|
|
||||||
<MenuItem key={city.id} value={city.id?.toString()}>
|
|
||||||
<Typography variant="body2">{city.name}</Typography>
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user