fix: prevent asset conflicts between React and Grid.js versions

Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

11
node_modules/lighthouse/flow-report/src/app.d.ts generated vendored Normal file
View File

@@ -0,0 +1,11 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
export declare const App: FunctionComponent<{
flowResult: LH.FlowResult;
options?: LH.FlowReportOptions;
}>;
//# sourceMappingURL=app.d.ts.map

71
node_modules/lighthouse/flow-report/src/app.tsx generated vendored Normal file
View File

@@ -0,0 +1,71 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {useLayoutEffect, useMemo, useRef, useState} from 'preact/hooks';
import {Sidebar} from './sidebar/sidebar';
import {Summary} from './summary/summary';
import {classNames, FlowResultContext, OptionsContext, useHashState} from './util';
import {Report} from './wrappers/report';
import {Topbar} from './topbar';
import {Header} from './header';
import {I18nProvider} from './i18n/i18n';
import {Styles} from './wrappers/styles';
function getAnchorElement(hashState: LH.HashState|null) {
if (!hashState || !hashState.anchor) return null;
return document.getElementById(hashState.anchor);
}
const Content: FunctionComponent = () => {
const hashState = useHashState();
const ref = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
const el = getAnchorElement(hashState);
if (el) {
el.scrollIntoView();
} else if (ref.current) {
ref.current.scrollTop = 0;
}
}, [hashState]);
return (
<div ref={ref} className="Content">
{
hashState ?
<>
<Header hashState={hashState}/>
<Report hashState={hashState}/>
</> :
<Summary/>
}
</div>
);
};
export const App: FunctionComponent<{
flowResult: LH.FlowResult,
options?: LH.FlowReportOptions
}> = ({flowResult, options}) => {
const [collapsed, setCollapsed] = useState(false);
const optionsValue = useMemo(() => options || {}, [options]);
return (
<OptionsContext.Provider value={optionsValue}>
<FlowResultContext.Provider value={flowResult}>
<I18nProvider>
<Styles/>
<div className={classNames('App', {'App--collapsed': collapsed})} data-testid="App">
<Topbar onMenuClick={() => setCollapsed(c => !c)} />
<Sidebar/>
<Content/>
</div>
</I18nProvider>
</FlowResultContext.Provider>
</OptionsContext.Provider>
);
};

20
node_modules/lighthouse/flow-report/src/common.d.ts generated vendored Normal file
View File

@@ -0,0 +1,20 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
declare const Separator: FunctionComponent;
declare const FlowStepIcon: FunctionComponent<{
mode: LH.Result.GatherMode;
}>;
declare const FlowSegment: FunctionComponent<{
mode?: LH.Result.GatherMode;
}>;
declare const FlowStepThumbnail: FunctionComponent<{
lhr: LH.Result;
width?: number;
height?: number;
}>;
export { Separator, FlowStepIcon, FlowSegment, FlowStepThumbnail, };
//# sourceMappingURL=common.d.ts.map

123
node_modules/lighthouse/flow-report/src/common.tsx generated vendored Normal file
View File

@@ -0,0 +1,123 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {useEffect, useState} from 'preact/hooks';
import {NavigationIcon, SnapshotIcon, TimespanIcon} from './icons';
import {getFilmstripFrames, getScreenDimensions} from './util';
import {Util} from '../../shared/util.js';
const ANIMATION_FRAME_DURATION_MS = 500;
const Separator: FunctionComponent = () => {
return <div className="Separator" role="separator"></div>;
};
const FlowStepIcon: FunctionComponent<{mode: LH.Result.GatherMode}> = ({mode}) => {
return <>
{
mode === 'navigation' && <NavigationIcon/>
}
{
mode === 'timespan' && <TimespanIcon/>
}
{
mode === 'snapshot' && <SnapshotIcon/>
}
</>;
};
const FlowSegment: FunctionComponent<{mode?: LH.Result.GatherMode}> = ({mode}) => {
return (
<div className="FlowSegment">
<div className="FlowSegment__top-line"/>
{
mode && <FlowStepIcon mode={mode}/>
}
<div className="FlowSegment__bottom-line"/>
</div>
);
};
const FlowStepAnimatedThumbnail: FunctionComponent<{
frames: Array<{data: string}>,
width: number,
height: number,
}> = ({frames, width, height}) => {
const [frameIndex, setFrameIndex] = useState(0);
// Handle a frame array of a different length being set.
const effectiveFrameIndex = frameIndex % frames.length;
useEffect(() => {
const interval = setInterval(
() => setFrameIndex(i => (i + 1) % frames.length),
ANIMATION_FRAME_DURATION_MS
);
return () => clearInterval(interval);
}, [frames.length]);
return (
<img
className="FlowStepThumbnail"
data-testid="FlowStepAnimatedThumbnail"
src={frames[effectiveFrameIndex].data}
style={{width, height}}
alt="Animated screenshots of a page tested by Lighthouse"
/>
);
};
const FlowStepThumbnail: FunctionComponent<{
lhr: LH.Result,
width?: number,
height?: number,
}> = ({lhr, width, height}) => {
const frames = getFilmstripFrames(lhr);
// Resize the image to fit the viewport aspect ratio.
const dimensions = getScreenDimensions(lhr);
if (width && height === undefined) {
height = dimensions.height * width / dimensions.width;
} else if (height && width === undefined) {
width = dimensions.width * height / dimensions.height;
}
if (!width || !height) {
console.warn(new Error('FlowStepThumbnail requested without any dimensions').stack);
return <></>;
}
let thumbnail;
if (frames?.length) {
thumbnail = frames[frames.length - 1].data;
if (lhr.gatherMode === 'timespan') {
return <FlowStepAnimatedThumbnail frames={frames} width={width} height={height} />;
}
} else {
thumbnail = Util.getFullPageScreenshot(lhr)?.screenshot.data;
}
return <>
{
thumbnail &&
<img
className="FlowStepThumbnail"
src={thumbnail}
style={{width, height}}
alt="Screenshot of a page tested by Lighthouse"
/>
}
</>;
};
export {
Separator,
FlowStepIcon,
FlowSegment,
FlowStepThumbnail,
};

10
node_modules/lighthouse/flow-report/src/header.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
export declare const Header: FunctionComponent<{
hashState: LH.HashState;
}>;
//# sourceMappingURL=header.d.ts.map

87
node_modules/lighthouse/flow-report/src/header.tsx generated vendored Normal file
View File

@@ -0,0 +1,87 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {FlowStepIcon, FlowStepThumbnail} from './common';
import {useLocalizedStrings} from './i18n/i18n';
import {getModeDescription, useFlowResult} from './util';
const SIDE_THUMBNAIL_HEIGHT = 80;
const MAIN_THUMBNAIL_HEIGHT = 120;
const HeaderThumbnail: FunctionComponent<{
lhr: LH.Result,
position: 'prev'|'next'|'main'
}> =
({lhr, position}) => {
const height = position === 'main' ? MAIN_THUMBNAIL_HEIGHT : SIDE_THUMBNAIL_HEIGHT;
return (
<div className={`HeaderThumbnail HeaderThumbnail--${position}`}>
<FlowStepThumbnail lhr={lhr} height={height}/>
<div className="HeaderThumbnail__icon">
<FlowStepIcon mode={lhr.gatherMode}/>
</div>
</div>
);
};
export const Header: FunctionComponent<{hashState: LH.HashState}> =
({hashState}) => {
const flowResult = useFlowResult();
const {index} = hashState;
const step = flowResult.steps[index];
const prevStep = flowResult.steps[index - 1];
const nextStep = flowResult.steps[index + 1];
const strings = useLocalizedStrings();
const modeDescription = getModeDescription(step.lhr.gatherMode, strings);
return (
<div className="Header">
{
prevStep && <>
{
flowResult.steps[index - 2] && <div className="Header__segment"/>
}
<div className="Header__prev-thumbnail">
<HeaderThumbnail lhr={prevStep.lhr} position="prev"/>
<div className="Header__segment"/>
</div>
<a
className="Header__prev-title"
href={`#index=${index - 1}`}
>{prevStep.name}</a>
</>
}
<div className="Header__current-thumbnail">
<HeaderThumbnail lhr={step.lhr} position="main"/>
</div>
<div className="Header__current-title">
{step.name}
<div className="Header__current-description">
{modeDescription}
</div>
</div>
{
nextStep && <>
<div className="Header__next-thumbnail">
<div className="Header__segment"/>
<HeaderThumbnail lhr={nextStep.lhr} position="next"/>
</div>
<a
className="Header__next-title"
href={`#index=${index + 1}`}
>{nextStep.name}</a>
{
flowResult.steps[index + 2] && <div className="Header__segment"/>
}
</>
}
</div>
);
};

View File

@@ -0,0 +1,10 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
export declare const HelpDialog: FunctionComponent<{
onClose: () => void;
}>;
//# sourceMappingURL=help-dialog.d.ts.map

