@redvars/peacock 3.3.3 → 3.4.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.
Files changed (155) hide show
  1. package/dist/IndividualComponent-DUINtMGK.js +67 -0
  2. package/dist/IndividualComponent-DUINtMGK.js.map +1 -0
  3. package/dist/assets/images/empty-state/no-document.svg +11 -12
  4. package/dist/assets/images/empty-state/page.svg +15 -9
  5. package/dist/bottom-sheet.js +238 -0
  6. package/dist/bottom-sheet.js.map +1 -0
  7. package/dist/{button-ClzS8JLq.js → button-COYCtuA8.js} +306 -149
  8. package/dist/button-COYCtuA8.js.map +1 -0
  9. package/dist/button-group-DsXquZQn.js +440 -0
  10. package/dist/button-group-DsXquZQn.js.map +1 -0
  11. package/dist/button-group.js +6 -4
  12. package/dist/button-group.js.map +1 -1
  13. package/dist/button.js +5 -3
  14. package/dist/button.js.map +1 -1
  15. package/dist/card-content.js +29 -0
  16. package/dist/card-content.js.map +1 -0
  17. package/dist/card.js +418 -44
  18. package/dist/card.js.map +1 -1
  19. package/dist/{chart-bar-DbnXQgvS.js → chart-bar-cn6rrna-.js} +2 -2
  20. package/dist/{chart-bar-DbnXQgvS.js.map → chart-bar-cn6rrna-.js.map} +1 -1
  21. package/dist/chart-bar.js +4 -3
  22. package/dist/chart-bar.js.map +1 -1
  23. package/dist/chart-doughnut.js +2 -1
  24. package/dist/chart-doughnut.js.map +1 -1
  25. package/dist/chart-pie.js +2 -1
  26. package/dist/chart-pie.js.map +1 -1
  27. package/dist/chart-stacked-bar.js +4 -3
  28. package/dist/chart-stacked-bar.js.map +1 -1
  29. package/dist/{class-map-59YGWLnx.js → class-map-3TAnCMAX.js} +3 -9
  30. package/dist/class-map-3TAnCMAX.js.map +1 -0
  31. package/dist/clock.js +2 -1
  32. package/dist/clock.js.map +1 -1
  33. package/dist/code-editor.js +6 -4
  34. package/dist/code-editor.js.map +1 -1
  35. package/dist/code-highlighter.js +5 -3
  36. package/dist/code-highlighter.js.map +1 -1
  37. package/dist/custom-elements-jsdocs.json +2458 -2753
  38. package/dist/custom-elements.json +2742 -757
  39. package/dist/dispatch-event-utils-B4odODQf.js.map +1 -1
  40. package/dist/index.js +14 -10
  41. package/dist/index.js.map +1 -1
  42. package/dist/number-counter.js +3 -2
  43. package/dist/number-counter.js.map +1 -1
  44. package/dist/{observe-theme-change-pALI5fmV.js → observe-theme-change-DKAIv5BB.js} +3 -2
  45. package/dist/observe-theme-change-DKAIv5BB.js.map +1 -0
  46. package/dist/peacock-loader.js +34 -8
  47. package/dist/peacock-loader.js.map +1 -1
  48. package/dist/property-1psGvXOq.js +10 -0
  49. package/dist/property-1psGvXOq.js.map +1 -0
  50. package/dist/{radio-b70_Ie9n.js → select-C3XAzenC.js} +1706 -192
  51. package/dist/select-C3XAzenC.js.map +1 -0
  52. package/dist/side-sheet.js +186 -0
  53. package/dist/side-sheet.js.map +1 -0
  54. package/dist/src/bottom-sheet/bottom-sheet.d.ts +42 -0
  55. package/dist/src/bottom-sheet/index.d.ts +1 -0
  56. package/dist/src/button/BaseButton.d.ts +4 -3
  57. package/dist/src/button/button/button.d.ts +4 -0
  58. package/dist/src/button/button-group/button-group.d.ts +32 -3
  59. package/dist/src/button/icon-button/icon-button.d.ts +4 -0
  60. package/dist/src/card/card-content.d.ts +15 -0
  61. package/dist/src/card/card.d.ts +37 -3
  62. package/dist/src/card/index.d.ts +1 -0
  63. package/dist/src/container/container.d.ts +1 -1
  64. package/dist/src/empty-state/empty-state.d.ts +1 -1
  65. package/dist/src/focus-ring/focus-ring.d.ts +4 -1
  66. package/dist/src/index.d.ts +6 -1
  67. package/dist/src/menu/menu/menu.d.ts +1 -0
  68. package/dist/src/menu/menu-item/menu-item.d.ts +0 -1
  69. package/dist/src/ripple/ripple.d.ts +19 -3
  70. package/dist/src/segmented-button/index.d.ts +2 -0
  71. package/dist/src/segmented-button/segmented-button-group.d.ts +46 -0
  72. package/dist/src/segmented-button/segmented-button.d.ts +65 -0
  73. package/dist/src/select/index.d.ts +3 -0
  74. package/dist/src/select/option.d.ts +55 -0
  75. package/dist/src/select/select.d.ts +116 -0
  76. package/dist/src/side-sheet/index.d.ts +1 -0
  77. package/dist/src/side-sheet/side-sheet.d.ts +41 -0
  78. package/dist/src/tabs/tab-group.d.ts +0 -1
  79. package/dist/src/tabs/tab.d.ts +8 -2
  80. package/dist/src/tabs/tabs.d.ts +13 -1
  81. package/dist/state-DwbEjqVk.js +10 -0
  82. package/dist/state-DwbEjqVk.js.map +1 -0
  83. package/dist/{style-map-DcB52w-l.js → style-map-CRFEoCEg.js} +2 -2
  84. package/dist/{style-map-DcB52w-l.js.map → style-map-CRFEoCEg.js.map} +1 -1
  85. package/dist/tsconfig.tsbuildinfo +1 -1
  86. package/dist/{unsafe-html-C2r3PyzF.js → unsafe-html-D3GHRaGQ.js} +2 -2
  87. package/dist/{unsafe-html-C2r3PyzF.js.map → unsafe-html-D3GHRaGQ.js.map} +1 -1
  88. package/package.json +1 -1
  89. package/readme.md +2 -2
  90. package/src/bottom-sheet/bottom-sheet.scss +88 -0
  91. package/src/bottom-sheet/bottom-sheet.ts +135 -0
  92. package/src/bottom-sheet/index.ts +1 -0
  93. package/src/button/BaseButton.ts +16 -7
  94. package/src/button/button/button-colors.scss +76 -5
  95. package/src/button/button/button-sizes.scss +39 -19
  96. package/src/button/button/button.scss +117 -116
  97. package/src/button/button/button.ts +23 -1
  98. package/src/button/button-group/button-group.scss +25 -22
  99. package/src/button/button-group/button-group.ts +121 -4
  100. package/src/button/icon-button/icon-button-sizes.scss +35 -15
  101. package/src/button/icon-button/icon-button.ts +21 -1
  102. package/src/card/card-colors.scss +10 -0
  103. package/src/card/card-content.ts +26 -0
  104. package/src/card/card.scss +221 -41
  105. package/src/card/card.ts +240 -7
  106. package/src/card/index.ts +1 -0
  107. package/src/code-editor/code-editor.ts +1 -1
  108. package/src/container/container.ts +1 -1
  109. package/src/empty-state/empty-state.scss +8 -0
  110. package/src/empty-state/empty-state.ts +2 -2
  111. package/src/focus-ring/focus-ring.ts +37 -19
  112. package/src/index.ts +7 -1
  113. package/src/menu/menu/menu.scss +24 -3
  114. package/src/menu/menu/menu.ts +23 -2
  115. package/src/menu/menu-item/menu-item.scss +1 -0
  116. package/src/menu/menu-item/menu-item.ts +1 -9
  117. package/src/peacock-loader.ts +28 -0
  118. package/src/ripple/ripple.ts +19 -3
  119. package/src/segmented-button/index.ts +2 -0
  120. package/src/segmented-button/segmented-button-group.scss +21 -0
  121. package/src/segmented-button/segmented-button-group.ts +110 -0
  122. package/src/segmented-button/segmented-button.scss +115 -0
  123. package/src/segmented-button/segmented-button.ts +175 -0
  124. package/src/select/index.ts +3 -0
  125. package/src/select/option.ts +109 -0
  126. package/src/select/select.scss +120 -0
  127. package/src/select/select.ts +486 -0
  128. package/src/side-sheet/index.ts +1 -0
  129. package/src/side-sheet/side-sheet.scss +79 -0
  130. package/src/side-sheet/side-sheet.ts +100 -0
  131. package/src/slider/slider.scss +0 -1
  132. package/src/tabs/demo/index.html +90 -0
  133. package/src/tabs/tab-group.ts +0 -3
  134. package/src/tabs/tab.scss +237 -25
  135. package/src/tabs/tab.ts +85 -11
  136. package/src/tabs/tabs.scss +37 -3
  137. package/src/tabs/tabs.ts +118 -2
  138. package/src/utils/dispatch-event-utils.ts +1 -0
  139. package/dist/IndividualComponent-Dt5xirYG.js +0 -73
  140. package/dist/IndividualComponent-Dt5xirYG.js.map +0 -1
  141. package/dist/button-ClzS8JLq.js.map +0 -1
  142. package/dist/button-group-BMS5WvaF.js +0 -292
  143. package/dist/button-group-BMS5WvaF.js.map +0 -1
  144. package/dist/chart-donut.js +0 -309
  145. package/dist/chart-donut.js.map +0 -1
  146. package/dist/class-map-59YGWLnx.js.map +0 -1
  147. package/dist/observe-theme-change-pALI5fmV.js.map +0 -1
  148. package/dist/radio-b70_Ie9n.js.map +0 -1
  149. package/dist/src/chart-donut/chart-donut.d.ts +0 -53
  150. package/dist/src/chart-donut/index.d.ts +0 -1
  151. package/dist/test/card.test.d.ts +0 -1
  152. package/src/chart-donut/chart-donut.scss +0 -37
  153. package/src/chart-donut/chart-donut.ts +0 -287
  154. package/src/chart-donut/demo/index.html +0 -51
  155. package/src/chart-donut/index.ts +0 -1
