@yadeck/slide-components-basic 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +16 -2
- package/dist/chrome.d.ts +15 -0
- package/dist/chrome.d.ts.map +1 -0
- package/dist/chrome.js +24 -0
- package/dist/chrome.js.map +1 -0
- package/dist/flow.d.ts +26 -0
- package/dist/flow.d.ts.map +1 -0
- package/dist/flow.js +46 -0
- package/dist/flow.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/layouts.d.ts +114 -0
- package/dist/layouts.d.ts.map +1 -0
- package/dist/layouts.js +220 -0
- package/dist/layouts.js.map +1 -0
- package/dist/panes.d.ts +43 -0
- package/dist/panes.d.ts.map +1 -0
- package/dist/panes.js +131 -0
- package/dist/panes.js.map +1 -0
- package/dist/styles.d.ts +12 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +157 -0
- package/dist/styles.js.map +1 -0
- package/package.json +60 -4
- package/src/chrome.tsx +43 -0
- package/src/flow.tsx +89 -0
- package/src/index.ts +91 -0
- package/src/layouts.tsx +509 -0
- package/src/panes.tsx +215 -0
- package/src/styles.ts +157 -0
package/dist/styles.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* basic の既定テーマ。Tailwind v4 の `@theme` トークンで、利用者は slide.config.ts の `theme` に
|
|
3
|
+
* 指定した CSS で同名トークンを上書きして色・フォント・文字サイズを差し替える。
|
|
4
|
+
* 色は `bg-primary` `text-ink`、フォントは `font-sans`、サイズは `text-h1` のように utility になる。
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_THEME = `
|
|
7
|
+
@theme {
|
|
8
|
+
--color-primary: #2a5bd7;
|
|
9
|
+
--color-primary-deep: #163a8f;
|
|
10
|
+
--color-accent: #e8c547;
|
|
11
|
+
--color-ink: #222222;
|
|
12
|
+
--color-muted: #666666;
|
|
13
|
+
--color-faint: #888888;
|
|
14
|
+
--color-surface: #ffffff;
|
|
15
|
+
--color-code: #1e1e1e;
|
|
16
|
+
--color-code-ink: #eeeeee;
|
|
17
|
+
|
|
18
|
+
--font-sans: "Helvetica Neue", Arial, "Hiragino Sans", "Noto Sans JP", sans-serif;
|
|
19
|
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
20
|
+
|
|
21
|
+
--text-body: 28px;
|
|
22
|
+
--text-small: 22px;
|
|
23
|
+
--text-h1: 64px;
|
|
24
|
+
--text-h2: 44px;
|
|
25
|
+
--text-h3: 34px;
|
|
26
|
+
--text-h4: 30px;
|
|
27
|
+
--text-display: 96px;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
/**
|
|
31
|
+
* utility を付けられない要素(RichText が生成する markdown 由来の p / ul / code など)のための最小 CSS。
|
|
32
|
+
* Layout 自体の見た目は layouts.tsx の className で持つ。
|
|
33
|
+
*/
|
|
34
|
+
export const BASIC_CSS = `
|
|
35
|
+
@layer components {
|
|
36
|
+
.yd-slide { font-family: var(--font-sans); color: var(--color-ink); font-size: var(--text-body); line-height: 1.5; }
|
|
37
|
+
.b-body p { margin: 0 0 .6em; }
|
|
38
|
+
.b-body p:last-child { margin-bottom: 0; }
|
|
39
|
+
.b-body ul { list-style: disc; padding-left: 1.2em; }
|
|
40
|
+
.b-body ol { list-style: decimal; padding-left: 1.2em; }
|
|
41
|
+
.b-body li::marker { color: var(--color-accent); }
|
|
42
|
+
/* 本文中のコード。合字(-> が → になる等)は原文と違って見えるので切る */
|
|
43
|
+
.yd-slide :not(pre) > code { font-family: var(--font-mono); font-size: .9em; font-variant-ligatures: none; background: color-mix(in srgb, var(--color-ink) 8%, transparent); padding: 0 .3em; border-radius: 4px; }
|
|
44
|
+
.b-body a { color: var(--color-primary); text-decoration: underline; }
|
|
45
|
+
|
|
46
|
+
/* code: pane の寸法(--pane-w / --pane-h)と行数・最大行長から、収まる最大のフォントサイズを選ぶ */
|
|
47
|
+
.yd-slide { --pane-w: 1120px; --pane-h: 500px; }
|
|
48
|
+
.b-code {
|
|
49
|
+
font-size: max(14px, min(
|
|
50
|
+
var(--text-body),
|
|
51
|
+
calc((var(--pane-w) - 48px) / var(--code-cols, 40) / 0.62),
|
|
52
|
+
calc((var(--pane-h) - var(--code-chrome, 40px)) / var(--code-rows, 10) / 1.375),
|
|
53
|
+
var(--code-fit, 9999px)
|
|
54
|
+
));
|
|
55
|
+
/* 下限まで縮めても入らない長い行は折り返す。はみ出して読めないよりよい */
|
|
56
|
+
white-space: pre-wrap;
|
|
57
|
+
overflow-wrap: anywhere;
|
|
58
|
+
font-variant-ligatures: none;
|
|
59
|
+
}
|
|
60
|
+
.b-code .hljs { color: var(--color-code-ink); }
|
|
61
|
+
.hljs-comment, .hljs-quote { color: #8b949e; font-style: italic; }
|
|
62
|
+
.hljs-keyword, .hljs-selector-tag, .hljs-literal, .hljs-type, .hljs-built_in, .hljs-doctag, .hljs-meta { color: #ff7b72; }
|
|
63
|
+
.hljs-string, .hljs-regexp, .hljs-addition, .hljs-attr, .hljs-meta .hljs-string { color: #a5d6ff; }
|
|
64
|
+
.hljs-number, .hljs-symbol, .hljs-bullet, .hljs-link, .hljs-variable, .hljs-template-variable { color: #79c0ff; }
|
|
65
|
+
.hljs-title, .hljs-section, .hljs-name, .hljs-selector-id, .hljs-selector-class, .hljs-function .hljs-title { color: #d2a8ff; }
|
|
66
|
+
.hljs-attribute, .hljs-params, .hljs-property { color: #ffa657; }
|
|
67
|
+
.hljs-emphasis { font-style: italic; }
|
|
68
|
+
.hljs-strong { font-weight: bold; }
|
|
69
|
+
.hljs-deletion { color: #ffa198; }
|
|
70
|
+
|
|
71
|
+
/* ---- flow(手順)。https://www.ameamelog.com/css-flow/ の 5 パターンを移植。寸法はスライド向けに 1.5 倍 ---- */
|
|
72
|
+
.b-flow { --flow-grad: linear-gradient(to right, var(--color-primary) 0%, color-mix(in srgb, var(--color-primary) 55%, white) 100%); margin: auto 0; padding: 0; list-style: none; width: 100%; }
|
|
73
|
+
.b-flow > li { list-style: none; }
|
|
74
|
+
.b-flow dl, .b-flow dd { margin: 0; }
|
|
75
|
+
.b-flow dt { font-size: var(--text-h4); font-weight: 700; color: var(--color-primary); line-height: 1.3; }
|
|
76
|
+
.b-flow dd { font-size: var(--text-small); color: color-mix(in srgb, var(--color-ink) 80%, transparent); line-height: 1.45; }
|
|
77
|
+
.b-flow .phase { display: inline-block; margin-left: .6em; padding: .1em .7em; border-radius: 999px; background: color-mix(in srgb, var(--color-primary) 10%, transparent); font-size: var(--text-small); font-weight: 700; color: var(--color-primary); vertical-align: middle; }
|
|
78
|
+
.b-flow .via { font-family: var(--font-mono); font-size: var(--text-small); font-weight: 700; color: var(--color-primary); white-space: nowrap; }
|
|
79
|
+
.b-flow .icon { font-size: 18px; font-weight: 700; color: #fff; background: var(--flow-grad); }
|
|
80
|
+
.b-flow.has-focus > li:not(.is-focus) { opacity: .35; }
|
|
81
|
+
.b-flow > li.is-focus .icon { background: var(--color-accent); color: var(--color-ink); }
|
|
82
|
+
.b-flow > li.is-focus dt { color: var(--color-ink); }
|
|
83
|
+
|
|
84
|
+
/* 1 カラム構成: 左に STEP ピル、縦レール、点と点線、右に見出しと説明 */
|
|
85
|
+
.b-flow-rail { padding-left: 180px; position: relative; }
|
|
86
|
+
.b-flow-rail::before { content: ""; width: 22px; height: 100%; background: color-mix(in srgb, var(--color-ink) 7%, transparent); margin-left: -11px; display: block; position: absolute; top: 0; left: 180px; }
|
|
87
|
+
.b-flow-rail > li { position: relative; }
|
|
88
|
+
.b-flow-rail > li:not(:last-child) { margin-bottom: 60px; }
|
|
89
|
+
.b-flow-rail > li .icon { padding: 10px 28px; display: block; position: absolute; top: 0; left: -180px; z-index: 2; }
|
|
90
|
+
.b-flow-rail > li .icon::after { content: ""; border-style: solid; border-width: 8px 0 8px 14px; border-color: transparent transparent transparent color-mix(in srgb, var(--color-primary) 55%, white); position: absolute; top: 50%; left: 100%; transform: translateY(-50%); }
|
|
91
|
+
.b-flow-rail > li.is-focus .icon::after { border-left-color: var(--color-accent); }
|
|
92
|
+
.b-flow-rail > li .via { position: absolute; top: -30px; left: 28px; transform: translateY(-50%); }
|
|
93
|
+
.b-flow-rail > li dl { padding-left: 100px; position: relative; }
|
|
94
|
+
.b-flow-rail > li dl::before, .b-flow-rail > li dl::after { content: ""; display: block; position: absolute; top: 24px; }
|
|
95
|
+
.b-flow-rail > li dl::before { width: 11px; height: 11px; margin-top: -5px; background: var(--color-primary); border-radius: 50%; left: -6px; z-index: 1; }
|
|
96
|
+
.b-flow-rail > li.is-focus dl::before { background: var(--color-accent); }
|
|
97
|
+
.b-flow-rail > li dl::after { width: 72px; border-bottom: 2px dashed color-mix(in srgb, var(--color-ink) 40%, transparent); left: 8px; }
|
|
98
|
+
.b-flow-rail > li dl dt { margin-bottom: 6px; }
|
|
99
|
+
|
|
100
|
+
/* ポップ: 吹き出しの番号 + 枠カードを横に並べ、間に › */
|
|
101
|
+
.b-flow-pop { --flow-gap: 56px; display: flex; justify-content: space-between; gap: var(--flow-gap); }
|
|
102
|
+
.b-flow-pop > li { flex: 1 1 0; min-width: 0; position: relative; }
|
|
103
|
+
.b-flow-pop > li .icon { font-family: var(--font-mono); font-size: 26px; line-height: 72px; width: 72px; background: var(--color-primary); margin: 0 auto 28px; display: block; border-radius: 50%; text-align: center; position: relative; letter-spacing: 2px; }
|
|
104
|
+
.b-flow-pop > li .icon::before { content: ""; border: solid transparent; border-width: 12px; border-top-color: var(--color-primary); position: absolute; top: calc(100% - 3px); left: 50%; transform: translateX(-50%); }
|
|
105
|
+
.b-flow-pop > li.is-focus .icon::before { border-top-color: var(--color-accent); }
|
|
106
|
+
.b-flow-pop > li .via { position: absolute; top: 24px; right: calc(100% + var(--flow-gap) / 2 + 4px); transform: translateX(50%); }
|
|
107
|
+
.b-flow-pop > li dl { padding: 32px 10px; word-break: auto-phrase; text-wrap: balance; border: 4px solid var(--color-primary); position: relative; text-align: center; height: calc(100% - 100px); }
|
|
108
|
+
.b-flow-pop > li.is-focus dl { border-color: var(--color-accent); }
|
|
109
|
+
/* 箱の間の ›。隙間(--flow-gap)の中央に置き、枠線に重ねない */
|
|
110
|
+
.b-flow-pop > li:not(:last-child) dl::before { content: ""; width: 36px; height: 36px; display: block; border-top: 8px solid var(--color-primary); border-right: 8px solid var(--color-primary); position: absolute; top: 50%; left: calc(100% + var(--flow-gap) / 2 - 8px); transform: translate(-50%, -50%) rotate(45deg); }
|
|
111
|
+
.b-flow-pop > li dl dt { margin-bottom: 10px; word-break: keep-all; }
|
|
112
|
+
.b-flow-pop > li dl dt .phase { display: block; margin: 8px auto 0; padding: .15em .6em; font-size: 18px; width: fit-content; max-width: 100%; box-sizing: border-box; white-space: nowrap; }
|
|
113
|
+
|
|
114
|
+
/* 枠線を使ったデザイン: 角丸の枠を縦に積み、点線と ▽ でつなぐ */
|
|
115
|
+
.b-flow-boxed > li { position: relative; }
|
|
116
|
+
.b-flow-boxed > li:not(:last-child) { margin-bottom: 40px; }
|
|
117
|
+
.b-flow-boxed > li:not(:first-child)::before { content: ""; height: 60px; display: block; border-left: 5px dotted color-mix(in srgb, var(--color-ink) 15%, transparent); position: absolute; top: -40px; left: calc(12% + 40px); z-index: 1; }
|
|
118
|
+
.b-flow-boxed > li .via { position: absolute; top: -20px; left: calc(12% + 72px); transform: translateY(-50%); }
|
|
119
|
+
.b-flow-boxed > li dl { width: 100%; padding: 8px 36px; display: flex; align-items: center; border: 3px solid var(--color-primary); border-radius: 14px; position: relative; background: var(--color-surface); }
|
|
120
|
+
.b-flow-boxed > li.is-focus dl { border-color: var(--color-accent); }
|
|
121
|
+
.b-flow-boxed > li:not(:last-child) dl::before, .b-flow-boxed > li:not(:last-child) dl::after { content: ""; border: solid transparent; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); }
|
|
122
|
+
.b-flow-boxed > li:not(:last-child) dl::before { border-width: 26px; border-top-color: var(--color-primary); }
|
|
123
|
+
.b-flow-boxed > li.is-focus:not(:last-child) dl::before { border-top-color: var(--color-accent); }
|
|
124
|
+
.b-flow-boxed > li:not(:last-child) dl::after { border-width: 23px; border-top-color: var(--color-surface); }
|
|
125
|
+
.b-flow-boxed > li dl dt { flex: 0 0 24%; margin-right: 2vw; text-align: center; }
|
|
126
|
+
.b-flow-boxed > li dl dt .icon { padding: 4px 14px; margin: 0 auto 6px; display: block; border-radius: 999px; width: fit-content; position: relative; z-index: 2; }
|
|
127
|
+
.b-flow-boxed > li dl dd { flex: 1; }
|
|
128
|
+
.b-flow-boxed > li dl dd .phase { display: block; width: fit-content; margin: 0 0 4px; }
|
|
129
|
+
|
|
130
|
+
/* スタイリッシュ: 全体を 1 つの枠で囲み、区切り線に ▽ */
|
|
131
|
+
.b-flow-framed { border: 4px solid var(--color-primary); }
|
|
132
|
+
.b-flow-framed > li { padding: 12px 36px; position: relative; }
|
|
133
|
+
.b-flow-framed > li:not(:last-child) { border-bottom: 4px solid var(--color-primary); }
|
|
134
|
+
.b-flow-framed > li:not(:last-child)::before, .b-flow-framed > li:not(:last-child)::after { content: ""; border: solid transparent; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); z-index: 1; }
|
|
135
|
+
.b-flow-framed > li:not(:last-child)::before { border-width: 26px; border-top-color: var(--color-primary); }
|
|
136
|
+
.b-flow-framed > li:not(:last-child)::after { border-width: 21px; border-top-color: var(--color-surface); }
|
|
137
|
+
.b-flow-framed > li .via { position: absolute; top: 0; left: calc(50% + 44px); transform: translateY(-50%); background: var(--color-surface); padding: 0 10px; z-index: 2; }
|
|
138
|
+
.b-flow-framed > li dl { display: flex; align-items: center; }
|
|
139
|
+
.b-flow-framed > li dl dt { flex: 0 0 24%; margin-right: 1vw; text-align: center; }
|
|
140
|
+
.b-flow-framed > li dl dt .icon { padding: 4px 14px; margin: 0 auto 6px; display: block; border-radius: 999px; width: fit-content; }
|
|
141
|
+
.b-flow-framed > li dl dd { flex: 1; }
|
|
142
|
+
.b-flow-framed > li dl dd .phase { display: block; width: fit-content; margin: 0 0 4px; }
|
|
143
|
+
|
|
144
|
+
/* 2 カラム構成: 右端が矢印の枠を 2 列に */
|
|
145
|
+
.b-flow-grid { display: flex; flex-wrap: wrap; gap: 48px 4%; }
|
|
146
|
+
.b-flow-grid > li { padding: 28px 44px 24px 36px; flex: 0 0 48%; border: 3px solid var(--color-primary); position: relative; background: var(--color-surface); }
|
|
147
|
+
.b-flow-grid > li.is-focus { border-color: var(--color-accent); }
|
|
148
|
+
.b-flow-grid > li:not(:last-child)::before, .b-flow-grid > li:not(:last-child)::after { content: ""; border: solid transparent; position: absolute; top: 50%; left: 100%; transform: translateY(-50%); }
|
|
149
|
+
.b-flow-grid > li:not(:last-child)::before { border-width: 30px; border-left-color: var(--color-primary); }
|
|
150
|
+
.b-flow-grid > li.is-focus:not(:last-child)::before { border-left-color: var(--color-accent); }
|
|
151
|
+
.b-flow-grid > li:not(:last-child)::after { border-width: 27px; border-left-color: var(--color-surface); }
|
|
152
|
+
.b-flow-grid > li .icon { padding: 7px 28px; display: block; border-radius: 999px; position: absolute; top: -18px; left: -14px; z-index: 2; }
|
|
153
|
+
.b-flow-grid > li .via { display: none; }
|
|
154
|
+
.b-flow-grid > li dl dt { margin-bottom: 6px; }
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;CAuB5B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0HxB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yadeck/slide-components-basic",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "YADECK: standard layouts (title, section, auto pane layout, two-column, cards, columns, code, image, video, flow diagrams, notes)",
|
|
5
|
+
"homepage": "https://onfi.github.io/yadeck/",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/onfi/yadeck.git",
|
|
9
|
+
"directory": "packages/slide-components-basic"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/onfi/yadeck/issues"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src",
|
|
26
|
+
"!src/**/*.test.ts",
|
|
27
|
+
"!src/**/*.test.tsx",
|
|
28
|
+
"!src/**/__snapshots__"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -b"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@yadeck/slide-core": "^0.1.1",
|
|
35
|
+
"@yadeck/slide-schema": "^0.1.1",
|
|
36
|
+
"highlight.js": "^11.12.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": "^19.0.0",
|
|
40
|
+
"zod": "^4.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"react": "^19.3.0",
|
|
44
|
+
"zod": "^4.6.0"
|
|
45
|
+
},
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"author": "onfi",
|
|
48
|
+
"keywords": [
|
|
49
|
+
"yadeck",
|
|
50
|
+
"slides",
|
|
51
|
+
"presentation",
|
|
52
|
+
"yaml",
|
|
53
|
+
"react",
|
|
54
|
+
"ai"
|
|
55
|
+
],
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=22"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/chrome.tsx
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Block, RichText, type BlockNode } from "@yadeck/slide-core";
|
|
2
|
+
|
|
3
|
+
/** スライド枠いっぱいに広げる共通のパディング付きコンテナ */
|
|
4
|
+
export const PAGE = "absolute inset-0 flex flex-col gap-6 px-20 py-14";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* スライド上部の見出し。accent の縦バーと primary の細い下線で、表紙と同じ文法にする。
|
|
8
|
+
* slide の description があれば見出しの下に 1 行添える
|
|
9
|
+
*/
|
|
10
|
+
/** 見出しの表示幅(全角 2、半角 1)。長い見出しは 1 段小さくして 1 行に収める */
|
|
11
|
+
function headingWidth(text: string): number {
|
|
12
|
+
let w = 0;
|
|
13
|
+
for (const ch of text.replace(/[`*_]/g, "")) w += (ch.codePointAt(0) ?? 0) > 0x2e7f ? 2 : 1;
|
|
14
|
+
return w;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const SlideHeading = ({ block, description }: { block: BlockNode; description?: string }) => (
|
|
18
|
+
<header className="flex flex-col gap-2">
|
|
19
|
+
<div
|
|
20
|
+
className={`flex items-center gap-4 border-b-2 border-primary/30 pb-3 ${
|
|
21
|
+
headingWidth(block.text ?? "") > 44 ? "[&_.b-heading]:text-h3" : "[&_.b-heading]:text-h2"
|
|
22
|
+
}`}
|
|
23
|
+
>
|
|
24
|
+
<span className="h-10 w-2 shrink-0 rounded-sm bg-accent" />
|
|
25
|
+
<Block data={block} />
|
|
26
|
+
</div>
|
|
27
|
+
{description && <RichText as="p" className="m-0 pl-6 text-small leading-snug text-muted" text={description} />}
|
|
28
|
+
</header>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 発表者ノート(note の block)。画面と印刷には出ないが、発表者モードがページの中から読み取るので、
|
|
33
|
+
* どの Layout も items のノートを必ず描く
|
|
34
|
+
*/
|
|
35
|
+
export const Notes = ({ items }: { items: BlockNode[] }) => (
|
|
36
|
+
<>
|
|
37
|
+
{items
|
|
38
|
+
.filter((b) => b.tags.includes("note"))
|
|
39
|
+
.map((b) => (
|
|
40
|
+
<Block key={b.id} data={b} />
|
|
41
|
+
))}
|
|
42
|
+
</>
|
|
43
|
+
);
|
package/src/flow.tsx
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { RichText, type BlockOf } from "@yadeck/slide-core";
|
|
3
|
+
|
|
4
|
+
export const flowType = z.object({
|
|
5
|
+
steps: z
|
|
6
|
+
.array(
|
|
7
|
+
z.object({
|
|
8
|
+
label: z.string().describe("段の名前。見出しとして出す"),
|
|
9
|
+
via: z.string().optional().describe("この段に入ってくる矢印のラベル(変換や処理の名前)"),
|
|
10
|
+
phase: z.string().optional().describe("この段が属する段階の名前。見出しの横に小さく出す"),
|
|
11
|
+
detail: z.string().optional().describe("この段で起きることの一言。説明として出す"),
|
|
12
|
+
}),
|
|
13
|
+
)
|
|
14
|
+
.min(2)
|
|
15
|
+
.describe("流れの各段。順番どおり"),
|
|
16
|
+
focus: z
|
|
17
|
+
.union([z.number().int().min(1), z.array(z.number().int().min(1))])
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("焦点にする段の番号(1 始まり)。配列で複数可。他の段は薄くなる"),
|
|
20
|
+
direction: z
|
|
21
|
+
.enum(["row", "column"])
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("row: 左から右、column: 上から下(既定)。見せ方の種類は block の tags(flow / flow boxed / flow framed / flow grid)で選ぶ"),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
type FlowProps = BlockOf<typeof flowType>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 流れ(手順)。マークアップと CSS は https://www.ameamelog.com/css-flow/ の 5 パターンを移植したもので、
|
|
30
|
+
* 色をテーマトークンに、寸法をスライド向けに直している(CSS は styles.ts の .b-flow-*)。
|
|
31
|
+
* 構造は共通: ul.b-flow > li > .icon(番号) + dl > dt(見出し) / dd(説明)
|
|
32
|
+
*/
|
|
33
|
+
function FlowList({ block, variant, numbered }: { block: FlowProps; variant: string; numbered: "step" | "plain" }) {
|
|
34
|
+
const focus = new Set(block.focus === undefined ? [] : Array.isArray(block.focus) ? block.focus : [block.focus]);
|
|
35
|
+
// boxed / framed は原型どおり番号を dt の中(見出しの上)に置く
|
|
36
|
+
const iconInTitle = variant === "boxed" || variant === "framed";
|
|
37
|
+
return (
|
|
38
|
+
<ul className={`b-flow b-flow-${variant} ${focus.size > 0 ? "has-focus" : ""}`}>
|
|
39
|
+
{block.steps.map((st, i) => {
|
|
40
|
+
const num = String(i + 1).padStart(2, "0");
|
|
41
|
+
const icon = <span className="icon">{numbered === "step" ? `STEP.${num}` : num}</span>;
|
|
42
|
+
return (
|
|
43
|
+
<li key={i} className={focus.has(i + 1) ? "is-focus" : ""}>
|
|
44
|
+
{!iconInTitle && icon}
|
|
45
|
+
{st.via && (
|
|
46
|
+
<span className="via">
|
|
47
|
+
<RichText text={st.via} />
|
|
48
|
+
</span>
|
|
49
|
+
)}
|
|
50
|
+
<dl>
|
|
51
|
+
<dt>
|
|
52
|
+
{iconInTitle && icon}
|
|
53
|
+
<RichText text={st.label} />
|
|
54
|
+
{st.phase && !iconInTitle && (
|
|
55
|
+
<span className="phase">
|
|
56
|
+
<RichText text={st.phase} />
|
|
57
|
+
</span>
|
|
58
|
+
)}
|
|
59
|
+
</dt>
|
|
60
|
+
{(st.detail || (st.phase && iconInTitle)) && (
|
|
61
|
+
<dd>
|
|
62
|
+
{st.phase && iconInTitle && (
|
|
63
|
+
<span className="phase">
|
|
64
|
+
<RichText text={st.phase} />
|
|
65
|
+
</span>
|
|
66
|
+
)}
|
|
67
|
+
{st.detail && <RichText text={st.detail} />}
|
|
68
|
+
</dd>
|
|
69
|
+
)}
|
|
70
|
+
</dl>
|
|
71
|
+
</li>
|
|
72
|
+
);
|
|
73
|
+
})}
|
|
74
|
+
</ul>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** `flow`: 縦は 1 カラム構成(STEP ピル + レール + 点線)、横(direction: row)はポップ(吹き出し番号 + 枠カード + ›) */
|
|
79
|
+
export const FlowBlock = (block: FlowProps) =>
|
|
80
|
+
block.direction === "row" ? <FlowList block={block} variant="pop" numbered="plain" /> : <FlowList block={block} variant="rail" numbered="step" />;
|
|
81
|
+
|
|
82
|
+
/** `flow boxed`: 枠線を使ったデザイン。角丸の枠を縦に積み、点線と ▽ でつなぐ */
|
|
83
|
+
export const FlowBoxed = (block: FlowProps) => <FlowList block={block} variant="boxed" numbered="step" />;
|
|
84
|
+
|
|
85
|
+
/** `flow framed`: スタイリッシュ。全体を 1 つの枠で囲み、区切り線に ▽ の切り込み */
|
|
86
|
+
export const FlowFramed = (block: FlowProps) => <FlowList block={block} variant="framed" numbered="step" />;
|
|
87
|
+
|
|
88
|
+
/** `flow grid`: 2 カラム構成。右端が矢印の枠を 2 列に */
|
|
89
|
+
export const FlowGrid = (block: FlowProps) => <FlowList block={block} variant="grid" numbered="step" />;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { BlockLayout, DeckLayout, SlideLayout, definePlugin } from "@yadeck/slide-core";
|
|
2
|
+
import {
|
|
3
|
+
BodyBlock,
|
|
4
|
+
CardSlide,
|
|
5
|
+
CodeBlock,
|
|
6
|
+
DefaultBlock,
|
|
7
|
+
DefaultDeck,
|
|
8
|
+
HeaderSlide,
|
|
9
|
+
HeadingBlock,
|
|
10
|
+
ImageBlock,
|
|
11
|
+
ItemBlock,
|
|
12
|
+
ListBlock,
|
|
13
|
+
NoteBlock,
|
|
14
|
+
ProfileSlide,
|
|
15
|
+
QuoteBlock,
|
|
16
|
+
SectionHeaderSlide,
|
|
17
|
+
StatBlock,
|
|
18
|
+
TwoColumnSlide,
|
|
19
|
+
VideoBlock,
|
|
20
|
+
codeType,
|
|
21
|
+
headerType,
|
|
22
|
+
headingType,
|
|
23
|
+
imageType,
|
|
24
|
+
itemType,
|
|
25
|
+
listType,
|
|
26
|
+
profileType,
|
|
27
|
+
quoteType,
|
|
28
|
+
sectionHeaderType,
|
|
29
|
+
statType,
|
|
30
|
+
videoType,
|
|
31
|
+
} from "./layouts.js";
|
|
32
|
+
import { FlowBlock, FlowBoxed, FlowFramed, FlowGrid, flowType } from "./flow.js";
|
|
33
|
+
import { ColumnsSlide, DefaultSlide } from "./panes.js";
|
|
34
|
+
import { BASIC_CSS, DEFAULT_THEME } from "./styles.js";
|
|
35
|
+
|
|
36
|
+
export * from "./layouts.js";
|
|
37
|
+
export * from "./panes.js";
|
|
38
|
+
export * from "./flow.js";
|
|
39
|
+
export * from "./chrome.js";
|
|
40
|
+
export { BASIC_CSS, DEFAULT_THEME } from "./styles.js";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 最小 tag セット。全 Interface に tags: [] の fallback を持つので、
|
|
44
|
+
* これを先頭に置けばどんな tags でも必ず何かが描画される。
|
|
45
|
+
*/
|
|
46
|
+
export const basic = definePlugin((c) => {
|
|
47
|
+
c.addStyle(DEFAULT_THEME);
|
|
48
|
+
c.addStyle(BASIC_CSS);
|
|
49
|
+
c.bind(DeckLayout, [], DefaultDeck, { description: "スライドを縦に並べる" });
|
|
50
|
+
|
|
51
|
+
c.bind(SlideLayout, [], DefaultSlide, {
|
|
52
|
+
description:
|
|
53
|
+
"汎用。テキスト(tags なし / body / list / quote / stat / item)は全部で 1 pane、code / image / video は 1 つずつ 1 pane に数え、1: 全面、2: 左右、3: ト型、4: 十字、5 以上: 左右 2 列に自動配置。テキストは 1 行なら中央に大きく、2 行以上なら箇条書き",
|
|
54
|
+
});
|
|
55
|
+
c.bind(SlideLayout, ["header"], HeaderSlide, { type: headerType, description: "表紙。title / subtitle を大きく、発表者情報を左下、イベント情報を右下、logo を右上" });
|
|
56
|
+
c.bind(SlideLayout, ["profile"], ProfileSlide, { type: profileType, description: "自己紹介。左に名前・肩書き・写真・アカウント、右に items" });
|
|
57
|
+
c.bind(SlideLayout, ["section-header"], SectionHeaderSlide, { type: sectionHeaderType, description: "章扉。number と heading、body だけを描く" });
|
|
58
|
+
c.bind(SlideLayout, ["two-column"], TwoColumnSlide, { description: "2 列の対比。left を付けた block を左列、right を右列、どちらも無い block は前置きとして列の上に置く。列の見出しは heading left / heading right" });
|
|
59
|
+
c.bind(SlideLayout, ["card"], CardSlide, { description: "item をカードにして並べる(3 枚までは 1 行、4 枚は 2×2、5 枚以上は 3 列)。他の block は前置きとして上に置く" });
|
|
60
|
+
c.bind(SlideLayout, ["columns"], ColumnsSlide, { description: "pane を横一列に等分する(4 まで。5 以上は 3 列 × 2 行)" });
|
|
61
|
+
|
|
62
|
+
c.bind(BlockLayout, [], DefaultBlock, { description: "本文 1 行。markdown 可" });
|
|
63
|
+
c.bind(BlockLayout, ["heading"], HeadingBlock, { type: headingType, description: "見出し。slide の上部に置かれる" });
|
|
64
|
+
c.bind(BlockLayout, ["body"], BodyBlock, { description: "本文。markdown 可。複数行は段落になる" });
|
|
65
|
+
c.bind(BlockLayout, ["list"], ListBlock, { type: listType, description: "行ごとの箇条書き" });
|
|
66
|
+
c.bind(BlockLayout, ["code"], CodeBlock, { type: codeType, description: "コード。highlight.js で色付けし、pane に収まるよう縮小する" });
|
|
67
|
+
c.bind(BlockLayout, ["image"], ImageBlock, { type: imageType, description: "画像。pane にアスペクト比維持で収める。text はキャプション" });
|
|
68
|
+
c.bind(BlockLayout, ["video"], VideoBlock, { type: videoType, description: "動画。YouTube の URL は embed に変換して iframe で埋め込む" });
|
|
69
|
+
c.bind(BlockLayout, ["flow"], FlowBlock, {
|
|
70
|
+
type: flowType,
|
|
71
|
+
description: "流れ(手順)。縦は STEP ピル + 縦レール + 見出しと説明(1 カラム構成)、横(direction: row)は吹き出し番号付きの枠カードを › でつなぐ。via / phase / detail / focus。1 つで 1 pane",
|
|
72
|
+
});
|
|
73
|
+
c.bind(BlockLayout, ["flow", "boxed"], FlowBoxed, {
|
|
74
|
+
type: flowType,
|
|
75
|
+
description: "流れ(手順)の別の見せ方。角丸の枠を縦に積み、点線と ▽ でつなぐ。左に STEP と見出し、右に説明",
|
|
76
|
+
});
|
|
77
|
+
c.bind(BlockLayout, ["flow", "framed"], FlowFramed, {
|
|
78
|
+
type: flowType,
|
|
79
|
+
description: "流れ(手順)の別の見せ方。全体を 1 つの枠で囲み、区切り線に ▽ の切り込み。左に STEP と見出し、右に説明",
|
|
80
|
+
});
|
|
81
|
+
c.bind(BlockLayout, ["flow", "grid"], FlowGrid, {
|
|
82
|
+
type: flowType,
|
|
83
|
+
description: "流れ(手順)の別の見せ方。右端が矢印形のカードを 2 列に並べる。段数が多いときに高さを取らない",
|
|
84
|
+
});
|
|
85
|
+
c.bind(BlockLayout, ["quote"], QuoteBlock, { type: quoteType, description: "引用" });
|
|
86
|
+
c.bind(BlockLayout, ["stat"], StatBlock, { type: statType, description: "数字や固有名詞を大きく見せる。label と補足の text" });
|
|
87
|
+
c.bind(BlockLayout, ["item"], ItemBlock, { type: itemType, description: "並列の項目(title + 説明の text)。card slide でカードになる" });
|
|
88
|
+
c.bind(BlockLayout, ["note"], NoteBlock, { description: "発表者ノート。画面にも印刷にも出ず、presenter モードに出る" });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export default basic;
|