Skip to content

Commit 280ac83

Browse files
authored
[FEATURE] Multiple Menu Support (#79)
* Support multiple menus with different state * Remove console log * Fix initial state and cleanup * Remove burger-menu service and keep state contained per menu * Fix tests + update readme * Use native dom helpers * Use state actions to handle all open and close requests This allows us to contain the `locked` logic to the state object * Cleanup * Update readme
1 parent 6a7c8f4 commit 280ac83

File tree

29 files changed

+488
-348
lines changed

29 files changed

+488
-348
lines changed

README.md

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ An off-canvas sidebar component with a collection of animations and styles using
1313

1414
- Easy to use & setup off-canvas menu
1515
- Mix and match from many menu & menu item animations
16-
- Control your menu from anywhere in your app
1716
- Swipe gesture support with changeable thresholds
1817
- Easily create your own animations
1918

@@ -123,8 +122,7 @@ This addon utilizes contextual components to be able to correctly control and an
123122

124123
- #### `locked`
125124

126-
Lock the menu in its current open / closed state. Please note that changes made
127-
directly via the burgerMenu service or {{burger.state.actions}} will still propagate.
125+
Lock the menu in its current open / closed state.
128126

129127
**Default: false**
130128

@@ -214,29 +212,7 @@ The individual menu item. This is required if you have specified an [itemAnimati
214212

215213
## The Menu State
216214

217-
### Via Component
218-
219-
The `{{burger-menu}}` component exposes multiple contextual components, but it also exposes a state object which allows you to control the state of the menu.
220-
221-
```hbs
222-
{{#burger-menu as |burger|}}
223-
{{#burger.outlet}}
224-
<a {{action burger.state.actions.toggle}} class="close fa fa-times"></a>
225-
{{/burger.outlet}}
226-
{{/burger-menu}}
227-
```
228-
229-
### Via Service
230-
231-
If you need a more programmatic solution, you can grab the menu state via injecting the `burgerMenu` service.
232-
233-
```js
234-
export default Ember.Component.extend({
235-
burgerMenu: Ember.inject.service()
236-
})
237-
```
238-
239-
### Usage
215+
The `{{burger-menu}}` component exposes multiple contextual components, but it also exposes a state object.
240216

241217
You can use the menu state object to modify pretty much any property.
242218

@@ -247,13 +223,7 @@ You can use the menu state object to modify pretty much any property.
247223
- `itemAnimation`
248224
- `customAnimation`
249225

250-
```js
251-
burgerMenu.set('width', 500);
252-
burgerMenu.toggleProperty('open');
253-
```
254-
255-
The state object also exposes some actions.
256-
226+
The state object also exposes some actions:
257227

258228
- #### `open`
259229

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const {
55
computed
66
} = Ember;
77

8-
export default Ember.Service.extend({
8+
export default Ember.Object.extend({
99
open: false,
1010
locked: false,
1111
width: 300,
@@ -28,9 +28,9 @@ export default Ember.Service.extend({
2828

2929
actions: computed(function() {
3030
return {
31-
open: () => this.set('open', true),
32-
close: () => this.set('open', false),
33-
toggle: () => this.toggleProperty('open')
31+
open: () => !this.get('locked') && this.set('open', true),
32+
close: () => !this.get('locked') && this.set('open', false),
33+
toggle: () => !this.get('locked') && this.toggleProperty('open')
3434
};
3535
}).readOnly()
3636
});

addon/components/bm-menu-item.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@ import computedStyleFor from 'ember-burger-menu/computed/style-for';
55
const {
66
$,
77
run,
8-
computed,
9-
inject: { service }
8+
computed
109
} = Ember;
1110

1211
export default Ember.Component.extend({
1312
layout,
1413
classNames: ['bm-menu-item'],
1514
attributeBindings: ['style'],
1615

17-
state: service('burgerMenu'),
16+
state: null,
1817

1918
menuItems: null,
2019
dismissOnClick: false,
@@ -36,8 +35,10 @@ export default Ember.Component.extend({
3635
},
3736

3837
click() {
39-
if (this.get('dismissOnClick') && !this.get('state.locked')) {
40-
this.set('state.open', false);
38+
this._super(...arguments);
39+
40+
if (this.get('dismissOnClick')) {
41+
this.get('state.actions').close();
4142
}
4243
}
4344
});

addon/components/bm-menu.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import computedStyleFor from 'ember-burger-menu/computed/style-for';
55
const {
66
computed,
77
observer,
8-
A: emberArray,
9-
inject: { service }
8+
A: emberArray
109
} = Ember;
1110

1211
export const OUTLET_MENU_ANIMATIONS = [
@@ -16,7 +15,7 @@ export const OUTLET_MENU_ANIMATIONS = [
1615

1716
export default Ember.Component.extend({
1817
layout,
19-
state: service('burgerMenu'),
18+
state: null,
2019

2120
itemTagName: 'div',
2221
dismissOnItemClick: false,

addon/components/bm-outlet.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@ import Ember from 'ember';
22
import layout from '../templates/components/bm-outlet';
33
import computedStyleFor from 'ember-burger-menu/computed/style-for';
44

5-
const {
6-
inject: { service }
7-
} = Ember;
8-
95
export default Ember.Component.extend({
106
layout,
117
classNames: ['bm-outlet'],
128
attributeBindings: ['style'],
13-
state: service('burgerMenu'),
9+
state: null,
1410
style: computedStyleFor('outlet').readOnly()
1511
});

addon/components/burger-menu.js

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Ember from 'ember';
22
import layout from '../templates/components/burger-menu';
33
import computedStyleFor from 'ember-burger-menu/computed/style-for';
44
import SwipeSupportMixin from 'ember-burger-menu/mixins/swipe-support';
5+
import State from 'ember-burger-menu/-private/state';
56
import DomMixin from 'ember-lifeline/mixins/dom';
67

78
const {
@@ -10,23 +11,22 @@ const {
1011
run,
1112
observer,
1213
computed,
13-
computed: { alias },
14-
inject: { service }
14+
computed: { alias }
1515
} = Ember;
1616

1717
export default Ember.Component.extend(DomMixin, SwipeSupportMixin, {
18+
layout,
1819
classNames: ['ember-burger-menu'],
1920
classNameBindings: ['open:is-open', 'translucentOverlay', 'animationClass', 'position'],
2021
attributeBindings: ['style'],
21-
layout,
22-
23-
state: service('burgerMenu'),
2422

2523
translucentOverlay: true,
2624
dismissOnClick: true,
2725
dismissOnEsc: true,
2826
gesturesEnabled: true,
2927

28+
state: computed(() => State.create()).readOnly(),
29+
3030
open: alias('state.open'),
3131
locked: alias('state.locked'),
3232
position: alias('state.position'),
@@ -46,63 +46,61 @@ export default Ember.Component.extend(DomMixin, SwipeSupportMixin, {
4646
run.cancel(this._setupEventsTimer);
4747
},
4848

49-
setupEvents: on('didInsertElement', observer('open', 'locked', function() {
50-
if (this.get('locked')) {
51-
this._setupEventsTimer = run.scheduleOnce('afterRender', this, '_teardownEvents');
52-
} else {
53-
let methodName = this.get('open') ? '_setupEvents' : '_teardownEvents';
54-
this._setupEventsTimer = run.scheduleOnce('afterRender', this, methodName);
55-
}
49+
setupEvents: on('didReceiveAttrs', observer('open', 'locked', function() {
50+
let methodName = (this.get('open') && !this.get('locked')) ? '_setupEvents' : '_teardownEvents';
51+
this._setupEventsTimer = run.scheduleOnce('afterRender', this, methodName);
5652
})),
5753

5854
_setupEvents() {
55+
let elementId = this.get('elementId');
56+
5957
if (this.get('dismissOnClick')) {
60-
this.addEventListener(this.$(), 'click', this.onClick);
61-
this.addEventListener(this.$(), 'touchstart', this.onClick);
58+
this.addEventListener($('body'), `click.${elementId}`, this.onClick);
59+
this.addEventListener($('body'), `touchstart.${elementId}`, this.onClick);
6260
}
6361

6462
if (this.get('dismissOnEsc')) {
65-
this.addEventListener(document, 'keyup', this.onKeyup);
63+
this.addEventListener(document, `keyup.${elementId}`, this.onKeyup);
6664
}
6765
},
6866

6967
_teardownEvents() {
70-
this.removeEventListener(this.$(), 'click', this.onClick);
71-
this.removeEventListener(this.$(), 'touchstart', this.onClick);
72-
this.removeEventListener(document, 'keyup', this.onKeyup);
68+
let elementId = this.get('elementId');
69+
70+
this.removeEventListener($('body'), `click.${elementId}`, this.onClick);
71+
this.removeEventListener($('body'), `touchstart.${elementId}`, this.onClick);
72+
this.removeEventListener(document, `keyup.${elementId}`, this.onKeyup);
7373
},
7474

7575
onClick(e) {
76-
// Close the menu if clicked outside of it
77-
if ($(e.target).closest('.bm-menu').length === 0) {
78-
e.stopPropagation();
79-
e.preventDefault();
76+
let elementId = this.get('elementId');
8077

81-
this.set('open', false);
78+
// Close the menu if clicked outside of it
79+
if ($(e.target).closest(`#${elementId} .bm-menu`).length === 0) {
80+
this.get('state.actions').close();
8281
}
8382
},
8483

8584
onKeyup(e) {
8685
if (e.keyCode === 27) {
87-
this.set('open', false);
86+
this.get('state.actions').close();
8887
}
8988
},
9089

9190
onSwipe(direction, target) {
9291
let position = this.get('position');
9392
let open = this.get('open');
94-
let locked = this.get('locked');
9593
let gesturesEnabled = this.get('gesturesEnabled');
9694
let isMenuSwipe = $(target).closest('.bm-menu').length > 0;
9795

98-
if (!gesturesEnabled || locked) {
96+
if (!gesturesEnabled) {
9997
return;
10098
}
10199

102100
if (open && isMenuSwipe && position === direction) {
103-
this.set('open', false);
101+
this.get('state.actions').close();
104102
} else if (!open && position !== direction) {
105-
this.set('open', true);
103+
this.get('state.actions').open();
106104
}
107105
}
108106
});

addon/mixins/swipe-support.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ import Ember from 'ember';
22

33
const {
44
isNone,
5-
inject: { service },
65
computed: { alias }
76
} = Ember;
87

98
let meta;
109

1110
export default Ember.Mixin.create({
12-
state: service('burgerMenu'),
1311
minSwipeDistance: alias('state.minSwipeDistance'),
1412
maxSwipeTime: alias('state.maxSwipeTime'),
1513

addon/styles/addon.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
.ember-burger-menu {
2020
position: relative;
21-
height: 100vh;
21+
height: 100%;
2222
overflow: hidden;
2323

2424
.bm-menu {

addon/templates/components/bm-menu.hbs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
item=(component 'bm-menu-item'
55
tagName=itemTagName
66
menuItems=menuItems
7-
dismissOnClick=dismissOnItemClick)
7+
dismissOnClick=dismissOnItemClick
8+
state=state)
89
)
910
}}
1011
</div>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{{yield (hash
2-
outlet=(component 'bm-outlet' containerId=elementId)
3-
menu=(component 'bm-menu' containerId=elementId)
2+
outlet=(component 'bm-outlet' containerId=elementId state=state)
3+
menu=(component 'bm-menu' containerId=elementId state=state)
44
state=state
55
)
66
}}

0 commit comments

Comments
 (0)