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>
|
||||
)}
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
<div className="w-full">
|
||||
<DataGrid
|
||||
|
||||
@@ -162,7 +162,9 @@ export const CarrierListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
<DataGrid
|
||||
rows={rows}
|
||||
|
||||
@@ -165,7 +165,9 @@ export const CityListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
<DataGrid
|
||||
rows={filteredRows}
|
||||
|
||||
@@ -117,7 +117,9 @@ export const CountryListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
<DataGrid
|
||||
rows={rows}
|
||||
|
||||
@@ -113,7 +113,9 @@ export const MediaListPage = observer(() => {
|
||||
return (
|
||||
<>
|
||||
<div className="w-full">
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
{canWriteMedia && ids.length > 0 && (
|
||||
<div className="flex justify-end mb-5 duration-300">
|
||||
|
||||
@@ -271,7 +271,9 @@ export const RouteListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
<DataGrid
|
||||
rows={rows}
|
||||
|
||||
@@ -182,7 +182,9 @@ export const SightListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
<DataGrid
|
||||
rows={rows}
|
||||
|
||||
@@ -299,7 +299,9 @@ export const SnapshotListPage = observer(() => {
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
<DataGrid
|
||||
rows={rows}
|
||||
|
||||
@@ -226,7 +226,9 @@ export const StationListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
<DataGrid
|
||||
rows={rows}
|
||||
|
||||
@@ -147,7 +147,9 @@ export const UserListPage = observer(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
<DataGrid
|
||||
rows={rows}
|
||||
|
||||
@@ -174,7 +174,9 @@ export const VehicleListPage = observer(() => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
{rows.length > 0 && (
|
||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||
)}
|
||||
|
||||
{canWriteVehicles && ids.length > 0 && (
|
||||
<div className="flex justify-end mb-5 duration-300">
|
||||
|
||||
@@ -6,11 +6,21 @@ import {
|
||||
SelectChangeEvent,
|
||||
Typography,
|
||||
Box,
|
||||
keyframes,
|
||||
} from "@mui/material";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { authStore, cityStore, selectedCityStore, snapshotStore, type City } from "@shared";
|
||||
import { MapPin } from "lucide-react";
|
||||
|
||||
const borderSpin = keyframes`
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 200% 50%;
|
||||
}
|
||||
`;
|
||||
|
||||
export const CitySelector: React.FC = observer(() => {
|
||||
const { selectedCity, setSelectedCity, isLocked } = selectedCityStore;
|
||||
const canLoadAllCities = authStore.isAdmin && authStore.canRead("cities");
|
||||
@@ -43,6 +53,8 @@ export const CitySelector: React.FC = observer(() => {
|
||||
})()
|
||||
: baseCities;
|
||||
|
||||
const noCitySelected = !selectedCity?.id;
|
||||
|
||||
const handleCityChange = (event: SelectChangeEvent<string>) => {
|
||||
const cityId = event.target.value;
|
||||
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 (
|
||||
<Box className="flex items-center gap-2">
|
||||
<MapPin size={16} className={isLocked ? "text-gray-400" : "text-white"} />
|
||||
<FormControl size="medium" sx={{ minWidth: 120 }}>
|
||||
<Select
|
||||
value={selectedCity?.id?.toString() || ""}
|
||||
onChange={handleCityChange}
|
||||
displayEmpty
|
||||
disabled={isLocked}
|
||||
sx={{
|
||||
height: "40px",
|
||||
color: "white",
|
||||
"&.Mui-disabled": {
|
||||
color: "rgba(255, 255, 255, 0.5)",
|
||||
WebkitTextFillColor: "rgba(255, 255, 255, 0.5)",
|
||||
},
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: isLocked
|
||||
? "rgba(255, 255, 255, 0.1)"
|
||||
: "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>
|
||||
{noCitySelected && !isLocked ? (
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
borderRadius: "4px",
|
||||
padding: "2px",
|
||||
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))",
|
||||
backgroundSize: "200% 100%",
|
||||
animation: `${borderSpin} 2.5s linear infinite`,
|
||||
}}
|
||||
>
|
||||
{selectElement}
|
||||
</Box>
|
||||
) : (
|
||||
selectElement
|
||||
)}
|
||||
</FormControl>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user