0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00

feat(toolbar): allow the user to change the placement (#10591)

* feat(toolbar): add `placement` to settings

* feat(toolbar): update `settings.placement` with `<select>`

* feat(toolbar): adjust position based on `settings.placement`

* test(toolbar): add a test case for `settings.placement`

* refactor(toolbar): extract select element from settings app

* feat(toolbar): allow select element to have colors

* test(toolbar): fix failed test case

* refactor(toolbar): add `placement` property to window element

* refactor(toolbar): notify apps when placement changes

* test(toolbar): fix failed test case

* refactor(toolbar): extract `synchronizePlacementOnUpdate` function

* chore: add changeset

* chore: update changeset

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>

---------

Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com>
Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
Ming-jun Lu 2024-04-10 22:40:38 +08:00 committed by GitHub
parent 3807e3e868
commit 39988ef8e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 313 additions and 13 deletions

View file

@ -0,0 +1,5 @@
---
"astro": minor
---
Adds a new dev toolbar settings option to change the horizontal placement of the dev toolbar on your screen: bottom left, bottom center, or bottom right.

View file

@ -349,4 +349,37 @@ test.describe('Dev Toolbar', () => {
await expect(appButton).not.toHaveClass('active');
}
});
test('can adjust the placement', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/audit-no-warning'));
const toolbar = page.locator('astro-dev-toolbar');
const settingsAppButton = toolbar.locator('button[data-app-id="astro:settings"]');
await settingsAppButton.click();
const settingsAppCanvas = toolbar.locator(
'astro-dev-toolbar-app-canvas[data-app-id="astro:settings"]'
);
const settingsWindow = settingsAppCanvas.locator('astro-dev-toolbar-window');
await expect(settingsWindow).toBeVisible();
for (const placement of ['bottom-left', 'bottom-center', 'bottom-right']) {
const select = toolbar.getByRole('combobox');
await expect(select).toBeVisible();
await select.selectOption(placement);
const toolbarRoot = toolbar.locator('#dev-toolbar-root');
await expect(toolbarRoot).toHaveAttribute('data-placement', placement);
for (const appId of ['astro:home', 'astro:xray', 'astro:settings']) {
const appButton = toolbar.locator(`button[data-app-id="${appId}"]`);
await appButton.click();
const appCanvas = toolbar.locator(`astro-dev-toolbar-app-canvas[data-app-id="${appId}"]`);
const appWindow = appCanvas.locator('astro-dev-toolbar-window');
await expect(appWindow).toBeVisible();
await expect(appWindow).toHaveJSProperty('placement', placement);
}
}
});
});

View file

