Styling: style menu- and theme-menu tabbed interface
This commit is contained in:
parent
a4a3b8a5ad
commit
14927497bd
6 changed files with 181 additions and 125 deletions
|
@ -1,20 +1,33 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import {createEventDispatcher} from "svelte";
|
||||
import {XCircleIcon} from "@rgossiaux/svelte-heroicons/solid";
|
||||
|
||||
/**
|
||||
* The slotted element will be shown on top, with a lower-opacity border
|
||||
*/
|
||||
const dispatch = createEventDispatcher<{ close }>();
|
||||
/**
|
||||
* The slotted element will be shown on top, with a lower-opacity border
|
||||
*/
|
||||
const dispatch = createEventDispatcher<{ close }>();
|
||||
</script>
|
||||
|
||||
<div class="absolute top-0 right-0 w-screen h-screen overflow-auto" style="background-color: #00000088">
|
||||
<div class="flex flex-col m-4 sm:m-6 p-4 sm:p-6 md:m-8 rounded normal-background">
|
||||
<slot name="close-button">
|
||||
<div class="w-8 h-8 absolute right-10 top-10 cursor-pointer" on:click={() => dispatch("close")}>
|
||||
<XCircleIcon />
|
||||
</div>
|
||||
</slot>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="absolute top-0 right-0 w-screen h-screen p-4 md:p-6" style="background-color: #00000088">
|
||||
<div class="content normal-background">
|
||||
<div class="rounded-xl">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="close-button">
|
||||
<!-- The close button is placed _after_ the default slot in order to always paint it on top -->
|
||||
<div class="w-8 h-8 absolute right-10 top-10 cursor-pointer" on:click={() => dispatch("close")}>
|
||||
<XCircleIcon/>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.content {
|
||||
height: calc( 100vh - 2rem );
|
||||
border-radius: 0.5rem;
|
||||
overflow-x: auto;
|
||||
box-shadow: 0 0 1rem #00000088;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,79 +1,121 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Thin wrapper around 'TabGroup' which binds the state
|
||||
*/
|
||||
/**
|
||||
* Thin wrapper around 'TabGroup' which binds the state
|
||||
*/
|
||||
|
||||
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui";
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import {Tab, TabGroup, TabList, TabPanel, TabPanels} from "@rgossiaux/svelte-headlessui";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
|
||||
export let tab: UIEventSource<number>;
|
||||
let tabElements: HTMLElement[] = [];
|
||||
$: tabElements[$tab]?.click();
|
||||
$: {
|
||||
if (tabElements[tab.data]) {
|
||||
window.setTimeout(() => tabElements[tab.data].click(), 50)
|
||||
export let tab: UIEventSource<number>;
|
||||
let tabElements: HTMLElement[] = [];
|
||||
$: tabElements[$tab]?.click();
|
||||
$: {
|
||||
if (tabElements[tab.data]) {
|
||||
window.setTimeout(() => tabElements[tab.data].click(), 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<TabGroup defaultIndex={1} on:change={(e) =>{if(e.detail >= 0){tab.setData( e.detail); }} }>
|
||||
<TabList>
|
||||
<Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}>
|
||||
<div bind:this={tabElements[0]} class="flex">
|
||||
<slot name="title0">
|
||||
Tab 0
|
||||
</slot>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}>
|
||||
<div bind:this={tabElements[1]} class="flex">
|
||||
<slot name="title1" />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}>
|
||||
<div bind:this={tabElements[2]} class="flex">
|
||||
<slot name="title2" />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}>
|
||||
<div bind:this={tabElements[3]} class="flex">
|
||||
<slot name="title3" />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}>
|
||||
<div bind:this={tabElements[4]} class="flex">
|
||||
<slot name="title4" />
|
||||
</div>
|
||||
</Tab>
|
||||
</TabList>
|
||||
<TabPanels defaultIndex={$tab}>
|
||||
<TabPanel>
|
||||
<slot name="content0">
|
||||
<div>Empty</div>
|
||||
</slot>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<slot name="content1" />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<slot name="content2" />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<slot name="content3" />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<slot name="content4" />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
<div class="tabbedgroup w-full h-full">
|
||||
<TabGroup class="h-full w-full flex flex-col" defaultIndex={1}
|
||||
on:change={(e) =>{if(e.detail >= 0){tab.setData( e.detail); }} }>
|
||||
<div class="flex bg-gray-300 items-center justify-between sticky top-0">
|
||||
<TabList class="flex flex-wrap">
|
||||
{#if $$slots.title1}
|
||||
<Tab class={({selected}) => "tab "+(selected ? "tab-selected" : "tab-unselected")}>
|
||||
<div bind:this={tabElements[0]} class="flex">
|
||||
<slot name="title0">
|
||||
Tab 0
|
||||
</slot>
|
||||
</div>
|
||||
</Tab>
|
||||
{/if}
|
||||
{#if $$slots.title1}
|
||||
<Tab class={({selected}) => "tab "+(selected ? "tab-selected" : "tab-unselected")}>
|
||||
<div bind:this={tabElements[1]} class="flex">
|
||||
<slot name="title1"/>
|
||||
</div>
|
||||
</Tab>
|
||||
{/if}
|
||||
{#if $$slots.title2}
|
||||
<Tab class={({selected}) => "tab "+(selected ? "tab-selected" : "tab-unselected")}>
|
||||
<div bind:this={tabElements[2]} class="flex">
|
||||
<slot name="title2"/>
|
||||
</div>
|
||||
</Tab>
|
||||
{/if}
|
||||
{#if $$slots.title3}
|
||||
<Tab class={({selected}) => "tab "+(selected ? "tab-selected" : "tab-unselected")}>
|
||||
<div bind:this={tabElements[3]} class="flex">
|
||||
<slot name="title3"/>
|
||||
</div>
|
||||
</Tab>
|
||||
{/if}
|
||||
{#if $$slots.title4}
|
||||
<Tab class={({selected}) => "tab "+(selected ? "tab-selected" : "tab-unselected")}>
|
||||
<div bind:this={tabElements[4]} class="flex">
|
||||
<slot name="title4"/>
|
||||
</div>
|
||||
</Tab>
|
||||
{/if}
|
||||
</TabList>
|
||||
<slot name="post-tablist"/>
|
||||
</div>
|
||||
<div class="overflow-y-auto normal-background">
|
||||
|
||||
<TabPanels defaultIndex={$tab}>
|
||||
<TabPanel>
|
||||
<slot name="content0">
|
||||
<div>Empty</div>
|
||||
</slot>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<slot name="content1"/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<slot name="content2"/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<slot name="content3"/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<slot name="content4"/>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</div>
|
||||
</TabGroup>
|
||||
</div>
|
||||
<style>
|
||||
.tab-selected {
|
||||
.tabbedgroup {
|
||||
max-height: 100vh;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:global(.tab) {
|
||||
margin: 0.25rem;
|
||||
padding: 0.25rem;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
:global(.tab .flex) {
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
:global(.tab span|div) {
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
:global(.tab-selected) {
|
||||
background-color: rgb(59 130 246);
|
||||
color: rgb(255 255 255);
|
||||
}
|
||||
|
||||
.tab-unselected {
|
||||
:global(.tab-unselected) {
|
||||
background-color: rgb(255 255 255);
|
||||
color: rgb(0 0 0);
|
||||
}
|
||||
|
|
|
@ -29,13 +29,15 @@
|
|||
{:else}
|
||||
<UserCircleIcon class="w-12 h-12" />
|
||||
{/if}
|
||||
<div class="flex flex-col relative">
|
||||
<div class="flex flex-col">
|
||||
<h3>{$userdetails.name}</h3>
|
||||
{#if description}
|
||||
<FromHtml src={description} />
|
||||
<a href={osmConnection.Backend() + "/profile/edit"} target="_blank">
|
||||
<PencilAltIcon class="p-2 w-6 h-6 subtle-background rounded-full absolute right-1 bottom-1" />
|
||||
<a href={osmConnection.Backend() + "/profile/edit"} target="_blank" class="link-no-underline flex subtle-background items-center w-fit self-end">
|
||||
<PencilAltIcon slot="image" class="p-2 w-8 h-8" />
|
||||
<Tr slot="message" t={Translations.t.userinfo.editDescription} />
|
||||
</a>
|
||||
|
||||
{:else}
|
||||
<Tr t={Translations.t. userinfo.noDescription} />
|
||||
<a href={osmConnection.Backend() + "/profile/edit"} target="_blank" class="flex subtle-background items-center">
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
import type {MapProperties} from "../Models/MapProperties";
|
||||
import Geosearch from "./BigComponents/Geosearch.svelte";
|
||||
import Translations from "./i18n/Translations";
|
||||
import {CogIcon, EyeIcon, MenuIcon} from "@rgossiaux/svelte-heroicons/solid";
|
||||
import {CogIcon, EyeIcon, MenuIcon, XCircleIcon} from "@rgossiaux/svelte-heroicons/solid";
|
||||
import Tr from "./Base/Tr.svelte";
|
||||
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte";
|
||||
import FloatOver from "./Base/FloatOver.svelte";
|
||||
|
@ -149,9 +149,12 @@
|
|||
<If condition={selectedElementView.map(v => v !== undefined && selectedLayer.data !== undefined && !selectedLayer.data.popupInFloatover,[ selectedLayer] )}>
|
||||
<ModalRight on:close={() => {selectedElement.setData(undefined)}}>
|
||||
<div class="absolute flex flex-col h-full w-full normal-background">
|
||||
<ToSvelte construct={new VariableUiElement(selectedElementTitle)}></ToSvelte>
|
||||
|
||||
<ToSvelte construct={new VariableUiElement(selectedElementView)}></ToSvelte>
|
||||
<ToSvelte construct={new VariableUiElement(selectedElementTitle)}>
|
||||
<!-- Title -->
|
||||
</ToSvelte>
|
||||
<ToSvelte construct={new VariableUiElement(selectedElementView).SetClass("overflow-auto")}>
|
||||
<!-- Main view -->
|
||||
</ToSvelte>
|
||||
</div>
|
||||
</ModalRight>
|
||||
</If>
|
||||
|
@ -163,9 +166,14 @@
|
|||
</If>
|
||||
|
||||
<If condition={state.guistate.themeIsOpened}>
|
||||
<!-- Theme page -->
|
||||
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
|
||||
<!-- Theme menu -->
|
||||
<FloatOver>
|
||||
<span slot="close-button"><!-- Disable the close button --></span>
|
||||
<TabbedGroup tab={state.guistate.themeViewTabIndex}>
|
||||
<div slot="post-tablist">
|
||||
<XCircleIcon class="w-8 h-8 mr-2" on:click={() => state.guistate.themeIsOpened.setData(false)}/>
|
||||
</div>
|
||||
|
||||
<Tr slot="title0" t={layout.title}/>
|
||||
|
||||
<div slot="content0">
|
||||
|
@ -240,8 +248,12 @@
|
|||
|
||||
<If condition={state.guistate.menuIsOpened}>
|
||||
<!-- Menu page -->
|
||||
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
|
||||
<FloatOver>
|
||||
<span slot="close-button"><!-- Hide the default close button --></span>
|
||||
<TabbedGroup tab={state.guistate.menuViewTabIndex}>
|
||||
<div slot="post-tablist">
|
||||
<XCircleIcon class="w-8 h-8 mr-2" on:click={() => state.guistate.menuIsOpened.setData(false)}/>
|
||||
</div>
|
||||
<div class="flex" slot="title0">
|
||||
<Tr t={Translations.t.general.menu.aboutMapComplete}/>
|
||||
</div>
|
||||
|
@ -276,16 +288,13 @@
|
|||
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({theme: "MapComplete"})}/>
|
||||
</a>
|
||||
{Constants.vNumber}
|
||||
<ToSvelte construct={Hotkeys.generateDocumentationDynamic}></ToSvelte>
|
||||
</div>
|
||||
|
||||
|
||||
<If condition={state.osmConnection.isLoggedIn} slot="title1">
|
||||
<div class="flex">
|
||||
<CogIcon class="w-6 h-6"/>
|
||||
<Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})}/>
|
||||
</div>
|
||||
</If>
|
||||
<div class="flex" slot="title1">
|
||||
<CogIcon class="w-6 h-6"/>
|
||||
<Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})}/>
|
||||
</div>
|
||||
|
||||
<div slot="content1">
|
||||
<!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it -->
|
||||
|
@ -316,6 +325,12 @@
|
|||
<Tr t={Translations.t.privacy.title}></Tr>
|
||||
</div>
|
||||
<ToSvelte construct={() => new PrivacyPolicy()} slot="content3"></ToSvelte>
|
||||
|
||||
<Tr slot="title4" t={Translations.t.advanced.title}/>
|
||||
<div class="flex flex-col" slot="content4">
|
||||
<ToSvelte construct={Hotkeys.generateDocumentationDynamic}></ToSvelte>
|
||||
|
||||
</div>
|
||||
</TabbedGroup>
|
||||
</FloatOver>
|
||||
</If>
|
||||
|
|
|
@ -735,14 +735,6 @@ video {
|
|||
top: 1rem;
|
||||
}
|
||||
|
||||
.right-1 {
|
||||
right: 0.25rem;
|
||||
}
|
||||
|
||||
.bottom-1 {
|
||||
bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.left-1\/2 {
|
||||
left: 50%;
|
||||
}
|
||||
|
@ -905,10 +897,6 @@ video {
|
|||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.mt-0 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.mt-8 {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
@ -1464,6 +1452,11 @@ video {
|
|||
background-color: rgb(219 234 254 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-gray-300 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(209 213 219 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-black {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
|
@ -1479,11 +1472,6 @@ video {
|
|||
background-color: rgb(224 231 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-gray-300 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(209 213 219 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
|
@ -2324,10 +2312,6 @@ input {
|
|||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.sm\:m-6 {
|
||||
margin: 1.5rem;
|
||||
}
|
||||
|
||||
.sm\:mx-1 {
|
||||
margin-left: 0.25rem;
|
||||
margin-right: 0.25rem;
|
||||
|
@ -2382,10 +2366,6 @@ input {
|
|||
border-width: 4px;
|
||||
}
|
||||
|
||||
.sm\:p-6 {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.sm\:p-1 {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
@ -2418,10 +2398,6 @@ input {
|
|||
margin: 0.25rem;
|
||||
}
|
||||
|
||||
.md\:m-8 {
|
||||
margin: 2rem;
|
||||
}
|
||||
|
||||
.md\:m-2 {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
@ -2483,6 +2459,10 @@ input {
|
|||
border-top-width: 2px;
|
||||
}
|
||||
|
||||
.md\:p-6 {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.md\:p-4 {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
{
|
||||
"advanced": {
|
||||
"title": "Advanced features"
|
||||
},
|
||||
"centerMessage": {
|
||||
"loadingData": "Loading data…",
|
||||
"ready": "Done!",
|
||||
|
@ -961,6 +964,7 @@
|
|||
"notImmediate": "Translations are not updated directly. This typically takes a few days"
|
||||
},
|
||||
"userinfo": {
|
||||
"editDescription": "Edit your profile description",
|
||||
"gotoInbox": "Open your inbox",
|
||||
"gotoSettings": "Go to your settings on OpenStreetMap.org",
|
||||
"noDescription": "You don't have a description on your profile yet",
|
||||
|
|
Loading…
Reference in a new issue