@redvars/peacock 3.5.0 → 3.5.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 (148) hide show
  1. package/dist/BaseButton-DuASuVth.js +219 -0
  2. package/dist/BaseButton-DuASuVth.js.map +1 -0
  3. package/dist/BaseHyperlinkMixin-BNuwbiEf.js +65 -0
  4. package/dist/BaseHyperlinkMixin-BNuwbiEf.js.map +1 -0
  5. package/dist/assets/components.css +1 -1
  6. package/dist/assets/components.css.map +1 -1
  7. package/dist/assets/styles.css +1 -1
  8. package/dist/assets/styles.css.map +1 -1
  9. package/dist/banner.js +12 -27
  10. package/dist/banner.js.map +1 -1
  11. package/dist/{button-DMN1dPAg.js → button-DouvOfEU.js} +77 -251
  12. package/dist/button-DouvOfEU.js.map +1 -0
  13. package/dist/{button-group-CX9CUUXk.js → button-group-CEdMwvJJ.js} +71 -42
  14. package/dist/button-group-CEdMwvJJ.js.map +1 -0
  15. package/dist/button-group.js +5 -5
  16. package/dist/button.js +3 -3
  17. package/dist/card.js +18 -73
  18. package/dist/card.js.map +1 -1
  19. package/dist/chart-bar.js.map +1 -1
  20. package/dist/chart-doughnut.js +2 -2
  21. package/dist/chart-doughnut.js.map +1 -1
  22. package/dist/chart-pie.js +2 -2
  23. package/dist/chart-pie.js.map +1 -1
  24. package/dist/chart-stacked-bar.js.map +1 -1
  25. package/dist/code-highlighter.js +2 -1
  26. package/dist/code-highlighter.js.map +1 -1
  27. package/dist/custom-elements-jsdocs.json +3105 -1494
  28. package/dist/custom-elements.json +9244 -7829
  29. package/dist/fab.js +421 -9
  30. package/dist/fab.js.map +1 -1
  31. package/dist/index.js +6 -6
  32. package/dist/{select-4pl4XBj7.js → navigation-rail-Lxetd5-Z.js} +2214 -1090
  33. package/dist/navigation-rail-Lxetd5-Z.js.map +1 -0
  34. package/dist/notification.js +3 -2
  35. package/dist/notification.js.map +1 -1
  36. package/dist/peacock-loader.js +22 -10
  37. package/dist/peacock-loader.js.map +1 -1
  38. package/dist/search.js +4 -0
  39. package/dist/search.js.map +1 -1
  40. package/dist/src/__mixins/BaseButtonMixin.d.ts +20 -0
  41. package/dist/src/__mixins/BaseHyperlinkMixin.d.ts +18 -0
  42. package/dist/src/__mixins/MixinConstructor.d.ts +1 -0
  43. package/dist/src/banner/banner.d.ts +0 -4
  44. package/dist/src/button/BaseButton.d.ts +4 -47
  45. package/dist/src/button/button/button.d.ts +32 -3
  46. package/dist/src/button/button-group/button-group.d.ts +2 -2
  47. package/dist/src/button/icon-button/icon-button.d.ts +33 -8
  48. package/dist/src/card/card.d.ts +4 -15
  49. package/dist/src/fab/fab.d.ts +4 -35
  50. package/dist/src/focus-ring/focus-ring.d.ts +11 -5
  51. package/dist/src/index.d.ts +3 -1
  52. package/dist/src/link/link.d.ts +1 -1
  53. package/dist/src/navigation-rail/index.d.ts +2 -0
  54. package/dist/src/navigation-rail/navigation-rail-item.d.ts +55 -0
  55. package/dist/src/navigation-rail/navigation-rail.d.ts +71 -0
  56. package/dist/src/sidebar-menu/index.d.ts +3 -0
  57. package/dist/src/sidebar-menu/sidebar-menu-item.d.ts +58 -0
  58. package/dist/src/sidebar-menu/sidebar-menu.d.ts +38 -0
  59. package/dist/src/sidebar-menu/sidebar-sub-menu.d.ts +35 -0
  60. package/dist/src/toolbar/toolbar.d.ts +10 -10
  61. package/dist/src/tooltip/tooltip.d.ts +3 -0
  62. package/dist/src/url-field/index.d.ts +1 -0
  63. package/dist/src/url-field/url-field.d.ts +48 -0
  64. package/dist/test/sidebar-menu.test.d.ts +1 -0
  65. package/dist/toolbar.js +10 -10
  66. package/dist/toolbar.js.map +1 -1
  67. package/dist/tsconfig.tsbuildinfo +1 -1
  68. package/package.json +1 -1
  69. package/readme.md +73 -65
  70. package/scss/mixin.scss +16 -0
  71. package/src/__mixins/BaseButtonMixin.ts +83 -0
  72. package/src/__mixins/BaseHyperlinkMixin.ts +68 -0
  73. package/src/__mixins/MixinConstructor.ts +1 -0
  74. package/src/{__base_element → __mixins}/README.md +2 -2
  75. package/src/banner/banner.scss +18 -22
  76. package/src/banner/banner.ts +1 -7
  77. package/src/button/BaseButton.ts +11 -100
  78. package/src/button/button/button-sizes.scss +4 -2
  79. package/src/button/button/button.ts +76 -23
  80. package/src/button/button-group/button-group.ts +2 -2
  81. package/src/button/icon-button/icon-button.ts +75 -33
  82. package/src/card/card.ts +11 -71
  83. package/src/chart-bar/chart-bar.ts +9 -14
  84. package/src/chart-bar/chart-stacked-bar.ts +12 -18
  85. package/src/chart-doughnut/chart-doughnut.ts +23 -27
  86. package/src/chart-pie/chart-pie.ts +19 -23
  87. package/src/checkbox/checkbox.scss +17 -34
  88. package/src/checkbox/checkbox.ts +3 -1
  89. package/src/code-highlighter/code-highlighter.scss +1 -0
  90. package/src/code-highlighter/code-highlighter.ts +1 -1
  91. package/src/date-picker/date-picker.ts +1 -1
  92. package/src/elevation/elevation.scss +5 -5
  93. package/src/fab/fab.ts +29 -100
  94. package/src/focus-ring/focus-ring.ts +47 -40
  95. package/src/index.ts +3 -1
  96. package/src/input/input.ts +3 -1
  97. package/src/link/link.ts +2 -2
  98. package/src/menu/menu-item/menu-item.ts +3 -1
  99. package/src/navigation-rail/index.ts +2 -0
  100. package/src/navigation-rail/navigation-rail-item.scss +216 -0
  101. package/src/navigation-rail/navigation-rail-item.ts +223 -0
  102. package/src/navigation-rail/navigation-rail.scss +72 -0
  103. package/src/navigation-rail/navigation-rail.ts +149 -0
  104. package/src/notification/notification.ts +3 -2
  105. package/src/number-field/number-field.ts +6 -4
  106. package/src/pagination/pagination.ts +6 -4
  107. package/src/peacock-loader.ts +22 -5
  108. package/src/search/search.ts +4 -0
  109. package/src/sidebar-menu/demo/index.html +68 -0
  110. package/src/sidebar-menu/index.ts +3 -0
  111. package/src/sidebar-menu/sidebar-menu-item.scss +102 -0
  112. package/src/sidebar-menu/sidebar-menu-item.ts +151 -0
  113. package/src/{tree-view/tree-view.scss → sidebar-menu/sidebar-menu.scss} +1 -1
  114. package/src/sidebar-menu/sidebar-menu.ts +182 -0
  115. package/src/sidebar-menu/sidebar-sub-menu.scss +130 -0
  116. package/src/sidebar-menu/sidebar-sub-menu.ts +160 -0
  117. package/src/skeleton/skeleton.scss +18 -24
  118. package/src/snackbar/snackbar.ts +1 -1
  119. package/src/tabs/tab.ts +4 -3
  120. package/src/text/text.css-component.scss +7 -1
  121. package/src/time-picker/time-picker.ts +1 -1
  122. package/src/toolbar/toolbar.ts +10 -10
  123. package/src/tooltip/tooltip.ts +24 -0
  124. package/src/url-field/index.ts +1 -0
  125. package/src/url-field/url-field.scss +50 -0
  126. package/src/url-field/url-field.ts +239 -0
  127. package/dist/button-DMN1dPAg.js.map +0 -1
  128. package/dist/button-group-CX9CUUXk.js.map +0 -1
  129. package/dist/fab-C5Nzxk0E.js +0 -497
  130. package/dist/fab-C5Nzxk0E.js.map +0 -1
  131. package/dist/select-4pl4XBj7.js.map +0 -1
  132. package/dist/spread-B5cgadZl.js +0 -32
  133. package/dist/spread-B5cgadZl.js.map +0 -1
  134. package/dist/src/__base_element/BaseHyperlink.d.ts +0 -20
  135. package/dist/src/tree-view/index.d.ts +0 -2
  136. package/dist/src/tree-view/tree-node.d.ts +0 -69
  137. package/dist/src/tree-view/tree-view.d.ts +0 -40
  138. package/dist/src/tree-view/wc-tree-view.d.ts +0 -6
  139. package/dist/test/tree-view.test.d.ts +0 -1
  140. package/dist/throttle-C7ZAPqtu.js +0 -24
  141. package/dist/throttle-C7ZAPqtu.js.map +0 -1
  142. package/src/__base_element/BaseHyperlink.ts +0 -42
  143. package/src/tree-view/demo/index.html +0 -57
  144. package/src/tree-view/index.ts +0 -2
  145. package/src/tree-view/tree-node.scss +0 -101
  146. package/src/tree-view/tree-node.ts +0 -268
  147. package/src/tree-view/tree-view.ts +0 -182
  148. package/src/tree-view/wc-tree-view.ts +0 -9
