Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
"use strict";
const {
createElement,
createFactory,
} = require("resource://devtools/client/shared/vendor/react.mjs");
const {
PropTypes,
} = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
const {
button,
const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js");
const Localized = createFactory(FluentReact.Localized);
const spacerCell = { "aria-hidden": true };
function EntryInfo({ fields, id }) {
return createElement(
"div",
{ id, popover: "auto" },
createElement(
"dl",
{},
...Object.entries(fields).flatMap(([key, value]) => [
createElement("dt", {}, key),
createElement(
"dd",
{},
value instanceof Array
? createElement("ul", {
children: value.map(child =>
createElement("li", {}, `${child}`)
),
})
: `${value}`
),
])
)
);
}
EntryInfo.propTypes = {
fields: PropTypes.object.isRequired,
id: PropTypes.string.isRequired,
};
function SessionHistoryDiagram({ current, diagrams, entriesByKey }) {
const cols = [];
// We're going to use <col> and <colgroup> to have a flat table. We do this
// instead of nested tables to make sure that this integrates with a11y.
for (let i = 0; i < diagrams.length; i++) {
const { start, end } = diagrams[i];
cols.push(createElement("col", { span: end - start }));
if (i < diagrams.length - 1) {
cols.push(createElement("col", { className: "diagram-spacer" }));
}
}
const headerCells = [];
for (let i = 0; i < diagrams.length; i++) {
const { start, end } = diagrams[i];
for (let index = start; index < end; index++) {
const props = index == current ? { id: "current" } : {};
headerCells.push(createElement("th", props, index));
}
if (i < diagrams.length - 1) {
headerCells.push(createElement("th", spacerCell));
}
}
const maxRows = Math.max(...diagrams.map(({ rows }) => rows.length));
let ctr = 0;
const bodyRows = [];
const diagramDone = new Array(diagrams.length).fill(false);
for (let rowIndex = 0; rowIndex < maxRows; rowIndex++) {
const cells = [];
for (let diagramIndex = 0; diagramIndex < diagrams.length; diagramIndex++) {
if (diagramIndex > 0 && rowIndex === 0) {
cells.push(createElement("td", { ...spacerCell, rowSpan: maxRows }));
}
if (diagramDone[diagramIndex]) {
continue;
}
const { rows, start, end } = diagrams[diagramIndex];
if (rowIndex < rows.length) {
for (const { age, key, sameDocNav } of rows[rowIndex]) {
const id = `entry-info-container-${start}-${ctr++}`;
const className = sameDocNav ? "same-document-nav" : "";
cells.push(
key
? createElement(
"td",
{ colSpan: age, className },
Localized(
{
id: "session-history-entry-info-button-title",
attrs: { title: true },
},
button(
{ popovertarget: id },
`${entriesByKey[key].url.pathname}${entriesByKey[key].url.search}`
)
),
createElement(EntryInfo, { fields: entriesByKey[key], id })
)
: createElement("td", { colSpan: age })
);
}
} else {
// In the case where we have column (or columns) that lack entries we
// want the column to not have borders between rows, but instead have
// the entire column be empty. This might e.g. happen when we add an
// iframe dynamically, creating a diagram that'd look like:
//
// +-----+-----+-----+-----+
// | top | top | top | top |
// +-----+-----+-----+-----+
// | | | a | a#1 |
// + + +-----+-----+
// | | | b |
// +-----+-----+-----+-----+
cells.push(
createElement("td", {
colSpan: end - start,
rowSpan: maxRows - rowIndex,
className: "diagram-padding",
})
);
diagramDone[diagramIndex] = true;
}
}
bodyRows.push(createElement("tr", {}, ...cells));
}
return createElement(
"table",
{ id: "diagram-container-table" },
createElement("colgroup", {}, ...cols),
createElement("thead", {}, createElement("tr", {}, ...headerCells)),
createElement("tbody", {}, ...bodyRows)
);
}
SessionHistoryDiagram.propTypes = {
current: PropTypes.number.isRequired,
diagrams: PropTypes.arrayOf(PropTypes.object).isRequired,
entriesByKey: PropTypes.object.isRequired,
};
// Exports
module.exports = SessionHistoryDiagram;