package/src/card/card.ts CHANGED
@@ -1,10 +1,14 @@
1
- import { LitElement, html } from 'lit';
2
- import { property } from 'lit/decorators.js';
1
+ import { LitElement, html, nothing, PropertyValues } from 'lit';
2
+ import { property, query, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import { dispatchActivationClick, isActivationClick } from '../utils/dispatch-event-utils.js';
5
+ import { observerSlotChangesWithCallback, throttle } from '../utils.js';
3
6
  import IndividualComponent from '../IndividualComponent.js';
4
7
  import styles from './card.scss';
8
+ import colorStyles from './card-colors.scss';
5
9
 
6
10
  type CardVariant = 'elevated' | 'filled' | 'outlined';
7
- type CardElevation = 0 | 1 | 2 | 3 | 4 | 5;
11
+
8
12
  /**
9
13
  * @label Card
10
14
  * @tag wc-card
@@ -24,15 +28,244 @@ type CardElevation = 0 | 1 | 2 | 3 | 4 | 5;
24
28
  */
25
29
  @IndividualComponent
26
30
  export class Card extends LitElement {
27
- static styles = [styles];
31
+ static styles = [styles, colorStyles];
28
32
 
33
+ #id = crypto.randomUUID();
34
+
29
35
  @property({ type: String, reflect: true })
30
36
  variant: CardVariant = 'elevated';
31
37
 
32
- @property({ type: Number, reflect: true })
33
- elevation: CardElevation = 1;
38
+ @property({type: Boolean, reflect: true})
39
+ disabled: boolean = false;
40
+
41
+ @property({ type: Boolean, reflect: true })
42
+ actionable: boolean = false;
43
+
44
+ /**
45
+ * If button is disabled, the reason why it is disabled.
46
+ */
47
+ @property({ attribute: 'disabled-reason' })
48
+ disabledReason: string = '';
49
+
50
+ /**
51
+ * Hyperlink to navigate to on click.
52
+ */
53
+ @property({ reflect: true }) href?: string;
54
+
55
+ /**
56
+ * Sets or retrieves the window or frame at which to target content.
57
+ */
58
+ @property() target: string = '_self';
59
+
60
+
61
+ /**
62
+ * Sets the delay for throttle in milliseconds. Defaults to 200 milliseconds.
63
+ */
64
+ @property() throttleDelay = 200;
65
+
66
+ /**
67
+ * States
68
+ */
69
+ @state()
70
+ isPressed = false;
71
+
72
+ @state()
73
+ private slotHasContent = false;
74
+
75
+
76
+ @query('.card') readonly cardElement!: HTMLElement | null;
77
+
78
+ @query('slot') readonly contentSlot!: HTMLSlotElement | null;
79
+
80
+ #tabindex?: number = 0;
81
+
82
+ #slottedTabIndexMap = new WeakMap<HTMLElement, string | null>();
83
+
84
+ override firstUpdated() {
85
+ this.__dispatchClickWithThrottle = throttle(
86
+ this.__dispatchClick,
87
+ this.throttleDelay,
88
+ );
89
+ observerSlotChangesWithCallback(
90
+ this.renderRoot.querySelector('slot'),
91
+ hasContent => {
92
+ this.slotHasContent = hasContent;
93
+ this.__syncSlottedChildrenTabIndex();
94
+ this.requestUpdate();
95
+ },
96
+ );
97
+ this.__syncSlottedChildrenTabIndex();
98
+ }
99
+
100
+ override updated(changedProperties: PropertyValues<this>) {
101
+ if (changedProperties.has('actionable') || changedProperties.has('href')) {
102
+ this.__syncSlottedChildrenTabIndex();
103
+ }
104
+ }
105
+
106
+ __syncSlottedChildrenTabIndex() {
107
+ if (!this.contentSlot) return;
108
+
109
+ const shouldDisableTabbing = this.actionable || this.__isLink();
110
+ const assignedChildren = this.contentSlot.assignedElements({ flatten: true });
111
+
112
+ assignedChildren.forEach(node => {
113
+ if (!(node instanceof HTMLElement)) return;
114
+
115
+ if (shouldDisableTabbing) {
116
+ if (!this.#slottedTabIndexMap.has(node)) {
117
+ this.#slottedTabIndexMap.set(node, node.getAttribute('tabindex'));
118
+ }
119
+
120
+ if (node.getAttribute('tabindex') !== '-1') {
121
+ node.setAttribute('tabindex', '-1');
122
+ }
123
+ return;
124
+ }
125
+
126
+ const originalTabIndex = this.#slottedTabIndexMap.get(node);
127
+ if (originalTabIndex === null) {
128
+ if (node.hasAttribute('tabindex')) {
129
+ node.removeAttribute('tabindex');
130
+ }
131
+ } else if (originalTabIndex !== undefined) {
132
+ if (node.getAttribute('tabindex') !== originalTabIndex) {
133
+ node.setAttribute('tabindex', originalTabIndex);
134
+ }
135
+ }
136
+
137
+ this.#slottedTabIndexMap.delete(node);
138
+ });
139
+ }
140
+
141
+ __dispatchClickWithThrottle: (event: MouseEvent | KeyboardEvent) => void =
142
+ event => {
143
+ this.__dispatchClick(event);
144
+ };
145
+
146
+ __dispatchClick = (event: MouseEvent | KeyboardEvent) => {
147
+ // If the button is soft-disabled or a disabled link, we need to explicitly
148
+ // prevent the click from propagating to other event listeners as well as
149
+ // prevent the default action.
150
+ if (this.disabled && this.href) {
151
+ event.stopImmediatePropagation();
152
+ event.preventDefault();
153
+ return;
154
+ }
155
+
156
+ if (!isActivationClick(event) || !this.cardElement) {
157
+ return;
158
+ }
159
+
160
+ this.focus();
161
+ dispatchActivationClick(this.cardElement);
162
+ };
163
+
164
+ __isLink() {
165
+ return !!this.href;
166
+ }
167
+
168
+ __getDisabledReasonID() {
169
+ return this.disabled && this.disabledReason
170
+ ? `disabled-reason-${this.#id}`
171
+ : nothing;
172
+ }
173
+
174
+ __renderDisabledReason() {
175
+ const disabledReasonID = this.__getDisabledReasonID();
176
+ if (disabledReasonID)
177
+ return html`<div
178
+ id="disabled-reason-${this.#id}"
179
+ role="tooltip"
180
+ aria-label=${this.disabledReason}
181
+ class="screen-reader-only"
182
+ >
183
+ ${this.disabledReason}
184
+ </div>`;
185
+ return nothing;
186
+ }
187
+
188
+ __handlePress = (event: KeyboardEvent | MouseEvent) => {
189
+ if (this.disabled) return;
190
+ if (
191
+ event instanceof KeyboardEvent &&
192
+ event.type === 'keydown' &&
193
+ (event.key === 'Enter' || event.key === ' ')
194
+ ) {
195
+ this.isPressed = true;
196
+ } else if (event.type === 'mousedown') {
197
+ this.isPressed = true;
198
+ } else {
199
+ this.isPressed = false;
200
+ }
201
+ };
202
+
203
+
204
+
34
205
 
35
206
  render() {
36
- return html`<div class="card"><slot></slot></div>`;
207
+
208
+ const isLink = this.__isLink();
209
+
210
+ const cssClasses = {
211
+ card: true,
212
+ 'card-element': true,
213
+ [`variant-${this.variant}`]: true,
214
+ actionable: (this.actionable && !this.disabled) || isLink,
215
+ disabled: this.disabled,
216
+ pressed: this.isPressed,
217
+ 'has-content': this.slotHasContent,
218
+ };
219
+
220
+ if (!isLink) {
221
+ return html`<button
222
+ class=${classMap(cssClasses)}
223
+ id="button"
224
+ tabindex=${this.#tabindex}
225
+ @click=${this.__dispatchClickWithThrottle}
226
+ @mousedown=${this.__handlePress}
227
+ @keydown=${this.__handlePress}
228
+ @keyup=${this.__handlePress}
229
+ ?aria-describedby=${this.__getDisabledReasonID()}
230
+ aria-disabled=${`${this.disabled}`}
231
+ ?disabled=${this.disabled}
232
+ >
233
+ ${this.renderCardContent()}
234
+ </button>`;
235
+ }
236
+ return html`<a
237
+ class=${classMap(cssClasses)}
238
+ id="button"
239
+ tabindex=${this.#tabindex}
240
+ href=${this.href}
241
+ target=${this.target}
242
+ @click=${this.__dispatchClickWithThrottle}
243
+ @mousedown=${this.__handlePress}
244
+ @keydown=${this.__handlePress}
245
+ @keyup=${this.__handlePress}
246
+ role="button"
247
+ ?aria-describedby=${this.__getDisabledReasonID()}
248
+ aria-disabled=${`${this.disabled}`}
249
+ >
250
+ ${this.renderCardContent()}
251
+ </a>`;
252
+ }
253
+
254
+ renderCardContent() {
255
+ return html`
256
+ <wc-focus-ring class="focus-ring" .control=${this} .forElement=${this.cardElement}></wc-focus-ring>
257
+ <wc-elevation class="elevation"></wc-elevation>
258
+ <div class="background"></div>
259
+ <div class="outline"></div>
260
+ <wc-ripple class="ripple"></wc-ripple>
261
+
262
+ <div class="card-content">
263
+
264
+ <div class="slot-container">
265
+ <slot @slotchange=${this.__syncSlottedChildrenTabIndex}></slot>
266
+ </div>
267
+
268
+ </div>
269
+ `;
37
270
  }
38
271
  }
package/src/card/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { Card } from './card.js';
2
+ export { CardContent } from './card-content.js';
@@ -21,7 +21,7 @@ import styles from './code-editor.scss';
21
21
  *
22
22
  * @example
23
23
  * ```html
24
- * <wc-code-editor language="javascript"></wc-code-editor>
24
+ * <wc-code-editor language="javascript" style="width: 80%;--code-editor-height: 9rem;height: 9rem;" ></wc-code-editor>
25
25
  * ```
26
26
  */
27
27
 
@@ -16,7 +16,7 @@ type ContainerSize = 'max' | 'xl' | 'lg' | 'md' | 'sm' | 'full';
16
16
  *
17
17
  * @example
18
18
  * ```html
19
- * <wc-container size="lg">Content</wc-container>
19
+ * <wc-container style="width: 80%; border: 1px dotted black;" size="md">Content</wc-container>
20
20
  * ```
21
21
  */
22
22
  export class Container extends LitElement {
@@ -64,6 +64,10 @@
64
64
  .content {
65
65
  width: 50%;
66
66
  }
67
+
68
+ .headline {
69
+ @include mixin.get-typography(title-medium);
70
+ }
67
71
  }
68
72
  }
