This commit is contained in:
guanghechen 2024-07-10 16:34:28 +08:00
parent cfa6efc6ed
commit 94d944bd68
25 changed files with 131 additions and 118 deletions

View file

@ -2,7 +2,7 @@ import React from "react";
import Image from "next/image"; import Image from "next/image";
import { ReactMarkdown } from "@/components/Markdown"; import { ReactMarkdown } from "@/components/Markdown";
interface ContextItemProps { interface ContextItemProperties {
item: { item: {
name: string; name: string;
url: string; url: string;
@ -16,7 +16,7 @@ interface ContextItemProps {
}; };
} }
const ContextItem: React.FC<ContextItemProps> = ({ item }) => { const ContextItem: React.FC<ContextItemProperties> = ({ item }) => {
return ( return (
<div className="border p-4 rounded-lg mb-4 dark:border-gray-700"> <div className="border p-4 rounded-lg mb-4 dark:border-gray-700">
<h4 className="font-bold text-black dark:text-white">{item.name}</h4> <h4 className="font-bold text-black dark:text-white">{item.name}</h4>

View file

@ -5,30 +5,30 @@ import React from "react";
import type { INodeRenderer, INodeRendererMap } from "./context"; import type { INodeRenderer, INodeRendererMap } from "./context";
import { useNodeRendererContext } from "./context"; import { useNodeRendererContext } from "./context";
export interface INodesRendererProps { export interface INodesRendererProperties {
/** /**
* Ast nodes. * Ast nodes.
*/ */
nodes?: Node[]; nodes?: Node[];
} }
export const NodesRenderer: React.FC<INodesRendererProps> = props => { export const NodesRenderer: React.FC<INodesRendererProperties> = properties => {
const { nodes } = props; const { nodes } = properties;
const { viewmodel } = useNodeRendererContext(); const { viewmodel } = useNodeRendererContext();
const rendererMap: Readonly<INodeRendererMap> = useStateValue(viewmodel.rendererMap$); const rendererMap: Readonly<INodeRendererMap> = useStateValue(viewmodel.rendererMap$);
if (!Array.isArray(nodes) || nodes.length <= 0) return <React.Fragment />; if (!Array.isArray(nodes) || nodes.length <= 0) return <React.Fragment />;
return <NodesRendererInner nodes={nodes} rendererMap={rendererMap} />; return <NodesRendererInner nodes={nodes} rendererMap={rendererMap} />;
}; };
interface IProps { interface IProperties {
nodes: Node[]; nodes: Node[];
rendererMap: Readonly<INodeRendererMap>; rendererMap: Readonly<INodeRendererMap>;
} }
class NodesRendererInner extends React.Component<IProps> { class NodesRendererInner extends React.Component<IProperties> {
public override shouldComponentUpdate(nextProps: Readonly<IProps>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<IProperties>): boolean {
const props = this.props; const properties = this.props;
return !isEqual(props.nodes, nextProps.nodes) || props.rendererMap !== nextProps.rendererMap; return !isEqual(properties.nodes, nextProperties.nodes) || properties.rendererMap !== nextProperties.rendererMap;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {

View file

@ -14,7 +14,7 @@ import { NodesRenderer } from "./NodesRenderer";
import { parser } from "./parser"; import { parser } from "./parser";
import { buildNodeRendererMap } from "./renderer"; import { buildNodeRendererMap } from "./renderer";
export interface IMarkdownProps { export interface IMarkdownProperties {
/** /**
* Text content of markdown. * Text content of markdown.
*/ */
@ -52,7 +52,7 @@ export interface IMarkdownProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export const ReactMarkdown: React.FC<IMarkdownProps> = props => { export const ReactMarkdown: React.FC<IMarkdownProperties> = properties => {
const { const {
presetDefinitionMap, presetDefinitionMap,
customizedRendererMap, customizedRendererMap,
@ -62,15 +62,15 @@ export const ReactMarkdown: React.FC<IMarkdownProps> = props => {
themeScheme = "lighten", themeScheme = "lighten",
className, className,
style, style,
} = props; } = properties;
const ast: Root = React.useMemo(() => { const ast: Root = React.useMemo(() => {
const asts: Root[] = Array.isArray(text) ? text.map(t => parser.parse(t)) : [parser.parse(text)]; const asts: Root[] = Array.isArray(text) ? text.map(t => parser.parse(t)) : [parser.parse(text)];
if (asts.length === 0) { if (asts.length === 0) {
return parser.parse(""); return parser.parse("");
} }
const root: Root = asts[0]; const root: Root = asts[0];
for (let i = 1; i < asts.length; ++i) { for (let index = 1; index < asts.length; ++index) {
root.children.push(...asts[i].children); root.children.push(...asts[index].children);
} }
return root; return root;
}, [text]); }, [text]);

View file

@ -4,7 +4,7 @@ import type { INodeRendererMap } from "./types";
export type IReactMarkdownThemeScheme = "lighten" | "darken" | string; export type IReactMarkdownThemeScheme = "lighten" | "darken" | string;
export interface IReactMarkdownViewModelProps { export interface IReactMarkdownViewModelProperties {
/** /**
* Link / Image reference definitions. * Link / Image reference definitions.
*/ */
@ -37,10 +37,10 @@ export class ReactMarkdownViewModel extends ViewModel {
public readonly showCodeLineno$: State<boolean>; public readonly showCodeLineno$: State<boolean>;
public readonly themeScheme$: State<IReactMarkdownThemeScheme>; public readonly themeScheme$: State<IReactMarkdownThemeScheme>;
constructor(props: IReactMarkdownViewModelProps) { constructor(properties: IReactMarkdownViewModelProperties) {
super(); super();
const { definitionMap, rendererMap, showCodeLineno, themeScheme } = props; const { definitionMap, rendererMap, showCodeLineno, themeScheme } = properties;
this.definitionMap$ = new State(definitionMap); this.definitionMap$ = new State(definitionMap);
this.rendererMap$ = new State(rendererMap); this.rendererMap$ = new State(rendererMap);
this.showCodeLineno$ = new State<boolean>(showCodeLineno); this.showCodeLineno$ = new State<boolean>(showCodeLineno);

View file

@ -11,9 +11,9 @@ import { NodesRenderer } from "../NodesRenderer";
* @see https://www.npmjs.com/package/@yozora/tokenizer-blockquote * @see https://www.npmjs.com/package/@yozora/tokenizer-blockquote
*/ */
export class BlockquoteRenderer extends React.Component<Blockquote> { export class BlockquoteRenderer extends React.Component<Blockquote> {
public override shouldComponentUpdate(nextProps: Readonly<Blockquote>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<Blockquote>): boolean {
const props = this.props; const properties = this.props;
return props.children !== nextProps.children; return properties.children !== nextProperties.children;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {

View file

@ -10,9 +10,9 @@ import { CodeRendererInner } from "./inner/CodeRendererInner";
* @see https://www.npmjs.com/package/@yozora/tokenizer-indented-code * @see https://www.npmjs.com/package/@yozora/tokenizer-indented-code
* @see https://www.npmjs.com/package/@yozora/tokenizer-fenced-code * @see https://www.npmjs.com/package/@yozora/tokenizer-fenced-code
*/ */
export const CodeRenderer: INodeRenderer<Code> = props => { export const CodeRenderer: INodeRenderer<Code> = properties => {
const { lang } = props; const { lang } = properties;
const value: string = props.value.replace(/[\n\r]+$/, ""); // Remove trailing line endings. const value: string = properties.value.replace(/[\n\r]+$/, ""); // Remove trailing line endings.
const { viewmodel } = useNodeRendererContext(); const { viewmodel } = useNodeRendererContext();
const preferCodeWrap: boolean = useStateValue(viewmodel.preferCodeWrap$); const preferCodeWrap: boolean = useStateValue(viewmodel.preferCodeWrap$);

View file

@ -11,9 +11,9 @@ import { NodesRenderer } from "../NodesRenderer";
* @see https://www.npmjs.com/package/@yozora/tokenizer-delete * @see https://www.npmjs.com/package/@yozora/tokenizer-delete
*/ */
export class DeleteRenderer extends React.Component<Delete> { export class DeleteRenderer extends React.Component<Delete> {
public override shouldComponentUpdate(nextProps: Readonly<Delete>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<Delete>): boolean {
const props = this.props; const properties = this.props;
return props.children !== nextProps.children; return properties.children !== nextProperties.children;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {

View file

@ -11,9 +11,9 @@ import { NodesRenderer } from "../NodesRenderer";
* @see https://www.npmjs.com/package/@yozora/tokenizer-emphasis * @see https://www.npmjs.com/package/@yozora/tokenizer-emphasis
*/ */
export class EmphasisRenderer extends React.Component<Emphasis> { export class EmphasisRenderer extends React.Component<Emphasis> {
public override shouldComponentUpdate(nextProps: Readonly<Emphasis>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<Emphasis>): boolean {
const props = this.props; const properties = this.props;
return props.children !== nextProps.children; return properties.children !== nextProperties.children;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {

View file

@ -6,7 +6,7 @@ import { NodesRenderer } from "../NodesRenderer";
type IHeading = "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; type IHeading = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
interface IProps extends Heading { interface IProperties extends Heading {
linkIcon?: React.ReactNode; linkIcon?: React.ReactNode;
} }
@ -16,14 +16,14 @@ interface IProps extends Heading {
* @see https://www.npmjs.com/package/@yozora/ast#heading * @see https://www.npmjs.com/package/@yozora/ast#heading
* @see https://www.npmjs.com/package/@yozora/tokenizer-heading * @see https://www.npmjs.com/package/@yozora/tokenizer-heading
*/ */
export class HeadingRenderer extends React.Component<IProps> { export class HeadingRenderer extends React.Component<IProperties> {
public override shouldComponentUpdate(nextProps: Readonly<IProps>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<IProperties>): boolean {
const props = this.props; const properties = this.props;
return ( return (
props.depth !== nextProps.depth || properties.depth !== nextProperties.depth ||
props.identifier !== nextProps.identifier || properties.identifier !== nextProperties.identifier ||
props.children !== nextProps.children || properties.children !== nextProperties.children ||
props.linkIcon !== nextProps.linkIcon properties.linkIcon !== nextProperties.linkIcon
); );
} }

View file

@ -10,13 +10,20 @@ import { ImageRendererInner } from "./inner/ImageRendererInner";
* @see https://www.npmjs.com/package/@yozora/ast#image * @see https://www.npmjs.com/package/@yozora/ast#image
* @see https://www.npmjs.com/package/@yozora/tokenizer-image * @see https://www.npmjs.com/package/@yozora/tokenizer-image
*/ */
export const ImageRenderer: INodeRenderer<Image> = props => { export const ImageRenderer: INodeRenderer<Image> = properties => {
const { url: src, alt, title, srcSet, sizes, loading } = props as Image & React.ImgHTMLAttributes<HTMLElement>; const {
url: source,
alt,
title,
srcSet,
sizes,
loading,
} = properties as Image & React.ImgHTMLAttributes<HTMLElement>;
return ( return (
<ImageRendererInner <ImageRendererInner
alt={alt} alt={alt}
src={src} src={source}
title={title} title={title}
srcSet={srcSet} srcSet={srcSet}
sizes={sizes} sizes={sizes}

View file

@ -10,19 +10,19 @@ import { ImageRendererInner } from "./inner/ImageRendererInner";
* @see https://www.npmjs.com/package/@yozora/ast#imageReference * @see https://www.npmjs.com/package/@yozora/ast#imageReference
* @see https://www.npmjs.com/package/@yozora/tokenizer-image-reference * @see https://www.npmjs.com/package/@yozora/tokenizer-image-reference
*/ */
export const ImageReferenceRenderer: INodeRenderer<ImageReference> = props => { export const ImageReferenceRenderer: INodeRenderer<ImageReference> = properties => {
const { viewmodel } = useNodeRendererContext(); const { viewmodel } = useNodeRendererContext();
const definitionMap: Readonly<Record<string, Definition>> = useStateValue(viewmodel.definitionMap$); const definitionMap: Readonly<Record<string, Definition>> = useStateValue(viewmodel.definitionMap$);
const { alt, srcSet, sizes, loading } = props as ImageReference & React.ImgHTMLAttributes<HTMLElement>; const { alt, srcSet, sizes, loading } = properties as ImageReference & React.ImgHTMLAttributes<HTMLElement>;
const definition = definitionMap[props.identifier]; const definition = definitionMap[properties.identifier];
const src: string = definition?.url ?? ""; const source: string = definition?.url ?? "";
const title: string | undefined = definition?.title; const title: string | undefined = definition?.title;
return ( return (
<ImageRendererInner <ImageRendererInner
alt={alt} alt={alt}
src={src} src={source}
title={title} title={title}
srcSet={srcSet} srcSet={srcSet}
sizes={sizes} sizes={sizes}

View file

@ -47,10 +47,10 @@ export function buildNodeRendererMap(
let hasChanged = false; let hasChanged = false;
const result: INodeRendererMap = {} as unknown as INodeRendererMap; const result: INodeRendererMap = {} as unknown as INodeRendererMap;
for (const [key, val] of Object.entries(customizedRendererMap)) { for (const [key, value] of Object.entries(customizedRendererMap)) {
if (val && val !== defaultNodeRendererMap[key]) { if (value && value !== defaultNodeRendererMap[key]) {
hasChanged = true; hasChanged = true;
result[key] = val; result[key] = value;
} }
} }

View file

@ -10,9 +10,9 @@ import { astClasses } from "../context";
* @see https://www.npmjs.com/package/@yozora/tokenizer-inline-code * @see https://www.npmjs.com/package/@yozora/tokenizer-inline-code
*/ */
export class InlineCodeRenderer extends React.Component<InlineCode> { export class InlineCodeRenderer extends React.Component<InlineCode> {
public override shouldComponentUpdate(nextProps: Readonly<InlineCode>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<InlineCode>): boolean {
const props = this.props; const properties = this.props;
return props.value !== nextProps.value; return properties.value !== nextProperties.value;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {

View file

@ -4,7 +4,7 @@ import React from "react";
import { astClasses } from "../../context"; import { astClasses } from "../../context";
import { CopyButton } from "./CopyButton"; import { CopyButton } from "./CopyButton";
interface IProps { interface IProperties {
darken: boolean; darken: boolean;
lang: string; lang: string;
value: string; value: string;
@ -16,7 +16,7 @@ interface IProps {
failedText?: string; failedText?: string;
} }
export class CodeRendererInner extends React.PureComponent<IProps> { export class CodeRendererInner extends React.PureComponent<IProperties> {
public override render(): React.ReactElement { public override render(): React.ReactElement {
const { calcContentForCopy } = this; const { calcContentForCopy } = this;
const { darken, lang, value, preferCodeWrap, showCodeLineno } = this.props; const { darken, lang, value, preferCodeWrap, showCodeLineno } = this.props;
@ -30,7 +30,7 @@ export class CodeRendererInner extends React.PureComponent<IProps> {
showLineNo={showCodeLineno && !preferCodeWrap} showLineNo={showCodeLineno && !preferCodeWrap}
darken={darken} darken={darken}
/> />
<div className={copyBtnCls}> <div className={copyButtonCls}>
<CopyButton calcContentForCopy={calcContentForCopy} /> <CopyButton calcContentForCopy={calcContentForCopy} />
</div> </div>
</code> </code>
@ -42,7 +42,7 @@ export class CodeRendererInner extends React.PureComponent<IProps> {
}; };
} }
const copyBtnCls = css({ const copyButtonCls = css({
position: "absolute", position: "absolute",
right: "4px", right: "4px",
top: "4px", top: "4px",
@ -58,7 +58,7 @@ const codeCls = cx(
borderRadius: "4px", borderRadius: "4px",
margin: "0px 0px 1.25em 0px", margin: "0px 0px 1.25em 0px",
backgroundColor: "var(--colorBgCode)", backgroundColor: "var(--colorBgCode)",
[`&:hover > .${copyBtnCls}`]: { [`&:hover > .${copyButtonCls}`]: {
display: "inline-block", display: "inline-block",
}, },
[`&&[data-wrap="true"] > div`]: { [`&&[data-wrap="true"] > div`]: {

View file

@ -1,7 +1,7 @@
import { css } from "@emotion/css"; import { css } from "@emotion/css";
import React from "react"; import React from "react";
interface IProps { interface IProperties {
src: string; src: string;
alt: string; alt: string;
title: string | undefined; title: string | undefined;
@ -11,17 +11,17 @@ interface IProps {
className: string; className: string;
} }
export class ImageRendererInner extends React.Component<IProps> { export class ImageRendererInner extends React.Component<IProperties> {
public override shouldComponentUpdate(nextProps: IProps): boolean { public override shouldComponentUpdate(nextProperties: IProperties): boolean {
const props = this.props; const properties = this.props;
return ( return (
props.src !== nextProps.src || properties.src !== nextProperties.src ||
props.alt !== nextProps.alt || properties.alt !== nextProperties.alt ||
props.title !== nextProps.title || properties.title !== nextProperties.title ||
props.srcSet !== nextProps.srcSet || properties.srcSet !== nextProperties.srcSet ||
props.sizes !== nextProps.sizes || properties.sizes !== nextProperties.sizes ||
props.loading !== nextProps.loading || properties.loading !== nextProperties.loading ||
props.className !== nextProps.className properties.className !== nextProperties.className
); );
} }

View file

@ -3,21 +3,21 @@ import type { Node } from "@yozora/ast";
import React from "react"; import React from "react";
import { NodesRenderer } from "../../NodesRenderer"; import { NodesRenderer } from "../../NodesRenderer";
interface IProps { interface IProperties {
url: string; url: string;
title: string | undefined; title: string | undefined;
childNodes: Node[] | undefined; childNodes: Node[] | undefined;
className: string; className: string;
} }
export class LinkRendererInner extends React.Component<IProps> { export class LinkRendererInner extends React.Component<IProperties> {
public override shouldComponentUpdate(nextProps: Readonly<IProps>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<IProperties>): boolean {
const props = this.props; const properties = this.props;
return ( return (
props.url !== nextProps.url || properties.url !== nextProperties.url ||
props.title !== nextProps.title || properties.title !== nextProperties.title ||
props.childNodes !== nextProps.childNodes || properties.childNodes !== nextProperties.childNodes ||
props.className !== nextProps.className properties.className !== nextProperties.className
); );
} }

View file

@ -10,7 +10,7 @@ import { LinkRendererInner } from "./inner/LinkRendererInner";
* @see https://www.npmjs.com/package/@yozora/tokenizer-autolink * @see https://www.npmjs.com/package/@yozora/tokenizer-autolink
* @see https://www.npmjs.com/package/@yozora/tokenizer-autolink-extension * @see https://www.npmjs.com/package/@yozora/tokenizer-autolink-extension
*/ */
export const LinkRenderer: INodeRenderer<Link> = props => { export const LinkRenderer: INodeRenderer<Link> = properties => {
const { url, title, children: childNodes } = props; const { url, title, children: childNodes } = properties;
return <LinkRendererInner url={url} title={title} childNodes={childNodes} className={astClasses.link} />; return <LinkRendererInner url={url} title={title} childNodes={childNodes} className={astClasses.link} />;
}; };

View file

@ -9,11 +9,13 @@ import { LinkRendererInner } from "./inner/LinkRendererInner";
* @see https://www.npmjs.com/package/@yozora/ast#linkReference * @see https://www.npmjs.com/package/@yozora/ast#linkReference
* @see https://www.npmjs.com/package/@yozora/tokenizer-link-reference * @see https://www.npmjs.com/package/@yozora/tokenizer-link-reference
*/ */
export const LinkReferenceRenderer: INodeRenderer<LinkReference> = props => { export const LinkReferenceRenderer: INodeRenderer<LinkReference> = properties => {
const { viewmodel } = useNodeRendererContext(); const { viewmodel } = useNodeRendererContext();
const definitionMap: Readonly<Record<string, Definition>> = useStateValue(viewmodel.definitionMap$); const definitionMap: Readonly<Record<string, Definition>> = useStateValue(viewmodel.definitionMap$);
const definition = definitionMap[props.identifier]; const definition = definitionMap[properties.identifier];
const url: string = definition?.url ?? ""; const url: string = definition?.url ?? "";
const title: string | undefined = definition?.title; const title: string | undefined = definition?.title;
return <LinkRendererInner url={url} title={title} childNodes={props.children} className={astClasses.linkReference} />; return (
<LinkRendererInner url={url} title={title} childNodes={properties.children} className={astClasses.linkReference} />
);
}; };

View file

@ -11,13 +11,13 @@ import { NodesRenderer } from "../NodesRenderer";
* @see https://www.npmjs.com/package/@yozora/tokenizer-list * @see https://www.npmjs.com/package/@yozora/tokenizer-list
*/ */
export class ListRenderer extends React.Component<List> { export class ListRenderer extends React.Component<List> {
public override shouldComponentUpdate(nextProps: Readonly<List>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<List>): boolean {
const props = this.props; const properties = this.props;
return ( return (
props.ordered !== nextProps.ordered || properties.ordered !== nextProperties.ordered ||
props.orderType !== nextProps.orderType || properties.orderType !== nextProperties.orderType ||
props.start !== nextProps.start || properties.start !== nextProperties.start ||
props.children !== nextProps.children properties.children !== nextProperties.children
); );
} }

View file

@ -11,9 +11,9 @@ import { NodesRenderer } from "../NodesRenderer";
* @see https://www.npmjs.com/package/@yozora/tokenizer-list-item * @see https://www.npmjs.com/package/@yozora/tokenizer-list-item
*/ */
export class ListItemRenderer extends React.Component<ListItem> { export class ListItemRenderer extends React.Component<ListItem> {
public override shouldComponentUpdate(nextProps: Readonly<ListItem>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<ListItem>): boolean {
const props = this.props; const properties = this.props;
return props.children !== nextProps.children; return properties.children !== nextProperties.children;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {

View file

@ -12,9 +12,9 @@ import { NodesRenderer } from "../NodesRenderer";
* @see https://www.npmjs.com/package/@yozora/tokenizer-paragraph * @see https://www.npmjs.com/package/@yozora/tokenizer-paragraph
*/ */
export class ParagraphRenderer extends React.Component<Paragraph> { export class ParagraphRenderer extends React.Component<Paragraph> {
public override shouldComponentUpdate(nextProps: Readonly<Paragraph>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<Paragraph>): boolean {
const props = this.props; const properties = this.props;
return props.children !== nextProps.children; return properties.children !== nextProperties.children;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {

View file

@ -11,9 +11,9 @@ import { NodesRenderer } from "../NodesRenderer";
* @see https://www.npmjs.com/package/@yozora/tokenizer-emphasis * @see https://www.npmjs.com/package/@yozora/tokenizer-emphasis
*/ */
export class StrongRenderer extends React.Component<Strong> { export class StrongRenderer extends React.Component<Strong> {
public override shouldComponentUpdate(nextProps: Readonly<Strong>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<Strong>): boolean {
const props = this.props; const properties = this.props;
return props.children !== nextProps.children; return properties.children !== nextProperties.children;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {

View file

@ -16,23 +16,25 @@ import { NodesRenderer } from "../NodesRenderer";
* @see https://www.npmjs.com/package/@yozora/tokenizer-table-cell * @see https://www.npmjs.com/package/@yozora/tokenizer-table-cell
*/ */
export class TableRenderer extends React.Component<Table> { export class TableRenderer extends React.Component<Table> {
public override shouldComponentUpdate(nextProps: Readonly<Table>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<Table>): boolean {
const props = this.props; const properties = this.props;
return !isEqual(props.columns, nextProps.columns) || !isEqual(props.children, nextProps.children); return (
!isEqual(properties.columns, nextProperties.columns) || !isEqual(properties.children, nextProperties.children)
);
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {
const { columns, children: rows } = this.props; const { columns, children: rows } = this.props;
const aligns = columns.map(col => col.align ?? undefined); const aligns = columns.map(col => col.align ?? undefined);
const [ths, ...tds] = rows.map(row => const [ths, ...tds] = rows.map(row =>
row.children.map((cell, idx) => <NodesRenderer key={idx} nodes={cell.children} />), row.children.map((cell, index) => <NodesRenderer key={index} nodes={cell.children} />),
); );
return ( return (
<table className={cls}> <table className={cls}>
<thead> <thead>
<tr> <tr>
{ths.map((children, idx) => ( {ths.map((children, index) => (
<Th key={idx} align={aligns[idx]}> <Th key={index} align={aligns[index]}>
{children} {children}
</Th> </Th>
))} ))}
@ -41,8 +43,8 @@ export class TableRenderer extends React.Component<Table> {
<tbody> <tbody>
{tds.map((row, rowIndex) => ( {tds.map((row, rowIndex) => (
<tr key={rowIndex}> <tr key={rowIndex}>
{row.map((children, idx) => ( {row.map((children, index) => (
<td key={idx} align={aligns[idx]}> <td key={index} align={aligns[index]}>
{children} {children}
</td> </td>
))} ))}
@ -54,22 +56,22 @@ export class TableRenderer extends React.Component<Table> {
} }
} }
interface IThProps { interface IThProperties {
align: "left" | "center" | "right" | undefined; align: "left" | "center" | "right" | undefined;
children: React.ReactNode; children: React.ReactNode;
} }
class Th extends React.Component<IThProps> { class Th extends React.Component<IThProperties> {
protected readonly ref: React.RefObject<HTMLTableCellElement>; protected readonly ref: React.RefObject<HTMLTableCellElement>;
constructor(props: IThProps) { constructor(properties: IThProperties) {
super(props); super(properties);
this.ref = { current: null }; this.ref = { current: null };
} }
public override shouldComponentUpdate(nextProps: Readonly<IThProps>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<IThProperties>): boolean {
const props = this.props; const properties = this.props;
return props.align !== nextProps.align || props.children !== nextProps.children; return properties.align !== nextProperties.align || properties.children !== nextProperties.children;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {
@ -84,6 +86,7 @@ class Th extends React.Component<IThProps> {
public override componentDidMount(): void { public override componentDidMount(): void {
const th = this.ref.current; const th = this.ref.current;
if (th) { if (th) {
// eslint-disable-next-line unicorn/prefer-dom-node-text-content
th.setAttribute("title", th.innerText); th.setAttribute("title", th.innerText);
} }
} }
@ -91,6 +94,7 @@ class Th extends React.Component<IThProps> {
public override componentDidUpdate(): void { public override componentDidUpdate(): void {
const th = this.ref.current; const th = this.ref.current;
if (th) { if (th) {
// eslint-disable-next-line unicorn/prefer-dom-node-text-content
th.setAttribute("title", th.innerText); th.setAttribute("title", th.innerText);
} }
} }

View file

@ -8,9 +8,9 @@ import React from "react";
* @see https://www.npmjs.com/package/@yozora/tokenizer-text * @see https://www.npmjs.com/package/@yozora/tokenizer-text
*/ */
export class TextRenderer extends React.Component<Text> { export class TextRenderer extends React.Component<Text> {
public override shouldComponentUpdate(nextProps: Readonly<Text>): boolean { public override shouldComponentUpdate(nextProperties: Readonly<Text>): boolean {
const props = this.props; const properties = this.props;
return props.value !== nextProps.value; return properties.value !== nextProperties.value;
} }
public override render(): React.ReactElement { public override render(): React.ReactElement {

View file

@ -26,7 +26,7 @@ interface ContextItemType {
score?: number; score?: number;
} }
interface NewsDetailProps { interface NewsDetailProperties {
news: { news: {
title: string; title: string;
sections: { sections: {
@ -37,7 +37,7 @@ interface NewsDetailProps {
}; };
} }
const NewsDetail: React.FC<NewsDetailProps> = ({ news }) => { const NewsDetail: React.FC<NewsDetailProperties> = ({ news }) => {
return ( return (
<article className="prose lg:prose-xl dark:prose-invert"> <article className="prose lg:prose-xl dark:prose-invert">
<h1 className="text-black dark:text-white">{news.title}</h1> <h1 className="text-black dark:text-white">{news.title}</h1>
@ -47,8 +47,8 @@ const NewsDetail: React.FC<NewsDetailProps> = ({ news }) => {
<p className="text-black dark:text-white">{section.content}</p> <p className="text-black dark:text-white">{section.content}</p>
<div className="mt-4"> <div className="mt-4">
<h3 className="text-black dark:text-white">Related Context:</h3> <h3 className="text-black dark:text-white">Related Context:</h3>
{section.context.map((item, i) => ( {section.context.map((item, index_) => (
<ContextItem key={i} item={item} /> <ContextItem key={index_} item={item} />
))} ))}
</div> </div>
</section> </section>