Skip to content

Commit c6375e5

Browse files
feat(b-sidebar): add optional backdrop support (bootstrap-vue#5182)
* feat(b-sidebar): add backdrop support * Update sidebar.js * Update sidebar.js * Update _sidebar.scss * Update sidebar.spec.js * Update sidebar.spec.js * Update _sidebar.scss * Update _sidebar.scss * Update styles.scss * Update _sidebar.scss * Update sidebar.js * Update sidebar.js * Update sidebar.js * lint * Update sidebar.js * Update package.json * Update sidebar.spec.js * Update sidebar.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.spec.js * Update README.md * Update README.md * Update README.md * Update README.md * Update dom.js * Update sidebar.js * Update dom.js * Update modal.js * Update modal.js * Update modal.js * Update dom.js * Update modal.js * lint * Update sidebar.js * Update sidebar.js * Update sidebar.js * Update sidebar.js * lint * Update sidebar.js * Update README.md * Update README.md * Update sidebar.js * Update sidebar.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.spec.js * Update sidebar.js * Update sidebar.js * Update sidebar.js * Update package.json * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update sidebar.js * Update _sidebar.scss * Update README.md * Update _sidebar.scss * Update dom.js Co-authored-by: Jacob Müller <jacob.mueller.elz@gmail.com>
1 parent 83180f1 commit c6375e5

File tree

8 files changed

+324
-78
lines changed

8 files changed

+324
-78
lines changed

docs/assets/scss/styles.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ table#table-transition-example {
271271
}
272272

