Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/components/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ const Chart = ({
if (printing) return createPortal(chart, document.body);

return (
<Flex direction="column" gap="lg" full>
<Flex column gap="lg" full>
{chart}

{/* controls */}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ const Dialog = ({ title, content, bottomContent, children }: Props) => {
<Root open={open} onClose={close}>
<div className={classes.fullscreen}>
<Content as={Fragment}>
<Flex direction="column" className={classes.content}>
<Flex column className={classes.content}>
<Title>{title}</Title>
<Description className="sr-only">{title}</Description>
<button className={classes.close} onClick={close}>
<FaCircleXmark />
</button>
<Flex className={classes.scroll} direction="column">
<Flex className={classes.scroll} column>
{content}
</Flex>
{bottomContent && (
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Download.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const Download = ({
return (
<Popover
content={
<Flex direction="column" hAlign="stretch" gap="xs">
<Flex column hAlign="stretch" gap="xs">
{raster && (
<>
<Button
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/FeatureCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Props = {
/** card with title, badge, and text/image */
const FeatureCard = ({ title, badge, content }: Props) => {
return (
<Flex direction="column" className={clsx("card", classes.card)}>
<Flex column className={clsx("card", classes.card)}>
<Flex wrap={false} gap="sm" className={clsx("full", classes.title)}>
<span className="primary">{title}</span>
{badge && <Badge className={classes.badge}>{badge}</Badge>}
Expand Down
25 changes: 12 additions & 13 deletions frontend/src/components/Flex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ type Props<TagName extends TagNames = "div"> = {
/** tag name */
tag?: TagNames;
/** flex display (whether container takes up full width) */
display?: "block" | "inline";
/** horizontal or vertical */
direction?: "row" | "column";
inline?: boolean;
/** vertical layout instead of horizontal */
column?: boolean;
/** amount of space between items */
gap?: "md" | "none" | "xs" | "sm" | "lg" | "xl";
/** vertical gap fraction of horizontal gap */
gapRatio?: 1 | 0.5 | 0.25 | 0;
/** whether to wrap items */
wrap?: true | false;
wrap?: boolean;
/** whether to make full width */
full?: true | false;
full?: boolean;
/** horizontal alignment */
hAlign?: "center" | "left" | "right" | "stretch" | "space";
/** vertical alignment */
Expand Down Expand Up @@ -51,8 +51,8 @@ const gapMap: Record<NonNullable<Props["gap"]>, number> = {
const Flex = <TagName extends TagNames>({
ref,
tag: Tag = "div",
display = "block",
direction = "row",
inline = false,
column = false,
gap = "md",
gapRatio = 1,
wrap = true,
Expand All @@ -66,12 +66,11 @@ const Flex = <TagName extends TagNames>({
const belowBreakpoint = useMediaQuery(`(max-width: ${breakpoint}px)`);

const flexStyles: CSSProperties = {
display: display === "block" ? "flex" : "inline-flex",
flexDirection: direction === "column" || belowBreakpoint ? "column" : "row",
justifyContent:
direction === "column" ? alignMap[vAlign] : alignMap[hAlign],
alignItems: direction === "column" ? alignMap[hAlign] : alignMap[vAlign],
flexWrap: wrap && direction === "row" ? "wrap" : "nowrap",
display: inline ? "inline-flex" : "flex",
flexDirection: column || belowBreakpoint ? "column" : "row",
justifyContent: column ? alignMap[vAlign] : alignMap[hAlign],
alignItems: column ? alignMap[hAlign] : alignMap[vAlign],
flexWrap: wrap && !column ? "wrap" : "nowrap",
gap: `${gapMap[gap] * gapRatio}px ${gapMap[gap]}px`,
width: full ? "100%" : undefined,
...style,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import classes from "./Footer.module.css";

/** at bottom of every page. singleton. */
const Footer = () => (
<Flex tag="footer" direction="column" gap="sm" className={classes.footer}>
<Flex tag="footer" column gap="sm" className={classes.footer}>
<Flex gap="sm" className={classes.icons}>
<Link
to="mailto:[email protected]"
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/components/Heading.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ h4.heading {
text-align: left;
}

:is(h3.heading, h4.heading) + :not(h3.heading, h4.heading) {
margin-top: -1em;
}

.icon {
color: var(--deep);
}
Expand Down
51 changes: 49 additions & 2 deletions frontend/src/components/Heading.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { cloneElement, useRef } from "react";
import { cloneElement, useEffect, useRef } from "react";
import type { JSX, ReactElement, ReactNode } from "react";
import { FaLink } from "react-icons/fa6";
import clsx from "clsx";
import { atom, useSetAtom } from "jotai";
import { kebabCase } from "lodash";
import Badge from "@/components/Badge";
import Link from "@/components/Link";
Expand All @@ -21,6 +22,17 @@ type Props = {
children: ReactNode;
};

type Heading = {
ref: HTMLHeadingElement;
id: string;
level: number;
icon?: ReactNode;
text: ReactNode;
};

/** global list of headings */
export const headingsAtom = atom<Heading[]>([]);

/**
* demarcates a new section/level of content. only use one level 1 per page.
* don't use levels below 4.
Expand All @@ -41,12 +53,47 @@ const Heading = ({
const id = kebabCase(anchor ?? renderText(children));

/** icon or badge */
let iconElement = <></>;
let iconElement: ReactNode = null;
if (typeof icon === "string")
iconElement = <Badge className={classes.badge}>{icon}</Badge>;
if (typeof icon === "object" && typeof icon.type === "function")
iconElement = cloneElement(icon, { className: classes.icon });

const setHeadings = useSetAtom(headingsAtom);

/** on every render */
useEffect(() => {
const element = ref.current;

if (element) {
setHeadings((headings) =>
headings
/** remove heading from list */
.filter((heading) => heading.ref !== element)
/** add heading to list */
.concat([
{ ref: element, id, level, icon: iconElement, text: children },
])
/**
* make sure list is in order of document appearance
* https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
*/
.sort((a, b) =>
a.ref.compareDocumentPosition(b.ref) &
Node.DOCUMENT_POSITION_FOLLOWING
? -1
: 1,
),
);
}

return () =>
/** remove heading from list */
setHeadings((headings) =>
headings.filter((heading) => heading.ref !== element),
);
});

return (
<Tag id={id} ref={ref} className={clsx(className, classes.heading)}>
{iconElement}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Mark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export type Type = keyof typeof types;
/** icon and text with color */
const Mark = ({ type = "info", icon, className, children }: Props) => (
<Flex
display="inline"
inline
gap="sm"
wrap={false}
className={clsx(className, classes.mark)}
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/components/Network.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -605,15 +605,15 @@ const Network = ({ filename = [], nodes: _nodes, edges: _edges }: Props) => {
const [, { toggleFullscreen }] = useFullscreen(containerRef);

return (
<Flex direction="column" full>
<Flex column full>
<div
ref={ref}
className={clsx("card", classes.network, expanded && classes.expanded)}
style={{ aspectRatio }}
>
{/* panel */}
<Flex
direction="column"
column
hAlign="left"
vAlign="top"
className={classes.panel}
Expand All @@ -622,7 +622,7 @@ const Network = ({ filename = [], nodes: _nodes, edges: _edges }: Props) => {
{selectedItems.length ? (
/** show info about selected nodes/edges */
<>
<Flex direction="column" hAlign="left" gap="sm">
<Flex column hAlign="left" gap="sm">
<strong>Selected items</strong>
{selectedItems.map((node, index) => (
<Fragment key={index}>
Expand Down Expand Up @@ -656,7 +656,7 @@ const Network = ({ filename = [], nodes: _nodes, edges: _edges }: Props) => {
) : (
/** if nothing selected, show color key */
<>
<Flex direction="column" hAlign="left" gap="sm">
<Flex column hAlign="left" gap="sm">
<div>
<strong>Nodes</strong>{" "}
<span className="secondary">
Expand All @@ -672,7 +672,7 @@ const Network = ({ filename = [], nodes: _nodes, edges: _edges }: Props) => {
/>
</Flex>

<Flex direction="column" hAlign="left" gap="sm">
<Flex column hAlign="left" gap="sm">
<div>
<strong>Edges</strong>{" "}
<span className="secondary">
Expand Down
20 changes: 4 additions & 16 deletions frontend/src/components/Radios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,13 @@ const Radios = <O extends Option>({
}, [value]);

return (
<Flex
direction="column"
hAlign="left"
role="group"
className={classes.container}
>
<Flex column hAlign="left" role="group" className={classes.container}>
<legend className={classes.label}>
{label}
{tooltip && <Help tooltip={tooltip} />}
</legend>

<Flex direction="column" gap="xs" hAlign="stretch">
<Flex column gap="xs" hAlign="stretch">
{options.map((option, index) => (
<Flex
tag="label"
Expand Down Expand Up @@ -117,15 +112,8 @@ const Radios = <O extends Option>({
)}

{/* text content */}
<Flex direction="column" hAlign="left" gap="sm">
<span
className={clsx(
"primary",
selectedWFallback === option.id && classes.checked,
)}
>
{option.primary}
</span>
<Flex column hAlign="left" gap="sm">
<span className="primary">{option.primary}</span>
{option.secondary && (
<span className="secondary">{option.secondary}</span>
)}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type Props = {
const Section = ({ fill, full, className, ...props }: Props) => (
<Flex
tag="section"
direction="column"
column
gap="lg"
vAlign="top"
className={clsx(className, classes.section, {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Select.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
transition: background var(--fast);
}

.option-active {
.option[data-active="true"] {
background: var(--off-white);
}

Expand Down
7 changes: 1 addition & 6 deletions frontend/src/components/SelectMulti.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,7 @@ const SelectMulti = <O extends Option>({
{options.map((option) => (
<ListboxOption key={option.id} value={option.id} as={Fragment}>
{({ focus, selected }) => (
<li
className={clsx(
classes.option,
focus && classes["option-active"],
)}
>
<li className={classes.option} data-active={focus}>
<FaCheck
className={classes.check}
style={{ opacity: selected ? 1 : 0 }}
Expand Down
7 changes: 1 addition & 6 deletions frontend/src/components/SelectSingle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,7 @@ const SelectSingle = <O extends Option>({
{options.map((option) => (
<ListboxOption key={option.id} value={option.id} as={Fragment}>
{({ focus, selected }) => (
<li
className={clsx(
classes.option,
focus && classes["option-active"],
)}
>
<li className={classes.option} data-active={focus}>
{/* check mark */}
<VscCircleFilled
className={classes.check}
Expand Down
9 changes: 1 addition & 8 deletions frontend/src/components/Table.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@
width: calc(100vw - 40px);
}

.scroll {
max-width: 100%;
overflow-x: auto;
border-radius: var(--rounded);
box-shadow: var(--box-shadow);
}

@media (max-width: 800px) {
.expanded th,
.expanded td {
Expand All @@ -39,7 +32,7 @@
color: var(--deep);
}

.header-button[data-active] {
.header-button[data-active="true"] {
color: var(--accent);
}

Expand Down
15 changes: 4 additions & 11 deletions frontend/src/components/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,8 @@ const Table = <Datum extends object>({
});

return (
<Flex
direction="column"
className={expanded ? classes.expanded : classes.collapsed}
>
<div className={classes.scroll}>
<Flex column className={expanded ? classes.expanded : classes.collapsed}>
<div className="table-wrapper">
{/* table */}
<table
className={classes.table}
Expand Down Expand Up @@ -320,9 +317,7 @@ const Table = <Datum extends object>({
<button
type="button"
className={classes["header-button"]}
data-active={
header.column.getIsSorted() ? "" : undefined
}
data-active={header.column.getIsSorted()}
onClick={header.column.getToggleSortingHandler()}
>
{header.column.getIsSorted() ? (
Expand Down Expand Up @@ -352,9 +347,7 @@ const Table = <Datum extends object>({
<button
type="button"
className={classes["header-button"]}
data-active={
header.column.getIsFiltered() ? "" : undefined
}
data-active={header.column.getIsFiltered()}
>
<FaFilter />
</button>
Expand Down
Loading