Skip to content

Commit b19bb3e

Browse files
committed
feat(ui): denser layer tiles, sheet polish, wider GeoJSON popups
- CSS: auto-fit layer grid, larger tiles/labels, less clipping on long names - LayerPackChipsScroller: restore two-row pack strip layout - loadGeoJsonLayer bindPopup uses shared popupLayout min/max width + test
1 parent 07a1f2e commit b19bb3e

5 files changed

Lines changed: 130 additions & 45 deletions

File tree

src/index.css

Lines changed: 90 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -860,20 +860,23 @@ a:hover {
860860
margin: 0;
861861
line-height: 1.45;
862862
white-space: normal !important;
863-
overflow-wrap: anywhere;
864-
word-break: break-word;
865-
min-width: 0;
866-
max-width: 100%;
863+
overflow-wrap: break-word;
864+
word-break: normal;
867865
box-sizing: border-box;
866+
/* Wider, viewport-safe card; width floor + ~420px preferred cap (matches LAYER_POPUP_*_WIDTH_PX) */
867+
width: min(420px, calc(100vw - 24px));
868+
min-width: min(320px, calc(100vw - 24px));
869+
max-width: min(420px, calc(100vw - 24px));
868870
}
869871

870872
.leaflet-popup.layer-popup-embed .leaflet-popup-tip-container {
871873
display: none;
872874
}
873875

