Skip to content

Commit

Permalink
show suggestions for bubble text field in overlay dropdown, resolves #…
Browse files Browse the repository at this point in the history
  • Loading branch information
vaf-hub committed May 10, 2021
1 parent 2009da9 commit 331cff0
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 40 deletions.
4 changes: 2 additions & 2 deletions src/calendar/view/CalendarEventEditDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ export function showCalendarEventDialog(date: Date, calendars: Map<Id, CalendarI
return m(".calendar-edit-container.pb", [
renderHeading(),
renderChangesMessage(),
m(".mb", m(ExpanderPanelN, {
m(".mb.rel", m(ExpanderPanelN, {
expanded: attendeesExpanded,
},
[
Expand Down Expand Up @@ -538,7 +538,7 @@ function makeBubbleTextField(viewModel: CalendarEventViewModel, contactModel: Co
},
}, contactModel)

const invitePeopleValueTextField = new BubbleTextField("addGuest_label", bubbleHandler, {marginLeft: 0}, () => renderConfidentialButton(viewModel))
const invitePeopleValueTextField = new BubbleTextField("addGuest_label", bubbleHandler, () => renderConfidentialButton(viewModel))
return invitePeopleValueTextField
}

Expand Down
52 changes: 43 additions & 9 deletions src/gui/base/BubbleTextField.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow
import {inputLineHeight, px, size} from "../size"
import m from "mithril"
import {animations, fontSize, height, transform} from "./../animation/Animations"
import {animations, fontSize, transform} from "./../animation/Animations"
import {assertMainOrNode} from "../../api/common/Env"
import {progressIcon} from "./Icon"
import type {ButtonAttrs} from "./ButtonN"
Expand All @@ -15,6 +15,7 @@ import {TabIndex} from "../../api/common/TutanotaConstants"
import {ease} from "../animation/Easing"
import type {TextFieldTypeEnum} from "./TextFieldN"
import {Type} from "./TextFieldN"
import {windowFacade} from "../../misc/WindowFacade"

assertMainOrNode()

Expand Down Expand Up @@ -63,6 +64,11 @@ export interface Suggestion {
selected: boolean;
}

/**
* Some parent element of the BubbleTextField needs to have position relative for the suggestions to be correctly positioned.
* We do not add position relative here because it allows us to show the suggestions dropdown even if a parent has overflow hidden
* by making the element with position relative a parent of that element!
*/
export class BubbleTextField<T> {
loading: ?Promise<void>;
bubbles: Bubble<T>[];
Expand All @@ -72,13 +78,14 @@ export class BubbleTextField<T> {
selectedSuggestion: ?Suggestion;
suggestionAnimation: Promise<void>;
bubbleHandler: BubbleHandler<T, Suggestion>;
view: Function;
view: (Vnode<T>) => Children;
oncreate: (VnodeDOM<T>) => mixed;

_textField: TextField;
_domSuggestions: HTMLElement;
_keyboardHeight: number;

constructor(labelIdOrLabelTextFunction: TranslationKey | lazy<string>, bubbleHandler: BubbleHandler<T, any>,
suggestionStyle: {[string]: any} = {}, injectionsRight: ?lazy<Children> = () => null, disabled: ?boolean = false) {
constructor(labelIdOrLabelTextFunction: TranslationKey | lazy<string>, bubbleHandler: BubbleHandler<T, any>, injectionsRight: ?lazy<Children> = () => null, disabled: ?boolean = false) {
this.loading = null
this.suggestions = []
this.selectedSuggestion = null
Expand Down Expand Up @@ -117,6 +124,15 @@ export class BubbleTextField<T> {

this.bubbleHandler = bubbleHandler

this._keyboardHeight = 0

this.oncreate = () => {
windowFacade.addKeyboardSizeListener((newSize) => {
this._keyboardHeight = newSize
this.animateSuggestionsHeight(this.suggestions.length, this.suggestions.length)
})
}

this.view = () => {
return m('.bubble-text-field', [
m(this._textField, {
Expand All @@ -133,10 +149,12 @@ export class BubbleTextField<T> {
})
}
}),
m(".suggestions.text-ellipsis.ml-negative-l", {
m(`.suggestions.abs.z4.full-width.elevated-bg.scroll.text-ellipsis${this.suggestions.length ? ".dropdown-shadow" : ""}`, {
oncreate: vnode => this._domSuggestions = vnode.dom,
onmousedown: e => this._textField.skipNextBlur = true,
style: suggestionStyle,
style: {
transition: "height 0.2s"
},
}, this.suggestions.map(s => m(s, {
mouseDownHandler: e => {
this.selectedSuggestion = s
Expand Down Expand Up @@ -190,9 +208,25 @@ export class BubbleTextField<T> {
}

animateSuggestionsHeight(currentCount: number, newCount: number) {
let currentHeight = this.bubbleHandler.suggestionHeight * currentCount
let newHeight = this.bubbleHandler.suggestionHeight * newCount
this.suggestionAnimation = this.suggestionAnimation.then(() => animations.add(this._domSuggestions, height(currentHeight, newHeight)))
// *-------------------* -
// | | |
// | ------------- | - <- top
// | | | |
// | |-----------| |
// |-------------------| - <- keyboardHeight
// | q w e r t z u i o | |
// | a s d f g h j k l | -
//
// On iOS screen is not resized when keyboard is opened. Instead we send a signal to WebView with keyboard height.
// We need to calculate how much space can be actually used for the dropdown. We cannot just add margin like we do with dialog
// because the suggestions dropdown is absolutely positioned.
if (this._domSuggestions) {
const desiredHeight = this.bubbleHandler.suggestionHeight * newCount
const top = this._domSuggestions.getBoundingClientRect().top
const availableHeight = window.innerHeight - top - this._keyboardHeight - size.vpad
const finalHeight = Math.min(availableHeight, desiredHeight)
this._domSuggestions.style.height = px(finalHeight)
}
}


Expand Down
48 changes: 24 additions & 24 deletions src/gui/base/Expander.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,31 @@ export class ExpanderButtonN implements MComponent<ExpanderAttrs> {
const a = vnode.attrs
return m(".flex.limit-width", [ // .limit-width does not work without .flex in IE11
m("button.expander.bg-transparent.pt-s.hover-ul.limit-width", {
style: a.style,
onclick: (event: MouseEvent) => {
a.expanded(!a.expanded())
event.stopPropagation()
style: a.style,
onclick: (event: MouseEvent) => {
a.expanded(!a.expanded())
event.stopPropagation()
},
oncreate: vnode => addFlash(vnode.dom),
onremove: (vnode) => removeFlash(vnode.dom),
"aria-expanded": String(!!a.expanded()),
}, m(".flex.items-center", [ // TODO remove wrapper after Firefox 52 has been deployed widely https://bugzilla.mozilla.org/show_bug.cgi?id=984869
(a.showWarning) ? m(Icon, {
icon: Icons.Warning,
style: {
fill: a.color ? a.color : theme.content_button,
}
}) : null,
m("small.b.text-ellipsis", {style: {color: a.color || theme.content_button}}, lang.getMaybeLazy(a.label).toUpperCase()),
m(Icon, {
icon: BootIcons.Expand,
class: "flex-center items-center",
style: {
fill: a.color ? a.color : theme.content_button,
'margin-right': px(-4), // icon is has 4px whitespace to the right,
transform: `rotateZ(${a.expanded() ? 180 : 0}deg)`,
transition: `transform ${DefaultAnimationTime}ms`
},
oncreate: vnode => addFlash(vnode.dom),
onremove: (vnode) => removeFlash(vnode.dom),
"aria-expanded": String(!!a.expanded()),
}, m(".flex.items-center", [ // TODO remove wrapper after Firefox 52 has been deployed widely https://bugzilla.mozilla.org/show_bug.cgi?id=984869
(a.showWarning) ? m(Icon, {
icon: Icons.Warning,
style: {
fill: a.color ? a.color : theme.content_button,
}
}) : null,
m("small.b.text-ellipsis", {style: {color: a.color || theme.content_button}}, lang.getMaybeLazy(a.label).toUpperCase()),
m(Icon, {
icon: BootIcons.Expand,
class: "flex-center items-center",
style: {
fill: a.color ? a.color : theme.content_button,
'margin-right': px(-4), // icon is has 4px whitespace to the right,
transform: `rotateZ(${a.expanded() ? 180 : 0}deg)`,
transition: `transform ${DefaultAnimationTime}ms`
},
}),
])),
])
Expand Down
6 changes: 3 additions & 3 deletions src/mail/editor/MailEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,8 @@ export class MailEditor implements MComponent<MailEditorAttrs> {
onload: (ev) => {
}
}, [
m(this.recipientFields.to.component),
m(ExpanderPanelN, {expanded: a.areDetailsExpanded},
m(".rel", m(this.recipientFields.to.component)),
m(".rel", m(ExpanderPanelN, {expanded: a.areDetailsExpanded},
m(".details", [
m(this.recipientFields.cc.component),
m(this.recipientFields.bcc.component),
Expand All @@ -412,7 +412,7 @@ export class MailEditor implements MComponent<MailEditorAttrs> {

]),
])
),
)),
isConfidential
? m(".external-recipients.overflow-hidden", {
oncreate: vnode => animate(vnode.dom, true),
Expand Down
2 changes: 1 addition & 1 deletion src/mail/editor/MailEditorViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export class MailEditorRecipientField implements RecipientInfoBubbleFactory {
this.field = fieldType

const handler = new RecipientInfoBubbleHandler(this, contactModel)
this.component = new BubbleTextField(_getRecipientFieldLabelTranslationKey(this.field), handler, {}, injectionsRight, disabled)
this.component = new BubbleTextField(_getRecipientFieldLabelTranslationKey(this.field), handler, injectionsRight, disabled)

// we want to fill in the field with existing recipients from the model
this.component.bubbles = this._modelRecipients().map(recipient => this.createBubble(recipient.name, recipient.mailAddress, recipient.contact))
Expand Down
2 changes: 1 addition & 1 deletion src/sharing/view/GroupSharingDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ function showAddParticipantDialog(model: GroupSharingModel, texts: GroupSharingT
title: () => lang.get("addParticipant_action"),
child: () => [
m(".pt", texts.addMemberMessage(customGroupName || realGroupName)),
m(invitePeopleValueTextField),
m(".rel", m(invitePeopleValueTextField)),
m(DropDownSelectorN, {
label: "permissions_label",
items: [
Expand Down

0 comments on commit 331cff0

Please sign in to comment.