119
node_modules/lighthouse/flow-report/src/help-dialog.tsx generated vendored Normal file
View File

@@ -0,0 +1,119 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent, JSX} from 'preact';
import {useLocalizedStrings} from './i18n/i18n';
import {CloseIcon, NavigationIcon, SnapshotIcon, TimespanIcon} from './icons';
const HelpDialogColumn: FunctionComponent<{
icon: JSX.Element,
userFriendlyModeLabel: string;
lighthouseOfficialModeLabel: string;
modeDescription: string;
useCaseInstruction: string;
useCases: string[];
availableCategories: string[];
}> = (props) => {
const strings = useLocalizedStrings();
return (
<div className="HelpDialogColumn">
<div className="HelpDialogColumn__legend">
<div className="HelpDialogColumnTimeline">
{props.icon}
<div className="HelpDialogColumnTimeline__line" />
</div>
<div className="HelpDialogColumn__legend-label">{props.userFriendlyModeLabel}</div>
</div>
<div className="HelpDialogColumn__header">
<div className="HelpDialogColumn__header-title">{props.lighthouseOfficialModeLabel}</div>
<p>{props.modeDescription}</p>
</div>
<div className="HelpDialogColumn__use-cases">
<p>{props.useCaseInstruction}</p>
<ul>
{props.useCases.map((useCase, i) => (
<li key={i}>{useCase}</li>
))}
</ul>
</div>
<div className="HelpDialogColumn__categories">
<p>{strings.categories}</p>
<ul>
{props.availableCategories.map((category, i) => (
<li key={i}>{category}</li>
))}
</ul>
</div>
</div>
);
};
export const HelpDialog: FunctionComponent<{onClose: () => void}> = ({
onClose,
}) => {
const strings = useLocalizedStrings();
return (
<div className="HelpDialog">
<div className="HelpDialog__title">
<div>{strings.helpDialogTitle}</div>
<div style={{flexGrow: 1}} />
<button className="HelpDialog__close" onClick={onClose}>
<CloseIcon />
</button>
</div>
<div className="HelpDialog__columns">
<HelpDialogColumn
icon={<NavigationIcon />}
userFriendlyModeLabel={strings.navigationDescription}
lighthouseOfficialModeLabel={strings.navigationReport}
modeDescription={strings.navigationLongDescription}
useCaseInstruction={strings.helpUseCaseInstructionNavigation}
useCases={[
strings.helpUseCaseNavigation1,
strings.helpUseCaseNavigation2,
strings.helpUseCaseNavigation3,
]}
availableCategories={[
strings.categoryPerformance,
strings.categoryAccessibility,
strings.categoryBestPractices,
strings.categorySeo,
strings.categoryProgressiveWebApp,
]}
/>
<HelpDialogColumn
icon={<TimespanIcon />}
userFriendlyModeLabel={strings.timespanDescription}
lighthouseOfficialModeLabel={strings.timespanReport}
modeDescription={strings.timespanLongDescription}
useCaseInstruction={strings.helpUseCaseInstructionTimespan}
useCases={[strings.helpUseCaseTimespan1, strings.helpUseCaseTimespan2]}
availableCategories={[
strings.categoryPerformance,
strings.categoryBestPractices,
]}
/>
<HelpDialogColumn
icon={<SnapshotIcon />}
userFriendlyModeLabel={strings.snapshotDescription}
lighthouseOfficialModeLabel={strings.snapshotReport}
modeDescription={strings.snapshotLongDescription}
useCaseInstruction={strings.helpUseCaseInstructionSnapshot}
useCases={[strings.helpUseCaseSnapshot1, strings.helpUseCaseSnapshot2]}
availableCategories={[
strings.categoryPerformance,
strings.categoryAccessibility,
strings.categoryBestPractices,
strings.categorySeo,
]}
/>
</div>
</div>
);
};

218
node_modules/lighthouse/flow-report/src/i18n/i18n.d.ts generated vendored Normal file
View File

@@ -0,0 +1,218 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
import { I18nFormatter } from '../../../report/renderer/i18n-formatter';
declare function useI18n(): {
formatter: I18nFormatter;
strings: {
navigationDescription: string;
timespanDescription: string;
snapshotDescription: string;
navigationLongDescription: string;
timespanLongDescription: string;
snapshotLongDescription: string;
navigationReport: string;
timespanReport: string;
snapshotReport: string;
summary: string;
allReports: string;
title: string;
categories: string;
categoryPerformance: string;
categoryAccessibility: string;
categoryBestPractices: string;
categorySeo: string;
categoryProgressiveWebApp: string;
desktop: string;
mobile: string;
ratingPass: string;
ratingAverage: string;
ratingFail: string;
ratingError: string;
navigationReportCount: string;
timespanReportCount: string;
snapshotReportCount: string;
save: string;
helpLabel: string;
helpDialogTitle: string;
helpUseCaseInstructionNavigation: string;
helpUseCaseInstructionTimespan: string;
helpUseCaseInstructionSnapshot: string;
helpUseCaseNavigation1: string;
helpUseCaseNavigation2: string;
helpUseCaseNavigation3: string;
helpUseCaseTimespan1: string;
helpUseCaseTimespan2: string;
helpUseCaseSnapshot1: string;
helpUseCaseSnapshot2: string;
passedAuditCount: string;
passableAuditCount: string;
informativeAuditCount: string;
highestImpact: string;
varianceDisclaimer: string;
calculatorLink: string;
showRelevantAudits: string;
opportunityResourceColumnLabel: string;
opportunitySavingsColumnLabel: string;
errorMissingAuditInfo: string;
errorLabel: string;
warningHeader: string;
warningAuditsGroupTitle: string;
passedAuditsGroupTitle: string;
notApplicableAuditsGroupTitle: string;
manualAuditsGroupTitle: string;
toplevelWarningsMessage: string;
crcInitialNavigation: string;
crcLongestDurationLabel: string;
snippetExpandButtonLabel: string;
snippetCollapseButtonLabel: string;
lsPerformanceCategoryDescription: string;
labDataTitle: string;
thirdPartyResourcesLabel: string;
viewTreemapLabel: string;
viewTraceLabel: string;
viewOriginalTraceLabel: string;
dropdownPrintSummary: string;
dropdownPrintExpanded: string;
dropdownCopyJSON: string;
dropdownSaveHTML: string;
dropdownSaveJSON: string;
dropdownViewer: string;
dropdownSaveGist: string;
dropdownDarkTheme: string;
runtimeSettingsDevice: string;
runtimeSettingsNetworkThrottling: string;
runtimeSettingsCPUThrottling: string;
runtimeSettingsUANetwork: string;
runtimeSettingsBenchmark: string;
runtimeSettingsAxeVersion: string;
runtimeSettingsScreenEmulation: string;
footerIssue: string;
runtimeNoEmulation: string;
runtimeMobileEmulation: string;
runtimeDesktopEmulation: string;
runtimeUnknown: string;
runtimeSingleLoad: string;
runtimeAnalysisWindow: string;
runtimeSingleLoadTooltip: string;
throttlingProvided: string;
show: string;
hide: string;
expandView: string;
collapseView: string;
runtimeSlow4g: string;
runtimeCustom: string;
firstPartyChipLabel: string;
openInANewTabTooltip: string;
unattributable: string;
};
};
declare function useLocalizedStrings(): {
navigationDescription: string;
timespanDescription: string;
snapshotDescription: string;
navigationLongDescription: string;
timespanLongDescription: string;
snapshotLongDescription: string;
navigationReport: string;
timespanReport: string;
snapshotReport: string;
summary: string;
allReports: string;
title: string;
categories: string;
categoryPerformance: string;
categoryAccessibility: string;
categoryBestPractices: string;
categorySeo: string;
categoryProgressiveWebApp: string;
desktop: string;
mobile: string;
ratingPass: string;
ratingAverage: string;
ratingFail: string;
ratingError: string;
navigationReportCount: string;
timespanReportCount: string;
snapshotReportCount: string;
save: string;
helpLabel: string;
helpDialogTitle: string;
helpUseCaseInstructionNavigation: string;
helpUseCaseInstructionTimespan: string;
helpUseCaseInstructionSnapshot: string;
helpUseCaseNavigation1: string;
helpUseCaseNavigation2: string;
helpUseCaseNavigation3: string;
helpUseCaseTimespan1: string;
helpUseCaseTimespan2: string;
helpUseCaseSnapshot1: string;
helpUseCaseSnapshot2: string;
passedAuditCount: string;
passableAuditCount: string;
informativeAuditCount: string;
highestImpact: string;
varianceDisclaimer: string;
calculatorLink: string;
showRelevantAudits: string;
opportunityResourceColumnLabel: string;
opportunitySavingsColumnLabel: string;
errorMissingAuditInfo: string;
errorLabel: string;
warningHeader: string;
warningAuditsGroupTitle: string;
passedAuditsGroupTitle: string;
notApplicableAuditsGroupTitle: string;
manualAuditsGroupTitle: string;
toplevelWarningsMessage: string;
crcInitialNavigation: string;
crcLongestDurationLabel: string;
snippetExpandButtonLabel: string;
snippetCollapseButtonLabel: string;
lsPerformanceCategoryDescription: string;
labDataTitle: string;
thirdPartyResourcesLabel: string;
viewTreemapLabel: string;
viewTraceLabel: string;
viewOriginalTraceLabel: string;
dropdownPrintSummary: string;
dropdownPrintExpanded: string;
dropdownCopyJSON: string;
dropdownSaveHTML: string;
dropdownSaveJSON: string;
dropdownViewer: string;
dropdownSaveGist: string;
dropdownDarkTheme: string;
runtimeSettingsDevice: string;
runtimeSettingsNetworkThrottling: string;
runtimeSettingsCPUThrottling: string;
runtimeSettingsUANetwork: string;
runtimeSettingsBenchmark: string;
runtimeSettingsAxeVersion: string;
runtimeSettingsScreenEmulation: string;
footerIssue: string;
runtimeNoEmulation: string;
runtimeMobileEmulation: string;
runtimeDesktopEmulation: string;
runtimeUnknown: string;
runtimeSingleLoad: string;
runtimeAnalysisWindow: string;
runtimeSingleLoadTooltip: string;
throttlingProvided: string;
show: string;
hide: string;
expandView: string;
collapseView: string;
runtimeSlow4g: string;
runtimeCustom: string;
firstPartyChipLabel: string;
openInANewTabTooltip: string;
unattributable: string;
};
declare function useStringFormatter(): (str: string, values?: Record<string, string | number>) => string;
declare const I18nProvider: FunctionComponent;
export { useI18n, useLocalizedStrings, useStringFormatter, I18nProvider, };
//# sourceMappingURL=i18n.d.ts.map

