8000 feat(COffcanvas): add static backdrop support · coreui/coreui-vue@c87ee00 · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c87ee00

Browse files
committed
feat(COffcanvas): add static backdrop support
1 parent 26c0479 commit c87ee00

File tree

2 files changed

+147
-110
lines changed

2 files changed

+147
-110
lines changed

packages/coreui-vue/src/components/offcanvas/COffcanvas.ts

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,28 @@ const COffcanvas = defineComponent({
1010
props: {
1111
/**
1212
* Apply a backdrop on body while offcanvas is open.
13+
*
14+
* @values boolean | 'static'
1315
*/
1416
backdrop: {
15-
type: Boolean,
17+
type: [Boolean, String],
1618
default: true,
17-
require: false,
19+
validator: (value: boolean | string) => {
20+
if (typeof value === 'string') {
21+
return ['static'].includes(value)
22+
}
23+
if (typeof value === 'boolean') {
24+
return true
25+
}
26+
return false
27+
},
1828
},
1929
/**
2030
* Closes the offcanvas when escape key is pressed.
2131
*/
2232
keyboard: {
2333
type: Boolean,
2434
default: true,
25-
require: false,
2635
},
2736
/**
2837
* Components placement, there’s no default placement.
@@ -43,15 +52,11 @@ const COffcanvas = defineComponent({
4352
scroll: {
4453
type: Boolean,
4554
default: false,
46-
required: false,
4755
},
4856
/**
4957
* Toggle the visibility of offcanvas component.
5058
*/
51-
visible: {
52-
type: Boolean,
53-
require: false,
54-
},
59+
visible: Boolean,
5560
},
5661
emits: [
5762
/**
@@ -93,44 +98,35 @@ const COffcanvas = defineComponent({
9398
emit('show')
9499
executeAfterTransition(() => done(), el as HTMLElement)
95100
setTimeout(() => {
96-
el.style.visibility = 'visible'
97101
el.classList.add('show')
98102
}, 1)
99103
}
100104
const handleAfterEnter = () => {
101-
window.addEventListener('mousedown', handleMouseDown)
102-
window.addEventListener('keyup', handleKeyUp)
105+
offcanvasRef.value.focus()
103106
}
104107
const handleLeave = (el: RendererElement, done: () => void) => {
105108
executeAfterTransition(() => done(), el as HTMLElement)
106-
window.removeEventListener('mousedown', handleMouseDown)
107-
window.removeEventListener('keyup', handleKeyUp)
108-
el.classList.remove('show')
109+
el.classList.add('hiding')
109110
}
110111
const handleAfterLeave = (el: RendererElement) => {
111-
el.style.visibility = 'hidden'
112+
el.classList.remove('show', 'hiding')
112113
}
113114

114115
const handleDismiss = () => {
115116
visible.value = false
116117
emit('hide')
117118
}
118119

119-
const handleKeyUp = (event: KeyboardEvent) => {
120-
if (offcanvasRef.value && !offcanvasRef.value.contains(event.target as HTMLElement)) {
121-
if (event.key === 'Escape' && props.keyboard && props.backdrop) {
122-
return handleDismiss()
123-
}
120+
const handleBackdropDismiss = () => {
121+
if (props.backdrop !== 'static') {
122+
handleDismiss()
124123
}
125124
}
126125

127-
const handleMouseDown = (event: Event) => {
128-
window.addEventListener('mouseup', () => handleMouseUp(event), { once: true })
129-
}
130-
131-
const handleMouseUp = (event: Event) => {
132-
if (offcanvasRef.value && !offcanvasRef.value.contains(event.target as HTMLElement)) {
133-
props.backdrop && F438 handleDismiss()
126+
const handleKeyDown = (event: KeyboardEvent) => {
127+
console.log('keydown')
128+
if (event.key === 'Escape' && props.keyboard) {
129+
handleDismiss()
134130
}
135131
}
136132

@@ -155,8 +151,10 @@ const COffcanvas = defineComponent({
155151
[`offcanvas-${props.placement}`]: props.placement,
156152
},
157153
],
154+
onKeydown: (event: KeyboardEvent) => handleKeyDown(event),
158155
ref: offcanvasRef,
159156
role: 'dialog',
157+
tabindex: -1,
160158
},
161159
slots.default && slots.default(),
162160
),
@@ -166,6 +164,7 @@ const COffcanvas = defineComponent({
166164
props.backdrop &&
167165
h(CBackdrop, {
168166
class: 'offcanvas-backdrop',
167+
onClick: handleBackdropDismiss,
169168
visible: visible.value,
170169
}),
171170
]

packages/docs/components/offcanvas.md

Lines changed: 121 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,126 @@ Use the buttons below to show and hide an offcanvas component.
8585
</script>
8686
```
8787

88+
### Body scrolling
89+
90+
Scrolling the `<body>` element is disabled when an offcanvas and its backdrop are visible. Use the scroll property to toggle `<body>` scrolling and backdrop to toggle the backdrop.
91+
92+
::: demo
93+
<CButton color="primary" @click="() => { visibleScrolling = !visibleScrolling }">Enable body scrolling</CButton>
94+
<COffcanvas :backdrop="false" placement="start" scroll :visible="visibleScrolling" @hide="() => { visibleScrolling = !visibleScrolling }">
95+
<COffcanvasHeader>
96+
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
97+
<CCloseButton class="text-reset" @click="() => { visibleScrolling = false }"/>
98+
</COffcanvasHeader>
99+
<COffcanvasBody>
100+
<p>Try scrolling the rest of the page to see this option in action.</p>
101+
</COffcanvasBody>
102+
</COffcanvas>
103+
:::
104+
```vue
105+
<template>
106+
<CButton color="primary" @click="() => { visibleScrolling = !visibleScrolling }">Enable body scrolling</CButton>
107+
<COffcanvas :backdrop="false" placement="start" scroll :visible="visibleScrolling" @hide="() => { visibleScrolling = !visibleScrolling }">
108+
<COffcanvasHeader>
109+
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
110+
<CCloseButton class="text-reset" @click="() => { visibleScrolling = false }"/>
111+
</COffcanvasHeader>
112+
<COffcanvasBody>
113+
<p>Try scrolling the rest of the page to see this option in action.</p>
114+
</COffcanvasBody>
115+
</COffcanvas>
116+
</template>
117+
<script>
118+
export default {
119+
data() {
120+
return {
121+
visibleScrolling: false,
122+
}
123+
}
124+
}
125+
</script>
126+
```
127+
128+
### Body scrolling and backdrop
129+
130+
You can also enable `<body>` scrolling with a visible backdrop.
131+
132+
::: demo
133+
<CButton color="primary" @click="() => { visibleWithBothOptions = !visibleWithBothOptions }">Enable both scrolling & backdrop</CButton>
134+
<COffcanvas placement="start" scroll :visible="visibleWithBothOptions" @hide="() => { visibleWithBothOptions = !visibleWithBothOptions }">
135+
<COffcanvasHeader>
136+
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
137+
<CCloseButton class="text-reset" @click="() => { visibleWithBothOptions = false }"/>
138+
</COffcanvasHeader>
139+
<COffcanvasBody>
140+
<p>Try scrolling the rest of the page to see this option in action.</p>
141+
</COffcanvasBody>
142+
</COffcanvas>
143+
:::
144+
```vue
145+
<template>
146+
<CButton color="primary" @click="() => { visibleWithBothOptions = !visibleWithBothOptions }">Enable both scrolling &amp; backdrop</CButton>
147+
<COffcanvas placement="start" scroll :visible="visibleWithBothOptions" @hide="() => { visibleWithBothOptions = !visibleWithBothOptions }">
148+
<COffcanvasHeader>
149+
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
150+
<CCloseButton class="text-reset" @click="() => { visibleWithBothOptions = false }"/>
151+
</COffcanvasHeader>
152+
<COffcanvasBody>
153+
<p>Try scrolling the rest of the page to see this option in action.</p>
154+
</COffcanvasBody>
155+
</COffcanvas>
156+
</template>
157+
<script>
158+
export default {
159+
data() {
160+
return {
161+
visibleWithBothOptions: false,
162+
}
163+
}
164+
}
165+
</script>
166+
```
167+
168+
### Static backdrop
169+
170+
If you set a `backdrop` to `static`, your React offcanvas component will not close when clicking outside of it.
171+
172+
::: demo
173+
<CButton color="primary" @click="() => { visibleWithStaticBackdrop = !visibleWithStaticBackdrop }">Toggle static offcanvas</CButton>
174+
<COffcanvas backdrop="static" placement="start" :visible="visibleWithStaticBackdrop" @hide="() => { visibleWithStaticBackdrop = !visibleWithStaticBackdrop }">
175+
<COffcanvasHeader>
176+
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
177+
<CCloseButton class="text-reset" @click="() => { visibleWithStaticBackdrop = false }"/>
178+
</COffcanvasHeader>
179+
<COffcanvasBody>
180+
<p>I will not close if you click outside of me.</p>
181+
</COffcanvasBody>
182+
</COffcanvas>
183+
:::
184+
```vue
185+
<template>
186+
<CButton color="primary" @click="() => { visibleWithStaticBackdrop = !visibleWithStaticBackdrop }">Toggle static offcanvas</CButton>
187+
<COffcanvas backdrop="static" placement="start" :visible="visibleWithStaticBackdrop" @hide="() => { visibleWithStaticBackdrop = !visibleWithStaticBackdrop }">
188+
<COffcanvasHeader>
189+
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
190+
<CCloseButton class="text-reset" @click="() => { visibleWithStaticBackdrop = false }"/>
191+
</COffcanvasHeader>
192+
<COffcanvasBody>
193+
<p>I will not close if you click outside of me.</p>
194+
</COffcanvasBody>
195+
</COffcanvas>
196+
</template>
197+
<script>
198+
export default {
199+
data() {
200+
return {
201+
visibleWithStaticBackdrop: false,
202+
}
203+
}
204+
}
205+
</script>
206+
```
207+
88208
## Placement
89209

90210
There's no default placement for offcanvas components, so you must add one of the modifier classes below;
@@ -210,88 +330,6 @@ Try the top, right, and bottom examples out below.
210330
</script>
211331
```
212332

213-
## Backdrop
214-
215-
Scrolling the `<body>` element is disabled when an offcanvas and its backdrop are visible. Use the `scroll` property to toggle `<body>` scrolling and `backdrop` to toggle the backdrop.
216-
217-
::: demo
218-
<CButton color="primary" @click="() => { visibleScrolling = !visibleScrolling }">Enable body scrolling</CButton>
219-
<CButton color="primary" @click="() => { visibleWithBackdrop = !visibleWithBackdrop }">Enable backdrop (default)</CButton>
220-
<CButton color="primary" @click="() => { visibleWithBothOptions = !visibleWithBothOptions }">Enable both scrolling & backdrop</CButton>
221-
<COffcanvas :backdrop="false" placement="start" scroll :visible="visibleScrolling" @hide="() => { visibleScrolling = !visibleScrolling }">
222-
<COffcanvasHeader>
223-
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
224-
<CCloseButton class="text-reset" @click="() => { visibleScrolling = false }"/>
225-
</COffcanvasHeader>
226-
<COffcanvasBody>
227-
<p>Try scrolling the rest of the page to see this option in action.</p>
228-
</COffcanvasBody>
229-
</COffcanvas>
230-
<COffcanvas placement="start" :visible="visibleWithBackdrop" @hide="() => { visibleWithBackdrop = !visibleWithBackdrop }">
231-
<COffcanvasHeader>
232-
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
233-
<CCloseButton class="text-reset" @click="() => { visibleWithBackdrop = false }"/>
234-
</COffcanvasHeader>
235-
<COffcanvasBody>
236-
<p>.....</p>
237-
</COffcanvasBody>
238-
</COffcanvas>
239-
<COffcanvas placement="start" scroll :visible="visibleWithBothOptions" @hide="() => { visibleWithBothOptions = !visibleWithBothOptions }">
240-
<COffcanvasHeader>
241-
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
242-
<CCloseButton class="text-reset" @click="() => { visibleWithBothOptions = false }"/>
243-
</COffcanvasHeader>
244-
<COffcanvasBody>
245-
<p>Try scrolling the rest of the page to see this option in action.</p>
246-
</COffcanvasBody>
247-
</COffcanvas>
248-
:::
249-
```vue
250-
<template>
251-
<CButton color="primary" @click="() => { visibleScrolling = !visibleScrolling }">Enable body scrolling</CButton>
252-
<CButton color="primary" @click="() => { visibleWithBackdrop = !visibleWithBackdrop }">Enable backdrop (default)</CButton>
253-
<CButton color="primary" @click="() => { visibleWithBothOptions = !visibleWithBothOptions }">Enable both scrolling &amp; backdrop</CButton>
254-
<COffcanvas :backdrop="false" placement="start" scroll :visible="visibleScrolling" @hide="() => { visibleScrolling = !visibleScrolling }">
255-
<COffcanvasHeader>
256-
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
257-
<CCloseButton class="text-reset" @click="() => { visibleScrolling = false }"/>
258-
</COffcanvasHeader>
259-
<COffcanvasBody>
260-
<p>Try scrolling the rest of the page to see this option in action.</p>
261-
</COffcanvasBody>
262-
</COffcanvas>
263-
<COffcanvas placement="start" :visible="visibleWithBackdrop" @hide="() => { visibleWithBackdrop = !visibleWithBackdrop }">
264-
<COffcanvasHeader>
265-
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
266-
<CCloseButton class="text-reset" @click="() => { visibleWithBackdrop = false }"/>
267-
</COffcanvasHeader>
268-
<COffcanvasBody>
269-
<p>.....</p>
270-
</COffcanvasBody>
271-
</COffcanvas>
272-
<COffcanvas placement="start" scroll :visible="visibleWithBothOptions" @hide="() => { visibleWithBothOptions = !visibleWithBothOptions }">
273-
<COffcanvasHeader>
274-
<COffcanvasTitle>Offcanvas</COffcanvasTitle>
275-
<CCloseButton class="text-reset" @click="() => { visibleWithBothOptions = false }"/>
276-
</COffcanvasHeader>
277-
<COffcanvasBody>
278-
<p>Try scrolling the rest of the page to see this option in action.</p>
279-
</COffcanvasBody>
280-
</COffcanvas>
281-
</template>
282-
<script>
283-
export default {
284-
data() {
285-
return {
286-
visibleScrolling: false,
287-
visibleWithBackdrop: false,
288-
visibleWithBothOptions: false,
289-
}
290-
}
291-
}
292-
</script>
293-
```
294-
295333
## Accessibility
296334

297335
Since the offcanvas panel is conceptually a modal dialog, be sure to add `aria-labelledby="..."`—referencing the offcanvas title—to `<COffcanvas>`. Note that you don’t need to add `role="dialog"` since we already add it automatically.
@@ -305,7 +343,7 @@ Since the offcanvas panel is conceptually a modal dialog, be sure to add `aria-l
305343
visibleEnd: false,
306344
visibleBottom: false,
307345
visibleScrolling: false,
308-
visibleWithBackdrop: false,
346+
visibleWithStaticBackdrop: false,
309347
visibleWithBothOptions: false,
310348
}
311349
}

0 commit comments

Comments
 (0)
0