package/src/fab/fab.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { html, LitElement, nothing } from 'lit';
2
- import { property, query, state } from 'lit/decorators.js';
1
+ import { html, nothing } from 'lit';
2
+ import { property, state } from 'lit/decorators.js';
3
3
  import { classMap } from 'lit/directives/class-map.js';
4
4
  import { ifDefined } from 'lit/directives/if-defined.js';
5
5
 
@@ -8,10 +8,10 @@ import { dispatchActivationClick, isActivationClick } from '@/__utils/dispatch-e
8
8
  import { throttle } from '@/__utils/throttle.js';
9
9
  import { spread } from '@/__directive/spread.js';
10
10
 
11
- import { IconProvider } from '../icon/icon.js';
12
11
  import styles from './fab.scss';
13
12
  import colorStyles from './fab-colors.scss';
14
13
  import sizeStyles from './fab-sizes.scss';
14
+ import { BaseButton } from '@/button/BaseButton.js';
15
15
 
16
16
  /**
17
17
  * @label FAB
@@ -31,32 +31,17 @@ import sizeStyles from './fab-sizes.scss';
31
31
  *
32
32
  * @example
33
33
  * ```html
34
- * <wc-fab name="add"></wc-fab>
34
+ * <wc-fab><wc-icon name="add"></wc-icon></wc-fab>
35
35
  * ```
36
36
  * @tags controls
37
37
  */
