@redvars/peacock 3.2.10 → 3.3.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 (197) hide show
  1. package/dist/{PeacockComponent-CxJc63xj.js → IndividualComponent-tDnXrOLV.js} +3 -3
  2. package/dist/IndividualComponent-tDnXrOLV.js.map +1 -0
  3. package/dist/{button-DaL4va7Q.js → button-BGFJfbT2.js} +22 -35
  4. package/dist/button-BGFJfbT2.js.map +1 -0
  5. package/dist/button-group.js +8 -8
  6. package/dist/button-group.js.map +1 -1
  7. package/dist/button.js +6 -5
  8. package/dist/button.js.map +1 -1
  9. package/dist/chart-donut.js +307 -0
  10. package/dist/chart-donut.js.map +1 -0
  11. package/dist/chart-doughnut.js +307 -0
  12. package/dist/chart-doughnut.js.map +1 -0
  13. package/dist/chart-pie.js +259 -0
  14. package/dist/chart-pie.js.map +1 -0
  15. package/dist/{class-map-BvQRv7eW.js → class-map-DpeNtqCn.js} +2 -2
  16. package/dist/{class-map-BvQRv7eW.js.map → class-map-DpeNtqCn.js.map} +1 -1
  17. package/dist/clock.js +5 -6
  18. package/dist/clock.js.map +1 -1
  19. package/dist/code-editor.js +37 -24
  20. package/dist/code-editor.js.map +1 -1
  21. package/dist/code-highlighter.js +21 -7
  22. package/dist/code-highlighter.js.map +1 -1
  23. package/dist/custom-elements-jsdocs.json +5377 -3122
  24. package/dist/custom-elements.json +5527 -3940
  25. package/dist/{dispatch-event-utils-vbdiOSeC.js → dispatch-event-utils-B4odODQf.js} +2 -15
  26. package/dist/dispatch-event-utils-B4odODQf.js.map +1 -0
  27. package/dist/index.js +13 -9
  28. package/dist/index.js.map +1 -1
  29. package/dist/number-counter.js +12 -10
  30. package/dist/number-counter.js.map +1 -1
  31. package/dist/{observe-theme-change-NneLARW8.js → observe-theme-change-BISF-Gl5.js} +2 -2
  32. package/dist/{observe-theme-change-NneLARW8.js.map → observe-theme-change-BISF-Gl5.js.map} +1 -1
  33. package/dist/peacock-loader.js +124 -62
  34. package/dist/peacock-loader.js.map +1 -1
  35. package/dist/query-QBcUV-L_.js +15 -0
  36. package/dist/query-QBcUV-L_.js.map +1 -0
  37. package/dist/{image-v3BujlY5.js → slider-Dk9CFWTG.js} +1606 -327
  38. package/dist/slider-Dk9CFWTG.js.map +1 -0
  39. package/dist/src/IndividualComponent.d.ts +1 -0
  40. package/dist/src/accordion/{accordion-item/accordion-item.d.ts → accordion-item.d.ts} +4 -4
  41. package/dist/src/accordion/{accordion/accordion.d.ts → accordion.d.ts} +6 -6
  42. package/dist/src/accordion/{accordion-item/index.d.ts → index.d.ts} +1 -0
  43. package/dist/src/avatar/avatar.d.ts +2 -2
  44. package/dist/src/badge/badge.d.ts +2 -2
  45. package/dist/src/breadcrumb/breadcrumb/breadcrumb.d.ts +7 -8
  46. package/dist/src/breadcrumb/breadcrumb-item/breadcrumb-item.d.ts +3 -3
  47. package/dist/src/button/button/button.d.ts +2 -2
  48. package/dist/src/button/button-group/button-group.d.ts +5 -5
  49. package/dist/src/button/icon-button/icon-button.d.ts +2 -2
  50. package/dist/src/chart-donut/chart-donut.d.ts +53 -0
  51. package/dist/src/chart-donut/index.d.ts +1 -0
  52. package/dist/src/chart-doughnut/chart-doughnut.d.ts +53 -0
  53. package/dist/src/chart-doughnut/index.d.ts +1 -0
  54. package/dist/src/chart-pie/chart-pie.d.ts +50 -0
  55. package/dist/src/chart-pie/index.d.ts +1 -0
  56. package/dist/src/checkbox/checkbox.d.ts +3 -6
  57. package/dist/src/chip/chip/chip.d.ts +4 -4
  58. package/dist/src/chip/tag/tag.d.ts +3 -3
  59. package/dist/src/clock/clock.d.ts +3 -4
  60. package/dist/src/code-editor/code-editor.d.ts +11 -9
  61. package/dist/src/container/container.d.ts +6 -11
  62. package/dist/src/date-picker/date-picker.d.ts +3 -3
  63. package/dist/src/divider/divider.d.ts +2 -2
  64. package/dist/src/elevation/elevation.d.ts +2 -2
  65. package/dist/src/empty-state/empty-state.d.ts +9 -2
  66. package/dist/src/field/field.d.ts +17 -0
  67. package/dist/src/focus-ring/focus-ring.d.ts +1 -1
  68. package/dist/src/icon/icon.d.ts +2 -2
  69. package/dist/src/image/image.d.ts +4 -12
  70. package/dist/src/index.d.ts +5 -1
  71. package/dist/src/input/input.d.ts +2 -2
  72. package/dist/src/link/link.d.ts +4 -5
  73. package/dist/src/menu/menu/menu.d.ts +16 -0
  74. package/dist/src/menu/menu-item/menu-item.d.ts +12 -0
  75. package/dist/src/menu/menu-list/menu-list.d.ts +15 -0
  76. package/dist/src/number-counter/number-counter.d.ts +9 -7
  77. package/dist/src/number-field/number-field.d.ts +1 -1
  78. package/dist/src/popover/index.d.ts +1 -1
  79. package/dist/src/progress/circular-progress/circular-progress.d.ts +3 -3
  80. package/dist/src/progress/linear-progress/linear-progress.d.ts +3 -3
  81. package/dist/src/ripple/ripple.d.ts +60 -4
  82. package/dist/src/skeleton/skeleton.d.ts +6 -5
  83. package/dist/src/slider/index.d.ts +1 -0
  84. package/dist/src/slider/slider.d.ts +52 -0
  85. package/dist/src/spinner/spinner.d.ts +2 -2
  86. package/dist/src/switch/switch.d.ts +2 -2
  87. package/dist/src/tabs/index.d.ts +4 -0
  88. package/dist/src/tabs/tab-group.d.ts +41 -0
  89. package/dist/src/tabs/tab-panel.d.ts +21 -0
  90. package/dist/src/tabs/tab.d.ts +58 -0
  91. package/dist/src/tabs/tabs.d.ts +27 -0
  92. package/dist/src/textarea/textarea.d.ts +3 -3
  93. package/dist/src/time-picker/time-picker.d.ts +3 -3
  94. package/dist/src/{popover/tooltip → tooltip}/tooltip.d.ts +6 -3
  95. package/dist/{state-B09bP3XH.js → state-8v48Exzh.js} +2 -2
  96. package/dist/{state-B09bP3XH.js.map → state-8v48Exzh.js.map} +1 -1
  97. package/dist/{style-map-B8xgVEc9.js → style-map-CfNHEkQp.js} +2 -2
  98. package/dist/{style-map-B8xgVEc9.js.map → style-map-CfNHEkQp.js.map} +1 -1
  99. package/dist/transform-DRuHEvar.js +3312 -0
  100. package/dist/transform-DRuHEvar.js.map +1 -0
  101. package/dist/tsconfig.tsbuildinfo +1 -1
  102. package/dist/{unsafe-html-B-dV3Jps.js → unsafe-html-CV6Je6HL.js} +2 -2
  103. package/dist/{unsafe-html-B-dV3Jps.js.map → unsafe-html-CV6Je6HL.js.map} +1 -1
  104. package/package.json +3 -1
  105. package/readme.md +2 -2
  106. package/src/{PeacockComponent.ts → IndividualComponent.ts} +1 -1
  107. package/src/accordion/{accordion-item/accordion-item.scss → accordion-item.scss} +1 -1
  108. package/src/accordion/{accordion-item/accordion-item.ts → accordion-item.ts} +5 -5
  109. package/src/accordion/{accordion/accordion.scss → accordion.scss} +2 -1
  110. package/src/accordion/{accordion/accordion.ts → accordion.ts} +6 -6
  111. package/src/accordion/{accordion-item/index.ts → index.ts} +2 -0
  112. package/src/avatar/avatar.ts +2 -2
  113. package/src/badge/badge.ts +2 -2
  114. package/src/breadcrumb/breadcrumb/breadcrumb.ts +7 -8
  115. package/src/breadcrumb/breadcrumb-item/breadcrumb-item.ts +3 -3
  116. package/src/button/BaseButton.ts +1 -1
  117. package/src/button/button/button.scss +9 -23
  118. package/src/button/button/button.ts +8 -8
  119. package/src/button/button-group/button-group.ts +7 -7
  120. package/src/button/icon-button/icon-button.ts +8 -8
  121. package/src/chart-donut/chart-donut.scss +37 -0
  122. package/src/chart-donut/chart-donut.ts +287 -0
  123. package/src/chart-donut/demo/index.html +51 -0
  124. package/src/chart-donut/index.ts +1 -0
  125. package/src/chart-doughnut/chart-donut.scss +37 -0
  126. package/src/chart-doughnut/chart-doughnut.ts +287 -0
  127. package/src/chart-doughnut/demo/index.html +51 -0
  128. package/src/chart-doughnut/index.ts +1 -0
  129. package/src/chart-pie/chart-pie.scss +27 -0
  130. package/src/chart-pie/chart-pie.ts +256 -0
  131. package/src/chart-pie/demo/index.html +51 -0
  132. package/src/chart-pie/index.ts +1 -0
  133. package/src/checkbox/checkbox.ts +3 -6
  134. package/src/chip/chip/chip.ts +6 -6
  135. package/src/chip/tag/tag.ts +6 -6
  136. package/src/clock/clock.ts +5 -6
  137. package/src/code-editor/code-editor.scss +3 -5
  138. package/src/code-editor/code-editor.ts +30 -15
  139. package/src/code-highlighter/code-highlighter.ts +19 -4
  140. package/src/container/container.ts +6 -11
  141. package/src/date-picker/date-picker.ts +7 -7
  142. package/src/divider/divider.ts +2 -2
  143. package/src/elevation/elevation.ts +2 -2
  144. package/src/empty-state/empty-state.ts +10 -3
  145. package/src/field/field.scss +4 -4
  146. package/src/field/field.ts +19 -2
  147. package/src/focus-ring/focus-ring.scss +2 -1
  148. package/src/focus-ring/focus-ring.ts +1 -1
  149. package/src/icon/icon.ts +2 -2
  150. package/src/icon/p-icon.ts +1 -1
  151. package/src/image/image.ts +4 -12
  152. package/src/index.ts +6 -2
  153. package/src/input/input.ts +6 -6
  154. package/src/link/link.ts +4 -5
  155. package/src/menu/menu/menu.ts +16 -0
  156. package/src/menu/menu-item/menu-item-colors.scss +2 -2
  157. package/src/menu/menu-item/menu-item.ts +14 -2
  158. package/src/menu/menu-list/menu-list.ts +16 -1
  159. package/src/number-counter/demo/index.html +1 -1
  160. package/src/number-counter/number-counter.ts +11 -9
  161. package/src/number-field/number-field.ts +7 -7
  162. package/src/peacock-loader.ts +71 -44
  163. package/src/popover/index.ts +1 -1
  164. package/src/progress/circular-progress/circular-progress.scss +1 -1
  165. package/src/progress/circular-progress/circular-progress.ts +3 -3
  166. package/src/progress/linear-progress/linear-progress.ts +3 -3
  167. package/src/ripple/ripple.ts +478 -94
  168. package/src/skeleton/skeleton.ts +6 -5
  169. package/src/slider/index.ts +1 -0
  170. package/src/slider/slider.scss +130 -0
  171. package/src/slider/slider.ts +178 -0
  172. package/src/spinner/spinner.ts +2 -2
  173. package/src/switch/switch.ts +4 -4
  174. package/src/tabs/index.ts +4 -0
  175. package/src/tabs/tab-group.scss +10 -0
  176. package/src/tabs/tab-group.ts +137 -0
  177. package/src/tabs/tab-panel.scss +12 -0
  178. package/src/tabs/tab-panel.ts +28 -0
  179. package/src/tabs/tab.scss +157 -0
  180. package/src/tabs/tab.ts +242 -0
  181. package/src/tabs/tabs.scss +18 -0
  182. package/src/tabs/tabs.ts +64 -0
  183. package/src/textarea/textarea.ts +5 -5
  184. package/src/time-picker/time-picker.ts +7 -7
  185. package/src/{popover/tooltip → tooltip}/tooltip.scss +1 -1
  186. package/src/{popover/tooltip → tooltip}/tooltip.ts +10 -6
  187. package/dist/PeacockComponent-CxJc63xj.js.map +0 -1
  188. package/dist/button-DaL4va7Q.js.map +0 -1
  189. package/dist/dispatch-event-utils-vbdiOSeC.js.map +0 -1
  190. package/dist/image-v3BujlY5.js.map +0 -1
  191. package/dist/src/PeacockComponent.d.ts +0 -1
  192. package/dist/src/accordion/accordion/index.d.ts +0 -1
  193. package/dist/src/avatar/p-avatar.d.ts +0 -3
  194. package/dist/src/badge/p-badge.d.ts +0 -3
  195. package/src/accordion/accordion/index.ts +0 -1
  196. package/src/avatar/p-avatar.ts +0 -5
  197. package/src/badge/p-badge.ts +0 -5
