Skip to content

Commit 25adc19

Browse files
committed
feat: updated Drawer for MDC v10
1 parent c4aaec1 commit 25adc19

File tree

7 files changed

+141
-39
lines changed

7 files changed

+141
-39
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ Update Progress Checklist:
213213
- [ ] Chips
214214
- [ ] Data Tables
215215
- [ ] Dialogs
216-
- [ ] Drawers
216+
- [x] Drawers
217217
- [ ] Image List
218218
- [ ] Checkboxes
219219
- [ ] Floating Label

packages/drawer/Drawer.svelte

+107-29
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,34 @@
55
class="
66
mdc-drawer
77
{className}
8+
{Object.keys(internalClasses).join(
9+
' '
10+
)}
811
{variant === 'dismissible'
912
? 'mdc-drawer--dismissible'
1013
: ''}
1114
{variant === 'modal' ? 'mdc-drawer--modal' : ''}
1215
"
13-
on:MDCDrawer:opened={updateOpen}
14-
on:MDCDrawer:closed={updateOpen}
16+
on:keydown={(event) => instance && instance.handleKeydown(event)}
17+
on:transitionend={(event) => instance && instance.handleTransitionEnd(event)}
1518
{...exclude($$props, ['use', 'class', 'variant', 'open'])}
1619
>
1720
<slot />
1821
</aside>
1922