90
node_modules/lighthouse/flow-report/src/i18n/i18n.tsx generated vendored Normal file
View File

@@ -0,0 +1,90 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {createContext, FunctionComponent} from 'preact';
import {useContext, useMemo} from 'preact/hooks';
import {formatMessage} from '../../../shared/localization/format';
import {I18nFormatter} from '../../../report/renderer/i18n-formatter';
import {UIStrings} from './ui-strings';
import {useFlowResult} from '../util';
import strings from './localized-strings.js';
import {UIStrings as ReportUIStrings} from '../../../report/renderer/report-utils.js';
import {Globals} from '../../../report/renderer/report-globals.js';
const I18nContext = createContext({
formatter: new I18nFormatter('en-US'),
strings: {...ReportUIStrings, ...UIStrings},
});
function useLhrLocale() {
const flowResult = useFlowResult();
const firstLhr = flowResult.steps[0].lhr;
const locale = firstLhr.configSettings.locale;
if (flowResult.steps.some(step => step.lhr.configSettings.locale !== locale)) {
console.warn('LHRs have inconsistent locales');
}
return {
locale,
lhrStrings: firstLhr.i18n.rendererFormattedStrings,
};
}
function useI18n() {
return useContext(I18nContext);
}
function useLocalizedStrings() {
const i18n = useI18n();
return i18n.strings;
}
function useStringFormatter() {
const {locale} = useLhrLocale();
return (str: string, values?: Record<string, string|number>) => {
return formatMessage(str, values, locale);
};
}
const I18nProvider: FunctionComponent = ({children}) => {
const {locale, lhrStrings} = useLhrLocale();
const i18n = useMemo(() => {
Globals.apply({
providedStrings: {
// Preload with strings from the first lhr.
// Used for legacy report components imported into the flow report.
...lhrStrings,
// Set any missing flow strings to default (english) values.
...UIStrings,
// `strings` is generated in build/build-report.js
...strings[locale],
},
i18n: new I18nFormatter(locale),
reportJson: null,
});
return {
formatter: Globals.i18n,
strings: Globals.strings as typeof UIStrings & typeof ReportUIStrings,
};
}, [locale, lhrStrings]);
return (
<I18nContext.Provider value={i18n}>
{children}
</I18nContext.Provider>
);
};
export {
useI18n,
useLocalizedStrings,
useStringFormatter,
I18nProvider,
};

View File

@@ -0,0 +1,9 @@
export default strings;
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
/** @type {Record<LH.Locale, import('./ui-strings.js').UIStringsType>} */
declare const strings: Record<LH.Locale, import('./ui-strings.js').UIStringsType>;
//# sourceMappingURL=localized-strings.d.ts.map

View File

@@ -0,0 +1,11 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
/** @type {Record<LH.Locale, import('./ui-strings.js').UIStringsType>} */
// @ts-expect-error This will be replaced with the localized strings at build time.
const strings = {};
export default strings;

View File

@@ -0,0 +1,48 @@
export namespace UIStrings {
const navigationDescription: string;
const timespanDescription: string;
const snapshotDescription: string;
const navigationLongDescription: string;
const timespanLongDescription: string;
const snapshotLongDescription: string;
const navigationReport: string;
const timespanReport: string;
const snapshotReport: string;
const summary: string;
const allReports: string;
const title: string;
const categories: string;
const categoryPerformance: string;
const categoryAccessibility: string;
const categoryBestPractices: string;
const categorySeo: string;
const categoryProgressiveWebApp: string;
const desktop: string;
const mobile: string;
const ratingPass: string;
const ratingAverage: string;
const ratingFail: string;
const ratingError: string;
const navigationReportCount: string;
const timespanReportCount: string;
const snapshotReportCount: string;
const save: string;
const helpLabel: string;
const helpDialogTitle: string;
const helpUseCaseInstructionNavigation: string;
const helpUseCaseInstructionTimespan: string;
const helpUseCaseInstructionSnapshot: string;
const helpUseCaseNavigation1: string;
const helpUseCaseNavigation2: string;
const helpUseCaseNavigation3: string;
const helpUseCaseTimespan1: string;
const helpUseCaseTimespan2: string;
const helpUseCaseSnapshot1: string;
const helpUseCaseSnapshot2: string;
const passedAuditCount: string;
const passableAuditCount: string;
const informativeAuditCount: string;
const highestImpact: string;
}
export type UIStringsType = typeof UIStrings;
//# sourceMappingURL=ui-strings.d.ts.map

View File

