Skip to content

feat(ui5-input): poc composition handling and typeahead #12147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion packages/main/src/Input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ type InputAccInfo = {
ariaInvalid?: boolean,
}

type CompositionEventHandler = (e?: CompositionEvent) => void;

// all sementic events
enum INPUT_EVENTS {
CHANGE = "change",
Expand Down Expand Up @@ -566,6 +568,14 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement
@property({ type: Array })
_linksListenersArray: Array<(args: any) => void> = [];

/**
* Indicates whether IME composition is currently active
* @default false
* @private
*/
@property({ type: Boolean, noAttribute: true })
_isComposing = false;

/**
* Defines the suggestion items.
*
Expand Down Expand Up @@ -628,6 +638,8 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement
_isLatestValueFromSuggestions: boolean;
_isChangeTriggeredBySuggestion: boolean;
_valueStateLinks: Array<HTMLElement>;
_onCompositionStartBound: CompositionEventHandler;
_onCompositionEndBound: CompositionEventHandler;
@i18n("@ui5/webcomponents")
static i18nBundle: I18nBundle;

Expand Down Expand Up @@ -702,17 +714,22 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement
this._keepInnerValue = false;
this._focusedAfterClear = false;
this._valueStateLinks = [];

this._onCompositionStartBound = this._onCompositionStart.bind(this);
this._onCompositionEndBound = this._onCompositionEnd.bind(this);
}

onEnterDOM() {
ResizeHandler.register(this, this._handleResizeBound);
registerUI5Element(this, this._updateAssociatedLabelsTexts.bind(this));
this._addCompositionEventListeners();
}

onExitDOM() {
ResizeHandler.deregister(this, this._handleResizeBound);
deregisterUI5Element(this);
this._removeLinksEventListeners();
this._removeCompositionEventListeners();
}

_highlightSuggestionItem(item: SuggestionItem) {
Expand Down Expand Up @@ -776,7 +793,9 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement
if (this._shouldAutocomplete && !isAndroid() && !autoCompletedChars && !this._isKeyNavigation) {
const item = this._getFirstMatchingItem(value);
if (item) {
this._handleTypeAhead(item);
if (!this._isComposing) {
this._handleTypeAhead(item);
}
this._selectMatchingItem(item);
}
}
Expand Down Expand Up @@ -1119,6 +1138,32 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement
}
}

_addCompositionEventListeners() {
const input = this.getInputDOMRefSync();
if (!input) { return; }

this._removeCompositionEventListeners();

input.addEventListener("compositionstart", this._onCompositionStartBound);
input.addEventListener("compositionend", this._onCompositionEndBound);
}

_removeCompositionEventListeners() {
const input = this.getInputDOMRefSync();
if (!input) { return; }

input.removeEventListener("compositionstart", this._onCompositionStartBound);
input.removeEventListener("compositionend", this._onCompositionEndBound);
}

_onCompositionStart() {
this._isComposing = true;
}

_onCompositionEnd() {
this._isComposing = false;
}

_clearPopoverFocusAndSelection() {
if (!this.showSuggestions || !this.Suggestions) {
return;
Expand Down
3 changes: 3 additions & 0 deletions packages/main/src/InputTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ export default function InputTemplate(this: Input, hooks?: { preContent: Templat
onKeyUp={this._onkeyup}
onClick={this._click}
onFocusIn={this.innerFocusIn}
// uncomment after preact version in updated
// onCompositionStart={this._onCompositionStart}
// onCompositionEnd={this._onCompositionEnd}
/>

{this._effectiveShowClearIcon &&
Expand Down
44 changes: 43 additions & 1 deletion packages/main/src/MultiComboBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ type MultiComboboxItemWithSelection = {
selected: boolean,
};

type CompositionEventHandler = (e?: CompositionEvent) => void;

/**
* @class
*
Expand Down Expand Up @@ -479,6 +481,14 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
@property({ type: Array })
_linksListenersArray: Array<(args: any) => void> = [];

/**
* Indicates whether IME composition is currently active
* @default false
* @private
*/
@property({ type: Boolean, noAttribute: true })
_isComposing = false;

/**
* Defines the component items.
* @public
Expand Down Expand Up @@ -533,6 +543,8 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
_itemsBeforeOpen: Array<MultiComboboxItemWithSelection>;
selectedItems: Array<IMultiComboBoxItem>;
_valueStateLinks: Array<HTMLElement>;
_onCompositionStartBound: CompositionEventHandler;
_onCompositionEndBound: CompositionEventHandler;
@i18n("@ui5/webcomponents")
static i18nBundle: I18nBundle;

Expand Down Expand Up @@ -584,15 +596,19 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
this._lastValue = this.getAttribute("value") || "";
this.currentItemIdx = -1;
this._valueStateLinks = [];
this._onCompositionStartBound = this._onCompositionStart.bind(this);
this._onCompositionEndBound = this._onCompositionEnd.bind(this);
}

onEnterDOM() {
ResizeHandler.register(this, this._handleResizeBound);
this._addCompositionEventListeners();
}

onExitDOM() {
ResizeHandler.deregister(this, this._handleResizeBound);
this._removeLinksEventListeners();
this._removeCompositionEventListeners();
}

_handleResize() {
Expand Down Expand Up @@ -1689,6 +1705,32 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
});
}

_addCompositionEventListeners() {
const input = this._innerInput;
if (!input) { return; }

this._removeCompositionEventListeners();

input.addEventListener("compositionstart", this._onCompositionStartBound);
input.addEventListener("compositionend", this._onCompositionEndBound);
}

_removeCompositionEventListeners() {
const input = this._innerInput;
if (!input) { return; }

input.removeEventListener("compositionstart", this._onCompositionStartBound);
input.removeEventListener("compositionend", this._onCompositionEndBound);
}

_onCompositionStart() {
this._isComposing = true;
}

_onCompositionEnd() {
this._isComposing = false;
}

onBeforeRendering() {
const input = this._innerInput;
const autoCompletedChars = input && (input.selectionEnd || 0) - (input.selectionStart || 0);
Expand Down Expand Up @@ -1728,7 +1770,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {

// Keep the original typed in text intact
this.valueBeforeAutoComplete = value;
item && this._handleTypeAhead(item, value);
item && !this._isComposing && this._handleTypeAhead(item, value);
}

if (this._shouldFilterItems) {
Expand Down
42 changes: 42 additions & 0 deletions packages/main/test/pages/ComboBox.html
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@
<ui5-cb-item text="Bulgaria"></ui5-cb-item>
<ui5-cb-item text="Canada"></ui5-cb-item>
<ui5-cb-item text="Germany"></ui5-cb-item>
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="東京"></ui5-cb-item>
<ui5-cb-item text="大阪"></ui5-cb-item>
</ui5-combobox>

<br>
Expand Down Expand Up @@ -218,6 +224,10 @@
<ui5-cb-item text="Bulgaria"></ui5-cb-item>
<ui5-cb-item text="Canada"></ui5-cb-item>
<ui5-cb-item text="Germany"></ui5-cb-item>
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
</ui5-combobox>

<br>
Expand All @@ -232,6 +242,10 @@
<ui5-combobox id="input-cb" class="combobox2auto">
<ui5-cb-item text="Argentina"></ui5-cb-item>
<ui5-cb-item text="Germany"></ui5-cb-item>
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
</ui5-combobox>

<br>
Expand All @@ -252,6 +266,10 @@
<ui5-cb-item text="Bulgaria"></ui5-cb-item>
<ui5-cb-item text="Canada"></ui5-cb-item>
<ui5-cb-item text="Germany"></ui5-cb-item>
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
</ui5-combobox>
</div>

Expand All @@ -263,6 +281,10 @@
<ui5-cb-item text="United States of America"></ui5-cb-item>
<ui5-cb-item text="Canada"></ui5-cb-item>
<ui5-cb-item text="Germany"></ui5-cb-item>
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
</ui5-combobox>
</div>

Expand All @@ -274,6 +296,10 @@ <h3>ComboBox in Compact</h3>
<ui5-cb-item text="United States of America"></ui5-cb-item>
<ui5-cb-item text="Canada"></ui5-cb-item>
<ui5-cb-item text="Germany"></ui5-cb-item>
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
</ui5-combobox>
</div>
</section>
Expand All @@ -286,6 +312,10 @@ <h3>ComboBox in Compact</h3>
<ui5-cb-item text="United States of America"></ui5-cb-item>
<ui5-cb-item text="Canada"></ui5-cb-item>
<ui5-cb-item text="Germany"></ui5-cb-item>
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
</ui5-combobox>
</div>

Expand All @@ -296,6 +326,10 @@ <h3>ComboBox in Compact</h3>
<ui5-cb-item text="Argentina"></ui5-cb-item>
<ui5-cb-item text="Germany"></ui5-cb-item>
<ui5-cb-item text="Argentina"></ui5-cb-item>
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
</ui5-combobox>
</div>

Expand All @@ -310,6 +344,10 @@ <h3>ComboBox in Compact</h3>

<div class="demo-section">
<ui5-combobox id="value-state-error-text" value-state="Negative">
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="Algeria"></ui5-cb-item>
<ui5-cb-item text="Argentina"></ui5-cb-item>
<ui5-cb-item text="Australia"></ui5-cb-item>
Expand Down Expand Up @@ -344,6 +382,10 @@ <h3>ComboBox in Compact</h3>

<div class="demo-section">
<ui5-combobox id="clear-icon-cb" value="Clear Icon" show-clear-icon>
<ui5-cb-item text="이 ㅚ"></ui5-cb-item>
<ui5-cb-item text="이 ㅚㅈ"></ui5-cb-item>
<ui5-cb-item text="잊ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="이즈ㅈㅅ"></ui5-cb-item>
<ui5-cb-item text="Algeria"></ui5-cb-item>
<ui5-cb-item text="Argentina"></ui5-cb-item>
<ui5-cb-item text="Australia"></ui5-cb-item>
Expand Down
36 changes: 36 additions & 0 deletions packages/main/test/pages/Input.html
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,42 @@ <h3>Input - open suggestions picker</h3>
{ "key": "Antigua", "text": "Antigua and Barbuda" }
]
},
/*
* Korean
* "이 ㅚ"
*/
{
"key": "ㅇ",
"text": "ㅇ",
"items": [
{ "key": "이", "text": "이 ㅚ" },
{ "key": "이 ㅚ", "text": "이 ㅚㅈ" },
{ "key": "잊", "text": "잊ㅈㅅ" },
{ "key": "이즈", "text": "이즈ㅈㅅ" },
]
},
/*
* Japanese
* 東京
*/
{
key: "か",
text: "か",
items: [
{ key: "からの", text: "東京" }
]
},
/*
* Chinese
* 廿人大卜
*/
{
"key": "廿",
"text": "廿",
"items": [
{ "key": "廿人", "text": "廿人大卜" }
]
},
{
"key": "B",
"text": "B",
Expand Down
6 changes: 6 additions & 0 deletions packages/main/test/pages/MultiComboBox.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@
<ui5-mcb-item text="Compact"></ui5-mcb-item>
<ui5-mcb-item selected text="Condensed"></ui5-mcb-item>
<ui5-mcb-item selected text="Longest word in the world"></ui5-mcb-item>
<ui5-mcb-item text="이 ㅚ"></ui5-mcb-item>
<ui5-mcb-item text="이 ㅚㅈ"></ui5-mcb-item>
<ui5-mcb-item selected text="잊ㅈㅅ"></ui5-mcb-item>
<ui5-mcb-item text="이즈ㅈㅅ"></ui5-mcb-item>
<ui5-mcb-item text="東京"></ui5-mcb-item>
<ui5-mcb-item text="大阪"></ui5-mcb-item>
</ui5-multi-combobox>
<br>
<br>
Expand Down
4 changes: 4 additions & 0 deletions packages/main/test/pages/MultiInput.html
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ <h1>Tokens + Suggestions</h1>
<ui5-suggestion-item text="amet"></ui5-suggestion-item>
<ui5-suggestion-item text="aute"></ui5-suggestion-item>
<ui5-suggestion-item text="excepteur"></ui5-suggestion-item>
<ui5-suggestion-item text="이 ㅚ"></ui5-suggestion-item>
<ui5-suggestion-item text="이 ㅚㅈ"></ui5-suggestion-item>
<ui5-suggestion-item text="잊ㅈㅅ"></ui5-suggestion-item>
<ui5-suggestion-item text="이즈ㅈㅅ"></ui5-suggestion-item>
<div slot="valueStateMessage">Information message. This is a <ui5-link href="#">Link</ui5-link>. Extra long text used as an information message. Extra long text used as an information message - 2. Extra long text used as an information message - 3.</div>
</ui5-multi-input>

Expand Down
Loading