@ -28,6 +28,7 @@ import type {
DevToolbarCard,
DevToolbarHighlight,
DevToolbarIcon,
DevToolbarSelect,
DevToolbarToggle,
DevToolbarTooltip,
DevToolbarWindow,
@ -3003,6 +3004,7 @@ declare global {
'astro-dev-toolbar-button': DevToolbarButton;
'astro-dev-toolbar-icon': DevToolbarIcon;
'astro-dev-toolbar-card': DevToolbarCard;
'astro-dev-toolbar-select': DevToolbarSelect;
// Deprecated names
// TODO: Remove in Astro 5.0

View file

@ -1,7 +1,11 @@
import type { DevToolbarApp, DevToolbarMetadata } from '../../../../@types/astro.js';
import { type Icon, isDefinedIcon } from '../ui-library/icons.js';
import { colorForIntegration, iconForIntegration } from './utils/icons.js';
import { closeOnOutsideClick, createWindowElement } from './utils/window.js';
import {
closeOnOutsideClick,
createWindowElement,
synchronizePlacementOnUpdate,
} from './utils/window.js';
const astroLogo =
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 99 26" width="100"><path fill="#fff" d="M6.70402 22.1453c-1.17459-1.0737-1.51748-3.3297-1.02811-4.9641.84853 1.0304 2.02424 1.3569 3.24204 1.5411 1.88005.2844 3.72635.178 5.47285-.6813.1998-.0984.3844-.2292.6027-.3617.1639.4755.2065.9554.1493 1.4439-.1392 1.1898-.7313 2.1088-1.673 2.8054-.3765.2787-.775.5278-1.1639.7905-1.1948.8075-1.518 1.7544-1.0691 3.1318.0107.0336.0202.0671.0444.149-.6101-.273-1.0557-.6705-1.39518-1.1931-.3586-.5517-.52921-1.1619-.53819-1.8221-.00449-.3213-.00449-.6455-.0477-.9623-.10551-.7722-.46804-1.118-1.15102-1.1379-.70094-.0205-1.2554.4129-1.40244 1.0953-.01122.0523-.02749.1041-.04377.1649l.00112.0006Z"/><path fill="url(#paint0_linear_386_2739)" d="M6.70402 22.1453c-1.17459-1.0737-1.51748-3.3297-1.02811-4.9641.84853 1.0304 2.02424 1.3569 3.24204 1.5411 1.88005.2844 3.72635.178 5.47285-.6813.1998-.0984.3844-.2292.6027-.3617.1639.4755.2065.9554.1493 1.4439-.1392 1.1898-.7313 2.1088-1.673 2.8054-.3765.2787-.775.5278-1.1639.7905-1.1948.8075-1.518 1.7544-1.0691 3.1318.0107.0336.0202.0671.0444.149-.6101-.273-1.0557-.6705-1.39518-1.1931-.3586-.5517-.52921-1.1619-.53819-1.8221-.00449-.3213-.00449-.6455-.0477-.9623-.10551-.7722-.46804-1.118-1.15102-1.1379-.70094-.0205-1.2554.4129-1.40244 1.0953-.01122.0523-.02749.1041-.04377.1649l.00112.0006Z"/><path fill="#fff" d="M0 16.909s3.47815-1.6944 6.96603-1.6944l2.62973-8.13858c.09846-.39359.38592-.66106.71044-.66106.3246 0 .612.26747.7105.66106l2.6297 8.13858c4.1309 0 6.966 1.6944 6.966 1.6944S14.7045.814589 14.693.782298C14.5234.306461 14.2371 0 13.8512 0H6.76183c-.38593 0-.66063.306461-.84174.782298C5.90733.81398 0 16.909 0 16.909ZM36.671 11.7318c0 1.4262-1.7739 2.2779-4.2302 2.2779-1.5985 0-2.1638-.3962-2.1638-1.2281 0-.8715.7018-1.2875 2.3003-1.2875 1.4426 0 2.6707.0198 4.0937.1981v.0396Zm.0195-1.7629c-.8772-.19808-2.2028-.31693-3.7818-.31693-4.6006 0-6.7644 1.08943-6.7644 3.62483 0 2.6344 1.4815 3.6446 4.9125 3.6446 2.9046 0 4.8735-.7328 5.5947-2.5354h.117c-.0195.4358-.039.8716-.039 1.2083 0 .931.156 1.0102.9162 1.0102h3.5869c-.1949-.5546-.3119-2.1194-.3119-3.4663 0-1.446.0585-2.5355.0585-4.00123 0-2.99098-1.7934-4.89253-7.4077-4.89253-2.4173 0-5.1074.41596-7.1543 1.03.1949.81213.4679 2.45617.6043 3.5258 1.774-.83193 4.2887-1.18847 6.2381-1.18847 2.6902 0 3.4309.61404 3.4309 1.86193v.4952ZM46.5325 12.5637c-.4874.0594-1.1502.0594-1.8325.0594-.7213 0-1.3841-.0198-1.8324-.0792 0 .1585-.0195.3367-.0195.4952 0 2.476 1.618 3.922 7.3102 3.922 5.3609 0 7.0958-1.4262 7.0958-3.9418 0-2.3769-1.1501-3.5456-6.238-3.8031-3.9573-.17827-4.3082-.61404-4.3082-1.10924 0-.57442.5068-.87154 3.158-.87154 2.7487 0 3.4894.37635 3.4894 1.16866v.17827c.3899-.01981 1.0917-.03961 1.813-.03961.6823 0 1.423.0198 1.8519.05942 0-.17827.0195-.33674.0195-.47539 0-2.91175-2.4172-3.86252-7.0958-3.86252-5.2634 0-7.0373 1.2875-7.0373 3.8031 0 2.25805 1.423 3.66445 6.472 3.88235 3.7233.1188 4.1327.5348 4.1327 1.1092 0 .6141-.6043.8914-3.2165.8914-3.0021 0-3.7623-.416-3.7623-1.2677v-.1189ZM63.6883 2.125c-1.423 1.32712-3.9768 2.65425-5.3998 3.01079.0195.73289.0195 2.07982.0195 2.81271l1.3061.01981c-.0195 1.40635-.039 3.10979-.039 4.23889 0 2.6344 1.3841 4.6152 5.6922 4.6152 1.813 0 3.0216-.1981 4.5226-.515-.1559-.9706-.3314-2.4562-.3898-3.5852-.8968.2971-2.0274.4556-3.275.4556-1.735 0-2.4368-.4754-2.4368-1.8422 0-1.1884 0-2.29767.0195-3.32768 2.2223.01981 4.4446.05943 5.7507.09904-.0195-1.03.0195-2.51559.078-3.50598-1.8909.03961-4.0157.05942-5.7702.05942.0195-.87154.039-1.70347.0585-2.5354h-.1365ZM75.3313 7.35427c.0195-1.03001.039-1.90156.0585-2.75329h-3.9183c.0585 1.70347.0585 3.44656.0585 6.00172 0 2.5553-.0195 4.3182-.0585 6.0018h4.4836c-.078-1.1885-.0975-3.189-.0975-4.8925 0-2.69388 1.0917-3.46638 3.5674-3.46638 1.1502 0 1.9689.13865 2.6902.39615.0195-1.01019.2144-2.97117.3314-3.84271-.7408-.21789-1.5595-.35655-2.5537-.35655-2.1249-.0198-3.6844.85174-4.4056 2.93156l-.156-.0198ZM94.8501 10.5235c0 2.1591-1.5595 3.1693-4.0157 3.1693-2.4368 0-3.9963-.9508-3.9963-3.1693 0-2.21846 1.579-3.05039 3.9963-3.05039 2.4367 0 4.0157.89135 4.0157 3.05039Zm4.0743-.099c0-4.29832-3.353-6.21968-8.09-6.21968-4.7566 0-7.9926 1.92136-7.9926 6.21968 0 4.2785 3.0216 6.5762 7.9731 6.5762 4.9904 0 8.1095-2.2977 8.1095-6.5762Z"/><defs><linearGradient id="paint0_linear_386_2739" x1="5.46011" x2="16.8017" y1="25.9999" y2="20.6412" gradientUnits="userSpaceOnUse"><stop stop-color="#D83333"/><stop offset="1" stop-color="#F041FF"/></linearGradient></defs></svg>';
@ -45,6 +49,7 @@ export default {
});
closeOnOutsideClick(eventTarget);
synchronizePlacementOnUpdate(eventTarget, canvas);
function fetchIntegrationData() {
fetch('https://astro.build/api/v1/dev-overlay/', {

View file

@ -1,6 +1,11 @@
import type { DevToolbarApp } from '../../../../@types/astro.js';
import { type Settings, settings } from '../settings.js';
import { closeOnOutsideClick, createWindowElement } from './utils/window.js';
import { isValidPlacement, placements } from '../ui-library/window.js';
import {
closeOnOutsideClick,
createWindowElement,
synchronizePlacementOnUpdate,
} from './utils/window.js';
interface SettingRow {
name: string;
@ -43,6 +48,22 @@ const settingsRows = [
}
},
},
{
name: 'Placement',
description: 'Adjust the placement of the dev toolbar.',
input: 'select',
settingKey: 'placement',
changeEvent: (evt: Event) => {
if (evt.currentTarget instanceof HTMLSelectElement) {
const placement = evt.currentTarget.value;
if (isValidPlacement(placement)) {
document.querySelector('astro-dev-toolbar')?.setToolbarPlacement(placement);
settings.updateSetting('placement', placement);
settings.logger.verboseLog(`Placement set to ${placement}`);
}
}
},
},
] satisfies SettingRow[];
export default {
@ -55,6 +76,7 @@ export default {
document.addEventListener('astro:after-swap', createSettingsWindow);
closeOnOutsideClick(eventTarget);
synchronizePlacementOnUpdate(eventTarget, canvas);
function createSettingsWindow() {
const windowElement = createWindowElement(
@ -161,10 +183,26 @@ export default {
case 'checkbox': {
const astroToggle = document.createElement('astro-dev-toolbar-toggle');
astroToggle.input.addEventListener('change', setting.changeEvent);
astroToggle.input.checked = settings.config[setting.settingKey];
astroToggle.input.checked = settings.config[setting.settingKey] as boolean;
label.append(astroToggle);
break;
}
case 'select': {
const astroSelect = document.createElement('astro-dev-toolbar-select');
placements.forEach((placement) => {
const option = document.createElement('option');
option.setAttribute('value', placement);
if (placement === settings.config[setting.settingKey]) {
option.selected = true;
}
option.textContent =
`${placement.slice(0, 1).toUpperCase()}${placement.slice(1)}`.replace('-', ' ');
astroSelect.append(option);
});
astroSelect.element.addEventListener('change', setting.changeEvent);
label.append(astroSelect);
break;
}
default:
break;
}

View file

@ -1,6 +1,10 @@
export function createWindowElement(content: string) {
import { settings } from '../../settings.js';
import type { Placement } from '../../ui-library/window.js';
export function createWindowElement(content: string, placement = settings.config.placement) {
const windowElement = document.createElement('astro-dev-toolbar-window');
windowElement.innerHTML = content;
windowElement.placement = placement;
return windowElement;
}
@ -30,3 +34,17 @@ export function closeOnOutsideClick(
}
});
}
export function synchronizePlacementOnUpdate(eventTarget: EventTarget, canvas: ShadowRoot) {
eventTarget.addEventListener('placement-updated', (evt) => {
if (!(evt instanceof CustomEvent)) {
return;
}
const windowElement = canvas.querySelector('astro-dev-toolbar-window');
if (!windowElement) {
return;
}
const event: CustomEvent<{ placement: Placement }> = evt;
windowElement.placement = event.detail.placement;
});
}

View file

@ -7,7 +7,11 @@ import {
getElementsPositionInDocument,
positionHighlight,
} from './utils/highlight.js';
import { closeOnOutsideClick, createWindowElement } from './utils/window.js';
import {
closeOnOutsideClick,
createWindowElement,
synchronizePlacementOnUpdate,
} from './utils/window.js';
const icon =
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#fff" d="M7.9 1.5v-.4a1.1 1.1 0 0 1 2.2 0v.4a1.1 1.1 0 1 1-2.2 0Zm-6.4 8.6a1.1 1.1 0 1 0 0-2.2h-.4a1.1 1.1 0 0 0 0 2.2h.4ZM12 3.7a1.1 1.1 0 0 0 1.4-.7l.4-1.1a1.1 1.1 0 0 0-2.1-.8l-.4 1.2a1.1 1.1 0 0 0 .7 1.4Zm-9.7 7.6-1.2.4a1.1 1.1 0 1 0 .8 2.1l1-.4a1.1 1.1 0 1 0-.6-2ZM20.8 17a1.9 1.9 0 0 1 0 2.6l-1.2 1.2a1.9 1.9 0 0 1-2.6 0l-4.3-4.2-1.6 3.6a1.9 1.9 0 0 1-1.7 1.2A1.9 1.9 0 0 1 7.5 20L2.7 5a1.9 1.9 0 0 1 2.4-2.4l15 5a1.9 1.9 0 0 1 .2 3.4l-3.7 1.6 4.2 4.3ZM19 18.3 14.6 14a1.9 1.9 0 0 1 .6-3l3.2-1.5L5.1 5.1l4.3 13.3 1.5-3.2a1.9 1.9 0 0 1 3-.6l4.4 4.4.7-.7Z"/></svg>';
@ -25,6 +29,7 @@ export default {
document.addEventListener('astro:page-load', refreshIslandsOverlayPositions);
closeOnOutsideClick(eventTarget);
synchronizePlacementOnUpdate(eventTarget, canvas);
function addIslandsOverlay() {
islandsOverlays.forEach(({ highlightElement }) => {

View file

@ -23,6 +23,7 @@ document.addEventListener('DOMContentLoaded', async () => {
DevToolbarButton,
DevToolbarBadge,
DevToolbarIcon,
DevToolbarSelect,
},
] = await Promise.all([
loadDevToolbarApps() as DevToolbarAppDefinition[],
@ -45,6 +46,7 @@ document.addEventListener('DOMContentLoaded', async () => {
customElements.define('astro-dev-toolbar-button', DevToolbarButton);
customElements.define('astro-dev-toolbar-badge', DevToolbarBadge);
customElements.define('astro-dev-toolbar-icon', DevToolbarIcon);
customElements.define('astro-dev-toolbar-select', DevToolbarSelect);
// Add deprecated names
// TODO: Remove in Astro 5.0

View file

@ -1,11 +1,15 @@
import type { Placement } from './ui-library/window.js';
export interface Settings {
disableAppNotification: boolean;
verbose: boolean;
placement: Placement;
}
export const defaultSettings = {
disableAppNotification: false,
verbose: false,
placement: 'bottom-center',
} satisfies Settings;
export const settings = getSettings();
@ -25,7 +29,7 @@ function getSettings() {
_settings = { ..._settings, ...JSON.parse(toolbarSettings) };
}
function updateSetting(key: keyof Settings, value: Settings[typeof key]) {
function updateSetting<Key extends keyof Settings>(key: Key, value: Settings[Key]) {
_settings[key] = value;
localStorage.setItem('astro:dev-toolbar:settings', JSON.stringify(_settings));
}

View file

@ -2,6 +2,7 @@
import type { DevToolbarApp as DevToolbarAppDefinition } from '../../../@types/astro.js';
import { settings } from './settings.js';
import { type Icon, getIconElement, isDefinedIcon } from './ui-library/icons.js';
import { type Placement } from './ui-library/window.js';
export type DevToolbarApp = DevToolbarAppDefinition & {
builtIn: boolean;
@ -57,8 +58,6 @@ export class AstroDevToolbar extends HTMLElement {
#dev-toolbar-root {
position: fixed;
bottom: 0px;
left: 50%;
transform: translate(-50%, 0%);
z-index: 2000000010;
display: flex;
flex-direction: column;
@ -75,6 +74,17 @@ export class AstroDevToolbar extends HTMLElement {
opacity: 0.2;
}
#dev-toolbar-root[data-placement="bottom-left"] {
left: 16px;
}
#dev-toolbar-root[data-placement="bottom-center"] {
left: 50%;
transform: translateX(-50%);
}
#dev-toolbar-root[data-placement="bottom-right"] {
right: 16px;
}
#dev-bar-hitbox-above,
#dev-bar-hitbox-below {
width: 100%;
@ -246,9 +256,7 @@ export class AstroDevToolbar extends HTMLElement {
width: 1px;
}
</style>
<div id="dev-toolbar-root" data-hidden ${
settings.config.disableAppNotification ? 'data-no-notification' : ''
}>
<div id="dev-toolbar-root" data-hidden ${settings.config.disableAppNotification ? 'data-no-notification' : ''} data-placement="${settings.config.placement}">
<div id="dev-bar-hitbox-above"></div>
<div id="dev-bar">
<div id="bar-container">
@ -559,6 +567,19 @@ export class AstroDevToolbar extends HTMLElement {
?.querySelector('#dropdown')
?.toggleAttribute('data-no-notification', !newStatus);
}
setToolbarPlacement(newPlacement: Placement) {
this.devToolbarContainer?.setAttribute('data-placement', newPlacement);
this.apps.forEach((app) => {
app.eventTarget.dispatchEvent(
new CustomEvent('placement-updated', {
detail: {
placement: newPlacement,
},
})
);
});
}
}
export class DevToolbarCanvas extends HTMLElement {

View file

@ -3,6 +3,7 @@ export { DevToolbarButton } from './button.js';
export { DevToolbarCard } from './card.js';
export { DevToolbarHighlight } from './highlight.js';
export { DevToolbarIcon } from './icon.js';
export { DevToolbarSelect } from './select.js';
export { DevToolbarToggle } from './toggle.js';
export { DevToolbarTooltip } from './tooltip.js';
export { DevToolbarWindow } from './window.js';

View file

@ -0,0 +1,108 @@
import { settings } from '../settings.js';
const styles = ['purple', 'gray', 'red', 'green', 'yellow', 'blue'] as const;
type SelectStyle = (typeof styles)[number];
export class DevToolbarSelect extends HTMLElement {
shadowRoot: ShadowRoot;
element: HTMLSelectElement;
_selectStyle: SelectStyle = 'gray';
get selectStyle() {
return this._selectStyle;
}
set selectStyle(value) {
if (!styles.includes(value)) {
settings.logger.error(`Invalid style: ${value}, expected one of ${styles.join(', ')}.`);
return;
}
this._selectStyle = value;
this.updateStyle();
}
static observedAttributes = ['select-style'];
constructor() {
super();
this.shadowRoot = this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
:host {
--purple-text: rgba(224, 204, 250, 1);
--purple-border: rgba(113, 24, 226, 1);
--gray-text: rgba(191, 193, 201, 1);
--gray-border:rgba(191, 193, 201, 1);
--red-text: rgba(249, 196, 215, 1);
--red-border: rgba(179, 62, 102, 1);
--green-text: rgba(213, 249, 196, 1);
--green-border: rgba(61, 125, 31, 1);
--yellow-text: rgba(249, 233, 196, 1);
--yellow-border: rgba(181, 138, 45, 1);
--blue-text: rgba(189, 195, 255, 1);
--blue-border: rgba(54, 69, 217, 1);
--text-color: var(--gray-text);
--border-color: var(--gray-border);
}
select {
appearance: none;
text-align-last: center;
display: inline-block;
font-family: system-ui, sans-serif;
font-size: 14px;
padding: 4px 24px 4px 8px;
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-color);
background-color: transparent;
background-image:
linear-gradient(45deg, transparent 50%, var(--text-color) 50%),
linear-gradient(135deg, var(--text-color) 50%, transparent 50%);
background-position:
calc(100% - 12px) calc(1em - 2px),
calc(100% - 8px) calc(1em - 2px);
background-size: 4px 4px;
background-repeat: no-repeat;
}
</style>
<style id="selected-style"></style>
<slot></slot>
`;
this.element = document.createElement('select');
this.shadowRoot.addEventListener('slotchange', (event) => {
if (event.target instanceof HTMLSlotElement) {
// Manually add slotted elements to <select> because it only accepts <option> as children and escapes other elements including <slot>
this.element.append(...event.target.assignedNodes());
}
});
}
connectedCallback() {
this.shadowRoot.append(this.element);
this.updateStyle();
}
attributeChangedCallback() {
if (this.hasAttribute('select-style')) {
this.selectStyle = this.getAttribute('select-style') as SelectStyle;
}
}
updateStyle() {
const style = this.shadowRoot.querySelector<HTMLStyleElement>('#selected-style');
if (style) {
style.innerHTML = `
:host {
--text-color: var(--${this.selectStyle}-text);
--border-color: var(--${this.selectStyle}-border);
}
`;
}
}
}

View file

@ -1,5 +1,32 @@
import { defaultSettings, settings } from '../settings.js';
export const placements = ['bottom-left', 'bottom-center', 'bottom-right'] as const;
export type Placement = (typeof placements)[number];
export function isValidPlacement(value: string): value is Placement {
return placements.map(String).includes(value);
}
export class DevToolbarWindow extends HTMLElement {
shadowRoot: ShadowRoot;
_placement: Placement = defaultSettings.placement;
get placement() {
return this._placement;
}
set placement(value) {
if (!isValidPlacement(value)) {
settings.logger.error(
`Invalid placement: ${value}, expected one of ${placements.join(', ')}, got ${value}.`
);
return;
}
this._placement = value;
this.updateStyle();
}
static observedAttributes = ['placement'];
constructor() {
super();
@ -24,8 +51,6 @@ export class DevToolbarWindow extends HTMLElement {
position: fixed;
z-index: 999999999;
bottom: 72px;
left: 50%;
transform: translateX(-50%);
box-shadow: 0px 0px 0px 0px rgba(19, 21, 26, 0.30), 0px 1px 2px 0px rgba(19, 21, 26, 0.29), 0px 4px 4px 0px rgba(19, 21, 26, 0.26), 0px 10px 6px 0px rgba(19, 21, 26, 0.15), 0px 17px 7px 0px rgba(19, 21, 26, 0.04), 0px 26px 7px 0px rgba(19, 21, 26, 0.01);
}
@ -75,8 +100,41 @@ export class DevToolbarWindow extends HTMLElement {
line-height: 1.5em;
}
</style>
<style id="selected-style"></style>
<slot />
`;
this.updateStyle();
}
attributeChangedCallback() {
if (this.hasAttribute('placement'))
this.placement = this.getAttribute('placement') as Placement;
}
updateStyle() {
const style = this.shadowRoot.querySelector<HTMLStyleElement>('#selected-style');
if (style) {
const styleMap: Record<Placement, string> = {
'bottom-left': `
:host {
left: 16px;
}
`,
'bottom-center': `
:host {
left: 50%;
transform: translateX(-50%);
}
`,
'bottom-right': `
:host {
right: 16px;
}
`,
};
style.innerHTML = styleMap[this.placement];
}
}
}