@redvars/peacock 3.6.2 → 3.7.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 (200) hide show
  1. package/dist/ButtonConstants-D06bY4uy.js +114 -0
  2. package/dist/ButtonConstants-D06bY4uy.js.map +1 -0
  3. package/dist/{BaseHyperlinkMixin-BNuwbiEf.js → NativeHyperlinkMixin-DrYXyfMQ.js} +8 -10
  4. package/dist/NativeHyperlinkMixin-DrYXyfMQ.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/button-colors-Dwnez1tR.js +586 -0
  10. package/dist/button-colors-Dwnez1tR.js.map +1 -0
  11. package/dist/button-group.js +8 -8
  12. package/dist/button-group.js.map +1 -1
  13. package/dist/button.js +236 -133
  14. package/dist/button.js.map +1 -1
  15. package/dist/calendar-column-view.js +0 -1
  16. package/dist/calendar-column-view.js.map +1 -1
  17. package/dist/calendar-month-view.js +0 -1
  18. package/dist/calendar-month-view.js.map +1 -1
  19. package/dist/canvas.js +126 -107
  20. package/dist/canvas.js.map +1 -1
  21. package/dist/card-content.js +0 -1
  22. package/dist/card-content.js.map +1 -1
  23. package/dist/card.js +96 -90
  24. package/dist/card.js.map +1 -1
  25. package/dist/cb-compound-expression.js +4 -1
  26. package/dist/cb-compound-expression.js.map +1 -1
  27. package/dist/cb-divider.js +0 -1
  28. package/dist/cb-divider.js.map +1 -1
  29. package/dist/cb-expression.js +0 -2
  30. package/dist/cb-expression.js.map +1 -1
  31. package/dist/cb-predicate.js +0 -1
  32. package/dist/cb-predicate.js.map +1 -1
  33. package/dist/code-highlighter.js +23 -6
  34. package/dist/code-highlighter.js.map +1 -1
  35. package/dist/custom-elements-jsdocs.json +5079 -17882
  36. package/dist/custom-elements.json +19272 -19314
  37. package/dist/fab.js +181 -117
  38. package/dist/fab.js.map +1 -1
  39. package/dist/flow-designer.js +4 -4
  40. package/dist/icon-button-DJ0kZXYr.js +318 -0
  41. package/dist/icon-button-DJ0kZXYr.js.map +1 -0
  42. package/dist/index.js +7 -7
  43. package/dist/{navigation-rail-CD7IrqbN.js → navigation-rail-CM_svs5_.js} +1311 -730
  44. package/dist/navigation-rail-CM_svs5_.js.map +1 -0
  45. package/dist/observe-slot-change-D8Xg-kSS.js +60 -0
  46. package/dist/observe-slot-change-D8Xg-kSS.js.map +1 -0
  47. package/dist/peacock-loader.js +10 -7
  48. package/dist/peacock-loader.js.map +1 -1
  49. package/dist/popover-content.js +0 -1
  50. package/dist/popover-content.js.map +1 -1
  51. package/dist/search.js +15 -15
  52. package/dist/search.js.map +1 -1
  53. package/dist/src/__controllers/attachable-controller.d.ts +109 -0
  54. package/dist/src/__mixins/{BaseButtonMixin.d.ts → NativeButtonMixin.d.ts} +3 -3
  55. package/dist/src/__mixins/{BaseHyperlinkMixin.d.ts → NativeHyperlinkMixin.d.ts} +3 -4
  56. package/dist/src/__utils/is-link.d.ts +1 -0
  57. package/dist/src/__utils/observe-slot-change.d.ts +1 -1
  58. package/dist/src/accordion/accordion-item.d.ts +0 -1
  59. package/dist/src/breadcrumb/breadcrumb-item/breadcrumb-item.d.ts +0 -1
  60. package/dist/src/button/ButtonConstants.d.ts +1 -0
  61. package/dist/src/button/GroupButtonInterface.d.ts +4 -0
  62. package/dist/src/button/button/button.d.ts +32 -7
  63. package/dist/src/button/button-group/button-group.d.ts +2 -1
  64. package/dist/src/button/icon-button/icon-button.d.ts +26 -5
  65. package/dist/src/button/index.d.ts +1 -1
  66. package/dist/src/calendar/calendar-column-view.d.ts +0 -1
  67. package/dist/src/calendar/calendar-month-view.d.ts +0 -1
  68. package/dist/src/canvas/canvas.d.ts +3 -3
  69. package/dist/src/card/card-content.d.ts +0 -1
  70. package/dist/src/card/card.d.ts +9 -6
  71. package/dist/src/chip/chip/chip.d.ts +22 -3
  72. package/dist/src/condition-builder/cb-compound-expression.d.ts +0 -1
  73. package/dist/src/condition-builder/cb-divider.d.ts +0 -1
  74. package/dist/src/condition-builder/cb-expression.d.ts +0 -1
  75. package/dist/src/condition-builder/cb-predicate.d.ts +0 -1
  76. package/dist/src/fab/fab.d.ts +20 -6
  77. package/dist/src/field/field.d.ts +1 -0
  78. package/dist/src/focus-ring/focus-ring.d.ts +26 -20
  79. package/dist/src/image/image.d.ts +2 -2
  80. package/dist/src/index.d.ts +1 -0
  81. package/dist/src/input/input.d.ts +1 -3
  82. package/dist/src/item/index.d.ts +1 -0
  83. package/dist/src/item/item.d.ts +49 -0
  84. package/dist/src/link/link.d.ts +1 -1
  85. package/dist/src/list/list-item.d.ts +1 -2
  86. package/dist/src/menu/menu-item/menu-item.d.ts +9 -11
  87. package/dist/src/menu/sub-menu/sub-menu.d.ts +1 -1
  88. package/dist/src/navigation-rail/navigation-rail-item.d.ts +0 -2
  89. package/dist/src/navigation-rail/navigation-rail.d.ts +2 -6
  90. package/dist/src/popover/popover-content.d.ts +0 -1
  91. package/dist/src/ripple/ripple.d.ts +9 -1
  92. package/dist/src/search/search.d.ts +2 -6
  93. package/dist/src/segmented-button/segmented-button.d.ts +0 -1
  94. package/dist/src/select/option.d.ts +0 -1
  95. package/dist/src/sidebar-menu/sidebar-menu-item.d.ts +0 -1
  96. package/dist/src/sidebar-menu/sidebar-sub-menu.d.ts +0 -1
  97. package/dist/src/tabs/tab-panel.d.ts +0 -1
  98. package/dist/src/tabs/tab.d.ts +4 -6
  99. package/dist/test/item.test.d.ts +1 -0
  100. package/dist/tsconfig.tsbuildinfo +1 -1
  101. package/package.json +4 -2
  102. package/readme.md +2 -2
  103. package/scss/components.scss +0 -1
  104. package/scss/mixin.scss +33 -13
  105. package/scss/styles.scss +1 -3
  106. package/src/__controllers/attachable-controller.ts +198 -0
  107. package/src/__mixins/NativeButtonMixin.ts +87 -0
  108. package/src/__mixins/{BaseHyperlinkMixin.ts → NativeHyperlinkMixin.ts} +15 -15
  109. package/src/__utils/is-link.ts +3 -0
  110. package/src/__utils/observe-slot-change.ts +46 -14
  111. package/src/accordion/accordion-item.scss +1 -1
  112. package/src/accordion/accordion-item.ts +0 -1
  113. package/src/breadcrumb/breadcrumb-item/breadcrumb-item.ts +0 -1
  114. package/src/button/ButtonConstants.ts +1 -0
  115. package/src/button/GroupButtonInterface.ts +4 -0
  116. package/src/button/button/button-colors.scss +2 -2
  117. package/src/button/button/button-layers.scss +124 -0
  118. package/src/button/button/button-sizes.scss +31 -53
  119. package/src/button/button/button.scss +139 -262
  120. package/src/button/button/button.ts +260 -106
  121. package/src/button/button/only-button.scss +13 -0
  122. package/src/button/button-group/button-group.ts +59 -17
  123. package/src/button/icon-button/icon-button-sizes.scss +12 -27
  124. package/src/button/icon-button/icon-button.ts +191 -83
  125. package/src/button/index.ts +1 -1
  126. package/src/calendar/calendar-column-view.ts +0 -1
  127. package/src/calendar/calendar-month-view.ts +0 -1
  128. package/src/canvas/canvas.scss +18 -6
  129. package/src/canvas/canvas.ts +125 -103
  130. package/src/card/card-content.ts +2 -3
  131. package/src/card/card.scss +87 -95
  132. package/src/card/card.ts +62 -60
  133. package/src/chip/chip/chip.scss +66 -71
  134. package/src/chip/chip/chip.ts +155 -56
  135. package/src/code-highlighter/code-highlighter.scss +1 -1
  136. package/src/code-highlighter/code-highlighter.ts +20 -5
  137. package/src/condition-builder/cb-compound-expression.scss +4 -0
  138. package/src/condition-builder/cb-compound-expression.ts +0 -1
  139. package/src/condition-builder/cb-divider.ts +0 -1
  140. package/src/condition-builder/cb-expression.scss +0 -1
  141. package/src/condition-builder/cb-expression.ts +0 -1
  142. package/src/condition-builder/cb-predicate.ts +0 -1
  143. package/src/elevation/elevation.scss +5 -1
  144. package/src/empty-state/empty-state.scss +1 -0
  145. package/src/fab/fab-colors.scss +2 -2
  146. package/src/fab/fab-sizes.scss +24 -34
  147. package/src/fab/fab.scss +77 -71
  148. package/src/fab/fab.ts +141 -65
  149. package/src/field/field.ts +6 -0
  150. package/src/focus-ring/focus-ring.ts +81 -72
  151. package/src/image/image.scss +21 -16
  152. package/src/image/image.ts +13 -14
  153. package/src/index.ts +1 -0
  154. package/src/input/input.ts +16 -25
  155. package/src/item/index.ts +1 -0
  156. package/src/item/item.scss +195 -0
  157. package/src/item/item.ts +362 -0
  158. package/src/link/link.scss +1 -10
  159. package/src/link/link.ts +4 -2
  160. package/src/list/list-item.ts +8 -8
  161. package/src/menu/menu/menu.ts +5 -9
  162. package/src/menu/menu-item/menu-item.scss +30 -108
  163. package/src/menu/menu-item/menu-item.ts +102 -133
  164. package/src/menu/sub-menu/sub-menu.ts +6 -3
  165. package/src/navigation-rail/navigation-rail-item.scss +5 -0
  166. package/src/navigation-rail/navigation-rail-item.ts +10 -15
  167. package/src/navigation-rail/navigation-rail.ts +2 -6
  168. package/src/peacock-loader.ts +5 -1
  169. package/src/popover/popover-content.ts +0 -1
  170. package/src/ripple/ripple.ts +52 -20
  171. package/src/search/search.scss +3 -0
  172. package/src/search/search.ts +11 -16
  173. package/src/segmented-button/segmented-button.ts +0 -1
  174. package/src/select/option.ts +1 -2
  175. package/src/select/select.scss +1 -10
  176. package/src/select/select.ts +2 -0
  177. package/src/sidebar-menu/sidebar-menu-item.ts +0 -1
  178. package/src/sidebar-menu/sidebar-sub-menu.ts +0 -1
  179. package/src/skeleton/skeleton.scss +5 -1
  180. package/src/tabs/tab-panel.ts +0 -1
  181. package/src/tabs/tab.ts +60 -70
  182. package/src/text/text.css-component.scss +3 -21
  183. package/src/tooltip/tooltip.scss +5 -8
  184. package/src/tooltip/tooltip.ts +1 -2
  185. package/dist/BaseButton-BNFAYn-S.js +0 -219
  186. package/dist/BaseButton-BNFAYn-S.js.map +0 -1
  187. package/dist/BaseHyperlinkMixin-BNuwbiEf.js.map +0 -1
  188. package/dist/button-colors-AvGh22Zn.js +0 -561
  189. package/dist/button-colors-AvGh22Zn.js.map +0 -1
  190. package/dist/icon-button-ohxHhy4t.js +0 -247
  191. package/dist/icon-button-ohxHhy4t.js.map +0 -1
  192. package/dist/navigation-rail-CD7IrqbN.js.map +0 -1
  193. package/dist/observe-slot-change-BGJfgg2E.js +0 -31
  194. package/dist/observe-slot-change-BGJfgg2E.js.map +0 -1
  195. package/dist/src/button/BaseButton.d.ts +0 -28
  196. package/dist/src/focus-ring/FocusAttachableController.d.ts +0 -8
  197. package/src/__mixins/BaseButtonMixin.ts +0 -83
  198. package/src/button/BaseButton.ts +0 -113
  199. package/src/focus-ring/FocusAttachableController.ts +0 -28
  200. package/src/popover/tooltip.css-component.scss +0 -19
