Skip to content

Commit fce0c82

Browse files
committed
fix(Dropdown): Fix dropdown positioning and improve components overall
1 parent 0f102d3 commit fce0c82

File tree

13 files changed

+170
-107
lines changed

13 files changed

+170
-107
lines changed

.storybook/storybook.scss

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,6 @@ body {
55
font-family: 'Roboto', sans-serif;
66
}
77

8-
html {
9-
box-sizing: border-box;
10-
}
11-
12-
*, *:before, *:after {
13-
//box-sizing: inherit;
14-
}
15-
168
#root {
179
padding: 20px;
1810
}

src/AutoComplete/AutoComplete.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
.mdl-autocomplete {
22
position: relative;
33
width: 300px;
4+
box-sizing: border-box;
5+
6+
*, *:before, *:after {
7+
box-sizing: inherit;
8+
}
49

510
.mdl-textfield {
611
width: 100%;

src/Dropdown/Dropdown.js

Lines changed: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,17 @@ export default class Dropdown extends Component {
3131
closeOnEsc: PropTypes.bool,
3232
closeOnOutsideClick: PropTypes.bool,
3333
offset: PropTypes.string,
34-
viewportPadding: PropTypes.number,
35-
cssPadding: PropTypes.number,
3634
target: PropTypes.element.isRequired,
3735
targetNode: PropTypes.any,
3836
useTargetWidth: PropTypes.bool,
3937
useTargetMinHeight: PropTypes.bool,
38+
viewportPadding: PropTypes.number,
4039
}
4140

4241
static defaultProps = {
4342
align: 'tl tl',
4443
closeOnEsc: true,
4544
closeOnOutsideClick: true,
46-
cssPadding: 10,
4745
offset: '0 0',
4846
viewportPadding: 10,
4947
}
@@ -59,101 +57,115 @@ export default class Dropdown extends Component {
5957
applyStyles(node, styles, this._reactInternalInstance)
6058
}
6159

62-
onOpen(portal) {
60+
onOpen(portalNode) {
6361
const {
64-
align,
65-
offset,
66-
cssPadding, // padding for styles (needed if menu is out of bounds)
67-
viewportPadding, // padding for viewport (needed if menu is out of bounds)
68-
useTargetWidth, // dropdown will inherit target width
69-
useTargetMinHeight, // dropdown will inherit target height as min-height
62+
align, offset, useTargetWidth, useTargetMinHeight, viewportPadding: pad
7063
} = this.props
7164

72-
// parse position
73-
let [ay,ax,ty,tx] = align.split('').map(a => a && POS[a]).filter(a => a)
74-
75-
// parse offset
76-
let [oy,ox] = offset.split(' ').map(o => parseInt(o))
77-
7865
// window is our boundary
79-
const { innerHeight } = window
66+
const { innerWidth, innerHeight } = window
8067

8168
// get target node
82-
const target = this.props.targetNode || findDOMNode(this)
69+
const targetNode = this.props.targetNode || findDOMNode(this)
8370

8471
// get bounding rects
85-
const portalRect = portal.getBoundingClientRect()
86-
const targetRect = target.getBoundingClientRect()
72+
const portal = portalNode.getBoundingClientRect()
73+
const target = targetNode.getBoundingClientRect()
8774

88-
// calculate padding
89-
const padding = viewportPadding + cssPadding + oy
75+
// parse position
76+
let [ay,ax,ty,tx] = align.split('').map(a => a && POS[a]).filter(a => a)
77+
78+
// parse offset
79+
let [oy,ox] = offset.split(' ').map(o => parseInt(o))
9080

9181
// calculate space above and below target
9282
let spaceAbove, spaceBelow
93-
if (ty === 'middle') {
94-
spaceAbove = targetRect.top + (targetRect.height / 2) - padding
95-
spaceBelow = innerHeight - targetRect.bottom + (targetRect.height / 2) - padding
96-
} else if (ty === 'top') {
83+
if (ty === 'top') {
9784
if (ay === 'top') {
98-
spaceAbove = targetRect.bottom - padding
99-
spaceBelow = innerHeight - targetRect.top - padding
100-
} else {
101-
spaceAbove = targetRect.top - padding
102-
spaceBelow = innerHeight - targetRect.bottom - padding
85+
spaceAbove = target.bottom - pad + oy
86+
spaceBelow = innerHeight - target.top - pad + oy
87+
} else if (ay === 'bottom') {
88+
spaceAbove = target.top - pad + oy
89+
spaceBelow = innerHeight - target.bottom - pad + oy
90+
} else if (ay === 'middle') {
10391
}
104-
} else {
92+
} else if (ty === 'bottom') {
10593
if (ay === 'top') {
106-
spaceAbove = targetRect.top - padding
107-
spaceBelow = innerHeight - targetRect.bottom - padding
108-
} else {
109-
spaceAbove = targetRect.bottom - padding
110-
spaceBelow = innerHeight - targetRect.bottom - padding
94+
spaceAbove = target.top - pad + oy
95+
spaceBelow = innerHeight - target.bottom - pad + oy
96+
} else if (ay === 'bottom') {
97+
spaceAbove = target.bottom - pad + oy
98+
spaceBelow = innerHeight - target.bottom - pad + oy
99+
} else if (ay === 'middle') {
100+
}
101+
} else if (ty === 'middle') {
102+
if (ay === 'top' || ay === 'bottom') {
103+
spaceAbove = target.top + (target.height / 2) - pad + oy
104+
spaceBelow = innerHeight - target.bottom + (target.height / 2) - pad + oy
105+
} else if (ay === 'middle') {
106+
spaceAbove = innerHeight - (pad * 2)
107+
spaceBelow = innerHeight - (pad * 2)
111108
}
112109
}
113110

114-
// calculate max height
115-
const maxHeight = Math.max(spaceAbove, spaceBelow)
116-
117-
// flip if neccesary
118-
if (ay === 'top' && spaceAbove > spaceBelow) {
119-
111+
// flip y if neccessary
112+
if (ay === 'top' && portal.height > spaceBelow && spaceAbove > spaceBelow) {
120113
// flip up
121114
ay = 'bottom'
122115
if (ty === 'top') {
123116
ty = 'bottom'
124-
} else {
117+
} else if (ty === 'bottom') {
125118
ty = 'top'
126119
}
127-
128-
} else if (ay === 'bottom' && spaceBelow > spaceAbove) {
129-
120+
} else if (ay === 'bottom' && portal.height > spaceAbove && spaceBelow > spaceAbove) {
130121
// flip down
131122
ay = 'top'
132123
if (ty === 'top') {
133124
ty = 'bottom'
134-
} else {
125+
} else if (ty === 'bottom') {
135126
ty = 'top'
136127
}
128+
}
137129

130+
// flip x if neccessary
131+
if (ax === 'left') {
132+
// flip left
133+
if (tx === 'left' && (target.left + portal.width + pad + ox) > innerWidth) {
134+
tx = 'right'
135+
ax = 'right'
136+
} else if (tx === 'right' && (target.right + portal.width + pad + ox) > innerWidth) {
137+
tx = 'left'
138+
ax = 'right'
139+
}
140+
} else if (ax === 'right') {
141+
// flip left
142+
if (tx === 'left' && (target.left - portal.width) < 0) {
143+
tx = 'right'
144+
ax = 'left'
145+
} else if (tx === 'right' && (target.right - portal.width) < 0) {
146+
tx = 'left'
147+
ax = 'left'
148+
}
138149
}
139150

140151
// apply max height
141-
this.applyStyles(portal, { maxHeight: `${maxHeight}px` })
152+
const maxHeight = Math.max(spaceAbove, spaceBelow)
153+
this.applyStyles(portalNode, { maxHeight: `${maxHeight}px` })
142154

143155
// use target width
144156
if (useTargetWidth) {
145-
this.applyStyles(portal, { width: `${targetRect.width}px` })
157+
this.applyStyles(portalNode, { width: `${target.width}px` })
146158
}
147159

148160
// use target height as min-height
149161
if (useTargetMinHeight) {
150-
this.applyStyles(portal, { minHeight: `${targetRect.height}px` })
162+
this.applyStyles(portalNode, { minHeight: `${target.height}px` })
151163
}
152164

153165
// tether
154166
this.tether = new Tether({
155-
element: portal,
156-
target: target,
167+
element: portalNode,
168+
target: targetNode,
157169
attachment: `${ay} ${ax}`,
158170
targetAttachment: `${ty} ${tx}`,
159171
offset: `${oy} ${ox}`,
@@ -164,15 +176,15 @@ export default class Dropdown extends Component {
164176
})
165177

166178
// fade in
167-
this.applyStyles(portal, { opacity: 1 })
179+
this.applyStyles(portalNode, { opacity: 1 })
168180

169181
// force reposition
170-
if (portalRect.height > maxHeight) {
182+
if (portal.height > maxHeight) {
171183
this.tether.position()
172184
}
173185
}
174186

175-
beforeClose(portal, remove) {
187+
beforeClose(portalNode, remove) {
176188
if (this.tether) {
177189
this.tether.destroy()
178190
}

src/Dropdown/Dropdown.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
border-radius: 2px;
99
background: #fff;
1010
transition: opacity .3s cubic-bezier(0.25,0.8,0.25,1);
11+
box-sizing: border-box;
1112
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),
1213
0 3px 1px -2px rgba(0,0,0,.2),
1314
0 1px 5px 0 rgba(0,0,0,.12);
15+
16+
*, *:before, *:after {
17+
box-sizing: inherit;
18+
}
1419
}

src/Menu/Menu.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
.mdl-portalmenu {
2+
box-sizing: border-box;
23

4+
*, *:before, *:after {
5+
box-sizing: inherit;
6+
}
37
}

src/MenuList/MenuList.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@
77
margin: 0;
88
padding: 0;
99
list-style: none;
10+
box-sizing: border-box;
11+
12+
*, *:before, *:after {
13+
box-sizing: inherit;
14+
}
1015
}

src/MultiSelectField/MultiSelectField.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default class MultiSelectField extends Component {
3636
this.onTextfieldBlur = this.onTextfieldBlur.bind(this)
3737
this.onChipClose = this.onChipClose.bind(this)
3838
if (props.showChipsBelow) {
39-
console.warn('Prop "showChipsBelow" is deprecated! Use "chipsBelow" instead.')
39+
console.warn('Prop showChipsBelow is deprecated! Use chipsBelow instead.')
4040
}
4141
}
4242

@@ -75,16 +75,15 @@ export default class MultiSelectField extends Component {
7575

7676
const { focused } = this.state
7777

78-
const allChildren = Children
79-
.toArray(this.props.children)
78+
const allChildren = Children.toArray(this.props.children)
8079

8180
const children = allChildren
8281
.filter(c => value && value.indexOf(c.props.value) === -1)
8382

8483
const options = children.length ? children : <Option disabled>Empty</Option>
8584

86-
const chips = allChildren
87-
.filter(c => value && value.indexOf(c.props.value) > -1)
85+
const chips = value
86+
.map(v => allChildren.find(c => c.props.value === v))
8887
.map(({props}) => (
8988
<Chip key={props.value} onClose={() => this.onChipClose(props.value)}>
9089
{props.children}

src/MultiSelectField/MultiSelectField.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
position: relative;
33
width: 300px;
44
padding: 20px 0;
5+
box-sizing: border-box;
6+
7+
*, *:before, *:after {
8+
box-sizing: inherit;
9+
}
510

611
.mdl-multiselect__container {
712
position: relative;

src/OptionList/OptionList.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@
77
margin: 0;
88
padding: 0;
99
list-style: none;
10+
box-sizing: border-box;
11+
12+
*, *:before, *:after {
13+
box-sizing: inherit;
14+
}
1015
}

src/SelectField/SelectField.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
.mdl-selectfield {
22
position: relative;
33
width: 300px;
4+
box-sizing: border-box;
5+
6+
*, *:before, *:after {
7+
box-sizing: inherit;
8+
}
49

510
.mdl-textfield {
611
width: 100%;

0 commit comments

Comments
 (0)