Skip to content

Commit

Permalink
Migrate Elements panel RulesList to new GenericList component
Browse files Browse the repository at this point in the history
  • Loading branch information
bvaughn committed Sep 8, 2023
1 parent 8757659 commit 5ac37f2
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 186 deletions.
38 changes: 10 additions & 28 deletions src/devtools/client/inspector/markup/components/rules/RulesList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ReactElement, useEffect, useMemo, useRef, useState } from "react";
import { FixedSizeList as List } from "react-window";
import { ReactElement, useMemo, useState } from "react";

import { getItemCount } from "devtools/client/inspector/markup/components/rules/utils/getItemCount";
import { RulesListData } from "devtools/client/inspector/markup/components/rules/RulesListData";
import { GenericList } from "replay-next/components/windowing/GenericList";
import { RuleState } from "ui/suspense/styleCaches";

import { ITEM_SIZE, ItemData, RulesListItem } from "./RulesListItem";
import { ITEM_SIZE, RulesListItem, RulesListItemData } from "./RulesListItem";

export function RulesList({
height,
Expand All @@ -19,14 +19,7 @@ export function RulesList({
}) {
const [showPseudoElements, setShowPseudoElements] = useState(true);

const outerRef = useRef<HTMLDivElement>(null);

const itemCount = useMemo(
() => getItemCount(rules, showPseudoElements),
[rules, showPseudoElements]
);

const itemData = useMemo<ItemData>(
const itemData = useMemo<RulesListItemData>(
() => ({
rules,
searchText,
Expand All @@ -36,26 +29,15 @@ export function RulesList({
[rules, showPseudoElements, searchText]
);

// react-window doesn't provide a way to declaratively set data-* attributes
useEffect(() => {
const element = outerRef.current;
if (element) {
element.setAttribute("data-test-id", "RulesList");
}
});

if (itemCount === 0) {
return noContentFallback;
}

return (
<List
children={RulesListItem}
<GenericList
dataTestId="RulesList"
fallbackForEmptyList={noContentFallback}
height={height}
itemCount={itemCount}
itemData={itemData}
itemRendererComponent={RulesListItem}
itemSize={ITEM_SIZE}
outerRef={outerRef}
listDataImplementation={RulesListData}
width="100%"
/>
);
Expand Down
140 changes: 140 additions & 0 deletions src/devtools/client/inspector/markup/components/rules/RulesListData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { RulesListItemData } from "devtools/client/inspector/markup/components/rules/RulesListItem";
import { GenericListData } from "replay-next/components/windowing/GenericListData";
import { RuleState } from "ui/suspense/styleCaches";

import { Item } from "./types";

export class RulesListData extends GenericListData<Item, RulesListItemData> {
private _rules: RuleState[] = [];
private _showPseudoElements: boolean = false;

getItemAtIndexImplementation(index: number): Item {
let currentInheritedNodeId = null;
let currentPseudoElement = null;

for (let rulesIndex = 0; rulesIndex < this._rules.length; rulesIndex++) {
const rule = this._rules[rulesIndex];

if (rule.declarations.filter(declaration => !declaration.isInvisible).length === 0) {
continue; // Collapse empty sets
}

if (!!rule.pseudoElement !== !!currentPseudoElement) {
currentPseudoElement = rule.pseudoElement;

if (index === 0) {
return {
isPseudoElement: !!rule.pseudoElement,
type: "pseudo",
};
} else {
index--;
}
}

if (rule.pseudoElement && !this._showPseudoElements) {
continue;
}

if (rule.inheritance?.inheritedSource != null) {
if ((rule.inheritance?.inheritedNodeId ?? null) !== currentInheritedNodeId) {
currentInheritedNodeId = rule.inheritance?.inheritedNodeId ?? null;

if (index === 0) {
return {
inheritedSource: rule.inheritance.inheritedSource,
type: "inheritance",
};
} else {
index--;
}
}
}

if (index === 0) {
return {
rule,
type: "header",
};
} else {
index--;
}

for (
let declarationIndex = 0;
declarationIndex < rule.declarations.length;
declarationIndex++
) {
if (index === 0) {
return {
declaration: rule.declarations[declarationIndex],
type: "declaration",
};
} else {
index--;
}
}

if (index === 0) {
return {
rule,
type: "footer",
};
} else {
index--;
}
}

throw Error("Could not find data for row");
}

getItemCountImplementation(): number {
let count = 0;
let currentInheritedNodeId = null;
let currentPseudoElement = null;

for (let index = 0; index < this._rules.length; index++) {
const rule = this._rules[index];

if (rule.declarations.filter(declaration => !declaration.isInvisible).length === 0) {
continue; // Collapse empty sets
}

if (!!rule.pseudoElement !== !!currentPseudoElement) {
count++;
currentPseudoElement = rule.pseudoElement;
}

if (rule.pseudoElement && !this._showPseudoElements) {
continue;
}

if (rule?.inheritance?.inheritedSource != null) {
// Render a header the first time a new inheritance section is encountered
if ((rule.inheritance?.inheritedNodeId ?? null) !== currentInheritedNodeId) {
currentInheritedNodeId = rule.inheritance?.inheritedNodeId ?? null;

count++;
}
}

count += 2; // Header and footer
count += rule.declarations.length;
}

return count;
}

updateItemDataImplementation(itemData: RulesListItemData): boolean {
const { rules, showPseudoElements } = itemData;

if (this._rules != rules || this._showPseudoElements != showPseudoElements) {
this._rules = rules;
this._showPseudoElements = showPseudoElements;

return true;
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { CSSProperties, Dispatch, Fragment, ReactNode, SetStateAction } from "react";

import {
isInheritanceItemData,
isPseudoElementItemData,
isRuleStateItemData,
Item,
isInheritanceItem,
isPseudoElementItem,
isRuleStateItem,
} from "devtools/client/inspector/markup/components/rules/types";
import { getItemDataForIndex } from "devtools/client/inspector/markup/components/rules/utils/getItemDataForIndex";
import Expandable from "replay-next/components/Expandable";
import { GenericListItemData } from "replay-next/components/windowing/GenericList";
import { DeclarationState, RuleState } from "ui/suspense/styleCaches";

import styles from "./RulesListItem.module.css";

export const ITEM_SIZE = 20;

export type ItemData = {
export type RulesListItemData = {
rules: RuleState[];
searchText: string;
setShowPseudoElements: Dispatch<SetStateAction<boolean>>;
Expand All @@ -25,34 +26,35 @@ export function RulesListItem({
index,
style,
}: {
data: ItemData;
data: GenericListItemData<Item, RulesListItemData>;
index: number;
style: CSSProperties;
}) {
const { rules, searchText, setShowPseudoElements, showPseudoElements } = data;
const { itemData, listData } = data;
const { searchText, setShowPseudoElements, showPseudoElements } = itemData;

const itemData = getItemDataForIndex(index, rules, showPseudoElements);
const item = listData.getItemAtIndex(index);

if (isInheritanceItemData(itemData)) {
if (isInheritanceItem(item)) {
return (
<InheritanceRenderer index={index} inheritedSource={itemData.inheritedSource} style={style} />
<InheritanceRenderer index={index} inheritedSource={item.inheritedSource} style={style} />
);
} else if (isRuleStateItemData(itemData)) {
return itemData.type === "header" ? (
} else if (isRuleStateItem(item)) {
return item.type === "header" ? (
<RuleStateHeaderRenderer
index={index}
rule={itemData.rule}
rule={item.rule}
searchText={searchText}
style={style}
/>
) : (
<RuleStateFooterRenderer index={index} style={style} />
);
} else if (isPseudoElementItemData(itemData)) {
} else if (isPseudoElementItem(item)) {
return (
<PseudoElementRenderer
index={index}
isPseudoElement={itemData.isPseudoElement}
isPseudoElement={item.isPseudoElement}
setShowPseudoElements={setShowPseudoElements}
showPseudoElements={showPseudoElements}
style={style}
Expand All @@ -61,7 +63,7 @@ export function RulesListItem({
} else {
return (
<DeclarationStateRenderer
declaration={itemData.declaration}
declaration={item.declaration}
index={index}
searchText={searchText}
style={style}
Expand Down
24 changes: 9 additions & 15 deletions src/devtools/client/inspector/markup/components/rules/types.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,39 @@
import { DeclarationState, RuleState } from "ui/suspense/styleCaches";

export type DeclarationStateItemData = {
export type DeclarationStateItem = {
declaration: DeclarationState;
type: "declaration";
};

export type InheritanceItemData = {
export type InheritanceItem = {
inheritedSource: string;
type: "inheritance";
};

export type PseudoElementItemData = {
export type PseudoElementItem = {
isPseudoElement: boolean;
type: "pseudo";
};

export type RuleStateItemData = {
export type RuleStateItem = {
rule: RuleState;
type: "header" | "footer";
};

export type ItemData =
| DeclarationStateItemData
| InheritanceItemData
| PseudoElementItemData
| RuleStateItemData;
export type Item = DeclarationStateItem | InheritanceItem | PseudoElementItem | RuleStateItem;

export function isDeclarationStateItemData(
itemData: ItemData
): itemData is DeclarationStateItemData {
export function isDeclarationStateItem(itemData: Item): itemData is DeclarationStateItem {
return itemData.type === "declaration";
}

export function isInheritanceItemData(itemData: ItemData): itemData is InheritanceItemData {
export function isInheritanceItem(itemData: Item): itemData is InheritanceItem {
return itemData.type === "inheritance";
}

export function isPseudoElementItemData(itemData: ItemData): itemData is PseudoElementItemData {
export function isPseudoElementItem(itemData: Item): itemData is PseudoElementItem {
return itemData.type === "pseudo";
}

export function isRuleStateItemData(itemData: ItemData): itemData is RuleStateItemData {
export function isRuleStateItem(itemData: Item): itemData is RuleStateItem {
return itemData.type === "header" || itemData.type === "footer";
}

This file was deleted.

Loading

0 comments on commit 5ac37f2

Please sign in to comment.