2023
<script>
21-
import { MDCDrawer } from '@material/drawer';
22-
import { onMount, onDestroy, afterUpdate, setContext } from 'svelte';
24+
import {
25+
MDCDismissibleDrawerFoundation,
26+
MDCModalDrawerFoundation,
27+
} from '@material/drawer';
28+
import { FocusTrap } from '@material/dom/focus-trap';
29+
import { onMount, onDestroy, setContext } from 'svelte';
2330
import { get_current_component } from 'svelte/internal';
2431
import {
2532
forwardEventsBuilder,
2633
exclude,
2734
useActions,
35+
dispatch,
2836
} from '@smui/common/internal.js';
2937
3038
const forwardEvents = forwardEventsBuilder(get_current_component(), [
@@ -39,52 +47,122 @@
3947
export let open = false;
4048
4149
let element;
42-
let drawer;
43-
let listPromiseResolve;
44-
let listPromise = new Promise((resolve) => (listPromiseResolve = resolve));
50+
let instance;
51+
let internalClasses = {};
52+
let previousFocus;
53+
let focusTrap;
54+
let scrim = false;
4555
4656
setContext('SMUI:list:nav', true);
4757
setContext('SMUI:list:item:nav', true);
58+
setContext('SMUI:list:wrapFocus', true);
4859
49-
if (variant === 'dismissible' || variant === 'modal') {
50-
setContext('SMUI:list:instantiate', false);
51-
setContext('SMUI:list:getInstance', getListInstancePromise);
60+
$: if (instance && instance.isOpen() !== open) {
61+
if (open) {
62+
instance.open();
63+
} else {
64+
instance.close();
65+
}
5266
}
5367
54-
$: if (drawer && drawer.open !== open) {
55-
drawer.open = open;
68+
let oldVariant = variant;
69+
$: if (oldVariant !== variant) {
70+
oldVariant = variant;
71+
instance && instance.destroy();
72+
internalClasses = {};
73+
instance = getInstance();
74+
instance && instance.init();
5675
}
5776
5877
onMount(() => {
59-
if (variant === 'dismissible' || variant === 'modal') {
60-
drawer = new MDCDrawer(element);
61-
listPromiseResolve(drawer.list_);
62-
}
78+
focusTrap = new FocusTrap(element, {
79+
// Component handles focusing on active nav item.
80+
skipInitialFocus: true,
81+
});
82+
83+
instance = getInstance();
84+
instance && instance.init();
6385
});
6486
6587
onDestroy(() => {
66-
drawer && drawer.destroy();
88+
instance && instance.destroy();
89+
scrim &&
90+
scrim.removeEventListener('SMUI:drawer:scrim:click', handleScrimClick);
6791
});
6892
69-
afterUpdate(() => {
70-
if (drawer && !(variant === 'dismissible' || variant === 'modal')) {
71-
drawer.destroy();
72-
drawer = undefined;
73-
} else if (!drawer && (variant === 'dismissible' || variant === 'modal')) {
74-
drawer = new MDCDrawer(element);
75-
listPromiseResolve(drawer.list_);
93+
function getInstance() {
94+
if (scrim) {
95+
scrim.removeEventListener('SMUI:drawer:scrim:click', handleScrimClick);
96+
}
97+
98+
if (variant === 'modal') {
99+
scrim = element.parentNode.querySelector('.mdc-drawer-scrim');
100+
if (scrim) {
101+
scrim.addEventListener('SMUI:drawer:scrim:click', handleScrimClick);
102+
}
76103
}
77-
});
78104
79-
function getListInstancePromise() {
80-
return listPromise;
105+
const Foundation = {
106+
dismissible: MDCDismissibleDrawerFoundation,
107+
modal: MDCModalDrawerFoundation,
108+
}[variant];
109+
110+
return Foundation
111+
? new Foundation({
112+
addClass,
113+
removeClass,
114+
hasClass: (className) => element.classList.contains(className),
115+
elementHasClass: (element, className) =>
116+
element.classList.contains(className),
117+
saveFocus: () => (previousFocus = document.activeElement),
118+
restoreFocus: () => {
119+
if (
120+
previousFocus &&
121+
previousFocus.focus &&
122+
element.contains(document.activeElement)
123+
) {
124+
previousFocus.focus();
125+
}
126+
},
127+
focusActiveNavigationItem: () => {
128+
const activeNavItemEl = element.querySelector(
129+
'.mdc-list-item--activated,.mdc-deprecated-list-item--activated'
130+
);
131+
if (activeNavItemEl) {
132+
activeNavItemEl.focus();
133+
}
134+
},
135+
notifyClose: () => {
136+
open = false;
137+
dispatch(element, 'MDCDrawer:closed');
138+
},
139+
notifyOpen: () => {
140+
open = true;
141+
dispatch(element, 'MDCDrawer:opened');
142+
},
143+
trapFocus: () => focusTrap.trapFocus(),
144+
releaseFocus: () => focusTrap.releaseFocus(),
145+
})
146+
: undefined;
147+
}
148+
149+
function addClass(className) {
150+
internalClasses[className] = true;
81151
}
82152
83-
function updateOpen() {
84-
open = drawer.open;
153+
function removeClass(className) {
154+
delete internalClasses[className];
155+
}
156+
157+
function handleScrimClick() {
158+
instance && instance.handleScrimClick();
85159
}
86160
87161
export function setOpen(value) {
88162
open = value;
89163
}
164+
165+
export function isOpen() {
166+
return open;
167+
}
90168
</script>

packages/drawer/Scrim.js

-7
This file was deleted.

packages/drawer/Scrim.svelte

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<svelte:component
2+
this={component}
3+
bind:this={element}
4+
use={[forwardEvents, ...use]}
5+
class="mdc-drawer-scrim {className}"
6+
on:click={(event) => dispatch(element, 'SMUI:drawer:scrim:click', event)}
7+
{...exclude($$props, ['use', 'class', 'component'])}
8+
>
9+
<slot />
10+
</svelte:component>
11+
12+
<script>
13+
import { get_current_component } from 'svelte/internal';
14+
import {
15+
forwardEventsBuilder,
16+
exclude,
17+
dispatch,
18+
} from '@smui/common/internal.js';
19+
import Div from '@smui/common/Div.svelte';
20+
21+
const forwardEvents = forwardEventsBuilder(get_current_component());
22+
23+
export let use = [];
24+
let className = '';
25+
export { className as class };
26+
27+
export let component = Div;
28+
29+
let element;
30+
</script>

packages/drawer/bare.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Content from './Content.js';
55
import Header from './Header.js';
66
import Title from './Title.js';
77
import Subtitle from './Subtitle.js';
8-
import Scrim from './Scrim.js';
8+
import Scrim from './Scrim.svelte';
99

1010
export default Drawer;
1111

packages/drawer/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
},
2929
"license": "Apache-2.0",
3030
"dependencies": {
31+
"@material/dom": "^10.0.0",
3132
"@material/drawer": "^10.0.0",
3233
"@smui/common": "file:../common"
3334
},

packages/list/List.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
export let twoLine = false;
8383
export let threeLine = false;
8484
export let vertical = true;
85-
export let wrapFocus = false;
85+
export let wrapFocus = getContext('SMUI:list:wrapFocus') ?? false;
8686
export let singleSelection = false;
8787
export let selectedIndex = null;
8888
export let radiolist = false;

0 commit comments

Comments
 (0)