@@ -1,15 +1,26 @@
1
- import { html } from 'lit';
2
- import { property, state } from 'lit/decorators.js';
1
+ import { html, LitElement, nothing } from 'lit';
2
+ import { property } 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
+ import { when } from 'lit/directives/when.js';
5
6
  import IndividualComponent from '@/IndividualComponent.js';
6
7
  import styles from './button.scss';
8
+ import onlyButton from './only-button.scss';
9
+ import buttonLayers from './button-layers.scss';
7
10
  import colorStyles from './button-colors.scss';
8
11
  import sizeStyles from './button-sizes.scss';
9
- import { observerSlotChangesWithCallback } from '@/__utils/observe-slot-change.js';
10
12
  import { throttle } from '@/__utils/throttle.js';
11
13
  import { spread } from '@/__directive/spread.js';
12
- import { BaseButton } from '../BaseButton.js';
14
+ import { isLink } from '@/__utils/is-link.js';
15
+ import { observerSlotChangesWithCallback } from '@/__utils/observe-slot-change.js';
16
+ import NativeButtonMixin from '@/__mixins/NativeButtonMixin.js';
17
+ import NativeHyperlinkMixin from '@/__mixins/NativeHyperlinkMixin.js';
18
+ import { GroupButtonInterface } from '@/button/GroupButtonInterface.js';
19
+ import {
20
+ dispatchActivationClick,
21
+ isActivationClick,
22
+ } from '@/__utils/dispatch-event-utils.js';
23
+ import { DISABLED_REASON_ID } from '@/button/ButtonConstants.js';
13
24
 
