@weasyprint-tsx/ui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "@weasyprint-tsx/ui",
3
+ "version" : "0.0.1",
4
+ "workspaces": [
5
+ "packages/*"
6
+ ]
7
+ }
@@ -0,0 +1,16 @@
1
+ .container {
2
+ margin-left: calc(-1 * var(--block-box-gap, 0));
3
+ }
4
+
5
+ .block {
6
+ display: inline-block;
7
+ box-sizing: border-box;
8
+ width: calc(var(--ratio, 1) * 100% / var(--block-box-basis, 1));
9
+ vertical-align: var(--block-box-align, middle);
10
+ padding-left: var(--block-box-gap, 0);
11
+ }
12
+
13
+ .centered > * {
14
+ display: block;
15
+ margin: 0 auto;
16
+ }
@@ -0,0 +1,66 @@
1
+ import { ComponentProps, toChildArray, VNode } from "preact";
2
+ import styles from "./BlockBox.module.css";
3
+ import { joinClasses, mergeStyle } from "./utils";
4
+
5
+ interface BlockBoxProps extends ComponentProps<"div"> {
6
+ children: VNode<BlockProps>[] | VNode<BlockProps>;
7
+ gap?: string;
8
+ basis?: number;
9
+ centered?: boolean;
10
+ align?: "middle" | "top" | "bottom";
11
+ }
12
+
13
+ interface BlockProps extends ComponentProps<"div"> {
14
+ ratio?: number;
15
+ centered?: boolean;
16
+ align?: "middle" | "top" | "bottom";
17
+ }
18
+
19
+ export function Block(_props: BlockProps): null {
20
+ return null;
21
+ }
22
+
23
+ export function BlockBox({
24
+ children,
25
+ className,
26
+ basis,
27
+ gap,
28
+ align,
29
+ centered: parentCentered = true,
30
+ style,
31
+ ...props
32
+ }: BlockBoxProps) {
33
+ const blockList = toChildArray(children).filter(
34
+ (child) => (child as VNode).type === Block,
35
+ ) as VNode<BlockProps>[];
36
+
37
+ const blockBasis =
38
+ basis ?? blockList.reduce((acc, b) => acc + (b.props.ratio ?? 1), 0);
39
+
40
+ const child = blockList.map(
41
+ ({ props: { style, ratio = 1, className, centered, align, ...props } }) => (
42
+ <div
43
+ style={mergeStyle(style, { "--ratio": ratio, "--block-box-align": align })}
44
+ className={joinClasses(
45
+ className,
46
+ styles.block,
47
+ (centered ?? parentCentered) ? styles.centered : undefined,
48
+ )}
49
+ {...props}
50
+ />
51
+ ),
52
+ );
53
+
54
+ return (
55
+ <div
56
+ children={child}
57
+ className={joinClasses(className, styles.container)}
58
+ style={mergeStyle(style, {
59
+ "--block-box-basis": blockBasis,
60
+ "--block-box-gap": gap,
61
+ "--block-box-align": align,
62
+ })}
63
+ {...props}
64
+ />
65
+ );
66
+ }
@@ -0,0 +1,17 @@
1
+ import hljs from "highlight.js";
2
+ import "highlight.js/styles/atom-one-dark.css";
3
+ import { ComponentProps } from "preact";
4
+
5
+ interface CodeBlockProps extends Omit<ComponentProps<"code">, "children"> {
6
+ language: string;
7
+ code: string;
8
+ }
9
+
10
+ export function CodeBlock({ code, language, ...props }: CodeBlockProps) {
11
+ const codeBlock = hljs.highlight(code, { language: language });
12
+ return (
13
+ <pre style={{breakInside : "avoid"}}>
14
+ <code {...props} dangerouslySetInnerHTML={{ __html: codeBlock.value }} />
15
+ </pre>
16
+ );
17
+ }
@@ -0,0 +1,24 @@
1
+ /* block mode: stacked answer lines */
2
+ .block {
3
+ display: block;
4
+ }
5
+
6
+ .line {
7
+ display: block;
8
+ min-height: var(--dotline-height, 6mm);
9
+
10
+ margin-bottom: 2mm;
11
+ border-bottom: 1px dotted var(--dotline-color, #808080);
12
+ &:is(:last-child) {
13
+ margin-bottom: 3mm;
14
+ }
15
+ }
16
+
17
+ /* inline mode: fill-in-the-blank inside <p> */
18
+ .inline {
19
+ display: inline-block;
20
+ vertical-align: baseline;
21
+ height: 0;
22
+ margin: 0 1mm;
23
+ border-bottom: 1px dotted var(--dotline-color, #808080);
24
+ }
@@ -0,0 +1,45 @@
1
+ import { ComponentProps } from "preact";
2
+ import styles from "./DotLine.module.css";
3
+ import { joinClasses, mergeStyle } from "./utils";
4
+
5
+ interface DotLineProps extends ComponentProps<"span"> {
6
+ num?: number;
7
+ width?: number | string;
8
+ inline?: boolean;
9
+ color?: string;
10
+ lineHeight?: string;
11
+ }
12
+
13
+ export function DotLine({
14
+ num = 1,
15
+ children,
16
+ width = "100%",
17
+ style,
18
+ className = "",
19
+ inline,
20
+ color,
21
+ lineHeight,
22
+ ...props
23
+ }: DotLineProps) {
24
+ if (num === 0) return;
25
+ const css = mergeStyle(style, { width, "--dotline-color": color, "--dotline-height": lineHeight });
26
+
27
+ if (inline)
28
+ return (
29
+ <span
30
+ className={joinClasses(className, styles.inline)}
31
+ style={css}
32
+ {...props}
33
+ />
34
+ );
35
+
36
+ return (
37
+ <span className={joinClasses(className, styles.block)} style={css} {...props}>
38
+ {Array.from({ length: num }, (_, k) => (
39
+ <span key={k} className={styles.line}>
40
+ {k === 0 ? children : null}
41
+ </span>
42
+ ))}
43
+ </span>
44
+ );
45
+ }
@@ -0,0 +1,4 @@
1
+ .equation {
2
+ /* display: block; */
3
+ padding: 2px;
4
+ }
@@ -0,0 +1,38 @@
1
+ import { renderToString } from "katex";
2
+ import "katex/contrib/mhchem";
3
+ import "katex/dist/katex.min.css";
4
+ import { ComponentProps } from "preact";
5
+ import styles from "./Equation.module.css";
6
+ import { joinClasses } from "./utils";
7
+
8
+ interface EquationProps extends Omit<ComponentProps<"span">, "children"> {
9
+ tex: string;
10
+ displayMode?: boolean;
11
+ aligned?: boolean;
12
+ chemical?: boolean;
13
+ }
14
+ export function Equation({
15
+ tex,
16
+ displayMode,
17
+ className = "",
18
+ aligned = false,
19
+ chemical = false,
20
+ ...props
21
+ }: EquationProps) {
22
+ const alignedCode = aligned
23
+ ? `\\begin{aligned}
24
+ ${tex}
25
+ \\end{aligned}`
26
+ : tex;
27
+ const chemCode = chemical ? `\\ce{${alignedCode}}` : alignedCode;
28
+
29
+ return (
30
+ <span
31
+ dangerouslySetInnerHTML={{
32
+ __html: renderToString(chemCode, { displayMode }),
33
+ }}
34
+ className={joinClasses(className, styles.equation)}
35
+ {...props}
36
+ />
37
+ );
38
+ }
@@ -0,0 +1,53 @@
1
+ .ol {
2
+ & > .li {
3
+ counter-increment: list-items;
4
+ &::before {
5
+ content: var(--ol-marker-pre, "") counter(list-items, decimal)
6
+ var(--ol-marker-post, ". ");
7
+ margin-right: 1mm;
8
+ }
9
+ }
10
+ &[data-counter="lower-alpha"] .li::before {
11
+ content: var(--ol-marker-pre, "") counter(list-items, lower-alpha)
12
+ var(--ol-marker-post, ". ");
13
+ }
14
+ &[data-counter="upper-alpha"] .li::before {
15
+ content: var(--ol-marker-pre, "") counter(list-items, upper-alpha)
16
+ var(--ol-marker-post, ". ");
17
+ }
18
+ &[data-counter="lower-roman"] .li::before {
19
+ content: var(--ol-marker-pre, "") counter(list-items, lower-roman)
20
+ var(--ol-marker-post, ". ");
21
+ }
22
+ &[data-counter="upper-roman"] .li::before {
23
+ content: var(--ol-marker-pre, "") counter(list-items, upper-roman)
24
+ var(--ol-marker-post, ". ");
25
+ }
26
+ }
27
+
28
+ .ul {
29
+ .li {
30
+ &::before {
31
+ content: var(--ul-marker, "•");
32
+ margin-right: 1mm;
33
+ }
34
+ }
35
+ }
36
+
37
+ /* hanging indent so wrapped text aligns under content, not marker */
38
+ .li {
39
+ &:not(:last-child) {
40
+ margin-bottom: var(--list-gap, 0);
41
+ }
42
+ }
43
+
44
+ /* pre+body side-by-side on an inner wrapper — outer div stays unstyled */
45
+ .preWrapper {
46
+ display: grid;
47
+ grid-template-columns: max-content 1fr;
48
+ align-items: start;
49
+ }
50
+
51
+ .pre {
52
+ padding-right: 1mm;
53
+ }
package/src/List.tsx ADDED
@@ -0,0 +1,120 @@
1
+ import { ComponentChildren, ComponentProps } from "preact";
2
+ import styles from "./List.module.css";
3
+ import { cssString, joinClasses, mergeStyle } from "./utils";
4
+
5
+ export type CounterType =
6
+ | "decimal"
7
+ | "lower-alpha"
8
+ | "upper-alpha"
9
+ | "lower-roman"
10
+ | "upper-roman";
11
+
12
+ interface ListItemProps extends ComponentProps<"div"> {
13
+ count?: number;
14
+ marker?: string;
15
+ }
16
+
17
+ interface ListProps extends ComponentProps<"div"> {
18
+ pre?: ComponentChildren;
19
+ gap?: string | number;
20
+ }
21
+
22
+ interface OLProps extends ListProps {
23
+ start?: number;
24
+ counterType?: CounterType;
25
+ markerPre?: string;
26
+ markerPost?: string;
27
+ }
28
+
29
+ interface ULProps extends ListProps {
30
+ marker?: string;
31
+ }
32
+
33
+ export function LI({
34
+ className = "",
35
+ count = NaN,
36
+ style,
37
+ marker,
38
+ ...props
39
+ }: ListItemProps) {
40
+ const css = mergeStyle(style, {
41
+ counterReset: count ? `list-items ${count - 1}` : undefined,
42
+ "--ul-marker": cssString(marker),
43
+ });
44
+ return (
45
+ <div style={css} {...props} className={joinClasses(className, styles.li)} />
46
+ );
47
+ }
48
+
49
+ function Body({
50
+ pre,
51
+ children,
52
+ }: {
53
+ pre?: ComponentChildren;
54
+ children: ComponentChildren;
55
+ }) {
56
+ if (!pre) return <>{children}</>;
57
+ return (
58
+ <div className={styles.preWrapper}>
59
+ <div className={styles.pre}>{pre}</div>
60
+ <div>{children}</div>
61
+ </div>
62
+ );
63
+ }
64
+
65
+ export function OL({
66
+ className = "",
67
+ start = 1,
68
+ counterType = "decimal",
69
+ markerPre,
70
+ markerPost,
71
+ style,
72
+ pre,
73
+ gap,
74
+ children,
75
+ ...props
76
+ }: OLProps) {
77
+ const css = mergeStyle(style, {
78
+ counterReset: `list-items ${start - 1}`,
79
+ "--ol-marker-pre": cssString(markerPre),
80
+ "--ol-marker-post": cssString(markerPost),
81
+ "--list-gap": gap,
82
+ });
83
+ return (
84
+ <div
85
+ {...props}
86
+ className={joinClasses(className, styles.ol)}
87
+ style={css}
88
+ data-counter={counterType}
89
+ >
90
+ <Body pre={pre}>{children}</Body>
91
+ </div>
92
+ );
93
+ }
94
+
95
+ export function UL({
96
+ className = "",
97
+ marker,
98
+ style,
99
+ children,
100
+ pre,
101
+ gap,
102
+ ...props
103
+ }: ULProps) {
104
+ const css = mergeStyle(style, {
105
+ "--ul-marker": cssString(marker),
106
+ "--list-gap": gap,
107
+ });
108
+ return (
109
+ <div {...props} className={joinClasses(className, styles.ul)} style={css}>
110
+ <Body pre={pre}>{children}</Body>
111
+ </div>
112
+ );
113
+ }
114
+
115
+ export function List({
116
+ kind,
117
+ ...props
118
+ }: ListProps & { kind: "ul" | "ol"; start?: number }) {
119
+ return kind === "ul" ? <UL {...props} /> : <OL {...props} />;
120
+ }
@@ -0,0 +1,14 @@
1
+ .pagebreak {
2
+ break-before: always;
3
+ height: 0;
4
+ width: 0;
5
+ margin: 0;
6
+ padding: 0;
7
+ }
8
+
9
+ .page {
10
+ min-height: 100%;
11
+ width: 100%;
12
+ break-before: always;
13
+ break-after: always;
14
+ }
package/src/Page.tsx ADDED
@@ -0,0 +1,33 @@
1
+ import { ComponentProps } from "preact";
2
+ import styles from "./Page.module.css";
3
+ import { joinClasses, mergeStyle } from "./utils";
4
+ interface PageProps {
5
+ page?: string;
6
+ }
7
+
8
+ export function PageBreak({ page }: PageProps) {
9
+ return (
10
+ <div
11
+ style={{
12
+ page,
13
+ }}
14
+ className={styles.pagebreak}
15
+ />
16
+ );
17
+ }
18
+ export function Page({
19
+ page,
20
+ style,
21
+ className = "",
22
+ ...props
23
+ }: PageProps & ComponentProps<"section">) {
24
+ const css = mergeStyle(style, { page });
25
+
26
+ return (
27
+ <section
28
+ style={css}
29
+ className={joinClasses(className, styles.page)}
30
+ {...props}
31
+ />
32
+ );
33
+ }
@@ -0,0 +1,55 @@
1
+ .table {
2
+ --table-border-width: 1px;
3
+ --table-border-color: currentColor;
4
+ border-collapse: collapse;
5
+ }
6
+
7
+ .table th,
8
+ .table td {
9
+ text-align: center;
10
+ vertical-align: middle;
11
+ }
12
+
13
+ .table th {
14
+ background-color: var(--table-header-color, transparent);
15
+ font-size: var(--table-header-fontsize, inherit);
16
+ }
17
+
18
+ .table td {
19
+ background-color: var(--table-cell-color, transparent);
20
+ font-size: var(--table-cell-fontsize, inherit);
21
+ }
22
+
23
+ /* ── Row orientation ──────────────────────────────────────────── */
24
+ .row td,
25
+ .row th {
26
+ border-right: var(--table-border-width, 1px) solid var(--table-border-color, currentColor);
27
+ border-bottom: var(--table-border-width, 1px) solid var(--table-border-color, currentColor);
28
+ }
29
+
30
+ .row tr td:last-child,
31
+ .row tr th:last-child {
32
+ border-right: none;
33
+ }
34
+
35
+ .row tr:last-child td,
36
+ .row tr:last-child th {
37
+ border-bottom: none;
38
+ }
39
+
40
+ /* ── Col orientation ──────────────────────────────────────────── */
41
+ .col td,
42
+ .col th {
43
+ border-right: var(--table-border-width, 1px) solid var(--table-border-color, currentColor);
44
+ border-bottom: var(--table-border-width, 1px) solid var(--table-border-color, currentColor);
45
+ }
46
+
47
+ .col tr td:last-child,
48
+ .col tr th:last-child {
49
+ border-right: none;
50
+ }
51
+
52
+ .col tbody tr:last-child td,
53
+ .col tbody tr:last-child th {
54
+ border-bottom: none;
55
+ }
package/src/Table.tsx ADDED
@@ -0,0 +1,164 @@
1
+ import {
2
+ ComponentChild,
3
+ ComponentProps,
4
+ toChildArray,
5
+ VNode
6
+ } from "preact";
7
+ import styles from "./Table.module.css";
8
+ import { joinClasses, mergeStyle } from "./utils";
9
+
10
+
11
+
12
+ export interface TableEntryProps extends ComponentProps<"th"> {
13
+ content: ComponentChild[];
14
+ contentClass?: string;
15
+ headerBg?: string;
16
+ cellBg?: string;
17
+ headerFontSize?: string;
18
+ cellFontSize?: string;
19
+ }
20
+
21
+ export function Entry(_props: TableEntryProps): null {
22
+ return null;
23
+ }
24
+
25
+ interface TableProps extends ComponentProps<"table"> {
26
+ orientation?: "col" | "row";
27
+ contentClass?: string;
28
+ headerClass?: string;
29
+ headerBg?: string;
30
+ cellBg?: string;
31
+ headerFontSize?: string;
32
+ cellFontSize?: string;
33
+ borderWidth?: string;
34
+ borderColor?: string;
35
+ children: VNode<TableEntryProps> | VNode<TableEntryProps>[];
36
+ }
37
+
38
+
39
+ export function Table({
40
+ children,
41
+ orientation = "col",
42
+ className,
43
+ style,
44
+ contentClass: parentContentClass = "",
45
+ headerClass,
46
+ headerBg,
47
+ cellBg,
48
+ headerFontSize,
49
+ cellFontSize,
50
+ borderWidth,
51
+ borderColor,
52
+ ...props
53
+ }: TableProps) {
54
+ const headers = toChildArray(children)
55
+ .filter((child) => (child as VNode).type === Entry)
56
+ .map((child) => (child as VNode<TableEntryProps>).props);
57
+
58
+ const tableClass = joinClasses(
59
+ className,
60
+ styles.table,
61
+ styles[orientation],
62
+ ).trim();
63
+
64
+ const tableStyle = mergeStyle(style, {
65
+ "--table-header-color": headerBg,
66
+ "--table-cell-color": cellBg,
67
+ "--table-header-fontsize": headerFontSize,
68
+ "--table-cell-fontsize": cellFontSize,
69
+ "--table-border-width": borderWidth,
70
+ "--table-border-color": borderColor,
71
+ });
72
+
73
+ if (orientation === "row") {
74
+ return (
75
+ <table className={tableClass} style={tableStyle} {...props}>
76
+ <tbody>
77
+ {headers.map(
78
+ ({
79
+ children,
80
+ content,
81
+ className,
82
+ contentClass,
83
+ style,
84
+ headerBg,
85
+ cellBg,
86
+ headerFontSize,
87
+ cellFontSize,
88
+ ...props
89
+ }, i) => (
90
+ <tr key={i}>
91
+ <th
92
+ scope="row"
93
+ className={joinClasses(className, headerClass)}
94
+ style={mergeStyle(style, {
95
+ "--table-header-color": headerBg,
96
+ "--table-header-fontsize": headerFontSize,
97
+ })}
98
+ {...props}
99
+ >
100
+ {children}
101
+ </th>
102
+ {content.map((cell, j) => (
103
+ <td
104
+ key={j}
105
+ className={joinClasses(contentClass, parentContentClass)}
106
+ style={{ "--table-cell-color": cellBg, "--table-cell-fontsize": cellFontSize }}
107
+ >
108
+ {cell}
109
+ </td>
110
+ ))}
111
+ </tr>
112
+ ),
113
+ )}
114
+ </tbody>
115
+ </table>
116
+ );
117
+ }
118
+
119
+ const rowCount = Math.max(...headers.map((h) => h.content.length));
120
+ return (
121
+ <table className={tableClass} style={tableStyle} {...props}>
122
+ <thead>
123
+ <tr>
124
+ {headers.map(
125
+ (
126
+ { children, content, className, contentClass, style, headerBg, cellBg, headerFontSize, cellFontSize, ...props },
127
+ i,
128
+ ) => (
129
+ <th
130
+ key={i}
131
+ className={joinClasses(className, headerClass)}
132
+ style={mergeStyle(style, {
133
+ "--table-header-color": headerBg,
134
+ "--table-header-fontsize": headerFontSize,
135
+ })}
136
+ {...props}
137
+ >
138
+ {children}
139
+ </th>
140
+ ),
141
+ )}
142
+ </tr>
143
+ </thead>
144
+ <tbody>
145
+ {Array.from({ length: rowCount }, (_, rowIdx) => (
146
+ <tr key={rowIdx}>
147
+ {headers.map(
148
+ ({ content, children, contentClass = "", cellBg, cellFontSize, ...props }, colIdx) => (
149
+ <td
150
+ key={colIdx}
151
+ {...props}
152
+ className={joinClasses(contentClass, parentContentClass)}
153
+ style={{ "--table-cell-color": cellBg, "--table-cell-fontsize": cellFontSize }}
154
+ >
155
+ {content[rowIdx]}
156
+ </td>
157
+ ),
158
+ )}
159
+ </tr>
160
+ ))}
161
+ </tbody>
162
+ </table>
163
+ );
164
+ }
@@ -0,0 +1,75 @@
1
+
2
+ .h1,
3
+ .h2,
4
+ .h3,
5
+ .h4,
6
+ .h5,
7
+ .h6 {
8
+ &::before {
9
+ float: left;
10
+ clear: left;
11
+ }
12
+ &[data-marker]::before {
13
+ content: attr(data-marker);
14
+ }
15
+ }
16
+
17
+
18
+ .h1 {
19
+ color: var(--h1-color, inherit);
20
+ font-size: var(--h1-fontsize, inherit);
21
+ counter-reset: count-h2 count-h3 count-h4 count-h5 count-h6;
22
+ counter-increment: count-h1;
23
+ &::before {
24
+ content: counter(count-h1, upper-roman) ". ";
25
+ }
26
+ }
27
+
28
+ .h2 {
29
+ color: var(--h2-color, inherit);
30
+ font-size: var(--h2-fontsize, inherit);
31
+ counter-reset: count-h3 count-h4 count-h5 count-h6;
32
+ counter-increment: count-h2;
33
+ &::before {
34
+ content: counter(count-h2, decimal) ". ";
35
+ }
36
+ }
37
+
38
+ .h3 {
39
+ color: var(--h3-color, inherit);
40
+ font-size: var(--h3-fontsize, inherit);
41
+ counter-reset: count-h4 count-h5 count-h6;
42
+ counter-increment: count-h3;
43
+ &::before {
44
+ content: counter(count-h3, lower-alpha) ". ";
45
+ }
46
+ }
47
+
48
+ .h4 {
49
+ color: var(--h4-color, inherit);
50
+ font-size: var(--h4-fontsize, inherit);
51
+ counter-reset: count-h5 count-h6;
52
+ counter-increment: count-h4;
53
+ &::before {
54
+ content: counter(count-h4, lower-alpha) ". ";
55
+ }
56
+ }
57
+
58
+ .h5 {
59
+ color: var(--h5-color, inherit);
60
+ font-size: var(--h5-fontsize, inherit);
61
+ counter-reset: count-h6;
62
+ counter-increment: count-h5;
63
+ &::before {
64
+ content: counter(count-h5, lower-alpha) ". ";
65
+ }
66
+ }
67
+
68
+ .h6 {
69
+ color: var(--h6-color, inherit);
70
+ font-size: var(--h6-fontsize, inherit);
71
+ counter-increment: count-h6;
72
+ &::before {
73
+ content: counter(count-h6, lower-alpha) ". ";
74
+ }
75
+ }
package/src/Titles.tsx ADDED
@@ -0,0 +1,105 @@
1
+ import { ComponentProps, ComponentType } from "preact";
2
+ import styles from "./Title.module.css";
3
+ import { joinClasses, mergeStyle } from "./utils";
4
+
5
+ type Htype = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
6
+
7
+ type Hprops<h extends Htype> = ComponentProps<h> & {
8
+ marker?: string;
9
+ color?: string;
10
+ fontSize?: string;
11
+ };
12
+
13
+ export function H1({ className = "", marker, color, fontSize, style, ...props }: Hprops<"h1">) {
14
+ return (
15
+ <h1
16
+ data-marker={marker}
17
+ className={joinClasses(className, styles.h1)}
18
+ style={mergeStyle(style, { "--h1-color": color, "--h1-fontsize": fontSize })}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+ export function H2({ className = "", marker, color, fontSize, style, ...props }: Hprops<"h2">) {
24
+ return (
25
+ <h2
26
+ data-marker={marker}
27
+ className={joinClasses(className, styles.h2)}
28
+ style={mergeStyle(style, { "--h2-color": color, "--h2-fontsize": fontSize })}
29
+ {...props}
30
+ />
31
+ );
32
+ }
33
+ export function H3({ className = "", marker, color, fontSize, style, ...props }: Hprops<"h3">) {
34
+ return (
35
+ <h3
36
+ data-marker={marker}
37
+ className={joinClasses(className, styles.h3)}
38
+ style={mergeStyle(style, { "--h3-color": color, "--h3-fontsize": fontSize })}
39
+ {...props}
40
+ />
41
+ );
42
+ }
43
+ export function H4({ className = "", marker, color, fontSize, style, ...props }: Hprops<"h4">) {
44
+ return (
45
+ <h4
46
+ data-marker={marker}
47
+ className={joinClasses(className, styles.h4)}
48
+ style={mergeStyle(style, { "--h4-color": color, "--h4-fontsize": fontSize })}
49
+ {...props}
50
+ />
51
+ );
52
+ }
53
+ export function H5({ className = "", marker, color, fontSize, style, ...props }: Hprops<"h5">) {
54
+ return (
55
+ <h5
56
+ data-marker={marker}
57
+ className={joinClasses(className, styles.h5)}
58
+ style={mergeStyle(style, { "--h5-color": color, "--h5-fontsize": fontSize })}
59
+ {...props}
60
+ />
61
+ );
62
+ }
63
+ export function H6({ className = "", marker, color, fontSize, style, ...props }: Hprops<"h6">) {
64
+ return (
65
+ <h6
66
+ data-marker={marker}
67
+ className={joinClasses(className, styles.h6)}
68
+ style={mergeStyle(style, { "--h6-color": color, "--h6-fontsize": fontSize })}
69
+ {...props}
70
+ />
71
+ );
72
+ }
73
+
74
+ const TAG_MAP = { h1: H1, h2: H2, h3: H3, h4: H4, h5: H5, h6: H6 };
75
+
76
+ type TitleProps<h extends Htype> = Hprops<h> & { type: h };
77
+ export function Title<h extends Htype>({ type, ...props }: TitleProps<h>) {
78
+ const Tag = TAG_MAP[type] as ComponentType<Hprops<h>>;
79
+ return <Tag {...(props as Hprops<h>)} />;
80
+ }
81
+
82
+ export function ResetCounter({
83
+ type,
84
+ value,
85
+ }: {
86
+ type: Htype | Htype[];
87
+ value?: number | (number | null)[];
88
+ }) {
89
+ const types = Array.isArray(type) ? type : [type];
90
+ const values = Array.isArray(value) ? value : [value];
91
+ return (
92
+ <div
93
+ style={{
94
+ counterReset: types
95
+ .map((t, k) =>
96
+ `count-${t} ${(values[k % types.length] ?? 1) - 1}`.trim(),
97
+ )
98
+ .join(" "),
99
+ visibility: "hidden",
100
+ height: 0,
101
+ width: 0,
102
+ }}
103
+ />
104
+ );
105
+ }
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ export { Block, BlockBox } from "./BlockBox";
2
+ export { CodeBlock } from "./CodeBlock";
3
+ export { DotLine } from "./DotLine";
4
+ export { Equation } from "./Equation";
5
+ export { LI, OL, UL } from "./List";
6
+ export { Page, PageBreak } from "./Page";
7
+ export { Entry, Table } from "./Table";
8
+ export { H1, H2, H3, H4, H5, H6, ResetCounter, Title } from "./Titles";
9
+
package/src/utils.tsx ADDED
@@ -0,0 +1,26 @@
1
+ import { CSSProperties, SignalLike, Signalish } from "preact";
2
+
3
+ export const cssString = (v: string | undefined) =>
4
+ v !== undefined ? `"${v}"` : undefined;
5
+
6
+ export function joinClasses(
7
+ ...classes: (string | SignalLike<string | undefined> | undefined)[]
8
+ ): string {
9
+ return classes.join(" ").trim();
10
+ }
11
+
12
+ export function mergeStyle(
13
+ base: Signalish<string | CSSProperties | undefined>,
14
+ extra: CSSProperties,
15
+ ): CSSProperties | string {
16
+ if (typeof base === "string") {
17
+ const extraStr = Object.entries(extra)
18
+ .filter(([, v]) => v != null)
19
+ .map(
20
+ ([k, v]) => `${k.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}:${v}`,
21
+ )
22
+ .join(";");
23
+ return extraStr ? `${base};${extraStr}` : base;
24
+ }
25
+ return { ...(base?.value as CSSProperties), ...extra };
26
+ }