mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-24 23:32:19 +10:00
fix(stylesheet): apply item-header to every header row on every template (#3357)
`SectionItemHeader` only rendered its own box when a template opted into `mainItemHeaderBorder` (only Ditgar did). Everywhere else it walked the header children and attached the resolved `item-header` style to the first descendant that happened to be a literal `View` or `InlineItemHeader`. Sections whose header starts with anything else — certifications, awards, projects, publications, references — matched nothing, so the style was silently dropped; stacked headers such as experience matched only their first row, so a second row went unstyled. The header now always renders its own `Div`, so `item-header` covers the whole header row of every section on every template. `Div` rather than `View` keeps the base row gap the rows used to inherit from the item box, and Ditgar keeps its tight header via `rowGap: 0` on its own `sectionItemHeader` slot, so rendered output is unchanged apart from the newly styled rows. `mainItemHeaderBorder` is now dead and removed. Fixes #3349
This commit is contained in:
@@ -19,11 +19,11 @@ import type {
|
||||
VolunteerItem,
|
||||
} from "@reactive-resume/schema/resume/data";
|
||||
import type { IconName } from "phosphor-icons-react-pdf/dynamic";
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import type { CombinedTextName } from "../../semantic/node-keys";
|
||||
import type { StyleInput, TemplatePlacement } from "./styles";
|
||||
import type { CustomItemSection, ItemSection } from "./types";
|
||||
import { Children, cloneElement, createContext, Fragment, isValidElement, use } from "react";
|
||||
import { Children, createContext, Fragment, isValidElement, use } from "react";
|
||||
import { View } from "#react-pdf-renderer";
|
||||
import { useRender } from "../../context";
|
||||
import { getResumeSectionIcon } from "../../section-icon";
|
||||
@@ -527,21 +527,19 @@ const SectionItem = ({ itemId, children, style }: SectionItemProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const InlineItemHeader = ({
|
||||
leading,
|
||||
middle,
|
||||
trailing,
|
||||
nodeKey,
|
||||
}: InlineItemHeaderProps & { nodeKey?: string | undefined }) => {
|
||||
/**
|
||||
* The single-line variant of an item header. `SectionItemHeader` owns the `item-header` node and
|
||||
* its resolved style, so this only lays the three slots out and addresses their template parts
|
||||
* against the surrounding item-header key.
|
||||
*/
|
||||
const InlineItemHeader = ({ leading, middle, trailing }: InlineItemHeaderProps) => {
|
||||
const inlineItemHeaderStyle = useTemplateStyle("inlineItemHeader");
|
||||
const leadingStyle = useTemplateStyle("inlineItemHeaderLeading");
|
||||
const middleStyle = useTemplateStyle("inlineItemHeaderMiddle");
|
||||
const trailingStyle = useTemplateStyle("inlineItemHeaderTrailing");
|
||||
|
||||
const resolved = useResolvedNode(nodeKey);
|
||||
const nodeKey = useSemanticNodeKey();
|
||||
const renderedChildKeys = useRenderedChildKeys(nodeKey);
|
||||
const visible = useSemanticNodeVisible(nodeKey);
|
||||
if (!visible) return null;
|
||||
const parts = [
|
||||
{
|
||||
nodeKey: semanticTemplatePartNodeKey(nodeKey, "inline-item-header-leading") ?? "inline-item-header-leading",
|
||||
@@ -584,11 +582,7 @@ const InlineItemHeader = ({
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<View {...resolvedPdfFlowProps(resolved)} style={composeStyles(inlineItemHeaderStyle, resolved.style)}>
|
||||
{projectRenderedChildren(renderedChildKeys, parts)}
|
||||
</View>
|
||||
);
|
||||
return <View style={composeStyles(inlineItemHeaderStyle)}>{projectRenderedChildren(renderedChildKeys, parts)}</View>;
|
||||
};
|
||||
|
||||
const stackedSidebarSplitRowStyle = {
|
||||
@@ -653,55 +647,25 @@ const ItemHeaderRow = ({ children, style }: ItemHeaderRowProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* The box around an item's header rows, exposed to Semantic CSS as `item-header`.
|
||||
*
|
||||
* It always renders its own `Div`, whatever shape the section's header markup has, so a resolved
|
||||
* `item-header` style covers every header row of every section on every template. `Div` rather than
|
||||
* `View` because the header rows keep the row gap they used to get from the item box around them.
|
||||
*/
|
||||
const SectionItemHeader = ({ children }: SectionItemHeaderProps) => {
|
||||
const itemNodeKey = useSemanticNodeKey();
|
||||
const itemHeaderNodeKey = itemNodeKey ? semanticNodeKeys.itemHeader(itemNodeKey) : undefined;
|
||||
const resolved = useResolvedNode(itemHeaderNodeKey);
|
||||
const mainItemHeaderBorder = useTemplateFeature("mainItemHeaderBorder");
|
||||
const sectionItemHeaderStyle = useTemplateStyle("sectionItemHeader");
|
||||
const exists = useSemanticNodeExists(itemHeaderNodeKey);
|
||||
const visible = useSemanticNodeVisible(itemHeaderNodeKey);
|
||||
if (!exists) return <>{children}</>;
|
||||
if (!visible) return null;
|
||||
|
||||
if (!mainItemHeaderBorder) {
|
||||
const bindFirstExistingView = (node: ReactNode): [ReactNode, boolean] => {
|
||||
if (!isValidElement(node)) return [node, false];
|
||||
if (node.type === InlineItemHeader) {
|
||||
const inline = node as ReactElement<InlineItemHeaderProps & { nodeKey?: string | undefined }>;
|
||||
return [cloneElement(inline, { nodeKey: itemHeaderNodeKey }), true];
|
||||
}
|
||||
if (node.type === View) {
|
||||
const view = node as ReactElement<{ style?: StyleInput }>;
|
||||
return [
|
||||
cloneElement(view, {
|
||||
...resolvedPdfFlowProps(resolved),
|
||||
style: composeStyles(view.props.style, resolved.style),
|
||||
}),
|
||||
true,
|
||||
];
|
||||
}
|
||||
if (node.type !== Fragment) return [node, false];
|
||||
|
||||
let bound = false;
|
||||
const fragment = node as ReactElement<{ children?: ReactNode }>;
|
||||
const nextChildren = Children.map(fragment.props.children, (child) => {
|
||||
if (bound) return child;
|
||||
const [next, didBind] = bindFirstExistingView(child);
|
||||
bound = didBind;
|
||||
return next;
|
||||
});
|
||||
return [cloneElement(fragment, {}, nextChildren), bound];
|
||||
};
|
||||
const [boundChildren] = bindFirstExistingView(children);
|
||||
return <SemanticNodeKeyProvider nodeKey={itemHeaderNodeKey}>{boundChildren}</SemanticNodeKeyProvider>;
|
||||
}
|
||||
|
||||
return (
|
||||
<SemanticNodeKeyProvider nodeKey={itemHeaderNodeKey}>
|
||||
<View {...resolvedPdfFlowProps(resolved)} style={composeStyles(sectionItemHeaderStyle, resolved.style)}>
|
||||
<Div nodeKey={itemHeaderNodeKey} style={composeStyles(sectionItemHeaderStyle)}>
|
||||
{children}
|
||||
</View>
|
||||
</Div>
|
||||
</SemanticNodeKeyProvider>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user