forked from MapComplete/MapComplete
		
	Accessibility: add focus trapping, debug tab cycling, UI tweaks for mobile browser
This commit is contained in:
		
							parent
							
								
									307549b593
								
							
						
					
					
						commit
						8ae4d810d6
					
				
					 19 changed files with 123 additions and 77 deletions
				
			
		
							
								
								
									
										13
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -6,7 +6,7 @@ | |||
|   "packages": { | ||||
|     "": { | ||||
|       "name": "mapcomplete", | ||||
|       "version": "0.36.1", | ||||
|       "version": "0.36.2", | ||||
|       "license": "GPL-3.0-or-later", | ||||
|       "dependencies": { | ||||
|         "@rgossiaux/svelte-headlessui": "^1.0.2", | ||||
|  | @ -56,6 +56,7 @@ | |||
|         "svg-path-parser": "^1.1.0", | ||||
|         "tailwind-merge": "^1.13.1", | ||||
|         "tailwindcss": "^3.1.8", | ||||
|         "trap-focus-svelte": "^1.0.1", | ||||
|         "vite-node": "^0.28.3", | ||||
|         "vitest": "^0.28.3", | ||||
|         "wikibase-sdk": "^7.14.0", | ||||
|  | @ -12140,6 +12141,11 @@ | |||
|         "node": ">=14" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/trap-focus-svelte": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/trap-focus-svelte/-/trap-focus-svelte-1.0.1.tgz", | ||||
|       "integrity": "sha512-qacSd68+c12mudUu9Mo70Ea16263ich2APFh1d0K7k9rLtwNcxlxNqA6l7Wv7xdzhJbC9TASxroiDSkiN2349w==" | ||||
|     }, | ||||
|     "node_modules/ts-api-utils": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", | ||||
|  | @ -22659,6 +22665,11 @@ | |||
|         "punycode": "^2.3.0" | ||||
|       } | ||||
|     }, | ||||
|     "trap-focus-svelte": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/trap-focus-svelte/-/trap-focus-svelte-1.0.1.tgz", | ||||
|       "integrity": "sha512-qacSd68+c12mudUu9Mo70Ea16263ich2APFh1d0K7k9rLtwNcxlxNqA6l7Wv7xdzhJbC9TASxroiDSkiN2349w==" | ||||
|     }, | ||||
|     "ts-api-utils": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", | ||||
|  |  | |||
|  | @ -143,6 +143,7 @@ | |||
|     "svg-path-parser": "^1.1.0", | ||||
|     "tailwind-merge": "^1.13.1", | ||||
|     "tailwindcss": "^3.1.8", | ||||
|     "trap-focus-svelte": "^1.0.1", | ||||
|     "vite-node": "^0.28.3", | ||||
|     "vitest": "^0.28.3", | ||||
|     "wikibase-sdk": "^7.14.0", | ||||
|  |  | |||
|  | @ -1796,14 +1796,14 @@ video { | |||
|   padding: 0.25rem; | ||||
| } | ||||
| 
 | ||||
| .p-0\.5 { | ||||
|   padding: 0.125rem; | ||||
| } | ||||
| 
 | ||||
| .p-0 { | ||||
|   padding: 0px; | ||||
| } | ||||
| 
 | ||||
| .p-0\.5 { | ||||
|   padding: 0.125rem; | ||||
| } | ||||
| 
 | ||||
