Add mobile menu

This commit is contained in:
Stanislav Romanov 2022-09-09 15:49:45 +03:00
parent fc53449cad
commit 46611e86e1
11 changed files with 317 additions and 60 deletions

View file

@ -25,6 +25,22 @@
:root {
@mixin light-vars;
--sidenav-width: 300px;
--header-height: 3.5rem;
--spacing-1: 0.25rem;
--spacing-2: 0.5rem;
--spacing-3: 0.75rem;
--spacing-4: 1rem;
--z-index-scroll-link: 10;
--z-index-mobile-bar: 20;
--z-index-sidenav: 30;
--z-index-modal-overlay: 40;
--z-index-modal: 50;
--z-index-dropdown: 60;
--z-index-context-menu: 70;
}
@media (prefers-color-scheme: dark) {
@ -51,6 +67,137 @@
}
}
body {
&.sidebar-is-opened {
overflow: hidden;
.sidenav {
visibility: visible;
}
.sidenav-background {
opacity: 1;
}
.sidenav-sections {
transform: translate3d(0, 0, 0);
}
}
}
.app {
display: grid;
grid-template-columns: 100%;
grid-template-rows: 1fr auto;
grid-template-areas: 'main';
min-height: 100vh;
}
.sidenav {
grid-area: sidenav;
position: fixed;
z-index: var(--z-index-sidenav);
top: 0;
right: 0;
bottom: 0;
left: 0;
visibility: hidden;
transition-property: visibility;
@apply border-r;
}
.sidenav-background {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transition-property: opacity;
transform: translateZ(0);
opacity: 0;
background: rgba(0, 0, 0, 0.5);
}
.sidenav-sections {
transition-duration: 200ms;
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: var(--sidenav-width);
transition-property: transform;
transform: translate3d(-100%, 0, 0);
@apply bg-base-7 p-4;
}
.main {
grid-area: main;
padding: calc(var(--spacing-4) + var(--header-height)) var(--spacing-4) var(--spacing-4);
@apply bg-base-7;
}
.mobile-bar {
top: 0;
left: 0;
height: var(--header-height);
width: 100%;
z-index: var(--z-index-mobile-bar);
align-items: center;
overflow: hidden;
/* min-width: calc(var(--sidenav-width) - 2px); */
@apply bg-base-7;
& > button {
position: absolute;
padding: var(--spacing-3);
background: transparent;
border: none;
cursor: pointer;
&:focus {
outline: 0;
}
}
}
@media (min-width: theme("screens.lg")) {
body.sidebar-is-opened {
overflow: initial;
}
.app {
grid-template-columns: var(--sidenav-width) calc(100% - var(--sidenav-width));
grid-template-areas: "sidenav main";
}
.sidenav {
position: relative;
visibility: visible;
}
.sidenav-background {
opacity: 0;
display: none;
}
.sidenav-sections {
position: relative;
transform: none;
transition-duration: none;
position: initial;
top: initial;
bottom: initial;
left: initial;
width: auto;
transition-property: initial;
}
.main {
padding: var(--spacing-4);
}
}
.royal-table {
@apply table-auto text-left mb-4;

View file

@ -0,0 +1 @@
export { DotsHorizontal as default } from 'ember-mdi';

View file

@ -0,0 +1 @@
export { Menu as default } from 'ember-mdi';

View file

@ -0,0 +1,20 @@
<div class="mobile-bar" ...attributes>
<button
type='button'
class='inline-flex items-center lg:hidden'
aria-label='Toggle sidebar'
{{on 'click' this.toggleSidebar}}
>
<Icons::Menu @title='Menu' />
</button>
<LinkTo @route="index" class='text-2xl font-display flex-1 flex items-center justify-center'>
MeiliAdmin
</LinkTo>
</div>
{{!-- <a
href='/'
title='{{@siteName}}'
class='text-2xl font-display flex-1 flex items-center justify-center lg:mt-6'
{{on 'click' this.closeSidebar}}
>{{@siteName}}</a> --}}

View file

@ -0,0 +1,14 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class MobileBarComponent extends Component {
@action
toggleSidebar() {
document.body.classList.toggle('sidebar-is-opened');
}
@action
closeSidebar() {
document.body.classList.remove('sidebar-is-opened');
}
}

View file

@ -0,0 +1,5 @@
<div
role='button'
class='sidenav-background'
{{on 'click' this.closeSidebar}}
></div>

View file

@ -0,0 +1,9 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class SidenavBackgroundComponent extends Component {
@action
closeSidebar() {
document.body.classList.remove('sidebar-is-opened');
}
}

View file

@ -1,18 +1,16 @@
<div class='relative min-h-screen flex'>
<div class='flex flex-col w-64 min-h-screen px-4 py-4 border-r'>
<LinkTo
@route='admin'
class='flex items-center justify-center font-semibold text-xl mt-4 mb-8'
>
MeiliAdmin
</LinkTo>
<div class='app'>
<MobileBar class='fixed flex shadow-sm lg:hidden' />
{{#let
'flex items-center px-4 py-2 font-semibold text-lg text-base-2'
as |classLink|
}}
<nav class='flex flex-col flex-1 mb-4'>
{{!-- <div class='px-4 py-2 text-sm text-base-3'>Indexes</div>
<aside class='sidenav'>
<SidenavBackground />
<div class='sidenav-sections'>
<MobileBar class='relative flex mb-4' />
{{#let
'flex items-center px-4 py-2 font-semibold text-lg text-base-2'
as |classLink|
}}
<nav class='flex flex-col flex-1 mb-4'>
{{!-- <div class='px-4 py-2 text-sm text-base-3'>Indexes</div>
{{#each @model.results as |item|}}
<LinkTo
@route='admin.indexes.item'
@ -23,57 +21,58 @@
{{capitalize item.uid}}
</LinkTo>
{{/each}} --}}
<LinkTo @route='admin.indexes.index' class={{classLink}}>
<Icons::FolderMultipleOutline class='fill-base-3 mr-2' />
Indexes
</LinkTo>
<LinkTo @route='admin.tasks.index' class={{classLink}}>
<Icons::FormatListChecks class='fill-base-3 mr-2' />
Tasks
</LinkTo>
<LinkTo @route='admin.keys.index' class={{classLink}}>
<Icons::KeyOutline class='fill-base-3 mr-2' />
Keys
</LinkTo>
<LinkTo @route='admin.indexes.index' class={{classLink}}>
<Icons::FolderMultipleOutline class='fill-base-3 mr-2' />
Indexes
</LinkTo>
<LinkTo @route='admin.tasks.index' class={{classLink}}>
<Icons::FormatListChecks class='fill-base-3 mr-2' />
Tasks
</LinkTo>
<LinkTo @route='admin.keys.index' class={{classLink}}>
<Icons::KeyOutline class='fill-base-3 mr-2' />
Keys
</LinkTo>
<Divider />
<Divider />
<a
href='https://docs.meilisearch.com/'
class={{classLink}}
target='_blank'
rel='noopener noreferrer'
>
<Icons::HelpCircleOutline class='fill-base-3 mr-2' />
Documentation
</a>
<a
href='https://github.com/kaermorchen/meiliadmin'
class={{classLink}}
target='_blank'
rel='noopener noreferrer'
>
<Icons::Github class='fill-base-3 mr-2' />
MeiliAdmin
</a>
<a
href='https://docs.meilisearch.com/'
class={{classLink}}
target='_blank'
rel='noopener noreferrer'
>
<Icons::HelpCircleOutline class='fill-base-3 mr-2' />
Documentation
</a>
<a
href='https://github.com/kaermorchen/meiliadmin'
class={{classLink}}
target='_blank'
rel='noopener noreferrer'
>
<Icons::Github class='fill-base-3 mr-2' />
MeiliAdmin
</a>
<Divider />
<Divider />
<button
type='button'
{{on 'click' this.invalidateSession}}
class={{classLink}}
>
<Icons::Logout class='fill-base-3 mr-2' />
Logout
</button>
</nav>
<button
type='button'
{{on 'click' this.invalidateSession}}
class={{classLink}}
>
<Icons::Logout class='fill-base-3 mr-2' />
Logout
</button>
</nav>
<ThemeToggler />
{{/let}}
</div>
<ThemeToggler />
{{/let}}
</div>
</aside>
<div class='flex-1 p-4'>
<main class='main'>
{{outlet}}
</div>
</main>
</div>

View file

@ -34,6 +34,15 @@ module.exports = {
dark: colors.green['800'],
},
},
zIndex: {
'scroll-link': '10',
'mobile-bar': '20',
sidenav: '30',
'modal-overlay': '40',
modal: '50',
dropdown: '60',
'context-menu': '70',
},
},
},
variants: {

View file

@ -0,0 +1,26 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'meiliadmin/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | mobile-bar', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });
await render(hbs`<MobileBar />`);
assert.dom(this.element).hasText('');
// Template block usage:
await render(hbs`
<MobileBar>
template block text
</MobileBar>
`);
assert.dom(this.element).hasText('template block text');
});
});

View file

@ -0,0 +1,26 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'meiliadmin/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | sidenav-background', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });
await render(hbs`<SidenavBackground />`);
assert.dom(this.element).hasText('');
// Template block usage:
await render(hbs`
<SidenavBackground>
template block text
</SidenavBackground>
`);
assert.dom(this.element).hasText('template block text');
});
});