@sakura-ui/sakura-ui 0.5.1 → 0.5.2
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 +2 -2
- package/packages/core/package.json +1 -1
- package/packages/core/src/components/Card.tsx +52 -56
- package/packages/core/src/components/Code.tsx +6 -6
- package/packages/core/src/components/Faq.tsx +30 -26
- package/packages/core/src/components/Heading.tsx +50 -48
- package/packages/core/src/components/Icon.tsx +34 -34
- package/packages/core/src/components/Link.tsx +13 -12
- package/packages/core/src/components/LinkCard.tsx +87 -76
- package/packages/core/src/components/List.tsx +20 -13
- package/packages/core/src/components/OverflowContainer.tsx +9 -9
- package/packages/core/src/components/Pre.tsx +9 -9
- package/packages/core/src/components/Table.tsx +29 -24
- package/packages/core/src/components/index.ts +1 -0
- package/packages/core/src/components/styles.ts +42 -0
- package/packages/core/src/index.ts +2 -0
- package/packages/forms/.turbo/turbo-build.log +2 -2
- package/packages/helper/.turbo/turbo-build.log +1 -1
- package/packages/helper/node_modules/.bin/vitest +2 -2
- package/packages/markdown/.turbo/turbo-build.log +4 -4
- package/packages/markdown/README.md +240 -4
- package/packages/markdown/dist/index.cjs.js +71 -137
- package/packages/markdown/dist/index.es.js +2624 -16739
- package/packages/markdown/dist/types/components/Markdown.d.ts +15 -10
- package/packages/markdown/dist/types/components/Markdown.d.ts.map +1 -1
- package/packages/markdown/dist/types/components/index.d.ts +1 -1
- package/packages/markdown/dist/types/components/index.d.ts.map +1 -1
- package/packages/markdown/dist/types/decorate.d.ts +24 -0
- package/packages/markdown/dist/types/decorate.d.ts.map +1 -0
- package/packages/markdown/dist/types/index.d.ts +3 -1
- package/packages/markdown/dist/types/index.d.ts.map +1 -1
- package/packages/markdown/dist/types/marked/attributes.d.ts +31 -0
- package/packages/markdown/dist/types/marked/attributes.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/directives/card.d.ts +3 -0
- package/packages/markdown/dist/types/marked/directives/card.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/directives/context.d.ts +36 -0
- package/packages/markdown/dist/types/marked/directives/context.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/directives/faq.d.ts +11 -0
- package/packages/markdown/dist/types/marked/directives/faq.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/directives/grid.d.ts +9 -0
- package/packages/markdown/dist/types/marked/directives/grid.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/directives/index.d.ts +8 -0
- package/packages/markdown/dist/types/marked/directives/index.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/directives/linkButton.d.ts +6 -0
- package/packages/markdown/dist/types/marked/directives/linkButton.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/directives/youtube.d.ts +3 -0
- package/packages/markdown/dist/types/marked/directives/youtube.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/html.d.ts +15 -0
- package/packages/markdown/dist/types/marked/html.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/index.d.ts +13 -0
- package/packages/markdown/dist/types/marked/index.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/registry.d.ts +8 -0
- package/packages/markdown/dist/types/marked/registry.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/renderer.d.ts +16 -0
- package/packages/markdown/dist/types/marked/renderer.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/tokenizer.d.ts +40 -0
- package/packages/markdown/dist/types/marked/tokenizer.d.ts.map +1 -0
- package/packages/markdown/dist/types/render.d.ts +22 -0
- package/packages/markdown/dist/types/render.d.ts.map +1 -0
- package/packages/markdown/dist/types/sanitize.d.ts +11 -0
- package/packages/markdown/dist/types/sanitize.d.ts.map +1 -0
- package/packages/markdown/node_modules/.bin/marked +55 -0
- package/packages/markdown/package.json +6 -19
- package/packages/markdown/src/components/Markdown.tsx +44 -346
- package/packages/markdown/src/components/index.ts +1 -1
- package/packages/markdown/src/decorate.ts +164 -0
- package/packages/markdown/src/index.ts +3 -1
- package/packages/markdown/src/marked/attributes.ts +88 -0
- package/packages/markdown/src/marked/directives/card.ts +77 -0
- package/packages/markdown/src/marked/directives/context.ts +39 -0
- package/packages/markdown/src/marked/directives/faq.ts +29 -0
- package/packages/markdown/src/marked/directives/grid.ts +50 -0
- package/packages/markdown/src/marked/directives/index.ts +29 -0
- package/packages/markdown/src/marked/directives/linkButton.ts +20 -0
- package/packages/markdown/src/marked/directives/youtube.ts +46 -0
- package/packages/markdown/src/marked/html.ts +55 -0
- package/packages/markdown/src/marked/index.ts +45 -0
- package/packages/markdown/src/marked/registry.ts +34 -0
- package/packages/markdown/src/marked/renderer.ts +52 -0
- package/packages/markdown/src/marked/tokenizer.ts +175 -0
- package/packages/markdown/src/render.ts +55 -0
- package/packages/markdown/src/sanitize.ts +194 -0
- package/packages/markdown/tests/Markdown.test.tsx +122 -0
- package/packages/markdown/tests/__snapshots__/Markdown.test.tsx.snap +451 -0
- package/packages/markdown/tests/anchors.test.ts +108 -0
- package/packages/markdown/tests/fixtures.ts +100 -0
- package/packages/markdown/tests/sanitize.test.ts +168 -0
- package/packages/markdown/tests/server.test.tsx +51 -0
- package/packages/markdown/tests/vitest.setup.ts +4 -0
- package/packages/markdown/tsconfig.test.json +11 -0
- package/packages/markdown/vite.config.ts +5 -1
- package/packages/tailwind-theme-plugin/.turbo/turbo-build.log +2 -2
- package/packages/markdown/dist/types/plugins/attr.d.ts +0 -3
- package/packages/markdown/dist/types/plugins/attr.d.ts.map +0 -1
- package/packages/markdown/dist/types/plugins/card.d.ts +0 -3
- package/packages/markdown/dist/types/plugins/card.d.ts.map +0 -1
- package/packages/markdown/dist/types/plugins/cell.d.ts +0 -3
- package/packages/markdown/dist/types/plugins/cell.d.ts.map +0 -1
- package/packages/markdown/dist/types/plugins/faq.d.ts +0 -3
- package/packages/markdown/dist/types/plugins/faq.d.ts.map +0 -1
- package/packages/markdown/dist/types/plugins/grid.d.ts +0 -3
- package/packages/markdown/dist/types/plugins/grid.d.ts.map +0 -1
- package/packages/markdown/dist/types/plugins/headings.d.ts +0 -11
- package/packages/markdown/dist/types/plugins/headings.d.ts.map +0 -1
- package/packages/markdown/dist/types/plugins/helper.d.ts +0 -9
- package/packages/markdown/dist/types/plugins/helper.d.ts.map +0 -1
- package/packages/markdown/dist/types/plugins/index.d.ts +0 -9
- package/packages/markdown/dist/types/plugins/index.d.ts.map +0 -1
- package/packages/markdown/dist/types/plugins/linkButton.d.ts +0 -3
- package/packages/markdown/dist/types/plugins/linkButton.d.ts.map +0 -1
- package/packages/markdown/dist/types/plugins/youtube.d.ts +0 -3
- package/packages/markdown/dist/types/plugins/youtube.d.ts.map +0 -1
- package/packages/markdown/src/plugins/attr.ts +0 -19
- package/packages/markdown/src/plugins/card.ts +0 -71
- package/packages/markdown/src/plugins/cell.ts +0 -47
- package/packages/markdown/src/plugins/faq.ts +0 -40
- package/packages/markdown/src/plugins/grid.ts +0 -54
- package/packages/markdown/src/plugins/headings.ts +0 -38
- package/packages/markdown/src/plugins/helper.ts +0 -27
- package/packages/markdown/src/plugins/index.ts +0 -8
- package/packages/markdown/src/plugins/linkButton.ts +0 -26
- package/packages/markdown/src/plugins/youtube.ts +0 -49
|
@@ -17,6 +17,14 @@ or
|
|
|
17
17
|
$ pnpm add @sakura-ui/markdown
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
## Client only
|
|
21
|
+
|
|
22
|
+
This renders in the browser and nowhere else. Sanitising and styling both walk
|
|
23
|
+
the DOM, so the conversion runs in an effect: on the server it produces nothing,
|
|
24
|
+
and the markup appears once the page is live.
|
|
25
|
+
|
|
26
|
+
With Next.js, load it through `next/dynamic` with `ssr: false`.
|
|
27
|
+
|
|
20
28
|
## Usage
|
|
21
29
|
When using libraries such as Gatsby or Next.js, please use them in combination with React Suspense.
|
|
22
30
|
```ts
|
|
@@ -46,7 +54,62 @@ export const MyPage = () => {
|
|
|
46
54
|
}
|
|
47
55
|
```
|
|
48
56
|
|
|
57
|
+
## Properties
|
|
58
|
+
|
|
59
|
+
| Property | Default | |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `children` | — | The Markdown to render. |
|
|
62
|
+
| `showToc` | `false` | Puts a table of contents above the document. |
|
|
63
|
+
| `tocTitle` | `目次` | The heading of that table of contents. |
|
|
64
|
+
| `shiftHeading` | `0` | Moves every heading down by this many levels, so that a document starting at `#` can sit under a page title. |
|
|
65
|
+
| `tocMaxDepth` | `2` | How deep the table of contents goes. Headings below it are still rendered. |
|
|
66
|
+
|
|
67
|
+
`shiftHeading` was called `shiftHeding` before 0.4.0.
|
|
68
|
+
|
|
69
|
+
## Raw HTML
|
|
70
|
+
|
|
71
|
+
Markdown may contain HTML, and it is sanitised before it reaches the page:
|
|
72
|
+
script, event handlers, and `javascript:` URLs never survive. Inline `style` is
|
|
73
|
+
kept, but only the properties the design system expects — `position: fixed`, for
|
|
74
|
+
instance, is dropped, since a transparent box over the page takes every click
|
|
75
|
+
aimed at what is underneath it.
|
|
76
|
+
|
|
77
|
+
An `iframe` is allowed only for the video the `youtube` directive embeds. One
|
|
78
|
+
pointing anywhere else is removed.
|
|
79
|
+
|
|
49
80
|
## Syntax
|
|
81
|
+
|
|
82
|
+
A directive is a name, then an optional `[label]`, then optional `{attributes}`,
|
|
83
|
+
in that order.
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
:link-button[Services and applications]{href=/services}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Quote a value that has a space in it. Without the quotes the value stops at the
|
|
90
|
+
first one, and the rest is read as another attribute and dropped — which is how
|
|
91
|
+
alternative text ends up as one long word.
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
::card-img{src=/photo.jpg alt="A field at dawn"}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Write `{#name}` to name the element, so that a link somewhere else can point at
|
|
98
|
+
it.
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
:::card{#pricing}
|
|
102
|
+
::card-title[What it costs]
|
|
103
|
+
:::
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`{#name}` is the only spelling. A sigil says something about the element and
|
|
107
|
+
means the same on every directive, while an attribute belongs to whichever
|
|
108
|
+
directive reads it, so `{id=name}` is left for a directive to take if one ever
|
|
109
|
+
wants it. The name is used as written and nothing else on the page is given it,
|
|
110
|
+
so a heading further down that would have produced the same one gets `-1`
|
|
111
|
+
instead.
|
|
112
|
+
|
|
50
113
|
### LinkButton
|
|
51
114
|
```
|
|
52
115
|
:link-button[Services and applications]{href=/services}
|
|
@@ -54,8 +117,14 @@ export const MyPage = () => {
|
|
|
54
117
|
<img width="288" alt="スクリーンショット 2024-07-26 23 44 39" src="https://github.com/user-attachments/assets/997ccf27-4d83-4fb5-b173-ae94cd7d76cb">
|
|
55
118
|
|
|
56
119
|
### YouTube
|
|
120
|
+
|
|
121
|
+
The address of the video, as it is copied from the browser. The watch page, the
|
|
122
|
+
share link, a short and an embed are all read; so is the id on its own. The
|
|
123
|
+
label becomes the frame's title, which is the name a screen reader reads out, so
|
|
124
|
+
write one.
|
|
125
|
+
|
|
57
126
|
```
|
|
58
|
-
::youtube[
|
|
127
|
+
::youtube[Introducing the service]{video=https://www.youtube.com/watch?v=yXdbvBzxeb8}
|
|
59
128
|
```
|
|
60
129
|
<img width="494" alt="スクリーンショット 2024-07-26 23 48 18" src="https://github.com/user-attachments/assets/a724cf27-a1af-4633-b9a8-27b25b04e3ae">
|
|
61
130
|
|
|
@@ -78,21 +147,35 @@ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
|
|
|
78
147
|
```
|
|
79
148
|
<img width="1143" alt="スクリーンショット 2024-07-26 23 51 46" src="https://github.com/user-attachments/assets/60b813b4-3a8c-451d-99b2-58c1e5b3a3b4">
|
|
80
149
|
|
|
150
|
+
A grid of cells is a layout — prose beside a figure is not two of something — so
|
|
151
|
+
it renders as plain boxes. A grid of cards is a list, and renders as one; see
|
|
152
|
+
below.
|
|
153
|
+
|
|
154
|
+
Use `::cell-img` in place of a Markdown image when the image is the top of the
|
|
155
|
+
cell; it carries the spacing that separates it from the text underneath.
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
:::cell
|
|
159
|
+
::cell-img{src=https://dummyimage.com/600x400/000/fff alt="A field at dawn"}
|
|
160
|
+
Lorem ipsum dolor sit amet.
|
|
161
|
+
:::
|
|
162
|
+
```
|
|
163
|
+
|
|
81
164
|
### Multi column layout with card
|
|
82
165
|
```
|
|
83
166
|
::::grid-cols-3
|
|
84
167
|
:::card
|
|
85
|
-
::card-img{alt=
|
|
168
|
+
::card-img{alt="A field at dawn" src=https://dummyimage.com/600x400/000/fff}
|
|
86
169
|
::card-title[Card title]
|
|
87
170
|
::card-description[Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.]
|
|
88
171
|
:::
|
|
89
172
|
:::card
|
|
90
|
-
::card-img{alt=
|
|
173
|
+
::card-img{alt="A field at dawn" src=https://dummyimage.com/600x400/000/fff}
|
|
91
174
|
::card-title[Card title]
|
|
92
175
|
::card-description[Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.]
|
|
93
176
|
:::
|
|
94
177
|
:::card
|
|
95
|
-
::card-img{alt=
|
|
178
|
+
::card-img{alt="A field at dawn" src=https://dummyimage.com/600x400/000/fff}
|
|
96
179
|
::card-title[Card title]
|
|
97
180
|
::card-description[Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.]
|
|
98
181
|
:::
|
|
@@ -100,6 +183,47 @@ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
|
|
|
100
183
|
```
|
|
101
184
|
<img width="1133" alt="スクリーンショット 2024-07-26 23 55 37" src="https://github.com/user-attachments/assets/9a6dff6f-7115-451a-9d15-f953ddad78b7">
|
|
102
185
|
|
|
186
|
+
Cards in a grid are things of one kind, and how many there are is part of what
|
|
187
|
+
the page says, so the grid becomes a `<ul>` and each card an `<li>`: a reader is
|
|
188
|
+
told there are three. Nothing is written to ask for this, the way nothing is
|
|
189
|
+
written to ask a run of `- ` for a list. A grid holding anything but cards is
|
|
190
|
+
left as a layout.
|
|
191
|
+
|
|
192
|
+
A Markdown list, meanwhile, only ever runs down the page, and there is no way to
|
|
193
|
+
put a class on one from here. This is how a list gets columns.
|
|
194
|
+
|
|
195
|
+
A card may end with `::card-footer`, for a date or a category — something that
|
|
196
|
+
belongs to the card but is not what it says.
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
:::card
|
|
200
|
+
::card-title[Card title]
|
|
201
|
+
::card-description[Lorem ipsum dolor sit amet.]
|
|
202
|
+
::card-footer[22 August 2026]
|
|
203
|
+
:::
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Card that is a link
|
|
207
|
+
|
|
208
|
+
Give the card an `href` and the whole of it becomes clickable. There is nothing
|
|
209
|
+
else an href on a card could mean, so it is not also asked for by name.
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
:::card{href=/services}
|
|
213
|
+
::card-title[Services and applications]
|
|
214
|
+
::card-description[Lorem ipsum dolor sit amet.]
|
|
215
|
+
::card-footer[22 August 2026]
|
|
216
|
+
:::
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The link itself goes on the title, and covers the card from there; the footer
|
|
220
|
+
grows an arrow. Only the title is read out as the name of the link, so write one
|
|
221
|
+
that says where it goes — a screen reader user meets it on its own, out of the
|
|
222
|
+
order the page is in.
|
|
223
|
+
|
|
224
|
+
That also means a link card wants a title. Without one there is no link, and the
|
|
225
|
+
card is only a box.
|
|
226
|
+
|
|
103
227
|
### FAQ
|
|
104
228
|
```
|
|
105
229
|
:::faq
|
|
@@ -127,3 +251,115 @@ You can contact the apprenticeship office through our official phone hotline abo
|
|
|
127
251
|
::::
|
|
128
252
|
```
|
|
129
253
|
<img width="1133" alt="スクリーンショット 2024-07-26 23 59 50" src="https://github.com/user-attachments/assets/0f6b51fb-068e-4173-8011-f782c1b894f6">
|
|
254
|
+
|
|
255
|
+
## How it is put together
|
|
256
|
+
|
|
257
|
+
Markdown becomes HTML in three steps, and each of them owns one thing.
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
markdown
|
|
261
|
+
│
|
|
262
|
+
│ src/render.ts builds a Marked for this call and runs the three steps
|
|
263
|
+
▼
|
|
264
|
+
marked ── src/marked/ the ::: syntax, as a marked extension
|
|
265
|
+
│
|
|
266
|
+
▼
|
|
267
|
+
HTML string
|
|
268
|
+
│
|
|
269
|
+
│ src/sanitize.ts takes out what must not reach the DOM
|
|
270
|
+
▼
|
|
271
|
+
DocumentFragment
|
|
272
|
+
│
|
|
273
|
+
│ src/decorate.ts puts the design system on it, collects the headings
|
|
274
|
+
▼
|
|
275
|
+
HTML string + headings
|
|
276
|
+
│
|
|
277
|
+
│ src/components/Markdown.tsx renders it, draws the table of contents
|
|
278
|
+
▼
|
|
279
|
+
page
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
| | |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `render.ts` | The entry point. A fresh `Marked` per call, so `marked.use()` never reaches another caller. |
|
|
285
|
+
| `sanitize.ts` | The DOMPurify configuration: which tags, which attributes, which CSS properties. Returns a fragment rather than a string, since the next step has to walk it. |
|
|
286
|
+
| `decorate.ts` | Runs over the finished document. Adds the classes, wraps tables so they scroll, marks up links that leave the page, generates the heading ids and collects them for the table of contents. |
|
|
287
|
+
| `components/Markdown.tsx` | The React side: the table of contents, the markup, and the jump to the anchor in the URL. |
|
|
288
|
+
|
|
289
|
+
Inside `src/marked/`:
|
|
290
|
+
|
|
291
|
+
| | |
|
|
292
|
+
|---|---|
|
|
293
|
+
| `registry.ts` | The list of directive names. **Adding or removing a directive starts here**; a name that is not in it is left as the text it is. |
|
|
294
|
+
| `tokenizer.ts` | Reads `:::name{attrs}` into tokens. Nesting needs no bookkeeping: the closing fence is the first line of at least as many colons, and an inner directive uses fewer. |
|
|
295
|
+
| `renderer.ts` | Builds what a renderer is given, then hands the token to the one that knows it. |
|
|
296
|
+
| `directives/` | The renderers, a file to a feature. `card.ts` holds the card and everything written inside one, `grid.ts` the columns and the cells, and so on. |
|
|
297
|
+
| `attributes.ts` | `{key=value}` and the balanced reader for `[label]`, which may hold brackets of its own. |
|
|
298
|
+
| `html.ts` | Escaping and URL cleaning. Nothing else builds an attribute by hand. |
|
|
299
|
+
| `index.ts` | Registers the block and inline extensions under one name, since marked looks the renderer up by the token type. |
|
|
300
|
+
|
|
301
|
+
Styling happens in `decorate.ts` rather than in `renderer.ts` so that a table
|
|
302
|
+
written as raw HTML looks like one written with pipes. The class strings come
|
|
303
|
+
from `@sakura-ui/core`, which exports them under a `styles` namespace; keeping a
|
|
304
|
+
copy here would mean the two drifting apart the first time a component is
|
|
305
|
+
restyled.
|
|
306
|
+
|
|
307
|
+
## Adding a directive
|
|
308
|
+
|
|
309
|
+
There are no plugins. The pipeline this replaced had one per directive; here it
|
|
310
|
+
is two files, and the tokenizer is shared.
|
|
311
|
+
|
|
312
|
+
**1. Name it in `registry.ts`.** Nothing outside this list is a directive, which
|
|
313
|
+
is why prose keeps its colons: `HH:MM` stays `HH:MM` because `MM` is not here.
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
const STATIC: Record<string, DirectiveKind[]> = {
|
|
317
|
+
callout: [CONTAINER],
|
|
318
|
+
...
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The kinds decide how it is written, and a name may take more than one — `faq-q`
|
|
323
|
+
is both a `LEAF` and a `CONTAINER`, so a short question fits on one line and a
|
|
324
|
+
long one does not have to.
|
|
325
|
+
|
|
326
|
+
| | Written | |
|
|
327
|
+
|---|---|---|
|
|
328
|
+
| `TEXT` | `:name[label]{attrs}` | Inside a sentence. |
|
|
329
|
+
| `LEAF` | `::name[label]{attrs}` | A line of its own, with nothing inside it. |
|
|
330
|
+
| `CONTAINER` | `:::name{attrs}` … `:::` | Holds Markdown, directives included. |
|
|
331
|
+
|
|
332
|
+
**2. Render it in `directives/`.** A file to a feature, each exporting a map
|
|
333
|
+
from name to a function returning a string. Put a new one beside the directive
|
|
334
|
+
it belongs with, or in a file of its own if it belongs with nothing, and add it
|
|
335
|
+
to `directives/index.ts`.
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
// directives/callout.ts
|
|
339
|
+
export const calloutRenderers: Record<string, DirectiveRenderer> = {
|
|
340
|
+
callout: ({ attrs, root, body, nl }) =>
|
|
341
|
+
`<aside${root({ class: classNames(styles.calloutStyle, attrs.class) })}>${body()}</aside>${nl}`
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The renderer is handed a context. Three of its fields do most of the work, and
|
|
346
|
+
using them is what keeps a new directive behaving like the others:
|
|
347
|
+
|
|
348
|
+
| | |
|
|
349
|
+
|---|---|
|
|
350
|
+
| `root(…)` | Attributes for the element the directive **is**. Adds the id from `{#name}`, and `data-sakura`, which tells `decorate.ts` the element is dressed already and to leave its classes alone. Headings are the exception: one rendered by a directive still has an id generated for it and still reaches the table of contents, which is how a card title gets there. |
|
|
351
|
+
| `own(…)` | The same without the id, for elements **inside** it. A link card's title is a heading around an anchor; only the heading is the directive. |
|
|
352
|
+
| `body()` | The children, already rendered — inline for a `TEXT` or `LEAF`, block for a `CONTAINER`. |
|
|
353
|
+
|
|
354
|
+
Classes come from `styles`, never written out here, or the directive stops
|
|
355
|
+
looking like the component the first time that component is restyled. URLs go
|
|
356
|
+
through `cleanUrl` and every attribute through `attrsToHtml`, both in `html.ts`;
|
|
357
|
+
nothing builds an attribute by hand.
|
|
358
|
+
|
|
359
|
+
**3. Anything a container has to know about its children** belongs in
|
|
360
|
+
`tokenizer.ts`, where they are still separate tokens. Once the renderers have run
|
|
361
|
+
they are one string with no seams. That is where a card is told it sits in a
|
|
362
|
+
grid of cards, and where a link card's title is handed the href.
|
|
363
|
+
|
|
364
|
+
Then add it to `tests/fixtures.ts`, which every snapshot test walks, and to the
|
|
365
|
+
document in `examples/src/pages/markdownSample.ts`.
|