@@ -0,0 +1,136 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
/** @typedef {typeof UIStrings} UIStringsType */
/* eslint-disable max-len */
export const UIStrings = {
/** Description of a report that evaluates a web page as it loads, but before the user interacts with it. */
navigationDescription: 'Page load',
/** Description of a report that evaluates a web page over a period of time where a user could have interacted with the page. */
timespanDescription: 'User interactions',
/** Description of a report that evaluates the state of a web page at a single point in time. */
snapshotDescription: 'Captured state of page',
/** Long-form description of a report that evaluates a web page as it loads, but before the user interacts with it. */
navigationLongDescription: 'Navigation reports analyze a single page load, exactly like the original Lighthouse reports.',
/** Long-form description of a report that evaluates a web page over a period of time where a user could have interacted with the page. */
timespanLongDescription: 'Timespan reports analyze an arbitrary period of time, typically containing user interactions.',
/** Long-form description of a report that evaluates the state of a web page at a single point in time. */
snapshotLongDescription: 'Snapshot reports analyze the page in a particular state, typically after user interactions.',
/** Label for a report that evaluates a page navigation. */
navigationReport: 'Navigation report',
/** Label for a report that evaluates a period of time where a user could have interacted with the page. */
timespanReport: 'Timespan report',
/** Label for a report that evaluates the state of a web page at a single point in time. */
snapshotReport: 'Snapshot report',
/** Title of a home page that summarizes and links to the other pages. */
summary: 'Summary',
/** Title of a report section lists and links to multiple sub-reports. */
allReports: 'All Reports',
/** Default title of a Lighthouse report over a user flow. "User Flow" refers to a series of user interactions on a page that a site developer wants to test. "Lighthouse" is a product name https://developers.google.com/web/tools/lighthouse. */
title: 'Lighthouse User Flow Report',
/** Label for a list of Lighthouse categories. */
categories: 'Categories',
/** Title of the Performance category of audits. Equivalent to 'Web performance', this term is inclusive of all web page speed and loading optimization topics. Also used as a label of a score gauge; try to limit to 20 characters. */
categoryPerformance: 'Performance',
/** Title of the Accessibility category of audits. This section contains audits focused on making web content accessible to all users. Also used as a label of a score gauge; try to limit to 20 characters. */
categoryAccessibility: 'Accessibility',
/** Title of the Best Practices category of audits. This is displayed at the top of a list of audits focused on topics related to following web development best practices and accepted guidelines. Also used as a label of a score gauge; try to limit to 20 characters. */
categoryBestPractices: 'Best Practices',
/** Title of the Search Engine Optimization (SEO) category of audits. This is displayed at the top of a list of audits focused on topics related to optimizing a website for indexing by search engines. Also used as a label of a score gauge; try to limit to 20 characters. */
categorySeo: 'SEO',
/** Title of the Progressive Web Application (PWA) category of audits. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. Also used as a label of a score gauge. */
categoryProgressiveWebApp: 'Progressive Web App',
/** Label for a report evaluating a web page. Label indicates that the report refers to the desktop version of the site. */
desktop: 'Desktop',
/** Label for a report evaluating a web page. Label indicates that the report refers to the mobile version of the site. */
mobile: 'Mobile',
/** Rating indicating that a report category is good/passing. */
ratingPass: 'Good',
/** Rating indicating that a report category is average or needs improvement. */
ratingAverage: 'Average',
/** Rating indicating that a report category is poor/failing. */
ratingFail: 'Poor',
/** Rating indicating that a report category rating could not be calculated because of an error. */
ratingError: 'Error',
/**
* @description Label indicating the number of Lighthouse reports that evaluate a web page as it loads.
* @example {2} numNavigation
*/
navigationReportCount: `{numNavigation, plural,
=1 {{numNavigation} navigation report}
other {{numNavigation} navigation reports}
}`,
/**
* @description Label indicating the number of Lighthouse reports that evaluate a web page over a period of time.
* @example {2} numTimespan
*/
timespanReportCount: `{numTimespan, plural,
=1 {{numTimespan} timespan report}
other {{numTimespan} timespan reports}
}`,
/**
* @description Label indicating the number of Lighthouse reports that evaluate a web page at a single point in time.
* @example {2} numSnapshot
*/
snapshotReportCount: `{numSnapshot, plural,
=1 {{numSnapshot} snapshot report}
other {{numSnapshot} snapshot reports}
}`,
/** Label for a button that saves a Lighthouse report to disk. */
save: 'Save',
/** Label for a button that toggles the help modal with explanations on how to interpret the Lighthouse flow report. */
helpLabel: 'Understanding Flows',
/** Title for a dialog that contains explanations on how to interpret the Lighthouse flow report. */
helpDialogTitle: 'Understanding the Lighthouse Flow Report',
/** Label for a list of example use cases for a mode in lighthouse that evaluates a web page as it loads, but before the user interacts with it. */
helpUseCaseInstructionNavigation: 'Use Navigation reports to...',
/** Label for a list of example use cases for a mode in lighthouse that evaluates a web page over a period of time where a user could have interacted with the page. */
helpUseCaseInstructionTimespan: 'Use Timespan reports to...',
/** Label for a list of example use cases for a mode in lighthouse that evaluates the state of a web page at a single point in time. */
helpUseCaseInstructionSnapshot: 'Use Snapshot reports to...',
/** Example use case for how Lighthouse can be applied in practice. Appears in a list with other examples. */
helpUseCaseNavigation1: 'Obtain a Lighthouse Performance score.',
/** Example use case for how Lighthouse can be applied in practice. Appears in a list with other examples. */
helpUseCaseNavigation2: 'Measure page load Performance metrics such as Largest Contentful Paint and Speed Index.',
/** Example use case for how Lighthouse can be applied in practice. Appears in a list with other examples. */
helpUseCaseNavigation3: 'Assess Progressive Web App capabilities.',
/** Example use case for how Lighthouse can be applied in practice. Appears in a list with other examples. */
helpUseCaseTimespan1: 'Measure layout shifts and JavaScript execution time on a series of interactions.',
/** Example use case for how Lighthouse can be applied in practice. Appears in a list with other examples. */
helpUseCaseTimespan2: 'Discover performance opportunities to improve the experience for long-lived pages and single-page applications.',
/** Example use case for how Lighthouse can be applied in practice. Appears in a list with other examples. */
helpUseCaseSnapshot1: 'Find accessibility issues in single page applications or complex forms.',
/** Example use case for how Lighthouse can be applied in practice. Appears in a list with other examples. */
helpUseCaseSnapshot2: 'Evaluate best practices of menus and UI elements hidden behind interaction.',
/**
* @description Label indicating the number of Lighthouse audits that passed.
* @example {2} numPassed
*/
passedAuditCount: `{numPassed, plural,
=1 {{numPassed} audit passed}
other {{numPassed} audits passed}
}`,
/**
* @description Label indicating the number of Lighthouse audits that are possible to pass for a page.
* @example {2} numPassableAudits
*/
passableAuditCount: `{numPassableAudits, plural,
=1 {{numPassableAudits} passable audit}
other {{numPassableAudits} passable audits}
}`,
/**
* @description Label indicating the number of Lighthouse audits that are informative.
* @example {2} numInformative
*/
informativeAuditCount: `{numInformative, plural,
=1 {{numInformative} informative audit}
other {{numInformative} informative audits}
}`,
/** Label for a list of Lighthouse audits that are the most impactful. */
highestImpact: 'Highest impact',
};

18
node_modules/lighthouse/flow-report/src/icons.d.ts generated vendored Normal file
View File

@@ -0,0 +1,18 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
declare const SummaryIcon: FunctionComponent;
declare const NavigationIcon: FunctionComponent;
declare const TimespanIcon: FunctionComponent;
declare const SnapshotIcon: FunctionComponent;
declare const CloseIcon: FunctionComponent;
declare const EnvIcon: FunctionComponent;
declare const NetworkIcon: FunctionComponent;
declare const CpuIcon: FunctionComponent;
declare const HamburgerIcon: FunctionComponent;
declare const InfoIcon: FunctionComponent;
export { SummaryIcon, NavigationIcon, TimespanIcon, SnapshotIcon, CloseIcon, EnvIcon, NetworkIcon, CpuIcon, HamburgerIcon, InfoIcon, };
//# sourceMappingURL=icons.d.ts.map

152
node_modules/lighthouse/flow-report/src/icons.tsx generated vendored Normal file
View File