874876
.layer-popup {
875-
min-width: 0;
876-
max-width: min(320px, calc(100vw - 24px));
877+
width: 100%;
878+
min-width: min(320px, calc(100vw - 24px));
879+
max-width: min(420px, calc(100vw - 24px));
877880
padding: 10px 12px;
878881
background: rgba(20, 20, 20, 0.55);
879882
backdrop-filter: blur(12px);
@@ -889,7 +892,7 @@ a:hover {
889892
font-size: 14px;
890893
font-weight: 700;
891894
line-height: 1.35;
892-
margin-bottom: 8px;
895+
margin-bottom: 6px;
893896
color: rgba(255, 255, 255, 0.98);
894897
word-break: break-word;
895898
}
@@ -901,11 +904,12 @@ a:hover {
901904
}
902905

903906
.layer-popup__row {
904-
display: flex;
905-
flex-direction: column;
906-
align-items: stretch;
907-
gap: 2px;
908-
margin-top: 8px;
907+
display: grid;
908+
grid-template-columns: minmax(0, 42%) minmax(0, 1fr);
909+
column-gap: 10px;
910+
row-gap: 2px;
911+
align-items: start;
912+
margin-top: 4px;
909913
line-height: 1.4;
910914
word-break: break-word;
911915
}
@@ -922,6 +926,10 @@ a:hover {
922926
text-transform: none;
923927
color: rgba(200, 210, 220, 0.68);
924928
margin: 0;
929+
min-width: 0;
930+
text-align: end;
931+
justify-self: end;
932+
max-width: 100%;
925933
}
926934

927935
.layer-popup__value {
@@ -932,8 +940,10 @@ a:hover {
932940
margin: 0;
933941
min-width: 0;
934942
white-space: normal;
935-
overflow-wrap: anywhere;
936-
word-break: break-word;
943+
overflow-wrap: break-word;
944+
word-break: normal;
945+
text-align: start;
946+
justify-self: stretch;
937947
}
938948

939949
.layer-popup__actions {
@@ -942,7 +952,7 @@ a:hover {
942952
flex-wrap: wrap;
943953
gap: 8px;
944954
justify-content: flex-start;
945-
margin-top: 10px;
955+
margin-top: 8px;
946956
padding-top: 2px;
947957
}
948958

@@ -3491,20 +3501,19 @@ input.unified-submit-name-input,
34913501
box-sizing: border-box;
34923502
}
34933503

3504+
.layer-packs-sheet-pack-scroller::-webkit-scrollbar {
3505+
display: none;
3506+
}
3507+
34943508
.layer-packs-sheet-pack-rows {
34953509
display: flex;
34963510
flex-direction: column;
3497-
align-items: stretch;
34983511
gap: 6px;
34993512
width: 100%;
35003513
min-width: 0;
35013514
box-sizing: border-box;
35023515
}
35033516

3504-
.layer-packs-sheet-pack-scroller::-webkit-scrollbar {
3505-
display: none;
3506-
}
3507-
35083517
.layer-packs-sheet-pack-row {
35093518
display: grid;
35103519
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
@@ -3519,6 +3528,8 @@ input.unified-submit-name-input,
35193528
display: flex;
35203529
flex-direction: row;
35213530
align-items: stretch;
3531+
flex: 1 1 auto;
3532+
width: 100%;
35223533
min-width: 0;
35233534
max-width: 100%;
35243535
padding: 0;
@@ -3670,17 +3681,29 @@ input.unified-submit-name-input,
36703681
--layer-tile-partial-border: rgba(243, 164, 41, 0.5);
36713682
--layer-tile-off-bg: rgba(255, 255, 255, 0.08);
36723683
--layer-tile-off-border: rgba(255, 255, 255, 0.2);
3684+
width: 100%;
3685+
min-width: 0;
3686+
box-sizing: border-box;
36733687
}
36743688

36753689
.layer-tiles-grid__list {
36763690
list-style: none;
36773691
display: grid;
3678-
/* Slightly wider min column = less truncation, fewer odd gaps. */
3679-
grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
3680-
gap: 5px 6px;
3692+
grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
3693+
grid-auto-rows: 1fr;
3694+
justify-content: stretch;
3695+
gap: 4px;
36813696
margin: 0;
36823697
padding: 0 0 2px;
36833698
align-content: start;
3699+
width: 100%;
3700+
min-width: 0;
3701+
box-sizing: border-box;
3702+
}
3703+
3704+
.layer-tiles-grid__item {
3705+
display: flex;
3706+
min-width: 0;
36843707
}
36853708

36863709
/* Compact card tiles: column layout, status rail, shadow (all rules below scoped to .layer-tiles-grid) */
@@ -3690,8 +3713,11 @@ input.unified-submit-name-input,
36903713
flex-direction: column;
36913714
align-items: stretch;
36923715
justify-content: flex-start;
3716+
flex: 1 1 auto;
3717+
width: 100%;
3718+
height: 100%;
36933719
/* Wins over global `.layer-tile { min-height: 0 !important }` */
3694-
min-height: 48px !important;
3720+
min-height: 50px !important;
36953721
padding: 0 !important;
36963722
text-align: start;
36973723
border-radius: 6px !important;
@@ -3767,18 +3793,18 @@ input.unified-submit-name-input,
37673793
display: flex;
37683794
align-items: center;
37693795
min-height: 0;
3770-
padding: 4px 5px 5px;
3796+
padding: 6px 7px 6px;
37713797
box-sizing: border-box;
37723798
}
37733799

37743800
.layer-tiles-grid .layer-tile__label {
37753801
display: -webkit-box;
37763802
-webkit-box-orient: vertical;
3777-
-webkit-line-clamp: 3;
3803+
-webkit-line-clamp: 4;
37783804
overflow: hidden;
3779-
font-size: 11.25px;
3805+
font-size: 12.5px;
37803806
font-weight: 600;
3781-
line-height: 1.2;
3807+
line-height: 1.25;
37823808
padding: 0;
37833809
text-align: start;
37843810
word-break: break-word;
@@ -3830,11 +3856,6 @@ input.unified-submit-name-input,
38303856
box-sizing: border-box;
38313857
}
38323858

3833-
/* Predictable but shorter menu — large min-height left obvious empty area below the tile grid. */
3834-
.layer-packs-sheet.layer-packs-sheet--size-stable {
3835-
min-height: min(300px, 52vh);
3836-
}
3837-
38383859
@media (min-width: 901px) {
38393860
.layer-packs-sheet {
38403861
border-radius: 14px;
@@ -4163,6 +4184,42 @@ input.unified-submit-name-input,
41634184
border-bottom: none;
41644185
}
41654186

4187+
.legend-tray__row--with-classes .legend-tray__row-label {
4188+
font-weight: 700;
4189+
}
4190+
4191+
.legend-tray__class-list {
4192+
list-style: none;
4193+
margin: 6px 0 0;
4194+
padding: 0;
4195+
padding-inline-start: 14px;
4196+
border-inline-start: 2px solid rgba(255, 255, 255, 0.1);
4197+
display: flex;
4198+
flex-direction: column;
4199+
gap: 2px;
4200+
width: 100%;
4201+
box-sizing: border-box;
4202+
}
4203+
4204+
.legend-tray__class-row {
4205+
display: flex;
4206+
flex-direction: column;
4207+
align-items: flex-end;
4208+
text-align: right;
4209+
font-size: 12.5px;
4210+
line-height: 1.35;
4211+
padding: 3px 0;
4212+
}
4213+
4214+
.legend-tray__row-inner--class {
4215+
font-weight: 400;
4216+
}
4217+
4218+
.legend-tray__class-label {
4219+
font-weight: 500;
4220+
color: rgba(255, 255, 255, 0.92);
4221+
}
4222+
41664223
.legend-tray__row-label {
41674224
font-weight: 600;
41684225
}

src/map/layers/loaders/loadGeoJsonLayer.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ vi.mock("leaflet", () => ({
1616
},
1717
}));
1818

19+
import { LAYER_POPUP_MAX_WIDTH_PX, LAYER_POPUP_MIN_WIDTH_PX } from "../popupLayout";
1920
import { loadGeoJsonLayer } from "./loadGeoJsonLayer";
2021

2122
const sampleFeatureCollection: FeatureCollection = {
@@ -133,6 +134,35 @@ describe("loadGeoJsonLayer", () => {
133134
expect(html).toContain("data-layer-popup-cta");
134135
});
135136

137+
it("binds popups with minWidth/maxWidth LAYER_POPUP_*_WIDTH_PX for readable width", async () => {
138+
const map = { addTo: vi.fn() } as unknown as import("leaflet").Map;
139+
let bindPopupMock: ReturnType<typeof vi.fn> | undefined;
140+
geoJSONMock.mockImplementation((data: FeatureCollection, options?: GeoJSONOptions) => {
141+
if (options?.onEachFeature && data.type === "FeatureCollection") {
142+
for (const feature of data.features) {
143+
bindPopupMock = vi.fn();
144+
const leafletFeature = { bindPopup: bindPopupMock, on: vi.fn() };
145+
options.onEachFeature(feature, leafletFeature as never);
146+
}
147+
}
148+
return { addTo: vi.fn().mockReturnThis() };
149+
});
150+
151+
await loadGeoJsonLayer({
152+
map,
153+
urls: { geojsonUrl: "https://example.com/x.geojson" },
154+
ui: { popup: { fields: [{ key: "name" }] } },
155+
});
156+
157+
expect(bindPopupMock).toHaveBeenCalledWith(
158+
expect.anything(),
159+
expect.objectContaining({
160+
minWidth: LAYER_POPUP_MIN_WIDTH_PX,
161+
maxWidth: LAYER_POPUP_MAX_WIDTH_PX,
162+
}),
163+
);
164+
});
165+
136166
it("stops DOM propagation on feature click when a popup is bound", async () => {
137167
const map = { addTo: vi.fn() } as unknown as import("leaflet").Map;
138168
let leafletOn: ReturnType<typeof vi.fn> | undefined;

src/map/layers/loaders/loadGeoJsonLayer.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import L from "leaflet";
44
import type { LoadedLayer, LoadLayerArgs } from "../types";
55
import { layerUiDeclaresPopupFields } from "../popupModel";
66
import { popupContentFromUi } from "../popupContent";
7+
import { LAYER_POPUP_MAX_WIDTH_PX, LAYER_POPUP_MIN_WIDTH_PX } from "../popupLayout";
78

89
function geoJsonLayerInteractive(args: LoadLayerArgs): boolean {
910
if (args.geojsonInteractive === false) return false;
@@ -37,7 +38,11 @@ export async function loadGeoJsonLayer(args: LoadLayerArgs): Promise<LoadedLayer
3738
});
3839
const initial = getPopupContent();
3940
if (initial) {
40-
leafletFeature.bindPopup(getPopupContent, { className: "layer-popup-embed", maxWidth: 320 });
41+
leafletFeature.bindPopup(getPopupContent, {
42+
className: "layer-popup-embed",
43+
minWidth: LAYER_POPUP_MIN_WIDTH_PX,
44+
maxWidth: LAYER_POPUP_MAX_WIDTH_PX,
45+
});
4146
leafletFeature.on("click", (e: LeafletMouseEvent) => {
4247
if (e.originalEvent) {
4348
L.DomEvent.stopPropagation(e.originalEvent);

src/pages/MapPage/LayerPackChipsScroller.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export default function LayerPackChipsScroller({
1717
}: Props) {
1818
const packs = registry?.packs ?? [];
1919
if (packs.length === 0) return null;
20-
2120
const midpoint = Math.ceil(packs.length / 2);
2221
const rowA = packs.slice(0, midpoint);
2322
const rowB = packs.slice(midpoint);
@@ -56,15 +55,9 @@ export default function LayerPackChipsScroller({
5655
role="region"
5756
aria-label="בחירת חבילת שכבות"
5857
>
59-
<div className="layer-packs-sheet-pack-rows">
60-
<div className="layer-packs-sheet-pack-row" role="presentation">
61-
{rowA.map(renderChip)}
62-
</div>
63-
{rowB.length > 0 ? (
64-
<div className="layer-packs-sheet-pack-row" role="presentation">
65-
{rowB.map(renderChip)}
66-
</div>
67-
) : null}
58+
<div className="layer-packs-sheet-pack-rows" role="presentation">
59+
<div className="layer-packs-sheet-pack-row">{rowA.map(renderChip)}</div>
60+
{rowB.length > 0 ? <div className="layer-packs-sheet-pack-row">{rowB.map(renderChip)}</div> : null}
6861
</div>
6962
</div>
7063
);

src/pages/MapPage/LayerPacksSheet.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default function LayerPacksSheet({
6060
>
6161
<div
6262
ref={dialogRef}
63-
className="layer-packs-sheet layer-packs-sheet--size-stable"
63+
className="layer-packs-sheet"
6464
role="dialog"
6565
aria-modal="true"
6666
aria-labelledby={labelId}

0 commit comments

Comments
 (0)