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 the user to change the placement #10591

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dry-eels-yell.md
Original file line number Diff line number Diff line change
@@ -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.
33 changes: 33 additions & 0 deletions packages/astro/e2e/dev-toolbar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
});
});
2 changes: 2 additions & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type {
DevToolbarCard,
DevToolbarHighlight,
DevToolbarIcon,
DevToolbarSelect,
DevToolbarToggle,
DevToolbarTooltip,
DevToolbarWindow,
Expand Down Expand Up @@ -2933,6 +2934,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
Expand Down
7 changes: 6 additions & 1 deletion packages/astro/src/runtime/client/dev-toolbar/apps/astro.ts
Original file line number Diff line number Diff line change
@@ -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:https://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>';
Expand Down Expand Up @@ -45,6 +49,7 @@ export default {
});

closeOnOutsideClick(eventTarget);
synchronizePlacementOnUpdate(eventTarget, canvas);

function fetchIntegrationData() {
fetch('https://astro.build/api/v1/dev-overlay/', {
Expand Down
42 changes: 40 additions & 2 deletions packages/astro/src/runtime/client/dev-toolbar/apps/settings.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -55,6 +76,7 @@ export default {
document.addEventListener('astro:after-swap', createSettingsWindow);

closeOnOutsideClick(eventTarget);
synchronizePlacementOnUpdate(eventTarget, canvas);

function createSettingsWindow() {
const windowElement = createWindowElement(
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

Expand Down Expand Up @@ -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;
});
}
7 changes: 6 additions & 1 deletion packages/astro/src/runtime/client/dev-toolbar/apps/xray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:https://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>';
Expand All @@ -25,6 +29,7 @@ export default {
document.addEventListener('astro:page-load', refreshIslandsOverlayPositions);

closeOnOutsideClick(eventTarget);
synchronizePlacementOnUpdate(eventTarget, canvas);

function addIslandsOverlay() {
islandsOverlays.forEach(({ highlightElement }) => {
Expand Down
2 changes: 2 additions & 0 deletions packages/astro/src/runtime/client/dev-toolbar/entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ document.addEventListener('DOMContentLoaded', async () => {
DevToolbarButton,
DevToolbarBadge,
DevToolbarIcon,
DevToolbarSelect,
},
] = await Promise.all([
loadDevToolbarApps() as DevToolbarAppDefinition[],
Expand All @@ -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
Expand Down
6 changes: 5 additions & 1 deletion packages/astro/src/runtime/client/dev-toolbar/settings.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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));
}
Expand Down
31 changes: 26 additions & 5 deletions packages/astro/src/runtime/client/dev-toolbar/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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%;
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Loading
Loading