14
25
  /**
15
26
  * @label Button
@@ -54,15 +65,24 @@ import { BaseButton } from '../BaseButton.js';
54
65
  * @tags display
55
66
  */
56
67
  @IndividualComponent
57
- export class Button extends BaseButton {
58
- static override styles = [styles, colorStyles, sizeStyles];
68
+ export class Button
69
+ extends NativeButtonMixin(NativeHyperlinkMixin(LitElement))
70
+ implements GroupButtonInterface
71
+ {
72
+ static override styles = [
73
+ buttonLayers,
74
+ styles,
75
+ onlyButton,
76
+ sizeStyles,
77
+ colorStyles,
78
+ ];
59
79
 
60
80
  /**
61
81
  * Icon alignment.
62
82
  * Possible values are `"start"`, `"end"`. Defaults to `"end"`.
63
83
  */
64
- @property({ attribute: 'icon-align' })
65
- iconAlign: 'start' | 'end' = 'end';
84
+ @property({ type: Boolean, reflect: true, attribute: 'trailing-icon' })
85
+ trailingIcon = false;
66
86
 
67
87
  /**
68
88
  * Button size.
@@ -71,39 +91,39 @@ export class Button extends BaseButton {
71
91
  @property({ reflect: true }) size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' = 'sm';
72
92
 
73
93
  /**
74
- * Type is preset of color and variant. Type will be only applied.
75
- *
76
- */
77
- @property({ type: String }) type?: 'primary' | 'secondary' | 'tertiary';
78
-
79
- /**
80
- * The visual style of the button.
81
- *
82
- * Possible variant values:
83
- * `"filled"` is a filled button.
84
- * `"outlined"` is an outlined button.
85
- * `"text"` is a transparent button.
86
- * `"tonal"` is a light color button.
87
- * `"elevated"` is elevated button
88
- */
89
- @property() variant:
90
- | 'elevated'
91
- | 'filled'
92
- | 'tonal'
93
- | 'outlined'
94
- | 'text'
95
- | 'neo' = 'filled';
96
-
97
- /**
98
- * Defines the primary color of the button. This can be set to predefined color names to apply specific color themes.
99
- */
100
- @property({ reflect: true }) color:
101
- | 'primary'
102
- | 'success'
103
- | 'danger'
104
- | 'warning'
105
- | 'surface'
106
- | 'on-surface' = 'primary';
94
+ * Type is preset of color and variant. Type will be only applied.
95
+ *
96
+ */
97
+ @property({ type: String }) type?: 'primary' | 'secondary' | 'tertiary';
98
+
99
+ /**
100
+ * The visual style of the button.
101
+ *
102
+ * Possible variant values:
103
+ * `"filled"` is a filled button.
104
+ * `"outlined"` is an outlined button.
105
+ * `"text"` is a transparent button.
106
+ * `"tonal"` is a light color button.
107
+ * `"elevated"` is elevated button
108
+ */
109
+ @property({ reflect: true }) variant:
110
+ | 'elevated'
111
+ | 'filled'
112
+ | 'tonal'
113
+ | 'outlined'
114
+ | 'text'
115
+ | 'neo' = 'filled';
116
+
117
+ /**
118
+ * Defines the primary color of the button. This can be set to predefined color names to apply specific color themes.
119
+ */
120
+ @property({ reflect: true }) color:
121
+ | 'primary'
122
+ | 'success'
123
+ | 'danger'
124
+ | 'warning'
125
+ | 'surface'
126
+ | 'on-surface' = 'primary';
107
127
 
108
128
  /**
109
129
  * Additional ARIA attributes to pass to the inner button/anchor element.
@@ -111,15 +131,12 @@ export class Button extends BaseButton {
111
131
  @property({ reflect: true })
112
132
  configAria?: { [key: string]: any };
113
133
 
114
- @state()
115
- private slotHasContent = false;
116
-
117
134
  override focus() {
118
- this.buttonElement?.focus();
135
+ this.getControl()?.focus();
119
136
  }
120
137
 
121
138
  override blur() {
122
- this.buttonElement?.blur();
139
+ this.getControl()?.blur();
123
140
  }
124
141
 
125
142
  override firstUpdated() {
@@ -127,15 +144,34 @@ export class Button extends BaseButton {
127
144
  this.__dispatchClick,
128
145
  this.throttleDelay,
129
146
  );
130
- observerSlotChangesWithCallback(
131
- this.renderRoot.querySelector('slot'),
132
- hasContent => {
133
- this.slotHasContent = hasContent;
134
- this.requestUpdate();
135
- },
136
- );
137
147
 
138
148
  this.__convertTypeToVariantAndColor();
149
+ // Initialize slot presence tracking for smooth transitions when label/icon are added/removed
150
+ const iconSlot = this.renderRoot.querySelector(
151
+ 'slot[name="icon"]',
152
+ ) as HTMLSlotElement | null;
153
+ const labelSlot = this.renderRoot.querySelector(
154
+ 'slot.label',
155
+ ) as HTMLSlotElement | null;
156
+
157
+ // Use MutationObserver-based helper so we react to content/character changes
158
+ if (iconSlot) {
159
+ this.__iconSlotCleanup = observerSlotChangesWithCallback(
160
+ iconSlot,
161
+ has => {
162
+ this.toggleAttribute('has-icon', has);
163
+ },
164
+ );
165
+ }
166
+
167
+ if (labelSlot) {
168
+ this.__labelSlotCleanup = observerSlotChangesWithCallback(
169
+ labelSlot,
170
+ has => {
171
+ this.toggleAttribute('has-label', has);
172
+ },
173
+ );
174
+ }
139
175
  }
140
176
 
141
177
  __convertTypeToVariantAndColor() {
@@ -155,82 +191,200 @@ export class Button extends BaseButton {
155
191
  }
156
192
 
157
193
  override render() {
194
+ const buttonId = isLink(this) ? 'link' : 'button';
195
+
196
+ return html`
197
+ <wc-focus-ring class="focus-ring" for=${buttonId}></wc-focus-ring>
198
+ <wc-elevation class="elevation"></wc-elevation>
199
+ ${when(
200
+ this.variant === 'neo',
201
+ () => html`<div class="neo-background"></div>`,
202
+ )}
203
+ <div class="background"></div>
204
+ ${when(
205
+ this.variant === 'outlined' || this.variant === 'neo',
206
+ () => html`<div class="outline"></div>`,
207
+ )}
208
+ <wc-ripple class="ripple" for=${buttonId}></wc-ripple>
209
+ <wc-skeleton class="skeleton"></wc-skeleton>
158
210
 
159
- const cssClasses = {
211
+ ${this.renderButtonElement()} ${this.__renderTooltip()}
212
+ `;
213
+ }
214
+
215
+ renderButtonElement() {
216
+ const isElementLink = isLink(this);
217
+
218
+ const cssClasses: any = {
160
219
  button: true,
161
- 'button-element': true,
162
- [`size-${this.size}`]: true,
163
- [`variant-${this.variant}`]: true,
164
- [`color-${this.color}`]: true,
165
- disabled: this.disabled || this.softDisabled,
166
- pressed: this.isPressed,
167
- 'has-content': this.slotHasContent,
168
- 'show-skeleton': this.skeleton,
169
- [`icon-align-${this.iconAlign}`]: true,
220
+ 'native-button': !isElementLink,
221
+ 'native-link': isElementLink,
222
+ 'trailing-icon': this.trailingIcon,
170
223
  };
171
224
 
172
- if (!this.__isLink()) {
173
- return html`<button
174
- class=${classMap(cssClasses)}
175
- id="button"
176
- type=${this.htmlType}
177
- @click=${this.__dispatchClickWithThrottle}
178
- @mousedown=${this.__handlePress}
179
- @keydown=${this.__handlePress}
180
- @keyup=${this.__handlePress}
181
-
182
- aria-describedby=${ifDefined(this.softDisabled ? BaseButton.DISABLED_REASON_ID : undefined)}
183
- ?aria-disabled=${this.softDisabled}
184
-
185
- ?disabled=${this.disabled}
186
- ${spread(this.configAria)}
187
- >
188
- ${this.renderButtonContent()}
189
- </button>
190
- ${this.__renderTooltip()}`;
191
- }
192
- return html`<a
225
+ if (isElementLink) {
226
+ return html`<a
193
227
  class=${classMap(cssClasses)}
194
- id="button"
228
+ id="link"
195
229
  href=${this.href}
196
230
  target=${this.target}
197
231
  tabindex=${this.disabled ? '-1' : '0'}
198
-
199
232
  @click=${this.__dispatchClick}
200
233
  @mousedown=${this.__handlePress}
201
234
  @keydown=${this.__handlePress}
202
235
  @keyup=${this.__handlePress}
203
236
  role="button"
204
-
205
- aria-describedby=${ifDefined(this.softDisabled ? BaseButton.DISABLED_REASON_ID : undefined)}
237
+ aria-describedby=${ifDefined(
238
+ this.softDisabled ? DISABLED_REASON_ID : undefined,
239
+ )}
206
240
  ?aria-disabled=${this.softDisabled}
207
-
208
241
  ${spread(this.configAria)}
209
242
  >
210
243
  ${this.renderButtonContent()}
211
- </a>
244
+ </a>`;
245
+ }
246
+ return html`<button
247
+ class=${classMap(cssClasses)}
248
+ id="button"
249
+ type=${this.htmlType}
250
+ @click=${this.__dispatchClickWithThrottle}
251
+ @mousedown=${this.__handlePress}
252
+ @keydown=${this.__handlePress}
253
+ @keyup=${this.__handlePress}
254
+ aria-describedby=${ifDefined(
255
+ this.softDisabled ? DISABLED_REASON_ID : undefined,
256
+ )}
257
+ ?aria-disabled=${this.softDisabled}
258
+ ?disabled=${this.disabled}
259
+ ${spread(this.configAria)}
260
+ >
261
+ ${this.renderButtonContent()}
262
+ </button>
212
263
  ${this.__renderTooltip()}`;
213
264
  }
214
265
 
215
266
  renderButtonContent() {
216
- return html`
217
- <wc-focus-ring class="focus-ring" for='button'></wc-focus-ring>
218
- <wc-elevation class="elevation"></wc-elevation>
219
- <div class="neo-background"></div>
220
- <div class="background"></div>
221
- <div class="outline"></div>
222
- <wc-ripple class="ripple"></wc-ripple>
223
- <wc-skeleton class="skeleton"></wc-skeleton>
267
+ return html` <slot class="icon-slot" name="icon"></slot>
268
+ <slot class="label"></slot>
269
+ <div class="touch"></div>
224
270
 
225
- <div class="button-content">
226
- <div class="slot-container">
227
- <slot></slot>
228
- </div>
271
+ ${this.__renderDisabledReason(this.softDisabled)}`;
272
+ }
229
273
 
230
- <slot name="icon"></slot>
231
- </div>
274
+ @property({ type: Boolean, reflect: true }) skeleton: boolean = false;
232
275
 
233
- ${this.__renderDisabledReason(this.softDisabled)}
234
- `;
276
+ @property({ type: Boolean, reflect: true }) toggle: boolean = false;
277
+
278
+ @property({ type: Boolean, reflect: true }) selected: boolean = false;
279
+
280
+ /**
281
+ * Sets the delay for throttle in milliseconds. Defaults to 200 milliseconds.
282
+ */
283
+ @property() throttleDelay = 200;
284
+
285
+ @property() tooltip?: string;
286
+
287
+ /**
288
+ * States
289
+ */
290
+ @property({ type: Boolean, reflect: true })
291
+ pressed = false;
292
+
293
+ // Query the internal control (button or link) on demand instead of
294
+ // keeping a persistent query reference.
295
+
296
+ // cleanup functions returned by observerSlotChangesWithCallback
297
+ private __iconSlotCleanup: (() => void) | null = null;
298
+
299
+ private __labelSlotCleanup: (() => void) | null = null;
300
+
301
+ override connectedCallback() {
302
+ super.connectedCallback();
303
+ this.addEventListener('click', this.__dispatchClickWithThrottle);
304
+ window.addEventListener('mouseup', this.__handlePress);
305
+ }
306
+
307
+ override disconnectedCallback() {
308
+ // disconnect slot observers first to avoid callbacks during teardown
309
+ try {
310
+ this.__iconSlotCleanup?.();
311
+ } catch (e) {
312
+ /* ignore */
313
+ }
314
+ try {
315
+ this.__labelSlotCleanup?.();
316
+ } catch (e) {
317
+ /* ignore */
318
+ }
319
+
320
+ window.removeEventListener('mouseup', this.__handlePress);
321
+ this.removeEventListener('click', this.__dispatchClickWithThrottle);
322
+ super.disconnectedCallback();
323
+ }
324
+
325
+ __handlePress = (event: KeyboardEvent | MouseEvent) => {
326
+ if (this.disabled || this.skeleton || this.softDisabled) return;
327
+ this.pressed =
328
+ (event instanceof KeyboardEvent &&
329
+ event.type === 'keydown' &&
330
+ (event.key === 'Enter' || event.key === ' ')) ||
331
+ event.type === 'mousedown';
332
+ };
333
+
334
+ __dispatchClickWithThrottle: (event: MouseEvent | KeyboardEvent) => void =
335
+ event => {
336
+ this.__dispatchClick(event);
337
+ };
338
+
339
+ __dispatchClick = (event: MouseEvent | KeyboardEvent) => {
340
+ // If the button is soft-disabled or a disabled link, we need to explicitly
341
+ // prevent the click from propagating to other event listeners as well as
342
+ // prevent the default action.
343
+ if (this.softDisabled || (this.disabled && this.href) || this.skeleton) {
344
+ event.stopImmediatePropagation();
345
+ event.preventDefault();
346
+ return;
347
+ }
348
+
349
+ const control = this.getControl();
350
+
351
+ if (!isActivationClick(event) || !control) {
352
+ return;
353
+ }
354
+
355
+ if (this.toggle) {
356
+ this.selected = !this.selected;
357
+ }
358
+
359
+ this.focus();
360
+ dispatchActivationClick(control);
361
+ };
362
+
363
+ private getControl(): HTMLElement | null {
364
+ return (
365
+ (this.renderRoot?.querySelector('#button') as HTMLElement | null) ??
366
+ (this.renderRoot?.querySelector('#link') as HTMLElement | null)
367
+ );
368
+ }
369
+
370
+ __renderDisabledReason(softDisabled: boolean) {
371
+ if (softDisabled)
372
+ return html`<div
373
+ id=${DISABLED_REASON_ID}
374
+ role="tooltip"
375
+ aria-label=${this.disabledReason}
376
+ class="screen-reader-only"
377
+ >
378
+ ${this.disabledReason}
379
+ </div>`;
380
+ return nothing;
381
+ }
382
+
383
+ __renderTooltip() {
384
+ if (this.tooltip) {
385
+ const buttonId = isLink(this) ? 'link' : 'button';
386
+ return html`<wc-tooltip class="tooltip" for=${buttonId}>${this.tooltip}</wc-tooltip>`;
387
+ }
388
+ return nothing;
235
389
  }
236
390
  }
@@ -0,0 +1,13 @@
1
+
2
+ /* When both icon and label are present, apply the configured spacing */
3
+ :host([has-icon][has-label]) .button {
4
+ gap: var(--private-button-icon-label-spacing);
5
+ }
6
+
7
+ :host(:not([trailing-icon])) .button {
8
+ flex-direction: row;
9
+ }
10
+
11
+ :host([trailing-icon]) .button {
12
+ flex-direction: row-reverse;
13
+ }
@@ -5,7 +5,7 @@ import IndividualComponent from '@/IndividualComponent.js';
5
5
  import styles from './button-group.scss';
6
6
  import { Button } from '../button/button.js';
7
7
  import { IconButton } from '../icon-button/icon-button.js';
8
- import { BaseButton } from '../BaseButton.js';
8
+ import type { GroupButtonInterface } from '@/button/GroupButtonInterface.js';
9
9
 
10
10
  /**
11
11
  * @label Button Group
@@ -25,7 +25,7 @@ import { BaseButton } from '../BaseButton.js';
25
25
  * @tags controls
26
26
  */
27
27
  @IndividualComponent
28
- export class ButtonGroup extends LitElement {
28
+ class ButtonGroup extends LitElement {
29
29
  // Lit prefers CSS-in-JS for better performance and scoping.
30
30
  // If you prefer keeping the SCSS file, you'll need a build tool (like Vite or Webpack)
31
31
  // that can import .scss files as lit-css.
@@ -57,19 +57,29 @@ export class ButtonGroup extends LitElement {
57
57
  * `"vertical"` stacks buttons in a column.
58
58
  * Defaults to `"horizontal"`.
59
59
  */
60
- @property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'horizontal';
60
+ @property({ reflect: true }) orientation: 'horizontal' | 'vertical' =
61
+ 'horizontal';
61
62
 
62
63
  /**
63
64
  * Color applied to all buttons in the group.
64
65
  * Possible values are `"primary"`, `"success"`, `"danger"`, `"warning"`, `"surface"`, `"on-surface"`.
65
66
  */
66
- @property({ reflect: true }) color?: 'primary' | 'success' | 'danger' | 'warning' | 'surface' | 'on-surface';
67
+ @property({ reflect: true }) color?:
68
+ | 'primary'
69
+ | 'success'
70
+ | 'danger'
71
+ | 'warning'
72
+ | 'surface'
73
+ | 'on-surface';
67
74
 
68
75
  /**
69
76
  * Visual style applied to all buttons in the group.
70
77
  * Possible values are `"filled"`, `"tonal"`, `"outlined"`.
71
78
  */
72
- @property({ attribute: 'button-variant', reflect: true }) buttonVariant?: 'filled' | 'tonal' | 'outlined';
79
+ @property({ attribute: 'button-variant', reflect: true }) buttonVariant?:
80
+ | 'filled'
81
+ | 'tonal'
82
+ | 'outlined';
73
83
 
74
84
  override updated() {
75
85
  this._syncChildren();
@@ -90,18 +100,21 @@ export class ButtonGroup extends LitElement {
90
100
  const isOnly = children.length === 1;
91
101
 
92
102
  if (this.color && 'color' in child) {
93
- (child as BaseButton).color = this.color;
103
+ (child as GroupButtonInterface).color = this.color;
94
104
  }
95
105
 
96
106
  if (this.buttonVariant && 'variant' in child) {
97
- (child as BaseButton).variant = this.buttonVariant;
107
+ (child as GroupButtonInterface).variant = this.buttonVariant;
98
108
  }
99
109
 
100
110
  if (this.variant === 'connected') {
101
111
  child.style.setProperty('--button-container-shape-variant', 'round');
102
112
 
103
113
  if (isOnly) {
104
- child.style.setProperty('--button-container-shape', 'var(--shape-corner-full)');
114
+ child.style.setProperty(
115
+ '--button-container-shape',
116
+ 'var(--shape-corner-full)',
117
+ );
105
118
  child.style.removeProperty('--button-container-shape-start-start');
106
119
  child.style.removeProperty('--button-container-shape-end-start');
107
120
  child.style.removeProperty('--button-container-shape-start-end');
@@ -116,10 +129,22 @@ export class ButtonGroup extends LitElement {
116
129
  child.style.removeProperty('--button-container-shape-end-end');
117
130
  } else {
118
131
  // Left button in horizontal group: round left corners, standard right corners
119
- child.style.setProperty('--button-container-shape-start-start', 'calc(var(--button-height) / 2)');
120
- child.style.setProperty('--button-container-shape-end-start', 'calc(var(--button-height) / 2)');
121
- child.style.setProperty('--button-container-shape-start-end', 'var(--shape-corner-medium)');
122
- child.style.setProperty('--button-container-shape-end-end', 'var(--shape-corner-medium)');
132
+ child.style.setProperty(
133
+ '--button-container-shape-start-start',
134
+ 'calc(var(--button-height) / 2)',
135
+ );
136
+ child.style.setProperty(
137
+ '--button-container-shape-end-start',
138
+ 'calc(var(--button-height) / 2)',
139
+ );
140
+ child.style.setProperty(
141
+ '--button-container-shape-start-end',
142
+ 'var(--shape-corner-medium)',
143
+ );
144
+ child.style.setProperty(
145
+ '--button-container-shape-end-end',
146
+ 'var(--shape-corner-medium)',
147
+ );
123
148
  }
124
149
  } else if (isLast) {
125
150
  child.style.removeProperty('--button-container-shape');
@@ -131,14 +156,29 @@ export class ButtonGroup extends LitElement {
131
156
  child.style.removeProperty('--button-container-shape-end-end');
132
157
  } else {
133
158
  // Right button in horizontal group: standard left corners, round right corners
134
- child.style.setProperty('--button-container-shape-start-start', 'var(--shape-corner-medium)');
135
- child.style.setProperty('--button-container-shape-end-start', 'var(--shape-corner-medium)');
136
- child.style.setProperty('--button-container-shape-start-end', 'calc(var(--button-height) / 2)');
137
- child.style.setProperty('--button-container-shape-end-end', 'calc(var(--button-height) / 2)');
159
+ child.style.setProperty(
160
+ '--button-container-shape-start-start',
161
+ 'var(--shape-corner-medium)',
162
+ );
163
+ child.style.setProperty(
164
+ '--button-container-shape-end-start',
165
+ 'var(--shape-corner-medium)',
166
+ );
167
+ child.style.setProperty(
168
+ '--button-container-shape-start-end',
169
+ 'calc(var(--button-height) / 2)',
170
+ );
171
+ child.style.setProperty(
172
+ '--button-container-shape-end-end',
173
+ 'calc(var(--button-height) / 2)',
174
+ );
138
175
  }
139
176
  } else {
140
177
  // Middle buttons: standard rounded corners on all sides
141
- child.style.setProperty('--button-container-shape', 'var(--shape-corner-medium)');
178
+ child.style.setProperty(
179
+ '--button-container-shape',
180
+ 'var(--shape-corner-medium)',
181
+ );
142
182
  child.style.removeProperty('--button-container-shape-start-start');
143
183
  child.style.removeProperty('--button-container-shape-end-start');
144
184
  child.style.removeProperty('--button-container-shape-start-end');
@@ -169,3 +209,5 @@ export class ButtonGroup extends LitElement {
169
209
  `;
170
210
  }
171
211
  }
212
+
213
+ export default ButtonGroup;
@@ -1,57 +1,42 @@
1
1
  @use '../../../scss/mixin';
2
2
 
3
+ .button .icon-slot {
4
+ overflow: initial;
5
+ }
6
+
3
7
  .button {
4
- --_container-padding: 0.75rem;
5
8
  width: var(--button-height);
6
- --_container-padding: 0;
9
+
10
+ --private-button-container-padding: 0.75rem;
11
+ --private-button-container-padding: 0;
7
12
  }
8
13
 
9
14
  :host([size='xs']),
10
15
  :host([size='extra-small']) {
11
16
  --button-height: 2rem;
12
- }
13
-
14
- :host([size='xs']) .button,
15
- :host([size='extra-small']) .button {
16
- --_button-icon-size: 1rem;
17
+ --private-button-icon-size: 1rem;
17
18
  }
18
19
 
19
20
 
20
21
  :host([size='sm']),
21
22
  :host([size='small']) {
22
23
  --button-height: 2.5rem;
23
- }
24
-
25
- :host([size='sm']) .button,
26
- :host([size='small']) .button {
27
- --_button-icon-size: 1.25rem;
28
- --_button-icon-label-spacing: 0.5rem;
24
+ --private-button-icon-size: 1.25rem;
29
25
  }
30
26
 
31
27
  :host([size='md']),
32
28
  :host([size='medium']) {
33
29
  --button-height: 3.5rem;
34
- }
35
-
36
- :host([size='md']) .button,
37
- :host([size='medium']) .button {
38
- --_button-icon-size: 1.5rem;
30
+ --private-button-icon-size: 1.5rem;
39
31
  }
40
32
 
41
33
  :host([size='lg']),
42
34
  :host([size='large']) {
43
35
  --button-height: 6rem;
44
- }
45
-
46
- :host([size='lg']) .button,
47
- :host([size='large']) .button {
48
- --_button-icon-size: 2rem;
36
+ --private-button-icon-size: 2rem;
49
37
  }
50
38
 
51
39
  :host([size='xl']) {
52
40
  --button-height: 8.5rem;
53
- }
54
-
55
- :host([size='xl']) .button {
56
- --_button-icon-size: 2.5rem;
41
+ --private-button-icon-size: 2.5rem;
57
42
  }