diff options
7 files changed, 168 insertions, 3 deletions
diff --git a/.changeset/metal-crabs-applaud.md b/.changeset/metal-crabs-applaud.md new file mode 100644 index 000000000..c7cb3a26a --- /dev/null +++ b/.changeset/metal-crabs-applaud.md @@ -0,0 +1,5 @@ +--- +"astro": minor +--- + +Adds a new radio checkbox component to the dev toolbar UI library (`astro-dev-toolbar-radio-checkbox`) diff --git a/.changeset/twelve-dolphins-roll.md b/.changeset/twelve-dolphins-roll.md new file mode 100644 index 000000000..baf944281 --- /dev/null +++ b/.changeset/twelve-dolphins-roll.md @@ -0,0 +1,5 @@ +--- +"astro": minor +--- + +Adds a new `buttonBorderRadius` property to the `astro-dev-toolbar-button` component for the dev toolbar component library. This property can be useful to make a fully rounded button with an icon in the center. diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 0cff203cf..af839ae61 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -33,6 +33,7 @@ import type { DevToolbarCard, DevToolbarHighlight, DevToolbarIcon, + DevToolbarRadioCheckbox, DevToolbarSelect, DevToolbarToggle, DevToolbarTooltip, @@ -3087,6 +3088,7 @@ declare global { 'astro-dev-toolbar-icon': DevToolbarIcon; 'astro-dev-toolbar-card': DevToolbarCard; 'astro-dev-toolbar-select': DevToolbarSelect; + 'astro-dev-toolbar-radio-checkbox': DevToolbarRadioCheckbox; // Deprecated names // TODO: Remove in Astro 5.0 diff --git a/packages/astro/src/runtime/client/dev-toolbar/entrypoint.ts b/packages/astro/src/runtime/client/dev-toolbar/entrypoint.ts index dc8d043a2..2558a4db2 100644 --- a/packages/astro/src/runtime/client/dev-toolbar/entrypoint.ts +++ b/packages/astro/src/runtime/client/dev-toolbar/entrypoint.ts @@ -25,6 +25,7 @@ document.addEventListener('DOMContentLoaded', async () => { DevToolbarBadge, DevToolbarIcon, DevToolbarSelect, + DevToolbarRadioCheckbox, }, ] = await Promise.all([ loadDevToolbarApps() as DevToolbarAppDefinition[], @@ -48,6 +49,7 @@ document.addEventListener('DOMContentLoaded', async () => { customElements.define('astro-dev-toolbar-badge', DevToolbarBadge); customElements.define('astro-dev-toolbar-icon', DevToolbarIcon); customElements.define('astro-dev-toolbar-select', DevToolbarSelect); + customElements.define('astro-dev-toolbar-radio-checkbox', DevToolbarRadioCheckbox); // Add deprecated names // TODO: Remove in Astro 5.0 diff --git a/packages/astro/src/runtime/client/dev-toolbar/ui-library/button.ts b/packages/astro/src/runtime/client/dev-toolbar/ui-library/button.ts index 9a3705817..067a1cf2a 100644 --- a/packages/astro/src/runtime/client/dev-toolbar/ui-library/button.ts +++ b/packages/astro/src/runtime/client/dev-toolbar/ui-library/button.ts @@ -2,13 +2,16 @@ import { settings } from '../settings.js'; const sizes = ['small', 'medium', 'large'] as const; const styles = ['ghost', 'outline', 'purple', 'gray', 'red', 'green', 'yellow', 'blue'] as const; +const borderRadii = ['normal', 'rounded'] as const; type ButtonSize = (typeof sizes)[number]; type ButtonStyle = (typeof styles)[number]; +type ButtonBorderRadius = (typeof borderRadii)[number]; export class DevToolbarButton extends HTMLElement { _size: ButtonSize = 'small'; _buttonStyle: ButtonStyle = 'purple'; + _buttonBorderRadius: ButtonBorderRadius = 'normal'; get size() { return this._size; @@ -40,7 +43,22 @@ export class DevToolbarButton extends HTMLElement { this.updateStyle(); } - static observedAttributes = ['button-style', 'size']; + get buttonBorderRadius() { + return this._buttonBorderRadius; + } + + set buttonBorderRadius(value) { + if (!borderRadii.includes(value)) { + settings.logger.error( + `Invalid border-radius: ${value}, expected one of ${borderRadii.join(', ')}, got ${value}.` + ); + return; + } + this._buttonBorderRadius = value; + this.updateStyle(); + } + + static observedAttributes = ['button-style', 'size', 'button-border-radius']; shadowRoot: ShadowRoot; @@ -88,8 +106,14 @@ export class DevToolbarButton extends HTMLElement { --small-font-size: 12px; --large-padding: 12px 16px; + --large-rounded-padding: 12px 12px; --medium-padding: 8px 12px; + --medium-rounded-padding: 8px 8px; --small-padding: 4px 8px; + --small-rounded-padding: 4px 4px; + + --normal-border-radius: 4px; + --rounded-border-radius: 9999px; border: 1px solid var(--border); padding: var(--padding); @@ -97,7 +121,7 @@ export class DevToolbarButton extends HTMLElement { background: var(--background); color: var(--text-color); - border-radius: 4px; + border-radius: var(--border-radius); display: flex; align-items: center; justify-content: center; @@ -137,8 +161,13 @@ export class DevToolbarButton extends HTMLElement { --background: var(--${this.buttonStyle}-background); --border: var(--${this.buttonStyle}-border); --font-size: var(--${this.size}-font-size); - --padding: var(--${this.size}-padding); --text-color: var(--${this.buttonStyle}-text); + ${ + this.buttonBorderRadius === 'normal' + ? '--padding: var(--' + this.size + '-padding);' + : '--padding: var(--' + this.size + '-rounded-padding);' + } + --border-radius: var(--${this.buttonBorderRadius}-border-radius); }`; } } diff --git a/packages/astro/src/runtime/client/dev-toolbar/ui-library/index.ts b/packages/astro/src/runtime/client/dev-toolbar/ui-library/index.ts index 7b1197ab7..56765c4ca 100644 --- a/packages/astro/src/runtime/client/dev-toolbar/ui-library/index.ts +++ b/packages/astro/src/runtime/client/dev-toolbar/ui-library/index.ts @@ -7,3 +7,4 @@ export { DevToolbarSelect } from './select.js'; export { DevToolbarToggle } from './toggle.js'; export { DevToolbarTooltip } from './tooltip.js'; export { DevToolbarWindow } from './window.js'; +export { DevToolbarRadioCheckbox } from './radio-checkbox.js'; diff --git a/packages/astro/src/runtime/client/dev-toolbar/ui-library/radio-checkbox.ts b/packages/astro/src/runtime/client/dev-toolbar/ui-library/radio-checkbox.ts new file mode 100644 index 000000000..a223bf1a8 --- /dev/null +++ b/packages/astro/src/runtime/client/dev-toolbar/ui-library/radio-checkbox.ts @@ -0,0 +1,121 @@ +const styles = ['purple', 'gray', 'red', 'green', 'yellow', 'blue'] as const; + +type RadioStyle = (typeof styles)[number]; + +export class DevToolbarRadioCheckbox extends HTMLElement { + private _radioStyle: RadioStyle = 'purple'; + input: HTMLInputElement; + + shadowRoot: ShadowRoot; + + get radioStyle() { + return this._radioStyle; + } + + set radioStyle(value) { + if (!styles.includes(value)) { + console.error(`Invalid style: ${value}, expected one of ${styles.join(', ')}.`); + return; + } + this._radioStyle = value; + this.updateStyle(); + } + + static observedAttributes = ['radio-style', 'checked', 'disabled', 'name', 'value']; + + constructor() { + super(); + this.shadowRoot = this.attachShadow({ mode: 'open' }); + + this.shadowRoot.innerHTML = ` + <style> + :host { + --purple-unchecked: rgba(224, 204, 250, 0.33); + --purple-checked: rgba(224, 204, 250, 1); + + --gray-unchecked: rgba(191, 193, 201, 0.33); + --gray-checked: rgba(191, 193, 201, 1); + + --red-unchecked: rgba(249, 196, 215, 0.33); + --red-checked: rgba(179, 62, 102, 1); + + --green-unchecked: rgba(213, 249, 196, 0.33); + --green-checked: rgba(61, 125, 31, 1); + + --yellow-unchecked: rgba(255, 236, 179, 0.33); + --yellow-checked: rgba(181, 138, 45, 1); + + --blue-unchecked: rgba(189, 195, 255, 0.33); + --blue-checked: rgba(54, 69, 217, 1); + } + + input[type="radio"] { + appearance: none; + -webkit-appearance: none; + display: flex; + align-content: center; + justify-content: center; + border: 2px solid var(--unchecked-color); + border-radius: 9999px; + width: 16px; + height: 16px; + } + + input[type="radio"]::before { + content: ""; + background-color: var(--checked-color); + width: 8px; + height: 8px; + border-radius: 9999px; + visibility: hidden; + margin: 2px; + } + + input[type="radio"]:checked { + border-color: var(--checked-color); + } + + input[type="radio"]:checked::before { + visibility: visible; + } + </style> + <style id="selected-style"></style> + `; + this.input = document.createElement('input'); + this.input.type = 'radio'; + this.shadowRoot.append(this.input); + } + + connectedCallback() { + this.updateInputState(); + this.updateStyle(); + } + + updateStyle() { + const styleElement = this.shadowRoot.querySelector<HTMLStyleElement>('#selected-style'); + + if (styleElement) { + styleElement.innerHTML = ` + :host { + --unchecked-color: var(--${this._radioStyle}-unchecked); + --checked-color: var(--${this._radioStyle}-checked); + } + `; + } + } + + updateInputState() { + this.input.checked = this.hasAttribute('checked'); + this.input.disabled = this.hasAttribute('disabled'); + this.input.name = this.getAttribute('name') || ''; + this.input.value = this.getAttribute('value') || ''; + } + + attributeChangedCallback() { + if (this.hasAttribute('radio-style')) { + this.radioStyle = this.getAttribute('radio-style') as RadioStyle; + } + + this.updateInputState(); + } +} |