38
38
  @IndividualComponent
39
- export class Fab extends LitElement {
39
+ export class Fab extends BaseButton {
40
40
  static override styles = [styles, colorStyles, sizeStyles];
41
41
 
42
42
  #id = crypto.randomUUID();
43
+
43
44
 
44
- #tabindex?: number = 0;
45
-
46
- /**
47
- * Name of the icon to display inside the FAB.
48
- */
49
- @property({ type: String, reflect: true }) name?: string;
50
-
51
- /**
52
- * Source URL for a custom icon.
53
- */
54
- @property({ type: String, reflect: true }) src?: string;
55
-
56
- /**
57
- * Icon provider. Defaults to `"material-symbols"`.
58
- */
59
- @property({ type: String }) provider: IconProvider = 'material-symbols';
60
45
 
61
46
  /**
62
47
  * Optional label text for the extended FAB variant.
@@ -93,21 +78,6 @@ export class Fab extends LitElement {
93
78
  */
94
79
  @property({ type: Boolean, reflect: true }) lowered: boolean = false;
95
80
 
96
- /**
97
- * If `true`, the user cannot interact with the FAB.
98
- */
99
- @property({ type: Boolean, reflect: true }) disabled: boolean = false;
100
-
101
- /**
102
- * Hyperlink to navigate to on click.
103
- */
104
- @property({ reflect: true }) href?: string;
105
-
106
- /**
107
- * Sets or retrieves the window or frame at which to target content.
108
- */
109
- @property() target: string = '_self';
110
-
111
81
  /**
112
82
  * Additional ARIA attributes to pass to the inner button/anchor element.
113
83
  */
@@ -127,26 +97,12 @@ export class Fab extends LitElement {
127
97
  @state()
128
98
  isPressed = false;
129
99
 
130
- @query('.fab') readonly fabElement!: HTMLElement | null;
131
-
132
100
  override focus() {
133
- this.fabElement?.focus();
101
+ this.buttonElement?.focus();
134
102
  }
135
103
 
136
104
  override blur() {
137
- this.fabElement?.blur();
138
- }
139
-
140
- override connectedCallback() {
141
- super.connectedCallback();
142
- this.addEventListener('click', this.__dispatchClickWithThrottle);
143
- window.addEventListener('mouseup', this.__handlePress);
144
- }
145
-
146
- override disconnectedCallback() {
147
- window.removeEventListener('mouseup', this.__handlePress);
148
- this.removeEventListener('click', this.__dispatchClickWithThrottle);
149
- super.disconnectedCallback();
105
+ this.buttonElement?.blur();
150
106
  }
151
107
 
152
108
  override firstUpdated() {
@@ -156,30 +112,6 @@ export class Fab extends LitElement {
156
112
  );
157
113
  }
158
114
 
159
- __handlePress = (event: KeyboardEvent | MouseEvent) => {
160
- if (this.disabled) return;
161
- if (
162
- event instanceof KeyboardEvent &&
163
- event.type === 'keydown' &&
164
- (event.key === 'Enter' || event.key === ' ')
165
- ) {
166
- this.isPressed = true;
167
- } else if (event.type === 'mousedown') {
168
- this.isPressed = true;
169
- } else {
170
- this.isPressed = false;
171
- }
172
- };
173
-
174
- __isLink() {
175
- return !!this.href;
176
- }
177
-
178
- __dispatchClickWithThrottle: (event: MouseEvent | KeyboardEvent) => void =
179
- event => {
180
- this.__dispatchClick(event);
181
- };
182
-
183
115
  __dispatchClick = (event: MouseEvent | KeyboardEvent) => {
184
116
  if (this.disabled && this.href) {
185
117
  event.stopImmediatePropagation();
@@ -187,12 +119,12 @@ export class Fab extends LitElement {
187
119
  return;
188
120
  }
189
121
 
190
- if (!isActivationClick(event) || !this.fabElement) {
122
+ if (!isActivationClick(event) || !this.buttonElement) {
191
123
  return;
192
124
  }
193
125
 
194
126
  this.focus();
195
- dispatchActivationClick(this.fabElement);
127
+ dispatchActivationClick(this.buttonElement);
196
128
  };
197
129
 
198
130
  __getDisabledReasonID() {
@@ -204,6 +136,7 @@ export class Fab extends LitElement {
204
136
  const isExtended = !!this.label;
205
137
 
206
138
  const cssClasses = {
139
+ button: true,
207
140
  fab: true,
208
141
  'fab-element': true,
209
142
  [`size-${this.size}`]: true,
@@ -218,15 +151,16 @@ export class Fab extends LitElement {
218
151
  if (!isLink) {
219
152
  return html`<button
220
153
  class=${classMap(cssClasses)}
221
- id="fab"
222
- tabindex=${this.#tabindex}
154
+ id="button"
223
155
  type="button"
224
156
  @click=${this.__dispatchClickWithThrottle}
225
157
  @mousedown=${this.__handlePress}
226
158
  @keydown=${this.__handlePress}
227
159
  @keyup=${this.__handlePress}
228
- aria-label=${this.label ?? this.name ?? nothing}
229
- aria-disabled=${`${this.disabled}`}
160
+
161
+ aria-describedby=${ifDefined(this.softDisabled ? BaseButton.DISABLED_REASON_ID : undefined)}
162
+ ?aria-disabled=${this.softDisabled}
163
+
230
164
  ?disabled=${this.disabled}
231
165
  ${spread(this.configAria)}
232
166
  >
@@ -237,17 +171,19 @@ export class Fab extends LitElement {
237
171
 
238
172
  return html`<a
239
173
  class=${classMap(cssClasses)}
240
- id="fab"
241
- tabindex=${this.#tabindex}
174
+ id="button"
175
+ tabindex=${this.disabled ? '-1' : '0'}
242
176
  href=${ifDefined(this.href)}
243
177
  target=${this.target}
244
- @click=${this.__dispatchClickWithThrottle}
178
+ @click=${this.__dispatchClick}
245
179
  @mousedown=${this.__handlePress}
246
180
  @keydown=${this.__handlePress}
247
181
  @keyup=${this.__handlePress}
248
182
  role="button"
249
- aria-label=${this.label ?? this.name ?? nothing}
250
- aria-disabled=${`${this.disabled}`}
183
+
184
+ aria-describedby=${ifDefined(this.softDisabled ? BaseButton.DISABLED_REASON_ID : undefined)}
185
+ ?aria-disabled=${this.softDisabled}
186
+
251
187
  ${spread(this.configAria)}
252
188
  >
253
189
  ${this.__renderFabContent(isExtended)}
@@ -257,29 +193,22 @@ export class Fab extends LitElement {
257
193
 
258
194
  __renderFabContent(isExtended: boolean) {
259
195
  return html`
260
- <wc-focus-ring class="focus-ring" .control=${this} .forElement=${this.fabElement}></wc-focus-ring>
196
+ <wc-focus-ring class="focus-ring" for='button'></wc-focus-ring>
261
197
  <wc-elevation class="elevation"></wc-elevation>
262
198
  <div class="background"></div>
263
199
  <wc-ripple class="ripple"></wc-ripple>
200
+ <wc-skeleton class="skeleton"></wc-skeleton>
264
201
 
265
202
  <div class="fab-content">
266
- <wc-icon
267
- class="fab-icon"
268
- name=${ifDefined(this.name)}
269
- src=${ifDefined(this.src)}
270
- provider=${this.provider}
271
- ></wc-icon>
203
+
204
+ <slot></slot>
272
205
  ${isExtended
273
206
  ? html`<span class="fab-label">${this.label}</span>`
274
207
  : nothing}
275
208
  </div>
209
+
210
+ ${this.__renderDisabledReason(this.softDisabled)}
276
211
  `;
277
212
  }
278
213
 
279
- __renderTooltip() {
280
- if (this.tooltip) {
281
- return html`<wc-tooltip for="fab">${this.tooltip}</wc-tooltip>`;
282
- }
283
- return nothing;
284
- }
285
214
  }
@@ -20,34 +20,14 @@ export class FocusRing extends LitElement {
20
20
 
21
21
  @property({ type: String }) for = '';
22
22
 
23
+ private __boundFocusin = this.__focusin.bind(this);
23
24
 
24
- render() {
25
- return nothing;
26
- }
27
-
28
- _control?: HTMLElement;
29
-
30
- _focusTarget?: HTMLElement;
25
+ private __boundFocusout = this.__focusout.bind(this);
31
26
 
32
- get control() {
33
- return this._control || null;
34
- }
35
-
36
- set control(control: HTMLElement | null) {
37
- if (control) {
38
- this._control = control;
39
- } else {
40
- this.detach();
41
- }
42
- }
27
+ private __boundPointerdown = this.__pointerdown.bind(this);
43
28
 
44
- set forElement(value: HTMLElement | null) {
45
- if (value) {
46
- this._focusTarget = value;
47
- this.attach();
48
- } else {
49
- this.detach();
50
- }
29
+ render() {
30
+ return nothing;
51
31
  }
52
32
 
53
33
  connectedCallback() {
@@ -60,6 +40,22 @@ export class FocusRing extends LitElement {
60
40
  super.disconnectedCallback();
61
41
  }
62
42
 
43
+ updated(changed: Map<string, unknown>) {
44
+ if (changed.has('for')) {
45
+ const prevId = changed.get('for') as string;
46
+ if (prevId) {
47
+ const root = this.parentElement?.getRootNode() as ShadowRoot | Document;
48
+ const prevEl = root?.getElementById(prevId) ?? document.getElementById(prevId);
49
+ if (prevEl) {
50
+ prevEl.removeEventListener('focusin', this.__boundFocusin);
51
+ prevEl.removeEventListener('focusout', this.__boundFocusout);
52
+ prevEl.removeEventListener('pointerdown', this.__boundPointerdown);
53
+ }
54
+ }
55
+ this.attach();
56
+ }
57
+ }
58
+
63
59
  __focusin() {
64
60
  const focusTarget = this.__getFocusTarget();
65
61
  this.visible = focusTarget?.matches(':focus-visible') ?? false;
@@ -73,35 +69,46 @@ export class FocusRing extends LitElement {
73
69
  this.visible = false;
74
70
  }
75
71
 
72
+ /**
73
+ * Resolves the element that should receive focus-ring event listeners by id.
74
+ * Prefers lookup from the current control's root node, then falls back to a
75
+ * document-level lookup.
76
+ *
77
+ * @returns The resolved focus target, if one can be found.
78
+ */
76
79
  __getFocusTarget(): HTMLElement | undefined {
77
-
78
- if (this._focusTarget) {
79
- return this._focusTarget;
80
+ if (this.for) {
81
+ const root = this.parentElement?.getRootNode() as ShadowRoot | Document;
82
+ if (root) {
83
+ const focusTarget = root.getElementById(this.for);
84
+ if (focusTarget) {
85
+ return focusTarget;
86
+ }
87
+ }
88
+ const focusTarget = document.getElementById(this.for);
89
+ if (focusTarget) {
90
+ return focusTarget;
91
+ }
80
92
  }
81
93
 
82
- const focusTarget = document.getElementById(this.for);
83
- if (focusTarget) {
84
- return focusTarget
85
- }
86
- return undefined;
94
+ return undefined;
87
95
  }
88
96
 
89
97
  attach() {
90
98
  const focusTarget = this.__getFocusTarget();
91
99
  if (focusTarget) {
92
- focusTarget.addEventListener('focusin', this.__focusin.bind(this));
93
- focusTarget.addEventListener('focusout', this.__focusout.bind(this));
94
- focusTarget.addEventListener('pointerdown', this.__pointerdown.bind(this));
100
+ focusTarget.addEventListener('focusin', this.__boundFocusin);
101
+ focusTarget.addEventListener('focusout', this.__boundFocusout);
102
+ focusTarget.addEventListener('pointerdown', this.__boundPointerdown);
95
103
  }
96
104
  }
97
105
 
98
106
  detach() {
99
107
  const focusTarget = this.__getFocusTarget();
100
108
  if (focusTarget) {
101
- focusTarget.removeEventListener('focusin', this.__focusin.bind(this));
102
- focusTarget.removeEventListener('focusout', this.__focusout.bind(this));
103
- focusTarget.removeEventListener('pointerdown', this.__pointerdown.bind(this));
109
+ focusTarget.removeEventListener('focusin', this.__boundFocusin);
110
+ focusTarget.removeEventListener('focusout', this.__boundFocusout);
111
+ focusTarget.removeEventListener('pointerdown', this.__boundPointerdown);
104
112
  }
105
- this._control = undefined;
106
113
  }
107
114
  }
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ export { LinearProgress } from './progress/linear-progress/index.js';
18
18
  export { CircularProgress } from './progress/circular-progress/index.js';
19
19
  export { Skeleton } from './skeleton/index.js';
20
20
  export { Input } from './input/index.js';
21
+ export { UrlField } from './url-field/index.js';
21
22
  export { Field } from './field/index.js';
22
23
  export { NumberField } from './number-field/index.js';
23
24
  export { DatePicker } from './date-picker/index.js';
@@ -44,7 +45,7 @@ export { ChartPie } from './chart-pie/index.js';
44
45
  export { ChartBar, ChartStackedBar } from './chart-bar/index.js';
45
46
  export { Table } from './table/index.js';
46
47
  export { Pagination } from './pagination/index.js';
47
- export { TreeView, TreeNode } from './tree-view/index.js';
48
+ export { SidebarMenu, SidebarMenuItem, SidebarSubMenu } from './sidebar-menu/index.js';
48
49
  export { Card } from './card/index.js';
49
50
  export { Banner } from './banner/index.js';
50
51
  export { Notification } from './notification/index.js';
@@ -57,4 +58,5 @@ export type { SelectOption } from './select/index.js';
57
58
  export { SelectOptionElement } from './select/index.js';
58
59
  export { Search } from './search/index.js';
59
60
  export { Toolbar } from './toolbar/index.js';
61
+ export { NavigationRail, NavigationRailItem } from './navigation-rail/index.js';
60
62
 
@@ -178,11 +178,13 @@ export class Input extends BaseInput {
178
178
  <wc-icon-button
179
179
  class="password-toggle"
180
180
  variant="text"
181
- name=${this.passwordVisible ? 'visibility_off' : 'visibility'}
182
181
  @click=${() => {
183
182
  this.passwordVisible = !this.passwordVisible;
184
183
  }}
185
184
  >
185
+ <wc-icon
186
+ name=${this.passwordVisible ? 'visibility_off' : 'visibility'}
187
+ ></wc-icon>
186
188
  </wc-icon-button>
187
189
  </pc-tooltip>
188
190
  `
package/src/link/link.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { html, LitElement } from 'lit';
2
2
  import { classMap } from 'lit/directives/class-map.js';
3
- import BaseHyperlink from '../__base_element/BaseHyperlink.js';
3
+ import BaseHyperlinkMixin from '../__mixins/BaseHyperlinkMixin.js';
4
4
  import styles from './link.scss';
5
5
 
6
6
  /**
@@ -16,7 +16,7 @@ import styles from './link.scss';
16
16
  * <wc-link href="#">Link</wc-link>
17
17
  * ```
18
18
  */
19
- export class Link extends BaseHyperlink(LitElement) {
19
+ export class Link extends BaseHyperlinkMixin(LitElement) {
20
20
  static styles = [styles];
21
21
 
22
22
  render() {
@@ -151,6 +151,7 @@ export class MenuItem extends LitElement {
151
151
 
152
152
  if (isLink) {
153
153
  return html`<a
154
+ id="item"
154
155
  class=${classMap(cssClasses)}
155
156
  href=${this.href}
156
157
  target=${this.target}
@@ -164,6 +165,7 @@ export class MenuItem extends LitElement {
164
165
  }
165
166
 
166
167
  return html`<div
168
+ id="item"
167
169
  class=${classMap(cssClasses)}
168
170
  aria-disabled=${String(this.disabled)}
169
171
  aria-haspopup=${this.hasSubmenu ? 'menu' : nothing}
@@ -176,7 +178,7 @@ export class MenuItem extends LitElement {
176
178
 
177
179
  renderContent() {
178
180
  return html`
179
- <wc-focus-ring class="focus-ring" .control=${this} .forElement=${this}></wc-focus-ring>
181
+ <wc-focus-ring class="focus-ring" for='item'></wc-focus-ring>
180
182
  <div class="background"></div>
181
183
  <wc-ripple class="ripple"></wc-ripple>
182
184
 
@@ -0,0 +1,2 @@
1
+ export { NavigationRail } from './navigation-rail.js';
2
+ export { NavigationRailItem } from './navigation-rail-item.js';
@@ -0,0 +1,216 @@
1
+ @use '../../scss/mixin';
2
+
3
+ @include mixin.base-styles;
4
+
5
+ :host {
6
+ display: block;
7
+ }
8
+
9
+ /* Reset native button/link styles */
10
+ .item-element {
11
+ background: transparent;
12
+ border: none;
13
+ appearance: none;
14
+ margin: 0;
15
+ outline: none;
16
+ text-decoration: none;
17
+ text-align: unset;
18
+ color: inherit;
19
+ }
20
+
21
+ .item {
22
+ position: relative;
23
+ display: flex;
24
+ flex-direction: column;
25
+ align-items: center;
26
+ justify-content: center;
27
+ width: 100%;
28
+ min-height: 3.5rem; /* 56dp */
29
+ padding-block: 0.25rem; /* 4dp vertical padding */
30
+ cursor: pointer;
31
+ gap: 0.25rem; /* 4dp gap between indicator and label */
32
+ box-sizing: border-box;
33
+
34
+ /* Color tokens */
35
+ --_inactive-icon-color: var(--nav-rail-inactive-icon-color, var(--color-on-surface-variant));
36
+ --_active-icon-color: var(--nav-rail-active-icon-color, var(--color-on-secondary-container));
37
+ --_inactive-label-color: var(--nav-rail-inactive-label-color, var(--color-on-surface-variant));
38
+ --_active-label-color: var(--nav-rail-active-label-color, var(--color-on-surface));
39
+ --_indicator-color: var(--nav-rail-indicator-color, var(--color-secondary-container));
40
+ --_indicator-shape: var(--nav-rail-indicator-shape, var(--shape-corner-full));
41
+ --_indicator-width: var(--nav-rail-indicator-width, 3.5rem); /* 56dp */
42
+ --_indicator-height: var(--nav-rail-indicator-height, 2rem); /* 32dp */
43
+ --_state-color: var(--_inactive-icon-color);
44
+
45
+ /* Focus ring */
46
+ .focus-ring {
47
+ z-index: 3;
48
+ --focus-ring-container-shape-start-start: var(--shape-corner-small);
49
+ --focus-ring-container-shape-start-end: var(--shape-corner-small);
50
+ --focus-ring-container-shape-end-start: var(--shape-corner-small);
51
+ --focus-ring-container-shape-end-end: var(--shape-corner-small);
52
+ }
53
+
54
+ /* Active indicator (pill behind icon) */
55
+ .indicator {
56
+ position: relative;
57
+ display: flex;
58
+ align-items: center;
59
+ justify-content: center;
60
+ width: var(--_indicator-width);
61
+ height: var(--_indicator-height);
62
+ border-radius: var(--_indicator-shape);
63
+ overflow: hidden;
64
+ flex-shrink: 0;
65
+ transition: background-color var(--duration-short4, 200ms) var(--easing-standard, ease);
66
+
67
+ .icon-container {
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ z-index: 1;
72
+ position: relative;
73
+ pointer-events: none;
74
+
75
+ ::slotted(*) {
76
+ --icon-size: 1.5rem; /* 24dp */
77
+ --icon-color: var(--_inactive-icon-color);
78
+ color: var(--_inactive-icon-color);
79
+ display: flex;
80
+ }
81
+ }
82
+ }
83
+
84
+ /* State layer for hover/press */
85
+ .state-layer {
86
+ position: absolute;
87
+ top: 0.25rem;
88
+ left: 50%;
89
+ transform: translateX(-50%);
90
+ width: var(--_indicator-width);
91
+ height: var(--_indicator-height);
92
+ pointer-events: none;
93
+ background-color: var(--_state-color);
94
+ opacity: 0;
95
+ z-index: 0;
96
+ border-radius: var(--_indicator-shape);
97
+ transition: opacity var(--duration-short4, 200ms) var(--easing-standard, ease);
98
+ }
99
+
100
+ .ripple {
101
+ z-index: 1;
102
+ --ripple-pressed-color: var(--_state-color);
103
+ --ripple-state-opacity: 0;
104
+ border-radius: var(--shape-corner-small, 4px);
105
+ }
106
+
107
+ /* Label */
108
+ .label {
109
+ @include mixin.get-typography('label-medium');
110
+ color: var(--_inactive-label-color);
111
+ text-align: center;
112
+ pointer-events: none;
113
+ z-index: 1;
114
+ transition: color var(--duration-short4, 200ms) var(--easing-standard, ease),
115
+ font-weight var(--duration-short4, 200ms) var(--easing-standard, ease);
116
+ }
117
+
118
+ /* Active icon slot: hidden by default */
119
+ .active-icon-slot {
120
+ display: none;
121
+ }
122
+
123
+ .hidden-slot {
124
+ display: none;
125
+ }
126
+
127
+ /* Item content layout */
128
+ .item-content {
129
+ display: flex;
130
+ flex-direction: column;
131
+ align-items: center;
132
+ gap: 0.25rem;
133
+ width: 100%;
134
+ z-index: 1;
135
+ }
136
+
137
+ /* Active state */
138
+ &.active {
139
+ --_state-color: var(--_active-icon-color);
140
+
141
+ .indicator {
142
+ background-color: var(--_indicator-color);
143
+
144
+ .icon-container {
145
+ ::slotted(*) {
146
+ --icon-color: var(--_active-icon-color);
147
+ color: var(--_active-icon-color);
148
+ }
149
+ }
150
+ }
151
+
152
+ .label {
153
+ color: var(--_active-label-color);
154
+ font-weight: var(--typography-label-medium-font-weight-bold, 700);
155
+ }
156
+ }
157
+
158
+ /* Active icon slot: show when active and slot has content */
159
+ &.active.has-active-icon {
160
+ .active-icon-slot {
161
+ display: flex;
162
+ }
163
+
164
+ .icon-slot {
165
+ display: none;
166
+ }
167
+ }
168
+
169
+ /* Hover state */
170
+ &:hover:not(.disabled) {
171
+ .state-layer {
172
+ opacity: 0.08;
173
+ }
174
+ }
175
+
176
+ /* Pressed state */
177
+ &.pressed:not(.disabled) {
178
+ .state-layer {
179
+ opacity: 0.12;
180
+ }
181
+ }
182
+
183
+ /* Disabled state */
184
+ &.disabled {
185
+ cursor: not-allowed;
186
+
187
+ .indicator {
188
+ .icon-container {
189
+ ::slotted(*) {
190
+ --icon-color: var(--color-on-surface);
191
+ color: var(--color-on-surface);
192
+ opacity: 0.38;
193
+ }
194
+ }
195
+ }
196
+
197
+ .label {
198
+ color: var(--color-on-surface);
199
+ opacity: 0.38;
200
+ }
201
+
202
+ .ripple {
203
+ display: none;
204
+ }
205
+ }
206
+ }
207
+
208
+ @media (prefers-reduced-motion: reduce) {
209
+ .item {
210
+ .indicator,
211
+ .state-layer,
212
+ .label {
213
+ transition: none;
214
+ }
215
+ }
216
+ }