import React, { type FormEvent } from "react"; import { MdFileUpload } from "react-icons/md"; import { MdAddCircleOutline } from "react-icons/md"; import { Trans, type WithTranslation, withTranslation } from "react-i18next"; import ModalLoading from "./ModalLoading"; import Modal from "./Modal"; import InputButton from "../InputButton"; import InputUrl from "../InputUrl"; import style from "../../libs/style"; import publicStyles from "../../config/styles.json"; type PublicStyleProps = { url: string thumbnailUrl: string title: string onSelect(...args: unknown[]): unknown }; class PublicStyle extends React.Component { render() { return
this.props.onSelect(this.props.url)} >
{this.props.title}
; } } type ModalOpenInternalProps = { isOpen: boolean onOpenToggle(): void onStyleOpen(...args: unknown[]): unknown fileHandle: FileSystemFileHandle | null } & WithTranslation; type ModalOpenState = { styleUrl: string error?: string | null activeRequest?: any activeRequestUrl?: string | null }; class ModalOpenInternal extends React.Component { constructor(props: ModalOpenInternalProps) { super(props); this.state = { styleUrl: "" }; } clearError() { this.setState({ error: null }); } onCancelActiveRequest(e: Event) { // Else the click propagates to the underlying modal if (e) e.stopPropagation(); if (this.state.activeRequest) { this.state.activeRequest.abort(); this.setState({ activeRequest: null, activeRequestUrl: null }); } } onStyleSelect = (styleUrl: string) => { this.clearError(); let canceled: boolean = false; fetch(styleUrl, { mode: "cors", credentials: "same-origin" }) .then(function (response) { return response.json(); }) .then((body) => { if (canceled) { return; } this.setState({ activeRequest: null, activeRequestUrl: null }); const mapStyle = style.ensureStyleValidity(body); console.log("Loaded style ", mapStyle.id); this.props.onStyleOpen(mapStyle); this.onOpenToggle(); }) .catch((err) => { this.setState({ error: `Failed to load: '${styleUrl}'`, activeRequest: null, activeRequestUrl: null }); console.error(err); console.warn("Could not open the style URL", styleUrl); }); this.setState({ activeRequest: { abort: function () { canceled = true; } }, activeRequestUrl: styleUrl }); }; onSubmitUrl = (e: FormEvent) => { e.preventDefault(); this.onStyleSelect(this.state.styleUrl); }; onOpenFile = async () => { this.clearError(); const pickerOpts: OpenFilePickerOptions = { types: [ { description: "json", accept: { "application/json": [".json"] }, }, ], multiple: false, }; const [fileHandle] = await window.showOpenFilePicker(pickerOpts) as Array; const file = await fileHandle.getFile(); const content = await file.text(); let mapStyle; try { mapStyle = JSON.parse(content); } catch (err) { this.setState({ error: (err as Error).toString() }); return; } mapStyle = style.ensureStyleValidity(mapStyle); this.props.onStyleOpen(mapStyle, fileHandle); this.onOpenToggle(); return file; }; // it is not guaranteed that the File System Access API is available on all // browsers. If the function is not available, a fallback behavior is used. onFileChanged = (files: FileList | null) => { if (!files) return; if (files.length === 0) return; const file = files[0]; const reader = new FileReader(); this.clearError(); reader.readAsText(file, "UTF-8"); reader.onload = e => { let mapStyle; try { mapStyle = JSON.parse(e.target?.result as string); } catch (err) { this.setState({ error: (err as Error).toString() }); return; } mapStyle = style.ensureStyleValidity(mapStyle); this.props.onStyleOpen(mapStyle); this.onOpenToggle(); }; reader.onerror = e => console.log(e.target); }; onOpenToggle() { this.setState({ styleUrl: "" }); this.clearError(); this.props.onOpenToggle(); } onChangeUrl = (url: string) => { this.setState({ styleUrl: url, }); }; render() { const t = this.props.t; const styleOptions = publicStyles.map(style => { return ; }); let errorElement; if (this.state.error) { errorElement = ( ); } return (
this.onOpenToggle()} title={t("Open Style")} > {errorElement}

{t("Open local Style")}

{t("Open a local JSON style from your computer.")}

{typeof window.showOpenFilePicker === "function" ? ( {t("Open Style")} ) : ( )}

{t("Load from URL")}

Load from a URL. Note that the URL must have CORS enabled.

Load from URL

{t("Gallery Styles")}

{t("Open one of the publicly available styles to start from.")}

{styleOptions}
this.onCancelActiveRequest(e)} message={t("Loading") + ": " + this.state.activeRequestUrl} />
); } } const ModalOpen = withTranslation()(ModalOpenInternal); export default ModalOpen;