Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

Recent changes to the geojson.io application. Entries are grouped by ship date.

## 2026-06-04

- **Delete all features:** Adds a delete all button to the feature editor header. Operation is undoable via Ctrl-Z. ([#993](https://github.com/mapbox/geojson.io/pull/993))
- **Numpad Delete key fix:** The Delete key on numpads (numlock off) now correctly deletes selected features. ([#993](https://github.com/mapbox/geojson.io/pull/993))
- **Map URL coordinate precision:** Increased lat/lng precision in the `?map=` query param from 2 to 4 decimal places. ([#993](https://github.com/mapbox/geojson.io/pull/993))
- **GeoJSON editor fold gutter:** Adds a fold gutter to the GeoJSON editor panel for collapsing nested objects and arrays. ([#993](https://github.com/mapbox/geojson.io/pull/993))
- **GL JS Update:** Bump to `v3.24.0` [#992](https://github.com/mapbox/geojson.io/pull/992)
- **Replace Standard Light and Dark with Classic Light/Dark:** Removes light and dark themes based on Mapbox Standard for `light-v11` and `dark-v11` for faster performance by default. [#992](https://github.com/mapbox/geojson.io/pull/992)
- **TMS Support for inverted -y**: Adds support for TMS raster tileset URLs which use an inverted y in the url `...{z}/{x}/{-y}.png` [#991](https://github.com/mapbox/geojson.io/pull/991)

## 2026-06-01

- **Add Mobile Meta viewport Tag** - Fix for browsers properly handling mobile viewports.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { CaretDownIcon, Crosshair1Icon } from '@radix-ui/react-icons';
import {
CaretDownIcon,
Crosshair1Icon,
TrashIcon
} from '@radix-ui/react-icons';
import {
Button,
StyledLabelSpan,
Expand All @@ -13,6 +17,11 @@ import { usePersistence } from 'app/lib/persistence/context';
import { useZoomTo } from 'app/hooks/use_zoom_to';
import { Popover as P, Tooltip } from 'radix-ui';
import type { FeatureMap } from 'types';
import { useCallback } from 'react';
import { dataAtom } from 'state/jotai';
import { useAtomCallback } from 'jotai/utils';
import { EMPTY_MOMENT } from 'app/lib/persistence/moment';
import { toast } from 'react-hot-toast';

function PreviewProperty({ featureMap }: { featureMap: FeatureMap }) {
const rep = usePersistence();
Expand Down Expand Up @@ -53,6 +62,37 @@ export function FeatureEditorFolderHeader({
const rep = usePersistence();
const [meta] = rep.useMetadata();
const zoomTo = useZoomTo();
const transact = rep.useTransact();

const handleDeleteAll = useAtomCallback(
useCallback(
async (get) => {
const data = get(dataAtom);
const allIds = Array.from(data.featureMap.keys());
if (allIds.length === 0) return;
await transact({
...EMPTY_MOMENT,
note: 'Deleted all features',
deleteFeatures: allIds
});
// UI notif with fallback note
toast.success(
(t) => (
<div>
<p>
<strong>All features deleted.</strong>
</p>
<p>
Use <pre style={{ display: 'inline' }}>Ctrl-z</pre> to undo.
</p>
</div>
),
{ duration: 2000 }
);
},
[transact]
)
);

const handleZoomToAll = () => {
const allFeatures = Array.from(featureMap.values());
Expand All @@ -75,18 +115,34 @@ export function FeatureEditorFolderHeader({
<PreviewProperty featureMap={featureMap} />
</StyledPopoverContent>
</P.Root>
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Button
onClick={handleZoomToAll}
size="xs"
disabled={featureMap.size === 0}
>
<Crosshair1Icon />
</Button>
</Tooltip.Trigger>
<TContent side="bottom">Zoom to all</TContent>
</Tooltip.Root>
<div>
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Button
className="mr-3"
onClick={handleZoomToAll}
size="xs"
disabled={featureMap.size === 0}
>
<Crosshair1Icon />
</Button>
</Tooltip.Trigger>
<TContent side="bottom">Zoom to all</TContent>
</Tooltip.Root>

<Tooltip.Root>
<Tooltip.Trigger asChild>
<Button
onClick={handleDeleteAll}
size="xs"
disabled={featureMap.size === 0}
>
<TrashIcon />
</Button>
</Tooltip.Trigger>
<TContent side="bottom">Delete all features</TContent>
</Tooltip.Root>
</div>
</div>
);
}
2 changes: 2 additions & 0 deletions app/components/panels/geojson_editor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { EditorState, Transaction } from '@codemirror/state';
import { EditorView, lineNumbers } from '@codemirror/view';
import { foldGutter } from '@codemirror/language';
import isEqual from 'lodash/isEqual';
import debounce from 'lodash/debounce';
import { Button } from 'app/components/elements';
Expand Down Expand Up @@ -76,6 +77,7 @@ export function GeoJSONEditor({
checker,
lintGutter(),
lineNumbers(),
foldGutter(),
preventDrop
],
[preventDrop]
Expand Down
2 changes: 1 addition & 1 deletion app/hooks/use_map_keybindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export function useMapKeybindings() {
e.preventDefault();
void onDelete();
},
keybindingOptions,
{ ...keybindingOptions, useKey: true },
[onDelete]
);
}
8 changes: 4 additions & 4 deletions app/lib/parse_map_param.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,16 @@ describe('serializeMapParam', () => {
).toBe('9.12/40/-73');
});

it('rounds lat/lng to 2 decimal places', () => {
it('rounds lat/lng to 5 decimal places', () => {
expect(
serializeMapParam({
zoom: 10,
lat: 40.12789,
lng: -73.98654,
lat: 40.12789817,
lng: -73.9865419238,
bearing: 0,
pitch: 0
})
).toBe('10/40.13/-73.99');
).toBe('10/40.1279/-73.98654');
});

it('rounds bearing to 1 decimal place', () => {
Expand Down
4 changes: 2 additions & 2 deletions app/lib/parse_map_param.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ export function serializeMapParam(camera: CameraPosition): string {

const parts: number[] = [
round(camera.zoom, 2),
round(camera.lat, 2),
round(camera.lng, 2)
round(camera.lat, 5),
round(camera.lng, 5)
];

if (bearing !== 0 || pitch !== 0) {
Expand Down
Loading