273273
// `<b-sidebar>` overrides for docs
274-
.b-sidebar {
274+
.b-sidebar-outer,
275+
.b-sidebar{
275276
z-index: 1071;
276277
}
277278

src/components/modal/modal.js

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import identity from '../../utils/identity'
55
import observeDom from '../../utils/observe-dom'
66
import { arrayIncludes, concat } from '../../utils/array'
77
import { getComponentConfig } from '../../utils/config'
8-
import { closest, contains, isVisible, requestAF, select, selectAll } from '../../utils/dom'
8+
import { closest, contains, getTabables, isVisible, requestAF, select } from '../../utils/dom'
99
import { isBrowser } from '../../utils/env'
1010
import { EVENT_OPTIONS_NO_CAPTURE, eventOn, eventOff } from '../../utils/events'
1111
import { stripTags } from '../../utils/html'
@@ -37,20 +37,6 @@ const OBSERVER_CONFIG = {
3737
attributeFilter: ['style', 'class']
3838
}
3939

40-
// Query selector to find all tabbable elements
41-
// (includes tabindex="-1", which we filter out after)
42-
const TABABLE_SELECTOR = [
43-
'button',
44-
'[href]:not(.disabled)',
45-
'input',
46-
'select',
47-
'textarea',
48-
'[tabindex]',
49-
'[contenteditable]'
50-
]
51-
.map(s => `${s}:not(:disabled):not([disabled])`)
52-
.join(', ')
53-
5440
// --- Utility methods ---
5541

5642
// Attempt to focus an element, and return true if successful
@@ -564,14 +550,6 @@ export const BModal = /*#__PURE__*/ Vue.extend({
564550
}
565551
return null
566552
},
567-
// Private method to get a list of all tabable elements within modal content
568-
getTabables() {
569-
// Find all tabable elements in the modal content
570-
// Assumes users have not used tabindex > 0 on elements!
571-
return selectAll(TABABLE_SELECTOR, this.$refs.content)
572-
.filter(isVisible)
573-
.filter(i => i.tabIndex > -1 && !i.disabled)
574-
},
575553
// Private method to finish showing modal
576554
doShow() {
577555
/* istanbul ignore next: commenting out for now until we can test stacking */
@@ -727,7 +705,7 @@ export const BModal = /*#__PURE__*/ Vue.extend({
727705
) {
728706
return
729707
}
730-
const tabables = this.getTabables()
708+
const tabables = getTabables(this.$refs.content)
731709
const { bottomTrap, topTrap } = this.$refs
732710
if (bottomTrap && target === bottomTrap) {
733711
// If user pressed TAB out of modal into our bottom trab trap element

src/components/sidebar/README.md

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
> Otherwise known as off-canvas or a side drawer, BootstrapVue's custom `<b-sidebar>` component is a
44
> fixed-position toggleable slide out box, which can be used for navigation, menus, details, etc. It
5-
> can be positioned on either the left (default) or right of the viewport.
5+
> can be positioned on either the left (default) or right of the viewport, with optional backdrop
6+
> support.
67
78
## Overview
89

@@ -118,8 +119,28 @@ for no shadow.
118119
### Borders
119120

120121
By default, `<b-sidebar>` has no borders. Use
121-
[border utility classes](/docs/reference/utility-classes) to add border(s) to `<b-sidebar>`, or use
122-
CSS style overrides.
122+
[border utility classes](/docs/reference/utility-classes) to add border(s) to `<b-sidebar>` (via the
123+
`sidebar-class` prop <span class="badge badge-secondary">2.12.0+</span>), or use CSS style
124+
overrides.
125+
126+
```html
127+
<template>
128+
<div>
129+
<b-button v-b-toggle.sidebar-border>Toggle Sidebar</b-button>
130+
<b-sidebar id="sidebar-border" sidebar-class="border-right border-danger">
131+
<div class="px-3 py-2">
132+
<p>
133+
Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis
134+
in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
135+
</p>
136+
<b-img src="https://picsum.photos/500/500/?image=54" fluid thumbnail></b-img>
137+
</div>
138+
</b-sidebar>
139+
</div>
140+
</template>
141+
142+
<!-- b-sidebar-border.vue -->
143+
```
123144

124145
### Width
125146

@@ -142,6 +163,9 @@ slide transition via the `no-slide` prop.
142163
[reduced motion section of our accessibility documentation](/docs/reference/accessibility) for
143164
additional details.
144165

166+
When disabling the slid transition, the fade transition of the [optional backdrop](#backdrop) will
167+
also be disabled.
168+
145169
### Z-index
146170

147171
The sidebar has a default `z-index` defined in SCSS/CSS. In some situations you may need to use a
@@ -237,6 +261,43 @@ In some instances, you may not want the content rendered when the sidebar is not
237261
the `lazy` prop on `<b-sidebar>`. When `lazy` is `true`, the body and optional footer will _not_ be
238262
rendered (removed from DOM) whenever the sidebar is closed.
239263

264+
### Backdrop
265+
266+
<span class="badge badge-info small">2.12.0+</span>
267+
268+
Add a basic backdrop when the side bar is open via the `backdrop` prop. When set to `true`, the
269+
sidebar will show an opaque backdrop. Clicking on the backdrop will close the sidebar, unless the
270+
`no-close-on-backdrop` prop is set to `true`.
271+
272+
```html
273+
<template>
274+
<div>
275+
<b-button v-b-toggle.sidebar-backdrop>Toggle Sidebar</b-button>
276+
<b-sidebar
277+
id="sidebar-backdrop"
278+
title="Sidebar with backdrop"
279+
backdrop
280+
shadow
281+
>
282+
<div class="px-3 py-2">
283+
<p>
284+
Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis
285+
in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
286+
</p>
287+
<b-img src="https://picsum.photos/500/500/?image=54" fluid thumbnail></b-img>
288+
</div>
289+
</b-sidebar>
290+
</div>
291+
</template>
292+
293+
<!-- b-sidebar-backdrop.vue -->
294+
```
295+
296+
Note that when the sidebar is open, it may still be possible to scroll the body (unlike the
297+
behaviour of modals). When the backdrop in enabled, focus constraint will attempt to keep focus
298+
within the sidebar. Note that in rare circumstances it might be possible for users to move focus to
299+
elements outside of the sidebar.
300+
240301
## Visibility control
241302

242303
### `v-b-toggle` directive
@@ -285,7 +346,11 @@ reader and keyboard-only users. When the sidebar is closed, the element that pre
285346
before the sidebar was opened will be re-focused.
286347

287348
When the sidebar is open, users can press <kbd>Esc</kbd> to close the sidebar. To disable this
288-
feature, set the `no-close-on-esc` prop to `true`.
349+
feature, set the `no-close-on-esc` prop to `true`. with the backdrop enabled, you can use the prop
350+
`no-close-on-backdrop` to disable the close on backdrop click feature.
351+
352+
When the `backdrop` prop is `true`, the sidebar will attempt to constrain focus within the sidebar,
353+
and the sidebar will have the attribute `aria-modal="true"` set.
289354

290355
When you have hidden the header, or do not have a title for the sidebar, set either `aria-label` to
291356
a string that describes the sidebar, or set `aria-labelledby` to an ID of an element that contains

src/components/sidebar/_sidebar.scss

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
1+
.b-sidebar-outer {
2+
position: fixed !important;
3+
top: 0;
4+
left: 0;
5+
right: 0;
6+
height: 0;
7+
overflow: visible;
8+
z-index: $b-sidebar-zindex;
9+
}
10+
11+
.b-sidebar-backdrop {
12+
position: fixed !important;
13+
top: 0;
14+
left: 0;
15+
z-index: -1;
16+
width: 100vw;
17+
height: 100vh;
18+
background-color: #000;
19+
opacity: 0.5;
20+
}
21+
122
.b-sidebar {
223
display: flex;
324
flex-direction: column;
425
position: fixed !important;
526
top: 0;
6-
bottom: 0;
27+
height: 100vh;
728
width: $b-sidebar-width;
829
max-width: 100% !important;
930
height: 100vh !important;
1031
margin: 0 !important;
1132
outline: 0;
1233
transform: translateX(0);
13-
z-index: $b-sidebar-zindex;
1434

1535
&.slide {
1636
transition: transform $b-sidebar-transition-duration ease-in-out;

src/components/sidebar/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@
5454
"prop": "closeLabel",
5555
"description": "`aria-label` to apply to the built-in close button. Defaults to 'Close'"
5656
},
57+
{
58+
"prop": "sidebarClass",
59+
"version": "2.12.0",
60+
"description": "Class, or classes, to apply to the sidebar content wrapper"
61+
},
5762
{
5863
"prop": "headerClass",
5964
"description": "Class, or classes, to apply to the built in header. Has no effect if prop `no-header` is set"
@@ -66,6 +71,11 @@
6671
"prop": "footerClass",
6772
"description": "Class, or classes, to apply to the optional `footer` slot"
6873
},
74+
{
75+
"prop": "backdrop",
76+
"version": "2.12.0",
77+
"description": "When `true`, shows a backdrop when the sidebar is open"
78+
},
6979
{
7080
"prop": "lazy",
7181
"description": "When set to `true`, the content of the sidebar will only be rendered while the sidebar is open"
@@ -82,6 +92,11 @@
8292
"prop": "noCloseOnEsc",
8393
"description": "When set to `true`, disables closing the sidebar when the user presses ESC"
8494
},
95+
{
96+
"prop": "noCloseOnBackdrop",
97+
"version": "2.12.0",
98+
"description": "When set to `true`, disables closing the sidebar when the user clicks on the backdrop. Requires that the prop `backdrop` be set"
99+
},
85100
{
86101
"prop": "noCloseOnRouteChange",
87102
"description": "When set to `true`, disables closing of the sidebar on route change"

0 commit comments

Comments
 (0)