Skip to content

Commit 24c1232

Browse files
authored
Merge pull request #222 from microsoftgraph/nivogt/card-window-bounds
Fixed issue where flyouts render outside of window bounds
2 parents 277031c + 8c87989 commit 24c1232

File tree

7 files changed

+313
-165
lines changed

7 files changed

+313
-165
lines changed

src/components/baseComponent.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* -------------------------------------------------------------------------------------------
66
*/
77

8-
import { LitElement } from 'lit-element';
8+
import { LitElement, PropertyValues } from 'lit-element';
99
/**
1010
* BaseComponent extends LitElement including ShadowRoot toggle and fireCustomEvent features
1111
*
@@ -45,7 +45,7 @@ export abstract class MgtBaseComponent extends LitElement {
4545
}
4646

4747
/**
48-
* Recieve ShadowRoot Disabled value
48+
* Receive ShadowRoot Disabled value
4949
*
5050
* @returns boolean _useShadowRoot value
5151
* @memberof MgtBaseComponent
@@ -81,4 +81,22 @@ export abstract class MgtBaseComponent extends LitElement {
8181
protected createRenderRoot() {
8282
return this.isShadowRootDisabled() ? this : super.createRenderRoot();
8383
}
84+
85+
/**
86+
* Invoked whenever the element is updated. Implement to perform
87+
* post-updating tasks via DOM APIs, for example, focusing an element.
88+
*
89+
* Setting properties inside this method will trigger the element to update
90+
* again after this update cycle completes.
91+
*
92+
* * @param changedProperties Map of changed properties with old values
93+
*/
94+
protected updated(changedProperties: PropertyValues) {
95+
super.updated(changedProperties);
96+
const event = new CustomEvent('updated', {
97+
bubbles: true,
98+
cancelable: true
99+
});
100+
this.dispatchEvent(event);
101+
}
84102
}

src/components/mgt-person/mgt-person.scss

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -137,20 +137,6 @@ mgt-person .user-email {
137137
-ms-grid-row: 2;
138138
}
139139

140-
.root .flyout {
141-
display: none;
142-
position: absolute;
143-
z-index: 1;
144-
opacity: 0;
145-
transition-property: opacity;
146-
transition-duration: 0.1s;
140+
.flyout {
147141
padding: 8px;
148142
}
149-
150-
.root .flyout.visible {
151-
display: inline-block;
152-
opacity: 1;
153-
&.openLeft {
154-
right: 0;
155-
}
156-
}

src/components/mgt-person/mgt-person.ts

Lines changed: 44 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
*/
77

88
import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
9-
import { customElement, html, property, PropertyValues } from 'lit-element';
9+
import { customElement, html, property, PropertyValues, TemplateResult } from 'lit-element';
1010
import { classMap } from 'lit-html/directives/class-map';
11+
import { styleMap } from 'lit-html/directives/style-map';
1112
import { Providers } from '../../Providers';
1213
import { ProviderState } from '../../providers/IProvider';
1314
import '../../styles/fabric-icon-font';
1415
import { getEmailFromGraphEntity } from '../../utils/GraphHelpers';
1516
import { MgtPersonCard } from '../mgt-person-card/mgt-person-card';
17+
import '../sub-components/mgt-flyout/mgt-flyout';
1618
import { MgtTemplatedComponent } from '../templatedComponent';
1719
import { PersonCardInteraction } from './../PersonCardInteraction';
1820
import { styles } from './mgt-person-css';
@@ -115,8 +117,8 @@ export class MgtPerson extends MgtTemplatedComponent {
115117
})
116118
public personCardInteraction: PersonCardInteraction = PersonCardInteraction.none;
117119

118-
@property({ attribute: false }) private _isPersonCardVisible: boolean = false;
119-
@property({ attribute: false }) private _personCardShouldRender: boolean = false;
120+
@property({ attribute: false }) private isPersonCardVisible: boolean = false;
121+
@property({ attribute: false }) private personCardShouldRender: boolean = false;
120122

121123
private _mouseLeaveTimeout;
122124
private _mouseEnterTimeout;
@@ -172,11 +174,11 @@ export class MgtPerson extends MgtTemplatedComponent {
172174
return html`
173175
<div
174176
class="root"
175-
@mouseenter=${this._handleMouseEnter}
176-
@mouseleave=${this._handleMouseLeave}
177-
@click=${this._handleMouseClick}
177+
@mouseenter=${this.handleMouseEnter}
178+
@mouseleave=${this.handleMouseLeave}
179+
@click=${this.handleMouseClick}
178180
>
179-
${person} ${this.renderPersonCard()}
181+
${this.renderFlyout(person)}
180182
</div>
181183
`;
182184
}
@@ -284,40 +286,41 @@ export class MgtPerson extends MgtTemplatedComponent {
284286
this.requestUpdate();
285287
}
286288

