chrome-devtools-frontend
Version:
Chrome DevTools UI
192 lines (168 loc) • 5.75 kB
text/typescript
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
export const enum AdornerCategories {
SECURITY = 'Security',
LAYOUT = 'Layout',
DEFAULT = 'Default',
}
export interface AdornerSetting {
adorner: string;
isEnabled: boolean;
}
export type AdornerSettingsMap = Map<string, boolean>;
export interface RegisteredAdorner {
readonly name: string;
readonly category: AdornerCategories;
readonly enabledByDefault: boolean;
}
// TODO(crbug.com/1167717): Make this a const enum again
// eslint-disable-next-line rulesdir/const_enum
export enum RegisteredAdorners {
GRID = 'grid',
SUBGRID = 'subgrid',
FLEX = 'flex',
AD = 'ad',
SCROLL_SNAP = 'scroll-snap',
CONTAINER = 'container',
SLOT = 'slot',
TOP_LAYER = 'top-layer',
REVEAL = 'reveal',
}
// This enum-like const object serves as the authoritative registry for all the
// adorners available.
export function getRegisteredAdorner(which: RegisteredAdorners): RegisteredAdorner {
switch (which) {
case RegisteredAdorners.GRID:
return {
name: 'grid',
category: AdornerCategories.LAYOUT,
enabledByDefault: true,
};
case RegisteredAdorners.SUBGRID:
return {
name: 'subgrid',
category: AdornerCategories.LAYOUT,
enabledByDefault: true,
};
case RegisteredAdorners.FLEX:
return {
name: 'flex',
category: AdornerCategories.LAYOUT,
enabledByDefault: true,
};
case RegisteredAdorners.AD:
return {
name: 'ad',
category: AdornerCategories.SECURITY,
enabledByDefault: true,
};
case RegisteredAdorners.SCROLL_SNAP:
return {
name: 'scroll-snap',
category: AdornerCategories.LAYOUT,
enabledByDefault: true,
};
case RegisteredAdorners.CONTAINER:
return {
name: 'container',
category: AdornerCategories.LAYOUT,
enabledByDefault: true,
};
case RegisteredAdorners.SLOT:
return {
name: 'slot',
category: AdornerCategories.LAYOUT,
enabledByDefault: true,
};
case RegisteredAdorners.TOP_LAYER:
return {
name: 'top-layer',
category: AdornerCategories.LAYOUT,
enabledByDefault: true,
};
case RegisteredAdorners.REVEAL:
return {
name: 'reveal',
category: AdornerCategories.DEFAULT,
enabledByDefault: true,
};
}
}
let adornerNameToCategoryMap: Map<string, AdornerCategories>|undefined = undefined;
function getCategoryFromAdornerName(name: string): AdornerCategories {
if (!adornerNameToCategoryMap) {
adornerNameToCategoryMap = new Map();
for (const {name, category} of Object.values(RegisteredAdorners).map(getRegisteredAdorner)) {
adornerNameToCategoryMap.set(name, category);
}
}
return adornerNameToCategoryMap.get(name) || AdornerCategories.DEFAULT;
}
export const DefaultAdornerSettings: AdornerSetting[] =
Object.values(RegisteredAdorners).map(getRegisteredAdorner).map(({name, enabledByDefault}) => ({
adorner: name,
isEnabled: enabledByDefault,
}));
interface SettingStore<Setting> {
get(): Setting;
set(setting: Setting): void;
}
export class AdornerManager {
#adornerSettings: AdornerSettingsMap = new Map();
#settingStore: SettingStore<AdornerSetting[]>;
constructor(settingStore: SettingStore<AdornerSetting[]>) {
this.#settingStore = settingStore;
this.#syncSettings();
}
updateSettings(settings: AdornerSettingsMap): void {
this.#adornerSettings = settings;
this.#persistCurrentSettings();
}
getSettings(): Readonly<AdornerSettingsMap> {
return this.#adornerSettings;
}
isAdornerEnabled(adornerText: string): boolean {
return this.#adornerSettings.get(adornerText) || false;
}
#persistCurrentSettings(): void {
const settingList = [];
for (const [adorner, isEnabled] of this.#adornerSettings) {
settingList.push({adorner, isEnabled});
}
this.#settingStore.set(settingList);
}
#loadSettings(): void {
const settingList = this.#settingStore.get();
for (const setting of settingList) {
this.#adornerSettings.set(setting.adorner, setting.isEnabled);
}
}
#syncSettings(): void {
this.#loadSettings();
// Prune outdated adorners and add new ones to the persistence.
const outdatedAdorners = new Set(this.#adornerSettings.keys());
for (const {adorner, isEnabled} of DefaultAdornerSettings) {
outdatedAdorners.delete(adorner);
if (!this.#adornerSettings.has(adorner)) {
this.#adornerSettings.set(adorner, isEnabled);
}
}
for (const outdatedAdorner of outdatedAdorners) {
this.#adornerSettings.delete(outdatedAdorner);
}
this.#persistCurrentSettings();
}
}
const OrderedAdornerCategories = [
AdornerCategories.SECURITY,
AdornerCategories.LAYOUT,
AdornerCategories.DEFAULT,
];
// Use idx + 1 for the order to avoid JavaScript's 0 == false issue
export const AdornerCategoryOrder = new Map(OrderedAdornerCategories.map((category, idx) => [category, idx + 1]));
export function compareAdornerNamesByCategory(nameA: string, nameB: string): number {
const orderA = AdornerCategoryOrder.get(getCategoryFromAdornerName(nameA)) || Number.POSITIVE_INFINITY;
const orderB = AdornerCategoryOrder.get(getCategoryFromAdornerName(nameB)) || Number.POSITIVE_INFINITY;
return orderA - orderB;
}