mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 13:17:00 +04:00
Add floating offcanvas and navbar variants with theme settings (#3100)
This commit is contained in:
1 parent
2b99ed5d38
commit
7e7b2cea34
24 files changed
+308
-38
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@tabler/core": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixed the menu row of the two-row navbar staying light with `data-bs-navbar-theme` set to `dark` or `primary`.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@tabler/core": minor
|
||||||
|
"@tabler/preview": minor
|
||||||
|
"@tabler/docs": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added `.navbar-floating` and the `data-bs-navbar-style="floating"` theme setting for a navbar and sidebar with a `0.5rem` gap.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@tabler/core": minor
|
||||||
|
"@tabler/preview": minor
|
||||||
|
"@tabler/docs": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added `.offcanvas-floating` and the `data-bs-offcanvas-style="floating"` theme setting for an offcanvas with a `0.5rem` gap.
|
||||||
@@ -14,7 +14,9 @@ export const themeDefaults = {
|
|||||||
'layout': 'default',
|
'layout': 'default',
|
||||||
'navbar': 'default',
|
'navbar': 'default',
|
||||||
'navbar-position': 'horizontal',
|
'navbar-position': 'horizontal',
|
||||||
|
'navbar-style': 'default',
|
||||||
'navbar-theme': 'default',
|
'navbar-theme': 'default',
|
||||||
|
'offcanvas-style': 'default',
|
||||||
'sidebar': 'default',
|
'sidebar': 'default',
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -82,19 +82,19 @@
|
|||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
"path": "./dist/css/tabler.css",
|
"path": "./dist/css/tabler.css",
|
||||||
"maxSize": "86 kB"
|
"maxSize": "87 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/css/tabler.min.css",
|
"path": "./dist/css/tabler.min.css",
|
||||||
"maxSize": "80 kB"
|
"maxSize": "81 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/css/tabler.rtl.css",
|
"path": "./dist/css/tabler.rtl.css",
|
||||||
"maxSize": "86 kB"
|
"maxSize": "87 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/css/tabler.rtl.min.css",
|
"path": "./dist/css/tabler.rtl.min.css",
|
||||||
"maxSize": "80 kB"
|
"maxSize": "81 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/css/tabler-flags.css",
|
"path": "./dist/css/tabler-flags.css",
|
||||||
|
|||||||
@@ -92,8 +92,13 @@
|
|||||||
// primary variant is the dark palette on a primary background (see
|
// primary variant is the dark palette on a primary background (see
|
||||||
// layout/_navbar.scss). The default value writes no attribute and means
|
// layout/_navbar.scss). The default value writes no attribute and means
|
||||||
// "follow the page", so there is no rule for it.
|
// "follow the page", so there is no rule for it.
|
||||||
|
//
|
||||||
|
// The two-row navbar keeps its menu row in a collapse wrapper next to the
|
||||||
|
// header, so that row is matched on its own.
|
||||||
html[data-bs-navbar-theme='dark'] .page > .navbar,
|
html[data-bs-navbar-theme='dark'] .page > .navbar,
|
||||||
html[data-bs-navbar-theme='primary'] .page > .navbar {
|
html[data-bs-navbar-theme='primary'] .page > .navbar,
|
||||||
|
html[data-bs-navbar-theme='dark'] .page > [class*='navbar-expand'] .navbar,
|
||||||
|
html[data-bs-navbar-theme='primary'] .page > [class*='navbar-expand'] .navbar {
|
||||||
@extend [data-bs-theme='dark'];
|
@extend [data-bs-theme='dark'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1567,6 +1567,8 @@ $navbar-hover-color: var(--body-color) !default;
|
|||||||
|
|
||||||
$navbar-border-width: var(--border-width) !default;
|
$navbar-border-width: var(--border-width) !default;
|
||||||
$navbar-border-color: var(--border-color) !default;
|
$navbar-border-color: var(--border-color) !default;
|
||||||
|
$navbar-floating-margin: 0.5rem !default;
|
||||||
|
$navbar-floating-border-radius: var(--border-radius-lg) !default;
|
||||||
|
|
||||||
$navbar-light-color: var(--body-color) !default;
|
$navbar-light-color: var(--body-color) !default;
|
||||||
$navbar-light-brand-color: var(--body-color) !default;
|
$navbar-light-brand-color: var(--body-color) !default;
|
||||||
@@ -2163,6 +2165,8 @@ $offcanvas-box-shadow: $modal-content-box-shadow-xs !default;
|
|||||||
$offcanvas-backdrop-bg: $backdrop-bg !default;
|
$offcanvas-backdrop-bg: $backdrop-bg !default;
|
||||||
$offcanvas-backdrop-opacity: $modal-backdrop-opacity !default;
|
$offcanvas-backdrop-opacity: $modal-backdrop-opacity !default;
|
||||||
$offcanvas-narrow-width: 20rem !default;
|
$offcanvas-narrow-width: 20rem !default;
|
||||||
|
$offcanvas-margin: 0.5rem !default;
|
||||||
|
$offcanvas-border-radius: $modal-content-border-radius !default;
|
||||||
// scss-docs-end offcanvas-variables
|
// scss-docs-end offcanvas-variables
|
||||||
|
|
||||||
// Chat
|
// Chat
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
--offcanvas-height: #{$offcanvas-vertical-height};
|
--offcanvas-height: #{$offcanvas-vertical-height};
|
||||||
--offcanvas-padding-x: #{$offcanvas-padding-x};
|
--offcanvas-padding-x: #{$offcanvas-padding-x};
|
||||||
--offcanvas-padding-y: #{$offcanvas-padding-y};
|
--offcanvas-padding-y: #{$offcanvas-padding-y};
|
||||||
|
--offcanvas-margin: #{$offcanvas-margin};
|
||||||
--offcanvas-color: #{$offcanvas-color};
|
--offcanvas-color: #{$offcanvas-color};
|
||||||
--offcanvas-bg: #{$offcanvas-bg-color};
|
--offcanvas-bg: #{$offcanvas-bg-color};
|
||||||
--offcanvas-border-width: #{$offcanvas-border-width};
|
--offcanvas-border-width: #{$offcanvas-border-width};
|
||||||
@@ -103,6 +104,7 @@
|
|||||||
@include media-breakpoint-up($next) {
|
@include media-breakpoint-up($next) {
|
||||||
--offcanvas-height: auto;
|
--offcanvas-height: auto;
|
||||||
--offcanvas-border-width: 0;
|
--offcanvas-border-width: 0;
|
||||||
|
--offcanvas-margin: 0px;
|
||||||
background-color: transparent !important; // stylelint-disable-line declaration-no-important
|
background-color: transparent !important; // stylelint-disable-line declaration-no-important
|
||||||
|
|
||||||
.offcanvas-header {
|
.offcanvas-header {
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ Navbar
|
|||||||
&.navbar-vertical {
|
&.navbar-vertical {
|
||||||
~ .navbar,
|
~ .navbar,
|
||||||
~ .page-wrapper {
|
~ .page-wrapper {
|
||||||
margin-inline-start: var(--sidebar-width);
|
margin-inline-start: calc(var(--sidebar-width) + var(--sidebar-gap, 0px));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +455,7 @@ Navbar
|
|||||||
~ .navbar,
|
~ .navbar,
|
||||||
~ .page-wrapper {
|
~ .page-wrapper {
|
||||||
margin-inline-start: 0;
|
margin-inline-start: 0;
|
||||||
margin-inline-end: var(--sidebar-width);
|
margin-inline-end: calc(var(--sidebar-width) + var(--sidebar-gap, 0px));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -624,6 +624,12 @@ html[data-bs-navbar-theme='primary'] .page > .navbar {
|
|||||||
--navbar-border-color: transparent;
|
--navbar-border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The menu row of the two-row navbar, which is not a direct child of .page.
|
||||||
|
html[data-bs-navbar-theme='primary'] .page > [class*='navbar-expand'] .navbar {
|
||||||
|
--navbar-bg: var(--primary);
|
||||||
|
--navbar-border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Navbar vertical
|
Navbar vertical
|
||||||
*/
|
*/
|
||||||
@@ -677,9 +683,12 @@ Navbar vertical
|
|||||||
// The hidden aside must leave no gap: its siblings offset themselves by
|
// The hidden aside must leave no gap: its siblings offset themselves by
|
||||||
// --sidebar-width, and this declaration sits on the same elements as the
|
// --sidebar-width, and this declaration sits on the same elements as the
|
||||||
// folded copy above, so it wins by coming later.
|
// folded copy above, so it wins by coming later.
|
||||||
|
// A length, not a bare 0: the offset is added up in calc(). A floating
|
||||||
|
// navbar keeps its own gap from the viewport edge.
|
||||||
> .navbar,
|
> .navbar,
|
||||||
> .page-wrapper {
|
> .page-wrapper {
|
||||||
--sidebar-width: 0;
|
--sidebar-width: 0px;
|
||||||
|
--sidebar-gap: var(--navbar-margin, 0px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,3 +873,58 @@ Navbar vertical
|
|||||||
box-shadow: inherit;
|
box-shadow: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Navbar floating
|
||||||
|
*/
|
||||||
|
// The navbar keeps a gap from the edges around it, like .offcanvas-floating,
|
||||||
|
// with an outline on every side instead of the single border line.
|
||||||
|
//
|
||||||
|
// Two routes to the same state: the .navbar-floating class on the navbar
|
||||||
|
// (static markup), or data-bs-navbar-style="floating" on <html> (the theme
|
||||||
|
// settings), which covers whichever navigation the page shows — the menu row
|
||||||
|
// of the two-row navbar included, which inherits the variables from its
|
||||||
|
// navbar-expand-* wrapper.
|
||||||
|
//
|
||||||
|
// The variables exist only from the navbar-expand-* breakpoint up, so below it
|
||||||
|
// every fallback applies and the navbar stays attached.
|
||||||
|
$navbar-floating-attr: "html[data-bs-navbar-style='floating'] .page >";
|
||||||
|
|
||||||
|
.navbar-floating,
|
||||||
|
#{$navbar-floating-attr} .navbar,
|
||||||
|
#{$navbar-floating-attr} [class*='navbar-expand'] .navbar {
|
||||||
|
margin: var(--navbar-margin, 0);
|
||||||
|
outline: var(--navbar-outline-width, 0) solid var(--navbar-border-color);
|
||||||
|
outline-offset: calc(-1 * var(--navbar-outline-width, 0px));
|
||||||
|
@include border-radius(var(--navbar-border-radius, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$navbar-floating-attr} [class*='navbar-expand'] .navbar {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $breakpoint in map.keys($grid-breakpoints) {
|
||||||
|
$next: breakpoint-next($breakpoint, $grid-breakpoints);
|
||||||
|
$infix: breakpoint-infix($next, $grid-breakpoints);
|
||||||
|
|
||||||
|
@include media-breakpoint-up($next) {
|
||||||
|
.navbar-floating.navbar-expand#{$infix},
|
||||||
|
#{$navbar-floating-attr} .navbar-expand#{$infix} {
|
||||||
|
--navbar-margin: #{$navbar-floating-margin};
|
||||||
|
--navbar-border-radius: #{$navbar-floating-border-radius};
|
||||||
|
--navbar-outline-width: var(--navbar-border-width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The page next to a floating sidebar moves over by the sidebar's two margins,
|
||||||
|
// and a floating navbar next to any sidebar keeps its own gap from it. The
|
||||||
|
// sibling offsets read --sidebar-gap only from the sidebar's breakpoint up.
|
||||||
|
.navbar-vertical ~ .navbar-floating {
|
||||||
|
--sidebar-gap: var(--navbar-margin, 0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-floating.navbar-vertical ~ :is(.navbar, .page-wrapper),
|
||||||
|
#{$navbar-floating-attr} .navbar-vertical ~ :is(.navbar, .page-wrapper) {
|
||||||
|
--sidebar-gap: calc(2 * #{$navbar-floating-margin});
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@ describe('navbar position', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('drops the sidebar offset from the page with no sidebar on it', () => {
|
it('drops the sidebar offset from the page with no sidebar on it', () => {
|
||||||
expect(flat).toMatch(/html:not\(\[data-bs-navbar-position=vertical\]\) \.page:has\(> \[class\*=navbar-expand\]:not\(\.navbar-vertical\)\) > \.navbar,[^{]*> \.page-wrapper \{ --sidebar-width: 0; \}/)
|
expect(flat).toMatch(/html:not\(\[data-bs-navbar-position=vertical\]\) \.page:has\(> \[class\*=navbar-expand\]:not\(\.navbar-vertical\)\) > \.navbar,[^{]*> \.page-wrapper \{ --sidebar-width: 0px; --sidebar-gap: var\(--navbar-margin, 0px\); \}/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('hides the horizontal navbar in the vertical position', () => {
|
it('hides the horizontal navbar in the vertical position', () => {
|
||||||
|
|||||||
@@ -30,3 +30,46 @@
|
|||||||
--offcanvas-narrow-width: #{$offcanvas-narrow-width};
|
--offcanvas-narrow-width: #{$offcanvas-narrow-width};
|
||||||
--offcanvas-width: var(--offcanvas-narrow-width);
|
--offcanvas-width: var(--offcanvas-narrow-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Floating variant: the panel keeps a gap from the viewport edges. Two routes
|
||||||
|
// to the same state: the .offcanvas-floating class on the panel, or
|
||||||
|
// data-bs-offcanvas-style="floating" on <html> (the theme settings), which
|
||||||
|
// covers every panel on the page — found by its direction class.
|
||||||
|
//
|
||||||
|
// --offcanvas-margin is 0px above the breakpoint of a responsive variant, so
|
||||||
|
// the inline content there is left alone.
|
||||||
|
$offcanvas-floating-attr: "html[data-bs-offcanvas-style='floating']";
|
||||||
|
|
||||||
|
.offcanvas-floating,
|
||||||
|
#{$offcanvas-floating-attr} :is(.offcanvas-start, .offcanvas-end, .offcanvas-top, .offcanvas-bottom) {
|
||||||
|
--offcanvas-transition: #{transform $offcanvas-transition-duration ease-in-out, translate $offcanvas-transition-duration ease-in-out};
|
||||||
|
max-width: calc(100% - 2 * var(--offcanvas-margin));
|
||||||
|
max-height: calc(100% - 2 * var(--offcanvas-margin));
|
||||||
|
margin: var(--offcanvas-margin);
|
||||||
|
border: var(--offcanvas-border-width) solid var(--offcanvas-border-color);
|
||||||
|
border-radius: $offcanvas-border-radius;
|
||||||
|
|
||||||
|
// The closed panel has to travel past its own margin to leave the viewport.
|
||||||
|
&.offcanvas-start {
|
||||||
|
/* rtl:ignore */
|
||||||
|
translate: calc(var(--dir) * -1 * var(--offcanvas-margin));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.offcanvas-end {
|
||||||
|
/* rtl:ignore */
|
||||||
|
translate: calc(var(--dir) * var(--offcanvas-margin));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.offcanvas-top {
|
||||||
|
translate: 0 calc(-1 * var(--offcanvas-margin));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.offcanvas-bottom {
|
||||||
|
translate: 0 var(--offcanvas-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.showing,
|
||||||
|
&.show:not(.hiding) {
|
||||||
|
translate: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,8 @@ classnames:
|
|||||||
modifier:
|
modifier:
|
||||||
- class: offcanvas-narrow
|
- class: offcanvas-narrow
|
||||||
desc: Narrower panel, 20rem instead of the default width
|
desc: Narrower panel, 20rem instead of the default width
|
||||||
|
- class: offcanvas-floating
|
||||||
|
desc: Keeps a 0.5rem gap between the panel and the viewport edges
|
||||||
direction:
|
direction:
|
||||||
- class: offcanvas-start
|
- class: offcanvas-start
|
||||||
desc: Slides in from the leading edge
|
desc: Slides in from the leading edge
|
||||||
@@ -64,6 +66,18 @@ Add `offcanvas-narrow` for a panel thinner than the default. It suits a short li
|
|||||||
<div class="offcanvas offcanvas-end offcanvas-narrow show position-static" tabindex="-1" id="offcanvas-narrow" aria-labelledby="offcanvas-narrow-label"> <div class="offcanvas-header"> <h2 class="offcanvas-title" id="offcanvas-narrow-label">Filters</h2> <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button> </div> <div class="offcanvas-body">Pick the filters you want to apply.</div> <div class="offcanvas-footer"> <button type="button" class="btn btn-primary w-100">Apply</button> </div> </div>
|
<div class="offcanvas offcanvas-end offcanvas-narrow show position-static" tabindex="-1" id="offcanvas-narrow" aria-labelledby="offcanvas-narrow-label"> <div class="offcanvas-header"> <h2 class="offcanvas-title" id="offcanvas-narrow-label">Filters</h2> <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button> </div> <div class="offcanvas-body">Pick the filters you want to apply.</div> <div class="offcanvas-footer"> <button type="button" class="btn btn-primary w-100">Apply</button> </div> </div>
|
||||||
</Example>
|
</Example>
|
||||||
|
|
||||||
|
## Floating panel
|
||||||
|
|
||||||
|
Add `offcanvas-floating` to keep a gap between the panel and the edges of the screen. The panel gets a border on every side and rounded corners, so it looks like a card over the page. It works with every direction and with the responsive `offcanvas-{breakpoint}` classes.
|
||||||
|
|
||||||
|
<Example raw code={`<div class="offcanvas offcanvas-end offcanvas-floating" tabindex="-1" id="offcanvas-floating" aria-labelledby="offcanvas-floating-label"> <div class="offcanvas-header"> <h2 class="offcanvas-title" id="offcanvas-floating-label">Settings</h2> <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button> </div> <div class="offcanvas-body">The panel does not touch the edges of the screen.</div> </div>`}>
|
||||||
|
<div class="offcanvas offcanvas-end offcanvas-floating show position-static" tabindex="-1" id="offcanvas-floating" aria-labelledby="offcanvas-floating-label"> <div class="offcanvas-header"> <h2 class="offcanvas-title" id="offcanvas-floating-label">Settings</h2> <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button> </div> <div class="offcanvas-body">The panel does not touch the edges of the screen.</div> </div>
|
||||||
|
</Example>
|
||||||
|
|
||||||
|
The gap is `0.5rem` by default. Change it with the `--tblr-offcanvas-margin` variable, or with `$offcanvas-margin` in SCSS.
|
||||||
|
|
||||||
|
To float every panel on the page without touching the markup, set [`data-bs-offcanvas-style="floating"`](/ui/layout/page-layouts#floating-offcanvas) on `<html>`.
|
||||||
|
|
||||||
## Cookies banner
|
## Cookies banner
|
||||||
|
|
||||||
An offcanvas at the bottom edge also works as a cookie banner. It stays visible until the user answers, and it doesn't cover the page.
|
An offcanvas at the bottom edge also works as a cookie banner. It stays visible until the user answers, and it doesn't cover the page.
|
||||||
|
|||||||
@@ -82,6 +82,40 @@ Inside it, a `container-xl` keeps the content aligned with the page. The full ma
|
|||||||
</header>
|
</header>
|
||||||
</Example>
|
</Example>
|
||||||
|
|
||||||
|
## Floating navbar
|
||||||
|
|
||||||
|
Add `navbar-floating` to keep a gap between the navbar and the edges around it. The navbar gets a border on every side and rounded corners. It floats only from its `navbar-expand-*` breakpoint up. Below the breakpoint it stays attached to the edges, like a regular navbar.
|
||||||
|
|
||||||
|
<Example code={`<header class="navbar navbar-expand-md navbar-floating d-print-none">...</header>`}>
|
||||||
|
<header class="navbar navbar-expand navbar-floating d-print-none">
|
||||||
|
<div class="container-xl">
|
||||||
|
<Logo class="me-3" />
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" href="#" aria-current="page">
|
||||||
|
<span class="nav-link-title">Home</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">
|
||||||
|
<span class="nav-link-title">Profile</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</Example>
|
||||||
|
|
||||||
|
The class works on the vertical sidebar too. The sidebar keeps its width, also when it is folded, and the page content next to it moves over by the same gap.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<aside class="navbar navbar-vertical navbar-expand-lg navbar-floating">...</aside>
|
||||||
|
```
|
||||||
|
|
||||||
|
The gap is `0.5rem` by default. Change it with `$navbar-floating-margin` in SCSS.
|
||||||
|
|
||||||
|
To float the navigation of the whole page without touching the markup, set [`data-bs-navbar-style="floating"`](/ui/layout/page-layouts#floating-navigation) on `<html>`.
|
||||||
|
|
||||||
## Language selector
|
## Language selector
|
||||||
|
|
||||||
Add a language selector to the navbar as a dropdown. The toggle shows the current language code and the menu lists the languages with a `data-lang` attribute, so a script can switch the locale. Give the toggle an `aria-label`, because the code alone does not say what the control does.
|
Add a language selector to the navbar as a dropdown. The toggle shows the current language code and the menu lists the languages with a `data-lang` attribute, so a script can switch the locale. Give the toggle an `aria-label`, because the code alone does not say what the control does.
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ You can also add tooltips with the link title to icon-only links, so users see t
|
|||||||
|
|
||||||
## Layout options
|
## Layout options
|
||||||
|
|
||||||
Beyond the sidebar, four more layout choices are available as attributes on `<html>`, applied before the page renders by [`tabler-theme.js`](/ui/getting-started/color-modes). Every one of them has a static route too, so you can hard-code a layout without any JavaScript.
|
Beyond the sidebar, six more layout choices are available as attributes on `<html>`, applied before the page renders by [`tabler-theme.js`](/ui/getting-started/color-modes). Every one of them has a static route too, so you can hard-code a layout without any JavaScript.
|
||||||
|
|
||||||
### Container width
|
### Container width
|
||||||
|
|
||||||
@@ -444,6 +444,30 @@ Use one route or the other, not both. The boxed frame is the containing block of
|
|||||||
|
|
||||||
Only the navbar that is a direct child of `.page` sticks. On the two-row navbar the menu row scrolls away underneath, so the sticky strip stays one row high. Without the attribute you can still wrap a navbar in your own `.sticky-top` element.
|
Only the navbar that is a direct child of `.page` sticks. On the two-row navbar the menu row scrolls away underneath, so the sticky strip stays one row high. Without the attribute you can still wrap a navbar in your own `.sticky-top` element.
|
||||||
|
|
||||||
|
### Floating navigation
|
||||||
|
|
||||||
|
`data-bs-navbar-style="floating"` keeps a gap between the navigation and the edges around it, and gives it a border and rounded corners:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<html data-bs-navbar-style="floating">
|
||||||
|
```
|
||||||
|
|
||||||
|
The attribute covers whichever navigation the page shows. The sidebar keeps its width, and the page next to it moves over by the same gap. On the two-row navbar both rows float. The navigation floats only from its `navbar-expand-*` breakpoint up. Without the attribute, add the [`navbar-floating`](/ui/layout/navbars#floating-navbar) class to a navbar in your markup:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<header class="navbar navbar-expand-md navbar-floating">...</header>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Floating offcanvas
|
||||||
|
|
||||||
|
`data-bs-offcanvas-style="floating"` does the same for every [offcanvas](/ui/components/offcanvas#floating-panel) on the page:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<html data-bs-offcanvas-style="floating">
|
||||||
|
```
|
||||||
|
|
||||||
|
Without the attribute, add the `offcanvas-floating` class to a single panel.
|
||||||
|
|
||||||
### Navigation position
|
### Navigation position
|
||||||
|
|
||||||
`data-bs-navbar-position` picks which navigation the page shows:
|
`data-bs-navbar-position` picks which navigation the page shows:
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ import HomepageContent from '@shared/components/HomepageContent.astro'
|
|||||||
import HeaderActionsButtons from '@shared/components/layout/HeaderActionsButtons.astro'
|
import HeaderActionsButtons from '@shared/components/layout/HeaderActionsButtons.astro'
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="Dashboard" pageHeader="Navbar overlap layout" pretitle="Overview" pageMenu="layout.navbar-overlap" pageLibs={['apexcharts', 'jsvectormap']} navbarOverlap navbarCondensed navbarDark>
|
<DefaultLayout title="Dashboard" pageHeader="Navbar floating" pretitle="Overview" pageMenu="layout.navbar-floating" pageLibs={['apexcharts', 'jsvectormap']} navbarCondensed navbarFloating>
|
||||||
<HeaderActionsButtons slot="page-header-actions" dark />
|
<HeaderActionsButtons slot="page-header-actions" />
|
||||||
<HomepageContent />
|
<HomepageContent />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||||
|
import HomepageContent from '@shared/components/HomepageContent.astro'
|
||||||
|
import HeaderActionsButtons from '@shared/components/layout/HeaderActionsButtons.astro'
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultLayout title="Dashboard" pageHeader="Vertical floating layout" pretitle="Overview" pageMenu="layout.vertical-floating" pageLibs={['apexcharts', 'jsvectormap']} navbarPosition="vertical" navbarFloating>
|
||||||
|
<HeaderActionsButtons slot="page-header-actions" />
|
||||||
|
<HomepageContent />
|
||||||
|
</DefaultLayout>
|
||||||
@@ -4,35 +4,51 @@ import { ucFirst as capitalize } from '@shared/lib/string-format'
|
|||||||
import ButtonList from '@ui/ButtonList.astro'
|
import ButtonList from '@ui/ButtonList.astro'
|
||||||
import Card from '@ui/Card.astro'
|
import Card from '@ui/Card.astro'
|
||||||
import CardBody from '@ui/CardBody.astro'
|
import CardBody from '@ui/CardBody.astro'
|
||||||
|
import CardHeader from '@ui/CardHeader.astro'
|
||||||
|
import CardTitle from '@ui/CardTitle.astro'
|
||||||
import Offcanvas from '@ui/Offcanvas.astro'
|
import Offcanvas from '@ui/Offcanvas.astro'
|
||||||
import OffcanvasHeader from '@ui/OffcanvasHeader.astro'
|
import OffcanvasHeader from '@ui/OffcanvasHeader.astro'
|
||||||
import OffcanvasBody from '@ui/OffcanvasBody.astro'
|
import OffcanvasBody from '@ui/OffcanvasBody.astro'
|
||||||
import DocsLink from '@ui/DocsLink.astro'
|
import DocsLink from '@ui/DocsLink.astro'
|
||||||
|
|
||||||
const directions = ['start', 'end', 'top', 'bottom'] as const
|
const directions = ['start', 'end', 'top', 'bottom'] as const
|
||||||
|
const variants = [
|
||||||
|
{ id: 'offcanvas', title: 'Default', floating: false },
|
||||||
|
{ id: 'offcanvasFloating', title: 'Floating', floating: true },
|
||||||
|
]
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="Offcanvas" pageMenu="base.offcanvas" description="An offcanvas panel slides in from an edge of the screen for navigation or supplementary content.">
|
<DefaultLayout title="Offcanvas" pageMenu="base.offcanvas" description="An offcanvas panel slides in from an edge of the screen for navigation or supplementary content.">
|
||||||
<DocsLink slot="page-header-actions" path="/ui/components/offcanvas" />
|
<DocsLink slot="page-header-actions" path="/ui/components/offcanvas" />
|
||||||
|
|
||||||
|
<div class="row row-cards">
|
||||||
|
{
|
||||||
|
variants.map((variant) => (
|
||||||
|
<div class="col-12">
|
||||||
<Card>
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{variant.title}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<ButtonList>
|
<ButtonList>
|
||||||
{
|
{directions.map((direction) => (
|
||||||
directions.map((direction) => (
|
<a class="btn" data-bs-toggle="offcanvas" href={`#${variant.id}${capitalize(direction)}`} role="button" aria-controls={`${variant.id}${capitalize(direction)}`}>
|
||||||
<a class="btn" data-bs-toggle="offcanvas" href={`#offcanvas${capitalize(direction)}`} role="button" aria-controls={`offcanvas${capitalize(direction)}`}>
|
|
||||||
Toggle {direction} offcanvas
|
Toggle {direction} offcanvas
|
||||||
</a>
|
</a>
|
||||||
))
|
))}
|
||||||
}
|
|
||||||
</ButtonList>
|
</ButtonList>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
|
variants.map((variant) =>
|
||||||
directions.map((direction) => (
|
directions.map((direction) => (
|
||||||
<Offcanvas direction={direction} tabindex="-1" id={`offcanvas${capitalize(direction)}`} aria-labelledby={`offcanvas${capitalize(direction)}Label`}>
|
<Offcanvas direction={direction} floating={variant.floating} tabindex="-1" id={`${variant.id}${capitalize(direction)}`} aria-labelledby={`${variant.id}${capitalize(direction)}Label`}>
|
||||||
<OffcanvasHeader title={`${capitalize(direction)} offcanvas`} titleId={`offcanvas${capitalize(direction)}Label`}>
|
<OffcanvasHeader title={`${capitalize(direction)} offcanvas`} titleId={`${variant.id}${capitalize(direction)}Label`}>
|
||||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close" />
|
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close" />
|
||||||
</OffcanvasHeader>
|
</OffcanvasHeader>
|
||||||
<OffcanvasBody>
|
<OffcanvasBody>
|
||||||
@@ -44,6 +60,7 @@ const directions = ['start', 'end', 'top', 'bottom'] as const
|
|||||||
</div>
|
</div>
|
||||||
</OffcanvasBody>
|
</OffcanvasBody>
|
||||||
</Offcanvas>
|
</Offcanvas>
|
||||||
))
|
)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
@@ -68,6 +68,16 @@ const tileSvg = (key: string, value: string): string => {
|
|||||||
return value === 'sticky'
|
return value === 'sticky'
|
||||||
? svg(frame(page) + lines(12, 4, [46, 72, 30, 60], border) + thumb(44) + `<rect x="0.5" y="19.5" width="95" height="5" fill="var(--tblr-gray-900)" opacity="0.18" />` + topBar(primary) + outline)
|
? svg(frame(page) + lines(12, 4, [46, 72, 30, 60], border) + thumb(44) + `<rect x="0.5" y="19.5" width="95" height="5" fill="var(--tblr-gray-900)" opacity="0.18" />` + topBar(primary) + outline)
|
||||||
: svg(frame(page) + topBar(primary) + lines(12, 30, [46, 72, 30], border) + thumb(24) + outline)
|
: svg(frame(page) + topBar(primary) + lines(12, 30, [46, 72, 30], border) + thumb(24) + outline)
|
||||||
|
case 'navbar-style':
|
||||||
|
// Floating: the bar keeps a gap from the edges and gets its own corners.
|
||||||
|
return value === 'floating'
|
||||||
|
? svg(frame(page) + `<rect x="5" y="5" width="86" height="14" rx="4" fill="${primary}" />` + [11, 29, 47].map((x) => `<rect x="${x}" y="10" width="14" height="4" rx="2" fill="${onAccent}" />`).join('') + lines(12, 30, [46, 72, 30], border) + outline)
|
||||||
|
: svg(frame(page) + topBar(primary) + [8, 26, 44].map((x) => `<rect x="${x}" y="8" width="14" height="4" rx="2" fill="${onAccent}" />`).join('') + lines(12, 30, [46, 72, 30], border) + outline)
|
||||||
|
case 'offcanvas-style':
|
||||||
|
// A panel open on the end side, over the dimmed page: flush with the edges, or a card with a gap.
|
||||||
|
return value === 'floating'
|
||||||
|
? svg(frame(page) + lines(10, 12, [40, 48, 28], border) + `<rect x="0.5" y="0.5" width="95" height="63" rx="6" fill="var(--tblr-gray-900)" opacity="0.18" /><rect x="53" y="5" width="38" height="54" rx="4" fill="${surface}" stroke="${border}" />` + lines(59, 12, [20, 26, 14], border) + outline)
|
||||||
|
: svg(frame(page) + lines(10, 12, [40, 48, 28], border) + `<rect x="0.5" y="0.5" width="95" height="63" rx="6" fill="var(--tblr-gray-900)" opacity="0.18" /><path d="M58 0.5h31.5a6 6 0 0 1 6 6v51a6 6 0 0 1 -6 6h-31.5z" fill="${surface}" /><rect x="57.5" y="0.5" width="0.5" height="63" fill="${border}" />` + lines(64, 12, [20, 26, 14], border) + outline)
|
||||||
case 'sidebar':
|
case 'sidebar':
|
||||||
if (value === 'folded') return svg(frame(page) + rail(16, primary) + dots(8, 12, 4, onAccent) + lines(26, 12, [50, 60, 34], border) + outline)
|
if (value === 'folded') return svg(frame(page) + rail(16, primary) + dots(8, 12, 4, onAccent) + lines(26, 12, [50, 60, 34], border) + outline)
|
||||||
if (value === 'folded-hover') return svg(frame(page) + rail(16, primary) + dots(8, 12, 4, onAccent) + lines(26, 12, [50, 60, 34], border) + `<rect x="16" y="0.5" width="4" height="63" fill="var(--tblr-gray-900)" opacity="0.12" /><rect x="16.5" y="0.5" width="28" height="63" fill="${surface}" stroke="${border}" />` + lines(21, 9.5, [18, 18, 18, 18], border, 11) + outline)
|
if (value === 'folded-hover') return svg(frame(page) + rail(16, primary) + dots(8, 12, 4, onAccent) + lines(26, 12, [50, 60, 34], border) + `<rect x="16" y="0.5" width="4" height="63" fill="var(--tblr-gray-900)" opacity="0.12" /><rect x="16.5" y="0.5" width="28" height="63" fill="${surface}" stroke="${border}" />` + lines(21, 9.5, [18, 18, 18, 18], border, 11) + outline)
|
||||||
@@ -93,7 +103,7 @@ const tileSvg = (key: string, value: string): string => {
|
|||||||
<Icon name="brush" />
|
<Icon name="brush" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<form class="offcanvas offcanvas-start" tabindex="-1" id="offcanvas-settings" role="dialog" aria-modal="true" aria-labelledby="offcanvas-settings-title">
|
<form class="offcanvas offcanvas-start offcanvas-floating" tabindex="-1" id="offcanvas-settings" role="dialog" aria-modal="true" aria-labelledby="offcanvas-settings-title">
|
||||||
<button type="button" class="btn-close position-absolute top-0 end-0 m-3 z-1" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
<button type="button" class="btn-close position-absolute top-0 end-0 m-3 z-1" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||||
|
|
||||||
{/* The body is the only scrolling area, so the actions below stay reachable. */}
|
{/* The body is the only scrolling area, so the actions below stay reachable. */}
|
||||||
|
|||||||
@@ -14,11 +14,9 @@ interface Props {
|
|||||||
class?: string;
|
class?: string;
|
||||||
/** name of a layout/headers/ component rendered instead of the title block, e.g. "profile" */
|
/** name of a layout/headers/ component rendered instead of the title block, e.g. "profile" */
|
||||||
file?: string | undefined;
|
file?: string | undefined;
|
||||||
/** text-white on .page-header (dark overlapping navbar) */
|
|
||||||
textWhite?: boolean | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, pretitle, description, class: className, file, textWhite } = Astro.props;
|
const { title, pretitle, description, class: className, file } = Astro.props;
|
||||||
|
|
||||||
const actionsSlot = ((await Astro.slots.render('actions')) ?? '').trim();
|
const actionsSlot = ((await Astro.slots.render('actions')) ?? '').trim();
|
||||||
---
|
---
|
||||||
@@ -27,7 +25,7 @@ const actionsSlot = ((await Astro.slots.render('actions')) ?? '').trim();
|
|||||||
{file === 'uptime' && <HeaderUptime />}
|
{file === 'uptime' && <HeaderUptime />}
|
||||||
{
|
{
|
||||||
!file && title && (
|
!file && title && (
|
||||||
<div class={`page-header${className ? ` ${className}` : ''} d-print-none${textWhite ? ' text-white' : ''}`}>
|
<div class={`page-header${className ? ` ${className}` : ''} d-print-none`}>
|
||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
<div class="row g-2 align-items-center">
|
<div class="row g-2 align-items-center">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ interface Props {
|
|||||||
condensed?: boolean | undefined;
|
condensed?: boolean | undefined;
|
||||||
dark?: boolean | undefined;
|
dark?: boolean | undefined;
|
||||||
transparent?: boolean;
|
transparent?: boolean;
|
||||||
overlap?: boolean | undefined;
|
|
||||||
hideBrand?: boolean | undefined;
|
hideBrand?: boolean | undefined;
|
||||||
/** use the sample menu (menu-sample.json) instead of the real one */
|
/** use the sample menu (menu-sample.json) instead of the real one */
|
||||||
sample?: boolean;
|
sample?: boolean;
|
||||||
@@ -41,7 +40,6 @@ const {
|
|||||||
condensed = false,
|
condensed = false,
|
||||||
dark = false,
|
dark = false,
|
||||||
transparent = false,
|
transparent = false,
|
||||||
overlap = false,
|
|
||||||
hideBrand = false,
|
hideBrand = false,
|
||||||
sample = false,
|
sample = false,
|
||||||
personId,
|
personId,
|
||||||
@@ -61,7 +59,6 @@ const breakpoint = 'md';
|
|||||||
const headerClass = [
|
const headerClass = [
|
||||||
`navbar navbar-expand-${breakpoint}`,
|
`navbar navbar-expand-${breakpoint}`,
|
||||||
transparent ? 'navbar-transparent' : background && `bg-${background}`,
|
transparent ? 'navbar-transparent' : background && `bg-${background}`,
|
||||||
overlap && 'navbar-overlap',
|
|
||||||
className,
|
className,
|
||||||
'd-print-none',
|
'd-print-none',
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -450,9 +450,9 @@
|
|||||||
"url": "layout-navbar-dark.html",
|
"url": "layout-navbar-dark.html",
|
||||||
"title": "Navbar dark"
|
"title": "Navbar dark"
|
||||||
},
|
},
|
||||||
"navbar-overlap": {
|
"navbar-floating": {
|
||||||
"url": "layout-navbar-overlap.html",
|
"url": "layout-navbar-floating.html",
|
||||||
"title": "Navbar overlap"
|
"title": "Navbar floating"
|
||||||
},
|
},
|
||||||
"navbar-sticky": {
|
"navbar-sticky": {
|
||||||
"url": "layout-navbar-sticky.html",
|
"url": "layout-navbar-sticky.html",
|
||||||
@@ -474,6 +474,10 @@
|
|||||||
"url": "layout-vertical-transparent.html",
|
"url": "layout-vertical-transparent.html",
|
||||||
"title": "Vertical transparent"
|
"title": "Vertical transparent"
|
||||||
},
|
},
|
||||||
|
"vertical-floating": {
|
||||||
|
"url": "layout-vertical-floating.html",
|
||||||
|
"title": "Vertical floating"
|
||||||
|
},
|
||||||
"folded": {
|
"folded": {
|
||||||
"url": "layout-folded.html",
|
"url": "layout-folded.html",
|
||||||
"title": "Folded sidebar",
|
"title": "Folded sidebar",
|
||||||
|
|||||||
@@ -46,13 +46,13 @@ interface Props {
|
|||||||
navbarCondensed?: boolean
|
navbarCondensed?: boolean
|
||||||
/** dark navbar */
|
/** dark navbar */
|
||||||
navbarDark?: boolean
|
navbarDark?: boolean
|
||||||
/** navbar overlapping the page header */
|
/** floating navigation: the navbar and the sidebar keep a gap from the edges */
|
||||||
navbarOverlap?: boolean
|
navbarFloating?: boolean
|
||||||
/** sticky navbar */
|
/** sticky navbar */
|
||||||
navbarSticky?: boolean
|
navbarSticky?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, pageLibs, pageHeader, pageHeaderFile, pretitle, description, pageMenu, navbarPosition, layout, containerCentered, wrapperFull, containerClass, bodyClass, rtl, sidebarEnd, sidebarMode, sidebarFoldToggle, navbarTransparent, navbarCondensed, navbarDark, navbarOverlap, navbarSticky } = Astro.props
|
const { title, pageLibs, pageHeader, pageHeaderFile, pretitle, description, pageMenu, navbarPosition, layout, containerCentered, wrapperFull, containerClass, bodyClass, rtl, sidebarEnd, sidebarMode, sidebarFoldToggle, navbarTransparent, navbarCondensed, navbarDark, navbarSticky, navbarFloating } = Astro.props
|
||||||
|
|
||||||
// Most pages repeat the page title verbatim, so `pageHeader` falls back to `title`.
|
// Most pages repeat the page title verbatim, so `pageHeader` falls back to `title`.
|
||||||
// Pages that deliberately have no header (blank, playgrounds) pass pageHeader={false}.
|
// Pages that deliberately have no header (blank, playgrounds) pass pageHeader={false}.
|
||||||
@@ -66,6 +66,7 @@ const themeSettings: Partial<Record<ThemeKey, string | undefined>> = {
|
|||||||
'navbar-position': navbarPosition,
|
'navbar-position': navbarPosition,
|
||||||
'layout': layout,
|
'layout': layout,
|
||||||
'navbar': navbarSticky ? 'sticky' : undefined,
|
'navbar': navbarSticky ? 'sticky' : undefined,
|
||||||
|
'navbar-style': navbarFloating ? 'floating' : undefined,
|
||||||
'navbar-theme': navbarDark ? 'dark' : undefined,
|
'navbar-theme': navbarDark ? 'dark' : undefined,
|
||||||
'sidebar': sidebarMode,
|
'sidebar': sidebarMode,
|
||||||
}
|
}
|
||||||
@@ -78,12 +79,12 @@ const themeSettings: Partial<Record<ThemeKey, string | undefined>> = {
|
|||||||
<!-- END SIDEBAR -->
|
<!-- END SIDEBAR -->
|
||||||
|
|
||||||
<!-- BEGIN NAVBAR -->
|
<!-- BEGIN NAVBAR -->
|
||||||
<Navbar hideSearch pageMenu={pageMenu} condensed={navbarCondensed} overlap={navbarOverlap} />
|
<Navbar hideSearch pageMenu={pageMenu} condensed={navbarCondensed} />
|
||||||
<!-- END NAVBAR -->
|
<!-- END NAVBAR -->
|
||||||
|
|
||||||
<div class:list={['page-wrapper', wrapperFull && 'page-wrapper-full']}>
|
<div class:list={['page-wrapper', wrapperFull && 'page-wrapper-full']}>
|
||||||
<!-- BEGIN PAGE HEADER -->
|
<!-- BEGIN PAGE HEADER -->
|
||||||
<PageHeader title={headerTitle} pretitle={pretitle} description={description} file={pageHeaderFile} textWhite={navbarOverlap && navbarDark}>
|
<PageHeader title={headerTitle} pretitle={pretitle} description={description} file={pageHeaderFile}>
|
||||||
<slot name="page-header-actions" slot="actions" />
|
<slot name="page-header-actions" slot="actions" />
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<!-- END PAGE HEADER -->
|
<!-- END PAGE HEADER -->
|
||||||
|
|||||||
@@ -139,6 +139,26 @@ export const themeSections: ThemeSection[] = [
|
|||||||
{ value: 'sticky', label: 'Sticky' },
|
{ value: 'sticky', label: 'Sticky' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'navbar-style',
|
||||||
|
legend: 'Navigation style',
|
||||||
|
hint: 'Attach the navigation to the edges, or let it float.',
|
||||||
|
control: 'tile',
|
||||||
|
options: [
|
||||||
|
{ value: 'default', label: 'Attached' },
|
||||||
|
{ value: 'floating', label: 'Floating' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'offcanvas-style',
|
||||||
|
legend: 'Offcanvas style',
|
||||||
|
hint: 'Attach side panels to the edges, or let them float.',
|
||||||
|
control: 'tile',
|
||||||
|
options: [
|
||||||
|
{ value: 'default', label: 'Attached' },
|
||||||
|
{ value: 'floating', label: 'Floating' },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'sidebar',
|
key: 'sidebar',
|
||||||
legend: 'Sidebar',
|
legend: 'Sidebar',
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ interface Props {
|
|||||||
direction?: 'start' | 'end' | 'top' | 'bottom' | undefined
|
direction?: 'start' | 'end' | 'top' | 'bottom' | undefined
|
||||||
/** responsive variant offcanvas-{responsive} ($grid-breakpoints minus xs) — replaces the base .offcanvas class */
|
/** responsive variant offcanvas-{responsive} ($grid-breakpoints minus xs) — replaces the base .offcanvas class */
|
||||||
responsive?: Exclude<Breakpoint, 'xs'> | undefined
|
responsive?: Exclude<Breakpoint, 'xs'> | undefined
|
||||||
|
/** core/scss/ui/_offcanvas.scss `.offcanvas-floating` — keeps a gap between the panel and the viewport edges */
|
||||||
|
floating?: boolean
|
||||||
show?: boolean
|
show?: boolean
|
||||||
class?: string
|
class?: string
|
||||||
/** defaults to "-1" when not provided */
|
/** defaults to "-1" when not provided */
|
||||||
@@ -17,11 +19,11 @@ interface Props {
|
|||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
const { direction, responsive, show, class: className, tabindex, role, ...rest } = Astro.props
|
const { direction, responsive, floating, show, class: className, tabindex, role, ...rest } = Astro.props
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- BEGIN OFFCANVAS -->
|
<!-- BEGIN OFFCANVAS -->
|
||||||
<div class:list={[responsive ? `offcanvas-${responsive}` : 'offcanvas', direction && `offcanvas-${direction}`, show && 'show', className]} tabindex={tabindex ?? '-1'} role={role ?? 'dialog'} {...rest}>
|
<div class:list={[responsive ? `offcanvas-${responsive}` : 'offcanvas', direction && `offcanvas-${direction}`, floating && 'offcanvas-floating', show && 'show', className]} tabindex={tabindex ?? '-1'} role={role ?? 'dialog'} {...rest}>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
<!-- END OFFCANVAS -->
|
<!-- END OFFCANVAS -->
|
||||||
Reference in new issue
Block a user