Skip to content

Commit

Permalink
Merge pull request #104 from ymrl/add_table_tips
Browse files Browse the repository at this point in the history
✨ add table Tips
  • Loading branch information
ymrl authored Jun 17, 2024
2 parents 49d9716 + ba8c559 commit ddd0c2a
Show file tree
Hide file tree
Showing 14 changed files with 1,593 additions and 53 deletions.
9 changes: 9 additions & 0 deletions src/components/SettingsEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ export const SettingsEditor = ({
>
<span className="text-sm">{t("settings.lang")}</span>
</Checkbox>
<Checkbox
onChange={(e) => {
handleChangeCheckbox("table", e);
}}
checked={settings.table}
disabled={disabled || !settings.accessibilityInfo}
>
<span className="text-sm">{t("settings.tables")}</span>
</Checkbox>
</div>
</fieldset>
</div>
Expand Down
6 changes: 6 additions & 0 deletions src/content/components/ElementInfo.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@
.ElementInfo__border--section {
border-color: var(--accent-skyblue);
}

.ElementInfo__border--fieldset {
border-color: var(--accent-brown);
border-style: dotted;
}
.ElementInfo__border--table,
.ElementInfo__border--tableCell {
border-color: var(--accent-skyblue);
}

.ElementInfo__tips {
position: absolute;
Expand All @@ -49,6 +54,7 @@
flex-direction: row;
width: max-content;
flex-wrap: wrap;
gap: 0.2em;
}

.ElementInfo__tips--left {
Expand Down
6 changes: 4 additions & 2 deletions src/content/components/ElementInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ export const ElementInfo = ({
const verticalPosition: VerticalPosition =
category === "page"
? "inner-top"
: category === "section" || category === "heading"
: category === "section" || category === "heading" || category === "table"
? y < tipFontSize * 2.4
? "inner-top"
: "outer-top"
: category === "image" || category === "fieldset"
: category === "image" ||
category === "fieldset" ||
category === "tableCell"
? y > tipFontSize * 2.4 && height < tipFontSize * 3.2
? "outer-top"
: "inner-top"
Expand Down
10 changes: 10 additions & 0 deletions src/content/components/Tip.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
gap: 0.4em;
color: #000;
background-color: #fff;
border-radius: 0.2em;
}
.Tip--name {
background-color: var(--base-green);
Expand All @@ -26,6 +27,15 @@
.Tip--landmark {
background-color: var(--base-yellowgreen);
}
.Tip--tableHeader {
background-color: var(--base-skyblue);
}
.Tip--tableSize {
background-color: var(--base-beige);
}
.Tip--tablePosition {
background-color: var(--base-beige);
}

.Tip--description {
background-color: var(--light-gray);
Expand Down
27 changes: 27 additions & 0 deletions src/content/components/Tip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import {
IoCodeSlash,
IoDocument,
IoFlag,
IoGrid,
IoGridOutline,
IoInformationCircle,
IoLanguage,
IoPin,
IoPricetag,
IoWarning,
} from "react-icons/io5";
Expand Down Expand Up @@ -89,6 +92,30 @@ const Icon = ({ type }: { type: TipType }) => {
aria-label={t("tip.ariaStatus")}
/>
);
case "tableHeader":
return (
<IoGrid
className="Tip__icon"
role="img"
aria-label={t("tip.tableHeader")}
/>
);
case "tableSize":
return (
<IoGridOutline
className="Tip__icon"
role="img"
aria-label={t("tip.tableSize")}
/>
);
case "tablePosition":
return (
<IoPin
className="Tip__icon"
role="img"
aria-label={t("tip.tablePosition")}
/>
);

case "pageTitle":
return (
Expand Down
139 changes: 88 additions & 51 deletions src/content/dom/collectElements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import { CategorySettings } from "../../settings";
import { SectionSelectors, isSection, sectionTips } from "./tips/sectionTips";
import { isPage, pageTips } from "./tips/pageTips";
import { LangSelectors, langTips, isLang } from "./tips/langTips";
import {
TableSelectors,
isTable,
isTableCell,
tableTips,
} from "./tips/tableTips";
import { InternalTable } from "./tips/internalTable";
import { getClosestByRoles } from "./getClosestByRoles";

const getSelector = (settings: Partial<CategorySettings>) => {
return [
Expand All @@ -30,20 +38,14 @@ const getSelector = (settings: Partial<CategorySettings>) => {
...(settings.ariaHidden ? AriraHiddenSelectors : []),
...(settings.section ? SectionSelectors : []),
...(settings.lang ? LangSelectors : []),
...(settings.table ? TableSelectors : []),
].join(",");
};

export const collectElements = (
root: Element,
excludes: Element[],
settings: Partial<CategorySettings> = {
image: true,
formControl: true,
link: true,
button: true,
heading: true,
ariaHidden: true,
},
settings: Partial<CategorySettings>,
options: {
srcdoc?: boolean;
} = {},
Expand Down Expand Up @@ -84,61 +86,96 @@ export const collectElements = (
const visibleHeight = w.innerHeight;

const selector = getSelector(settings);
const internalTables: InternalTable[] = [];

return {
rootHeight,
rootWidth,
elements: selector
? [
...(settings.page && rootTagName === "body" ? [root] : []),
...root.querySelectorAll(getSelector(settings)),
]
.filter((el) => !isHidden(el))
.map((el: Element) => {
if (excludes.some((exclude: Element) => exclude.contains(el)))
return null;
const elementPosition = getElementPosition(el, w, offsetX, offsetY);
if (
elementPosition.absoluteX + elementPosition.width < visibleX ||
elementPosition.absoluteY + elementPosition.height < visibleY ||
elementPosition.absoluteX > visibleX + visibleWidth ||
elementPosition.absoluteY > visibleY + visibleHeight
) {
return null;
}

const name = computeAccessibleName(el);
const nameTips: ElementTip[] = name
? [{ type: "name", content: name }]
: [];
return {
...elementPosition,
category: getElementCategory(el),
tips: [
...headingTips(el, name),
...nameTips,
...imageTips(el, name),
...formTips(el, name),
...buttonTips(el, name),
...linkTips(el, name),
...ariaHiddenTips(el),
...sectionTips(el, name),
...langTips(el),
...pageTips(el, !!options.srcdoc),
...globalTips(el),
],
};
})
.filter((el): el is ElementMeta => el !== null)
: [],
elements: [
...(settings.page && rootTagName === "body" ? [root] : []),
...(selector ? [...root.querySelectorAll(getSelector(settings))] : []),
]
.filter((el) => !isHidden(el))
.map((el: Element) => {
if (excludes.some((exclude: Element) => exclude.contains(el)))
return null;
const elementPosition = getElementPosition(el, w, offsetX, offsetY);
if (
elementPosition.absoluteX + elementPosition.width < visibleX ||
elementPosition.absoluteY + elementPosition.height < visibleY ||
elementPosition.absoluteX > visibleX + visibleWidth ||
elementPosition.absoluteY > visibleY + visibleHeight
) {
return null;
}
return {
...elementPosition,
category: getElementCategory(el),
tips: tipsForElement(el, { ...options, internalTables }),
};
})
.filter((el): el is ElementMeta => el !== null),
};
};

const getTableTips = (
el: Element,
internalTables: InternalTable[],
): ElementTip[] => {
if (isTable(el) || isTableCell(el)) {
const tagName = el.tagName.toLowerCase();
const tableEl =
tagName === "table"
? el
: ["th", "td"].includes(tagName)
? el.closest("table")
: getClosestByRoles(el, ["table", "grid", "treegrid"]);
if (tableEl) {
const internalTable = internalTables.find((t) => t.element === tableEl);
if (!internalTable) {
const newTable = new InternalTable(tableEl);
internalTables.push(newTable);
return tableTips(el, newTable);
}
return tableTips(el, internalTable);
}
}
return [];
};

const tipsForElement = (
el: Element,
options: {
srcdoc?: boolean;
internalTables: InternalTable[];
},
): ElementTip[] => {
const name = computeAccessibleName(el);
const nameTips: ElementTip[] = name ? [{ type: "name", content: name }] : [];
return [
...headingTips(el, name),
...nameTips,
...imageTips(el, name),
...formTips(el, name),
...buttonTips(el, name),
...linkTips(el, name),
...ariaHiddenTips(el),
...sectionTips(el, name),
...getTableTips(el, options.internalTables),
...langTips(el),
...pageTips(el, !!options.srcdoc),
...globalTips(el),
];
};

const getElementCategory = (el: Element): Category => {
if (isPage(el)) return "page";
if (isImage(el)) return "image";
if (isHeading(el)) return "heading";
if (isFormControl(el) || isLink(el) || isButton(el)) return "control";
if (isSection(el)) return "section";
if (isTable(el)) return "table";
if (isTableCell(el)) return "tableCell";
if (isFieldset(el)) return "fieldset";
if (isSection(el) || isLang(el)) return "section";
return "general";
Expand Down
Loading

0 comments on commit ddd0c2a

Please sign in to comment.