287-
private _handleMouseClick() {
288-
if (this.personCardInteraction === PersonCardInteraction.click && !this._isPersonCardVisible) {
289-
this._showPersonCard();
289+
private handleMouseClick() {
290+
if (this.personCardInteraction === PersonCardInteraction.click && !this.isPersonCardVisible) {
291+
this.showPersonCard();
290292
} else {
291-
this._hidePersonCard();
293+
this.hidePersonCard();
292294
}
293295
}
294296

295-
private _handleMouseEnter(e: MouseEvent) {
297+
private handleMouseEnter(e: MouseEvent) {
296298
clearTimeout(this._mouseEnterTimeout);
297299
clearTimeout(this._mouseLeaveTimeout);
298300
if (this.personCardInteraction !== PersonCardInteraction.hover) {
299301
return;
300302
}
301-
this._mouseEnterTimeout = setTimeout(this._showPersonCard.bind(this), 500);
303+
this._mouseEnterTimeout = setTimeout(this.showPersonCard.bind(this), 500);
302304
}
303305

304-
private _handleMouseLeave(e: MouseEvent) {
306+
private handleMouseLeave(e: MouseEvent) {
305307
clearTimeout(this._mouseEnterTimeout);
306308
clearTimeout(this._mouseLeaveTimeout);
307-
this._mouseLeaveTimeout = setTimeout(this._hidePersonCard.bind(this), 500);
309+
this._mouseLeaveTimeout = setTimeout(this.hidePersonCard.bind(this), 500);
308310
}
309311

310-
private _showPersonCard() {
311-
if (!this._personCardShouldRender) {
312-
this._personCardShouldRender = true;
312+
private showPersonCard() {
313+
if (!this.personCardShouldRender) {
314+
this.personCardShouldRender = true;
313315
}
314316

315-
this._isPersonCardVisible = true;
317+
this.isPersonCardVisible = true;
316318
}
317319

318-
private _hidePersonCard() {
319-
this._isPersonCardVisible = false;
320-
const personCard = this.querySelector('mgt-person-card') as MgtPersonCard;
320+
private hidePersonCard() {
321+
this.isPersonCardVisible = false;
322+
const personCard = (this.querySelector('mgt-person-card') ||
323+
this.renderRoot.querySelector('mgt-person-card')) as MgtPersonCard;
321324
if (personCard) {
322325
personCard.isExpanded = false;
323326
}
@@ -332,46 +335,28 @@ export class MgtPerson extends MgtTemplatedComponent {
332335
return null;
333336
}
334337

335-
private renderPersonCard() {
336-
// ensure person card is only rendered when needed
337-
if (this.personCardInteraction === PersonCardInteraction.none || !this._personCardShouldRender) {
338-
return;
339-
}
340-
// logic for rendering left if there is no space
341-
const personRect = this.renderRoot.querySelector('.root').getBoundingClientRect();
342-
const leftEdge = personRect.left;
343-
const rightEdge = (window.innerWidth || document.documentElement.clientWidth) - personRect.right;
344-
this._openLeft = rightEdge < leftEdge;
345-
346-
// logic for rendering up
347-
const bottomEdge = (window.innerHeight || document.documentElement.clientHeight) - personRect.bottom;
348-
this._openUp = bottomEdge < 175;
349-
350-
// find position to renderup to
351-
let customStyle = null;
352-
if (this._openUp) {
353-
const personSize = this.getBoundingClientRect().bottom - this.getBoundingClientRect().top;
354-
customStyle = `bottom: ${personSize / 2 + 8}px`;
338+
private renderFlyout(anchor: TemplateResult) {
339+
if (this.personCardInteraction === PersonCardInteraction.none) {
340+
return anchor;
355341
}
356342

357-
const flyoutClasses = {
358-
flyout: true,
359-
openLeft: this._openLeft,
360-
openUp: this._openUp,
361-
visible: this._isPersonCardVisible
362-
};
363-
if (this._isPersonCardVisible) {
364-
const image = this.getImage();
343+
const image = this.getImage();
344+
const flyout = this.personCardShouldRender
345+
? html`
346+
<div slot="flyout" class="flyout">
347+
${this.renderTemplate('person-card', { person: this.personDetails, personImage: image }) ||
348+
html`
349+
<mgt-person-card .personDetails=${this.personDetails} .personImage=${image}> </mgt-person-card>
350+
`}
351+
</div>
352+
`
353+
: null;
365354

366-
return html`
367-
<div style="${customStyle}" class=${classMap(flyoutClasses)}>
368-
${this.renderTemplate('person-card', { person: this.personDetails, personImage: image }) ||
369-
html`
370-
<mgt-person-card .personDetails=${this.personDetails} .personImage=${image}> </mgt-person-card>
371-
`}
372-
</div>
373-
`;
374-
}
355+
return html`
356+
<mgt-flyout .isOpen=${this.isPersonCardVisible}>
357+
${anchor} ${flyout}
358+
</mgt-flyout>
359+
`;
375360
}
376361

377362
private renderDetails() {

src/components/mgt-tasks/mgt-tasks.scss

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,21 +198,16 @@ mgt-tasks,
198198
}
199199
.Picker {
200200
background-color: white;
201-
display: block;
202-
position: absolute;
203201
box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
204202
-webkit-background-clip: padding-box;
205203
-moz-background-clip: padding-box;
206204
background-clip: padding-box;
207-
width: 450px;
205+
width: 350px;
208206
color: var(--task-detail-color, black);
209-
z-index: 1;
207+
margin: 8px;
210208
mgt-people-picker {
211209
--separator-margin: 0px 10px 0px 10px;
212210
}
213-
&.Hidden {
214-
display: none;
215-
}
216211
}
217212

218213
input,

0 commit comments

Comments
 (0)