Skip to content

Commit 023626f

Browse files
committed
fix(b-sidebar): avoid content layout change on toggle
1 parent 0a14828 commit 023626f

File tree

1 file changed

+83
-79
lines changed

1 file changed

+83
-79
lines changed

src/components/sidebar/sidebar.js

Lines changed: 83 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,73 @@ const renderContent = (h, ctx) => {
182182
}
183183

184184
const renderBackdrop = (h, ctx) => {
185-
if (!ctx.backdrop) {
186-
return h()
185+
let $backdrop = h()
186+
if (ctx.backdrop) {
187+
const { backdropVariant } = ctx
188+
189+
$backdrop = h('div', {
190+
directives: [{ name: 'show', value: ctx.localShow }],
191+
staticClass: 'b-sidebar-backdrop',
192+
class: { [`bg-${backdropVariant}`]: backdropVariant },
193+
on: { click: ctx.onBackdropClick }
194+
})
187195
}
188196

189-
const { backdropVariant } = ctx
197+
return h(BVTransition, { props: { noFade: ctx.noSlide } }, [$backdrop])
198+
}
199+
200+
const renderTabTrap = (h, ctx, focusHandler) => {
201+
if (ctx.noEnforceFocus) {
202+
return h()
203+
}
190204

191-
return h('div', {
192-
directives: [{ name: 'show', value: ctx.localShow }],
193-
staticClass: 'b-sidebar-backdrop',
194-
class: { [`bg-${backdropVariant}`]: backdropVariant },
195-
on: { click: ctx.onBackdropClick }
205+
return h('span', {
206+
class: { 'd-none': !ctx.localShow },
207+
attrs: { tabindex: '0' },
208+
on: { focus: focusHandler }
196209
})
197210
}
198211

212+
const renderSidebar = (h, ctx) => {
213+
const { bgVariant, width, textVariant } = ctx
214+
const shadow = ctx.shadow === '' ? true : ctx.shadow
215+
216+
return h(
217+
'transition',
218+
{
219+
props: ctx.transitionProps,
220+
on: {
221+
beforeEnter: ctx.onBeforeEnter,
222+
afterEnter: ctx.onAfterEnter,
223+
afterLeave: ctx.onAfterLeave
224+
}
225+
},
226+
[
227+
h(
228+
ctx.tag,
229+
{
230+
staticClass: CLASS_NAME,
231+
class: [
232+
{
233+
shadow: shadow === true,
234+
[`shadow-${shadow}`]: shadow && shadow !== true,
235+
[`${CLASS_NAME}-right`]: ctx.right,
236+
[`bg-${bgVariant}`]: bgVariant,
237+
[`text-${textVariant}`]: textVariant
238+
},
239+
ctx.sidebarClass
240+
],
241+
style: { width },
242+
attrs: ctx.computedAttrs,
243+
directives: [{ name: 'show', value: ctx.localShow }],
244+
ref: 'content'
245+
},
246+
[renderContent(h, ctx)]
247+
)
248+
]
249+
)
250+
}
251+
199252
// --- Main component ---
200253

201254
// @vue/component
@@ -214,6 +267,9 @@ export const BSidebar = /*#__PURE__*/ Vue.extend({
214267
}
215268
},
216269
computed: {
270+
sidebarId() {
271+
return this.safeId()
272+
},
217273
transitionProps() {
218274
return this.noSlide
219275
? /* istanbul ignore next */ { css: true }
@@ -245,7 +301,7 @@ export const BSidebar = /*#__PURE__*/ Vue.extend({
245301
computedAttrs() {
246302
return {
247303
...this.bvAttrs,
248-
id: this.safeId(),
304+
id: this.sidebarId,
249305
tabindex: '-1',
250306
role: 'dialog',
251307
'aria-modal': this.backdrop ? 'true' : 'false',
@@ -256,8 +312,8 @@ export const BSidebar = /*#__PURE__*/ Vue.extend({
256312
}
257313
},
258314
watch: {
259-
[MODEL_PROP_NAME](newValue, oldValue) {
260-
if (newValue !== oldValue) {
315+
[MODEL_PROP_NAME](newValue) {
316+
if (newValue !== this.localShow) {
261317
this.localShow = newValue
262318
}
263319
},
@@ -284,7 +340,7 @@ export const BSidebar = /*#__PURE__*/ Vue.extend({
284340
this.listenOnRoot(ROOT_ACTION_EVENT_NAME_REQUEST_STATE, this.handleSync)
285341
// Send out a gratuitous state event to ensure toggle button is synced
286342
this.$nextTick(() => {
287-
this.emitState(this.localShow)
343+
this.emitState()
288344
})
289345
},
290346
/* istanbul ignore next */
@@ -300,22 +356,22 @@ export const BSidebar = /*#__PURE__*/ Vue.extend({
300356
this.localShow = false
301357
},
302358
emitState(state = this.localShow) {
303-
this.emitOnRoot(ROOT_EVENT_NAME_STATE, this.safeId(), state)
359+
this.emitOnRoot(ROOT_EVENT_NAME_STATE, this.sidebarId, state)
304360
},
305361
emitSync(state = this.localShow) {
306-
this.emitOnRoot(ROOT_EVENT_NAME_SYNC_STATE, this.safeId(), state)
362+
this.emitOnRoot(ROOT_EVENT_NAME_SYNC_STATE, this.sidebarId, state)
307363
},
308364
handleToggle(id) {
309-
// Note `safeId()` can be null until after mount
310-
if (id && id === this.safeId()) {
365+
// Note `sidebarId` can be `null` until after mount
366+
if (id && id === this.sidebarId) {
311367
this.localShow = !this.localShow
312368
}
313369
},
314370
handleSync(id) {
315-
// Note `safeId()` can be null until after mount
316-
if (id && id === this.safeId()) {
371+
// Note `sidebarId` can be `null` until after mount
372+
if (id && id === this.sidebarId) {
317373
this.$nextTick(() => {
318-
this.emitSync(this.localShow)
374+
this.emitSync()
319375
})
320376
}
321377
},
@@ -332,13 +388,11 @@ export const BSidebar = /*#__PURE__*/ Vue.extend({
332388
},
333389
/* istanbul ignore next */
334390
onTopTrapFocus() {
335-
const tabables = getTabables(this.$refs.content)
336-
this.enforceFocus(tabables.reverse()[0])
391+
this.enforceFocus(getTabables(this.$refs.content).reverse()[0])
337392
},
338393
/* istanbul ignore next */
339394
onBottomTrapFocus() {
340-
const tabables = getTabables(this.$refs.content)
341-
this.enforceFocus(tabables[0])
395+
this.enforceFocus(getTabables(this.$refs.content)[0])
342396
},
343397
onBeforeEnter() {
344398
// Returning focus to `document.body` may cause unwanted scrolls,
@@ -367,61 +421,6 @@ export const BSidebar = /*#__PURE__*/ Vue.extend({
367421
}
368422
},
369423
render(h) {
370-
const { bgVariant, width, textVariant, localShow } = this
371-
const shadow = this.shadow === '' ? true : this.shadow
372-
373-
let $sidebar = h(
374-
this.tag,
375-
{
376-
staticClass: CLASS_NAME,
377-
class: [
378-
{
379-
shadow: shadow === true,
380-
[`shadow-${shadow}`]: shadow && shadow !== true,
381-
[`${CLASS_NAME}-right`]: this.right,
382-
[`bg-${bgVariant}`]: bgVariant,
383-
[`text-${textVariant}`]: textVariant
384-
},
385-
this.sidebarClass
386-
],
387-
style: { width },
388-
attrs: this.computedAttrs,
389-
directives: [{ name: 'show', value: localShow }],
390-
ref: 'content'
391-
},
392-
[renderContent(h, this)]
393-
)
394-
395-
$sidebar = h(
396-
'transition',
397-
{
398-
props: this.transitionProps,
399-
on: {
400-
beforeEnter: this.onBeforeEnter,
401-
afterEnter: this.onAfterEnter,
402-
afterLeave: this.onAfterLeave
403-
}
404-
},
405-
[$sidebar]
406-
)
407-
408-
const $backdrop = h(BVTransition, { props: { noFade: this.noSlide } }, [
409-
renderBackdrop(h, this)
410-
])
411-
412-
let $tabTrapTop = h()
413-
let $tabTrapBottom = h()
414-
if (this.backdrop && localShow) {
415-
$tabTrapTop = h('div', {
416-
attrs: { tabindex: '0' },
417-
on: { focus: this.onTopTrapFocus }
418-
})
419-
$tabTrapBottom = h('div', {
420-
attrs: { tabindex: '0' },
421-
on: { focus: this.onBottomTrapFocus }
422-
})
423-
}
424-
425424
return h(
426425
'div',
427426
{
@@ -430,7 +429,12 @@ export const BSidebar = /*#__PURE__*/ Vue.extend({
430429
attrs: { tabindex: '-1' },
431430
on: { keydown: this.onKeydown }
432431
},
433-
[$tabTrapTop, $sidebar, $tabTrapBottom, $backdrop]
432+
[
433+
renderTabTrap(h, this, this.onTopTrapFocus),
434+
renderSidebar(h, this),
435+
renderTabTrap(h, this, this.onBottomTrapFocus),
436+
renderBackdrop(h, this)
437+
]
434438
)
435439
}
436440
})

0 commit comments

Comments
 (0)