feat: implement resizable public wiki sidebar with improved content fetching, map zoom limits, and layout adjustments
Build and Release / release (push) Successful in 36s

This commit is contained in:
2026-05-20 18:02:10 +07:00
parent 8c0bff8082
commit 7e025fb449
9 changed files with 191 additions and 29 deletions
+67 -8
View File
@@ -8,7 +8,7 @@ import TimelineBar from "@/uhm/components/ui/TimelineBar";
import { fetchEntities, type Entity } from "@/uhm/api/entities"; import { fetchEntities, type Entity } from "@/uhm/api/entities";
import { fetchGeometriesByBBox } from "@/uhm/api/geometries"; import { fetchGeometriesByBBox } from "@/uhm/api/geometries";
import { ApiError } from "@/uhm/api/http"; import { ApiError } from "@/uhm/api/http";
import { fetchWikiBySlug, searchWikisByTitle, type Wiki } from "@/uhm/api/wikis"; import { fetchWikiBySlug, getContentByVersionWikiId, searchWikisByTitle, type Wiki } from "@/uhm/api/wikis";
import { import {
BACKGROUND_LAYER_OPTIONS, BACKGROUND_LAYER_OPTIONS,
type BackgroundLayerId, type BackgroundLayerId,
@@ -88,6 +88,34 @@ export default function Page() {
const [linkEntityPopup, setLinkEntityPopup] = useState<LinkEntityPopupState | null>(null); const [linkEntityPopup, setLinkEntityPopup] = useState<LinkEntityPopupState | null>(null);
const [entityFocusToken, setEntityFocusToken] = useState(0); const [entityFocusToken, setEntityFocusToken] = useState(0);
const [sidebarWidth, setSidebarWidth] = useState<number>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("public-wiki-sidebar-width");
if (saved) {
const parsed = parseInt(saved, 10);
if (!isNaN(parsed) && parsed >= 320 && parsed <= 800) {
return parsed;
}
}
}
return 420;
});
const [isLargeScreen, setIsLargeScreen] = useState(false);
useEffect(() => {
if (typeof window === "undefined") return;
const handleResize = () => {
setIsLargeScreen(window.innerWidth >= 1024);
};
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
const maxDragWidth = typeof window !== "undefined"
? Math.min(800, window.innerWidth - 340)
: 800;
const timelineFetchRequestRef = useRef(0); const timelineFetchRequestRef = useRef(0);
const hoverHideTimerRef = useRef<number | null>(null); const hoverHideTimerRef = useRef<number | null>(null);
const hoverPopupHoveredRef = useRef(false); const hoverPopupHoveredRef = useRef(false);
@@ -339,7 +367,7 @@ export default function Page() {
selectEntity(onlyEntityId, { selectEntity(onlyEntityId, {
sourceFeatureId: selectedFeatureIds[0], sourceFeatureId: selectedFeatureIds[0],
focusMap: false, focusMap: true,
selectGeometry: false, selectGeometry: false,
}); });
}, [activeEntityId, relations.geometryEntityIds, selectEntity, selectedFeatureIds]); }, [activeEntityId, relations.geometryEntityIds, selectEntity, selectedFeatureIds]);
@@ -386,6 +414,8 @@ export default function Page() {
}; };
}, [linkEntityPopup]); }, [linkEntityPopup]);
const cachedWiki = activeWikiSlug ? (wikiCache[activeWikiSlug] as Wiki & { __fetched?: boolean }) : undefined;
useEffect(() => { useEffect(() => {
if (!activeWikiSlug) { if (!activeWikiSlug) {
setIsActiveWikiLoading(false); setIsActiveWikiLoading(false);
@@ -393,10 +423,13 @@ export default function Page() {
return; return;
} }
const cached = wikiCache[activeWikiSlug] || relations.wikiBySlug[activeWikiSlug] || null; if (cachedWiki && (cachedWiki.__fetched || cachedWiki.id === "__not_found__")) {
if (cached?.content) {
setIsActiveWikiLoading(false); setIsActiveWikiLoading(false);
if (cachedWiki.id === "__not_found__") {
setActiveWikiError("Không tìm thấy wiki cho entity đã chọn.");
} else {
setActiveWikiError(null); setActiveWikiError(null);
}
return; return;
} }
@@ -407,9 +440,30 @@ export default function Page() {
try { try {
const row = await fetchWikiBySlug(activeWikiSlug); const row = await fetchWikiBySlug(activeWikiSlug);
if (disposed) return; if (disposed) return;
if (row) { if (row) {
setWikiCache((prev) => ({ ...prev, [activeWikiSlug]: row })); let versionContent = row.content;
try {
if (row.content_sample?.[0]?.id) {
const res = await getContentByVersionWikiId(row.content_sample[0].id);
if (res?.data?.content) {
versionContent = res.data.content;
}
}
} catch (err) {
console.error("Failed to fetch version content:", err);
}
if (disposed) return;
setWikiCache((prev) => ({
...prev,
[activeWikiSlug]: { ...row, content: versionContent, __fetched: true } as any,
}));
} else { } else {
setWikiCache((prev) => ({
...prev,
[activeWikiSlug]: { id: "__not_found__", project_id: "" },
}));
setActiveWikiError("Không tìm thấy wiki cho entity đã chọn."); setActiveWikiError("Không tìm thấy wiki cho entity đã chọn.");
} }
} catch (err) { } catch (err) {
@@ -423,7 +477,7 @@ export default function Page() {
return () => { return () => {
disposed = true; disposed = true;
}; };
}, [activeWikiSlug, relations.wikiBySlug, wikiCache]); }, [activeWikiSlug, cachedWiki]);
const handleWikiLinkRequest = useCallback(async ({ slug, rect }: { slug: string; rect: DOMRect }) => { const handleWikiLinkRequest = useCallback(async ({ slug, rect }: { slug: string; rect: DOMRect }) => {
const linkedEntityIds = relations.wikiEntityIdsBySlug[slug] || []; const linkedEntityIds = relations.wikiEntityIdsBySlug[slug] || [];
@@ -482,7 +536,7 @@ export default function Page() {
highlightFeatures={activeEntityGeometries} highlightFeatures={activeEntityGeometries}
focusFeatureCollection={activeEntityGeometries} focusFeatureCollection={activeEntityGeometries}
focusRequestKey={entityFocusToken} focusRequestKey={entityFocusToken}
focusPadding={activeEntityId ? { top: 84, right: 500, bottom: 116, left: 84 } : { top: 84, right: 84, bottom: 116, left: 84 }} focusPadding={activeEntityId && isLargeScreen ? { top: 84, right: sidebarWidth + 80, bottom: 116, left: 84 } : { top: 84, right: 84, bottom: 116, left: 84 }}
/> />
) : ( ) : (
<div className="h-screen w-full bg-[#0b1220]" /> <div className="h-screen w-full bg-[#0b1220]" />
@@ -496,6 +550,7 @@ export default function Page() {
isLoading={isTimelineLoading} isLoading={isTimelineLoading}
disabled={false} disabled={false}
statusText={timelineStatus} statusText={timelineStatus}
style={activeEntityId && isLargeScreen ? { right: `${sidebarWidth + 32}px` } : undefined}
/> />
<div className="absolute left-4 top-4 z-20 w-[280px] max-w-[calc(100vw-2rem)] overflow-hidden rounded-xl border border-white/10 bg-slate-950/92 shadow-xl backdrop-blur"> <div className="absolute left-4 top-4 z-20 w-[280px] max-w-[calc(100vw-2rem)] overflow-hidden rounded-xl border border-white/10 bg-slate-950/92 shadow-xl backdrop-blur">
@@ -632,7 +687,7 @@ export default function Page() {
) : null} ) : null}
{activeEntity ? ( {activeEntity ? (
<aside className="absolute bottom-4 right-4 top-4 z-20 w-[420px] max-w-[calc(100vw-2rem)]"> <aside className="absolute bottom-4 right-4 top-4 z-20 max-w-[calc(100vw-2rem)]">
<PublicWikiSidebar <PublicWikiSidebar
entity={activeEntity} entity={activeEntity}
wiki={activeWiki} wiki={activeWiki}
@@ -643,8 +698,12 @@ export default function Page() {
setActiveWikiSlug(null); setActiveWikiSlug(null);
setActiveWikiError(null); setActiveWikiError(null);
setLinkEntityPopup(null); setLinkEntityPopup(null);
setSelectedFeatureIds([]);
}} }}
onWikiLinkRequest={handleWikiLinkRequest} onWikiLinkRequest={handleWikiLinkRequest}
sidebarWidth={sidebarWidth}
onSidebarWidthChange={setSidebarWidth}
maxDragWidth={maxDragWidth}
/> />
</aside> </aside>
) : null} ) : null}
+2 -2
View File
@@ -61,13 +61,13 @@ export default function LandingPage() {
<div className="fixed inset-0 bg-gradient-to-b from-[#FDFBF7]/80 via-[#FDFBF7]/70 to-[#FDFBF7]/90 -z-10 pointer-events-none"></div> <div className="fixed inset-0 bg-gradient-to-b from-[#FDFBF7]/80 via-[#FDFBF7]/70 to-[#FDFBF7]/90 -z-10 pointer-events-none"></div>
{/* --- HEADER NAVBAR --- */} {/* --- HEADER NAVBAR --- */}
<header className="fixed top-0 w-full px-6 py-4 flex justify-between items-center backdrop-blur-sm bg-[#FDFBF7]/70 z-50 border-b border-[#A88B4C]/20"> <header className="sticky top-0 w-full px-6 py-4 flex justify-between items-center backdrop-blur-sm bg-[#FDFBF7]/70 z-40 border-b border-[#A88B4C]/20">
<div className="text-xl font-bold tracking-widest text-[#2D3A3A] uppercase"> <div className="text-xl font-bold tracking-widest text-[#2D3A3A] uppercase">
<span className="text-[#A88B4C]">Geo</span>History <span className="text-[#A88B4C]">Geo</span>History
</div> </div>
</header> </header>
<main className="max-w-6xl mx-auto px-6 pt-32 pb-24 flex flex-col gap-32 w-full relative"> <main className="max-w-6xl mx-auto px-6 pt-8 pb-24 flex flex-col gap-32 w-full relative">
{/* --- PHẦN 1: GIỚI THIỆU TỔNG QUAN --- */} {/* --- PHẦN 1: GIỚI THIỆU TỔNG QUAN --- */}
<section className="min-h-[70vh] flex flex-col justify-center relative"> <section className="min-h-[70vh] flex flex-col justify-center relative">
<h1 className="text-5xl md:text-7xl font-black leading-tight tracking-tight mb-6"> <h1 className="text-5xl md:text-7xl font-black leading-tight tracking-tight mb-6">
+1 -1
View File
@@ -34,7 +34,7 @@ export default function AdminLayout({
? "ml-0" ? "ml-0"
: isExpanded || isHovered : isExpanded || isHovered
? "lg:ml-[290px]" ? "lg:ml-[290px]"
: "lg:ml-[0px]"; : "lg:ml-[88px]";
return ( return (
<div className="min-h-screen xl:flex"> <div className="min-h-screen xl:flex">
+2 -2
View File
@@ -446,8 +446,8 @@ export function buildPathArrowGeometry(coords: [number, number][]): Geometry | n
if (bodyPoints.length < 2) return null; if (bodyPoints.length < 2) return null;
const tailWidth = clampNumber(totalLength * 0.005, 8000, 40000); const tailWidth = clampNumber(totalLength * 0.02, 5, 40000);
const shoulderWidth = clampNumber(totalLength * 0.015, 18000, 100000); const shoulderWidth = clampNumber(totalLength * 0.1, 10, 100000);
const headWidth = shoulderWidth * 2.0; const headWidth = shoulderWidth * 2.0;
const leftBody: ProjectedPoint[] = []; const leftBody: ProjectedPoint[] = [];
+7 -3
View File
@@ -72,6 +72,8 @@ export function useMapSync({
const fitToDraftBoundsRef = useRef(fitToDraftBounds); const fitToDraftBoundsRef = useRef(fitToDraftBounds);
const highlightFeaturesRef = useRef<FeatureCollection | null>(highlightFeatures || null); const highlightFeaturesRef = useRef<FeatureCollection | null>(highlightFeatures || null);
const imageOverlayRef = useRef<MapImageOverlay | null>(imageOverlay || null); const imageOverlayRef = useRef<MapImageOverlay | null>(imageOverlay || null);
const focusFeatureCollectionRef = useRef<FeatureCollection | null | undefined>(focusFeatureCollection);
const focusPaddingRef = useRef<number | maplibregl.PaddingOptions | undefined>(focusPadding);
const fitBoundsAppliedRef = useRef(false); const fitBoundsAppliedRef = useRef(false);
@@ -85,6 +87,8 @@ export function useMapSync({
useEffect(() => { fitToDraftBoundsRef.current = fitToDraftBounds; }, [fitToDraftBounds]); useEffect(() => { fitToDraftBoundsRef.current = fitToDraftBounds; }, [fitToDraftBounds]);
useEffect(() => { highlightFeaturesRef.current = highlightFeatures || null; }, [highlightFeatures]); useEffect(() => { highlightFeaturesRef.current = highlightFeatures || null; }, [highlightFeatures]);
useEffect(() => { imageOverlayRef.current = imageOverlay || null; }, [imageOverlay]); useEffect(() => { imageOverlayRef.current = imageOverlay || null; }, [imageOverlay]);
useEffect(() => { focusFeatureCollectionRef.current = focusFeatureCollection; }, [focusFeatureCollection]);
useEffect(() => { focusPaddingRef.current = focusPadding; }, [focusPadding]);
useEffect(() => { useEffect(() => {
fitBoundsAppliedRef.current = false; fitBoundsAppliedRef.current = false;
@@ -217,7 +221,7 @@ export function useMapSync({
useEffect(() => { useEffect(() => {
if (focusRequestKey === null || focusRequestKey === undefined) return; if (focusRequestKey === null || focusRequestKey === undefined) return;
const map = mapRef.current; const map = mapRef.current;
const target = focusFeatureCollection; const target = focusFeatureCollectionRef.current;
if (!target || !target.features.length) return; if (!target || !target.features.length) return;
if (!map) return; if (!map) return;
@@ -226,7 +230,7 @@ export function useMapSync({
const focus = () => { const focus = () => {
if (cancelled || mapRef.current !== map || !map.isStyleLoaded()) return; if (cancelled || mapRef.current !== map || !map.isStyleLoaded()) return;
fitMapToFeatureCollection(map, target, focusPadding, { fitMapToFeatureCollection(map, target, focusPaddingRef.current, {
duration: 550, duration: 550,
maxZoom: 10, maxZoom: 10,
pointZoom: 9, pointZoom: 9,
@@ -243,7 +247,7 @@ export function useMapSync({
cancelled = true; cancelled = true;
if (rafId !== null) cancelAnimationFrame(rafId); if (rafId !== null) cancelAnimationFrame(rafId);
}; };
}, [focusFeatureCollection, focusPadding, focusRequestKey, mapRef]); }, [focusRequestKey, mapRef]);
return { return {
applyDraftToMap, applyDraftToMap,
+7 -2
View File
@@ -12,6 +12,7 @@ type Props = {
statusText?: string | null; statusText?: string | null;
filterEnabled?: boolean; filterEnabled?: boolean;
onFilterEnabledChange?: (enabled: boolean) => void; onFilterEnabledChange?: (enabled: boolean) => void;
style?: React.CSSProperties;
}; };
export default function TimelineBar({ export default function TimelineBar({
@@ -24,6 +25,7 @@ export default function TimelineBar({
statusText, statusText,
filterEnabled, filterEnabled,
onFilterEnabledChange, onFilterEnabledChange,
style,
}: Props) { }: Props) {
const lower = FIXED_TIMELINE_START_YEAR; const lower = FIXED_TIMELINE_START_YEAR;
const upper = FIXED_TIMELINE_END_YEAR; const upper = FIXED_TIMELINE_END_YEAR;
@@ -58,6 +60,7 @@ export default function TimelineBar({
padding: "10px 12px", padding: "10px 12px",
color: "#e2e8f0", color: "#e2e8f0",
backdropFilter: "blur(2px)", backdropFilter: "blur(2px)",
...style,
}} }}
title={helperText || undefined} title={helperText || undefined}
> >
@@ -65,7 +68,9 @@ export default function TimelineBar({
style={{ style={{
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
gap: "10px", flexWrap: "wrap",
rowGap: "8px",
columnGap: "10px",
fontSize: "12px", fontSize: "12px",
}} }}
> >
@@ -129,7 +134,7 @@ export default function TimelineBar({
aria-label="Timeline year" aria-label="Timeline year"
style={{ style={{
flex: 1, flex: 1,
minWidth: 0, minWidth: "120px",
accentColor: "#22c55e", accentColor: "#22c55e",
cursor: effectiveDisabled ? "not-allowed" : "pointer", cursor: effectiveDisabled ? "not-allowed" : "pointer",
opacity: effectiveDisabled ? 0.6 : 1, opacity: effectiveDisabled ? 0.6 : 1,
+90 -5
View File
@@ -18,6 +18,9 @@ type Props = {
error?: string | null; error?: string | null;
onClose: () => void; onClose: () => void;
onWikiLinkRequest: (request: { slug: string; rect: DOMRect }) => void; onWikiLinkRequest: (request: { slug: string; rect: DOMRect }) => void;
sidebarWidth?: number;
onSidebarWidthChange?: (width: number) => void;
maxDragWidth?: number;
}; };
function escapeHtml(input: string): string { function escapeHtml(input: string): string {
@@ -30,9 +33,12 @@ function escapeHtml(input: string): string {
} }
function normalizeWikiContentToHtml(raw: string | null | undefined): string { function normalizeWikiContentToHtml(raw: string | null | undefined): string {
const value = String(raw || "").trim(); let value = String(raw || "").trim();
if (!value.length) return ""; if (!value.length) return "";
// Replace non-breaking spaces to allow text wrap
value = value.replaceAll("&nbsp;", " ").replaceAll("\u00a0", " ");
if (value[0] === "<") return value; if (value[0] === "<") return value;
return `<p>${escapeHtml(value).replace(/\n/g, "<br/>")}</p>`; return `<p>${escapeHtml(value).replace(/\n/g, "<br/>")}</p>`;
@@ -122,8 +128,52 @@ export default function PublicWikiSidebar({
error, error,
onClose, onClose,
onWikiLinkRequest, onWikiLinkRequest,
sidebarWidth,
onSidebarWidthChange,
maxDragWidth,
}: Props) { }: Props) {
const contentRootRef = useRef<HTMLDivElement | null>(null); const contentRootRef = useRef<HTMLDivElement | null>(null);
const [localWidth, setLocalWidth] = useState<number>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("public-wiki-sidebar-width");
if (saved) {
const parsed = parseInt(saved, 10);
if (!isNaN(parsed) && parsed >= 320 && parsed <= 800) {
return parsed;
}
}
}
return 420;
});
const width = sidebarWidth ?? localWidth;
const setWidth = onSidebarWidthChange ?? setLocalWidth;
const maxDragWidthLimit = maxDragWidth ?? 800;
const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
event.preventDefault();
const startX = event.clientX;
const startWidth = width;
const onMove = (e: PointerEvent) => {
const deltaX = e.clientX - startX;
const nextWidth = Math.max(320, Math.min(maxDragWidthLimit, startWidth - deltaX));
setWidth(nextWidth);
if (typeof window !== "undefined") {
localStorage.setItem("public-wiki-sidebar-width", String(nextWidth));
}
};
const onUp = () => {
window.removeEventListener("pointermove", onMove);
window.removeEventListener("pointerup", onUp);
};
window.addEventListener("pointermove", onMove);
window.addEventListener("pointerup", onUp);
};
const [activeHeadingId, setActiveHeadingId] = useState<string | null>(null); const [activeHeadingId, setActiveHeadingId] = useState<string | null>(null);
const processedWiki = useMemo(() => { const processedWiki = useMemo(() => {
if (!wiki) return { html: "", toc: [] as TocItem[] }; if (!wiki) return { html: "", toc: [] as TocItem[] };
@@ -187,7 +237,19 @@ export default function PublicWikiSidebar({
}, [onWikiLinkRequest, renderHtml]); }, [onWikiLinkRequest, renderHtml]);
return ( return (
<div className="flex h-full min-h-0 flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-xl dark:border-gray-800 dark:bg-gray-950"> <div
style={{ width: `${width}px` }}
className="relative flex h-full min-h-0 flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-xl dark:border-gray-800 dark:bg-gray-950"
>
{/* Drag Handle on the left edge */}
<div
onPointerDown={handlePointerDown}
className="absolute left-0 top-0 bottom-0 w-[6px] cursor-col-resize z-50 group select-none hover:bg-black/[0.03] dark:hover:bg-white/[0.02]"
title="Kéo để chỉnh kích thước"
>
{/* Visual drag line overlay */}
<div className="absolute left-[2px] top-0 bottom-0 w-[2px] bg-transparent group-hover:bg-brand-500/50 group-active:bg-brand-500 transition-colors" />
</div>
<div className="border-b border-gray-200 px-4 py-4 dark:border-gray-800"> <div className="border-b border-gray-200 px-4 py-4 dark:border-gray-800">
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="min-w-0"> <div className="min-w-0">
@@ -271,6 +333,11 @@ export default function PublicWikiSidebar({
height: auto; height: auto;
overflow-y: visible; overflow-y: visible;
padding: 18px 18px 22px; padding: 18px 18px 22px;
line-height: 1.6;
font-size: 14.5px;
word-wrap: break-word;
word-break: break-word;
overflow-wrap: break-word;
} }
.uhm-wiki-sidebar-view.ql-editor p { .uhm-wiki-sidebar-view.ql-editor p {
margin: 0 0 0.75em; margin: 0 0 0.75em;
@@ -317,16 +384,22 @@ export default function PublicWikiSidebar({
border: 1px solid rgba(226, 232, 240, 1); border: 1px solid rgba(226, 232, 240, 1);
border-radius: 10px; border-radius: 10px;
background: rgba(248, 250, 252, 1); background: rgba(248, 250, 252, 1);
overflow: auto; overflow-x: auto;
white-space: pre-wrap;
word-break: break-all;
} }
:is(.dark *) .uhm-wiki-sidebar-view.ql-editor pre { :is(.dark *) .uhm-wiki-sidebar-view.ql-editor pre {
border-color: rgba(51, 65, 85, 1); border-color: rgba(51, 65, 85, 1);
background: rgba(2, 6, 23, 0.4); background: rgba(2, 6, 23, 0.4);
} }
.uhm-wiki-sidebar-view.ql-editor img { .uhm-wiki-sidebar-view.ql-editor img {
max-width: 100%; display: block !important;
height: auto; max-width: 100% !important;
height: auto !important;
border-radius: 8px; border-radius: 8px;
float: none !important;
margin: 1.25em auto !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
} }
.uhm-wiki-sidebar-view.ql-editor a { .uhm-wiki-sidebar-view.ql-editor a {
text-decoration: underline; text-decoration: underline;
@@ -352,6 +425,18 @@ export default function PublicWikiSidebar({
:is(.dark *) .uhm-wiki-sidebar-view.ql-editor a[href="__missing__"] { :is(.dark *) .uhm-wiki-sidebar-view.ql-editor a[href="__missing__"] {
color: #f87171; color: #f87171;
} }
@media (max-width: 640px) {
.uhm-wiki-sidebar-view.ql-editor {
padding: 14px 14px 20px;
font-size: 13.5px;
}
.uhm-wiki-sidebar-view.ql-editor h1 {
font-size: 1.4em;
}
.uhm-wiki-sidebar-view.ql-editor h2 {
font-size: 1.2em;
}
}
`}</style> `}</style>
</div> </div>
); );
+1 -1
View File
@@ -1,7 +1,7 @@
export const PATH_ARROW_ICON_ID = "path-arrow-icon"; export const PATH_ARROW_ICON_ID = "path-arrow-icon";
export const MAP_MIN_ZOOM = 2; export const MAP_MIN_ZOOM = 2;
export const MAP_MAX_ZOOM = 10; export const MAP_MAX_ZOOM = 14;
export const RASTER_BASE_SOURCE_ID = "rasterBase"; export const RASTER_BASE_SOURCE_ID = "rasterBase";
export const RASTER_BASE_LAYER_ID = "raster-base-layer"; export const RASTER_BASE_LAYER_ID = "raster-base-layer";
+12 -3
View File
@@ -102,7 +102,7 @@ export function buildLineGeotypeLayers(
source: pathArrowSourceId, source: pathArrowSourceId,
filter: ["==", TYPE_MATCH_EXPR, style.typeId], filter: ["==", TYPE_MATCH_EXPR, style.typeId],
paint: { paint: {
"fill-color": statusColor(style.color), "fill-color": statusFillColor(style.color),
"fill-opacity": [ "fill-opacity": [
"case", "case",
SELECTED_EXPR, SELECTED_EXPR,
@@ -140,7 +140,7 @@ export function buildPolygonGeotypeLayers(
source: sourceId, source: sourceId,
filter: polygonFilter(style.typeId), filter: polygonFilter(style.typeId),
paint: { paint: {
"fill-color": statusColor(style.fillColor), "fill-color": statusFillColor(style.fillColor),
"fill-opacity": [ "fill-opacity": [
"case", "case",
SELECTED_EXPR, SELECTED_EXPR,
@@ -183,13 +183,22 @@ function statusStroke(normalColor: string): maplibregl.ExpressionSpecification {
return [ return [
"case", "case",
SELECTED_EXPR, SELECTED_EXPR,
SELECTED_STROKE, SELECTED_COLOR,
DRAFT_ENTITY_EXPR, DRAFT_ENTITY_EXPR,
DRAFT_STROKE, DRAFT_STROKE,
normalColor, normalColor,
]; ];
} }
function statusFillColor(normalColor: string): maplibregl.ExpressionSpecification {
return [
"case",
DRAFT_ENTITY_EXPR,
DRAFT_COLOR,
normalColor,
];
}
function lineFilter(typeId: string): maplibregl.ExpressionSpecification { function lineFilter(typeId: string): maplibregl.ExpressionSpecification {
return ["all", LINE_GEOMETRY_FILTER, ["==", TYPE_MATCH_EXPR, typeId]]; return ["all", LINE_GEOMETRY_FILTER, ["==", TYPE_MATCH_EXPR, typeId]];
} }