69
73
 
@@ -79,6 +83,10 @@
79
83
  justify-content: center;
80
84
  }
81
85
  }
86
+
87
+ .headline {
88
+ @include mixin.get-typography(title-small);
89
+ }
82
90
  }
83
91
  }
84
92
 
@@ -12,7 +12,7 @@ import styles from './empty-state.scss';
12
12
  *
13
13
  * @example
14
14
  * ```html
15
- * <wc-empty-state headline="No items found"></wc-empty-state>
15
+ * <wc-empty-state width="80%" headline="No items found"></wc-empty-state>
16
16
  * ```
17
17
  */
18
18
  export class EmptyState extends LitElement {
@@ -52,7 +52,7 @@ export class EmptyState extends LitElement {
52
52
 
53
53
  __renderTitle() {
54
54
  if (!this.headline) return nothing;
55
- return html`<div class="title">${this.headline}</div>`;
55
+ return html`<div class="headline">${this.headline}</div>`;
56
56
  }
57
57
 
58
58
  __renderDescription() {
@@ -18,7 +18,7 @@ export class FocusRing extends LitElement {
18
18
 
19
19
  @property({ type: Boolean, reflect: true }) visible: boolean = false;
20
20
 
21
- @property({ type: String}) element = '';
21
+ @property({ type: String }) for = '';
22
22
 
23
23
 
24
24
  render() {
@@ -27,6 +27,8 @@ export class FocusRing extends LitElement {
27
27
 
28
28
  _control?: HTMLElement;
29
29
 
30
+ _focusTarget?: HTMLElement;
31
+
30
32
  get control() {
31
33
  return this._control || null;
32
34
  }
@@ -39,6 +41,15 @@ export class FocusRing extends LitElement {
39
41
  }
40
42
  }
41
43
 
44
+ set forElement(value: HTMLElement | null) {
45
+ if (value) {
46
+ this._focusTarget = value;
47
+ this.attach();
48
+ } else {
49
+ this.detach();
50
+ }
51
+ }
52
+
42
53
  connectedCallback() {
43
54
  super.connectedCallback();
44
55
  this.attach();
@@ -50,8 +61,8 @@ export class FocusRing extends LitElement {
50
61
  }
51
62
 
52
63
  __focusin() {
53
- // @ts-ignore
54
- this.visible = this._control[this.element].matches(':focus-visible') ?? false;
64
+ const focusTarget = this.__getFocusTarget();
65
+ this.visible = focusTarget?.matches(':focus-visible') ?? false;
55
66
  }
56
67
 
57
68
  __focusout() {
@@ -62,27 +73,34 @@ export class FocusRing extends LitElement {
62
73
  this.visible = false;
63
74
  }
64
75
 
76
+ __getFocusTarget(): HTMLElement | undefined {
77
+
78
+ if (this._focusTarget) {
79
+ return this._focusTarget;
80
+ }
81
+
82
+ const focusTarget = document.getElementById(this.for);
83
+ if (focusTarget) {
84
+ return focusTarget
85
+ }
86
+ return undefined;
87
+ }
88
+
65
89
  attach() {
66
- // @ts-ignore
67
- if (this._control && this._control[this.element]) {
68
- // @ts-ignore
69
- this._control[this.element].addEventListener('focusin', this.__focusin.bind(this));
70
- // @ts-ignore
71
- this._control[this.element].addEventListener('focusout', this.__focusin.bind(this));
72
- // @ts-ignore
73
- this._control[this.element].addEventListener('pointerdown', this.__focusin.bind(this));
90
+ const focusTarget = this.__getFocusTarget();
91
+ 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));
74
95
  }
75
96
  }
76
97
 
77
98
  detach() {
78
- // @ts-ignore
79
- if (this._control && this._control[this.element]) {
80
- // @ts-ignore
81
- this._control[this.element].removeEventListener('focusin', this.__focusin);
82
- // @ts-ignore
83
- this._control[this.element].removeEventListener('focusout', this.__focusout);
84
- // @ts-ignore
85
- this._control[this.element].removeEventListener('pointerdown', this.__pointerdown);
99
+ const focusTarget = this.__getFocusTarget();
100
+ 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));
86
104
  }
87
105
  this._control = undefined;
88
106
  }
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export { Divider } from './divider/index.js';
5
5
  export { Clock } from './clock/index.js';
6
6
  export { Elevation } from './elevation/index.js';
7
7
  export { Button, ButtonGroup, IconButton } from './button/index.js';
8
+ export { SegmentedButton, SegmentedButtonGroup } from './segmented-button/index.js';
8
9
 
9
10
  export { FocusRing } from './focus-ring/index.js';
10
11
  export { Ripple } from './ripple/index.js';
@@ -37,7 +38,6 @@ export { CodeEditor } from './code-editor/index.js';
37
38
  export { Image } from './image/index.js';
38
39
  export { Tab, TabGroup, TabPanel, Tabs } from './tabs/index.js';
39
40
  export { Slider } from './slider/index.js';
40
- export { ChartDonut } from './chart-donut/index.js';
41
41
  export { ChartDoughnut } from './chart-doughnut/index.js';
42
42
  export { ChartPie } from './chart-pie/index.js';
43
43
  export { ChartBar, ChartStackedBar } from './chart-bar/index.js';
@@ -47,3 +47,9 @@ export { TreeView, TreeNode } from './tree-view/index.js';
47
47
  export { Card } from './card/index.js';
48
48
  export { Snackbar } from './snackbar/index.js';
49
49
  export { Radio } from './radio/index.js';
50
+ export { BottomSheet } from './bottom-sheet/index.js';
51
+ export { SideSheet } from './side-sheet/index.js';
52
+ export { Select } from './select/index.js';
53
+ export type { SelectOption } from './select/index.js';
54
+ export { SelectOptionElement } from './select/index.js';
55
+
@@ -4,23 +4,37 @@
4
4
 
5
5
  .menu {
6
6
  display: flex;
7
- position: relative;
7
+ position: fixed;
8
8
  z-index: var(--menu-z-index, 1000);
9
9
  min-width: 112px;
10
10
  padding-block: var(--spacing-050);
11
+ transform-origin: top center;
12
+
13
+ --_menu-enter-duration: var(--duration-medium1, 250ms);
14
+ --_menu-exit-duration: var(--duration-short4, 200ms);
15
+ --_menu-enter-easing: cubic-bezier(0.05, 0.7, 0.1, 1);
16
+ --_menu-exit-easing: cubic-bezier(0.3, 0, 0.8, 0.15);
17
+
18
+ transition-property: opacity, transform, visibility;
19
+ transition-duration: var(--_menu-exit-duration), var(--_menu-exit-duration), 0ms;
20
+ transition-delay: 0ms, 0ms, var(--_menu-exit-duration);
21
+ transition-timing-function: var(--_menu-exit-easing), var(--_menu-exit-easing), linear;
11
22
 
12
23
  &.closed {
13
- display: none;
14
24
  opacity: 0;
15
25
  visibility: hidden;
16
26
  pointer-events: none;
27
+ transform: translateY(-4px) scale(0.97);
17
28
  }
18
29
 
19
30
  &.open {
20
- display: flex;
21
31
  opacity: 1;
22
32
  visibility: visible;
23
33
  pointer-events: auto;
34
+ transform: translateY(0) scale(1);
35
+ transition-duration: var(--_menu-enter-duration), var(--_menu-enter-duration), 0ms;
36
+ transition-delay: 0ms, 0ms, 0ms;
37
+ transition-timing-function: var(--_menu-enter-easing), var(--_menu-enter-easing), linear;
24
38
  }
25
39
 
26
40
  .menu-content {
@@ -73,6 +87,13 @@
73
87
  }
74
88
  }
75
89
 
90
+ @media (prefers-reduced-motion: reduce) {
91
+ .menu {
92
+ transition: none;
93
+ transform: none;
94
+ }
95
+ }
96
+
76
97
  .menu {
77
98
  --_container-shape-start-start: var(--shape-corner-large);
78
99
  --_container-shape-start-end: var(--shape-corner-large);
@@ -181,7 +181,6 @@ export class Menu extends LitElement {
181
181
  for (let index = 0; index < enabledItems.length; index += 1) {
182
182
  const currentItem = enabledItems[index];
183
183
  currentItem.tabIndex = index === this.activeIndex ? 0 : -1;
184
- currentItem.selected = index === this.activeIndex;
185
184
  }
186
185
  }
187
186
 
@@ -226,10 +225,23 @@ export class Menu extends LitElement {
226
225
  return this._enabledItems()[0] ?? null;
227
226
  }
228
227
 
228
+ private _ownsKeyboardEvent(event: KeyboardEvent) {
229
+ const path = event.composedPath();
230
+ const ownedItems = this.items;
231
+
232
+ return path.some(target => target instanceof MenuItem && ownedItems.includes(target));
233
+ }
234
+
229
235
  private _onItemActivate = (event: Event) => {
230
236
  const customEvent = event as CustomEvent<{ item: MenuItem }>;
237
+ const { item } = customEvent.detail;
238
+ const ownedItems = this.items;
239
+ if (!ownedItems.includes(item)) {
240
+ return;
241
+ }
242
+
231
243
  const enabledItems = this._enabledItems();
232
- const nextIndex = enabledItems.indexOf(customEvent.detail.item);
244
+ const nextIndex = enabledItems.indexOf(item);
233
245
  if (nextIndex >= 0) {
234
246
  this.activeIndex = nextIndex;
235
247
  this._syncRovingTabIndex();
@@ -238,10 +250,15 @@ export class Menu extends LitElement {
238
250
 
239
251
  private _onItemRequestClose = (event: Event) => {
240
252
  const customEvent = event as CustomEvent<{
253
+ item: MenuItem;
241
254
  reason: 'click-selection' | 'keydown';
242
255
  key?: string;
243
256
  }>;
244
257
 
258
+ if (!this.items.includes(customEvent.detail.item)) {
259
+ return;
260
+ }
261
+
245
262
  if (customEvent.defaultPrevented) {
246
263
  return;
247
264
  }
@@ -259,6 +276,10 @@ export class Menu extends LitElement {
259
276
  return;
260
277
  }
261
278
 
279
+ if (!this._ownsKeyboardEvent(event)) {
280
+ return;
281
+ }
282
+
262
283
  switch (event.key) {
263
284
  case 'ArrowDown':
264
285
  event.preventDefault();
@@ -4,6 +4,7 @@
4
4
 
5
5
  :host {
6
6
  padding-inline: var(--spacing-050);
7
+ outline: none;
7
8
  }
8
9
 
9
10
  .menu-item {
@@ -138,10 +138,6 @@ export class MenuItem extends LitElement {
138
138
  return !!this.href;
139
139
  }
140
140
 
141
- get focusTarget() {
142
- return this;
143
- }
144
-
145
141
  render() {
146
142
  const isLink = this.__isLink();
147
143
 
@@ -180,11 +176,7 @@ export class MenuItem extends LitElement {
180
176
 
181
177
  renderContent() {
182
178
  return html`
183
- <wc-focus-ring
184
- class="focus-ring"
185
- .control=${this}
186
- element="focusTarget"
187
- ></wc-focus-ring>
179
+ <wc-focus-ring class="focus-ring" .control=${this} .forElement=${this}></wc-focus-ring>
188
180
  <div class="background"></div>
189
181
  <wc-ripple class="ripple"></wc-ripple>
190
182
 
@@ -11,6 +11,8 @@ import { Divider } from './divider/divider.js';
11
11
  import { Button } from './button/button/button.js';
12
12
  import { ButtonGroup } from './button/button-group/button-group.js';
13
13
  import { IconButton } from './button/icon-button/icon-button.js';
14
+ import { SegmentedButton } from './segmented-button/segmented-button.js';
15
+ import { SegmentedButtonGroup } from './segmented-button/segmented-button-group.js';
14
16
  import { Input } from './input/input.js';
15
17
  import { Field } from './field/field.js';
16
18
  import { NumberField } from './number-field/number-field.js';
@@ -53,7 +55,12 @@ import { Table } from './table/table.js';
53
55
  import { Pagination } from './pagination/pagination.js';
54
56
  import { TreeView } from './tree-view/tree-view.js';
55
57
  import { Card } from './card/card.js';
58
+ import { CardContent } from './card/card-content.js';
56
59
  import { Snackbar } from './snackbar/snackbar.js';
60
+ import { BottomSheet } from './bottom-sheet/bottom-sheet.js';
61
+ import { SideSheet } from './side-sheet/side-sheet.js';
62
+ import { Select } from './select/select.js';
63
+ import { SelectOptionElement } from './select/option.js';
57
64
 
58
65
  const distDirectory = `${import.meta.url}/..`;
59
66
  await loadCSS(`${distDirectory}/assets/styles.css`);
@@ -102,6 +109,12 @@ const loaderConfig: LoaderConfig = {
102
109
  'wc-button-group': {
103
110
  CustomElementClass: ButtonGroup,
104
111
  },
112
+ 'wc-segmented-button': {
113
+ CustomElementClass: SegmentedButton,
114
+ },
115
+ 'wc-segmented-button-group': {
116
+ CustomElementClass: SegmentedButtonGroup,
117
+ },
105
118
  'wc-divider': {
106
119
  CustomElementClass: Divider,
107
120
  },
@@ -141,6 +154,9 @@ const loaderConfig: LoaderConfig = {
141
154
  'wc-card': {
142
155
  CustomElementClass: Card,
143
156
  },
157
+ 'wc-card-content': {
158
+ CustomElementClass: CardContent,
159
+ },
144
160
  'wc-tag': {
145
161
  CustomElementClass: Tag,
146
162
  },
@@ -238,6 +254,18 @@ const loaderConfig: LoaderConfig = {
238
254
  'wc-snackbar': {
239
255
  CustomElementClass: Snackbar,
240
256
  },
257
+ 'wc-bottom-sheet': {
258
+ CustomElementClass: BottomSheet,
259
+ },
260
+ 'wc-side-sheet': {
261
+ CustomElementClass: SideSheet,
262
+ },
263
+ 'wc-select': {
264
+ CustomElementClass: Select,
265
+ },
266
+ 'wc-option': {
267
+ CustomElementClass: SelectOptionElement,
268
+ },
241
269
  'wc-chart-doughnut': {
242
270
  importPath: `${distDirectory}/chart-doughnut.js`,
243
271
  },
@@ -91,10 +91,26 @@ const FORCED_COLORS = window.matchMedia('(forced-colors: active)');
91
91
  *
92
92
  * @example
93
93
  * ```html
94
- * <button style="position: relative;">
94
+ * <style>
95
+ * .ripple-surface {
96
+ * position: relative;
97
+ * display: inline-flex;
98
+ * align-items: center;
99
+ * justify-content: center;
100
+ * width: 220px;
101
+ * height: 64px;
102
+ * border-radius: 12px;
103
+ * background: var(--color-surface-container-high);
104
+ * color: var(--color-on-surface);
105
+ * overflow: hidden;
106
+ * cursor: pointer;
107
+ * user-select: none;
108
+ * }
109
+ * </style>
110
+ * <div class="ripple-surface">
95
111
  * <wc-ripple></wc-ripple>
96
- * Click me
97
- * </button>
112
+ * Ripple Effect
113
+ * </div>
98
114
  * ```
99
115
  * @tags display
100
116
  */