@@ -0,0 +1,152 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
/* eslint-disable max-len */
const SummaryIcon: FunctionComponent = () => {
return (
<svg width="14" viewBox="0 0 18 16" fill="none" role="img">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 2C0 1.17 0.67 0.5 1.5 0.5C2.33 0.5 3 1.17 3 2C3 2.83 2.33 3.5 1.5 3.5C0.67 3.5 0 2.83 0 2ZM0 8C0 7.17 0.67 6.5 1.5 6.5C2.33 6.5 3 7.17 3 8C3 8.83 2.33 9.5 1.5 9.5C0.67 9.5 0 8.83 0 8ZM1.5 12.5C0.67 12.5 0 13.18 0 14C0 14.82 0.68 15.5 1.5 15.5C2.32 15.5 3 14.82 3 14C3 13.18 2.33 12.5 1.5 12.5ZM18 15H5V13H18V15ZM5 9H18V7H5V9ZM5 3V1H18V3H5Z" fill="currentColor"/>
</svg>
);
};
const NavigationIcon: FunctionComponent = () => {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
role="img"
aria-label="Icon representing a navigation report">
<circle
cx="8"
cy="8"
r="7"
fill="none"
stroke="currentColor"
stroke-width="2"/>
</svg>
);
};
const TimespanIcon: FunctionComponent = () => {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
role="img"
aria-label="Icon representing a timespan report">
<circle
cx="8"
cy="8"
r="7"
fill="none"
stroke="currentColor"
stroke-width="2"/>
<path
d="m 8,4 v 4 l 4,1.9999998"
stroke="currentColor"
stroke-width="1.5"/>
</svg>
);
};
const SnapshotIcon: FunctionComponent = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
role="img"
aria-label="Icon representing a snapshot report">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M 12.2038,12.2812 C 11.1212,13.3443 9.6372,14 8,14 7.81038,14 7.62281,13.9912 7.43768,13.974 L 10.3094,9 Z M 12.8925,11.4741 10.0207,6.5 H 13.811 C 13.9344,6.97943 14,7.48205 14,8 c 0,1.2947 -0.4101,2.4937 -1.1075,3.4741 z M 13.456,5.5 H 7.71135 L 9.6065,2.21749 C 11.3203,2.69259 12.7258,3.90911 13.456,5.5 Z M 8.5624,2.02601 C 8.3772,2.0088 8.1896,2 8,2 6.36282,2 4.8788,2.65572 3.79622,3.71885 L 5.69061,7.00002 Z M 3.10749,4.52594 C 2.4101,5.5063 2,6.70526 2,8 2,8.5179 2.06563,9.0206 2.18903,9.5 H 5.97927 Z M 2.54404,10.5 c 0.73017,1.5909 2.1357,2.8074 3.84949,3.2825 L 8.2887,10.5 Z M 16,8 c 0,4.4183 -3.5817,8 -8,8 C 3.58172,16 0,12.4183 0,8 0,3.58172 3.58172,0 8,0 c 4.4183,0 8,3.58172 8,8 z"
fill="currentColor"/>
</svg>
);
};
const CloseIcon: FunctionComponent = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="currentColor"
role="img"
aria-label="Icon representing a close action"
>
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
</svg>
);
};
const EnvIcon: FunctionComponent = () => {
return (
<svg width="15" height="12" viewBox="0 0 15 12" fill="none" role="img">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.33317 2.00008H13.9998V0.666748H3.33317C2.59984 0.666748 1.99984 1.26675 1.99984 2.00008V9.33341H0.666504V11.3334H7.99984V9.33341H3.33317V2.00008ZM13.9998 3.33341H9.99984C9.63317 3.33341 9.33317 3.63341 9.33317 4.00008V10.6667C9.33317 11.0334 9.63317 11.3334 9.99984 11.3334H13.9998C14.3665 11.3334 14.6665 11.0334 14.6665 10.6667V4.00008C14.6665 3.63341 14.3665 3.33341 13.9998 3.33341ZM10.6665 9.33341H13.3332V4.66675H10.6665V9.33341Z" fill="currentColor"/>
</svg>
);
};
const NetworkIcon: FunctionComponent = () => {
return (
<svg width="16" height="11" viewBox="0 0 16 11" fill="none" role="img">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666687 3.26663L2.00002 4.59997C3.92002 2.67997 6.52669 1.87997 9.02002 2.18663L9.81335 0.399966C6.59335 -0.173367 3.16002 0.779966 0.666687 3.26663ZM10.6 0.599966C10.4867 0.599966 10.3867 0.659966 10.3267 0.753299L10.28 0.853299L6.82669 8.61996C6.72002 8.8133 6.65335 9.02663 6.65335 9.25996C6.65335 9.99996 7.25335 10.6 7.99335 10.6C8.63335 10.6 9.17335 10.1466 9.30002 9.53996L9.30669 9.51997L10.9334 0.933299C10.9334 0.746633 10.7867 0.599966 10.6 0.599966ZM15.3334 3.26663L14 4.59997C13.1867 3.78663 12.2534 3.17997 11.2534 2.76663L11.6067 0.886633C12.9667 1.38663 14.24 2.1733 15.3334 3.26663ZM11.3334 7.26663L12.6667 5.9333C12.1334 5.39997 11.5334 4.98663 10.8934 4.6733L10.5267 6.61997C10.8067 6.79997 11.08 7.0133 11.3334 7.26663ZM4.66669 7.26663L3.33335 5.9333C4.67335 4.5933 6.45335 3.95997 8.20669 4.0133L7.35335 5.9333C6.37335 6.0733 5.42002 6.5133 4.66669 7.26663Z" fill="currentColor"/>
</svg>
);
};
const CpuIcon: FunctionComponent = () => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" role="img">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.5 7.16667V5.5H13.8333V3.83333C13.8333 2.91667 13.0833 2.16667 12.1667 2.16667H10.5V0.5H8.83333V2.16667H7.16667V0.5H5.5V2.16667H3.83333C2.91667 2.16667 2.16667 2.91667 2.16667 3.83333V5.5H0.5V7.16667H2.16667V8.83333H0.5V10.5H2.16667V12.1667C2.16667 13.0833 2.91667 13.8333 3.83333 13.8333H5.5V15.5H7.16667V13.8333H8.83333V15.5H10.5V13.8333H12.1667C13.0833 13.8333 13.8333 13.0833 13.8333 12.1667V10.5H15.5V8.83333H13.8333V7.16667H15.5ZM10.5 5.5H5.5V10.5H10.5V5.5ZM3.83333 12.1667H12.1667V3.83333H3.83333V12.1667Z" fill="currentColor"/>
</svg>
);
};
const HamburgerIcon: FunctionComponent = () => {
return (
<svg viewBox="0 0 18 12" width="18" height="12" role="img">
<rect width="18" height="2" fill="currentColor"></rect>
<rect y="5" width="18" height="2" fill="currentColor"></rect>
<rect y="10" width="18" height="2" fill="currentColor"></rect>
</svg>
);
};
const InfoIcon: FunctionComponent = () => {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 7C13 10.3137 10.3137 13 7 13C3.68629 13 1 10.3137 1 7C1 3.68629 3.68629 1 7 1C10.3137 1 13 3.68629 13 7ZM14 7C14 10.866 10.866 14 7 14C3.13401 14 0 10.866 0 7C0 3.13401 3.13401 0 7 0C10.866 0 14 3.13401 14 7ZM7.66658 11H6.33325V9.66667H7.66658V11ZM4.33325 5.66667C4.33325 4.19333 5.52659 3 6.99992 3C8.47325 3 9.66658 4.19333 9.66658 5.66667C9.66658 6.52194 9.1399 6.98221 8.62709 7.43036C8.1406 7.85551 7.66658 8.26975 7.66658 9H6.33325C6.33325 7.78582 6.96133 7.30439 7.51355 6.88112C7.94674 6.54907 8.33325 6.25281 8.33325 5.66667C8.33325 4.93333 7.73325 4.33333 6.99992 4.33333C6.26658 4.33333 5.66658 4.93333 5.66658 5.66667H4.33325Z" fill="currentColor"/>
</svg>
);
};
export {
SummaryIcon,
NavigationIcon,
TimespanIcon,
SnapshotIcon,
CloseIcon,
EnvIcon,
NetworkIcon,
CpuIcon,
HamburgerIcon,
InfoIcon,
};

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
export declare const SidebarFlow: FunctionComponent;
//# sourceMappingURL=flow.d.ts.map

View File

@@ -0,0 +1,69 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {FlowSegment} from '../common';
import {classNames, useHashState, useFlowResult} from '../util';
import {Separator} from '../common';
const SidebarFlowStep: FunctionComponent<{
mode: LH.Result.GatherMode,
href: string,
label: string,
isCurrent: boolean,
}> = ({href, label, mode, isCurrent}) => {
return (
<a
className={classNames('SidebarFlowStep', {'Sidebar--current': isCurrent})}
href={href}
>
<div>
<FlowSegment mode={mode}/>
</div>
<div className={`SidebarFlowStep__label SidebarFlowStep__label--${mode}`}>{label}</div>
</a>
);
};
const SidebarFlowSeparator: FunctionComponent = () => {
return (
<div className="SidebarFlowSeparator">
<FlowSegment/>
<Separator/>
<FlowSegment/>
</div>
);
};
export const SidebarFlow: FunctionComponent = () => {
const flowResult = useFlowResult();
const hashState = useHashState();
return (
<div className="SidebarFlow">
{
flowResult.steps.map((step, index) => {
const {lhr, name} = step;
return <>
{
lhr.gatherMode === 'navigation' && index !== 0 ?
<SidebarFlowSeparator/> :
undefined
}
<SidebarFlowStep
key={lhr.fetchTime}
mode={lhr.gatherMode}
href={`#index=${index}`}
label={name}
isCurrent={index === hashState?.index}
/>
</>;
})
}
</div>
);
};

View File

@@ -0,0 +1,17 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
declare const SidebarSummary: FunctionComponent;
declare const SidebarRuntimeSettings: FunctionComponent<{
settings: LH.ConfigSettings;
}>;
declare const SidebarHeader: FunctionComponent<{
title: string;
date: string;
}>;
declare const Sidebar: FunctionComponent;
export { SidebarSummary, SidebarRuntimeSettings, SidebarHeader, Sidebar, };
//# sourceMappingURL=sidebar.d.ts.map

View File

