@riebeckite/plugin-sidenotes 0.0.31 → 0.0.32
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/README.md +75 -51
- package/README_ja.md +73 -52
- package/dist/index.d.ts +4 -6
- package/dist/index.js +32 -118
- package/dist/src/options.d.ts +1 -2
- package/dist/src/render.d.ts +6 -10
- package/dist/src/types.d.ts +12 -18
- package/package.json +2 -8
- package/style.css +55 -54
- package/dist/client.d.ts +0 -2
- package/dist/client.js +0 -81
- package/dist/src/client.d.ts +0 -17
package/README.md
CHANGED
|
@@ -2,33 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- Generated from docs/docs/plugins/sidenotes.md. Edit the canonical documentation in docs/docs/plugins and run `pnpm docs:sync`. -->
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
footnote
|
|
8
|
-
|
|
5
|
+
Footnote styling for Riebeckite. Authors keep writing ordinary GFM footnotes
|
|
6
|
+
(`[^1]` and `[^1]: text`). By default the definitions stay in the trailing
|
|
7
|
+
footnote list at the end of the article. Set `placement: "margin"` to render
|
|
8
|
+
them as Tufte-style margin notes instead.
|
|
9
9
|
|
|
10
10
|
[日本語](./README_ja.md)
|
|
11
11
|
|
|
12
12
|
## Overview
|
|
13
13
|
|
|
14
|
-
The plugin registers a rehype plugin that
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
The plugin registers a rehype plugin that reads the footnote markup the core
|
|
15
|
+
pipeline produces from `remark-gfm`.
|
|
16
|
+
|
|
17
|
+
With the default `placement: "end"`:
|
|
18
|
+
|
|
19
|
+
- The trailing `<section data-footnotes>` gets `.rr-sidenotes__footnotes` and
|
|
20
|
+
`data-rr-sidenotes-placement="end"`.
|
|
21
|
+
- Each reference gets `rr-sidenotes__ref` on its `sup` and
|
|
22
|
+
`rr-sidenotes__toggle` on the link. The native `href`, `id`, and back-links
|
|
23
|
+
are left untouched, so navigation keeps working without JavaScript.
|
|
24
|
+
- The section stays at the end of the article at every width.
|
|
25
|
+
|
|
26
|
+
With `placement: "margin"`:
|
|
27
|
+
|
|
28
|
+
- Each definition becomes a `.rr-sidenotes__note` aside after the nearest block
|
|
29
|
+
that contains its first reference. The note stays in the article flow until
|
|
30
|
+
there is room for an article rail, then becomes sticky in the right margin
|
|
31
|
+
(`@media (min-width: 88rem)`).
|
|
32
|
+
- The reference points at the note (`href="#rr-sidenotes-…"`).
|
|
33
|
+
- The trailing definitions section is hidden with
|
|
34
|
+
`data-rr-sidenotes-placement="margin"`.
|
|
35
|
+
|
|
36
|
+
The plugin ships no client entry; both placements work without JavaScript.
|
|
32
37
|
|
|
33
38
|
## Usage
|
|
34
39
|
|
|
@@ -45,53 +50,73 @@ export default defineConfig({
|
|
|
45
50
|
Write normal GFM footnotes:
|
|
46
51
|
|
|
47
52
|
```md
|
|
48
|
-
Riebeckite
|
|
53
|
+
Riebeckite keeps notes in the footnote list at the end.[^1]
|
|
54
|
+
|
|
55
|
+
[^1]: The note text appears at the end of the article.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
To render margin notes instead:
|
|
49
59
|
|
|
50
|
-
|
|
60
|
+
```ts
|
|
61
|
+
plugins: [sidenotes({ placement: "margin" })];
|
|
51
62
|
```
|
|
52
63
|
|
|
53
64
|
## Options
|
|
54
65
|
|
|
55
66
|
| Option | Type | Default | Description |
|
|
56
67
|
| ------ | ---- | ------- | ----------- |
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `popoverAlignment` | `"bottom" \| "end"` | `"bottom"` | Where the mobile popover is anchored |
|
|
68
|
+
| `placement` | `"end" \| "margin"` | `"end"` | `"end"` keeps the trailing footnote list; `"margin"` moves definitions next to their references |
|
|
69
|
+
| `className` | `string` | `""` | Extra CSS class on each margin note (ignored when `placement` is `"end"`) |
|
|
70
|
+
| `ariaLabel` | `string` | `"Sidenote"` | Accessible name prefix for each margin note (`"Sidenote 1"`) |
|
|
71
|
+
| `openLabel` | `string` | `"Footnote"` | Accessible label prefix for each reference (`"Footnote 1"`) |
|
|
62
72
|
|
|
63
73
|
```ts
|
|
64
74
|
sidenotes({
|
|
75
|
+
placement: "margin",
|
|
65
76
|
ariaLabel: "Note",
|
|
66
|
-
popoverAlignment: "end",
|
|
67
77
|
});
|
|
68
78
|
```
|
|
69
79
|
|
|
70
80
|
## Output
|
|
71
81
|
|
|
82
|
+
Default (`placement: "end"`):
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<p>
|
|
86
|
+
Riebeckite keeps notes here.<sup class="rr-sidenotes__ref">
|
|
87
|
+
<a href="#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref
|
|
88
|
+
class="rr-sidenotes__toggle" aria-label="Footnote 1">1</a></sup>
|
|
89
|
+
</p>
|
|
90
|
+
<section data-footnotes class="footnotes rr-sidenotes__footnotes"
|
|
91
|
+
data-rr-sidenotes-placement="end">
|
|
92
|
+
<h2 class="sr-only" id="footnote-label">Footnotes</h2>
|
|
93
|
+
<ol><li id="user-content-fn-1"><p>The note text.</p></li></ol>
|
|
94
|
+
</section>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`placement: "margin"`:
|
|
98
|
+
|
|
72
99
|
```html
|
|
73
100
|
<p>
|
|
74
|
-
Riebeckite
|
|
75
|
-
<a href="#
|
|
76
|
-
data-rr-sidenotes-ref aria-
|
|
77
|
-
aria-label="Footnote 1">1</a></sup>
|
|
101
|
+
Riebeckite keeps notes here.<sup class="rr-sidenotes__ref">
|
|
102
|
+
<a href="#rr-sidenotes-1" id="user-content-fnref-1" class="rr-sidenotes__toggle"
|
|
103
|
+
data-rr-sidenotes-ref aria-label="Footnote 1">1</a></sup>
|
|
78
104
|
</p>
|
|
79
|
-
<aside class="rr-sidenotes__note
|
|
105
|
+
<aside class="rr-sidenotes__note" id="rr-sidenotes-1"
|
|
80
106
|
data-rr-sidenotes-note aria-label="Sidenote 1" tabindex="-1">
|
|
81
107
|
<span class="rr-sidenotes__index" aria-hidden="true">1</span>
|
|
82
|
-
<div class="rr-sidenotes__body"><p>The note text
|
|
108
|
+
<div class="rr-sidenotes__body"><p>The note text.</p></div>
|
|
83
109
|
</aside>
|
|
84
110
|
```
|
|
85
111
|
|
|
86
112
|
## Accessibility
|
|
87
113
|
|
|
88
|
-
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
as in ordinary GFM output.
|
|
114
|
+
- With `placement: "end"`, references keep the native GFM href, id, and
|
|
115
|
+
back-links; only the `aria-label` from `openLabel` is added.
|
|
116
|
+
- With `placement: "margin"`, the reference links to the note, and the note is
|
|
117
|
+
labelled with `aria-label` and can receive focus (`tabindex="-1"`).
|
|
118
|
+
- The footnote list and references use `scroll-margin-block-start` so the
|
|
119
|
+
target is not hidden behind a sticky header.
|
|
95
120
|
|
|
96
121
|
## Style
|
|
97
122
|
|
|
@@ -102,8 +127,8 @@ import "@riebeckite/plugin-sidenotes/style.css";
|
|
|
102
127
|
```
|
|
103
128
|
|
|
104
129
|
Stable hooks follow the `rr-sidenotes` convention: `rr-sidenotes__toggle`,
|
|
105
|
-
`rr-
|
|
106
|
-
`rr-
|
|
130
|
+
`rr-sidenotes__ref`, `rr-sidenotes__note`, `rr-sidenotes__index`,
|
|
131
|
+
`rr-sidenotes__body`, and `rr-sidenotes__footnotes`.
|
|
107
132
|
|
|
108
133
|
## Exports
|
|
109
134
|
|
|
@@ -112,15 +137,14 @@ Stable hooks follow the `rr-sidenotes` convention: `rr-sidenotes__toggle`,
|
|
|
112
137
|
- `rehypeSidenotes(options?)` — the rehype transformer
|
|
113
138
|
- `resolveSidenotesOptions(options?)` — apply option defaults
|
|
114
139
|
- `renderSidenotesReference(input)` / `renderSidenotesNote(input)` — HTML builders
|
|
115
|
-
- `
|
|
116
|
-
- Types: `SidenotesOptions`, `ResolvedSidenotesOptions`, `SidenotesClientOptions`
|
|
140
|
+
- Types: `SidenotesOptions`, `ResolvedSidenotesOptions`
|
|
117
141
|
|
|
118
142
|
## Limitations
|
|
119
143
|
|
|
120
|
-
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
restyle `.rr-sidenotes__note` freely.
|
|
144
|
+
- With `placement: "margin"`, footnotes that reuse the same definition share
|
|
145
|
+
one note.
|
|
146
|
+
- In margin placement the trailing definitions section stays in the DOM but is
|
|
147
|
+
hidden; themes can restyle `.rr-sidenotes__note` freely.
|
|
124
148
|
- Notes inside tables or deeply nested inline markup are placed after the
|
|
125
149
|
nearest block ancestor, so their vertical position is approximate.
|
|
126
150
|
|
package/README_ja.md
CHANGED
|
@@ -2,35 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- Generated from docs/docs/plugins/sidenotes.ja.md. Edit the canonical documentation in docs/docs/plugins and run `pnpm docs:sync`. -->
|
|
4
4
|
|
|
5
|
-
Riebeckite
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
換えます。
|
|
5
|
+
Riebeckite 用の脚注スタイル・プラグインです。執筆者は通常の GFM 脚注
|
|
6
|
+
(`[^1]` と `[^1]: 本文`)をそのまま書き続けます。既定では脚注定義は記事末尾の
|
|
7
|
+
脚注リストに残ります。`placement: "margin"` を指定すると、Tufte スタイルの
|
|
8
|
+
マージンノートとして参照の横に表示します。
|
|
10
9
|
|
|
11
10
|
[English](./README.md)
|
|
12
11
|
|
|
13
12
|
## 仕組み
|
|
14
13
|
|
|
15
14
|
このプラグインは、`remark-gfm` の出力をコアのパイプラインが脚注マークアップ
|
|
16
|
-
に変換したものを、rehype
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
15
|
+
に変換したものを、rehype プラグインで読み取ります。
|
|
16
|
+
|
|
17
|
+
既定の `placement: "end"` では:
|
|
18
|
+
|
|
19
|
+
- 文末の `<section data-footnotes>` に `.rr-sidenotes__footnotes` と
|
|
20
|
+
`data-rr-sidenotes-placement="end"` が付きます。
|
|
21
|
+
- 各参照は `sup` に `rr-sidenotes__ref`、リンクに
|
|
22
|
+
`rr-sidenotes__toggle` が付きます。元の `href`、`id`、バックリンクはその
|
|
23
|
+
まま残るため、JavaScript なしで遷移できます。
|
|
24
|
+
- セクションは画面幅にかかわらず記事末尾に表示されます。
|
|
25
|
+
|
|
26
|
+
`placement: "margin"` では:
|
|
27
|
+
|
|
28
|
+
- 各定義は、最初の参照を含む最も近いブロックの後ろに
|
|
29
|
+
`.rr-sidenotes__note` の aside として置かれます。記事の横に置ける幅
|
|
30
|
+
(`@media (min-width: 88rem)`)までは本文フロー内に表示し、それ以上の幅では
|
|
31
|
+
右マージンに sticky で表示します。
|
|
32
|
+
- 参照のリンク先はノート(`href="#rr-sidenotes-…"`)になります。
|
|
33
|
+
- 文末の脚注定義セクションは
|
|
34
|
+
`data-rr-sidenotes-placement="margin"` で非表示になります。
|
|
35
|
+
|
|
36
|
+
クライアントエントリはありません。どちらの配置も JavaScript なしで動作します。
|
|
34
37
|
|
|
35
38
|
## 使い方
|
|
36
39
|
|
|
@@ -47,53 +50,73 @@ export default defineConfig({
|
|
|
47
50
|
通常の GFM 脚注を書くだけです。
|
|
48
51
|
|
|
49
52
|
```md
|
|
50
|
-
Riebeckite
|
|
53
|
+
Riebeckite は脚注を記事末尾のリストに表示します。[^1]
|
|
51
54
|
|
|
52
|
-
[^1]:
|
|
55
|
+
[^1]: 記事の最後に注記が表示されます。
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
マージンノートにする場合:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
plugins: [sidenotes({ placement: "margin" })];
|
|
53
62
|
```
|
|
54
63
|
|
|
55
64
|
## オプション
|
|
56
65
|
|
|
57
66
|
| オプション | 型 | 既定値 | 説明 |
|
|
58
67
|
| ---------- | -- | ------ | ---- |
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `popoverAlignment` | `"bottom" \| "end"` | `"bottom"` | モバイルのポップオーバーの表示位置 |
|
|
68
|
+
| `placement` | `"end" \| "margin"` | `"end"` | `"end"` は文末の脚注リスト、`"margin"` は参照の横のマージンノート |
|
|
69
|
+
| `className` | `string` | `""` | 各マージンノートに追加する CSS クラス(`"end"` では無視) |
|
|
70
|
+
| `ariaLabel` | `string` | `"Sidenote"` | 各マージンノートのアクセシブル名の接頭辞(`"Sidenote 1"` の形) |
|
|
71
|
+
| `openLabel` | `string` | `"Footnote"` | 各参照リンクのアクセシブル名の接頭辞(`"Footnote 1"`) |
|
|
64
72
|
|
|
65
73
|
```ts
|
|
66
74
|
sidenotes({
|
|
75
|
+
placement: "margin",
|
|
67
76
|
ariaLabel: "注記",
|
|
68
|
-
popoverAlignment: "end",
|
|
69
77
|
});
|
|
70
78
|
```
|
|
71
79
|
|
|
72
80
|
## 出力
|
|
73
81
|
|
|
82
|
+
既定(`placement: "end"`):
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<p>
|
|
86
|
+
Riebeckite は脚注を記事末尾に表示します。<sup class="rr-sidenotes__ref">
|
|
87
|
+
<a href="#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref
|
|
88
|
+
class="rr-sidenotes__toggle" aria-label="Footnote 1">1</a></sup>
|
|
89
|
+
</p>
|
|
90
|
+
<section data-footnotes class="footnotes rr-sidenotes__footnotes"
|
|
91
|
+
data-rr-sidenotes-placement="end">
|
|
92
|
+
<h2 class="sr-only" id="footnote-label">Footnotes</h2>
|
|
93
|
+
<ol><li id="user-content-fn-1"><p>記事の最後に注記が表示されます。</p></li></ol>
|
|
94
|
+
</section>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`placement: "margin"`:
|
|
98
|
+
|
|
74
99
|
```html
|
|
75
100
|
<p>
|
|
76
|
-
Riebeckite
|
|
77
|
-
<a href="#
|
|
78
|
-
data-rr-sidenotes-ref aria-
|
|
79
|
-
aria-label="Footnote 1">1</a></sup>
|
|
101
|
+
Riebeckite は参照の横に注記を表示します。<sup class="rr-sidenotes__ref">
|
|
102
|
+
<a href="#rr-sidenotes-1" id="user-content-fnref-1" class="rr-sidenotes__toggle"
|
|
103
|
+
data-rr-sidenotes-ref aria-label="Footnote 1">1</a></sup>
|
|
80
104
|
</p>
|
|
81
|
-
<aside class="rr-sidenotes__note
|
|
105
|
+
<aside class="rr-sidenotes__note" id="rr-sidenotes-1"
|
|
82
106
|
data-rr-sidenotes-note aria-label="Sidenote 1" tabindex="-1">
|
|
83
107
|
<span class="rr-sidenotes__index" aria-hidden="true">1</span>
|
|
84
|
-
<div class="rr-sidenotes__body"><p
|
|
108
|
+
<div class="rr-sidenotes__body"><p>参照の横に表示される注記です。</p></div>
|
|
85
109
|
</aside>
|
|
86
110
|
```
|
|
87
111
|
|
|
88
112
|
## アクセシビリティ
|
|
89
113
|
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
します。
|
|
114
|
+
- `placement: "end"` では、参照は GFM 標準の `href`、`id`、バックリンクを
|
|
115
|
+
そのまま保ち、`openLabel` から作った `aria-label` だけを追加します。
|
|
116
|
+
- `placement: "margin"` では、参照はノートへリンクします。ノートは
|
|
117
|
+
`aria-label` でラベル付けされ、`tabindex="-1"` でフォーカスを受け取れます。
|
|
118
|
+
- 脚注リストと参照には `scroll-margin-block-start` を指定し、sticky
|
|
119
|
+
ヘッダーに隠れないようにしています。
|
|
97
120
|
|
|
98
121
|
## スタイル
|
|
99
122
|
|
|
@@ -104,8 +127,8 @@ import "@riebeckite/plugin-sidenotes/style.css";
|
|
|
104
127
|
```
|
|
105
128
|
|
|
106
129
|
安定フックは `rr-sidenotes` 規約に従います。`rr-sidenotes__toggle`、
|
|
107
|
-
`rr-
|
|
108
|
-
`rr-
|
|
130
|
+
`rr-sidenotes__ref`、`rr-sidenotes__note`、`rr-sidenotes__index`、
|
|
131
|
+
`rr-sidenotes__body`、`rr-sidenotes__footnotes` です。
|
|
109
132
|
|
|
110
133
|
## エクスポート
|
|
111
134
|
|
|
@@ -114,16 +137,14 @@ import "@riebeckite/plugin-sidenotes/style.css";
|
|
|
114
137
|
- `rehypeSidenotes(options?)`:rehype トランスフォーマー
|
|
115
138
|
- `resolveSidenotesOptions(options?)`:オプションの既定値を適用する
|
|
116
139
|
- `renderSidenotesReference(input)` / `renderSidenotesNote(input)`:HTML 生成関数
|
|
117
|
-
- `
|
|
118
|
-
- 型: `SidenotesOptions`, `ResolvedSidenotesOptions`, `SidenotesClientOptions`
|
|
140
|
+
- 型: `SidenotesOptions`, `ResolvedSidenotesOptions`
|
|
119
141
|
|
|
120
142
|
## 制約
|
|
121
143
|
|
|
122
|
-
-
|
|
144
|
+
- `placement: "margin"` では、同じ定義を再利用した脚注は 1 つのノートを共有
|
|
123
145
|
します。
|
|
124
|
-
-
|
|
125
|
-
|
|
126
|
-
再スタイルできます。
|
|
146
|
+
- マージン配置では、文末の脚注定義セクションは DOM に残りますが非表示に
|
|
147
|
+
なります。テーマは `.rr-sidenotes__note` を自由に再スタイルできます。
|
|
127
148
|
- テーブル内や深く入れ子になったインライン要素の中の注は、最も近いブロック
|
|
128
149
|
要素の後ろに置かれるため、縦位置はおおよその位置になります。
|
|
129
150
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import type { SidenotesOptions } from "./src/types.js";
|
|
2
|
-
export type { SidenotesClientOptions } from "./src/client.js";
|
|
3
|
-
export { initSidenotes } from "./src/client.js";
|
|
4
2
|
export { resolveSidenotesOptions } from "./src/options.js";
|
|
5
3
|
export { rehypeSidenotes } from "./src/rehype.js";
|
|
6
4
|
export { renderHastChildren, renderSidenotesNote, renderSidenotesReference, sidenotesKey, sidenotesNoteId, } from "./src/render.js";
|
|
7
5
|
export type { ResolvedSidenotesOptions, SidenotesOptions, } from "./src/types.js";
|
|
8
6
|
export declare const SIDENOTES_PLUGIN_NAME = "sidenotes";
|
|
9
7
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* Styles GFM footnotes for Riebeckite. By default the definitions stay in the
|
|
9
|
+
* trailing footnote list at the end of the article. With
|
|
10
|
+
* `placement: "margin"`, a rehype plugin rewrites the generated footnote
|
|
11
|
+
* markup into an inline reference plus a margin note.
|
|
14
12
|
*/
|
|
15
13
|
export declare function sidenotes(options?: SidenotesOptions): import("@riebeckite/core").RiebeckitePlugin<SidenotesOptions>;
|
|
16
14
|
/** Alias matching the `*Plugin` suffix used by other plugin factories. */
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// index.ts
|
|
2
2
|
import {
|
|
3
|
-
createClientEntry,
|
|
4
3
|
createStyleAsset,
|
|
5
4
|
definePlugin
|
|
6
5
|
} from "@riebeckite/core";
|
|
@@ -8,8 +7,7 @@ import {
|
|
|
8
7
|
// src/options.ts
|
|
9
8
|
var DEFAULT_SIDENOTES_ARIA_LABEL = "Sidenote";
|
|
10
9
|
var DEFAULT_SIDENOTES_OPEN_LABEL = "Footnote";
|
|
11
|
-
var
|
|
12
|
-
var DEFAULT_SIDENOTES_POPOVER_ALIGNMENT = "bottom";
|
|
10
|
+
var DEFAULT_SIDENOTES_PLACEMENT = "end";
|
|
13
11
|
function resolveSidenotesOptions(options = {}) {
|
|
14
12
|
return {
|
|
15
13
|
className: normalizeNonEmpty(options.className, ""),
|
|
@@ -21,13 +19,9 @@ function resolveSidenotesOptions(options = {}) {
|
|
|
21
19
|
options.openLabel,
|
|
22
20
|
DEFAULT_SIDENOTES_OPEN_LABEL
|
|
23
21
|
),
|
|
24
|
-
|
|
25
|
-
options.
|
|
26
|
-
|
|
27
|
-
),
|
|
28
|
-
popoverAlignment: normalizePopoverAlignment(
|
|
29
|
-
options.popoverAlignment,
|
|
30
|
-
DEFAULT_SIDENOTES_POPOVER_ALIGNMENT
|
|
22
|
+
placement: normalizePlacement(
|
|
23
|
+
options.placement,
|
|
24
|
+
DEFAULT_SIDENOTES_PLACEMENT
|
|
31
25
|
)
|
|
32
26
|
};
|
|
33
27
|
}
|
|
@@ -36,15 +30,15 @@ function normalizeNonEmpty(value, fallback) {
|
|
|
36
30
|
const trimmed = value.trim();
|
|
37
31
|
return trimmed === "" ? fallback : trimmed;
|
|
38
32
|
}
|
|
39
|
-
function
|
|
40
|
-
return value === "
|
|
33
|
+
function normalizePlacement(value, fallback) {
|
|
34
|
+
return value === "margin" ? "margin" : value === "end" ? "end" : fallback;
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
// src/render.ts
|
|
44
38
|
import { escapeHtml, escapeHtmlAttribute } from "@riebeckite/core";
|
|
39
|
+
var SIDENOTES_REF_CLASS = "rr-sidenotes__ref";
|
|
45
40
|
var SIDENOTES_TOGGLE_CLASS = "rr-sidenotes__toggle";
|
|
46
41
|
var SIDENOTES_NOTE_CLASS = "rr-sidenotes__note";
|
|
47
|
-
var SIDENOTES_POPOVER_PREFIX = "rr-sidenotes__note--popover-";
|
|
48
42
|
var SIDENOTES_INDEX_CLASS = "rr-sidenotes__index";
|
|
49
43
|
var SIDENOTES_BODY_CLASS = "rr-sidenotes__body";
|
|
50
44
|
var SIDENOTES_FOOTNOTES_CLASS = "rr-sidenotes__footnotes";
|
|
@@ -62,15 +56,15 @@ function sidenotesNoteId(key) {
|
|
|
62
56
|
}
|
|
63
57
|
function renderSidenotesReference(input) {
|
|
64
58
|
const { href, id, index, options } = input;
|
|
65
|
-
const key = sidenotesKey(href)
|
|
59
|
+
const key = sidenotesKey(href);
|
|
60
|
+
const target = key === null ? href : `#${sidenotesNoteId(key)}`;
|
|
66
61
|
const label = `${options.openLabel} ${index}`.trim();
|
|
67
|
-
return `<a href="${escapeHtmlAttribute(
|
|
62
|
+
return `<a href="${escapeHtmlAttribute(target)}" id="${escapeHtmlAttribute(id)}" class="${SIDENOTES_TOGGLE_CLASS}" data-rr-sidenotes-ref aria-label="${escapeHtmlAttribute(label)}">${escapeHtml(index)}</a>`;
|
|
68
63
|
}
|
|
69
64
|
function renderSidenotesNote(input) {
|
|
70
65
|
const { key, index, bodyHtml, options } = input;
|
|
71
66
|
const className = [
|
|
72
67
|
SIDENOTES_NOTE_CLASS,
|
|
73
|
-
`${SIDENOTES_POPOVER_PREFIX}${options.popoverAlignment}`,
|
|
74
68
|
...options.className === "" ? [] : [options.className]
|
|
75
69
|
].join(" ");
|
|
76
70
|
const label = `${options.ariaLabel} ${index}`.trim();
|
|
@@ -137,6 +131,7 @@ function hyphenateAttribute(name) {
|
|
|
137
131
|
var FOOTNOTE_REFERENCE_ATTRIBUTE = "dataFootnoteRef";
|
|
138
132
|
var FOOTNOTES_SECTION_ATTRIBUTE = "dataFootnotes";
|
|
139
133
|
var FOOTNOTE_ID_PREFIX_PATTERN = /^(?:user-content-)?fn-(.+)$/;
|
|
134
|
+
var PLACEMENT_ATTRIBUTE = "data-rr-sidenotes-placement";
|
|
140
135
|
var BLOCK_TAG_NAMES = /* @__PURE__ */ new Set([
|
|
141
136
|
"article",
|
|
142
137
|
"aside",
|
|
@@ -170,7 +165,6 @@ var BLOCK_TAG_NAMES = /* @__PURE__ */ new Set([
|
|
|
170
165
|
"tr",
|
|
171
166
|
"ul"
|
|
172
167
|
]);
|
|
173
|
-
var SIDENOTES_REF_CLASS = "rr-sidenotes__ref";
|
|
174
168
|
function rehypeSidenotes(options = {}) {
|
|
175
169
|
const resolved = resolveSidenotesOptions(options);
|
|
176
170
|
return (tree) => {
|
|
@@ -183,6 +177,14 @@ function transformSidenotes(tree, options) {
|
|
|
183
177
|
const notes = collectFootnoteNotes(section);
|
|
184
178
|
if (notes.size === 0) return;
|
|
185
179
|
addClassName(section, SIDENOTES_FOOTNOTES_CLASS);
|
|
180
|
+
section.properties = {
|
|
181
|
+
...section.properties,
|
|
182
|
+
[PLACEMENT_ATTRIBUTE]: options.placement
|
|
183
|
+
};
|
|
184
|
+
if (options.placement === "end") {
|
|
185
|
+
markReferences(tree, options);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
186
188
|
const insertions = [];
|
|
187
189
|
walkElements(tree, [], (ref, parent, index, ancestors) => {
|
|
188
190
|
if (!isFootnoteReference(ref)) return;
|
|
@@ -232,6 +234,14 @@ function transformSidenotes(tree, options) {
|
|
|
232
234
|
});
|
|
233
235
|
applyInsertions(insertions);
|
|
234
236
|
}
|
|
237
|
+
function markReferences(tree, options) {
|
|
238
|
+
walkElements(tree, [], (ref, parent) => {
|
|
239
|
+
if (parent.tagName === "sup") addClassName(parent, SIDENOTES_REF_CLASS);
|
|
240
|
+
addClassName(ref, SIDENOTES_TOGGLE_CLASS);
|
|
241
|
+
const label = `${options.openLabel} ${getTextContent(ref)}`.trim();
|
|
242
|
+
ref.properties = { ...ref.properties, "aria-label": label };
|
|
243
|
+
});
|
|
244
|
+
}
|
|
235
245
|
function findFootnoteSection(node) {
|
|
236
246
|
if (node.type === "element" && node.tagName === "section" && hasProperty(node, FOOTNOTES_SECTION_ATTRIBUTE)) {
|
|
237
247
|
return node;
|
|
@@ -369,85 +379,6 @@ function getChildren(node) {
|
|
|
369
379
|
return Array.isArray(children) ? children : [];
|
|
370
380
|
}
|
|
371
381
|
|
|
372
|
-
// src/client.ts
|
|
373
|
-
var DEFAULT_SELECTOR = "[data-rr-sidenotes-ref]";
|
|
374
|
-
var NOTE_OPEN_CLASS = "rr-sidenotes__note--open";
|
|
375
|
-
var DESKTOP_QUERY = "(min-width: 48rem)";
|
|
376
|
-
var DEFAULT_OPEN_LABEL = "Footnote";
|
|
377
|
-
var DEFAULT_CLOSE_LABEL = "Close sidenote";
|
|
378
|
-
function initSidenotes(options = {}) {
|
|
379
|
-
const desktopQuery = window.matchMedia(DESKTOP_QUERY);
|
|
380
|
-
if (desktopQuery.matches) return;
|
|
381
|
-
const toggles = Array.from(
|
|
382
|
-
document.querySelectorAll(
|
|
383
|
-
options.selector ?? DEFAULT_SELECTOR
|
|
384
|
-
)
|
|
385
|
-
);
|
|
386
|
-
if (toggles.length === 0) return;
|
|
387
|
-
const openLabel = options.openLabel ?? DEFAULT_OPEN_LABEL;
|
|
388
|
-
const closeLabel = options.closeLabel ?? DEFAULT_CLOSE_LABEL;
|
|
389
|
-
const noteFor = (toggle) => {
|
|
390
|
-
const controls = toggle.getAttribute("aria-controls");
|
|
391
|
-
if (!controls) return null;
|
|
392
|
-
const note = document.getElementById(controls);
|
|
393
|
-
return note instanceof HTMLElement ? note : null;
|
|
394
|
-
};
|
|
395
|
-
const isInsideSidenotes = (target) => {
|
|
396
|
-
if (!target) return false;
|
|
397
|
-
for (const toggle of toggles) {
|
|
398
|
-
const note = noteFor(toggle);
|
|
399
|
-
if (toggle.contains(target) || note?.contains(target)) return true;
|
|
400
|
-
}
|
|
401
|
-
return false;
|
|
402
|
-
};
|
|
403
|
-
const setOpen = (toggle, note, open) => {
|
|
404
|
-
const index = toggle.textContent?.trim() ?? "";
|
|
405
|
-
const label = open ? `${closeLabel} ${index}`.trim() : `${openLabel} ${index}`.trim();
|
|
406
|
-
toggle.setAttribute("aria-expanded", String(open));
|
|
407
|
-
toggle.setAttribute("aria-label", label);
|
|
408
|
-
note.classList.toggle(NOTE_OPEN_CLASS, open);
|
|
409
|
-
if (open) note.focus({ preventScroll: true });
|
|
410
|
-
};
|
|
411
|
-
const closeAll = () => {
|
|
412
|
-
for (const toggle of toggles) {
|
|
413
|
-
const note = noteFor(toggle);
|
|
414
|
-
if (note?.classList.contains(NOTE_OPEN_CLASS)) {
|
|
415
|
-
setOpen(toggle, note, false);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
for (const toggle of toggles) {
|
|
420
|
-
toggle.addEventListener("click", (event) => {
|
|
421
|
-
event.preventDefault();
|
|
422
|
-
const note = noteFor(toggle);
|
|
423
|
-
if (!note) return;
|
|
424
|
-
const isOpen = note.classList.contains(NOTE_OPEN_CLASS);
|
|
425
|
-
if (!isOpen) closeAll();
|
|
426
|
-
setOpen(toggle, note, !isOpen);
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
const onKeydown = (event) => {
|
|
430
|
-
if (event.key !== "Escape") return;
|
|
431
|
-
closeAll();
|
|
432
|
-
};
|
|
433
|
-
const onClickOutside = (event) => {
|
|
434
|
-
if (isInsideSidenotes(event.target instanceof Node ? event.target : null)) {
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
closeAll();
|
|
438
|
-
};
|
|
439
|
-
document.addEventListener("keydown", onKeydown);
|
|
440
|
-
document.addEventListener("click", onClickOutside);
|
|
441
|
-
const onViewportChange = (event) => {
|
|
442
|
-
if (!event.matches) return;
|
|
443
|
-
closeAll();
|
|
444
|
-
document.removeEventListener("keydown", onKeydown);
|
|
445
|
-
document.removeEventListener("click", onClickOutside);
|
|
446
|
-
desktopQuery.removeEventListener("change", onViewportChange);
|
|
447
|
-
};
|
|
448
|
-
desktopQuery.addEventListener("change", onViewportChange);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
382
|
// index.ts
|
|
452
383
|
var SIDENOTES_PLUGIN_NAME = "sidenotes";
|
|
453
384
|
function sidenotes(options = {}) {
|
|
@@ -455,7 +386,7 @@ function sidenotes(options = {}) {
|
|
|
455
386
|
return definePlugin({
|
|
456
387
|
name: SIDENOTES_PLUGIN_NAME,
|
|
457
388
|
processedContentCache: {
|
|
458
|
-
version: "sidenotes-
|
|
389
|
+
version: "sidenotes-v2",
|
|
459
390
|
dependencyMode: "none"
|
|
460
391
|
},
|
|
461
392
|
options,
|
|
@@ -466,17 +397,6 @@ function sidenotes(options = {}) {
|
|
|
466
397
|
SIDENOTES_PLUGIN_NAME,
|
|
467
398
|
"@riebeckite/plugin-sidenotes/style.css"
|
|
468
399
|
)
|
|
469
|
-
],
|
|
470
|
-
clientEntries: [
|
|
471
|
-
createClientEntry(
|
|
472
|
-
SIDENOTES_PLUGIN_NAME,
|
|
473
|
-
"@riebeckite/plugin-sidenotes/client",
|
|
474
|
-
"initSidenotes",
|
|
475
|
-
{
|
|
476
|
-
openLabel: resolved.openLabel,
|
|
477
|
-
closeLabel: resolved.closeLabel
|
|
478
|
-
}
|
|
479
|
-
)
|
|
480
400
|
]
|
|
481
401
|
});
|
|
482
402
|
}
|
|
@@ -484,28 +404,22 @@ var sidenotesPlugin = sidenotes;
|
|
|
484
404
|
function validateSidenotesOptions(options) {
|
|
485
405
|
if (!options) return [];
|
|
486
406
|
const issues = [];
|
|
487
|
-
for (const key of [
|
|
488
|
-
"className",
|
|
489
|
-
"ariaLabel",
|
|
490
|
-
"openLabel",
|
|
491
|
-
"closeLabel"
|
|
492
|
-
]) {
|
|
407
|
+
for (const key of ["className", "ariaLabel", "openLabel"]) {
|
|
493
408
|
const value = options[key];
|
|
494
409
|
if (value !== void 0 && (typeof value !== "string" || value.trim() === "")) {
|
|
495
410
|
issues.push({ path: key, message: "Expected a non-empty string." });
|
|
496
411
|
}
|
|
497
412
|
}
|
|
498
|
-
if (options.
|
|
413
|
+
if (options.placement !== void 0 && options.placement !== "end" && options.placement !== "margin") {
|
|
499
414
|
issues.push({
|
|
500
|
-
path: "
|
|
501
|
-
message: 'Expected "
|
|
415
|
+
path: "placement",
|
|
416
|
+
message: 'Expected "end" or "margin".'
|
|
502
417
|
});
|
|
503
418
|
}
|
|
504
419
|
return issues;
|
|
505
420
|
}
|
|
506
421
|
export {
|
|
507
422
|
SIDENOTES_PLUGIN_NAME,
|
|
508
|
-
initSidenotes,
|
|
509
423
|
rehypeSidenotes,
|
|
510
424
|
renderHastChildren,
|
|
511
425
|
renderSidenotesNote,
|
package/dist/src/options.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { ResolvedSidenotesOptions, SidenotesOptions } from "./types.js";
|
|
2
2
|
export declare const DEFAULT_SIDENOTES_ARIA_LABEL = "Sidenote";
|
|
3
3
|
export declare const DEFAULT_SIDENOTES_OPEN_LABEL = "Footnote";
|
|
4
|
-
export declare const
|
|
5
|
-
export declare const DEFAULT_SIDENOTES_POPOVER_ALIGNMENT = "bottom";
|
|
4
|
+
export declare const DEFAULT_SIDENOTES_PLACEMENT = "end";
|
|
6
5
|
/**
|
|
7
6
|
* Applies defaults to `SidenotesOptions`. Pure and deterministic so the
|
|
8
7
|
* plugin can resolve options once and reuse them for every processed tree.
|
package/dist/src/render.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { HastNode, ResolvedSidenotesOptions } from "./types.js";
|
|
|
3
3
|
export declare const SIDENOTES_REF_CLASS = "rr-sidenotes__ref";
|
|
4
4
|
export declare const SIDENOTES_TOGGLE_CLASS = "rr-sidenotes__toggle";
|
|
5
5
|
export declare const SIDENOTES_NOTE_CLASS = "rr-sidenotes__note";
|
|
6
|
-
export declare const SIDENOTES_POPOVER_PREFIX = "rr-sidenotes__note--popover-";
|
|
7
6
|
export declare const SIDENOTES_INDEX_CLASS = "rr-sidenotes__index";
|
|
8
7
|
export declare const SIDENOTES_BODY_CLASS = "rr-sidenotes__body";
|
|
9
8
|
export declare const SIDENOTES_FOOTNOTES_CLASS = "rr-sidenotes__footnotes";
|
|
@@ -11,10 +10,10 @@ export declare const SIDENOTES_NOTE_ID_PREFIX = "rr-sidenotes-";
|
|
|
11
10
|
/** Extracts the footnote key from a reference href. Returns `null` when the
|
|
12
11
|
* href does not look like a footnote target. */
|
|
13
12
|
export declare function sidenotesKey(href: string): string | null;
|
|
14
|
-
/** Stable id shared by a sidenote and its
|
|
13
|
+
/** Stable id shared by a sidenote and its reference links. */
|
|
15
14
|
export declare function sidenotesNoteId(key: string): string;
|
|
16
15
|
export type SidenotesReferenceInput = {
|
|
17
|
-
/** Existing `href` of the GFM footnote reference,
|
|
16
|
+
/** Existing `href` of the GFM footnote reference, used when no key exists. */
|
|
18
17
|
href: string;
|
|
19
18
|
/** Existing `id` of the GFM footnote reference, kept for back-links. */
|
|
20
19
|
id: string;
|
|
@@ -23,9 +22,9 @@ export type SidenotesReferenceInput = {
|
|
|
23
22
|
options: ResolvedSidenotesOptions;
|
|
24
23
|
};
|
|
25
24
|
/**
|
|
26
|
-
* Renders the inline reference link that replaces a GFM footnote reference
|
|
27
|
-
* The surrounding `sup` is managed by the rehype transformer
|
|
28
|
-
* the existing wrapper when one is already present.
|
|
25
|
+
* Renders the inline reference link that replaces a GFM footnote reference in
|
|
26
|
+
* margin placement. The surrounding `sup` is managed by the rehype transformer
|
|
27
|
+
* so it can reuse the existing wrapper when one is already present.
|
|
29
28
|
*/
|
|
30
29
|
export declare function renderSidenotesReference(input: SidenotesReferenceInput): string;
|
|
31
30
|
export type SidenotesNoteInput = {
|
|
@@ -35,10 +34,7 @@ export type SidenotesNoteInput = {
|
|
|
35
34
|
bodyHtml: string;
|
|
36
35
|
options: ResolvedSidenotesOptions;
|
|
37
36
|
};
|
|
38
|
-
/**
|
|
39
|
-
* Renders the sidenote: a static margin note on desktop and the popover
|
|
40
|
-
* source on mobile.
|
|
41
|
-
*/
|
|
37
|
+
/** Renders the margin note definition as an `aside` next to its reference. */
|
|
42
38
|
export declare function renderSidenotesNote(input: SidenotesNoteInput): string;
|
|
43
39
|
/**
|
|
44
40
|
* Serializes existing hast children into the HTML embedded in a sidenote
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,40 +1,34 @@
|
|
|
1
1
|
/** Options accepted by `sidenotes()`. */
|
|
2
2
|
export type SidenotesOptions = {
|
|
3
3
|
/**
|
|
4
|
-
* Extra CSS class appended to each
|
|
5
|
-
* `rr-sidenotes__note
|
|
4
|
+
* Extra CSS class appended to each margin note root, on top of
|
|
5
|
+
* `rr-sidenotes__note`. Ignored when `placement` is `"end"`.
|
|
6
6
|
*/
|
|
7
7
|
className?: string;
|
|
8
8
|
/**
|
|
9
|
-
* Accessible name prefix for each
|
|
10
|
-
* `"${ariaLabel} ${index}"`.
|
|
9
|
+
* Accessible name prefix for each margin note. The rendered label is
|
|
10
|
+
* `"${ariaLabel} ${index}"`. Ignored when `placement` is `"end"`.
|
|
11
11
|
*/
|
|
12
12
|
ariaLabel?: string;
|
|
13
13
|
/**
|
|
14
|
-
* Accessible label prefix for a reference link
|
|
15
|
-
*
|
|
16
|
-
* `"Footnote"`.
|
|
14
|
+
* Accessible label prefix for a reference link. The rendered label is
|
|
15
|
+
* `"${openLabel} ${index}"`. Defaults to `"Footnote"`.
|
|
17
16
|
*/
|
|
18
17
|
openLabel?: string;
|
|
19
18
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* Where footnote definitions render. `"end"` keeps the GFM footnote list at
|
|
20
|
+
* the end of the article and only styles the references. `"margin"` moves
|
|
21
|
+
* each definition next to its reference as a margin note. Defaults to
|
|
22
|
+
* `"end"`.
|
|
23
23
|
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Where the mobile popover is anchored. Defaults to `"bottom"` (a bottom
|
|
27
|
-
* sheet); `"end"` is a right-edge panel.
|
|
28
|
-
*/
|
|
29
|
-
popoverAlignment?: "bottom" | "end";
|
|
24
|
+
placement?: "end" | "margin";
|
|
30
25
|
};
|
|
31
26
|
/** `SidenotesOptions` with every default applied. */
|
|
32
27
|
export type ResolvedSidenotesOptions = {
|
|
33
28
|
className: string;
|
|
34
29
|
ariaLabel: string;
|
|
35
30
|
openLabel: string;
|
|
36
|
-
|
|
37
|
-
popoverAlignment: "bottom" | "end";
|
|
31
|
+
placement: "end" | "margin";
|
|
38
32
|
};
|
|
39
33
|
/** Minimal hast element shape the sidenotes transformer reads and emits. */
|
|
40
34
|
export type HastElementNode = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-sidenotes",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.32",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -12,16 +12,10 @@
|
|
|
12
12
|
"source": "./index.ts",
|
|
13
13
|
"default": "./dist/index.js"
|
|
14
14
|
},
|
|
15
|
-
"./client": {
|
|
16
|
-
"types": "./dist/client.d.ts",
|
|
17
|
-
"import": "./dist/client.js",
|
|
18
|
-
"source": "./client.ts",
|
|
19
|
-
"default": "./dist/client.js"
|
|
20
|
-
},
|
|
21
15
|
"./style.css": "./style.css"
|
|
22
16
|
},
|
|
23
17
|
"dependencies": {
|
|
24
|
-
"@riebeckite/core": "0.0.
|
|
18
|
+
"@riebeckite/core": "0.0.32"
|
|
25
19
|
},
|
|
26
20
|
"publishConfig": {
|
|
27
21
|
"access": "public"
|
package/style.css
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* @riebeckite/plugin-sidenotes
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* `placement: "end"` (default) keeps the GFM footnote list at the end of the
|
|
5
|
+
* article and only styles the references. `placement: "margin"` collects the
|
|
6
|
+
* definitions into `aside` notes: inline until an article rail fits, sticky in
|
|
7
|
+
* the right margin from 88rem. Plugin CSS intentionally loads before theme
|
|
8
|
+
* CSS, so a theme can restyle these hooks without editing the plugin.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
.rr-sidenotes__toggle {
|
|
@@ -21,6 +22,51 @@
|
|
|
21
22
|
outline-offset: 2px;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
.rr-sidenotes__ref {
|
|
26
|
+
scroll-margin-block-start: var(--rr-sidenotes-scroll-margin, 6rem);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.rr-sidenotes__footnotes {
|
|
30
|
+
--rr-sidenotes-ink: var(--rb-color-ink, #202020);
|
|
31
|
+
--rr-sidenotes-muted: var(--rb-color-muted, #6b6b6b);
|
|
32
|
+
--rr-sidenotes-rule: color-mix(
|
|
33
|
+
in srgb,
|
|
34
|
+
var(--rr-sidenotes-ink) 18%,
|
|
35
|
+
transparent
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
margin-block-start: var(--rb-space-6, 3rem);
|
|
39
|
+
padding-block-start: var(--rb-space-3, 1.5rem);
|
|
40
|
+
border-block-start: var(--rb-rule-width, 1px) solid var(--rr-sidenotes-rule);
|
|
41
|
+
color: var(--rr-sidenotes-ink);
|
|
42
|
+
font-size: 0.875rem;
|
|
43
|
+
line-height: 1.6;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.rr-sidenotes__footnotes li {
|
|
47
|
+
scroll-margin-block-start: var(--rr-sidenotes-scroll-margin, 6rem);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.rr-sidenotes__footnotes li::marker {
|
|
51
|
+
color: var(--rr-sidenotes-muted);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.rr-sidenotes__footnotes [data-footnote-backref] {
|
|
55
|
+
margin-inline-start: 0.35em;
|
|
56
|
+
color: var(--rr-sidenotes-muted);
|
|
57
|
+
text-decoration: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.rr-sidenotes__footnotes [data-footnote-backref]:hover {
|
|
61
|
+
color: var(--rr-sidenotes-ink);
|
|
62
|
+
text-decoration: underline;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* The list is the link target only when notes stay in the article flow. */
|
|
66
|
+
.rr-sidenotes__footnotes[data-rr-sidenotes-placement="margin"] {
|
|
67
|
+
display: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
24
70
|
.rr-sidenotes__note {
|
|
25
71
|
--rr-sidenotes-ink: var(--rb-color-ink, #202020);
|
|
26
72
|
--rr-sidenotes-muted: var(--rb-color-muted, #6b6b6b);
|
|
@@ -30,6 +76,11 @@
|
|
|
30
76
|
transparent
|
|
31
77
|
);
|
|
32
78
|
|
|
79
|
+
max-inline-size: 100%;
|
|
80
|
+
margin-block: var(--rb-space-3, 1.5rem);
|
|
81
|
+
padding-inline-start: var(--rb-space-2, 1rem);
|
|
82
|
+
border-inline-start: calc(var(--rb-rule-width, 1px) * 2) solid
|
|
83
|
+
var(--rr-sidenotes-rule);
|
|
33
84
|
color: var(--rr-sidenotes-ink);
|
|
34
85
|
font-size: 0.8125rem;
|
|
35
86
|
line-height: 1.55;
|
|
@@ -51,56 +102,6 @@
|
|
|
51
102
|
margin-bottom: 0;
|
|
52
103
|
}
|
|
53
104
|
|
|
54
|
-
.rr-sidenotes__footnotes {
|
|
55
|
-
font-size: 0.875rem;
|
|
56
|
-
color: var(--rr-sidenotes-ink);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/* Mobile: notes are hidden and become tap-open popovers. */
|
|
60
|
-
@media (max-width: 48rem) {
|
|
61
|
-
.rr-sidenotes__note {
|
|
62
|
-
position: fixed;
|
|
63
|
-
z-index: 60;
|
|
64
|
-
bottom: var(--rb-space-2, 1rem);
|
|
65
|
-
left: var(--rb-space-2, 1rem);
|
|
66
|
-
right: var(--rb-space-2, 1rem);
|
|
67
|
-
display: none;
|
|
68
|
-
max-height: min(60vh, 24rem);
|
|
69
|
-
overflow: auto;
|
|
70
|
-
padding: var(--rb-space-3, 1.5rem);
|
|
71
|
-
background: var(--rb-color-surface, #ffffff);
|
|
72
|
-
border: var(--rb-rule-width, 1px) solid var(--rr-sidenotes-rule);
|
|
73
|
-
box-shadow: 0 0.5rem 1.5rem rgb(0 0 0 / 0.18);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/* Right-edge panel variant driven by `popoverAlignment: "end"`. */
|
|
77
|
-
.rr-sidenotes__note--popover-end {
|
|
78
|
-
top: 50%;
|
|
79
|
-
bottom: auto;
|
|
80
|
-
left: auto;
|
|
81
|
-
width: min(20rem, calc(100vw - 2 * var(--rb-space-2, 1rem)));
|
|
82
|
-
transform: translateY(-50%);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.rr-sidenotes__note--open {
|
|
86
|
-
display: block;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
@media (min-width: 48rem) {
|
|
91
|
-
.rr-sidenotes__note {
|
|
92
|
-
max-inline-size: 100%;
|
|
93
|
-
margin-block: var(--rb-space-3, 1.5rem);
|
|
94
|
-
padding-inline-start: var(--rb-space-2, 1rem);
|
|
95
|
-
border-inline-start: calc(var(--rb-rule-width, 1px) * 2) solid
|
|
96
|
-
var(--rr-sidenotes-rule);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
.rr-sidenotes__footnotes {
|
|
100
|
-
display: none;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
105
|
@media (min-width: 88rem) {
|
|
105
106
|
.rr-sidenotes__note {
|
|
106
107
|
position: sticky;
|
package/dist/client.d.ts
DELETED
package/dist/client.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// src/client.ts
|
|
2
|
-
var DEFAULT_SELECTOR = "[data-rr-sidenotes-ref]";
|
|
3
|
-
var NOTE_OPEN_CLASS = "rr-sidenotes__note--open";
|
|
4
|
-
var DESKTOP_QUERY = "(min-width: 48rem)";
|
|
5
|
-
var DEFAULT_OPEN_LABEL = "Footnote";
|
|
6
|
-
var DEFAULT_CLOSE_LABEL = "Close sidenote";
|
|
7
|
-
function initSidenotes(options = {}) {
|
|
8
|
-
const desktopQuery = window.matchMedia(DESKTOP_QUERY);
|
|
9
|
-
if (desktopQuery.matches) return;
|
|
10
|
-
const toggles = Array.from(
|
|
11
|
-
document.querySelectorAll(
|
|
12
|
-
options.selector ?? DEFAULT_SELECTOR
|
|
13
|
-
)
|
|
14
|
-
);
|
|
15
|
-
if (toggles.length === 0) return;
|
|
16
|
-
const openLabel = options.openLabel ?? DEFAULT_OPEN_LABEL;
|
|
17
|
-
const closeLabel = options.closeLabel ?? DEFAULT_CLOSE_LABEL;
|
|
18
|
-
const noteFor = (toggle) => {
|
|
19
|
-
const controls = toggle.getAttribute("aria-controls");
|
|
20
|
-
if (!controls) return null;
|
|
21
|
-
const note = document.getElementById(controls);
|
|
22
|
-
return note instanceof HTMLElement ? note : null;
|
|
23
|
-
};
|
|
24
|
-
const isInsideSidenotes = (target) => {
|
|
25
|
-
if (!target) return false;
|
|
26
|
-
for (const toggle of toggles) {
|
|
27
|
-
const note = noteFor(toggle);
|
|
28
|
-
if (toggle.contains(target) || note?.contains(target)) return true;
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
31
|
-
};
|
|
32
|
-
const setOpen = (toggle, note, open) => {
|
|
33
|
-
const index = toggle.textContent?.trim() ?? "";
|
|
34
|
-
const label = open ? `${closeLabel} ${index}`.trim() : `${openLabel} ${index}`.trim();
|
|
35
|
-
toggle.setAttribute("aria-expanded", String(open));
|
|
36
|
-
toggle.setAttribute("aria-label", label);
|
|
37
|
-
note.classList.toggle(NOTE_OPEN_CLASS, open);
|
|
38
|
-
if (open) note.focus({ preventScroll: true });
|
|
39
|
-
};
|
|
40
|
-
const closeAll = () => {
|
|
41
|
-
for (const toggle of toggles) {
|
|
42
|
-
const note = noteFor(toggle);
|
|
43
|
-
if (note?.classList.contains(NOTE_OPEN_CLASS)) {
|
|
44
|
-
setOpen(toggle, note, false);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
for (const toggle of toggles) {
|
|
49
|
-
toggle.addEventListener("click", (event) => {
|
|
50
|
-
event.preventDefault();
|
|
51
|
-
const note = noteFor(toggle);
|
|
52
|
-
if (!note) return;
|
|
53
|
-
const isOpen = note.classList.contains(NOTE_OPEN_CLASS);
|
|
54
|
-
if (!isOpen) closeAll();
|
|
55
|
-
setOpen(toggle, note, !isOpen);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
const onKeydown = (event) => {
|
|
59
|
-
if (event.key !== "Escape") return;
|
|
60
|
-
closeAll();
|
|
61
|
-
};
|
|
62
|
-
const onClickOutside = (event) => {
|
|
63
|
-
if (isInsideSidenotes(event.target instanceof Node ? event.target : null)) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
closeAll();
|
|
67
|
-
};
|
|
68
|
-
document.addEventListener("keydown", onKeydown);
|
|
69
|
-
document.addEventListener("click", onClickOutside);
|
|
70
|
-
const onViewportChange = (event) => {
|
|
71
|
-
if (!event.matches) return;
|
|
72
|
-
closeAll();
|
|
73
|
-
document.removeEventListener("keydown", onKeydown);
|
|
74
|
-
document.removeEventListener("click", onClickOutside);
|
|
75
|
-
desktopQuery.removeEventListener("change", onViewportChange);
|
|
76
|
-
};
|
|
77
|
-
desktopQuery.addEventListener("change", onViewportChange);
|
|
78
|
-
}
|
|
79
|
-
export {
|
|
80
|
-
initSidenotes
|
|
81
|
-
};
|
package/dist/src/client.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mobile popover behavior for `@riebeckite/plugin-sidenotes`.
|
|
3
|
-
*
|
|
4
|
-
* Desktop margin notes are static and need no JavaScript: this entry ignores
|
|
5
|
-
* the page whenever `(min-width: 48rem)` matches and disconnects itself when
|
|
6
|
-
* the viewport crosses into the desktop layout.
|
|
7
|
-
*/
|
|
8
|
-
/** Public options exposed to the browser through the plugin's client entry. */
|
|
9
|
-
export type SidenotesClientOptions = {
|
|
10
|
-
/** Selector for reference links. Defaults to `[data-rr-sidenotes-ref]`. */
|
|
11
|
-
selector?: string;
|
|
12
|
-
/** Accessible label prefix for a reference link while its popover is closed. */
|
|
13
|
-
openLabel?: string;
|
|
14
|
-
/** Accessible label prefix for a reference link while its popover is open. */
|
|
15
|
-
closeLabel?: string;
|
|
16
|
-
};
|
|
17
|
-
export declare function initSidenotes(options?: SidenotesClientOptions): void;
|