feat: use FileSystemFileHandle to modify a file on the local filesystem (#965)

## Launch Checklist

<!-- Thanks for the PR! Feel free to add or remove items from the
checklist. -->


 - [x] Briefly describe the changes in this PR.
 - [x] Link to related issues.
- [x] Include before/after visuals or gifs if this PR includes visual
changes.
 - [ ] Write tests for all new functionality.
 - [x] Add an entry to `CHANGELOG.md` under the `## main` section.

## Changes

- This pull request makes use of the FileSystemFileHandle API to modify
a local file. No need to download it - just click save.
- I don't know how to cover this functionality by tests so I need
directions in case test coverage is required.
- The pull request adds
[@types/wicg-file-system-access](https://www.npmjs.com/package/@types/wicg-file-system-access)
as a new dev dependency which I am not really pleased about but can't
think of a way around it.

## Known Limitations

- The used File API is only available in when accessed from https or on
localhost.
- There is no visual indicator that the file has been saved. Previously
the browser showed it as a new download.

## Issue

- https://github.com/maplibre/maputnik/issues/964

## Screenshots

### Menu
<img
src="https://github.com/user-attachments/assets/dfcfc5c2-0019-4857-ba26-224b5598fc11"
/>

### Open modal
<img
src="https://github.com/user-attachments/assets/4e1293e8-16b6-4b86-925b-3bebb49d8ca6"
height="200px" />

### Save modal
<img
src="https://github.com/user-attachments/assets/4f10e2c0-2dd3-4726-a613-30e0406619b0"
height="200px" />

---------

Co-authored-by: Harel M <harel.mazor@gmail.com>
This commit is contained in:
Joscha
2025-01-09 17:54:26 +01:00
committed by GitHub
parent c6174a57d9
commit d50ea76347
14 changed files with 170 additions and 97 deletions

View File

@@ -129,6 +129,7 @@ type AppState = {
export: boolean
debug: boolean
}
fileHandle: FileSystemFileHandle | null
}
export default class App extends React.Component<any, AppState> {
@@ -284,6 +285,7 @@ export default class App extends React.Component<any, AppState> {
openlayersDebugOptions: {
debugToolbox: false,
},
fileHandle: null,
}
this.layerWatcher = new LayerWatcher({
@@ -611,7 +613,8 @@ export default class App extends React.Component<any, AppState> {
}
}
openStyle = (styleObj: StyleSpecification & {id: string}) => {
openStyle = (styleObj: StyleSpecification & {id: string}, fileHandle: FileSystemFileHandle | null) => {
this.setState({fileHandle: fileHandle});
styleObj = this.setDefaultValues(styleObj)
this.onStyleChanged(styleObj)
}
@@ -847,6 +850,10 @@ export default class App extends React.Component<any, AppState> {
this.setModal(modalName, !this.state.isOpen[modalName]);
}
onSetFileHandle(fileHandle: FileSystemFileHandle | null) {
this.setState({fileHandle: fileHandle});
}
onChangeOpenlayersDebug = (key: keyof AppState["openlayersDebugOptions"], value: boolean) => {
this.setState({
openlayersDebugOptions: {
@@ -949,11 +956,14 @@ export default class App extends React.Component<any, AppState> {
onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.export}
onOpenToggle={this.toggleModal.bind(this, 'export')}
fileHandle={this.state.fileHandle}
onSetFileHandle={this.onSetFileHandle}
/>
<ModalOpen
isOpen={this.state.isOpen.open}
onStyleOpen={this.openStyle}
onOpenToggle={this.toggleModal.bind(this, 'open')}
fileHandle={this.state.fileHandle}
/>
<ModalSources
mapStyle={this.state.mapStyle}