@@ -0,0 +1,106 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {ReportUtils} from '../../../report/renderer/report-utils.js';
import {Separator} from '../common';
import {useI18n, useLocalizedStrings} from '../i18n/i18n';
import {CpuIcon, EnvIcon, NetworkIcon, SummaryIcon} from '../icons';
import {classNames, useHashState, useFlowResult} from '../util';
import {SidebarFlow} from './flow';
const SidebarSummary: FunctionComponent = () => {
const hashState = useHashState();
const strings = useLocalizedStrings();
return (
<a
href="#"
className={classNames('SidebarSummary', {'Sidebar--current': hashState === null})}
data-testid="SidebarSummary"
>
<div className="SidebarSummary__icon">
<SummaryIcon/>
</div>
<div className="SidebarSummary__label">{strings.summary}</div>
</a>
);
};
const SidebarRuntimeSettings: FunctionComponent<{settings: LH.ConfigSettings}> =
({settings}) => {
const strings = useLocalizedStrings();
const env = ReportUtils.getEmulationDescriptions(settings);
const deviceEmulationString = env.screenEmulation ?
`${env.deviceEmulation} - ${env.screenEmulation}` :
env.deviceEmulation;
return (
<div className="SidebarRuntimeSettings">
<div className="SidebarRuntimeSettings__item" title={strings.runtimeSettingsDevice}>
<div className="SidebarRuntimeSettings__item--icon">
<EnvIcon/>
</div>
{
deviceEmulationString
}
</div>
<div
className="SidebarRuntimeSettings__item"
title={strings.runtimeSettingsNetworkThrottling}
>
<div className="SidebarRuntimeSettings__item--icon">
<NetworkIcon/>
</div>
{
env.summary
}
</div>
<div className="SidebarRuntimeSettings__item" title={strings.runtimeSettingsCPUThrottling}>
<div className="SidebarRuntimeSettings__item--icon">
<CpuIcon/>
</div>
{
`${settings.throttling.cpuSlowdownMultiplier}x slowdown`
}
</div>
</div>
);
};
const SidebarHeader: FunctionComponent<{title: string, date: string}> = ({title, date}) => {
const i18n = useI18n();
return (
<div className="SidebarHeader">
<div className="SidebarHeader__title">{title}</div>
<div className="SidebarHeader__date">{i18n.formatter.formatDateTime(date)}</div>
</div>
);
};
const Sidebar: FunctionComponent = () => {
const flowResult = useFlowResult();
const firstLhr = flowResult.steps[0].lhr;
return (
<div className="Sidebar">
<SidebarHeader title={flowResult.name} date={firstLhr.fetchTime}/>
<Separator/>
<SidebarSummary/>
<Separator/>
<SidebarFlow/>
<Separator/>
<SidebarRuntimeSettings settings={firstLhr.configSettings}/>
</div>
);
};
export {
SidebarSummary,
SidebarRuntimeSettings,
SidebarHeader,
Sidebar,
};

View File

@@ -0,0 +1,19 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
declare const SummaryTooltip: FunctionComponent<{
category: LH.ReportResult.Category;
gatherMode: LH.Result.GatherMode;
url: string;
}>;
declare const SummaryCategory: FunctionComponent<{
category: LH.ReportResult.Category | undefined;
href: string;
gatherMode: LH.Result.GatherMode;
finalDisplayedUrl: string;
}>;
export { SummaryTooltip, SummaryCategory, };
//# sourceMappingURL=category.d.ts.map

View File

@@ -0,0 +1,182 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {ReportUtils} from '../../../report/renderer/report-utils.js';
import {Separator} from '../common';
import {CategoryScore} from '../wrappers/category-score';
import {useI18n, useStringFormatter, useLocalizedStrings} from '../i18n/i18n';
import {Markdown} from '../wrappers/markdown';
import type {UIStringsType} from '../i18n/ui-strings';
const MAX_TOOLTIP_AUDITS = 2;
type ScoredAuditRef = LH.ReportResult.AuditRef & {result: {score: number}};
function getGatherModeLabel(gatherMode: LH.Result.GatherMode, strings: UIStringsType) {
switch (gatherMode) {
case 'navigation': return strings.navigationReport;
case 'timespan': return strings.timespanReport;
case 'snapshot': return strings.snapshotReport;
}
}
function getCategoryRating(rating: string, strings: UIStringsType) {
switch (rating) {
case 'pass': return strings.ratingPass;
case 'average': return strings.ratingAverage;
case 'fail': return strings.ratingFail;
case 'error': return strings.ratingError;
}
}
function getScoreToBeGained(audit: ScoredAuditRef): number {
return audit.weight * (1 - audit.result.score);
}
function getOverallSavings(audit: LH.ReportResult.AuditRef): number {
return (
audit.result.details &&
audit.result.details.overallSavingsMs
) || 0;
}
const SummaryTooltipAudit: FunctionComponent<{audit: LH.ReportResult.AuditRef}> = ({audit}) => {
const rating = ReportUtils.calculateRating(audit.result.score, audit.result.scoreDisplayMode);
return (
<div className={`SummaryTooltipAudit SummaryTooltipAudit--${rating}`}>
<Markdown text={audit.result.title}/>
</div>
);
};
const SummaryTooltipAudits: FunctionComponent<{category: LH.ReportResult.Category}> =
({category}) => {
const strings = useLocalizedStrings();
function isRelevantAudit(audit: LH.ReportResult.AuditRef): audit is ScoredAuditRef {
return audit.result.score !== null &&
// Metrics should not be displayed in this group.
audit.group !== 'metrics' &&
// Audits in performance group "hidden" should not be counted.
(audit.group !== 'hidden' || category.id !== 'performance') &&
// We don't want unweighted audits except for opportunities with potential savings.
(audit.weight > 0 || getOverallSavings(audit) > 0) &&
// Passing audits should never be high impact.
!ReportUtils.showAsPassed(audit.result);
}
const audits = category.auditRefs
.filter(isRelevantAudit)
.sort((a, b) => {
// Remaining score should always be 0 for perf opportunities because weight is 0.
// In that case, we want to sort by `overallSavingsMs`.
const remainingScoreA = getScoreToBeGained(a);
const remainingScoreB = getScoreToBeGained(b);
if (remainingScoreA !== remainingScoreB) return remainingScoreB - remainingScoreA;
return getOverallSavings(b) - getOverallSavings(a);
})
.splice(0, MAX_TOOLTIP_AUDITS);
if (!audits.length) return null;
return (
<div className="SummaryTooltipAudits">
<div className="SummaryTooltipAudits__title">{strings.highestImpact}</div>
{
audits.map(audit => <SummaryTooltipAudit key={audit.id} audit={audit}/>)
}
</div>
);
};
const SummaryTooltip: FunctionComponent<{
category: LH.ReportResult.Category,
gatherMode: LH.Result.GatherMode,
url: string,
}> = ({category, gatherMode, url}) => {
const strings = useLocalizedStrings();
const str_ = useStringFormatter();
const {
numPassed,
numPassableAudits,
numInformative,
totalWeight,
} = ReportUtils.calculateCategoryFraction(category);
const i18n = useI18n();
const displayAsFraction = ReportUtils.shouldDisplayAsFraction(gatherMode);
const score = displayAsFraction ?
numPassed / numPassableAudits :
category.score;
const rating = score === null ? 'error' : ReportUtils.calculateRating(score);
return (
<div className="SummaryTooltip">
<div className="SummaryTooltip__title">{getGatherModeLabel(gatherMode, strings)}</div>
<div className="SummaryTooltip__url">{url}</div>
<Separator/>
<div className="SummaryTooltip__category">
<div className="SummaryTooltip__category-title">
{category.title}
</div>
{
totalWeight !== 0 &&
<div className={`SummaryTooltip__rating SummaryTooltip__rating--${rating}`}>
<span>{getCategoryRating(rating, strings)}</span>
{
!displayAsFraction && category.score !== null && <>
<span> · </span>
<span>{i18n.formatter.formatInteger(category.score * 100)}</span>
</>
}
</div>
}
</div>
<div className="SummaryTooltip__fraction">
<span>{str_(strings.passedAuditCount, {numPassed})}</span>
<span> / </span>
<span>{str_(strings.passableAuditCount, {numPassableAudits})}</span>
</div>
{numInformative !== 0 &&
<div className="SummaryTooltip__informative">
{str_(strings.informativeAuditCount, {numInformative})}
</div>
}
<SummaryTooltipAudits category={category}/>
</div>
);
};
const SummaryCategory: FunctionComponent<{
category: LH.ReportResult.Category|undefined,
href: string,
gatherMode: LH.Result.GatherMode,
finalDisplayedUrl: string,
}> = ({category, href, gatherMode, finalDisplayedUrl}) => {
return (
<div className="SummaryCategory">
{
category ?
<div className="SummaryCategory__content">
<CategoryScore
category={category}
href={href}
gatherMode={gatherMode}
/>
<SummaryTooltip category={category} gatherMode={gatherMode} url={finalDisplayedUrl}/>
</div> :
<div className="SummaryCategory__null" data-testid="SummaryCategory__null"/>
}
</div>
);
};
export {
SummaryTooltip,
SummaryCategory,
};

View File

@@ -0,0 +1,18 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
/**
* The div should behave like a JSX <>...</>. This still allows us to identify "rows" with CSS selectors.
*/
declare const SummaryFlowStep: FunctionComponent<{
lhr: LH.Result;
label: string;
hashIndex: number;
}>;
declare const SummaryHeader: FunctionComponent;
declare const Summary: FunctionComponent;
export { SummaryFlowStep, SummaryHeader, Summary, };
//# sourceMappingURL=summary.d.ts.map

View File

