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 {
|
||||
attribution = await Imgur.singleton.DownloadAttribution(image)
|
||||
writeFileSync(cachedView, JSON.stringify(attribution))
|
||||
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 }>()
|
||||
|
||||
export let extraClasses = "p-4 md:p-6"
|
||||
|
||||
let mainContent: HTMLElement
|
||||
const dispatch = createEventDispatcher<{ close }>();
|
||||
|
||||
export let extraClasses = "p-4 md:p-6";
|
||||
|
||||
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,21 +37,23 @@
|
|||
</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%;
|
||||
border-radius: 0.5rem;
|
||||
overflow-x: hidden;
|
||||
box-shadow: 0 0 1rem #00000088;
|
||||
}
|
||||
.content {
|
||||
height: 100%;
|
||||
border-radius: 0.5rem;
|
||||
overflow-x: hidden;
|
||||
box-shadow: 0 0 1rem #00000088;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -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…
Reference in a new issue