@momentum-design/components 0.123.5 → 0.124.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.
Files changed (35) hide show
  1. package/dist/browser/index.js +469 -318
  2. package/dist/browser/index.js.map +3 -3
  3. package/dist/components/menubar/menubar.component.js +1 -1
  4. package/dist/components/menuitem/menuitem.styles.js +1 -1
  5. package/dist/components/menusection/menusection.component.d.ts +5 -0
  6. package/dist/components/menusection/menusection.component.js +5 -0
  7. package/dist/components/menusection/menusection.styles.js +10 -7
  8. package/dist/components/navmenuitem/navmenuitem.component.d.ts +74 -27
  9. package/dist/components/navmenuitem/navmenuitem.component.js +135 -87
  10. package/dist/components/navmenuitem/navmenuitem.constants.d.ts +1 -0
  11. package/dist/components/navmenuitem/navmenuitem.constants.js +1 -0
  12. package/dist/components/navmenuitem/navmenuitem.styles.js +64 -29
  13. package/dist/components/popover/popover.component.js +4 -2
  14. package/dist/components/sidenavigation/sidenavigation.component.d.ts +50 -6
  15. package/dist/components/sidenavigation/sidenavigation.component.js +132 -25
  16. package/dist/components/sidenavigation/sidenavigation.constants.d.ts +1 -0
  17. package/dist/components/sidenavigation/sidenavigation.constants.js +1 -0
  18. package/dist/components/sidenavigation/sidenavigation.context.d.ts +1 -2
  19. package/dist/components/sidenavigation/sidenavigation.context.js +8 -9
  20. package/dist/components/sidenavigation/sidenavigation.styles.js +76 -8
  21. package/dist/components/textarea/textarea.component.d.ts +56 -0
  22. package/dist/components/textarea/textarea.component.js +122 -2
  23. package/dist/components/textarea/textarea.styles.js +25 -0
  24. package/dist/custom-elements.json +481 -160
  25. package/dist/react/index.d.ts +1 -1
  26. package/dist/react/index.js +1 -1
  27. package/dist/react/menusection/index.d.ts +5 -0
  28. package/dist/react/menusection/index.js +5 -0
  29. package/dist/react/navmenuitem/index.d.ts +9 -3
  30. package/dist/react/navmenuitem/index.js +9 -3
  31. package/dist/react/sidenavigation/index.d.ts +12 -2
  32. package/dist/react/sidenavigation/index.js +12 -2
  33. package/dist/react/textarea/index.d.ts +20 -0
  34. package/dist/react/textarea/index.js +20 -0
  35. package/package.json +1 -1
@@ -1,9 +1,15 @@
1
1
  import { css } from 'lit';
2
2
  const styles = css `
3
3
  :host {
4
- --mdc-sidenavigation-expanded-width: 15rem;
4
+ --mdc-sidenavigation-expanded-width: 14.5rem;
5
+ --mdc-sidenavigation-expanded-left-padding: 1rem;
6
+ --mdc-sidenavigation-expanded-right-padding: 1rem;
5
7
  --mdc-sidenavigation-collapsed-width: 4.5rem;
8
+ --mdc-sidenavigation-collapsed-left-padding: 1rem;
9
+ --mdc-sidenavigation-collapsed-right-padding: 1rem;
6
10
  --mdc-sidenavigation-vertical-divider-button-z-index: auto;
11
+ --mdc-sidenavigation-top-padding: 1rem;
12
+ --mdc-sidenavigation-bottom-padding: 1rem;
7
13
 
8
14
  display: flex;
9
15
  height: 100%;
@@ -25,20 +31,63 @@ const styles = css `
25
31
 
26
32
  :host::part(scrollable-section) {
27
33
  flex-grow: 1;
28
- overflow: auto;
29
- padding: 1rem 0;
34
+ overflow-y: auto;
35
+ overflow-x: hidden;
36
+ padding: var(--mdc-sidenavigation-top-padding) 0;
30
37
  min-height: 0;
31
38
  }
32
39
 
