/*** Copyright (c) Meta Platforms, Inc. and affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @flow*/import * as React from 'react';
import {
useContext,
unstable_useCacheRefresh as useCacheRefresh,
useTransition,
} from 'react';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
import Store from '../../store';
import sharedStyles from './InspectedElementSharedStyles.css';
import styles from './InspectedElementErrorsAndWarningsTree.css';
import {SettingsContext} from '../Settings/SettingsContext';
import {
clearErrorsForElement as clearErrorsForElementAPI,
clearWarningsForElement as clearWarningsForElementAPI,
} from 'react-devtools-shared/src/backendAPI';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
type Props = {
bridge: FrontendBridge,
inspectedElement: InspectedElement,
store: Store,
};export default function InspectedElementErrorsAndWarningsTree({
bridge,inspectedElement,store,}: Props): React.Node {
const refresh = useCacheRefresh();
const [isErrorsTransitionPending, startClearErrorsTransition] =
useTransition();
const clearErrorsForInspectedElement = () => {
const {id} = inspectedElement;
const rendererID = store.getRendererIDForElement(id);
if (rendererID !== null) {
startClearErrorsTransition(() => {
clearErrorsForElementAPI({
bridge,
id,
rendererID,
});refresh();
});}};const [isWarningsTransitionPending, startClearWarningsTransition] =
useTransition();
const clearWarningsForInspectedElement = () => {
const {id} = inspectedElement;
const rendererID = store.getRendererIDForElement(id);
if (rendererID !== null) {
startClearWarningsTransition(() => {
clearWarningsForElementAPI({
bridge,
id,
rendererID,
});refresh();
});}};const {showInlineWarningsAndErrors} = useContext(SettingsContext);
if (!showInlineWarningsAndErrors) {
return null;
}const {errors, warnings} = inspectedElement;
return (
<React.Fragment>
{errors.length > 0 && (
<Tree
badgeClassName={styles.ErrorBadge}
bridge={bridge}
className={styles.ErrorTree}
clearMessages={clearErrorsForInspectedElement}
entries={errors}
isTransitionPending={isErrorsTransitionPending}
label="errors"
messageClassName={styles.Error}
/>)}
{warnings.length > 0 && (
<Tree
badgeClassName={styles.WarningBadge}
bridge={bridge}
className={styles.WarningTree}
clearMessages={clearWarningsForInspectedElement}
entries={warnings}
isTransitionPending={isWarningsTransitionPending}
label="warnings"
messageClassName={styles.Warning}
/>)}
</React.Fragment>
);}type TreeProps = {badgeClassName: string,actions: React$Node,
className: string,clearMessages: () => void,entries: Array<[string, number]>,
isTransitionPending: boolean,label: string,messageClassName: string,};function Tree({badgeClassName,actions,className,clearMessages,entries,isTransitionPending,label,messageClassName,}: TreeProps) {if (entries.length === 0) {
return null;}return (<div className={`${sharedStyles.InspectedElementTree} ${className}`}>
<div className={`${sharedStyles.HeaderRow} ${styles.HeaderRow}`}>
<div className={sharedStyles.Header}>{label}</div>
<Buttondisabled={isTransitionPending}onClick={clearMessages}title={`Clear all ${label} for this component`}>
<ButtonIcon type="clear" /></Button></div>{entries.map(([message, count], index) => (
<ErrorOrWarningViewkey={`${label}-${index}`}
badgeClassName={badgeClassName}className={messageClassName}count={count}message={message}/>))}</div>);}type ErrorOrWarningViewProps = {badgeClassName: string,className: string,count: number,message: string,};function ErrorOrWarningView({className,badgeClassName,count,message,}: ErrorOrWarningViewProps) {return (<div className={className}>{count > 1 && <div className={badgeClassName}>{count}</div>}<div className={styles.Message} title={message}>
{message}</div></div>);}