@momentum-design/components 0.133.40 → 0.134.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.js +459 -433
- package/dist/browser/index.js.map +4 -4
- package/dist/components/animation/animation.component.d.ts +20 -1
- package/dist/components/animation/animation.component.js +40 -8
- package/dist/components/input/index.d.ts +1 -0
- package/dist/components/input/index.js +1 -0
- package/dist/components/input/input.component.d.ts +7 -1
- package/dist/components/input/input.component.js +36 -3
- package/dist/components/input/input.styles.js +20 -0
- package/dist/components/input/input.types.d.ts +3 -1
- package/dist/components/textarea/textarea.component.d.ts +1 -35
- package/dist/components/textarea/textarea.component.js +6 -87
- package/dist/components/textarea/textarea.constants.d.ts +0 -1
- package/dist/components/textarea/textarea.constants.js +0 -2
- package/dist/custom-elements.json +757 -49
- package/dist/react/input/index.d.ts +6 -0
- package/dist/react/input/index.js +5 -0
- package/dist/react/password/index.d.ts +8 -6
- package/dist/react/password/index.js +1 -0
- package/dist/react/searchfield/index.d.ts +8 -6
- package/dist/react/searchfield/index.js +1 -0
- package/dist/react/searchpopover/index.d.ts +2 -0
- package/dist/react/searchpopover/index.js +1 -0
- package/dist/react/textarea/index.d.ts +2 -1
- package/dist/react/textarea/index.js +1 -1
- package/dist/utils/mixins/CharacterLimitMixin.d.ts +37 -0
- package/dist/utils/mixins/CharacterLimitMixin.js +124 -0
- package/package.json +1 -1
|
@@ -20,6 +20,12 @@ declare class Animation extends Component {
|
|
|
20
20
|
* @default undefined
|
|
21
21
|
*/
|
|
22
22
|
name?: AnimationNames;
|
|
23
|
+
/**
|
|
24
|
+
* URL pointing to a Lottie JSON animation file.
|
|
25
|
+
* When provided, it takes precedence over the `name` property.
|
|
26
|
+
* @default undefined
|
|
27
|
+
*/
|
|
28
|
+
src?: string;
|
|
23
29
|
/**
|
|
24
30
|
* How many times to loop the animation
|
|
25
31
|
* - "true" - infinite
|
|
@@ -48,6 +54,11 @@ declare class Animation extends Component {
|
|
|
48
54
|
* @internal
|
|
49
55
|
*/
|
|
50
56
|
private lottieInstance?;
|
|
57
|
+
/**
|
|
58
|
+
* Cached animation data from the last successful fetch/import
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
private cachedAnimationData?;
|
|
51
62
|
/**
|
|
52
63
|
* Container for the animation
|
|
53
64
|
* @internal
|
|
@@ -60,9 +71,13 @@ declare class Animation extends Component {
|
|
|
60
71
|
get animation(): AnimationItem | undefined;
|
|
61
72
|
private getLoopValue;
|
|
62
73
|
/**
|
|
63
|
-
* Create new
|
|
74
|
+
* Create new lottie instance for the loaded data
|
|
64
75
|
*/
|
|
65
76
|
private onLoadSuccessHandler;
|
|
77
|
+
/**
|
|
78
|
+
* Create or re-create the lottie instance with the given animation data
|
|
79
|
+
*/
|
|
80
|
+
private createLottieInstance;
|
|
66
81
|
/**
|
|
67
82
|
* Error handler for animation loading
|
|
68
83
|
*/
|
|
@@ -71,6 +86,10 @@ declare class Animation extends Component {
|
|
|
71
86
|
* Import animation data dynamically
|
|
72
87
|
*/
|
|
73
88
|
private getAnimationData;
|
|
89
|
+
/**
|
|
90
|
+
* Fetch animation data from a URL
|
|
91
|
+
*/
|
|
92
|
+
private fetchAnimationFromUrl;
|
|
74
93
|
updated(changedProperties: PropertyValues): void;
|
|
75
94
|
disconnectedCallback(): void;
|
|
76
95
|
/**
|
|
@@ -91,9 +91,18 @@ class Animation extends Component {
|
|
|
91
91
|
return true;
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
|
-
* Create new
|
|
94
|
+
* Create new lottie instance for the loaded data
|
|
95
95
|
*/
|
|
96
96
|
onLoadSuccessHandler(animationData) {
|
|
97
|
+
this.cachedAnimationData = animationData;
|
|
98
|
+
this.createLottieInstance(animationData);
|
|
99
|
+
// Dispatch load event when animation ready to play
|
|
100
|
+
this.dispatchEvent(new CustomEvent('load', { bubbles: true, cancelable: true, detail: { name: this.name } }));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create or re-create the lottie instance with the given animation data
|
|
104
|
+
*/
|
|
105
|
+
createLottieInstance(animationData) {
|
|
97
106
|
if (this.lottieInstance) {
|
|
98
107
|
this.lottieInstance.removeEventListener('complete', this.onCompleteHandler);
|
|
99
108
|
this.lottieInstance.destroy();
|
|
@@ -109,8 +118,6 @@ class Animation extends Component {
|
|
|
109
118
|
});
|
|
110
119
|
this.lottieInstance.addEventListener('complete', this.onCompleteHandler);
|
|
111
120
|
}
|
|
112
|
-
// Dispatch load event when animation ready to play
|
|
113
|
-
this.dispatchEvent(new CustomEvent('load', { bubbles: true, cancelable: true, detail: { name: this.name } }));
|
|
114
121
|
}
|
|
115
122
|
/**
|
|
116
123
|
* Error handler for animation loading
|
|
@@ -127,7 +134,10 @@ class Animation extends Component {
|
|
|
127
134
|
* Import animation data dynamically
|
|
128
135
|
*/
|
|
129
136
|
getAnimationData() {
|
|
130
|
-
if (this.
|
|
137
|
+
if (this.src) {
|
|
138
|
+
this.fetchAnimationFromUrl(this.src);
|
|
139
|
+
}
|
|
140
|
+
else if (this.name && animationManifest[this.name]) {
|
|
131
141
|
// Make sure the path is point to a folder (and its sub-folders) that contains animation data only
|
|
132
142
|
// otherwise bundlers (eg. webpack) will try to process everything in this folder including the types.d.ts
|
|
133
143
|
const path = animationManifest[this.name].replace(/^\.\/lottie/, '');
|
|
@@ -139,14 +149,32 @@ class Animation extends Component {
|
|
|
139
149
|
this.onLoadFailHandler(new Error(`Invalid animation name: ${this.name}`));
|
|
140
150
|
}
|
|
141
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Fetch animation data from a URL
|
|
154
|
+
*/
|
|
155
|
+
fetchAnimationFromUrl(url) {
|
|
156
|
+
fetch(url)
|
|
157
|
+
.then(response => {
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
throw new Error(`Failed to fetch animation from URL: ${url} (${response.status})`);
|
|
160
|
+
}
|
|
161
|
+
return response.json();
|
|
162
|
+
})
|
|
163
|
+
.then((animationData) => this.onLoadSuccessHandler(animationData))
|
|
164
|
+
.catch((error) => this.onLoadFailHandler(error));
|
|
165
|
+
}
|
|
142
166
|
updated(changedProperties) {
|
|
143
167
|
super.updated(changedProperties);
|
|
144
|
-
// fetch animation data when
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (changedProperties.has('name') || changedProperties.has('loop') || changedProperties.has('autoplay')) {
|
|
168
|
+
// fetch animation data when the animation source changes
|
|
169
|
+
if (changedProperties.has('name') || changedProperties.has('src')) {
|
|
170
|
+
this.cachedAnimationData = undefined;
|
|
148
171
|
this.getAnimationData();
|
|
149
172
|
}
|
|
173
|
+
else if ((changedProperties.has('loop') || changedProperties.has('autoplay')) && this.cachedAnimationData) {
|
|
174
|
+
// re-create the lottie instance from cache for parameter changes,
|
|
175
|
+
// because lottie does not have an API for changing them on the fly
|
|
176
|
+
this.createLottieInstance(this.cachedAnimationData);
|
|
177
|
+
}
|
|
150
178
|
if (changedProperties.has('ariaLabel') || changedProperties.has('ariaLabelledby')) {
|
|
151
179
|
this.role = this.ariaLabel || this.ariaLabelledby ? ROLE.IMG : null;
|
|
152
180
|
}
|
|
@@ -169,6 +197,10 @@ __decorate([
|
|
|
169
197
|
property({ type: String, reflect: true }),
|
|
170
198
|
__metadata("design:type", String)
|
|
171
199
|
], Animation.prototype, "name", void 0);
|
|
200
|
+
__decorate([
|
|
201
|
+
property({ type: String, reflect: true }),
|
|
202
|
+
__metadata("design:type", String)
|
|
203
|
+
], Animation.prototype, "src", void 0);
|
|
172
204
|
__decorate([
|
|
173
205
|
property({ type: String, reflect: true }),
|
|
174
206
|
__metadata("design:type", String)
|
|
@@ -3,7 +3,7 @@ import FormfieldWrapper from '../formfieldwrapper';
|
|
|
3
3
|
import type { IconNames } from '../icon/icon.types';
|
|
4
4
|
import { AssociatedFormControl } from '../../utils/mixins/FormInternalsMixin';
|
|
5
5
|
import type { AutoCapitalizeType, AutoCompleteType, InputType } from './input.types';
|
|
6
|
-
declare const Input_base: import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyToActionMixin").KeyToActionInterface> & import("../../utils/mixins/index.types").Constructor<import("../../models").Component & import("../../utils/mixins/AutoFocusOnMountMixin").AutoFocusOnMountMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/FormInternalsMixin").FormInternalsMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/DataAriaLabelMixin").DataAriaLabelMixinInterface> & typeof FormfieldWrapper;
|
|
6
|
+
declare const Input_base: import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/CharacterLimitMixin").CharacterLimitMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyToActionMixin").KeyToActionInterface> & import("../../utils/mixins/index.types").Constructor<import("../../models").Component & import("../../utils/mixins/AutoFocusOnMountMixin").AutoFocusOnMountMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/FormInternalsMixin").FormInternalsMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/DataAriaLabelMixin").DataAriaLabelMixinInterface> & typeof FormfieldWrapper;
|
|
7
7
|
/**
|
|
8
8
|
* mdc-input is a component that allows users to input text.
|
|
9
9
|
* It contains:
|
|
@@ -23,6 +23,8 @@ declare const Input_base: import("../../utils/mixins/index.types").Constructor<i
|
|
|
23
23
|
* @event focus - (React: onFocus) This event is dispatched when the input receives focus.
|
|
24
24
|
* @event blur - (React: onBlur) This event is dispatched when the input loses focus.
|
|
25
25
|
* @event clear - (React: onClear) This event is dispatched when the input text is cleared.
|
|
26
|
+
* @event limitexceeded - (React: onLimitExceeded) This event is dispatched once when the character limit
|
|
27
|
+
* exceeds or restored.
|
|
26
28
|
*
|
|
27
29
|
* @dependency mdc-icon
|
|
28
30
|
* @dependency mdc-text
|
|
@@ -69,6 +71,8 @@ declare const Input_base: import("../../utils/mixins/index.types").Constructor<i
|
|
|
69
71
|
* @csspart input-section - The container for the input field, leading icon, and prefix text elements.
|
|
70
72
|
* @csspart input-text - The input field element.
|
|
71
73
|
* @csspart trailing-button - The trailing button element that is displayed to clear the input field when the `trailingButton` property is set to true.
|
|
74
|
+
* @csspart input-footer - The footer element that contains the helper text and character counter.
|
|
75
|
+
* @csspart character-counter - The character counter element.
|
|
72
76
|
*/
|
|
73
77
|
declare class Input extends Input_base implements AssociatedFormControl {
|
|
74
78
|
/**
|
|
@@ -155,6 +159,7 @@ declare class Input extends Input_base implements AssociatedFormControl {
|
|
|
155
159
|
*/
|
|
156
160
|
attributeChangedCallback(name: string, old: string | null, value: string | null): void;
|
|
157
161
|
private setInputValidity;
|
|
162
|
+
protected updated(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
158
163
|
/**
|
|
159
164
|
* Updates the value of the input field.
|
|
160
165
|
* Sets the form value.
|
|
@@ -213,6 +218,7 @@ declare class Input extends Input_base implements AssociatedFormControl {
|
|
|
213
218
|
*/
|
|
214
219
|
protected renderTrailingButton(show?: boolean): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
215
220
|
protected renderInputElement(type: InputType, hidePlaceholder?: boolean): import("lit-html").TemplateResult<1>;
|
|
221
|
+
protected renderInputFooter(): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
216
222
|
render(): import("lit-html").TemplateResult<1>;
|
|
217
223
|
static styles: Array<CSSResult>;
|
|
218
224
|
static shadowRootOptions: {
|
|
@@ -12,11 +12,12 @@ import { property } from 'lit/decorators.js';
|
|
|
12
12
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
13
13
|
import { live } from 'lit/directives/live.js';
|
|
14
14
|
import FormfieldWrapper from '../formfieldwrapper';
|
|
15
|
-
import { DEFAULTS as FORMFIELD_DEFAULTS } from '../formfieldwrapper/formfieldwrapper.constants';
|
|
15
|
+
import { DEFAULTS as FORMFIELD_DEFAULTS, VALIDATION } from '../formfieldwrapper/formfieldwrapper.constants';
|
|
16
16
|
import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
|
|
17
17
|
import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
|
|
18
18
|
import { AutoFocusOnMountMixin } from '../../utils/mixins/AutoFocusOnMountMixin';
|
|
19
19
|
import { KeyToActionMixin, ACTIONS, NAV_MODES } from '../../utils/mixins/KeyToActionMixin';
|
|
20
|
+
import { CharacterLimitMixin } from '../../utils/mixins/CharacterLimitMixin';
|
|
20
21
|
import { AUTO_CAPITALIZE, AUTO_COMPLETE, DEFAULTS, PREFIX_TEXT_OPTIONS } from './input.constants';
|
|
21
22
|
import styles from './input.styles';
|
|
22
23
|
/**
|
|
@@ -38,6 +39,8 @@ import styles from './input.styles';
|
|
|
38
39
|
* @event focus - (React: onFocus) This event is dispatched when the input receives focus.
|
|
39
40
|
* @event blur - (React: onBlur) This event is dispatched when the input loses focus.
|
|
40
41
|
* @event clear - (React: onClear) This event is dispatched when the input text is cleared.
|
|
42
|
+
* @event limitexceeded - (React: onLimitExceeded) This event is dispatched once when the character limit
|
|
43
|
+
* exceeds or restored.
|
|
41
44
|
*
|
|
42
45
|
* @dependency mdc-icon
|
|
43
46
|
* @dependency mdc-text
|
|
@@ -84,8 +87,10 @@ import styles from './input.styles';
|
|
|
84
87
|
* @csspart input-section - The container for the input field, leading icon, and prefix text elements.
|
|
85
88
|
* @csspart input-text - The input field element.
|
|
86
89
|
* @csspart trailing-button - The trailing button element that is displayed to clear the input field when the `trailingButton` property is set to true.
|
|
90
|
+
* @csspart input-footer - The footer element that contains the helper text and character counter.
|
|
91
|
+
* @csspart character-counter - The character counter element.
|
|
87
92
|
*/
|
|
88
|
-
class Input extends KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)))) {
|
|
93
|
+
class Input extends CharacterLimitMixin(KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper))))) {
|
|
89
94
|
constructor() {
|
|
90
95
|
super(...arguments);
|
|
91
96
|
/**
|
|
@@ -181,8 +186,23 @@ class Input extends KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(Da
|
|
|
181
186
|
if (!this.inputElement.validity.valid && this.validationMessage) {
|
|
182
187
|
this.inputElement.setCustomValidity(this.validationMessage);
|
|
183
188
|
}
|
|
189
|
+
else if (this.maxCharacterLimit &&
|
|
190
|
+
this.value.length > this.maxCharacterLimit &&
|
|
191
|
+
this.helpTextType === VALIDATION.ERROR &&
|
|
192
|
+
this.helpText) {
|
|
193
|
+
this.inputElement.setCustomValidity(this.helpText);
|
|
194
|
+
}
|
|
184
195
|
this.setValidity();
|
|
185
196
|
}
|
|
197
|
+
updated(changedProperties) {
|
|
198
|
+
super.updated(changedProperties);
|
|
199
|
+
if (changedProperties.has('value')) {
|
|
200
|
+
this.handleCharacterOverflowStateChange();
|
|
201
|
+
}
|
|
202
|
+
if (changedProperties.has('helpText')) {
|
|
203
|
+
this.announceCharacterLimitWarning();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
186
206
|
/**
|
|
187
207
|
* Updates the value of the input field.
|
|
188
208
|
* Sets the form value.
|
|
@@ -191,6 +211,7 @@ class Input extends KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(Da
|
|
|
191
211
|
updateValue() {
|
|
192
212
|
this.value = this.inputElement.value;
|
|
193
213
|
this.internals.setFormValue(this.inputElement.value);
|
|
214
|
+
this.announceCharacterLimitWarning();
|
|
194
215
|
}
|
|
195
216
|
/**
|
|
196
217
|
* Handles the input event of the input field.
|
|
@@ -342,6 +363,12 @@ class Input extends KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(Da
|
|
|
342
363
|
@keydown=${this.handleKeyDown}
|
|
343
364
|
/>`;
|
|
344
365
|
}
|
|
366
|
+
renderInputFooter() {
|
|
367
|
+
if (!this.helpText && !this.maxCharacterLimit) {
|
|
368
|
+
return nothing;
|
|
369
|
+
}
|
|
370
|
+
return html ` <div part="input-footer">${this.renderHelperText()} ${this.renderCharacterCounter()}</div> `;
|
|
371
|
+
}
|
|
345
372
|
render() {
|
|
346
373
|
return html `
|
|
347
374
|
${this.renderLabel()}
|
|
@@ -353,7 +380,13 @@ class Input extends KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(Da
|
|
|
353
380
|
</div>
|
|
354
381
|
<slot name="trailing-button">${this.renderTrailingButton()}</slot>
|
|
355
382
|
</div>
|
|
356
|
-
|
|
383
|
+
<mdc-screenreaderannouncer
|
|
384
|
+
identity="${this.inputId}"
|
|
385
|
+
announcement="${ifDefined(this.characterLimitAriaLiveAnnouncer)}"
|
|
386
|
+
data-aria-live="polite"
|
|
387
|
+
delay="500"
|
|
388
|
+
></mdc-screenreaderannouncer>
|
|
389
|
+
${this.renderInputFooter()}
|
|
357
390
|
`;
|
|
358
391
|
}
|
|
359
392
|
}
|
|
@@ -122,6 +122,26 @@ const styles = [
|
|
|
122
122
|
opacity: 0;
|
|
123
123
|
pointer-events: none;
|
|
124
124
|
}
|
|
125
|
+
|
|
126
|
+
:host::part(input-footer) {
|
|
127
|
+
display: flex;
|
|
128
|
+
justify-content: space-between;
|
|
129
|
+
align-items: center;
|
|
130
|
+
width: 100%;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
:host::part(character-counter) {
|
|
134
|
+
margin-left: auto;
|
|
135
|
+
color: var(--mdc-input-support-text-color);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
:host([help-text-type='error'])::part(character-counter) {
|
|
139
|
+
color: var(--mdc-help-text-color);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
:host([disabled])::part(character-counter) {
|
|
143
|
+
color: var(--mds-color-theme-text-primary-disabled);
|
|
144
|
+
}
|
|
125
145
|
`,
|
|
126
146
|
...hostFocusRingStyles(true),
|
|
127
147
|
];
|
|
@@ -9,11 +9,13 @@ type InputChangeEvent = TypedCustomEvent<Input>;
|
|
|
9
9
|
type InputFocusEvent = OverrideEventTarget<FocusEvent, Input>;
|
|
10
10
|
type InputBlurEvent = OverrideEventTarget<FocusEvent, Input>;
|
|
11
11
|
type InputClearEvent = TypedCustomEvent<Input>;
|
|
12
|
+
type InputLimitExceededEvent = TypedCustomEvent<Input>;
|
|
12
13
|
interface Events {
|
|
13
14
|
onInputEvent: InputInputEvent;
|
|
14
15
|
onChangeEvent: InputChangeEvent;
|
|
15
16
|
onFocusEvent: InputFocusEvent;
|
|
16
17
|
onBlurEvent: InputBlurEvent;
|
|
17
18
|
onClearEvent: InputClearEvent;
|
|
19
|
+
onLimitExceededEvent: InputLimitExceededEvent;
|
|
18
20
|
}
|
|
19
|
-
export type { AutoCapitalizeType, AutoCompleteType, InputType, InputInputEvent, InputChangeEvent, InputFocusEvent, InputBlurEvent, InputClearEvent, Events, };
|
|
21
|
+
export type { AutoCapitalizeType, AutoCompleteType, InputType, InputInputEvent, InputChangeEvent, InputFocusEvent, InputBlurEvent, InputClearEvent, InputLimitExceededEvent, Events, };
|
|
@@ -2,7 +2,7 @@ import { CSSResult, nothing, PropertyValueMap } from 'lit';
|
|
|
2
2
|
import FormfieldWrapper from '../formfieldwrapper';
|
|
3
3
|
import type { AutoCapitalizeType } from '../input/input.types';
|
|
4
4
|
import type { WrapType, AutoCompleteType } from './textarea.types';
|
|
5
|
-
declare const Textarea_base: import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyDownHandledMixin").KeyDownHandledMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyToActionMixin").KeyToActionInterface> & import("../../utils/mixins/index.types").Constructor<import("../../models").Component & import("../../utils/mixins/AutoFocusOnMountMixin").AutoFocusOnMountMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/FormInternalsMixin").FormInternalsMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/DataAriaLabelMixin").DataAriaLabelMixinInterface> & typeof FormfieldWrapper;
|
|
5
|
+
declare const Textarea_base: import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/CharacterLimitMixin").CharacterLimitMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyDownHandledMixin").KeyDownHandledMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyToActionMixin").KeyToActionInterface> & import("../../utils/mixins/index.types").Constructor<import("../../models").Component & import("../../utils/mixins/AutoFocusOnMountMixin").AutoFocusOnMountMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/FormInternalsMixin").FormInternalsMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/DataAriaLabelMixin").DataAriaLabelMixinInterface> & typeof FormfieldWrapper;
|
|
6
6
|
/**
|
|
7
7
|
* mdc-textarea component, which is used to get the multi-line text input from the user.
|
|
8
8
|
* It contains:
|
|
@@ -130,18 +130,6 @@ declare class Textarea extends Textarea_base {
|
|
|
130
130
|
* The minimum number of characters that the textarea field can accept.
|
|
131
131
|
*/
|
|
132
132
|
minlength?: number;
|
|
133
|
-
/**
|
|
134
|
-
* The maximum character limit for the textarea field for character counter.
|
|
135
|
-
*/
|
|
136
|
-
maxCharacterLimit?: number;
|
|
137
|
-
/**
|
|
138
|
-
* Template string for the announcement that will be read by screen readers when the max character limit is set.
|
|
139
|
-
* Consumers must use the placeholders `%{number-of-characters}` and `%{max-character-limit}` in the string,
|
|
140
|
-
* which will be dynamically replaced with the actual values at runtime.
|
|
141
|
-
* For example: `%{number-of-characters} out of %{max-character-limit} characters are typed.`
|
|
142
|
-
* Example output: "93 out of 140 characters are typed."
|
|
143
|
-
*/
|
|
144
|
-
characterLimitAnnouncement?: string;
|
|
145
133
|
/**
|
|
146
134
|
* Controls whether the textarea is resizable via the resize button.
|
|
147
135
|
* When set to false, the resize button will be hidden.
|
|
@@ -160,10 +148,6 @@ declare class Textarea extends Textarea_base {
|
|
|
160
148
|
*/
|
|
161
149
|
inputElement: HTMLTextAreaElement;
|
|
162
150
|
/** @internal */
|
|
163
|
-
private ariaLiveAnnouncer?;
|
|
164
|
-
/** @internal */
|
|
165
|
-
private characterLimitExceedingFired;
|
|
166
|
-
/** @internal */
|
|
167
151
|
private resizeStartY;
|
|
168
152
|
/** @internal */
|
|
169
153
|
private resizeStartRows;
|
|
@@ -182,29 +166,12 @@ declare class Textarea extends Textarea_base {
|
|
|
182
166
|
*/
|
|
183
167
|
handleValueChange(): void;
|
|
184
168
|
protected updated(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
185
|
-
/**
|
|
186
|
-
* Dispatches the character overflow state change event.
|
|
187
|
-
* @returns void
|
|
188
|
-
*/
|
|
189
|
-
private dispatchCharacterOverflowStateChangeEvent;
|
|
190
|
-
/**
|
|
191
|
-
* Handles the character overflow state change.
|
|
192
|
-
* Dispatches the character overflow state change event if the character limit is exceeded or restored.
|
|
193
|
-
* @returns void
|
|
194
|
-
*/
|
|
195
|
-
private handleCharacterOverflowStateChange;
|
|
196
169
|
/**
|
|
197
170
|
* Updates the value of the textarea field.
|
|
198
171
|
* Sets the form value.
|
|
199
172
|
* @returns void
|
|
200
173
|
*/
|
|
201
174
|
private updateValue;
|
|
202
|
-
/**
|
|
203
|
-
* Announces the character limit warning based on the current value length.
|
|
204
|
-
* If the value length exceeds the max character limit, the help text is announced (if help text is present).
|
|
205
|
-
* If the value length does not exceed the max character limit, then the character limit announcement is announced.
|
|
206
|
-
*/
|
|
207
|
-
private announceMaxLengthWarning;
|
|
208
175
|
/**
|
|
209
176
|
* Handles the change event of the textarea field.
|
|
210
177
|
* Updates the value and sets the validity of the textarea field.
|
|
@@ -216,7 +183,6 @@ declare class Textarea extends Textarea_base {
|
|
|
216
183
|
* @param event - Event which contains information about the value change.
|
|
217
184
|
*/
|
|
218
185
|
private onChange;
|
|
219
|
-
protected renderCharacterCounter(): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
220
186
|
protected renderTextareaFooter(): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
221
187
|
/**
|
|
222
188
|
* Handles the resize button keydown event for keyboard-based resizing.
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { html, nothing } from 'lit';
|
|
11
11
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
12
|
-
import { property, query
|
|
12
|
+
import { property, query } from 'lit/decorators.js';
|
|
13
13
|
import FormfieldWrapper from '../formfieldwrapper';
|
|
14
14
|
import { DEFAULTS as FORMFIELD_DEFAULTS, VALIDATION } from '../formfieldwrapper/formfieldwrapper.constants';
|
|
15
15
|
import { AUTO_CAPITALIZE } from '../input/input.constants';
|
|
@@ -18,6 +18,7 @@ import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
|
|
|
18
18
|
import { AutoFocusOnMountMixin } from '../../utils/mixins/AutoFocusOnMountMixin';
|
|
19
19
|
import { ACTIONS, KeyToActionMixin } from '../../utils/mixins/KeyToActionMixin';
|
|
20
20
|
import { KeyDownHandledMixin } from '../../utils/mixins/KeyDownHandledMixin';
|
|
21
|
+
import { CharacterLimitMixin } from '../../utils/mixins/CharacterLimitMixin';
|
|
21
22
|
import { AUTO_COMPLETE, WRAP, DEFAULTS } from './textarea.constants';
|
|
22
23
|
import styles from './textarea.styles';
|
|
23
24
|
/**
|
|
@@ -105,7 +106,7 @@ import styles from './textarea.styles';
|
|
|
105
106
|
* @cssproperty --mdc-textarea-text-line-height - Line height for the textarea field
|
|
106
107
|
* @cssproperty --mdc-textarea-container-background-color - Background color for the textarea container
|
|
107
108
|
*/
|
|
108
|
-
class Textarea extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper))))) {
|
|
109
|
+
class Textarea extends CharacterLimitMixin(KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)))))) {
|
|
109
110
|
constructor() {
|
|
110
111
|
super(...arguments);
|
|
111
112
|
/**
|
|
@@ -135,8 +136,6 @@ class Textarea extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixi
|
|
|
135
136
|
*/
|
|
136
137
|
this.resizable = false;
|
|
137
138
|
/** @internal */
|
|
138
|
-
this.characterLimitExceedingFired = false;
|
|
139
|
-
/** @internal */
|
|
140
139
|
this.resizeStartY = 0;
|
|
141
140
|
/** @internal */
|
|
142
141
|
this.resizeStartRows = 0;
|
|
@@ -268,39 +267,7 @@ class Textarea extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixi
|
|
|
268
267
|
}
|
|
269
268
|
// When helpText gets changed, we need to re-announce the max length warning
|
|
270
269
|
if (changedProperties.has('helpText')) {
|
|
271
|
-
this.
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Dispatches the character overflow state change event.
|
|
276
|
-
* @returns void
|
|
277
|
-
*/
|
|
278
|
-
dispatchCharacterOverflowStateChangeEvent() {
|
|
279
|
-
this.dispatchEvent(new CustomEvent('limitexceeded', {
|
|
280
|
-
detail: {
|
|
281
|
-
currentCharacterCount: this.value.length,
|
|
282
|
-
maxCharacterLimit: this.maxCharacterLimit,
|
|
283
|
-
value: this.value,
|
|
284
|
-
},
|
|
285
|
-
bubbles: true,
|
|
286
|
-
composed: true,
|
|
287
|
-
}));
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Handles the character overflow state change.
|
|
291
|
-
* Dispatches the character overflow state change event if the character limit is exceeded or restored.
|
|
292
|
-
* @returns void
|
|
293
|
-
*/
|
|
294
|
-
handleCharacterOverflowStateChange() {
|
|
295
|
-
if (this.maxCharacterLimit) {
|
|
296
|
-
if (this.value.length > this.maxCharacterLimit && !this.characterLimitExceedingFired) {
|
|
297
|
-
this.dispatchCharacterOverflowStateChangeEvent();
|
|
298
|
-
this.characterLimitExceedingFired = true;
|
|
299
|
-
}
|
|
300
|
-
else if (this.value.length <= this.maxCharacterLimit && this.characterLimitExceedingFired) {
|
|
301
|
-
this.dispatchCharacterOverflowStateChangeEvent();
|
|
302
|
-
this.characterLimitExceedingFired = false;
|
|
303
|
-
}
|
|
270
|
+
this.announceCharacterLimitWarning();
|
|
304
271
|
}
|
|
305
272
|
}
|
|
306
273
|
/**
|
|
@@ -311,33 +278,7 @@ class Textarea extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixi
|
|
|
311
278
|
updateValue() {
|
|
312
279
|
this.value = this.textarea.value;
|
|
313
280
|
this.internals.setFormValue(this.textarea.value);
|
|
314
|
-
this.
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Announces the character limit warning based on the current value length.
|
|
318
|
-
* If the value length exceeds the max character limit, the help text is announced (if help text is present).
|
|
319
|
-
* If the value length does not exceed the max character limit, then the character limit announcement is announced.
|
|
320
|
-
*/
|
|
321
|
-
announceMaxLengthWarning() {
|
|
322
|
-
this.ariaLiveAnnouncer = '';
|
|
323
|
-
if (!this.maxCharacterLimit || this.value.length === 0) {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
if (this.helpText && this.value.length > this.maxCharacterLimit) {
|
|
327
|
-
// We need to assign the same value multiple times, when the input reaches the max limit,
|
|
328
|
-
// Lit does a `===` strict comparison and doesn't update the value
|
|
329
|
-
// Hence we need to manually wait for the update to complete and then assign the value.
|
|
330
|
-
this.updateComplete
|
|
331
|
-
.then(() => {
|
|
332
|
-
this.ariaLiveAnnouncer = this.helpText;
|
|
333
|
-
})
|
|
334
|
-
.catch(() => { });
|
|
335
|
-
}
|
|
336
|
-
else if (this.characterLimitAnnouncement && this.value.length <= this.maxCharacterLimit) {
|
|
337
|
-
this.ariaLiveAnnouncer = this.characterLimitAnnouncement
|
|
338
|
-
.replace('%{number-of-characters}', this.value.length.toString())
|
|
339
|
-
.replace('%{max-character-limit}', this.maxCharacterLimit.toString());
|
|
340
|
-
}
|
|
281
|
+
this.announceCharacterLimitWarning();
|
|
341
282
|
}
|
|
342
283
|
/**
|
|
343
284
|
* Handles the change event of the textarea field.
|
|
@@ -354,16 +295,6 @@ class Textarea extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixi
|
|
|
354
295
|
const EventConstructor = event.constructor;
|
|
355
296
|
this.dispatchEvent(new EventConstructor(event.type, event));
|
|
356
297
|
}
|
|
357
|
-
renderCharacterCounter() {
|
|
358
|
-
if (!this.maxCharacterLimit) {
|
|
359
|
-
return nothing;
|
|
360
|
-
}
|
|
361
|
-
return html `
|
|
362
|
-
<mdc-text part="character-counter" tagname="span" type=${DEFAULTS.CHARACTER_COUNTER_TYPE}>
|
|
363
|
-
${this.value.length}/${this.maxCharacterLimit}
|
|
364
|
-
</mdc-text>
|
|
365
|
-
`;
|
|
366
|
-
}
|
|
367
298
|
renderTextareaFooter() {
|
|
368
299
|
if (!this.helpText && !this.maxCharacterLimit) {
|
|
369
300
|
return nothing;
|
|
@@ -427,7 +358,7 @@ class Textarea extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixi
|
|
|
427
358
|
></textarea>
|
|
428
359
|
<mdc-screenreaderannouncer
|
|
429
360
|
identity="${this.inputId}"
|
|
430
|
-
announcement="${ifDefined(this.
|
|
361
|
+
announcement="${ifDefined(this.characterLimitAriaLiveAnnouncer)}"
|
|
431
362
|
data-aria-live="polite"
|
|
432
363
|
delay="500"
|
|
433
364
|
></mdc-screenreaderannouncer>
|
|
@@ -488,14 +419,6 @@ __decorate([
|
|
|
488
419
|
property({ type: Number }),
|
|
489
420
|
__metadata("design:type", Number)
|
|
490
421
|
], Textarea.prototype, "minlength", void 0);
|
|
491
|
-
__decorate([
|
|
492
|
-
property({ type: Number, attribute: 'max-character-limit' }),
|
|
493
|
-
__metadata("design:type", Number)
|
|
494
|
-
], Textarea.prototype, "maxCharacterLimit", void 0);
|
|
495
|
-
__decorate([
|
|
496
|
-
property({ type: String, attribute: 'character-limit-announcement' }),
|
|
497
|
-
__metadata("design:type", String)
|
|
498
|
-
], Textarea.prototype, "characterLimitAnnouncement", void 0);
|
|
499
422
|
__decorate([
|
|
500
423
|
property({ type: Boolean, reflect: true }),
|
|
501
424
|
__metadata("design:type", Boolean)
|
|
@@ -508,8 +431,4 @@ __decorate([
|
|
|
508
431
|
query('textarea'),
|
|
509
432
|
__metadata("design:type", HTMLTextAreaElement)
|
|
510
433
|
], Textarea.prototype, "inputElement", void 0);
|
|
511
|
-
__decorate([
|
|
512
|
-
state(),
|
|
513
|
-
__metadata("design:type", String)
|
|
514
|
-
], Textarea.prototype, "ariaLiveAnnouncer", void 0);
|
|
515
434
|
export default Textarea;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import utils from '../../utils/tag-name';
|
|
2
|
-
import { TYPE as FONT_TYPE } from '../text/text.constants';
|
|
3
2
|
const TAG_NAME = utils.constructTagName('textarea');
|
|
4
3
|
const WRAP = {
|
|
5
4
|
HARD: 'hard',
|
|
@@ -10,7 +9,6 @@ const AUTO_COMPLETE = {
|
|
|
10
9
|
ON: 'on',
|
|
11
10
|
};
|
|
12
11
|
const DEFAULTS = {
|
|
13
|
-
CHARACTER_COUNTER_TYPE: FONT_TYPE.BODY_MIDSIZE_REGULAR,
|
|
14
12
|
ROWS: 5,
|
|
15
13
|
COLS: 40,
|
|
16
14
|
WRAP: WRAP.SOFT,
|