40
+ :host::part(scrollable-menubar) {
41
+ width: 100%;
42
+ }
43
+
44
+ ::slotted([slot='scrollable-menubar']) {
45
+ --mdc-menusection-divider-margin-block: 0.5rem 0.75rem;
46
+ --mdc-menusection-gap: 0.25rem;
47
+ }
48
+
49
+ ::slotted(mdc-navmenuitem[slot='scrollable-menubar']) {
50
+ margin-bottom: 0.25rem;
51
+ }
52
+
53
+ :host([expanded]) ::slotted([slot='scrollable-menubar']) {
54
+ --mdc-menusection-divider-width: var(--mdc-sidenavigation-expanded-width);
55
+ --mdc-menusection-header-padding: 0.25rem calc(var(--mdc-sidenavigation-expanded-left-padding) + 0.5rem) 0
56
+ calc(var(--mdc-sidenavigation-expanded-left-padding) + 0.5rem);
57
+ }
58
+
59
+ :host(:not([expanded])) ::slotted([slot='scrollable-menubar']) {
60
+ --mdc-menusection-divider-width: var(--mdc-sidenavigation-collapsed-width);
61
+ }
62
+
33
63
  :host::part(fixed-section) {
34
64
  flex-shrink: 0;
35
65
  display: flex;
36
66
  flex-direction: column;
37
67
  align-items: flex-start;
38
- padding-bottom: 1rem;
68
+ padding-bottom: var(--mdc-sidenavigation-bottom-padding);
39
69
  gap: 0.5rem;
40
70
  }
41
71
 
72
+ :host::part(fixed-menubar) {
73
+ width: 100%;
74
+ }
75
+
76
+ ::slotted([slot='fixed-menubar']) {
77
+ --mdc-menusection-divider-margin-block: 0.5rem 0.75rem;
78
+ --mdc-menusection-gap: 0.25rem;
79
+ }
80
+
81
+ :host([expanded]) ::slotted([slot='fixed-menubar']) {
82
+ --mdc-menusection-divider-width: var(--mdc-sidenavigation-expanded-width);
83
+ --mdc-menusection-header-padding: 0.25rem calc(var(--mdc-sidenavigation-expanded-left-padding) + 0.5rem) 0
84
+ calc(var(--mdc-sidenavigation-expanded-left-padding) + 0.5rem);
85
+ }
86
+
87
+ :host(:not([expanded])) ::slotted([slot='fixed-menubar']) {
88
+ --mdc-menusection-divider-width: var(--mdc-sidenavigation-collapsed-width);
89
+ }
90
+
42
91
  :host::part(brand-logo-container) {
43
92
  display: flex;
44
93
  align-items: center;
@@ -47,12 +96,20 @@ const styles = css `
47
96
  border-radius: 1.25rem;
48
97
  }
49
98
 
50
- :host(:dir(ltr))::part(brand-logo-container) {
51
- padding: 0.5rem 0rem 0.5rem 1.5rem;
99
+ :host([expanded]:dir(ltr))::part(brand-logo-container) {
100
+ padding: 0.5rem 0rem 0.5rem calc(var(--mdc-sidenavigation-expanded-left-padding) + 0.5rem);
101
+ }
102
+
103
+ :host([expanded]:dir(rtl))::part(brand-logo-container) {
104
+ padding: 0.5rem calc(var(--mdc-sidenavigation-expanded-left-padding) + 0.5rem) 0.5rem 0rem;
52
105
  }
53
106
 
54
- :host(:dir(rtl))::part(brand-logo-container) {
55
- padding: 0.5rem 1.5rem 0.5rem 0rem;
107
+ :host(:not([expanded]):dir(ltr))::part(brand-logo-container) {
108
+ padding: 0.5rem 0rem 0.5rem calc(var(--mdc-sidenavigation-collapsed-left-padding) + 0.5rem);
109
+ }
110
+
111
+ :host(:not([expanded]):dir(rtl))::part(brand-logo-container) {
112
+ padding: 0.5rem calc(var(--mdc-sidenavigation-collapsed-left-padding) + 0.5rem) 0.5rem 0rem;
56
113
  }
57
114
 
58
115
  ::slotted([slot='brand-logo']) {
@@ -63,11 +120,22 @@ const styles = css `
63
120
  }
64
121
 
65
122
  :host::part(separator) {
123
+ flex-shrink: 0;
66
124
  margin-bottom: 0.75rem;
67
125
  }
68
126
 
69
127
  :host::part(vertical-divider-button) {
70
128
  z-index: var(--mdc-sidenavigation-vertical-divider-button-z-index);
71
129
  }
130
+
131
+ :host([variant='flexible-on-hover'])::part(vertical-divider),
132
+ :host([variant='flexible-on-hover'])::part(vertical-divider-button) {
133
+ opacity: 0;
134
+ }
135
+
136
+ :host([data-grabber-visible][variant='flexible-on-hover'])::part(vertical-divider),
137
+ :host([data-grabber-visible][variant='flexible-on-hover'])::part(vertical-divider-button) {
138
+ opacity: 1;
139
+ }
72
140
  `;
73
141
  export default [styles];
@@ -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
  ];