feat(playground): improve jsx authoring starters
12
PLAN.md
@@ -1,6 +1,6 @@
|
||||
# JSX / md2pdf ロードマップ
|
||||
|
||||
最終更新: 2026-05-09
|
||||
最終更新: 2026-05-11
|
||||
|
||||
## 目的
|
||||
|
||||
@@ -44,6 +44,13 @@ rich content の応用範囲を磨く。
|
||||
- `Document` root、margin-aware な `Header` / `Footer`、page 全体座標の `Static` は入った。Templates starter / docs
|
||||
を通じて repeated layer、margin area、`Absolute` origin が直感とズレないかを確認し、必要なら wording やサンプルを直す。
|
||||
- `Header` / `Footer` の実用パターンとして、page number、repeated title、watermark / stamp などを starter に残す。
|
||||
- React に慣れた人と AI authoring を主な利用者として意識する。`Text` / MVT は JSX render 時に高さを測れるため、
|
||||
starter / docs では「通常は `height` を書かない」体験に寄せる。固定 field、画像、absolute overlay など
|
||||
明示寸法が意味を持つ場所だけ `height` を書く。
|
||||
- POC playground (`react-pdfme-jsx`) の実用サンプルを参考に、invoice、form、report、research paper、Japanese business
|
||||
document、header/footer/page number、watermark / badge などの starter を増やす。ただし POC 専用 API
|
||||
(`autoHeight`, `staticHeader`, `For` など) はそのまま持ち込まず、現行の `Document` / `Header` / `Footer` /
|
||||
通常の JS 配列処理へ寄せる。
|
||||
- 編集の正は generated `Template + inputs`。JSX source は初期生成 / regeneration 用の metadata として扱う。
|
||||
Designer で編集した template を JSX に自動逆変換することは当面目指さない。
|
||||
- JSX playground は source authoring と Viewer preview に寄せる。Form 入力確認は生成方法に依存しない共通 project 導線として
|
||||
@@ -63,7 +70,8 @@ rich content の応用範囲を磨く。
|
||||
- Form 入力や dynamic layout reflow で `Text` / MVT が runtime に `overflow: "expand"` した場合、
|
||||
JSX の親 `Box` / container は自動では広がらない。生成後の `Box` は rectangle schema であり、
|
||||
子 schema の実描画高さと親 container を結び直す contract がまだないため。親子 container dynamic
|
||||
layout は将来の設計課題として扱う。
|
||||
layout は実装したい重要課題として扱う。まずは JSX から生成された visual `Box` と子 text/MVT の関係を
|
||||
metadata として template に残すか、単一 text/MVT 子の decoration に畳むかを比較する。
|
||||
- `Absolute` は `Page`, top `Static`, `Box` 内の小さな escape hatch として扱う。`Stack` / `Row`
|
||||
直下対応、anchor / top-right / bottom-right shorthand、z-index 的な描画順制御は必要性が出てから検討する。
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -359,6 +359,23 @@ describe('@pdfme/jsx renderToTemplate', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('advances Stack children by auto-measured Text height', async () => {
|
||||
const result = await renderToTemplate(
|
||||
<Page margin={0}>
|
||||
<Stack gap={2}>
|
||||
<Text width={24} lineHeight={1.2}>
|
||||
This text wraps and does not specify height.
|
||||
</Text>
|
||||
<Text>After measured text</Text>
|
||||
</Stack>
|
||||
</Page>,
|
||||
);
|
||||
|
||||
const [measured, after] = result.template.schemas[0] ?? [];
|
||||
expect(measured?.height).toBeGreaterThan(0);
|
||||
expect(after?.position.y).toBeCloseTo((measured?.height ?? 0) + 2);
|
||||
});
|
||||
|
||||
it('distributes Row width across flex children', async () => {
|
||||
const result = await renderToTemplate(
|
||||
<Page size={{ width: 100, height: 100 }} margin={0}>
|
||||
@@ -555,6 +572,31 @@ describe('@pdfme/jsx renderToTemplate', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('grows Box background around auto-measured child content', async () => {
|
||||
const result = await renderToTemplate(
|
||||
<Page margin={0}>
|
||||
<Stack>
|
||||
<Box background="#eeeeee" padding={{ x: 3, y: 2 }}>
|
||||
<Text width={24} lineHeight={1.2}>
|
||||
Auto measured text inside a visual Box.
|
||||
</Text>
|
||||
</Box>
|
||||
<Text>After box</Text>
|
||||
</Stack>
|
||||
</Page>,
|
||||
);
|
||||
|
||||
const [box, text, after] = result.template.schemas[0] ?? [];
|
||||
expect(text?.height).toBeGreaterThan(0);
|
||||
expect(box).toMatchObject({
|
||||
type: 'rectangle',
|
||||
color: '#eeeeee',
|
||||
position: { x: 0, y: 0 },
|
||||
height: (text?.height ?? 0) + 4,
|
||||
});
|
||||
expect(after?.position.y).toBeCloseTo(box?.height ?? 0);
|
||||
});
|
||||
|
||||
it('does not render a rectangle schema for a Box without visual styles', async () => {
|
||||
const result = await renderToTemplate(
|
||||
<Page margin={0}>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"y": 0
|
||||
},
|
||||
"width": 174,
|
||||
"height": 7,
|
||||
"height": 3.273984,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -43,7 +43,7 @@
|
||||
"y": 279
|
||||
},
|
||||
"width": 174,
|
||||
"height": 6,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -72,7 +72,7 @@
|
||||
"y": 18
|
||||
},
|
||||
"width": 174,
|
||||
"height": 12,
|
||||
"height": 9.003456,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -91,13 +91,13 @@
|
||||
{
|
||||
"name": "text_4",
|
||||
"type": "text",
|
||||
"content": "Switch the preview to Form and edit the fields directly.",
|
||||
"content": "Open this generated template in Form/Viewer to edit the fields.",
|
||||
"position": {
|
||||
"x": 18,
|
||||
"y": 37
|
||||
"y": 34.003456
|
||||
},
|
||||
"width": 174,
|
||||
"height": 8,
|
||||
"height": 3.683232,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -118,10 +118,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 18,
|
||||
"y": 52
|
||||
"y": 44.686688000000004
|
||||
},
|
||||
"width": 84,
|
||||
"height": 24,
|
||||
"height": 21.864736,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -136,10 +136,10 @@
|
||||
"content": "Customer name",
|
||||
"position": {
|
||||
"x": 22,
|
||||
"y": 56
|
||||
"y": 48.686688000000004
|
||||
},
|
||||
"width": 76,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -161,7 +161,7 @@
|
||||
"content": "Mina Carter",
|
||||
"position": {
|
||||
"x": 22,
|
||||
"y": 63
|
||||
"y": 53.551424000000004
|
||||
},
|
||||
"width": 76,
|
||||
"height": 9,
|
||||
@@ -198,10 +198,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 108,
|
||||
"y": 52
|
||||
"y": 44.686688000000004
|
||||
},
|
||||
"width": 84,
|
||||
"height": 24,
|
||||
"height": 21.864736,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -216,10 +216,10 @@
|
||||
"content": "Email",
|
||||
"position": {
|
||||
"x": 112,
|
||||
"y": 56
|
||||
"y": 48.686688000000004
|
||||
},
|
||||
"width": 76,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -241,7 +241,7 @@
|
||||
"content": "mina@example.com",
|
||||
"position": {
|
||||
"x": 112,
|
||||
"y": 63
|
||||
"y": 53.551424000000004
|
||||
},
|
||||
"width": 76,
|
||||
"height": 9,
|
||||
@@ -278,10 +278,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 18,
|
||||
"y": 83
|
||||
"y": 73.551424
|
||||
},
|
||||
"width": 174,
|
||||
"height": 34.093024,
|
||||
"height": 31.95776,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -296,10 +296,10 @@
|
||||
"content": "Message",
|
||||
"position": {
|
||||
"x": 22,
|
||||
"y": 87
|
||||
"y": 77.551424
|
||||
},
|
||||
"width": 166,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -321,7 +321,7 @@
|
||||
"content": "{\"firstName\":\"Mina\",\"plan\":\"Growth\",\"status\":\"Ready for review\"}",
|
||||
"position": {
|
||||
"x": 22,
|
||||
"y": 94
|
||||
"y": 82.41615999999999
|
||||
},
|
||||
"width": 166,
|
||||
"height": 19.093024,
|
||||
@@ -365,10 +365,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 18,
|
||||
"y": 124.093024
|
||||
"y": 112.509184
|
||||
},
|
||||
"width": 56,
|
||||
"height": 43,
|
||||
"height": 40.864736,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -383,10 +383,10 @@
|
||||
"content": "Logo upload",
|
||||
"position": {
|
||||
"x": 22,
|
||||
"y": 128.093024
|
||||
"y": 116.509184
|
||||
},
|
||||
"width": 48,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -408,7 +408,7 @@
|
||||
"content": "",
|
||||
"position": {
|
||||
"x": 22,
|
||||
"y": 135.093024
|
||||
"y": 121.37392
|
||||
},
|
||||
"width": 48,
|
||||
"height": 28,
|
||||
@@ -421,10 +421,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 80,
|
||||
"y": 124.093024
|
||||
"y": 112.509184
|
||||
},
|
||||
"width": 112,
|
||||
"height": 38,
|
||||
"height": 17.2588832,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -436,13 +436,13 @@
|
||||
{
|
||||
"name": "text_9",
|
||||
"type": "text",
|
||||
"content": "This preset keeps the generated template editable. The Form preview writes changed input values back into the playground state, so Generate PDF uses the latest edits.",
|
||||
"content": "Editable fields usually keep explicit heights for predictable input boxes. Read-only text can usually omit height and let JSX measure it.",
|
||||
"position": {
|
||||
"x": 84,
|
||||
"y": 128.093024
|
||||
"y": 116.509184
|
||||
},
|
||||
"width": 104,
|
||||
"height": 30,
|
||||
"height": 9.2588832,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
"content": "@pdfme/jsx beta",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 3
|
||||
"y": 0
|
||||
},
|
||||
"width": 80,
|
||||
"height": 6,
|
||||
"height": 3.273984,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -37,13 +37,13 @@
|
||||
{
|
||||
"name": "text_2",
|
||||
"type": "text",
|
||||
"content": "Header / Footer / Absolute",
|
||||
"content": "Header and Footer repeat on every page",
|
||||
"position": {
|
||||
"x": 96,
|
||||
"y": 3
|
||||
"y": 0
|
||||
},
|
||||
"width": 80,
|
||||
"height": 6,
|
||||
"height": 3.273984,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -79,10 +79,10 @@
|
||||
"content": "Generated from JSX",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 281.8
|
||||
"y": 279.3
|
||||
},
|
||||
"width": 80,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -104,10 +104,10 @@
|
||||
"content": "Page {currentPage} of {totalPages}",
|
||||
"position": {
|
||||
"x": 96,
|
||||
"y": 281.8
|
||||
"y": 279.3
|
||||
},
|
||||
"width": 54,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -175,10 +175,10 @@
|
||||
"content": "Invoice",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 19
|
||||
"y": 19.659807999999998
|
||||
},
|
||||
"width": 92,
|
||||
"height": 12,
|
||||
"height": 9.821952,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -200,10 +200,10 @@
|
||||
"content": "A compact authoring example using Stack, Row, Table and visual schemas.",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 33
|
||||
"y": 31.48176
|
||||
},
|
||||
"width": 92,
|
||||
"height": 6,
|
||||
"height": 6.8584320000000005,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -241,7 +241,7 @@
|
||||
"y": 47
|
||||
},
|
||||
"width": 82,
|
||||
"height": 30,
|
||||
"height": 26.800336,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -259,7 +259,7 @@
|
||||
"y": 51
|
||||
},
|
||||
"width": 74,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -281,10 +281,10 @@
|
||||
"content": "Kumo Coffee\nAki Tanaka\naki@example.com",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 58
|
||||
"y": 55.864736
|
||||
},
|
||||
"width": 74,
|
||||
"height": 15,
|
||||
"height": 13.9356,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -314,7 +314,7 @@
|
||||
"y": 47
|
||||
},
|
||||
"width": 90,
|
||||
"height": 39,
|
||||
"height": 26.864736,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -332,7 +332,7 @@
|
||||
"y": 51
|
||||
},
|
||||
"width": 82,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -351,13 +351,13 @@
|
||||
{
|
||||
"name": "list_1",
|
||||
"type": "list",
|
||||
"content": "[\"Layout primitives create regular pdfme schemas.\",\"\\tNested rows and boxes stay readable.\",\"Download the generated template JSON.\"]",
|
||||
"content": "[\"Text and MVT can omit height; JSX measures them while rendering.\",\"\\tBoxes without height grow around measured children.\",\"Download the generated template JSON.\"]",
|
||||
"position": {
|
||||
"x": 108,
|
||||
"y": 58
|
||||
"y": 55.864736
|
||||
},
|
||||
"width": 82,
|
||||
"height": 24,
|
||||
"height": 14,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -381,7 +381,7 @@
|
||||
"content": "[[\"Design system setup\",\"1\",\"$800\"],[\"PDF template automation\",\"2\",\"$1,200\"],[\"QA and playground review\",\"1\",\"$350\"]]",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 93
|
||||
"y": 80.864736
|
||||
},
|
||||
"width": 178,
|
||||
"height": 36,
|
||||
@@ -458,10 +458,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 136
|
||||
"y": 123.864736
|
||||
},
|
||||
"width": 130,
|
||||
"height": 28,
|
||||
"height": 12.4198784,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -476,10 +476,10 @@
|
||||
"content": "**Note:** read-only Text can use inline-markdown. Editable Text intentionally cannot.",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 140
|
||||
"y": 127.864736
|
||||
},
|
||||
"width": 122,
|
||||
"height": 20,
|
||||
"height": 4.4198784,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -500,7 +500,7 @@
|
||||
"type": "ellipse",
|
||||
"position": {
|
||||
"x": 152,
|
||||
"y": 136
|
||||
"y": 123.864736
|
||||
},
|
||||
"width": 22,
|
||||
"height": 22,
|
||||
@@ -517,7 +517,7 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 176,
|
||||
"y": 136
|
||||
"y": 123.864736
|
||||
},
|
||||
"width": 18,
|
||||
"height": 22,
|
||||
@@ -540,7 +540,7 @@
|
||||
"y": 18
|
||||
},
|
||||
"width": 178,
|
||||
"height": 10,
|
||||
"height": 7.366464,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -562,10 +562,10 @@
|
||||
"content": "PageBreak creates another schemas array in the generated template. This page shows that JSX is only an authoring layer: the output remains a normal pdfme Template.",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 34
|
||||
"y": 31.366464
|
||||
},
|
||||
"width": 178,
|
||||
"height": 22,
|
||||
"height": 9.2588832,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -587,10 +587,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 62
|
||||
"y": 46.6253472
|
||||
},
|
||||
"width": 178,
|
||||
"height": 34,
|
||||
"height": 14.9723632,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -605,10 +605,10 @@
|
||||
"content": "Try changing numbers, colors, Stack gaps, Row widths, or Table rows. The Viewer updates after a short debounce.",
|
||||
"position": {
|
||||
"x": 21,
|
||||
"y": 67
|
||||
"y": 51.6253472
|
||||
},
|
||||
"width": 168,
|
||||
"height": 24,
|
||||
"height": 4.9723632,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"y": 277
|
||||
},
|
||||
"width": 174,
|
||||
"height": 6,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -47,7 +47,7 @@
|
||||
"y": 20
|
||||
},
|
||||
"width": 174,
|
||||
"height": 12,
|
||||
"height": 9.003456,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -69,10 +69,10 @@
|
||||
"content": "JSX から日本語を含むテンプレートを作成する例です。",
|
||||
"position": {
|
||||
"x": 18,
|
||||
"y": 39
|
||||
"y": 36.003456
|
||||
},
|
||||
"width": 174,
|
||||
"height": 8,
|
||||
"height": 3.683232,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -93,10 +93,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 18,
|
||||
"y": 54
|
||||
"y": 46.686688000000004
|
||||
},
|
||||
"width": 174,
|
||||
"height": 46,
|
||||
"height": 26.165296,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -111,10 +111,10 @@
|
||||
"content": "pdfme の JSX authoring は、通常の pdfme Template と inputs を生成するための薄いレイヤーです。日本語を扱う場合は、Viewer や generator の options.font に NotoSansJP などのフォントを登録してください。",
|
||||
"position": {
|
||||
"x": 23,
|
||||
"y": 59
|
||||
"y": 51.686688000000004
|
||||
},
|
||||
"width": 164,
|
||||
"height": 36,
|
||||
"height": 16.165296,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -137,7 +137,7 @@
|
||||
"content": "[[\"フォント\",\"NotoSansJP\"],[\"出力\",\"Template + inputs\"],[\"プレビュー\",\"Viewer / Form\"]]",
|
||||
"position": {
|
||||
"x": 18,
|
||||
"y": 107
|
||||
"y": 79.851984
|
||||
},
|
||||
"width": 174,
|
||||
"height": 36,
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
"content": "Quarterly product report",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 280.5
|
||||
"y": 279
|
||||
},
|
||||
"width": 80,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -40,10 +40,10 @@
|
||||
"content": "Page {currentPage} of {totalPages}",
|
||||
"position": {
|
||||
"x": 96,
|
||||
"y": 280.5
|
||||
"y": 279
|
||||
},
|
||||
"width": 50,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -72,7 +72,7 @@
|
||||
"y": 18
|
||||
},
|
||||
"width": 110,
|
||||
"height": 10,
|
||||
"height": 8.18496,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -94,10 +94,10 @@
|
||||
"content": "A layout-focused preset for reports and internal briefs.",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 30
|
||||
"y": 28.18496
|
||||
},
|
||||
"width": 110,
|
||||
"height": 7,
|
||||
"height": 3.683232,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -119,10 +119,10 @@
|
||||
"content": "Healthy",
|
||||
"position": {
|
||||
"x": 126,
|
||||
"y": 29
|
||||
"y": 28.594208000000002
|
||||
},
|
||||
"width": 42,
|
||||
"height": 8,
|
||||
"height": 3.273984,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -143,10 +143,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 44
|
||||
"y": 38.868192
|
||||
},
|
||||
"width": 56,
|
||||
"height": 24,
|
||||
"height": 20.2312,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -161,10 +161,10 @@
|
||||
"content": "Activation",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 48
|
||||
"y": 42.868192
|
||||
},
|
||||
"width": 48,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -186,10 +186,10 @@
|
||||
"content": "74%",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 55
|
||||
"y": 47.732928
|
||||
},
|
||||
"width": 48,
|
||||
"height": 9,
|
||||
"height": 7.366464,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -210,10 +210,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 77,
|
||||
"y": 44
|
||||
"y": 38.868192
|
||||
},
|
||||
"width": 56,
|
||||
"height": 24,
|
||||
"height": 20.2312,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -228,10 +228,10 @@
|
||||
"content": "Retention",
|
||||
"position": {
|
||||
"x": 81,
|
||||
"y": 48
|
||||
"y": 42.868192
|
||||
},
|
||||
"width": 48,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -253,10 +253,10 @@
|
||||
"content": "61%",
|
||||
"position": {
|
||||
"x": 81,
|
||||
"y": 55
|
||||
"y": 47.732928
|
||||
},
|
||||
"width": 48,
|
||||
"height": 9,
|
||||
"height": 7.366464,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -277,10 +277,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 138,
|
||||
"y": 44
|
||||
"y": 38.868192
|
||||
},
|
||||
"width": 56,
|
||||
"height": 24,
|
||||
"height": 20.2312,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -295,10 +295,10 @@
|
||||
"content": "Expansion",
|
||||
"position": {
|
||||
"x": 142,
|
||||
"y": 48
|
||||
"y": 42.868192
|
||||
},
|
||||
"width": 48,
|
||||
"height": 5,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -320,10 +320,10 @@
|
||||
"content": "28%",
|
||||
"position": {
|
||||
"x": 142,
|
||||
"y": 55
|
||||
"y": 47.732928
|
||||
},
|
||||
"width": 48,
|
||||
"height": 9,
|
||||
"height": 7.366464,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -344,10 +344,10 @@
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 16,
|
||||
"y": 75
|
||||
"y": 66.099392
|
||||
},
|
||||
"width": 178,
|
||||
"height": 77,
|
||||
"height": 46.1698592,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -362,10 +362,10 @@
|
||||
"content": "Notes",
|
||||
"position": {
|
||||
"x": 21,
|
||||
"y": 80
|
||||
"y": 71.099392
|
||||
},
|
||||
"width": 168,
|
||||
"height": 7,
|
||||
"height": 4.910976,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -387,10 +387,10 @@
|
||||
"content": "The JSX authoring layer is useful when a document has repeated visual patterns but still needs to become a normal pdfme template. This example uses boxes, rows, static footer content, and simple visual bars.",
|
||||
"position": {
|
||||
"x": 21,
|
||||
"y": 91
|
||||
"y": 80.010368
|
||||
},
|
||||
"width": 168,
|
||||
"height": 28,
|
||||
"height": 9.2588832,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
@@ -410,13 +410,13 @@
|
||||
{
|
||||
"name": "list_1",
|
||||
"type": "list",
|
||||
"content": "[\"Use Row and Stack for predictable layout.\",\"Use Box for padding, borders, and backgrounds.\",\"Use Static or Footer for repeated page content.\"]",
|
||||
"content": "[\"Use Row and Stack for predictable layout.\",\"Use Box for padding, borders, and backgrounds.\",\"Use Footer for repeated page content.\"]",
|
||||
"position": {
|
||||
"x": 21,
|
||||
"y": 123
|
||||
"y": 93.26925119999999
|
||||
},
|
||||
"width": 168,
|
||||
"height": 24,
|
||||
"height": 14,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
{
|
||||
"basePdf": {
|
||||
"width": 210,
|
||||
"height": 297,
|
||||
"padding": [
|
||||
18,
|
||||
20,
|
||||
18,
|
||||
20
|
||||
],
|
||||
"staticSchema": [
|
||||
{
|
||||
"name": "text_1",
|
||||
"type": "text",
|
||||
"content": "Research brief — {currentPage} / {totalPages}",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 279
|
||||
},
|
||||
"width": 170,
|
||||
"height": 2.8647359999999997,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"alignment": "right",
|
||||
"verticalAlignment": "top",
|
||||
"fontSize": 7,
|
||||
"fontName": "NotoSansJP",
|
||||
"lineHeight": 1,
|
||||
"characterSpacing": 0,
|
||||
"fontColor": "#64748b",
|
||||
"backgroundColor": "",
|
||||
"textFormat": "plain",
|
||||
"strikethrough": false,
|
||||
"underline": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"schemas": [
|
||||
[
|
||||
{
|
||||
"name": "text_2",
|
||||
"type": "text",
|
||||
"content": "Practical Notes on JSX Authoring for PDF Templates",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 18
|
||||
},
|
||||
"width": 170,
|
||||
"height": 9.044380799999999,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"alignment": "center",
|
||||
"verticalAlignment": "top",
|
||||
"fontSize": 17,
|
||||
"fontName": "NotoSansJP",
|
||||
"lineHeight": 1.3,
|
||||
"characterSpacing": 0,
|
||||
"fontColor": "#111827",
|
||||
"backgroundColor": "",
|
||||
"textFormat": "plain",
|
||||
"strikethrough": false,
|
||||
"underline": false
|
||||
},
|
||||
{
|
||||
"name": "box_1",
|
||||
"type": "rectangle",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 32.0443808
|
||||
},
|
||||
"width": 170,
|
||||
"height": 23.050448000000003,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"color": "#f3f4f6",
|
||||
"borderColor": "#9ca3af",
|
||||
"borderWidth": 0.2,
|
||||
"radius": 0
|
||||
},
|
||||
{
|
||||
"name": "text_3",
|
||||
"type": "text",
|
||||
"content": "Abstract — This preset mirrors a paper-style document and intentionally omits height on most Text nodes. JSX measures each block while rendering, and the parent Box grows around the abstract without manual geometry.",
|
||||
"position": {
|
||||
"x": 24,
|
||||
"y": 36.0443808
|
||||
},
|
||||
"width": 162,
|
||||
"height": 15.050448000000001,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"alignment": "left",
|
||||
"verticalAlignment": "top",
|
||||
"fontSize": 9,
|
||||
"fontName": "NotoSansJP",
|
||||
"lineHeight": 1.5,
|
||||
"characterSpacing": 0,
|
||||
"fontColor": "#374151",
|
||||
"backgroundColor": "",
|
||||
"textFormat": "plain",
|
||||
"strikethrough": false,
|
||||
"underline": false
|
||||
},
|
||||
{
|
||||
"name": "text_4",
|
||||
"type": "text",
|
||||
"content": "1. Introduction",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 60.0948288
|
||||
},
|
||||
"width": 170,
|
||||
"height": 5.8522464,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"alignment": "left",
|
||||
"verticalAlignment": "top",
|
||||
"fontSize": 11,
|
||||
"fontName": "NotoSansJP",
|
||||
"lineHeight": 1.3,
|
||||
"characterSpacing": 0,
|
||||
"fontColor": "#111827",
|
||||
"backgroundColor": "",
|
||||
"textFormat": "plain",
|
||||
"strikethrough": false,
|
||||
"underline": false
|
||||
},
|
||||
{
|
||||
"name": "text_5",
|
||||
"type": "text",
|
||||
"content": "Since structured templates are easier to review than absolute-position JSON, JSX can act as a readable authoring surface for humans and AI. The generated output is still an ordinary pdfme Template, so it can be opened in Designer after generation.",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 67.4470752
|
||||
},
|
||||
"width": 170,
|
||||
"height": 15.050448000000001,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"alignment": "left",
|
||||
"verticalAlignment": "top",
|
||||
"fontSize": 9,
|
||||
"fontName": "NotoSansJP",
|
||||
"lineHeight": 1.5,
|
||||
"characterSpacing": 0,
|
||||
"fontColor": "#374151",
|
||||
"backgroundColor": "",
|
||||
"textFormat": "plain",
|
||||
"strikethrough": false,
|
||||
"underline": false
|
||||
},
|
||||
{
|
||||
"name": "text_6",
|
||||
"type": "text",
|
||||
"content": "2. Layout model",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 87.4975232
|
||||
},
|
||||
"width": 170,
|
||||
"height": 5.8522464,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"alignment": "left",
|
||||
"verticalAlignment": "top",
|
||||
"fontSize": 11,
|
||||
"fontName": "NotoSansJP",
|
||||
"lineHeight": 1.3,
|
||||
"characterSpacing": 0,
|
||||
"fontColor": "#111827",
|
||||
"backgroundColor": "",
|
||||
"textFormat": "plain",
|
||||
"strikethrough": false,
|
||||
"underline": false
|
||||
},
|
||||
{
|
||||
"name": "text_7",
|
||||
"type": "text",
|
||||
"content": "Text and MultiVariableText can omit height. During JSX rendering, pdfme measures their content and advances the surrounding Stack or Box. Use explicit height only when you want a fixed field or a fixed visual area.",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 94.8497696
|
||||
},
|
||||
"width": 170,
|
||||
"height": 10.287648,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"alignment": "left",
|
||||
"verticalAlignment": "top",
|
||||
"fontSize": 9,
|
||||
"fontName": "NotoSansJP",
|
||||
"lineHeight": 1.5,
|
||||
"characterSpacing": 0,
|
||||
"fontColor": "#374151",
|
||||
"backgroundColor": "",
|
||||
"textFormat": "plain",
|
||||
"strikethrough": false,
|
||||
"underline": false
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "text_8",
|
||||
"type": "text",
|
||||
"content": "Appendix",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 18
|
||||
},
|
||||
"width": 170,
|
||||
"height": 5.320224,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"alignment": "left",
|
||||
"verticalAlignment": "top",
|
||||
"fontSize": 13,
|
||||
"fontName": "NotoSansJP",
|
||||
"lineHeight": 1,
|
||||
"characterSpacing": 0,
|
||||
"fontColor": "#111827",
|
||||
"backgroundColor": "",
|
||||
"textFormat": "plain",
|
||||
"strikethrough": false,
|
||||
"underline": false
|
||||
},
|
||||
{
|
||||
"name": "text_9",
|
||||
"type": "text",
|
||||
"content": "Designer edits are applied to the generated Template. The JSX source remains a seed for regeneration, not a lossless representation of later Designer changes.",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 28.320224
|
||||
},
|
||||
"width": 170,
|
||||
"height": 10.287648,
|
||||
"rotate": 0,
|
||||
"opacity": 1,
|
||||
"readOnly": true,
|
||||
"alignment": "left",
|
||||
"verticalAlignment": "top",
|
||||
"fontSize": 9,
|
||||
"fontName": "NotoSansJP",
|
||||
"lineHeight": 1.5,
|
||||
"characterSpacing": 0,
|
||||
"fontColor": "#374151",
|
||||
"backgroundColor": "",
|
||||
"textFormat": "plain",
|
||||
"strikethrough": false,
|
||||
"underline": false
|
||||
}
|
||||
]
|
||||
],
|
||||
"author": "pdfme"
|
||||
}
|
||||
@@ -737,6 +737,31 @@
|
||||
],
|
||||
"title": "Report page"
|
||||
},
|
||||
{
|
||||
"name": "jsx-research-paper",
|
||||
"author": "pdfme",
|
||||
"path": "jsx-research-paper/template.json",
|
||||
"thumbnailPath": "jsx-research-paper/thumbnail.png",
|
||||
"pageCount": 2,
|
||||
"fieldCount": 9,
|
||||
"schemaTypes": [
|
||||
"rectangle",
|
||||
"text"
|
||||
],
|
||||
"fontNames": [
|
||||
"NotoSansJP"
|
||||
],
|
||||
"hasCJK": false,
|
||||
"basePdfKind": "blank",
|
||||
"description": "A paper-style document that leans on measured text height and PageBreak.",
|
||||
"sourceKind": "jsx",
|
||||
"tags": [
|
||||
"Research",
|
||||
"Article",
|
||||
"Multi-page"
|
||||
],
|
||||
"title": "Research paper"
|
||||
},
|
||||
{
|
||||
"name": "z-fold-brochure",
|
||||
"author": "hitomi-t260g",
|
||||
|
||||
@@ -737,6 +737,31 @@
|
||||
],
|
||||
"title": "Report page"
|
||||
},
|
||||
{
|
||||
"name": "jsx-research-paper",
|
||||
"author": "pdfme",
|
||||
"path": "jsx-research-paper/template.json",
|
||||
"thumbnailPath": "jsx-research-paper/thumbnail.png",
|
||||
"pageCount": 2,
|
||||
"fieldCount": 9,
|
||||
"schemaTypes": [
|
||||
"rectangle",
|
||||
"text"
|
||||
],
|
||||
"fontNames": [
|
||||
"NotoSansJP"
|
||||
],
|
||||
"hasCJK": false,
|
||||
"basePdfKind": "blank",
|
||||
"description": "A paper-style document that leans on measured text height and PageBreak.",
|
||||
"sourceKind": "jsx",
|
||||
"tags": [
|
||||
"Research",
|
||||
"Article",
|
||||
"Multi-page"
|
||||
],
|
||||
"title": "Research paper"
|
||||
},
|
||||
{
|
||||
"name": "z-fold-brochure",
|
||||
"author": "hitomi-t260g",
|
||||
|
||||
@@ -84,6 +84,12 @@
|
||||
"sourceKind": "jsx",
|
||||
"tags": ["CJK", "Table"]
|
||||
},
|
||||
"jsx-research-paper": {
|
||||
"title": "Research paper",
|
||||
"description": "A paper-style document that leans on measured text height and PageBreak.",
|
||||
"sourceKind": "jsx",
|
||||
"tags": ["Research", "Article", "Multi-page"]
|
||||
},
|
||||
"jsx-report": {
|
||||
"title": "Report page",
|
||||
"description": "A dashboard-style report with cards, progress bars, list content, and page footer.",
|
||||
|
||||
@@ -13,23 +13,23 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
source: `return (
|
||||
<Document size="A4" margin={{ x: 16, y: 18 }} font="NotoSansJP">
|
||||
<Header>
|
||||
<Row height={12} alignItems="center" justifyContent="space-between">
|
||||
<Text width={80} height={6} size={8} color="#64748b">
|
||||
<Row alignItems="center" justifyContent="space-between">
|
||||
<Text width={80} size={8} color="#64748b">
|
||||
@pdfme/jsx beta
|
||||
</Text>
|
||||
<Text width={80} height={6} size={8} align="right" color="#64748b">
|
||||
Header / Footer / Absolute
|
||||
<Text width={80} size={8} align="right" color="#64748b">
|
||||
Header and Footer repeat on every page
|
||||
</Text>
|
||||
</Row>
|
||||
</Header>
|
||||
|
||||
<Footer>
|
||||
<Line height={0.3} color="#cbd5e1" />
|
||||
<Row height={10} alignItems="center" justifyContent="space-between">
|
||||
<Text width={80} height={5} size={7} color="#64748b">
|
||||
<Row alignItems="center" justifyContent="space-between">
|
||||
<Text width={80} size={7} color="#64748b">
|
||||
Generated from JSX
|
||||
</Text>
|
||||
<Text width={54} height={5} size={7} align="right" color="#64748b">
|
||||
<Text width={54} size={7} align="right" color="#64748b">
|
||||
{'Page {currentPage} of {totalPages}'}
|
||||
</Text>
|
||||
</Row>
|
||||
@@ -46,10 +46,10 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
<Stack gap={7}>
|
||||
<Row alignItems="center" justifyContent="space-between">
|
||||
<Stack width={92} gap={2}>
|
||||
<Text height={12} size={24} color="#0f172a">
|
||||
<Text size={24} color="#0f172a">
|
||||
Invoice
|
||||
</Text>
|
||||
<Text height={6} size={9} color="#475569">
|
||||
<Text size={9} color="#475569">
|
||||
A compact authoring example using Stack, Row, Table and visual schemas.
|
||||
</Text>
|
||||
</Stack>
|
||||
@@ -61,11 +61,10 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
<Row gap={6}>
|
||||
<Box width={82} padding={4} borderColor="#e2e8f0" borderWidth={0.4} background="#f8fafc">
|
||||
<Stack gap={2}>
|
||||
<Text height={5} size={7} color="#64748b">
|
||||
<Text size={7} color="#64748b">
|
||||
Bill to
|
||||
</Text>
|
||||
<MultiVariableText
|
||||
height={15}
|
||||
size={10}
|
||||
lineHeight={1.25}
|
||||
text={'{company}\\n{name}\\n{email}'}
|
||||
@@ -79,15 +78,14 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
</Box>
|
||||
<Box flex={1} padding={4} borderColor="#e2e8f0" borderWidth={0.4}>
|
||||
<Stack gap={2}>
|
||||
<Text height={5} size={7} color="#64748b">
|
||||
<Text size={7} color="#64748b">
|
||||
Summary
|
||||
</Text>
|
||||
<List
|
||||
height={24}
|
||||
size={8}
|
||||
items={[
|
||||
'Layout primitives create regular pdfme schemas.',
|
||||
{ text: 'Nested rows and boxes stay readable.', level: 1 },
|
||||
'Text and MVT can omit height; JSX measures them while rendering.',
|
||||
{ text: 'Boxes without height grow around measured children.', level: 1 },
|
||||
'Download the generated template JSON.',
|
||||
]}
|
||||
/>
|
||||
@@ -113,7 +111,7 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
|
||||
<Row gap={6}>
|
||||
<Box flex={1} padding={4} background="#fefce8" borderColor="#facc15" borderWidth={0.4}>
|
||||
<Text height={20} size={8} lineHeight={1.35} textFormat="inline-markdown">
|
||||
<Text size={8} lineHeight={1.35} textFormat="inline-markdown">
|
||||
**Note:** read-only Text can use inline-markdown. Editable Text intentionally cannot.
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -129,14 +127,14 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
|
||||
<Page>
|
||||
<Stack gap={6}>
|
||||
<Text height={10} size={18} color="#0f172a">
|
||||
<Text size={18} color="#0f172a">
|
||||
Second page
|
||||
</Text>
|
||||
<Text height={22} size={9} lineHeight={1.35} overflow="expand">
|
||||
<Text size={9} lineHeight={1.35} overflow="expand">
|
||||
PageBreak creates another schemas array in the generated template. This page shows that JSX is only an authoring layer: the output remains a normal pdfme Template.
|
||||
</Text>
|
||||
<Box padding={5} borderColor="#cbd5e1" borderWidth={0.4} background="#f8fafc">
|
||||
<Text height={24} size={9} lineHeight={1.35}>
|
||||
<Text size={9} lineHeight={1.35}>
|
||||
Try changing numbers, colors, Stack gaps, Row widths, or Table rows. The Viewer updates after a short debounce.
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -152,30 +150,30 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
source: `return (
|
||||
<Document size="A4" margin={{ x: 18, y: 18 }} font="NotoSansJP">
|
||||
<Header>
|
||||
<Text height={7} size={8} color="#64748b">
|
||||
<Text size={8} color="#64748b">
|
||||
Editable fields example
|
||||
</Text>
|
||||
</Header>
|
||||
|
||||
<Footer>
|
||||
<Text height={6} size={7} align="right" color="#64748b">
|
||||
<Text size={7} align="right" color="#64748b">
|
||||
{'Page {currentPage} of {totalPages}'}
|
||||
</Text>
|
||||
</Footer>
|
||||
|
||||
<Page>
|
||||
<Stack gap={7}>
|
||||
<Text height={12} size={22} color="#111827">
|
||||
<Text size={22} color="#111827">
|
||||
Customer Intake Form
|
||||
</Text>
|
||||
<Text height={8} size={9} color="#6b7280">
|
||||
Switch the preview to Form and edit the fields directly.
|
||||
<Text size={9} color="#6b7280">
|
||||
Open this generated template in Form/Viewer to edit the fields.
|
||||
</Text>
|
||||
|
||||
<Row gap={6}>
|
||||
<Box flex={1} padding={4} borderColor="#d1d5db" borderWidth={0.4}>
|
||||
<Stack gap={2}>
|
||||
<Text height={5} size={7} color="#6b7280">
|
||||
<Text size={7} color="#6b7280">
|
||||
Customer name
|
||||
</Text>
|
||||
<Text name="customerName" height={9} size={11} padding={1.5} borderColor="#cbd5e1" borderWidth={0.3}>
|
||||
@@ -185,7 +183,7 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
</Box>
|
||||
<Box flex={1} padding={4} borderColor="#d1d5db" borderWidth={0.4}>
|
||||
<Stack gap={2}>
|
||||
<Text height={5} size={7} color="#6b7280">
|
||||
<Text size={7} color="#6b7280">
|
||||
Email
|
||||
</Text>
|
||||
<Text name="email" height={9} size={11} padding={1.5} borderColor="#cbd5e1" borderWidth={0.3}>
|
||||
@@ -197,7 +195,7 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
|
||||
<Box padding={4} background="#f8fafc" borderColor="#cbd5e1" borderWidth={0.4}>
|
||||
<Stack gap={2}>
|
||||
<Text height={5} size={7} color="#64748b">
|
||||
<Text size={7} color="#64748b">
|
||||
Message
|
||||
</Text>
|
||||
<MultiVariableText
|
||||
@@ -217,15 +215,15 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
<Row gap={6}>
|
||||
<Box width={56} padding={4} borderColor="#d1d5db" borderWidth={0.4}>
|
||||
<Stack gap={2}>
|
||||
<Text height={5} size={7} color="#6b7280">
|
||||
<Text size={7} color="#6b7280">
|
||||
Logo upload
|
||||
</Text>
|
||||
<Image name="logo" width={48} height={28} />
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box flex={1} padding={4} background="#ecfeff" borderColor="#06b6d4" borderWidth={0.4}>
|
||||
<Text height={30} size={9} lineHeight={1.35}>
|
||||
This preset keeps the generated template editable. The Form preview writes changed input values back into the playground state, so Generate PDF uses the latest edits.
|
||||
<Text size={9} lineHeight={1.35}>
|
||||
Editable fields usually keep explicit heights for predictable input boxes. Read-only text can usually omit height and let JSX measure it.
|
||||
</Text>
|
||||
</Box>
|
||||
</Row>
|
||||
@@ -242,11 +240,11 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
source: `return (
|
||||
<Document size="A4" margin={{ x: 16, y: 18 }} font="NotoSansJP">
|
||||
<Footer>
|
||||
<Row height={8} justifyContent="space-between" alignItems="center">
|
||||
<Text width={80} height={5} size={7} color="#64748b">
|
||||
<Row justifyContent="space-between" alignItems="center">
|
||||
<Text width={80} size={7} color="#64748b">
|
||||
Quarterly product report
|
||||
</Text>
|
||||
<Text width={50} height={5} size={7} align="right" color="#64748b">
|
||||
<Text width={50} size={7} align="right" color="#64748b">
|
||||
{'Page {currentPage} of {totalPages}'}
|
||||
</Text>
|
||||
</Row>
|
||||
@@ -256,14 +254,14 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
<Stack gap={7}>
|
||||
<Row justifyContent="space-between" alignItems="end">
|
||||
<Stack width={110} gap={2}>
|
||||
<Text height={10} size={20} color="#0f172a">
|
||||
<Text size={20} color="#0f172a">
|
||||
Product Health Report
|
||||
</Text>
|
||||
<Text height={7} size={9} color="#64748b">
|
||||
<Text size={9} color="#64748b">
|
||||
A layout-focused preset for reports and internal briefs.
|
||||
</Text>
|
||||
</Stack>
|
||||
<Text width={42} height={8} size={8} align="right" color="#16a34a">
|
||||
<Text width={42} size={8} align="right" color="#16a34a">
|
||||
Healthy
|
||||
</Text>
|
||||
</Row>
|
||||
@@ -276,8 +274,8 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
].map(([label, value, background]) => (
|
||||
<Box flex={1} padding={4} background={background} borderColor="#d1d5db" borderWidth={0.3}>
|
||||
<Stack gap={2}>
|
||||
<Text height={5} size={7} color="#64748b">{label}</Text>
|
||||
<Text height={9} size={18} color="#0f172a">{value}</Text>
|
||||
<Text size={7} color="#64748b">{label}</Text>
|
||||
<Text size={18} color="#0f172a">{value}</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
))}
|
||||
@@ -285,19 +283,18 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
|
||||
<Box padding={5} borderColor="#cbd5e1" borderWidth={0.4}>
|
||||
<Stack gap={4}>
|
||||
<Text height={7} size={12} color="#0f172a">
|
||||
<Text size={12} color="#0f172a">
|
||||
Notes
|
||||
</Text>
|
||||
<Text height={28} size={9} lineHeight={1.35} overflow="expand">
|
||||
<Text size={9} lineHeight={1.35} overflow="expand">
|
||||
The JSX authoring layer is useful when a document has repeated visual patterns but still needs to become a normal pdfme template. This example uses boxes, rows, static footer content, and simple visual bars.
|
||||
</Text>
|
||||
<List
|
||||
height={24}
|
||||
size={8}
|
||||
items={[
|
||||
'Use Row and Stack for predictable layout.',
|
||||
'Use Box for padding, borders, and backgrounds.',
|
||||
'Use Static or Footer for repeated page content.',
|
||||
'Use Footer for repeated page content.',
|
||||
]}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -314,22 +311,22 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
source: `return (
|
||||
<Document size="A4" margin={{ x: 18, y: 20 }} font="NotoSansJP">
|
||||
<Footer>
|
||||
<Text height={6} size={7} align="right" color="#64748b">
|
||||
<Text size={7} align="right" color="#64748b">
|
||||
{'{currentPage} / {totalPages} ページ'}
|
||||
</Text>
|
||||
</Footer>
|
||||
|
||||
<Page>
|
||||
<Stack gap={7}>
|
||||
<Text height={12} size={22} color="#0f172a">
|
||||
<Text size={22} color="#0f172a">
|
||||
お知らせ
|
||||
</Text>
|
||||
<Text height={8} size={9} color="#64748b">
|
||||
<Text size={9} color="#64748b">
|
||||
JSX から日本語を含むテンプレートを作成する例です。
|
||||
</Text>
|
||||
|
||||
<Box padding={5} background="#f8fafc" borderColor="#cbd5e1" borderWidth={0.4}>
|
||||
<Text height={36} size={10} lineHeight={1.45} overflow="expand">
|
||||
<Text size={10} lineHeight={1.45} overflow="expand">
|
||||
pdfme の JSX authoring は、通常の pdfme Template と inputs を生成するための薄いレイヤーです。日本語を扱う場合は、Viewer や generator の options.font に NotoSansJP などのフォントを登録してください。
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -352,6 +349,68 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
|
||||
</Stack>
|
||||
</Page>
|
||||
</Document>
|
||||
);`,
|
||||
},
|
||||
{
|
||||
id: 'research-paper',
|
||||
label: 'Research paper',
|
||||
description: 'A paper-style document that leans on measured text height and PageBreak.',
|
||||
source: `const sections = [
|
||||
{
|
||||
title: '1. Introduction',
|
||||
body: 'Since structured templates are easier to review than absolute-position JSON, JSX can act as a readable authoring surface for humans and AI. The generated output is still an ordinary pdfme Template, so it can be opened in Designer after generation.',
|
||||
},
|
||||
{
|
||||
title: '2. Layout model',
|
||||
body: 'Text and MultiVariableText can omit height. During JSX rendering, pdfme measures their content and advances the surrounding Stack or Box. Use explicit height only when you want a fixed field or a fixed visual area.',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Document size="A4" margin={{ x: 20, y: 18 }} font="NotoSansJP">
|
||||
<Footer>
|
||||
<Text size={7} align="right" color="#64748b">
|
||||
{'Research brief — {currentPage} / {totalPages}'}
|
||||
</Text>
|
||||
</Footer>
|
||||
|
||||
<Page>
|
||||
<Stack gap={5}>
|
||||
<Text size={17} align="center" lineHeight={1.3} color="#111827">
|
||||
Practical Notes on JSX Authoring for PDF Templates
|
||||
</Text>
|
||||
|
||||
<Box background="#f3f4f6" borderColor="#9ca3af" borderWidth={0.2} padding={4}>
|
||||
<Text size={9} lineHeight={1.5} color="#374151">
|
||||
Abstract — This preset mirrors a paper-style document and intentionally omits height on
|
||||
most Text nodes. JSX measures each block while rendering, and the parent Box grows around
|
||||
the abstract without manual geometry.
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
{sections.map((section) => (
|
||||
<Stack gap={1.5}>
|
||||
<Text size={11} lineHeight={1.3} color="#111827">
|
||||
{section.title}
|
||||
</Text>
|
||||
<Text size={9} lineHeight={1.5} color="#374151">
|
||||
{section.body}
|
||||
</Text>
|
||||
</Stack>
|
||||
))}
|
||||
|
||||
<PageBreak />
|
||||
|
||||
<Text size={13} color="#111827">
|
||||
Appendix
|
||||
</Text>
|
||||
<Text size={9} lineHeight={1.5} color="#374151">
|
||||
Designer edits are applied to the generated Template. The JSX source remains a seed for
|
||||
regeneration, not a lossless representation of later Designer changes.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Page>
|
||||
</Document>
|
||||
);`,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -37,10 +37,10 @@ const { template, inputs } = await renderToTemplate(
|
||||
<Document size="A4" margin={{ x: 16, y: 18 }}>
|
||||
<Page>
|
||||
<Stack gap={6}>
|
||||
<Text height={12} size={24}>
|
||||
<Text size={24}>
|
||||
Invoice
|
||||
</Text>
|
||||
<Text height={8} color="#64748b">
|
||||
<Text color="#64748b">
|
||||
Generated from JSX, rendered by pdfme.
|
||||
</Text>
|
||||
<Table
|
||||
@@ -81,6 +81,11 @@ const pdf = await generate({
|
||||
The layout API intentionally borrows useful ideas from Flexbox without trying to be CSS-compatible.
|
||||
Use `gap`, `margin`, `alignItems`, `justifyContent`, and `flex` / `flexGrow` for compact templates.
|
||||
|
||||
`Text`, `MultiVariableText`, `List`, and `Table` can usually omit `height`. JSX measures their initial
|
||||
content while rendering and advances the surrounding `Stack`, `Row`, or `Box`. Use explicit `height`
|
||||
when you want a fixed field, a fixed visual area, or predictable form input box. A `Box` without
|
||||
`height` grows around its children during JSX rendering.
|
||||
|
||||
## Schema Components
|
||||
|
||||
`@pdfme/jsx` currently includes the main static and form-oriented schemas:
|
||||
@@ -98,6 +103,10 @@ Use `gap`, `margin`, `alignItems`, `justifyContent`, and `flex` / `flexGrow` for
|
||||
If a component has a `name`, it becomes input-backed by default. If it has no `name`, it is rendered as
|
||||
read-only content. This mirrors the pdfme template data model.
|
||||
|
||||
Because the output is a normal pdfme template, Designer edits apply to the generated `Template`. JSX is
|
||||
the seed authoring surface; it is not a lossless source format after the template has been edited in
|
||||
Designer.
|
||||
|
||||
`Table` uses `columnWeights` for relative column sizing. The values are normalized to pdfme
|
||||
`headWidthPercentages`, so `columnWeights={[30, 70]}` means a 30/70 split, not `30mm` / `70mm`.
|
||||
Missing or invalid weights default to `1`, so pass one weight per column when the exact ratio
|
||||
@@ -125,15 +134,15 @@ For repeated content, prefer `Document` with document-level `Header` / `Footer`:
|
||||
return (
|
||||
<Document size="A4" margin={{ x: 16, y: 18 }}>
|
||||
<Header>
|
||||
<Text height={6}>Report</Text>
|
||||
<Text>Report</Text>
|
||||
</Header>
|
||||
<Footer>
|
||||
<Text height={6} align="right">
|
||||
<Text align="right">
|
||||
{'Page {currentPage} of {totalPages}'}
|
||||
</Text>
|
||||
</Footer>
|
||||
<Page>
|
||||
<Text height={12}>Body</Text>
|
||||
<Text>Body</Text>
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
@@ -142,6 +151,10 @@ return (
|
||||
To migrate from the earlier beta static API, move `Header` / `Footer` / `Static` out of `Page` and
|
||||
place them directly inside `Document`.
|
||||
|
||||
`Header` and `Footer` are margin-aware. Their coordinate origin is the page margin area, so they are
|
||||
usually the right choice for titles, page numbers, and repeated document chrome. `Static` uses the full
|
||||
page coordinate system and is intended for advanced overlays such as watermarks, crop marks, or stamps.
|
||||
|
||||
## Current Limitations
|
||||
|
||||
- `@pdfme/jsx` is still beta. Component names and layout details may still be refined.
|
||||
@@ -153,3 +166,5 @@ place them directly inside `Document`.
|
||||
fonts, and generator/viewer options.
|
||||
- `Header`, `Footer`, and `Static` are supported only as direct children of `Document`. They generate
|
||||
blank `basePdf.staticSchema` and cannot be used with a custom PDF `basePdf`.
|
||||
- If a form input later expands at runtime, a parent `Box` rectangle is not dynamically resized yet.
|
||||
Dynamic parent/child container reflow is a future layout feature.
|
||||
|
||||
@@ -37,10 +37,10 @@ const { template, inputs } = await renderToTemplate(
|
||||
<Document size="A4" margin={{ x: 16, y: 18 }}>
|
||||
<Page>
|
||||
<Stack gap={6}>
|
||||
<Text height={12} size={24}>
|
||||
<Text size={24}>
|
||||
Invoice
|
||||
</Text>
|
||||
<Text height={8} color="#64748b">
|
||||
<Text color="#64748b">
|
||||
Generated from JSX, rendered by pdfme.
|
||||
</Text>
|
||||
<Table
|
||||
@@ -80,6 +80,11 @@ const pdf = await generate({
|
||||
layout API は CSS/Flexbox 互換を目指していません。`gap`, `margin`, `alignItems`,
|
||||
`justifyContent`, `flex` / `flexGrow` など、template authoring に必要な部分だけを小さく取り込んでいます。
|
||||
|
||||
`Text`, `MultiVariableText`, `List`, `Table` は通常 `height` を省略できます。JSX render 時に初期 content
|
||||
を測定し、周囲の `Stack`, `Row`, `Box` を進めます。固定 field、固定 visual area、Form の入力 box を
|
||||
明確にしたい場合だけ `height` を指定してください。`height` のない `Box` は、JSX render 時点では子要素の高さに
|
||||
合わせて伸びます。
|
||||
|
||||
## schema component
|
||||
|
||||
`@pdfme/jsx` には、主要な static / form 向け schema component が含まれています。
|
||||
@@ -97,6 +102,10 @@ layout API は CSS/Flexbox 互換を目指していません。`gap`, `margin`,
|
||||
component に `name` がある場合は input-backed schema になり、`name` がない場合は read-only content として
|
||||
描画されます。これは pdfme template のデータモデルに合わせた挙動です。
|
||||
|
||||
出力は通常の pdfme template なので、Designer で編集した内容は generated `Template` に反映されます。JSX は
|
||||
初期生成 / 再生成のための authoring surface であり、Designer 編集後の template を lossless に JSX へ戻すものでは
|
||||
ありません。
|
||||
|
||||
`Table` の `columnWeights` は相対的な列幅の重みです。値は pdfme の `headWidthPercentages` に正規化されるため、
|
||||
`columnWeights={[30, 70]}` は `30mm` / `70mm` ではなく 30/70 の比率を意味します。不足している weight や不正な
|
||||
weight は `1` として扱われるため、正確な比率が必要な場合は列数分の weight を指定してください。以前の beta build
|
||||
@@ -127,20 +136,24 @@ API が beta の間は、ブラウザ上の sandbox と module 解決を小さ
|
||||
return (
|
||||
<Document size="A4" margin={{ x: 16, y: 18 }}>
|
||||
<Header>
|
||||
<Text height={6}>Report</Text>
|
||||
<Text>Report</Text>
|
||||
</Header>
|
||||
<Footer>
|
||||
<Text height={6} align="right">
|
||||
<Text align="right">
|
||||
{'Page {currentPage} of {totalPages}'}
|
||||
</Text>
|
||||
</Footer>
|
||||
<Page>
|
||||
<Text height={12}>Body</Text>
|
||||
<Text>Body</Text>
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
```
|
||||
|
||||
`Header` / `Footer` は margin-aware です。座標の基準は page margin 領域なので、title、page number、
|
||||
繰り返し表示する document chrome に向いています。`Static` は page 全体座標を使う低レベル API で、
|
||||
watermark、crop mark、stamp などの advanced overlay 向けです。
|
||||
|
||||
## 現在の制限
|
||||
|
||||
- `@pdfme/jsx` は beta です。component 名や layout の細部は今後調整される可能性があります。
|
||||
@@ -150,3 +163,5 @@ return (
|
||||
- 出力は `Template + inputs` です。実際の描画は通常通り pdfme plugins, fonts, generator/viewer options に依存します。
|
||||
- `Header`, `Footer`, `Static` は `Document` の direct child としてのみ使えます。これらは blank
|
||||
`basePdf.staticSchema` を生成するため、custom PDF `basePdf` とは併用できません。
|
||||
- Form 入力によって runtime に text / MVT が expand した場合、親 `Box` の rectangle はまだ自動では
|
||||
再計算されません。親子 container の dynamic reflow は今後の layout 機能として扱います。
|
||||
|
||||