@momentum-design/components 0.124.0 → 0.125.0
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 +200 -162
- package/dist/browser/index.js.map +3 -3
- package/dist/components/textarea/textarea.component.d.ts +56 -0
- package/dist/components/textarea/textarea.component.js +122 -2
- package/dist/components/textarea/textarea.styles.js +25 -0
- package/dist/custom-elements.json +477 -366
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +2 -2
- package/dist/react/textarea/index.d.ts +20 -0
- package/dist/react/textarea/index.js +20 -0
- package/package.json +1 -1
|
@@ -23,6 +23,21 @@ declare const Textarea_base: import("../../utils/mixins/index.types").Constructo
|
|
|
23
23
|
* help-text attribute with the error message using limitexceeded event.
|
|
24
24
|
* The same help-text value will be used for the validation message to be displayed.
|
|
25
25
|
*
|
|
26
|
+
* ### Accessibility
|
|
27
|
+
*
|
|
28
|
+
* #### Resize
|
|
29
|
+
*
|
|
30
|
+
* Accessible text area resizing can be turned on with the `resizable`.
|
|
31
|
+
* It is strongly recommended to set the `resize-button-aria-label` attribute as well to describe what it is and what are the shortcuts (up/down arrows) of the button.
|
|
32
|
+
*
|
|
33
|
+
* #### Best practices
|
|
34
|
+
*
|
|
35
|
+
* - Always provide a `label` for screen readers to identify the textarea's purpose
|
|
36
|
+
* - Use `help-text` to provide additional context or instructions
|
|
37
|
+
* - When using `max-character-limit`, consider providing `character-limit-announcement` for screen reader updates
|
|
38
|
+
* - Use appropriate `help-text-type` (error, warning, success) to convey validation state
|
|
39
|
+
* - Ensure `validation-message` is set for form validation errors
|
|
40
|
+
*
|
|
26
41
|
* @tagname mdc-textarea
|
|
27
42
|
*
|
|
28
43
|
* @event input - (React: onInput) This event is dispatched when the value of the textarea field changes (every press).
|
|
@@ -47,6 +62,11 @@ declare const Textarea_base: import("../../utils/mixins/index.types").Constructo
|
|
|
47
62
|
* @csspart required-indicator - The required indicator element that is displayed next to the label when the `required` property is set to true.
|
|
48
63
|
* @csspart info-icon-btn - The info icon button element that is displayed next to the label when the `toggletip-text` property is set.
|
|
49
64
|
* @csspart label-toggletip - The toggletip element that is displayed when the info icon button is clicked.
|
|
65
|
+
* @csspart textarea - The textarea element.
|
|
66
|
+
* @csspart textarea-container - The container element that wraps the textarea and resize button.
|
|
67
|
+
* @csspart textarea-footer - The footer element that contains the character counter.
|
|
68
|
+
* @csspart character-counter - The character counter element.
|
|
69
|
+
* @csspart resize-button - The resize button element (shown when `resizable` is true).
|
|
50
70
|
* @csspart help-text - The helper/validation text element.
|
|
51
71
|
* @csspart helper-icon - The helper/validation icon element that is displayed next to the helper/validation text.
|
|
52
72
|
* @csspart help-text-container - The container for the helper/validation icon and text elements.
|
|
@@ -122,6 +142,18 @@ declare class Textarea extends Textarea_base {
|
|
|
122
142
|
* Example output: "93 out of 140 characters are typed."
|
|
123
143
|
*/
|
|
124
144
|
characterLimitAnnouncement?: string;
|
|
145
|
+
/**
|
|
146
|
+
* Controls whether the textarea is resizable via the resize button.
|
|
147
|
+
* When set to false, the resize button will be hidden.
|
|
148
|
+
* @default false
|
|
149
|
+
*/
|
|
150
|
+
resizable: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Provides an accessible label for the resize button.
|
|
153
|
+
* This value is used to set the `aria-label` attribute for the button.
|
|
154
|
+
* @default ''
|
|
155
|
+
*/
|
|
156
|
+
resizeButtonAriaLabel?: string;
|
|
125
157
|
/**
|
|
126
158
|
* @internal
|
|
127
159
|
* The textarea element
|
|
@@ -131,6 +163,10 @@ declare class Textarea extends Textarea_base {
|
|
|
131
163
|
private ariaLiveAnnouncer?;
|
|
132
164
|
/** @internal */
|
|
133
165
|
private characterLimitExceedingFired;
|
|
166
|
+
/** @internal */
|
|
167
|
+
private resizeStartY;
|
|
168
|
+
/** @internal */
|
|
169
|
+
private resizeStartRows;
|
|
134
170
|
protected get textarea(): HTMLTextAreaElement;
|
|
135
171
|
connectedCallback(): void;
|
|
136
172
|
protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
@@ -191,6 +227,26 @@ declare class Textarea extends Textarea_base {
|
|
|
191
227
|
private onChange;
|
|
192
228
|
protected renderCharacterCounter(): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
193
229
|
protected renderTextareaFooter(): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
230
|
+
/**
|
|
231
|
+
* Handles the resize button keydown event for keyboard-based resizing.
|
|
232
|
+
* @param event - The keyboard event.
|
|
233
|
+
*/
|
|
234
|
+
private handleResizeKeyDown;
|
|
235
|
+
/**
|
|
236
|
+
* Handles the start of pointer-based resizing.
|
|
237
|
+
* @param event - The pointer event.
|
|
238
|
+
*/
|
|
239
|
+
private handlePointerDown;
|
|
240
|
+
/**
|
|
241
|
+
* Handles pointer movement during resizing.
|
|
242
|
+
* @param event - The pointer event.
|
|
243
|
+
*/
|
|
244
|
+
private handlePointerMove;
|
|
245
|
+
/**
|
|
246
|
+
* Handles the end of pointer-based resizing.
|
|
247
|
+
* @param event - The pointer event.
|
|
248
|
+
*/
|
|
249
|
+
private handlePointerUp;
|
|
194
250
|
render(): import("lit-html").TemplateResult<1>;
|
|
195
251
|
static styles: Array<CSSResult>;
|
|
196
252
|
}
|
|
@@ -16,6 +16,7 @@ import { AUTO_CAPITALIZE } from '../input/input.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
|
+
import { KEYS } from '../../utils/keys';
|
|
19
20
|
import { AUTO_COMPLETE, WRAP, DEFAULTS } from './textarea.constants';
|
|
20
21
|
import styles from './textarea.styles';
|
|
21
22
|
/**
|
|
@@ -38,6 +39,21 @@ import styles from './textarea.styles';
|
|
|
38
39
|
* help-text attribute with the error message using limitexceeded event.
|
|
39
40
|
* The same help-text value will be used for the validation message to be displayed.
|
|
40
41
|
*
|
|
42
|
+
* ### Accessibility
|
|
43
|
+
*
|
|
44
|
+
* #### Resize
|
|
45
|
+
*
|
|
46
|
+
* Accessible text area resizing can be turned on with the `resizable`.
|
|
47
|
+
* It is strongly recommended to set the `resize-button-aria-label` attribute as well to describe what it is and what are the shortcuts (up/down arrows) of the button.
|
|
48
|
+
*
|
|
49
|
+
* #### Best practices
|
|
50
|
+
*
|
|
51
|
+
* - Always provide a `label` for screen readers to identify the textarea's purpose
|
|
52
|
+
* - Use `help-text` to provide additional context or instructions
|
|
53
|
+
* - When using `max-character-limit`, consider providing `character-limit-announcement` for screen reader updates
|
|
54
|
+
* - Use appropriate `help-text-type` (error, warning, success) to convey validation state
|
|
55
|
+
* - Ensure `validation-message` is set for form validation errors
|
|
56
|
+
*
|
|
41
57
|
* @tagname mdc-textarea
|
|
42
58
|
*
|
|
43
59
|
* @event input - (React: onInput) This event is dispatched when the value of the textarea field changes (every press).
|
|
@@ -62,6 +78,11 @@ import styles from './textarea.styles';
|
|
|
62
78
|
* @csspart required-indicator - The required indicator element that is displayed next to the label when the `required` property is set to true.
|
|
63
79
|
* @csspart info-icon-btn - The info icon button element that is displayed next to the label when the `toggletip-text` property is set.
|
|
64
80
|
* @csspart label-toggletip - The toggletip element that is displayed when the info icon button is clicked.
|
|
81
|
+
* @csspart textarea - The textarea element.
|
|
82
|
+
* @csspart textarea-container - The container element that wraps the textarea and resize button.
|
|
83
|
+
* @csspart textarea-footer - The footer element that contains the character counter.
|
|
84
|
+
* @csspart character-counter - The character counter element.
|
|
85
|
+
* @csspart resize-button - The resize button element (shown when `resizable` is true).
|
|
65
86
|
* @csspart help-text - The helper/validation text element.
|
|
66
87
|
* @csspart helper-icon - The helper/validation icon element that is displayed next to the helper/validation text.
|
|
67
88
|
* @csspart help-text-container - The container for the helper/validation icon and text elements.
|
|
@@ -106,8 +127,64 @@ class Textarea extends AutoFocusOnMountMixin(FormInternalsMixin(DataAriaLabelMix
|
|
|
106
127
|
* @default 'off'
|
|
107
128
|
*/
|
|
108
129
|
this.autocomplete = AUTO_COMPLETE.OFF;
|
|
130
|
+
/**
|
|
131
|
+
* Controls whether the textarea is resizable via the resize button.
|
|
132
|
+
* When set to false, the resize button will be hidden.
|
|
133
|
+
* @default false
|
|
134
|
+
*/
|
|
135
|
+
this.resizable = false;
|
|
109
136
|
/** @internal */
|
|
110
137
|
this.characterLimitExceedingFired = false;
|
|
138
|
+
/** @internal */
|
|
139
|
+
this.resizeStartY = 0;
|
|
140
|
+
/** @internal */
|
|
141
|
+
this.resizeStartRows = 0;
|
|
142
|
+
/**
|
|
143
|
+
* Handles the start of pointer-based resizing.
|
|
144
|
+
* @param event - The pointer event.
|
|
145
|
+
*/
|
|
146
|
+
this.handlePointerDown = (event) => {
|
|
147
|
+
if (this.readonly) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const resizeButton = event.currentTarget;
|
|
151
|
+
if (!resizeButton)
|
|
152
|
+
return;
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
this.resizeStartY = event.clientY;
|
|
155
|
+
this.resizeStartRows = this.rows || DEFAULTS.ROWS;
|
|
156
|
+
resizeButton.setPointerCapture(event.pointerId);
|
|
157
|
+
resizeButton.addEventListener('pointermove', this.handlePointerMove);
|
|
158
|
+
resizeButton.addEventListener('pointerup', this.handlePointerUp);
|
|
159
|
+
resizeButton.addEventListener('lostpointercapture', this.handlePointerUp);
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Handles pointer movement during resizing.
|
|
163
|
+
* @param event - The pointer event.
|
|
164
|
+
*/
|
|
165
|
+
this.handlePointerMove = (event) => {
|
|
166
|
+
if (!this.textarea)
|
|
167
|
+
return;
|
|
168
|
+
const deltaY = event.clientY - this.resizeStartY;
|
|
169
|
+
const lineHeight = parseFloat(window.getComputedStyle(this.textarea).lineHeight);
|
|
170
|
+
const rowsChange = Math.round(deltaY / lineHeight);
|
|
171
|
+
this.rows = Math.max(1, this.resizeStartRows + rowsChange);
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Handles the end of pointer-based resizing.
|
|
175
|
+
* @param event - The pointer event.
|
|
176
|
+
*/
|
|
177
|
+
this.handlePointerUp = (event) => {
|
|
178
|
+
const resizeButton = event.currentTarget;
|
|
179
|
+
if (!resizeButton)
|
|
180
|
+
return;
|
|
181
|
+
if (event.type === 'pointerup' && resizeButton.hasPointerCapture(event.pointerId)) {
|
|
182
|
+
resizeButton.releasePointerCapture(event.pointerId);
|
|
183
|
+
}
|
|
184
|
+
resizeButton.removeEventListener('pointermove', this.handlePointerMove);
|
|
185
|
+
resizeButton.removeEventListener('pointerup', this.handlePointerUp);
|
|
186
|
+
resizeButton.removeEventListener('lostpointercapture', this.handlePointerUp);
|
|
187
|
+
};
|
|
111
188
|
}
|
|
112
189
|
get textarea() {
|
|
113
190
|
return this.inputElement;
|
|
@@ -311,8 +388,30 @@ class Textarea extends AutoFocusOnMountMixin(FormInternalsMixin(DataAriaLabelMix
|
|
|
311
388
|
}
|
|
312
389
|
return html ` <div part="textarea-footer">${this.renderHelperText()} ${this.renderCharacterCounter()}</div> `;
|
|
313
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* Handles the resize button keydown event for keyboard-based resizing.
|
|
393
|
+
* @param event - The keyboard event.
|
|
394
|
+
*/
|
|
395
|
+
handleResizeKeyDown(event) {
|
|
396
|
+
if (this.readonly) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const currentRows = this.rows || DEFAULTS.ROWS;
|
|
400
|
+
let newRows;
|
|
401
|
+
if (event.key === KEYS.ARROW_UP) {
|
|
402
|
+
newRows = Math.max(1, currentRows - 1);
|
|
403
|
+
}
|
|
404
|
+
else if (event.key === KEYS.ARROW_DOWN) {
|
|
405
|
+
newRows = currentRows + 1;
|
|
406
|
+
}
|
|
407
|
+
if (newRows !== undefined) {
|
|
408
|
+
event.preventDefault();
|
|
409
|
+
event.stopPropagation();
|
|
410
|
+
this.rows = newRows;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
314
413
|
render() {
|
|
315
|
-
var _a;
|
|
414
|
+
var _a, _b;
|
|
316
415
|
return html `
|
|
317
416
|
${this.renderLabel()}
|
|
318
417
|
<div class="mdc-focus-ring" part="textarea-container">
|
|
@@ -344,6 +443,19 @@ class Textarea extends AutoFocusOnMountMixin(FormInternalsMixin(DataAriaLabelMix
|
|
|
344
443
|
announcement="${ifDefined(this.ariaLiveAnnouncer)}"
|
|
345
444
|
data-aria-live="polite"
|
|
346
445
|
></mdc-screenreaderannouncer>
|
|
446
|
+
${this.resizable ? html `
|
|
447
|
+
<mdc-button
|
|
448
|
+
part="resize-button"
|
|
449
|
+
class="own-focus-ring"
|
|
450
|
+
variant="tertiary"
|
|
451
|
+
size="24"
|
|
452
|
+
prefix-icon="resize-corner-regular"
|
|
453
|
+
aria-label=${(_b = this.resizeButtonAriaLabel) !== null && _b !== void 0 ? _b : ''}
|
|
454
|
+
?disabled="${this.disabled || this.readonly}"
|
|
455
|
+
@keydown=${this.handleResizeKeyDown}
|
|
456
|
+
@pointerdown=${this.handlePointerDown}
|
|
457
|
+
></mdc-button>
|
|
458
|
+
` : nothing}
|
|
347
459
|
</div>
|
|
348
460
|
${this.renderTextareaFooter()}
|
|
349
461
|
`;
|
|
@@ -355,7 +467,7 @@ __decorate([
|
|
|
355
467
|
__metadata("design:type", String)
|
|
356
468
|
], Textarea.prototype, "placeholder", void 0);
|
|
357
469
|
__decorate([
|
|
358
|
-
property({ type: Number }),
|
|
470
|
+
property({ type: Number, reflect: true }),
|
|
359
471
|
__metadata("design:type", Number)
|
|
360
472
|
], Textarea.prototype, "rows", void 0);
|
|
361
473
|
__decorate([
|
|
@@ -394,6 +506,14 @@ __decorate([
|
|
|
394
506
|
property({ type: String, attribute: 'character-limit-announcement' }),
|
|
395
507
|
__metadata("design:type", String)
|
|
396
508
|
], Textarea.prototype, "characterLimitAnnouncement", void 0);
|
|
509
|
+
__decorate([
|
|
510
|
+
property({ type: Boolean, reflect: true }),
|
|
511
|
+
__metadata("design:type", Boolean)
|
|
512
|
+
], Textarea.prototype, "resizable", void 0);
|
|
513
|
+
__decorate([
|
|
514
|
+
property({ type: String, attribute: 'resize-button-aria-label' }),
|
|
515
|
+
__metadata("design:type", String)
|
|
516
|
+
], Textarea.prototype, "resizeButtonAriaLabel", void 0);
|
|
397
517
|
__decorate([
|
|
398
518
|
query('textarea'),
|
|
399
519
|
__metadata("design:type", HTMLTextAreaElement)
|
|
@@ -35,6 +35,7 @@ const styles = [
|
|
|
35
35
|
padding: 0.375rem 0.25rem 0.25rem 0.75rem;
|
|
36
36
|
background-color: var(--mdc-textarea-container-background-color);
|
|
37
37
|
width: 100%;
|
|
38
|
+
position: relative;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
:host(:dir(rtl))::part(textarea-container) {
|
|
@@ -101,6 +102,30 @@ const styles = [
|
|
|
101
102
|
opacity: 0;
|
|
102
103
|
pointer-events: none;
|
|
103
104
|
}
|
|
105
|
+
|
|
106
|
+
:host::part(resize-button) {
|
|
107
|
+
position: absolute;
|
|
108
|
+
bottom: 0.25rem;
|
|
109
|
+
inset-inline-end: 0.25rem;
|
|
110
|
+
cursor: nwse-resize;
|
|
111
|
+
opacity: 0.5;
|
|
112
|
+
z-index: 1;
|
|
113
|
+
border-radius: 50%;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
:host::part(resize-button):focus-visible {
|
|
117
|
+
opacity: 1;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
:host(:dir(rtl))::part(resize-button) {
|
|
121
|
+
cursor: nesw-resize;
|
|
122
|
+
transform: scaleX(-1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
:host([readonly])::part(resize-button),
|
|
126
|
+
:host([disabled])::part(resize-button) {
|
|
127
|
+
cursor: default;
|
|
128
|
+
}
|
|
104
129
|
`,
|
|
105
130
|
...hostFocusRingStyles(true),
|
|
106
131
|
];
|