@@ -0,0 +1,171 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {useMemo} from 'preact/hooks';
import {FlowSegment, FlowStepThumbnail, Separator} from '../common';
import {getModeDescription, useFlowResult} from '../util';
import {ReportUtils} from '../../../report/renderer/report-utils.js';
import {SummaryCategory} from './category';
import {useStringFormatter, useLocalizedStrings} from '../i18n/i18n';
const DISPLAYED_CATEGORIES = ['performance', 'accessibility', 'best-practices', 'seo'];
const THUMBNAIL_WIDTH = 40;
const SummaryNavigationHeader: FunctionComponent<{lhr: LH.Result}> = ({lhr}) => {
const strings = useLocalizedStrings();
return (
<div className="SummaryNavigationHeader" data-testid="SummaryNavigationHeader">
<FlowSegment/>
<div className="SummaryNavigationHeader__url">
<a rel="noopener" target="_blank" href={lhr.finalDisplayedUrl}>{lhr.finalDisplayedUrl}</a>
</div>
<div className="SummaryNavigationHeader__category">
{strings.categoryPerformance}
</div>
<div className="SummaryNavigationHeader__category">
{strings.categoryAccessibility}
</div>
<div className="SummaryNavigationHeader__category">
{strings.categoryBestPractices}
</div>
<div className="SummaryNavigationHeader__category">
{strings.categorySeo}
</div>
</div>
);
};
/**
* The div should behave like a JSX <>...</>. This still allows us to identify "rows" with CSS selectors.
*/
const SummaryFlowStep: FunctionComponent<{
lhr: LH.Result,
label: string,
hashIndex: number,
}> = ({lhr, label, hashIndex}) => {
const reportResult = useMemo(() => ReportUtils.prepareReportResult(lhr), [lhr]);
const strings = useLocalizedStrings();
const modeDescription = getModeDescription(lhr.gatherMode, strings);
return (
<div className="SummaryFlowStep">
{
lhr.gatherMode === 'navigation' || hashIndex === 0 ?
<SummaryNavigationHeader lhr={lhr}/> :
<div className="SummaryFlowStep__separator">
<FlowSegment/>
<Separator/>
</div>
}
<FlowStepThumbnail lhr={lhr} width={THUMBNAIL_WIDTH}/>
<FlowSegment mode={lhr.gatherMode}/>
<div className="SummaryFlowStep__label">
<div className="SummaryFlowStep__mode">{modeDescription}</div>
<a className="SummaryFlowStep__link" href={`#index=${hashIndex}`}>{label}</a>
</div>
{
DISPLAYED_CATEGORIES.map(c => (
<SummaryCategory
key={c}
category={reportResult.categories[c]}
href={`#index=${hashIndex}&anchor=${c}`}
gatherMode={lhr.gatherMode}
finalDisplayedUrl={lhr.finalDisplayedUrl}
/>
))
}
</div>
);
};
/**
* For the summary flow, there are many different cells with different contents and different display properties.
* CSS grid makes it easier to enforce things like content alignment and column width (e.g. all category columns have the same width).
*/
const SummaryFlow: FunctionComponent = () => {
const flowResult = useFlowResult();
return (
<div className="SummaryFlow">
{
flowResult.steps.map((step, index) =>
<SummaryFlowStep
key={step.lhr.fetchTime}
lhr={step.lhr}
label={step.name}
hashIndex={index}
/>
)
}
</div>
);
};
const SummaryHeader: FunctionComponent = () => {
const flowResult = useFlowResult();
const strings = useLocalizedStrings();
const str_ = useStringFormatter();
let numNavigation = 0;
let numTimespan = 0;
let numSnapshot = 0;
for (const step of flowResult.steps) {
switch (step.lhr.gatherMode) {
case 'navigation':
numNavigation++;
break;
case 'timespan':
numTimespan++;
break;
case 'snapshot':
numSnapshot++;
break;
}
}
const subtitleCounts = [];
if (numNavigation) subtitleCounts.push(str_(strings.navigationReportCount, {numNavigation}));
if (numTimespan) subtitleCounts.push(str_(strings.timespanReportCount, {numTimespan}));
if (numSnapshot) subtitleCounts.push(str_(strings.snapshotReportCount, {numSnapshot}));
const subtitle = subtitleCounts.join(' · ');
return (
<div className="SummaryHeader">
<div className="SummaryHeader__title">{strings.summary}</div>
<div className="SummaryHeader__subtitle">{subtitle}</div>
</div>
);
};
const SummarySectionHeader: FunctionComponent = ({children}) => {
return (
<div className="SummarySectionHeader">
<div className="SummarySectionHeader__content">{children}</div>
<Separator/>
</div>
);
};
const Summary: FunctionComponent = () => {
const strings = useLocalizedStrings();
return (
<div className="Summary" data-testid="Summary">
<SummaryHeader/>
<Separator/>
<SummarySectionHeader>{strings.allReports}</SummarySectionHeader>
<SummaryFlow/>
</div>
);
};
export {
SummaryFlowStep,
SummaryHeader,
Summary,
};

15
node_modules/lighthouse/flow-report/src/topbar.d.ts generated vendored Normal file
View File

@@ -0,0 +1,15 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent, JSX } from 'preact';
declare function saveHtml(flowResult: LH.FlowResult, htmlStr: string): void;
declare namespace saveHtml {
var saveFile: typeof import("../../report/renderer/api").saveFile;
}
declare const Topbar: FunctionComponent<{
onMenuClick: JSX.MouseEventHandler<HTMLButtonElement>;
}>;
export { Topbar, saveHtml, };
//# sourceMappingURL=topbar.d.ts.map

104
node_modules/lighthouse/flow-report/src/topbar.tsx generated vendored Normal file
View File

@@ -0,0 +1,104 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent, JSX} from 'preact';
import {useState} from 'preact/hooks';
import {HelpDialog} from './help-dialog';
import {getFlowResultFilenamePrefix} from '../../report/generator/file-namer';
import {useLocalizedStrings} from './i18n/i18n';
import {HamburgerIcon, InfoIcon} from './icons';
import {useFlowResult, useOptions} from './util';
import {saveFile} from '../../report/renderer/api';
function saveHtml(flowResult: LH.FlowResult, htmlStr: string) {
const blob = new Blob([htmlStr], {type: 'text/html'});
const filename = getFlowResultFilenamePrefix(flowResult) + '.html';
saveHtml.saveFile(blob, filename);
}
// Store `saveFile` here so we can do dependency injection.
saveHtml.saveFile = saveFile;
/* eslint-disable max-len */
const Logo: FunctionComponent = () => {
return (
<svg role="img" class="lh-topbar__logo" title="Lighthouse logo" width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path d="m14 7 10-7 10 7v10h5v7h-5l5 24H9l5-24H9v-7h5V7Z" fill="#F63"/>
<path d="M31.561 24H14l-1.689 8.105L31.561 24ZM18.983 48H9l1.022-4.907L35.723 32.27l1.663 7.98L18.983 48Z" fill="#FFA385"/>
<path fill="#FF3" d="M20.5 10h7v7h-7z"/>
</svg>
);
};
/* eslint-enable max-len */
const TopbarButton: FunctionComponent<{
onClick: JSX.MouseEventHandler<HTMLButtonElement>,
label: string,
}> =
({onClick, label, children}) => {
return (
<button className="TopbarButton" onClick={onClick} aria-label={label}>
{children}
</button>
);
};
const Topbar: FunctionComponent<{onMenuClick: JSX.MouseEventHandler<HTMLButtonElement>}> =
({onMenuClick}) => {
const flowResult = useFlowResult();
const strings = useLocalizedStrings();
const [showHelpDialog, setShowHelpDialog] = useState(false);
const {getReportHtml, saveAsGist} = useOptions();
return (
<div className="Topbar">
<TopbarButton onClick={onMenuClick} label="Button that opens and closes the sidebar">
<HamburgerIcon/>
</TopbarButton>
<div className="Topbar__logo">
<Logo/>
</div>
<div className="Topbar__title">{strings.title}</div>
{
getReportHtml &&
<TopbarButton
onClick={() => {
const htmlStr = getReportHtml(flowResult);
saveHtml(flowResult, htmlStr);
}}
label="Button that saves the report as HTML"
>{strings.save}</TopbarButton>
}
{
saveAsGist &&
<TopbarButton
onClick={() => saveAsGist(flowResult)}
label="Button that saves the report to a gist"
>{strings.dropdownSaveGist}</TopbarButton>
}
<div style={{flexGrow: 1}} />
<TopbarButton
onClick={() => setShowHelpDialog(previous => !previous)}
label="Button that toggles the help dialog"
>
<div className="Topbar__help-label">
<InfoIcon/>
{strings.helpLabel}
</div>
</TopbarButton>
{showHelpDialog ?
<HelpDialog onClose={() => setShowHelpDialog(false)} /> :
null
}
</div>
);
};
export {
Topbar,
saveHtml,
};

30
node_modules/lighthouse/flow-report/src/util.d.ts generated vendored Normal file
View File

