-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: extract message-input logic into reusable mixin (#8249)
- Loading branch information
1 parent
86e36af
commit eae2558
Showing
5 changed files
with
234 additions
and
205 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
packages/message-input/src/vaadin-message-input-mixin.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/** | ||
* @license | ||
* Copyright (c) 2021 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
*/ | ||
import type { Constructor } from '@open-wc/dedupe-mixin'; | ||
import type { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js'; | ||
|
||
export interface MessageInputI18n { | ||
send: string; | ||
message: string; | ||
} | ||
|
||
export declare function MessageInputMixin<T extends Constructor<HTMLElement>>( | ||
base: T, | ||
): Constructor<ControllerMixinClass> & Constructor<MessageInputMixinClass> & T; | ||
|
||
export declare class MessageInputMixinClass { | ||
/** | ||
* Current content of the text input field | ||
*/ | ||
value: string | null | undefined; | ||
|
||
/** | ||
* The object used to localize this component. | ||
* For changing the default localization, change the entire | ||
* `i18n` object. | ||
* | ||
* The object has the following JSON structure and default values: | ||
* | ||
* ``` | ||
* { | ||
* // Used as the button label | ||
* send: 'Send', | ||
* | ||
* // Used as the input field's placeholder and aria-label | ||
* message: 'Message' | ||
* } | ||
* ``` | ||
*/ | ||
i18n: MessageInputI18n; | ||
|
||
/** | ||
* Set to true to disable this element. | ||
*/ | ||
disabled: boolean; | ||
} |
180 changes: 180 additions & 0 deletions
180
packages/message-input/src/vaadin-message-input-mixin.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
/** | ||
* @license | ||
* Copyright (c) 2021 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
*/ | ||
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js'; | ||
import { SlotController } from '@vaadin/component-base/src/slot-controller.js'; | ||
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js'; | ||
|
||
/** | ||
* @polymerMixin | ||
* @mixes ControllerMixin | ||
*/ | ||
export const MessageInputMixin = (superClass) => | ||
class MessageInputMixinClass extends ControllerMixin(superClass) { | ||
static get properties() { | ||
return { | ||
/** | ||
* Current content of the text input field | ||
*/ | ||
value: { | ||
type: String, | ||
value: '', | ||
}, | ||
|
||
/** | ||
* The object used to localize this component. | ||
* For changing the default localization, change the entire | ||
* `i18n` object. | ||
* | ||
* The object has the following JSON structure and default values: | ||
* | ||
* ``` | ||
* { | ||
* // Used as the button label | ||
* send: 'Send', | ||
* | ||
* // Used as the input field's placeholder and aria-label | ||
* message: 'Message' | ||
* } | ||
* ``` | ||
* | ||
* @type {!MessageInputI18n} | ||
* @default {English} | ||
*/ | ||
i18n: { | ||
type: Object, | ||
value: () => ({ | ||
send: 'Send', | ||
message: 'Message', | ||
}), | ||
}, | ||
|
||
/** | ||
* Set to true to disable this element. | ||
* @type {boolean} | ||
*/ | ||
disabled: { | ||
type: Boolean, | ||
value: false, | ||
reflectToAttribute: true, | ||
}, | ||
|
||
/** @private */ | ||
_button: { | ||
type: Object, | ||
}, | ||
|
||
/** @private */ | ||
_textArea: { | ||
type: Object, | ||
}, | ||
}; | ||
} | ||
|
||
static get observers() { | ||
return [ | ||
'__buttonPropsChanged(_button, disabled, i18n)', | ||
'__textAreaPropsChanged(_textArea, disabled, i18n, value)', | ||
]; | ||
} | ||
|
||
/** @protected */ | ||
ready() { | ||
super.ready(); | ||
|
||
this._buttonController = new SlotController(this, 'button', 'vaadin-button', { | ||
initializer: (btn) => { | ||
btn.setAttribute('theme', 'primary contained'); | ||
|
||
btn.addEventListener('click', () => { | ||
this.__submit(); | ||
}); | ||
|
||
this._button = btn; | ||
}, | ||
}); | ||
this.addController(this._buttonController); | ||
|
||
this._textAreaController = new SlotController(this, 'textarea', 'vaadin-text-area', { | ||
initializer: (textarea) => { | ||
textarea.addEventListener('value-changed', (event) => { | ||
this.value = event.detail.value; | ||
}); | ||
|
||
textarea.addEventListener('keydown', (event) => { | ||
if (event.key === 'Enter' && !event.shiftKey) { | ||
event.preventDefault(); | ||
event.stopImmediatePropagation(); | ||
this.__submit(); | ||
} | ||
}); | ||
|
||
const input = textarea.inputElement; | ||
input.removeAttribute('aria-labelledby'); | ||
|
||
// Set initial height to one row | ||
input.setAttribute('rows', 1); | ||
input.style.minHeight = '0'; | ||
|
||
this._textArea = textarea; | ||
}, | ||
}); | ||
this.addController(this._textAreaController); | ||
|
||
this._tooltipController = new TooltipController(this); | ||
this.addController(this._tooltipController); | ||
} | ||
|
||
focus() { | ||
if (this._textArea) { | ||
this._textArea.focus(); | ||
} | ||
} | ||
|
||
/** @private */ | ||
__buttonPropsChanged(button, disabled, i18n) { | ||
if (button) { | ||
button.disabled = disabled; | ||
button.textContent = i18n.send; | ||
} | ||
} | ||
|
||
/** @private */ | ||
__textAreaPropsChanged(textArea, disabled, i18n, value) { | ||
if (textArea) { | ||
textArea.disabled = disabled; | ||
textArea.value = value; | ||
|
||
const message = i18n.message; | ||
textArea.placeholder = message; | ||
|
||
if (message) { | ||
textArea.inputElement.setAttribute('aria-label', message); | ||
} else { | ||
textArea.inputElement.removeAttribute('aria-label'); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Submits the current value as an custom event named 'submit'. | ||
* It also clears the text input and refocuses it for sending another message. | ||
* In UI, can be triggered by pressing the submit button or pressing enter key when field is focused. | ||
* It does not submit anything if text is empty. | ||
*/ | ||
__submit() { | ||
if (this.value !== '') { | ||
this.dispatchEvent(new CustomEvent('submit', { detail: { value: this.value } })); | ||
this.value = ''; | ||
} | ||
this._textArea.focus(); | ||
} | ||
|
||
/** | ||
* Fired when a new message is submitted with `<vaadin-message-input>`, either | ||
* by clicking the "send" button, or pressing the Enter key. | ||
* @event submit | ||
*/ | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.