Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 1 | // Copyright 2021 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | import * as LitHtml from '../../lit-html/lit-html.js'; |
Benedikt Meurer | c7c3c1a | 2023-11-16 15:13:05 | [diff] [blame] | 6 | import * as VisualLogging from '../../visual_logging/visual_logging.js'; |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 7 | import * as ComponentHelpers from '../helpers/helpers.js'; |
| 8 | import * as IconButton from '../icon_button/icon_button.js'; |
| 9 | |
| 10 | import buttonStyles from './button.css.js'; |
| 11 | |
| 12 | declare global { |
| 13 | interface HTMLElementTagNameMap { |
| 14 | 'devtools-button': Button; |
| 15 | } |
| 16 | } |
| 17 | |
| 18 | export const enum Variant { |
| 19 | PRIMARY = 'primary', |
Alex Rudenko | f8fd48e | 2023-11-21 13:20:35 | [diff] [blame] | 20 | TONAL = 'tonal', |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 21 | OUTLINED = 'outlined', |
| 22 | TEXT = 'text', |
Alex Rudenko | e6ba352 | 2021-10-15 06:09:48 | [diff] [blame] | 23 | TOOLBAR = 'toolbar', |
Alex Rudenko | fea82db | 2023-04-25 11:31:45 | [diff] [blame] | 24 | // Just like toolbar but has a style similar to a primary button. |
| 25 | PRIMARY_TOOLBAR = 'primary_toolbar', |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 26 | ICON = 'icon', |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 27 | ICON_TOGGLE = 'icon_toggle', |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 28 | ADORNER_ICON = 'adorner_icon', |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 29 | } |
| 30 | |
Alex Rudenko | 1a41fb4 | 2021-09-30 09:12:14 | [diff] [blame] | 31 | export const enum Size { |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 32 | MICRO = 'MICRO', |
Alex Rudenko | 1a41fb4 | 2021-09-30 09:12:14 | [diff] [blame] | 33 | SMALL = 'SMALL', |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 34 | REGULAR = 'REGULAR', |
Alex Rudenko | 1a41fb4 | 2021-09-30 09:12:14 | [diff] [blame] | 35 | } |
| 36 | |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 37 | export const enum ToggleType { |
| 38 | PRIMARY = 'primary-toggle', |
| 39 | RED = 'red-toggle', |
| 40 | } |
| 41 | |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 42 | type ButtonType = 'button'|'submit'|'reset'; |
| 43 | |
Alex Rudenko | e6ba352 | 2021-10-15 06:09:48 | [diff] [blame] | 44 | interface ButtonState { |
Alex Rudenko | 0f8c2b5 | 2023-12-29 05:08:58 | [diff] [blame] | 45 | iconUrl?: string; |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 46 | variant?: Variant; |
Alex Rudenko | 1a41fb4 | 2021-09-30 09:12:14 | [diff] [blame] | 47 | size?: Size; |
Alex Rudenko | 1bef13a | 2021-10-19 10:20:55 | [diff] [blame] | 48 | disabled: boolean; |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 49 | toggled?: boolean; |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 50 | toggleOnClick?: boolean; |
| 51 | checked?: boolean; |
| 52 | pressed?: boolean; |
Alex Rudenko | fce2db0 | 2021-11-09 09:25:57 | [diff] [blame] | 53 | active: boolean; |
Riho Isawa | bb8f398 | 2021-11-26 11:58:16 | [diff] [blame] | 54 | spinner?: boolean; |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 55 | type: ButtonType; |
| 56 | value?: string; |
Johan Bay | 5f9228d | 2022-01-07 08:02:40 | [diff] [blame] | 57 | title?: string; |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 58 | iconName?: string; |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 59 | toggledIconName?: string; |
| 60 | toggleType?: ToggleType; |
Benedikt Meurer | c7c3c1a | 2023-11-16 15:13:05 | [diff] [blame] | 61 | jslogContext?: string; |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 62 | longClickable?: boolean; |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 63 | } |
| 64 | |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 65 | interface CommonButtonData { |
| 66 | variant: Variant; |
Alex Rudenko | 0f8c2b5 | 2023-12-29 05:08:58 | [diff] [blame] | 67 | iconUrl?: string; |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 68 | iconName?: string; |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 69 | toggledIconName?: string; |
| 70 | toggleType?: ToggleType; |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 71 | toggleOnClick?: boolean; |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 72 | size?: Size; |
| 73 | disabled?: boolean; |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 74 | toggled?: boolean; |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 75 | checked?: boolean; |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 76 | active?: boolean; |
| 77 | spinner?: boolean; |
| 78 | type?: ButtonType; |
| 79 | value?: string; |
| 80 | title?: string; |
Benedikt Meurer | c7c3c1a | 2023-11-16 15:13:05 | [diff] [blame] | 81 | jslogContext?: string; |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 82 | longClickable?: boolean; |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 83 | } |
| 84 | |
| 85 | export type ButtonData = CommonButtonData&(|{ |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 86 | variant: Variant.PRIMARY_TOOLBAR | Variant.TOOLBAR | Variant.ICON, |
Alex Rudenko | 0f8c2b5 | 2023-12-29 05:08:58 | [diff] [blame] | 87 | iconUrl: string, |
| 88 | }|{ |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 89 | variant: Variant.PRIMARY_TOOLBAR | Variant.TOOLBAR | Variant.ICON, |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 90 | iconName: string, |
Alex Rudenko | e6ba352 | 2021-10-15 06:09:48 | [diff] [blame] | 91 | }|{ |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 92 | variant: Variant.PRIMARY | Variant.OUTLINED | Variant.TONAL | Variant.TEXT | Variant.ADORNER_ICON, |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 93 | }|{ |
| 94 | variant: Variant.ICON_TOGGLE, |
| 95 | iconName: string, |
| 96 | toggledIconName: string, |
| 97 | toggleType: ToggleType, |
| 98 | toggled: boolean, |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 99 | }); |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 100 | |
| 101 | export class Button extends HTMLElement { |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 102 | static formAssociated = true; |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 103 | static readonly litTagName = LitHtml.literal`devtools-button`; |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 104 | readonly #shadow = this.attachShadow({mode: 'open', delegatesFocus: true}); |
Tim van der Lippe | 0ea22df | 2021-12-15 11:27:37 | [diff] [blame] | 105 | readonly #boundRender = this.#render.bind(this); |
| 106 | readonly #boundOnClick = this.#onClick.bind(this); |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 107 | readonly #props: ButtonState = { |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 108 | size: Size.REGULAR, |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 109 | toggleOnClick: true, |
Alex Rudenko | 1bef13a | 2021-10-19 10:20:55 | [diff] [blame] | 110 | disabled: false, |
Alex Rudenko | fce2db0 | 2021-11-09 09:25:57 | [diff] [blame] | 111 | active: false, |
Riho Isawa | bb8f398 | 2021-11-26 11:58:16 | [diff] [blame] | 112 | spinner: false, |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 113 | type: 'button', |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 114 | longClickable: false, |
Alex Rudenko | 1a41fb4 | 2021-09-30 09:12:14 | [diff] [blame] | 115 | }; |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 116 | #isEmpty = true; |
Jack Franklin | 2204d75 | 2022-11-21 14:10:10 | [diff] [blame] | 117 | #internals = this.attachInternals(); |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 118 | |
| 119 | constructor() { |
| 120 | super(); |
Alex Rudenko | 1bef13a | 2021-10-19 10:20:55 | [diff] [blame] | 121 | this.setAttribute('role', 'presentation'); |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 122 | this.addEventListener('click', this.#boundOnClick, true); |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 123 | } |
| 124 | |
| 125 | /** |
| 126 | * Perfer using the .data= setter instead of setting the individual properties |
| 127 | * for increased type-safety. |
| 128 | */ |
Alex Rudenko | e6ba352 | 2021-10-15 06:09:48 | [diff] [blame] | 129 | set data(data: ButtonData) { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 130 | this.#props.variant = data.variant; |
Alex Rudenko | 0f8c2b5 | 2023-12-29 05:08:58 | [diff] [blame] | 131 | this.#props.iconUrl = data.iconUrl; |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 132 | this.#props.iconName = data.iconName; |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 133 | this.#props.toggledIconName = data.toggledIconName; |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 134 | this.#props.toggleOnClick = data.toggleOnClick !== undefined ? data.toggleOnClick : true; |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 135 | this.#props.size = Size.REGULAR; |
Jack Franklin | 98adbb5 | 2022-07-13 10:23:21 | [diff] [blame] | 136 | |
| 137 | if ('size' in data && data.size) { |
| 138 | this.#props.size = data.size; |
| 139 | } |
Jack Franklin | 98adbb5 | 2022-07-13 10:23:21 | [diff] [blame] | 140 | |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 141 | this.#props.active = Boolean(data.active); |
Jack Franklin | 98adbb5 | 2022-07-13 10:23:21 | [diff] [blame] | 142 | this.#props.spinner = Boolean('spinner' in data ? data.spinner : false); |
| 143 | |
| 144 | this.#props.type = 'button'; |
| 145 | if ('type' in data && data.type) { |
| 146 | this.#props.type = data.type; |
| 147 | } |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 148 | this.#props.toggled = data.toggled; |
| 149 | this.#props.toggleType = data.toggleType; |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 150 | this.#props.checked = data.checked; |
Tim van der Lippe | 0ea22df | 2021-12-15 11:27:37 | [diff] [blame] | 151 | this.#setDisabledProperty(data.disabled || false); |
Johan Bay | 5f9228d | 2022-01-07 08:02:40 | [diff] [blame] | 152 | this.#props.title = data.title; |
Benedikt Meurer | c7c3c1a | 2023-11-16 15:13:05 | [diff] [blame] | 153 | this.#props.jslogContext = data.jslogContext; |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 154 | this.#props.longClickable = data.longClickable; |
Tim van der Lippe | 2d9a95c | 2022-01-04 15:18:03 | [diff] [blame] | 155 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 156 | } |
| 157 | |
Alex Rudenko | 0f8c2b5 | 2023-12-29 05:08:58 | [diff] [blame] | 158 | set iconUrl(iconUrl: string|undefined) { |
| 159 | this.#props.iconUrl = iconUrl; |
| 160 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
| 161 | } |
| 162 | |
Alex Rudenko | 9d873e5 | 2023-05-09 14:26:17 | [diff] [blame] | 163 | set iconName(iconName: string|undefined) { |
| 164 | this.#props.iconName = iconName; |
| 165 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
| 166 | } |
| 167 | |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 168 | set toggledIconName(toggledIconName: string) { |
| 169 | this.#props.toggledIconName = toggledIconName; |
| 170 | } |
| 171 | |
| 172 | set toggleType(toggleType: ToggleType) { |
| 173 | this.#props.toggleType = toggleType; |
| 174 | } |
| 175 | |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 176 | set variant(variant: Variant) { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 177 | this.#props.variant = variant; |
Tim van der Lippe | 2d9a95c | 2022-01-04 15:18:03 | [diff] [blame] | 178 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 179 | } |
| 180 | |
Alex Rudenko | 1a41fb4 | 2021-09-30 09:12:14 | [diff] [blame] | 181 | set size(size: Size) { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 182 | this.#props.size = size; |
Tim van der Lippe | 2d9a95c | 2022-01-04 15:18:03 | [diff] [blame] | 183 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Alex Rudenko | 1a41fb4 | 2021-09-30 09:12:14 | [diff] [blame] | 184 | } |
| 185 | |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 186 | set type(type: ButtonType) { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 187 | this.#props.type = type; |
Tim van der Lippe | 2d9a95c | 2022-01-04 15:18:03 | [diff] [blame] | 188 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 189 | } |
| 190 | |
Randolf Jung | ffd1424 | 2023-04-19 00:32:25 | [diff] [blame] | 191 | override set title(title: string) { |
Alex Rudenko | 47efbe1 | 2022-01-19 11:48:50 | [diff] [blame] | 192 | this.#props.title = title; |
| 193 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
| 194 | } |
| 195 | |
Alex Rudenko | 1bef13a | 2021-10-19 10:20:55 | [diff] [blame] | 196 | set disabled(disabled: boolean) { |
Tim van der Lippe | 0ea22df | 2021-12-15 11:27:37 | [diff] [blame] | 197 | this.#setDisabledProperty(disabled); |
Tim van der Lippe | 2d9a95c | 2022-01-04 15:18:03 | [diff] [blame] | 198 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Alex Rudenko | 1bef13a | 2021-10-19 10:20:55 | [diff] [blame] | 199 | } |
| 200 | |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 201 | set toggleOnClick(toggleOnClick: boolean) { |
| 202 | this.#props.toggleOnClick = toggleOnClick; |
| 203 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
| 204 | } |
| 205 | |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 206 | set toggled(toggled: boolean) { |
| 207 | this.#props.toggled = toggled; |
| 208 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
| 209 | } |
| 210 | |
Kim-Anh Tran | f2e8fca | 2024-05-14 08:28:22 | [diff] [blame] | 211 | get toggled(): boolean { |
| 212 | return Boolean(this.#props.toggled); |
| 213 | } |
| 214 | |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 215 | set checked(checked: boolean) { |
| 216 | this.#props.checked = checked; |
| 217 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
| 218 | } |
| 219 | |
| 220 | set pressed(pressed: boolean) { |
| 221 | this.#props.pressed = pressed; |
| 222 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
| 223 | } |
| 224 | |
Alex Rudenko | fce2db0 | 2021-11-09 09:25:57 | [diff] [blame] | 225 | set active(active: boolean) { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 226 | this.#props.active = active; |
Tim van der Lippe | 2d9a95c | 2022-01-04 15:18:03 | [diff] [blame] | 227 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Alex Rudenko | fce2db0 | 2021-11-09 09:25:57 | [diff] [blame] | 228 | } |
| 229 | |
Alex Rudenko | 409fe74 | 2023-10-26 11:56:23 | [diff] [blame] | 230 | get active(): boolean { |
| 231 | return this.#props.active; |
| 232 | } |
| 233 | |
Riho Isawa | bb8f398 | 2021-11-26 11:58:16 | [diff] [blame] | 234 | set spinner(spinner: boolean) { |
| 235 | this.#props.spinner = spinner; |
Tim van der Lippe | 2d9a95c | 2022-01-04 15:18:03 | [diff] [blame] | 236 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Riho Isawa | bb8f398 | 2021-11-26 11:58:16 | [diff] [blame] | 237 | } |
| 238 | |
Benedikt Meurer | c7c3c1a | 2023-11-16 15:13:05 | [diff] [blame] | 239 | get jslogContext(): string|undefined { |
| 240 | return this.#props.jslogContext; |
| 241 | } |
| 242 | |
| 243 | set jslogContext(jslogContext: string|undefined) { |
| 244 | this.#props.jslogContext = jslogContext; |
| 245 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
| 246 | } |
| 247 | |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 248 | set longClickable(longClickable: boolean) { |
| 249 | this.#props.longClickable = longClickable; |
| 250 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
| 251 | } |
| 252 | |
Tim van der Lippe | 0ea22df | 2021-12-15 11:27:37 | [diff] [blame] | 253 | #setDisabledProperty(disabled: boolean): void { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 254 | this.#props.disabled = disabled; |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 255 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Alex Rudenko | 1bef13a | 2021-10-19 10:20:55 | [diff] [blame] | 256 | } |
| 257 | |
Randolf Jung | ffd1424 | 2023-04-19 00:32:25 | [diff] [blame] | 258 | override focus(): void { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 259 | this.#shadow.querySelector('button')?.focus(); |
Alex Rudenko | 4871110 | 2021-09-29 08:24:10 | [diff] [blame] | 260 | } |
| 261 | |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 262 | connectedCallback(): void { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 263 | this.#shadow.adoptedStyleSheets = [buttonStyles]; |
Tim van der Lippe | 2d9a95c | 2022-01-04 15:18:03 | [diff] [blame] | 264 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 265 | } |
| 266 | |
Tim van der Lippe | 0ea22df | 2021-12-15 11:27:37 | [diff] [blame] | 267 | #onClick(event: Event): void { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 268 | if (this.#props.disabled) { |
Alex Rudenko | 1bef13a | 2021-10-19 10:20:55 | [diff] [blame] | 269 | event.stopPropagation(); |
| 270 | event.preventDefault(); |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 271 | return; |
| 272 | } |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 273 | if (this.form && this.#props.type === 'submit') { |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 274 | event.preventDefault(); |
| 275 | this.form.dispatchEvent(new SubmitEvent('submit', { |
| 276 | submitter: this, |
| 277 | })); |
| 278 | } |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 279 | if (this.form && this.#props.type === 'reset') { |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 280 | event.preventDefault(); |
| 281 | this.form.reset(); |
Alex Rudenko | 1bef13a | 2021-10-19 10:20:55 | [diff] [blame] | 282 | } |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 283 | if (this.#props.toggleOnClick && this.#props.variant === Variant.ICON_TOGGLE && this.#props.iconName) { |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 284 | this.toggled = !this.#props.toggled; |
| 285 | } |
Alex Rudenko | 1bef13a | 2021-10-19 10:20:55 | [diff] [blame] | 286 | } |
| 287 | |
Tim van der Lippe | 0ea22df | 2021-12-15 11:27:37 | [diff] [blame] | 288 | #onSlotChange(event: Event): void { |
Alex Rudenko | 50b3a3e | 2021-09-29 11:01:46 | [diff] [blame] | 289 | const slot = event.target as HTMLSlotElement | undefined; |
| 290 | const nodes = slot?.assignedNodes(); |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 291 | this.#isEmpty = !nodes || !Boolean(nodes.length); |
Tim van der Lippe | 2d9a95c | 2022-01-04 15:18:03 | [diff] [blame] | 292 | void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender); |
Alex Rudenko | 50b3a3e | 2021-09-29 11:01:46 | [diff] [blame] | 293 | } |
| 294 | |
Alex Rudenko | fea82db | 2023-04-25 11:31:45 | [diff] [blame] | 295 | #isToolbarVariant(): boolean { |
| 296 | return this.#props.variant === Variant.TOOLBAR || this.#props.variant === Variant.PRIMARY_TOOLBAR; |
| 297 | } |
| 298 | |
Tim van der Lippe | 0ea22df | 2021-12-15 11:27:37 | [diff] [blame] | 299 | #render(): void { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 300 | if (!this.#props.variant) { |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 301 | throw new Error('Button requires a variant to be defined'); |
| 302 | } |
Alex Rudenko | fea82db | 2023-04-25 11:31:45 | [diff] [blame] | 303 | if (this.#isToolbarVariant()) { |
Alex Rudenko | 0f8c2b5 | 2023-12-29 05:08:58 | [diff] [blame] | 304 | if (!this.#props.iconUrl && !this.#props.iconName) { |
Alex Rudenko | e6ba352 | 2021-10-15 06:09:48 | [diff] [blame] | 305 | throw new Error('Toolbar button requires an icon'); |
| 306 | } |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 307 | if (!this.#isEmpty) { |
Jack Franklin | 7e5333c | 2023-02-07 17:13:19 | [diff] [blame] | 308 | throw new Error('Toolbar button does not accept children'); |
Alex Rudenko | e6ba352 | 2021-10-15 06:09:48 | [diff] [blame] | 309 | } |
| 310 | } |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 311 | if (this.#props.variant === Variant.ICON) { |
Alex Rudenko | 0f8c2b5 | 2023-12-29 05:08:58 | [diff] [blame] | 312 | if (!this.#props.iconUrl && !this.#props.iconName) { |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 313 | throw new Error('Icon button requires an icon'); |
Wolfgang Beyer | babb220 | 2022-03-10 16:32:37 | [diff] [blame] | 314 | } |
| 315 | if (!this.#isEmpty) { |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 316 | throw new Error('Icon button does not accept children'); |
Wolfgang Beyer | babb220 | 2022-03-10 16:32:37 | [diff] [blame] | 317 | } |
| 318 | } |
Alex Rudenko | 0f8c2b5 | 2023-12-29 05:08:58 | [diff] [blame] | 319 | if (this.#props.iconName && this.#props.iconUrl) { |
| 320 | throw new Error('Both iconName and iconUrl are provided.'); |
| 321 | } |
| 322 | const hasIcon = Boolean(this.#props.iconUrl) || Boolean(this.#props.iconName); |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 323 | const classes = { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 324 | primary: this.#props.variant === Variant.PRIMARY, |
Alex Rudenko | f8fd48e | 2023-11-21 13:20:35 | [diff] [blame] | 325 | tonal: this.#props.variant === Variant.TONAL, |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 326 | outlined: this.#props.variant === Variant.OUTLINED, |
| 327 | text: this.#props.variant === Variant.TEXT, |
Alex Rudenko | fea82db | 2023-04-25 11:31:45 | [diff] [blame] | 328 | toolbar: this.#isToolbarVariant(), |
| 329 | 'primary-toolbar': this.#props.variant === Variant.PRIMARY_TOOLBAR, |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 330 | icon: this.#props.variant === Variant.ICON || this.#props.variant === Variant.ICON_TOGGLE, |
| 331 | 'primary-toggle': this.#props.toggleType === ToggleType.PRIMARY, |
| 332 | 'red-toggle': this.#props.toggleType === ToggleType.RED, |
| 333 | toggled: Boolean(this.#props.toggled), |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 334 | checked: Boolean(this.#props.checked), |
Alex Rudenko | 770e4c6 | 2023-04-26 07:58:24 | [diff] [blame] | 335 | 'text-with-icon': hasIcon && !this.#isEmpty, |
| 336 | 'only-icon': hasIcon && this.#isEmpty, |
Alex Rudenko | 0bd7a1b | 2024-01-04 06:58:33 | [diff] [blame] | 337 | 'only-text': !hasIcon && !this.#isEmpty, |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 338 | micro: this.#props.size === Size.MICRO, |
Alex Rudenko | c122e16 | 2024-01-31 18:56:05 | [diff] [blame] | 339 | small: Boolean(this.#props.size === Size.SMALL), |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 340 | active: this.#props.active, |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 341 | }; |
Riho Isawa | bb8f398 | 2021-11-26 11:58:16 | [diff] [blame] | 342 | const spinnerClasses = { |
| 343 | primary: this.#props.variant === Variant.PRIMARY, |
Kateryna Prokopenko | 0895c17 | 2024-04-17 09:49:13 | [diff] [blame] | 344 | outlined: this.#props.variant === Variant.OUTLINED, |
Riho Isawa | bb8f398 | 2021-11-26 11:58:16 | [diff] [blame] | 345 | disabled: Boolean(this.#props.disabled), |
Alex Rudenko | 3aa078e | 2024-01-03 13:52:47 | [diff] [blame] | 346 | spinner: true, |
Riho Isawa | bb8f398 | 2021-11-26 11:58:16 | [diff] [blame] | 347 | }; |
Benedikt Meurer | c7c3c1a | 2023-11-16 15:13:05 | [diff] [blame] | 348 | const jslog = |
| 349 | this.#props.jslogContext && VisualLogging.action().track({click: true}).context(this.#props.jslogContext); |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 350 | // clang-format off |
| 351 | LitHtml.render( |
| 352 | LitHtml.html` |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 353 | <button title=${LitHtml.Directives.ifDefined(this.#props.title)} .disabled=${this.#props.disabled} class=${LitHtml.Directives.classMap(classes)} aria-pressed=${LitHtml.Directives.ifDefined(this.#props.pressed)} jslog=${LitHtml.Directives.ifDefined(jslog)}> |
Benedikt Meurer | ea0cf6f | 2024-01-02 11:52:28 | [diff] [blame] | 354 | ${hasIcon |
| 355 | ? LitHtml.html` |
Kateryna Prokopenko | cc83004 | 2024-05-03 16:30:46 | [diff] [blame] | 356 | <${IconButton.Icon.Icon.litTagName} name=${this.#props.toggled ? this.#props.toggledIconName : this.#props.iconName || this.#props.iconUrl}> |
Benedikt Meurer | ea0cf6f | 2024-01-02 11:52:28 | [diff] [blame] | 357 | </${IconButton.Icon.Icon.litTagName}>` |
| 358 | : ''} |
Kateryna Prokopenko | 17cba89 | 2024-07-26 13:33:56 | [diff] [blame^] | 359 | ${this.#props.longClickable ? LitHtml.html`<${IconButton.Icon.Icon.litTagName} name=${'triangle-bottom-right'} class="long-click"> |
| 360 | </${IconButton.Icon.Icon.litTagName}>` |
| 361 | : ''} |
Riho Isawa | bb8f398 | 2021-11-26 11:58:16 | [diff] [blame] | 362 | ${this.#props.spinner ? LitHtml.html`<span class=${LitHtml.Directives.classMap(spinnerClasses)}></span>` : ''} |
Tim van der Lippe | 0ea22df | 2021-12-15 11:27:37 | [diff] [blame] | 363 | <slot @slotchange=${this.#onSlotChange}></slot> |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 364 | </button> |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 365 | `, this.#shadow, {host: this}); |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 366 | // clang-format on |
| 367 | } |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 368 | |
| 369 | // Based on https://web.dev/more-capable-form-controls/ to make custom elements form-friendly. |
| 370 | // Form controls usually expose a "value" property. |
| 371 | get value(): string { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 372 | return this.#props.value || ''; |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 373 | } |
| 374 | set value(value: string) { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 375 | this.#props.value = value; |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 376 | } |
| 377 | |
| 378 | // The following properties and methods aren't strictly required, |
| 379 | // but browser-level form controls provide them. Providing them helps |
| 380 | // ensure consistency with browser-provided controls. |
Jack Franklin | 061f09c | 2022-07-12 14:31:09 | [diff] [blame] | 381 | get form(): HTMLFormElement|null { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 382 | return this.#internals.form; |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 383 | } |
| 384 | get name(): string|null { |
| 385 | return this.getAttribute('name'); |
| 386 | } |
| 387 | get type(): ButtonType { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 388 | return this.#props.type; |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 389 | } |
| 390 | get validity(): ValidityState { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 391 | return this.#internals.validity; |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 392 | } |
| 393 | get validationMessage(): string { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 394 | return this.#internals.validationMessage; |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 395 | } |
| 396 | get willValidate(): boolean { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 397 | return this.#internals.willValidate; |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 398 | } |
Jack Franklin | 2204d75 | 2022-11-21 14:10:10 | [diff] [blame] | 399 | checkValidity(): boolean { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 400 | return this.#internals.checkValidity(); |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 401 | } |
Jack Franklin | 2204d75 | 2022-11-21 14:10:10 | [diff] [blame] | 402 | reportValidity(): boolean { |
Tim van der Lippe | 055956b | 2021-11-25 16:10:56 | [diff] [blame] | 403 | return this.#internals.reportValidity(); |
Alex Rudenko | 351e6f2 | 2021-11-25 09:20:04 | [diff] [blame] | 404 | } |
Alex Rudenko | e4465d1 | 2021-08-09 11:37:37 | [diff] [blame] | 405 | } |
| 406 | |
Benedikt Meurer | d87daa6 | 2024-02-20 11:00:38 | [diff] [blame] | 407 | customElements.define('devtools-button', Button); |