Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(toolbar): Allow every element to have every color #10186

Merged
merged 9 commits into from
Mar 8, 2024
Merged
5 changes: 5 additions & 0 deletions .changeset/khaki-elephants-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"astro": minor
---

Adds the ability to set colors on all the included UI elements for dev toolbar apps. Previously, only badge and buttons could be customized.
10 changes: 8 additions & 2 deletions packages/astro/src/runtime/client/dev-toolbar/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ function getSettings() {
localStorage.setItem('astro:dev-toolbar:settings', JSON.stringify(_settings));
}

function log(message: string) {
function log(message: string, level: 'log' | 'warn' | 'error' = 'log') {
// eslint-disable-next-line no-console
console.log(
console[level](
`%cAstro`,
'background: linear-gradient(66.77deg, #D83333 0%, #F041FF 100%); color: white; padding-inline: 4px; border-radius: 2px; font-family: monospace;',
message
Expand All @@ -46,6 +46,12 @@ function getSettings() {
updateSetting,
logger: {
log,
warn: (message: string) => {
log(message, 'warn');
},
error: (message: string) => {
log(message, 'error');
},
verboseLog: (message: string) => {
if (_settings.verbose) {
log(message);
Expand Down
120 changes: 84 additions & 36 deletions packages/astro/src/runtime/client/dev-toolbar/ui-library/badge.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,53 @@
type BadgeSize = 'small' | 'large';
type BadgeStyle = 'purple' | 'gray' | 'red' | 'green' | 'yellow';
import { settings } from '../settings.js';

const sizes = ['small', 'large'] as const;
const styles = ['purple', 'gray', 'red', 'green', 'yellow', 'blue'] as const;

type BadgeSize = (typeof sizes)[number];
type BadgeStyle = (typeof styles)[number];

export class DevToolbarBadge extends HTMLElement {
size: BadgeSize = 'small';
badgeStyle: BadgeStyle = 'purple';
_size: BadgeSize = 'small';
_badgeStyle: BadgeStyle = 'purple';

get size() {
return this._size;
}

set size(value) {
if (!sizes.includes(value)) {
settings.logger.error(
`Invalid size: ${value}, expected one of ${sizes.join(', ')}, got ${value}.`
);
return;
}
this._size = value;
this.updateStyle();
}

get badgeStyle() {
return this._badgeStyle;
}

set badgeStyle(value) {
if (!styles.includes(value)) {
settings.logger.error(
`Invalid style: ${value}, expected one of ${styles.join(', ')}, got ${value}.`
);
return;
}
this._badgeStyle = value;
this.updateStyle();
}

shadowRoot: ShadowRoot;

static observedAttributes = ['badge-style', 'size'];

constructor() {
super();
this.shadowRoot = this.attachShadow({ mode: 'open' });

if (this.hasAttribute('size')) this.size = this.getAttribute('size') as BadgeSize;

if (this.hasAttribute('badge-style'))
this.badgeStyle = this.getAttribute('badge-style') as BadgeStyle;

const classes = [`badge--${this.size}`, `badge--${this.badgeStyle}`];
this.shadowRoot.innerHTML = `
<style>
.badge {
Expand All @@ -25,47 +56,64 @@ export class DevToolbarBadge extends HTMLElement {
border: 1px solid transparent;
padding: 8px;
font-size: 12px;
color: #fff;
height: 20px;
color: var(--text-color);
height: var(--size);
border: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: center;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}

.badge--large {
height: 24px;
}
--purple-text: rgba(224, 204, 250, 1);
--purple-border: rgba(113, 24, 226, 1);

.badge--gray {
color: rgba(191, 193, 201, 1);
border-color: rgba(191, 193, 201, 1);
}
--gray-text: rgba(191, 193, 201, 1);
--gray-border:rgba(191, 193, 201, 1);

.badge--purple {
color: rgba(224, 204, 250, 1);
border-color: rgba(113, 24, 226, 1);
}
--red-text: rgba(249, 196, 215, 1);
--red-border: rgba(179, 62, 102, 1);

.badge--red {
color: rgba(249, 196, 215, 1);
border-color: rgba(179, 62, 102, 1);
}
--green-text: rgba(213, 249, 196, 1);
--green-border: rgba(61, 125, 31, 1);

.badge--green {
color: rgba(213, 249, 196, 1);
border-color: rgba(61, 125, 31, 1);
}
--yellow-text: rgba(249, 233, 196, 1);
--yellow-border: rgba(181, 138, 45, 1);

.badge--yellow {
color: rgba(249, 233, 196, 1);
border-color: rgba(181, 138, 45, 1);
--blue-text: rgba(189, 195, 255, 1);
--blue-border: rgba(54, 69, 217, 1);

--large: 24px;
--small: 20px;
}
</style>
<style id="selected-style"></style>

<div class="badge ${classes.join(' ')}">
<div class="badge">
<slot></slot>
</div>
`;
}

connectedCallback() {
this.updateStyle();
}

attributeChangedCallback() {
if (this.hasAttribute('badge-style'))
this.badgeStyle = this.getAttribute('badge-style') as BadgeStyle;

if (this.hasAttribute('size')) this.size = this.getAttribute('size') as BadgeSize;
}

updateStyle() {
const style = this.shadowRoot.getElementById('selected-style') as HTMLStyleElement;

style.innerHTML = `
.badge {
--text-color: var(--${this.badgeStyle}-text);
--border-color: var(--${this.badgeStyle}-border);
--size: var(--${this.size});
}
`;
}
}
163 changes: 112 additions & 51 deletions packages/astro/src/runtime/client/dev-toolbar/ui-library/button.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,110 @@
type ButtonSize = 'small' | 'medium' | 'large';
type ButtonStyle = 'ghost' | 'outline' | 'purple' | 'gray' | 'red';
import { settings } from '../settings.js';

const sizes = ['small', 'medium', 'large'] as const;
const styles = ['ghost', 'outline', 'purple', 'gray', 'red', 'green', 'yellow', 'blue'] as const;

type ButtonSize = (typeof sizes)[number];
type ButtonStyle = (typeof styles)[number];

export class DevToolbarButton extends HTMLElement {
size: ButtonSize = 'small';
buttonStyle: ButtonStyle = 'purple';
_size: ButtonSize = 'small';
_buttonStyle: ButtonStyle = 'purple';

get size() {
return this._size;
}

set size(value) {
if (!sizes.includes(value)) {
settings.logger.error(
`Invalid size: ${value}, expected one of ${sizes.join(', ')}, got ${value}.`
);
return;
}
this._size = value;
this.updateStyle();
}

get buttonStyle() {
return this._buttonStyle;
}

set buttonStyle(value) {
if (!styles.includes(value)) {
settings.logger.error(
`Invalid style: ${value}, expected one of ${styles.join(', ')}, got ${value}.`
);
return;
}
this._buttonStyle = value;
this.updateStyle();
}

static observedAttributes = ['button-style', 'size'];

shadowRoot: ShadowRoot;

constructor() {
super();
this.shadowRoot = this.attachShadow({ mode: 'open' });

if (this.hasAttribute('size')) this.size = this.getAttribute('size') as ButtonSize;

if (this.hasAttribute('button-style'))
this.buttonStyle = this.getAttribute('button-style') as ButtonStyle;

const classes = [`button--${this.size}`, `button--${this.buttonStyle}`];

this.shadowRoot.innerHTML = `
<style>
button {
border: 1px solid transparent;
color: #fff;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
--purple-background: rgba(113, 24, 226, 1);
--purple-border: rgba(224, 204, 250, 0.33);
--purple-text: #fff;

button:hover {
cursor: pointer;
}
--gray-background: rgba(52, 56, 65, 1);
--gray-border: rgba(71, 78, 94, 1);
--gray-text: #fff;

.button--small {
font-size: 12px;
padding: 4px 8px;
}
--red-background: rgba(179, 62, 102, 1);
--red-border: rgba(249, 196, 215, 0.33);
--red-text: #fff;

.button--medium {
font-size: 14px;
padding: 8px 12px;
}
--green-background: rgba(213, 249, 196, 1);
--green-border: rgba(61, 125, 31, 1);
--green-text: #000;

.button--large {
font-size: 16px;
padding: 12px 16px;
}
--yellow-background: rgba(255, 236, 179, 1);
--yellow-border: rgba(255, 191, 0, 1);
--yellow-text: #000;

.button--ghost {
background: transparent;
}
--blue-background: rgba(54, 69, 217, 1);
--blue-border: rgba(189, 195, 255, 1);
--blue-text: #fff;

.button--outline {
background: transparent;
border-color: #fff;
}
--outline-background: transparent;
--outline-border: #fff;
--outline-text: #fff;

.button--purple {
background: rgba(113, 24, 226, 1);
border-color: rgba(224, 204, 250, 0.33);
}
--ghost-background: transparent;
--ghost-border: transparent;
--ghost-text: #fff;

--large-font-size: 16px;
--medium-font-size: 14px;
--small-font-size: 12px;

--large-padding: 12px 16px;
--medium-padding: 8px 12px;
--small-padding: 4px 8px;

.button--gray {
background: rgba(52, 56, 65, 1);
border-color: rgba(71, 78, 94, 1);
border: 1px solid var(--border);
padding: var(--padding);
font-size: var(--font-size);
background: var(--background);

color: var(--text-color);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}

.button--red {
background: rgba(179, 62, 102, 1);
border-color: rgba(249, 196, 215, 0.33);
button:hover {
cursor: pointer;
}

/* TODO: Remove "astro-dev-overlay-icon" in Astro 5.0 */
Expand All @@ -81,10 +116,36 @@ export class DevToolbarButton extends HTMLElement {
margin-left: 0.5em;
}
</style>
<style id="selected-style"></style>

<button class="${classes.join(' ')}">
<button>
<slot></slot>
</button>
`;
}

connectedCallback() {
this.updateStyle();
}

updateStyle() {
const style = this.shadowRoot.getElementById('selected-style') as HTMLStyleElement;

style.innerHTML = `
button {
--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);
}
`;
}

attributeChangedCallback() {
if (this.hasAttribute('size')) this.size = this.getAttribute('size') as ButtonSize;

if (this.hasAttribute('button-style'))
this.buttonStyle = this.getAttribute('button-style') as ButtonStyle;
}
}
Loading
Loading