@@ -0,0 +1,30 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import type { UIStringsType } from './i18n/ui-strings';
declare const FlowResultContext: import("preact").Context<import("../../types/lhr/flow-result").default | undefined>;
declare const OptionsContext: import("preact").Context<LH.FlowReportOptions>;
declare function classNames(...args: Array<string | undefined | Record<string, boolean>>): string;
declare function getScreenDimensions(reportResult: LH.Result): {
width: number;
height: number;
};
declare function getFilmstripFrames(reportResult: LH.Result): Array<{
data: string;
}> | undefined;
declare function getModeDescription(mode: LH.Result.GatherMode, strings: UIStringsType): string;
declare function useFlowResult(): LH.FlowResult;
declare function useHashParams(...params: string[]): (string | null)[];
declare function useHashState(): LH.HashState | null;
/**
* Creates a DOM subtree from non-preact code (e.g. LH report renderer).
* @param renderCallback Callback that renders a DOM subtree.
* @param inputs Changes to these values will trigger a re-render of the DOM subtree.
* @return Reference to the element that will contain the DOM subtree.
*/
declare function useExternalRenderer<T extends Element>(renderCallback: () => Node, inputs?: ReadonlyArray<unknown>): import("preact/hooks").Ref<T>;
declare function useOptions(): LH.FlowReportOptions;
export { FlowResultContext, OptionsContext, classNames, getScreenDimensions, getFilmstripFrames, getModeDescription, useFlowResult, useHashParams, useHashState, useExternalRenderer, useOptions, };
//# sourceMappingURL=util.d.ts.map

156
node_modules/lighthouse/flow-report/src/util.ts generated vendored Normal file
View File

@@ -0,0 +1,156 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {createContext} from 'preact';
import {useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'preact/hooks';
import type {UIStringsType} from './i18n/ui-strings';
const FlowResultContext = createContext<LH.FlowResult|undefined>(undefined);
const OptionsContext = createContext<LH.FlowReportOptions>({});
function getHashParam(param: string): string|null {
const params = new URLSearchParams(location.hash.replace('#', '?'));
return params.get(param);
}
function classNames(...args: Array<string|undefined|Record<string, boolean>>): string {
const classes = [];
for (const arg of args) {
if (!arg) continue;
if (typeof arg === 'string') {
classes.push(arg);
continue;
}
const applicableClasses = Object.entries(arg)
.filter(([_, shouldApply]) => shouldApply)
.map(([className]) => className);
classes.push(...applicableClasses);
}
return classes.join(' ');
}
function getScreenDimensions(reportResult: LH.Result) {
const {width, height} = reportResult.configSettings.screenEmulation;
return {width, height};
}
function getFilmstripFrames(
reportResult: LH.Result
): Array<{data: string}> | undefined {
const filmstripAudit = reportResult.audits['screenshot-thumbnails'];
if (!filmstripAudit) return undefined;
const frameItems =
filmstripAudit.details &&
filmstripAudit.details.type === 'filmstrip' &&
filmstripAudit.details.items;
return frameItems || undefined;
}
function getModeDescription(mode: LH.Result.GatherMode, strings: UIStringsType) {
switch (mode) {
case 'navigation': return strings.navigationDescription;
case 'timespan': return strings.timespanDescription;
case 'snapshot': return strings.snapshotDescription;
}
}
function useFlowResult(): LH.FlowResult {
const flowResult = useContext(FlowResultContext);
if (!flowResult) throw Error('useFlowResult must be called in the FlowResultContext');
return flowResult;
}
function useHashParams(...params: string[]) {
const [paramValues, setParamValues] = useState(params.map(getHashParam));
// Use two-way-binding on the URL hash.
// Triggers a re-render if any param changes.
useEffect(() => {
function hashListener() {
const newParamValues = params.map(getHashParam);
if (newParamValues.every((value, i) => value === paramValues[i])) return;
setParamValues(newParamValues);
}
window.addEventListener('hashchange', hashListener);
return () => window.removeEventListener('hashchange', hashListener);
}, [paramValues]);
return paramValues;
}
function useHashState(): LH.HashState|null {
const flowResult = useFlowResult();
const [indexString, anchor] = useHashParams('index', 'anchor');
// Memoize result so a new object is not created on every call.
return useMemo(() => {
if (!indexString) return null;
const index = Number(indexString);
if (!Number.isFinite(index)) {
console.warn(`Invalid hash index: ${indexString}`);
return null;
}
const step = flowResult.steps[index];
if (!step) {
console.warn(`No flow step at index ${index}`);
return null;
}
return {currentLhr: step.lhr, index, anchor};
}, [indexString, flowResult, anchor]);
}
/**
* Creates a DOM subtree from non-preact code (e.g. LH report renderer).
* @param renderCallback Callback that renders a DOM subtree.
* @param inputs Changes to these values will trigger a re-render of the DOM subtree.
* @return Reference to the element that will contain the DOM subtree.
*/
function useExternalRenderer<T extends Element>(
renderCallback: () => Node,
inputs?: ReadonlyArray<unknown>
) {
const ref = useRef<T>(null);
useLayoutEffect(() => {
if (!ref.current) return;
const root = renderCallback();
ref.current.append(root);
return () => {
if (ref.current?.contains(root)) ref.current.removeChild(root);
};
}, inputs);
return ref;
}
function useOptions() {
return useContext(OptionsContext);
}
export {
FlowResultContext,
OptionsContext,
classNames,
getScreenDimensions,
getFilmstripFrames,
getModeDescription,
useFlowResult,
useHashParams,
useHashState,
useExternalRenderer,
useOptions,
};

View File

@@ -0,0 +1,12 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
export declare const CategoryScore: FunctionComponent<{
category: LH.ReportResult.Category;
href: string;
gatherMode: LH.Result.GatherMode;
}>;
//# sourceMappingURL=category-score.d.ts.map

View File

@@ -0,0 +1,28 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {renderCategoryScore} from '../../../report/renderer/api';
import {useExternalRenderer} from '../util';
export const CategoryScore: FunctionComponent<{
category: LH.ReportResult.Category,
href: string,
gatherMode: LH.Result.GatherMode,
}> = ({category, href, gatherMode}) => {
const ref = useExternalRenderer<HTMLDivElement>(() => {
return renderCategoryScore(category, {
gatherMode,
omitLabel: true,
onPageAnchorRendered: link => link.href = href,
});
}, [category, href]);
return (
<div ref={ref} data-testid="CategoryScore"/>
);
};

View File

@@ -0,0 +1,10 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
export declare const Markdown: FunctionComponent<{
text: string;
}>;
//# sourceMappingURL=markdown.d.ts.map

View File

@@ -0,0 +1,18 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {convertMarkdownCodeSnippets} from '../../../report/renderer/api';
import {useExternalRenderer} from '../util';
export const Markdown: FunctionComponent<{text: string}> = ({text}) => {
const ref = useExternalRenderer<HTMLSpanElement>(() => {
return convertMarkdownCodeSnippets(text);
}, [text]);
return <span ref={ref}/>;
};

View File

@@ -0,0 +1,10 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
export declare const Report: FunctionComponent<{
hashState: LH.HashState;
}>;
//# sourceMappingURL=report.d.ts.map

View File

@@ -0,0 +1,47 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {renderReport} from '../../../report/renderer/api.js';
import {useExternalRenderer} from '../util';
/**
* The default behavior of anchor links is not compatible with the flow report's hash navigation.
* This function converts a category score anchor link to a flow report link.
* e.g. <a href="#link"> -> <a href="#index=0&anchor=link">
*/
function convertAnchor(link: HTMLAnchorElement, index: number) {
// Clear existing event listeners by cloning node.
const newLink = link.cloneNode(true) as HTMLAnchorElement;
if (!newLink.hash) return newLink;
const nodeId = link.hash.substr(1);
newLink.hash = `#index=${index}&anchor=${nodeId}`;
newLink.onclick = e => {
e.preventDefault();
const el = document.getElementById(nodeId);
if (el) el.scrollIntoView();
};
link.replaceWith(newLink);
}
export const Report: FunctionComponent<{hashState: LH.HashState}> =
({hashState}) => {
const ref = useExternalRenderer<HTMLDivElement>(() => {
return renderReport(hashState.currentLhr, {
disableFireworks: true,
disableDarkMode: true,
omitTopbar: true,
omitGlobalStyles: true,
onPageAnchorRendered: link => convertAnchor(link, hashState.index),
});
}, [hashState]);
return (
<div ref={ref} data-testid="Report"/>
);
};

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { FunctionComponent } from 'preact';
export declare const Styles: FunctionComponent;
//# sourceMappingURL=styles.d.ts.map

View File

@@ -0,0 +1,16 @@
/**
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import {FunctionComponent} from 'preact';
import {createStylesElement} from '../../../report/renderer/api';
import {useExternalRenderer} from '../util';
export const Styles: FunctionComponent = () => {
const ref = useExternalRenderer<HTMLDivElement>(createStylesElement);
return <div ref={ref}/>;
};