@@ -1,134 +1,518 @@
1
- import { LitElement, html, css } from 'lit';
1
+ import { LitElement, html, css, PropertyValues } from 'lit';
2
+ import { property, query, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
2
4
 
5
+ const PRESS_GROW_MS = 450;
6
+ const MINIMUM_PRESS_MS = 225;
7
+ const INITIAL_ORIGIN_SCALE = 0.2;
8
+ const PADDING = 10;
9
+ const SOFT_EDGE_MINIMUM_SIZE = 75;
10
+ const SOFT_EDGE_CONTAINER_RATIO = 0.35;
11
+ const PRESS_PSEUDO = '::after';
12
+ const ANIMATION_FILL = 'forwards';
13
+
14
+ /**
15
+ * Interaction states for the ripple.
16
+ *
17
+ * On Touch:
18
+ * - `INACTIVE -> TOUCH_DELAY -> WAITING_FOR_CLICK -> INACTIVE`
19
+ *
20
+ * On Mouse or Pen:
21
+ * - `INACTIVE -> WAITING_FOR_CLICK -> INACTIVE`
22
+ */
23
+ enum State {
24
+ /**
25
+ * Initial state of the control, no touch in progress.
26
+ *
27
+ * Transitions:
28
+ * - on touch down: transition to `TOUCH_DELAY`.
29
+ * - on mouse down: transition to `WAITING_FOR_CLICK`.
30
+ */
31
+ INACTIVE,
32
+ /**
33
+ * Touch down has been received, waiting to determine if it's a swipe or
34
+ * scroll.
35
+ *
36
+ * Transitions:
37
+ * - on touch up: begin press; transition to `WAITING_FOR_CLICK`.
38
+ * - on cancel: transition to `INACTIVE`.
39
+ * - after `TOUCH_DELAY_MS`: begin press; transition to `HOLDING`.
40
+ */
41
+ TOUCH_DELAY,
42
+ /**
43
+ * A touch has been deemed to be a press
44
+ *
45
+ * Transitions:
46
+ * - on up: transition to `WAITING_FOR_CLICK`.
47
+ */
48
+ HOLDING,
49
+ /**
50
+ * The user touch has finished, transition into rest state.
51
+ *
52
+ * Transitions:
53
+ * - on click end press; transition to `INACTIVE`.
54
+ */
55
+ WAITING_FOR_CLICK,
56
+ }
57
+
58
+ /**
59
+ * Events that the ripple listens to.
60
+ */
61
+ const EVENTS = [
62
+ 'click',
63
+ 'contextmenu',
64
+ 'pointercancel',
65
+ 'pointerdown',
66
+ 'pointerenter',
67
+ 'pointerleave',
68
+ 'pointerup',
69
+ ];
70
+
71
+ /**
72
+ * Delay reacting to touch so that we do not show the ripple for a swipe or
73
+ * scroll interaction.
74
+ */
75
+ const TOUCH_DELAY_MS = 150;
76
+
77
+ /**
78
+ * Used to detect if HCM is active. Events do not process during HCM when the
79
+ * ripple is not displayed.
80
+ */
81
+ const FORCED_COLORS = window.matchMedia('(forced-colors: active)');
82
+
83
+ /**
84
+ * @label Ripple
85
+ * @tag wc-ripple
86
+ * @rawTag ripple
87
+ *
88
+ * @summary Provides ripple effect for interactive elements.
89
+ * @overview
90
+ * <p>Ripple creates a visual feedback effect when users interact with buttons or other clickable elements.</p>
91
+ *
92
+ * @example
93
+ * ```html
94
+ * <button style="position: relative;">
95
+ * <wc-ripple></wc-ripple>
96
+ * Click me
97
+ * </button>
98
+ * ```
99
+ * @tags display
100
+ */
3
101
  export class Ripple extends LitElement {
4
102
  static styles = css`
5
103
  :host {
6
- position: absolute;
7
- inset: 0; /* top/left/bottom/right: 0 */
8
- pointer-events: none; /* Let clicks pass through to parent */
9
- overflow: hidden;
10
- border-radius: inherit; /* Inherit parent's rounded corners */
11
- z-index: 0;
12
- --ripple-state-opacity: 0;
13
- --ripple-pressed-color: var(--color-on-surface);
104
+ display: flex;
105
+ margin: auto;
106
+ pointer-events: none;
14
107
  }
15
108
 
16
- .ripple:before {
17
- content: '';
18
- opacity: var(--ripple-state-opacity);
19
- pointer-events: none;
20
- position: absolute;
109
+ :host([disabled]) {
110
+ display: none;
111
+ }
21
112
 
22
- background-color: var(--ripple-pressed-color);
23
- inset: 0;
24
- transition:
25
- opacity 15ms linear,
26
- background-color 15ms linear;
113
+ @media (forced-colors: active) {
114
+ :host {
115
+ display: none;
116
+ }
27
117
  }
28
118
 
29
- .ripple:after {
30
- content: '';
31
- opacity: var(--ripple-state-opacity);
32
- pointer-events: none;
119
+ :host,
120
+ .surface {
121
+ border-radius: inherit;
122
+ corner-shape: inherit;
33
123
  position: absolute;
34
- background: radial-gradient(
35
- closest-side,
36
- var(--ripple-pressed-color) max(100% - 70px, 65%),
37
- transparent 100%
38
- );
39
- transform-origin: center center;
40
- transition: opacity 375ms linear;
41
-
42
- width: 25px;
43
- top: 0px;
44
- transform: translate(51.4375px, 7.5px) scale(8.75941);
45
- left: 0px;
46
- height: 25px;
47
- }
48
-
49
- .ripple-effect {
50
- position: absolute;
51
- border-radius: 50%;
52
- background-color: var(--ripple-pressed-color);
53
- opacity: 0.12; /* Material 3 State Layer Opacity */
54
- transform: scale(0);
55
- animation: ripple-anim 600ms linear forwards;
124
+ inset: 0;
125
+ overflow: hidden;
56
126
  }
57
127
 
58
- @keyframes ripple-anim {
59
- to {
60
- transform: scale(1);
128
+ .surface {
129
+ -webkit-tap-highlight-color: transparent;
130
+
131
+ &::before,
132
+ &::after {
133
+ content: '';
61
134
  opacity: 0;
135
+ position: absolute;
136
+ }
137
+
138
+ &::before {
139
+ background-color: var(--ripple-pressed-color, var(--color-on-surface));
140
+ inset: 0;
141
+ transition: opacity 15ms linear, background-color 15ms linear;
142
+ }
143
+
144
+ &::after {
145
+ background: radial-gradient(
146
+ closest-side,
147
+ var(--ripple-pressed-color, var(--color-on-surface)) max(calc(100% - 70px), 65%),
148
+ transparent 100%
149
+ );
150
+ transform-origin: center center;
151
+ transition: opacity 375ms linear;
62
152
  }
63
153
  }
154
+
155
+ .hovered::before {
156
+ opacity: 0.08;
157
+ }
158
+
159
+ .pressed::after {
160
+ opacity: 0.12;
161
+ transition-duration: 105ms;
162
+ }
64
163
  `;
65
164
 
66
- connectedCallback() {
165
+ /**
166
+ * Disables the ripple.
167
+ */
168
+ @property({ type: Boolean, reflect: true }) disabled = false;
169
+
170
+ @state() private hovered = false;
171
+
172
+ @state() private pressed = false;
173
+
174
+ @query('.surface') private readonly mdRoot!: HTMLElement | null;
175
+
176
+ private rippleSize = '';
177
+
178
+ private rippleScale = '';
179
+
180
+ private initialSize = 0;
181
+
182
+ private growAnimation?: Animation;
183
+
184
+ private state = State.INACTIVE;
185
+
186
+ private rippleStartEvent?: PointerEvent;
187
+
188
+ override connectedCallback() {
67
189
  super.connectedCallback();
68
- // We defer slightly to ensure the parent DOM is ready
69
- requestAnimationFrame(() => {
70
- this._setupParent();
71
- });
190
+ // Needed for VoiceOver, which will create a "group" if the element is a
191
+ // sibling to other content.
192
+ this.setAttribute('aria-hidden', 'true');
193
+ // Attach to parent
194
+ this.attach(this.parentElement!);
72
195
  }
73
196
 
74
- disconnectedCallback() {
197
+ override disconnectedCallback() {
75
198
  super.disconnectedCallback();
76
- if (this.parentElement) {
77
- this.parentElement.removeEventListener('pointerdown', this._createRipple);
199
+ this.detach();
200
+ }
201
+
202
+ attach(control: HTMLElement) {
203
+ if (!control) return;
204
+ EVENTS.forEach(event => {
205
+ control.addEventListener(event, this.handleEvent.bind(this));
206
+ });
207
+ }
208
+
209
+ detach() {
210
+ const control = this.parentElement;
211
+ if (!control) return;
212
+ EVENTS.forEach(event => {
213
+ control.removeEventListener(event, this.handleEvent.bind(this));
214
+ });
215
+ }
216
+
217
+ protected override render() {
218
+ const classes = {
219
+ 'hovered': this.hovered,
220
+ 'pressed': this.pressed,
221
+ };
222
+
223
+ return html`<div class="surface ${classMap(classes)}"></div>`;
224
+ }
225
+
226
+ protected override update(changedProps: PropertyValues<Ripple>) {
227
+ if (changedProps.has('disabled') && this.disabled) {
228
+ this.hovered = false;
229
+ this.pressed = false;
230
+ }
231
+ super.update(changedProps);
232
+ }
233
+
234
+ handlePointerenter(event: PointerEvent) {
235
+ if (!this.shouldReactToEvent(event)) {
236
+ return;
237
+ }
238
+
239
+ this.hovered = true;
240
+ }
241
+
242
+ handlePointerleave(event: PointerEvent) {
243
+ if (!this.shouldReactToEvent(event)) {
244
+ return;
245
+ }
246
+
247
+ this.hovered = false;
248
+
249
+ // release a held mouse or pen press that moves outside the element
250
+ if (this.state !== State.INACTIVE) {
251
+ this.endPressAnimation();
78
252
  }
79
253
  }
80
254
 
81
- _setupParent() {
82
- const parent = this.parentElement;
83
- if (!parent) return;
255
+ private handlePointerup(event: PointerEvent) {
256
+ if (!this.shouldReactToEvent(event)) {
257
+ return;
258
+ }
259
+
260
+ if (this.state === State.HOLDING) {
261
+ this.state = State.WAITING_FOR_CLICK;
262
+ return;
263
+ }
264
+
265
+ if (this.state === State.TOUCH_DELAY) {
266
+ this.state = State.WAITING_FOR_CLICK;
267
+ this.startPressAnimation(this.rippleStartEvent);
268
+ return;
269
+ }
270
+ }
271
+
272
+ private async handlePointerdown(event: PointerEvent) {
273
+ if (!this.shouldReactToEvent(event)) {
274
+ return;
275
+ }
84
276
 
85
- // 1. Force parent to be relative so we can position absolutely inside it
86
- const style = window.getComputedStyle(parent);
87
- if (style.position === 'static') {
88
- parent.style.position = 'relative';
277
+ this.rippleStartEvent = event;
278
+ if (!this.isTouch(event)) {
279
+ this.state = State.WAITING_FOR_CLICK;
280
+ this.startPressAnimation(event);
281
+ return;
89
282
  }
90
283
 
91
- // 2. Attach listener to the parent
92
- parent.addEventListener('pointerdown', this._createRipple);
284
+ // Wait for a hold after touch delay
285
+ this.state = State.TOUCH_DELAY;
286
+ await new Promise((resolve) => {
287
+ setTimeout(resolve, TOUCH_DELAY_MS);
288
+ });
289
+
290
+ if (this.state !== State.TOUCH_DELAY) {
291
+ return;
292
+ }
293
+
294
+ this.state = State.HOLDING;
295
+ this.startPressAnimation(event);
296
+ }
297
+
298
+ private handleClick() {
299
+ if (this.disabled) {
300
+ return;
301
+ }
302
+
303
+ if (this.state === State.WAITING_FOR_CLICK) {
304
+ this.endPressAnimation();
305
+ return;
306
+ }
307
+
308
+ if (this.state === State.INACTIVE) {
309
+ // keyboard synthesized click event
310
+ this.startPressAnimation();
311
+ this.endPressAnimation();
312
+ }
313
+ }
314
+
315
+ private handlePointercancel(event: PointerEvent) {
316
+ if (!this.shouldReactToEvent(event)) {
317
+ return;
318
+ }
319
+
320
+ this.endPressAnimation();
321
+ }
322
+
323
+ private handleContextmenu() {
324
+ if (this.disabled) {
325
+ return;
326
+ }
327
+
328
+ this.endPressAnimation();
329
+ }
330
+
331
+ private determineRippleSize() {
332
+ const { height, width } = this.getBoundingClientRect();
333
+ const maxDim = Math.max(height, width);
334
+ const softEdgeSize = Math.max(
335
+ SOFT_EDGE_CONTAINER_RATIO * maxDim,
336
+ SOFT_EDGE_MINIMUM_SIZE,
337
+ );
338
+
339
+ const initialSize = Math.floor(maxDim * INITIAL_ORIGIN_SCALE);
340
+ const hypotenuse = Math.sqrt(width ** 2 + height ** 2);
341
+ const maxRadius = hypotenuse + PADDING;
342
+
343
+ this.initialSize = initialSize;
344
+ const maybeZoomedScale = (maxRadius + softEdgeSize) / initialSize;
345
+ this.rippleScale = `${maybeZoomedScale}`;
346
+ this.rippleSize = `${initialSize}px`;
93
347
  }
94
348
 
95
- // Arrow function to bind 'this' automatically
96
- _createRipple = (event: PointerEvent) => {
97
- const parent = this.parentElement;
98
- if (!parent) return;
349
+ private getTranslationCoordinates(positionEvent?: Event) {
350
+ const { height, width } = this.getBoundingClientRect();
351
+ // end in the center
352
+ const endPoint = {
353
+ x: (width - this.initialSize) / 2,
354
+ y: (height - this.initialSize) / 2,
355
+ };
356
+
357
+ let startPoint;
358
+ if (positionEvent instanceof PointerEvent) {
359
+ startPoint = this.getNormalizedPointerEventCoords(positionEvent);
360
+ } else {
361
+ startPoint = {
362
+ x: width / 2,
363
+ y: height / 2,
364
+ };
365
+ }
99
366
 
100
- const rect = parent.getBoundingClientRect();
367
+ // center around start point
368
+ startPoint = {
369
+ x: startPoint.x - this.initialSize / 2,
370
+ y: startPoint.y - this.initialSize / 2,
371
+ };
101
372
 
102
- // 1. Calculate diameter (furthest corner)
103
- const diameter = Math.max(rect.width, rect.height) * 2.5;
104
- const radius = diameter / 2;
373
+ return { startPoint, endPoint };
374
+ }
105
375
 
106
- // 2. Calculate position relative to the parent
107
- const x = event.clientX - rect.left;
108
- const y = event.clientY - rect.top;
376
+ private startPressAnimation(positionEvent?: Event) {
377
+ if (!this.mdRoot) {
378
+ return;
379
+ }
109
380
 
110
- // 3. Create the ripple element
111
- // We create this manually to avoid triggering a full Lit render cycle
112
- // for a transient, fire-and-forget animation.
113
- const ripple = document.createElement('div');
114
- ripple.classList.add('ripple-effect');
381
+ this.pressed = true;
382
+ this.growAnimation?.cancel();
383
+ this.determineRippleSize();
384
+ const { startPoint, endPoint } =
385
+ this.getTranslationCoordinates(positionEvent);
386
+ const translateStart = `${startPoint.x}px, ${startPoint.y}px`;
387
+ const translateEnd = `${endPoint.x}px, ${endPoint.y}px`;
115
388
 
116
- ripple.style.width = `${diameter}px`;
117
- ripple.style.height = `${diameter}px`;
118
- ripple.style.left = `${x - radius}px`;
119
- ripple.style.top = `${y - radius}px`;
389
+ this.growAnimation = this.mdRoot.animate(
390
+ {
391
+ top: [0, 0],
392
+ left: [0, 0],
393
+ height: [this.rippleSize, this.rippleSize],
394
+ width: [this.rippleSize, this.rippleSize],
395
+ transform: [
396
+ `translate(${translateStart}) scale(1)`,
397
+ `translate(${translateEnd}) scale(${this.rippleScale})`,
398
+ ],
399
+ },
400
+ {
401
+ pseudoElement: PRESS_PSEUDO,
402
+ duration: PRESS_GROW_MS,
403
+ easing: 'cubic-bezier(0.2, 0, 0, 1)',
404
+ fill: ANIMATION_FILL,
405
+ },
406
+ );
407
+ }
120
408
 
121
- // 4. Append to Shadow DOM
122
- this.renderRoot.appendChild(ripple);
409
+ private async endPressAnimation() {
410
+ this.rippleStartEvent = undefined;
411
+ this.state = State.INACTIVE;
412
+ const animation = this.growAnimation;
413
+ let pressAnimationPlayState = Infinity;
414
+ if (typeof animation?.currentTime === 'number') {
415
+ pressAnimationPlayState = animation.currentTime;
416
+ } else if (animation?.currentTime) {
417
+ pressAnimationPlayState = animation.currentTime.to('ms').value;
418
+ }
123
419
 
124
- // 5. Cleanup
125
- ripple.addEventListener('animationend', () => {
126
- ripple.remove();
420
+ if (pressAnimationPlayState >= MINIMUM_PRESS_MS) {
421
+ this.pressed = false;
422
+ return;
423
+ }
424
+
425
+ await new Promise((resolve) => {
426
+ setTimeout(resolve, MINIMUM_PRESS_MS - pressAnimationPlayState);
127
427
  });
128
- };
129
428
 
130
- render() {
131
- // No HTML needed in the template, we inject ripples dynamically
132
- return html`<div class="ripple"></div>`;
429
+ if (this.growAnimation !== animation) {
430
+ // A new press animation was started. The old animation was canceled and
431
+ // should not finish the pressed state.
432
+ return;
433
+ }
434
+
435
+ this.pressed = false;
436
+ }
437
+
438
+ /**
439
+ * Returns `true` if
440
+ * - the ripple element is enabled
441
+ * - the pointer is primary for the input type
442
+ * - the pointer is the pointer that started the interaction, or will start
443
+ * the interaction
444
+ * - the pointer is a touch, or the pointer state has the primary button
445
+ * held, or the pointer is hovering
446
+ */
447
+ private shouldReactToEvent(event: PointerEvent) {
448
+ if (this.disabled || !event.isPrimary) {
449
+ return false;
450
+ }
451
+
452
+ if (
453
+ this.rippleStartEvent &&
454
+ this.rippleStartEvent.pointerId !== event.pointerId
455
+ ) {
456
+ return false;
457
+ }
458
+
459
+ if (event.type === 'pointerenter' || event.type === 'pointerleave') {
460
+ return !this.isTouch(event);
461
+ }
462
+
463
+ const isPrimaryButton = event.buttons === 1;
464
+ return this.isTouch(event) || isPrimaryButton;
465
+ }
466
+
467
+ private isTouch({ pointerType }: PointerEvent) {
468
+ return pointerType === 'touch';
469
+ }
470
+
471
+ async handleEvent(event: Event) {
472
+ if (FORCED_COLORS?.matches) {
473
+ // Skip event logic since the ripple is `display: none`.
474
+ return;
475
+ }
476
+
477
+ switch (event.type) {
478
+ case 'click':
479
+ this.handleClick();
480
+ break;
481
+ case 'contextmenu':
482
+ this.handleContextmenu();
483
+ break;
484
+ case 'pointercancel':
485
+ this.handlePointercancel(event as PointerEvent);
486
+ break;
487
+ case 'pointerdown':
488
+ await this.handlePointerdown(event as PointerEvent);
489
+ break;
490
+ case 'pointerenter':
491
+ this.handlePointerenter(event as PointerEvent);
492
+ break;
493
+ case 'pointerleave':
494
+ this.handlePointerleave(event as PointerEvent);
495
+ break;
496
+ case 'pointerup':
497
+ this.handlePointerup(event as PointerEvent);
498
+ break;
499
+ default:
500
+ break;
501
+ }
502
+ }
503
+
504
+ private getNormalizedPointerEventCoords(pointerEvent: PointerEvent): {
505
+ x: number;
506
+ y: number;
507
+ } {
508
+ const { scrollX, scrollY } = window;
509
+ const { left, top } = this.getBoundingClientRect();
510
+ const documentX = scrollX + left;
511
+ const documentY = scrollY + top;
512
+ const { pageX, pageY } = pointerEvent;
513
+ return {
514
+ x: pageX - documentX,
515
+ y: pageY - documentY,
516
+ };
133
517
  }
134
518
  }
@@ -4,14 +4,15 @@ import styles from './skeleton.scss';
4
4
 
5
5
  /**
6
6
  * @label Skeleton
7
- *
8
- * @tag base-skeleton
7
+ * @tag wc-skeleton
9
8
  * @rawTag skeleton
10
- *
11
9
  * @summary Adds a skeleton effect to an element.
12
- *
13
- *
14
10
  * @tags display
11
+ *
12
+ * @example
13
+ * ```html
14
+ * <wc-skeleton visible></wc-skeleton>
15
+ * ```
15
16
  */
16
17
  export class Skeleton extends LitElement {
17
18
  static styles = [styles];
@@ -0,0 +1 @@
1
+ export { Slider } from './slider.js';