| .p-12 { | ||||
|   padding: 3rem; | ||||
| } | ||||
|  | @ -2244,7 +2244,6 @@ body { | |||
| 
 | ||||
| .focusable { | ||||
|   /* Not a 'real' class, but rather an indication to FloatOver and ModalRight to, when they open, grab the focus */ | ||||
|   border: 1px solid red | ||||
| } | ||||
| 
 | ||||
| svg, | ||||
|  |  | |||
|  | @ -190,19 +190,45 @@ export default class GenerateImageAnalysis extends Script { | |||
|         if (!existsSync(viewDir)) { | ||||
|             mkdirSync(viewDir) | ||||
|         } | ||||
|         const targetpath = datapath + "/views.csv" | ||||
| 
 | ||||
|         const total = allImages.size | ||||
|         let dloaded = 0 | ||||
|         let skipped = 0 | ||||
|         let err = 0 | ||||
|         for (const image of Array.from(allImages)) { | ||||
|             const cachedView = viewDir + "/" + image.replace(/\\/g, "_") | ||||
|             const cachedView = viewDir + "/" + image.replace(/\//g, "_") | ||||
|             let attribution: LicenseInfo | ||||
|             if (existsSync(cachedView)) { | ||||
|                 attribution = JSON.parse(readFileSync(cachedView, "utf8")) | ||||
|                 skipped++ | ||||
|             } else { | ||||
|                 try { | ||||
|                     attribution = await Imgur.singleton.DownloadAttribution(image) | ||||
|                     await ScriptUtils.sleep(500) | ||||
|                     writeFileSync(cachedView, JSON.stringify(attribution)) | ||||
|                     dloaded++ | ||||
|                 } catch (e) { | ||||
|                     err++ | ||||
|                     continue | ||||
|                 } | ||||
|             } | ||||
|             results.push([image, attribution.views]) | ||||
|             if (dloaded % 50 === 0) { | ||||
|                 console.log({ | ||||
|                     dloaded, | ||||
|                     skipped, | ||||
|                     total, | ||||
|                     err, | ||||
|                     progress: Math.round(dloaded + skipped + err), | ||||
|                 }) | ||||
|             } | ||||
| 
 | ||||
|             if ((dloaded + skipped + err) % 100 === 0) { | ||||
|                 console.log("Writing views to", targetpath) | ||||
|                 fs.writeFileSync(targetpath, results.map((r) => r.join(",")).join("\n")) | ||||
|             } | ||||
|         } | ||||
|         const targetpath = datapath + "/views.csv" | ||||
|         console.log("Writing views to", targetpath) | ||||
|         fs.writeFileSync(targetpath, results.map((r) => r.join(",")).join("\n")) | ||||
|     } | ||||
|  | @ -416,8 +442,8 @@ export default class GenerateImageAnalysis extends Script { | |||
|         const imageBackupPath = args[0] | ||||
|         await this.downloadData(datapath, cached) | ||||
| 
 | ||||
|         await this.downloadMetadata(datapath) | ||||
|         await this.downloadViews(datapath) | ||||
|         await this.downloadMetadata(datapath) | ||||
|         await this.downloadAllImages(datapath, imageBackupPath) | ||||
|         this.analyze(datapath) | ||||
|     } | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ export default class UserRelatedState { | |||
|     public readonly installedUserThemes: Store<string[]> | ||||
|     public readonly showAllQuestionsAtOnce: UIEventSource<boolean> | ||||
|     public readonly showTags: UIEventSource<"no" | undefined | "always" | "yes" | "full"> | ||||
|     public readonly showCrosshair: UIEventSource<"yes" | undefined> | ||||
|     public readonly showCrosshair: UIEventSource<"yes" | "always" | "no" | undefined> | ||||
|     public readonly fixateNorth: UIEventSource<undefined | "yes"> | ||||
|     public readonly homeLocation: FeatureSource | ||||
|     /** | ||||
|  |  | |||
|  | @ -1,10 +1,11 @@ | |||
| import { Translation } from "../UI/i18n/Translation" | ||||
| import { DenominationConfigJson } from "./ThemeConfig/Json/UnitConfigJson" | ||||
| import Translations from "../UI/i18n/Translations" | ||||
| import { Store } from "../Logic/UIEventSource" | ||||
| import BaseUIElement from "../UI/BaseUIElement" | ||||
| import Toggle from "../UI/Input/Toggle" | ||||
| 
 | ||||
| /** | ||||
|  * A 'denomination' is one way to write a certain quantity. | ||||
|  * For example, 'meter', 'kilometer', 'mile' and 'foot' are all possible ways to quantify 'length' | ||||
|  */ | ||||
| export class Denomination { | ||||
|     public readonly canonical: string | ||||
|     public readonly _canonicalSingular: string | ||||
|  | @ -53,8 +54,8 @@ export class Denomination { | |||
| 
 | ||||
|     /** | ||||
|      * Create a representation of the given value | ||||
|      * @param value: the value from OSM | ||||
|      * @param actAsDefault: if set and the value can be parsed as number, will be parsed and trimmed | ||||
|      * @param value the value from OSM | ||||
|      * @param actAsDefault if set and the value can be parsed as number, will be parsed and trimmed | ||||
|      * | ||||
|      * const unit = new Denomination({ | ||||
|      *               canonicalDenomination: "m", | ||||
|  | @ -82,6 +83,8 @@ export class Denomination { | |||
|      * unit.canonicalValue("42", true) // =>"42"
 | ||||
|      * unit.canonicalValue("42 m", true) // =>"42"
 | ||||
|      * unit.canonicalValue("42 meter", true) // =>"42"
 | ||||
|      * | ||||
|      * | ||||
|      */ | ||||
|     public canonicalValue(value: string, actAsDefault: boolean): string { | ||||
|         if (value === undefined) { | ||||
|  |  | |||
|  | @ -1,28 +1,35 @@ | |||
| <script lang="ts"> | ||||
|   import { createEventDispatcher, onMount } from "svelte"; | ||||
|   import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid" | ||||
|   import { twMerge } from "tailwind-merge" | ||||
| 
 | ||||
|   import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import { twMerge } from "tailwind-merge"; | ||||
|   import { Utils } from "../../Utils"; | ||||
|   import { trapFocus } from 'trap-focus-svelte' | ||||
|   /** | ||||
|    * The slotted element will be shown on top, with a lower-opacity border | ||||
|    */ | ||||
|   const dispatch = createEventDispatcher<{ close }>() | ||||
|   const dispatch = createEventDispatcher<{ close }>(); | ||||
| 
 | ||||
|   export let extraClasses = "p-4 md:p-6" | ||||
|   export let extraClasses = "p-4 md:p-6"; | ||||
| 
 | ||||
|   let mainContent: HTMLElement | ||||
|   let mainContent: HTMLElement; | ||||
|   onMount(() => { | ||||
|     console.log("Mounting floatover") | ||||
|     mainContent?.focus() | ||||
|   }) | ||||
|     requestAnimationFrame(() => { | ||||
|       Utils.focusOnFocusableChild(mainContent); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <div | ||||
|   class={twMerge("absolute top-0 right-0 h-screen w-screen", extraClasses)} | ||||
|   style="background-color: #00000088; z-index: 20" | ||||
|   on:click={() => { | ||||
|   <!-- Draw the background over the total screen --> | ||||
| <div class="w-screen h-screen absolute top-0 left-0" style="background-color: #00000088; z-index: 20"   on:click={() => { | ||||
|     dispatch("close") | ||||
|   }} | ||||
|   }}> | ||||
| </div> | ||||
| <!-- draw a _second_ absolute div, placed using 'bottom' which will be above the navigation bar on mobile browsers --> | ||||
| <div | ||||
|   class={twMerge("absolute bottom-0 right-0 h-full w-screen", extraClasses)} | ||||
|   use:trapFocus | ||||
|   style="z-index: 21" | ||||
| > | ||||
|   <div bind:this={mainContent} class="content normal-background" on:click|stopPropagation={() => {}}> | ||||
|     <div class="h-full rounded-xl"> | ||||
|  | @ -30,16 +37,18 @@ | |||
|     </div> | ||||
|     <slot name="close-button"> | ||||
|       <!-- The close button is placed _after_ the default slot in order to always paint it on top --> | ||||
|       <div | ||||
|         class="absolute right-10 top-10 h-8 w-8 cursor-pointer" | ||||
|       <button | ||||
|         class="absolute right-10 top-10 h-8 w-8 cursor-pointer p-0 border-none bg-white" | ||||
|         on:click={() => dispatch("close")} | ||||
|       > | ||||
|         <XCircleIcon /> | ||||
|       </div> | ||||
|       </button> | ||||
|     </slot> | ||||
|   </div> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| <style> | ||||
|     .content { | ||||
|         height: 100%; | ||||
|  |  | |||
|  | @ -10,14 +10,14 @@ | |||
| 
 | ||||
|   export let state: { | ||||
|     osmConnection: OsmConnection | ||||
|     featureSwitches?: { featureSwitchUserbadge?: UIEventSource<boolean> } | ||||
|     featureSwitches?: { featureSwitchEnableLogin?: UIEventSource<boolean> } | ||||
|   } | ||||
|   /** | ||||
|    * If set, 'loading' will act as if we are already logged in. | ||||
|    */ | ||||
|   export let ignoreLoading: boolean = false | ||||
|   let loadingStatus = state?.osmConnection?.loadingStatus ?? new ImmutableStore("logged-in") | ||||
|   let badge = state?.featureSwitches?.featureSwitchUserbadge ?? new ImmutableStore(true) | ||||
|   let badge = state?.featureSwitches?.featureSwitchEnableLogin ?? new ImmutableStore(true) | ||||
|   const t = Translations.t.general | ||||
|   const offlineModes: Partial<Record<OsmServiceState, Translation>> = { | ||||
|     offline: t.loginFailedOfflineMode, | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
|   import { createEventDispatcher, onMount } from "svelte"; | ||||
|   import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import { Utils } from "../../Utils"; | ||||
|   import { trapFocus } from 'trap-focus-svelte' | ||||
| 
 | ||||
|   /** | ||||
|    * The slotted element will be shown on the right side | ||||
|  | @ -13,13 +14,13 @@ | |||
|   onMount(() => { | ||||
|     window.setTimeout( | ||||
|       () => Utils.focusOnFocusableChild(mainContent), 250 | ||||
|        | ||||
|     ) | ||||
|   }) | ||||
|     ); | ||||
|   }); | ||||
| </script> | ||||
| 
 | ||||
| <div | ||||
|   bind:this={mainContent} | ||||
|   use:trapFocus | ||||
|   class="absolute top-0 right-0 h-screen w-full overflow-y-auto drop-shadow-2xl md:w-6/12 lg:w-5/12 xl:w-4/12" | ||||
|   style="max-width: 100vw; max-height: 100vh" | ||||
| > | ||||
|  |  | |||
|  | @ -14,10 +14,8 @@ | |||
|   export let selectedElement: Feature | ||||
|   export let highlightedRendering: UIEventSource<string> = undefined | ||||
| 
 | ||||
|   let tags: UIEventSource<Record<string, string>> = state.featureProperties.getStore(selectedElement.properties.id) | ||||
|   $: { | ||||
|     tags = state.featureProperties.getStore(selectedElement.properties.id) | ||||
|   } | ||||
|   export let tags: UIEventSource<Record<string, string>> = state.featureProperties.getStore(selectedElement.properties.id) | ||||
| 
 | ||||
|   let _metatags: Record<string, string> | ||||
|   onDestroy( | ||||
|     state.userRelatedState.preferencesAsTags.addCallbackAndRun((tags) => { | ||||
|  | @ -28,7 +26,7 @@ | |||
|   let knownTagRenderings: Store<TagRenderingConfig[]> =  tags.mapD(tgs =>  layer.tagRenderings.filter( | ||||
|     (config) => | ||||
|       (config.condition?.matchesProperties(tgs) ?? true) && | ||||
|       config.metacondition?.matchesProperties({ ...tgs, ..._metatags } ?? true) && | ||||
|       (config.metacondition?.matchesProperties({ ...tgs, ..._metatags }) ?? true) && | ||||
|       config.IsKnown(tgs) | ||||
|   )) | ||||
| </script> | ||||
|  | @ -39,7 +37,7 @@ | |||
|     <Tr t={Translations.t.general.returnToTheMap} /> | ||||
|   </button> | ||||
| {:else} | ||||
|   <div class="flex h-full flex-col gap-y-2 overflow-y-auto p-1 px-2 focusable" tabindex="-1"> | ||||
|   <div class="flex h-full w-full flex-col gap-y-2 overflow-y-auto p-1 px-2 focusable" tabindex="-1"> | ||||
|     {#each $knownTagRenderings as config (config.id)} | ||||
|       <TagRenderingEditable | ||||
|         {tags} | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ import { twMerge } from "tailwind-merge"; | |||
| export let image: ProvidedImage | ||||
| export let clss: string = undefined | ||||
| async function download() { | ||||
|     const response = await fetch(image.url) | ||||
|     const response = await fetch(image.url_hd ?? image.url ) | ||||
|     const blob = await response.blob() | ||||
|     Utils.offerContentsAsDownloadableFile(blob, new URL(image.url).pathname.split("/").at(-1), { | ||||
|         mimetype: "image/jpg", | ||||
|  | @ -22,11 +22,11 @@ async function download() { | |||
| </script> | ||||
| 
 | ||||
| <div class={twMerge("w-full h-full relative", clss)}> | ||||
|   <div class="absolute top-0 left-0 w-full h-full overflow-hidden"> | ||||
|   <div class="absolute top-0 left-0 w-full h-full overflow-hidden panzoom-container focusable"> | ||||
|     <ImagePreview image={image} /> | ||||
|   </div> | ||||
|   <div class="absolute bottom-0 left-0 w-full pointer-events-none flex flex-wrap justify-between items-end"> | ||||
|     <div class="pointer-events-auto w-fit opacity-50 hover:opacity-100 transition-colors duration-200"> | ||||
|     <div class="pointer-events-auto w-fit opacity-50 hover:opacity-100 transition-colors duration-200 m-1"> | ||||
|       <ImageAttribution image={image} /> | ||||
|     </div> | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,4 +25,4 @@ | |||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <img bind:this={panzoomEl} src={image.url_hd ?? image.url} class="w-full h-auto"/> | ||||
| <img bind:this={panzoomEl} src={image.url_hd ?? image.url} class="w-fit h-fit panzoom-image"/> | ||||
|  |  | |||
|  | @ -13,8 +13,11 @@ | |||
|   import Loading from "../Base/Loading.svelte" | ||||
| 
 | ||||
|   export let state: SpecialVisualizationState | ||||
|   export let tags: Store<OsmTags> | ||||
|   export let featureId = tags.data.id | ||||
|   export let tags: Store<OsmTags> = undefined | ||||
|   export let featureId = tags?.data?.id | ||||
|   if(featureId === undefined){ | ||||
|     throw "No tags or featureID given" | ||||
|   } | ||||
|   export let showThankYou: boolean = true | ||||
|   const { uploadStarted, uploadFinished, retried, failed } = | ||||
|     state.imageUploadManager.getCountsFor(featureId) | ||||
|  |  | |||
|  | @ -77,7 +77,7 @@ export interface SpecialVisualizationState { | |||
|         readonly showTags: UIEventSource<"no" | undefined | "always" | "yes" | "full"> | ||||
|         readonly mangroveIdentity: MangroveIdentity | ||||
|         readonly showAllQuestionsAtOnce: UIEventSource<boolean> | ||||
|         readonly preferencesAsTags: Store<Record<string, string>> | ||||
|         readonly preferencesAsTags: UIEventSource<Record<string, string>> | ||||
|         readonly language: UIEventSource<string> | ||||
|     } | ||||
|     readonly lastClickObject: WritableFeatureSource | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ import FeatureReviews from "../Logic/Web/MangroveReviews" | |||
| import Maproulette from "../Logic/Maproulette" | ||||
| import SvelteUIElement from "./Base/SvelteUIElement" | ||||
| import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" | ||||
| import { Feature, Point } from "geojson" | ||||
| import { Feature } from "geojson" | ||||
| import { GeoOperations } from "../Logic/GeoOperations" | ||||
| import CreateNewNote from "./Popup/CreateNewNote.svelte" | ||||
| import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte" | ||||
|  | @ -48,8 +48,7 @@ import UserProfile from "./BigComponents/UserProfile.svelte" | |||
| import Link from "./Base/Link" | ||||
| import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||
| import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | ||||
| import { OsmTags, WayId } from "../Models/OsmFeature" | ||||
| import MoveWizard from "./Popup/MoveWizard" | ||||
| import { WayId } from "../Models/OsmFeature" | ||||
| import SplitRoadWizard from "./Popup/SplitRoadWizard" | ||||
| import { ExportAsGpxViz } from "./Popup/ExportAsGpxViz" | ||||
| import WikipediaPanel from "./Wikipedia/WikipediaPanel.svelte" | ||||
|  | @ -82,6 +81,8 @@ import MarkAsFavouriteMini from "./Popup/MarkAsFavouriteMini.svelte" | |||
| import NextChangeViz from "./OpeningHours/NextChangeViz.svelte" | ||||
| import NearbyImages from "./Image/NearbyImages.svelte" | ||||
| import NearbyImagesCollapsed from "./Image/NearbyImagesCollapsed.svelte" | ||||
| import { svelte } from "@sveltejs/vite-plugin-svelte" | ||||
| import MoveWizard from "./Popup/MoveWizard.svelte" | ||||
| 
 | ||||
| class NearbyImageVis implements SpecialVisualization { | ||||
|     // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
 | ||||
|  | @ -515,12 +516,11 @@ export default class SpecialVisualizations { | |||
|                         return undefined | ||||
|                     } | ||||
| 
 | ||||
|                     return new MoveWizard( | ||||
|                         <Feature<Point>>feature, | ||||
|                         <UIEventSource<OsmTags>>tagSource, | ||||
|                     return new SvelteUIElement(MoveWizard, { | ||||
|                         state, | ||||
|                         layer.allowMove | ||||
|                     ) | ||||
|                         featureToMove: feature, | ||||
|                         layer, | ||||
|                     }) | ||||
|                 }, | ||||
|             }, | ||||
|             { | ||||
|  |  | |||
|  | @ -89,7 +89,7 @@ | |||
|      | ||||
|   }) | ||||
|    | ||||
|   let selectedLayer: UIEventSource<LayerConfig> = state.selectedElement.mapD(element => state.layout.getMatchingLayer(element.properties)); | ||||
|   let selectedLayer: Store<LayerConfig> = state.selectedElement.mapD(element => state.layout.getMatchingLayer(element.properties)); | ||||
| 
 | ||||
|   let currentZoom = state.mapProperties.zoom; | ||||
|   let showCrosshair = state.userRelatedState.showCrosshair; | ||||
|  | @ -125,7 +125,6 @@ | |||
|         bounds={state.mapProperties.bounds} | ||||
|         perLayer={state.perLayer} | ||||
|         selectedElement={state.selectedElement} | ||||
|         {selectedLayer} | ||||
|       /> | ||||
|     </div> | ||||
|   </If> | ||||
|  | @ -144,7 +143,6 @@ | |||
|     {#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()} | ||||
|       <MapControlButton | ||||
|         on:click={() => { | ||||
|           selectedLayer.setData(currentViewLayer) | ||||
|           selectedElement.setData(state.currentView.features?.data?.[0]) | ||||
|         }} | ||||
|       > | ||||
|  | @ -269,7 +267,7 @@ | |||
|     > | ||||
|       <XCircleIcon /> | ||||
|     </div> | ||||
|     <ImageOperations clss="focusable" image={$previewedImage} /> | ||||
|     <ImageOperations image={$previewedImage} /> | ||||
|   </FloatOver> | ||||
| </If> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1658,7 +1658,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | |||
|             } | ||||
|             const child = <HTMLElement>childs.item(0) | ||||
|             if (child === null) { | ||||
|                 console.log("Focussing on child element: no child element found for", el) | ||||
|                 return undefined | ||||
|             } | ||||
|             if ( | ||||
|  | @ -1668,7 +1667,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | |||
|             ) { | ||||
|                 child.setAttribute("tabindex", "-1") | ||||
|             } | ||||
|             console.log("Focussing on", child) | ||||
|             child?.focus() | ||||
|         }) | ||||
|     } | ||||
|  |  | |||
|  | @ -69,12 +69,11 @@ body { | |||
|     color: var(--foreground-color); | ||||
|     font-family: "Helvetica Neue", Arial, sans-serif; | ||||
| } | ||||
| 
 | ||||
| .focusable { | ||||
|     /* Not a 'real' class, but rather an indication to FloatOver and ModalRight to, when they open, grab the focus */ | ||||
|     border: 1px solid red | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| svg, | ||||
| img { | ||||
|     box-sizing: content-box; | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ | |||
| <body> | ||||
| 
 | ||||
| 
 | ||||
| <div class="h-full" id="maindiv"> | ||||
| <div class="h-screen" id="maindiv"> | ||||
|     <div id="default-main h-full"> | ||||
|         <div class="w-full h-screen flex flex-col items-center justify-between  p-8"> | ||||
|             <div class="w-full h-full flex flex-col items-center"> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue