@riebeckite/plugin-navigation 0.0.32 → 0.0.33
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 +5 -1
- package/README_ja.md +21 -16
- package/dist/index.d.ts +0 -1
- package/dist/index.js +37 -18
- package/dist/src/navigation.d.ts +4 -2
- package/dist/src/types.d.ts +2 -1
- package/package.json +7 -7
- package/style.css +5 -0
package/README.md
CHANGED
|
@@ -10,6 +10,10 @@ the Site owns placement — where each rendered navigation appears.
|
|
|
10
10
|
|
|
11
11
|
[日本語](./README_ja.md)
|
|
12
12
|
|
|
13
|
+
## Live example
|
|
14
|
+
|
|
15
|
+
The header links — Docs, Plugins, Themes, and GitHub — are generated from the `navigation()` configuration. The Documentation sidebar is the Docs collection navigation.
|
|
16
|
+
|
|
13
17
|
## Overview
|
|
14
18
|
|
|
15
19
|
`navigation()` reads the manifest's discoverable entries — the notes that are
|
|
@@ -150,7 +154,7 @@ tab.
|
|
|
150
154
|
## Exports
|
|
151
155
|
|
|
152
156
|
- `navigation(options?)` — plugin factory
|
|
153
|
-
- `
|
|
157
|
+
- `navigation` — alias of `navigation`
|
|
154
158
|
- `NAVIGATION_PLUGIN_NAME` — the plugin name, `"navigation"`
|
|
155
159
|
- `buildNavigation(entries, options?)` — build a model from manifest entries
|
|
156
160
|
- `resolveSiteNavigation(config, manifest)` — find the enabled plugin and build
|
package/README_ja.md
CHANGED
|
@@ -5,21 +5,25 @@
|
|
|
5
5
|
サイトナビゲーションを提供する Riebeckite プラグインです。`navigation()` は
|
|
6
6
|
Vault にすでにあるノートから主要ナビゲーションを導出します。Riebeckite 専用の
|
|
7
7
|
ファイルも必須 frontmatter も不要なので、既存の Obsidian Vault をそのまま
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
公開できます。ナビゲーションのモデル、描画される UI、インタラクションの
|
|
9
|
+
スタイルはプラグインが持ち、どこに置くかは Site が決めます。
|
|
10
10
|
|
|
11
11
|
[English](./README.md)
|
|
12
12
|
|
|
13
|
+
## 実行例
|
|
14
|
+
|
|
15
|
+
ヘッダーの Docs・Plugins・Themes・GitHub のリンクは、`navigation()` の設定から生成されています。右側の Documentation サイドバーは Docs コレクションのナビゲーションです。
|
|
16
|
+
|
|
13
17
|
## 仕組み
|
|
14
18
|
|
|
15
19
|
`navigation()` はマニフェストの discoverable エントリ(公開され、ルーティング
|
|
16
20
|
可能なノート)を読み、スラッグ階層でまとめます。
|
|
17
21
|
|
|
18
|
-
-
|
|
22
|
+
- フォルダはセクションになります
|
|
19
23
|
- `index` / `README` ノートはそのフォルダへのリンクになり、同じフォルダの
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
- タイトルはノート自身の `title`
|
|
24
|
+
直下のノートは子になります
|
|
25
|
+
- それ以外のノートはリンクになります
|
|
26
|
+
- タイトルはノート自身の `title` を使い、なければスラッグのセグメントから作ります
|
|
23
27
|
|
|
24
28
|
index ノートを持たないフォルダは、子をまとめたラベルとして表示されます。
|
|
25
29
|
モデルは `manifest.discoverableEntries` から作るため、下書き・予約公開・
|
|
@@ -59,10 +63,11 @@ const secondary = model?.secondary ?? [];
|
|
|
59
63
|
`SiteNav` は解決済みのツリーを `<nav>` ランドマークとネストしたリストとして
|
|
60
64
|
描画し、`MobileSiteNav` はコンパクトな `<details>` メニューを描画します。子の
|
|
61
65
|
再帰描画、現在パスの判定、言語を考慮した正規化、外部リンクの扱い、`aria-current`
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
の付与はプラグインが行います。リストの流し方、リンク、子メニュー、フォーカス
|
|
67
|
+
表示、モバイルパネルといった `rb-nav` の UI も、プラグインが自身の `style.css`
|
|
68
|
+
で提供します(自動生成されるプラグインスタイル経由で読み込まれます)。Site は
|
|
69
|
+
コンポーネントのルートを独自のレイアウト用ラッパーに置くだけにし、`rb-nav` の
|
|
70
|
+
子要素をスタイルしたり、モバイルメニューのマークアップを組み立てたりしないでください。
|
|
66
71
|
|
|
67
72
|
```tsx
|
|
68
73
|
import { MobileSiteNav, SiteNav } from "@riebeckite/plugin-navigation";
|
|
@@ -87,13 +92,13 @@ import { MobileSiteNav, SiteNav } from "@riebeckite/plugin-navigation";
|
|
|
87
92
|
```
|
|
88
93
|
|
|
89
94
|
`path` は現在のリクエストパスです。`language` は現在のコンテンツ言語で、指定すると
|
|
90
|
-
`SiteNav` は `/language`
|
|
95
|
+
`SiteNav` は `/language` プレフィックスを取り除いてからアクティブな項目を判定します。
|
|
91
96
|
そのため、言語プレフィックスのない手書き href でもローカライズされたページに
|
|
92
97
|
一致します。`localizeHref` を渡すと現在の言語に合わせて href を書き換えられます。
|
|
93
98
|
`label` でランドマークのラベルを、`class` / `className` で `<nav>` のクラスを
|
|
94
99
|
拡張できます。
|
|
95
100
|
|
|
96
|
-
|
|
101
|
+
ルートレベルで意図的に見た目を上書きする場合は、`SiteNav` のルートまたはラッパーに
|
|
97
102
|
次の公開カスタムプロパティを指定します。`--rb-nav-gap`、
|
|
98
103
|
`--rb-nav-list-flex-wrap`、`--rb-nav-list-justify-content`、
|
|
99
104
|
`--rb-nav-item-display`、`--rb-nav-item-padding`、
|
|
@@ -152,7 +157,7 @@ navigation({
|
|
|
152
157
|
## エクスポート
|
|
153
158
|
|
|
154
159
|
- `navigation(options?)`:プラグインファクトリ
|
|
155
|
-
- `
|
|
160
|
+
- `navigation`:`navigation` のエイリアス
|
|
156
161
|
- `NAVIGATION_PLUGIN_NAME`:プラグイン名 `"navigation"`
|
|
157
162
|
- `buildNavigation(entries, options?)`:マニフェストエントリからモデルを構築する
|
|
158
163
|
- `resolveSiteNavigation(config, manifest)`:有効なプラグインを見つけてモデルを
|
|
@@ -165,9 +170,9 @@ navigation({
|
|
|
165
170
|
|
|
166
171
|
## 制約
|
|
167
172
|
|
|
168
|
-
- `l10n`
|
|
169
|
-
多言語 Vault
|
|
170
|
-
|
|
173
|
+
- `l10n` プラグインが言語メタデータを付与している場合、導出は表示中の言語に追従し、
|
|
174
|
+
多言語 Vault のナビゲーションが言語を混在させることはありません。ローカライズの
|
|
175
|
+
メタデータがない Vault では、discoverable な全エントリを使います。
|
|
171
176
|
- 並び順はタイトルのアルファベット順です。frontmatter の順序は読みません。
|
|
172
177
|
- `exclude` / `include` / `order` はまだありません。実際のサイトが必要とした
|
|
173
178
|
ときにだけ追加します。
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,3 @@ export { MobileSiteNav, SiteNav } from "./components/site-nav.js";
|
|
|
4
4
|
export { buildNavigation, NAVIGATION_PLUGIN_NAME, resolveSiteNavigation, } from "./src/navigation.js";
|
|
5
5
|
export type { NavigationItem, NavigationOptions, NavigationSource, SiteNavigation, } from "./src/types.js";
|
|
6
6
|
export declare function navigation(options?: NavigationOptions): import("@riebeckite/core").RiebeckitePlugin<NavigationOptions>;
|
|
7
|
-
export declare const navigationPlugin: typeof navigation;
|
package/dist/index.js
CHANGED
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
var ROOT_SEGMENT = "";
|
|
9
9
|
var LOCALIZATION_TRANSLATION_KEY = "l10n.translationId";
|
|
10
10
|
var NAVIGATION_PLUGIN_NAME = "navigation";
|
|
11
|
-
function buildNavigation(entries, options = {}) {
|
|
11
|
+
function buildNavigation(entries, options = {}, folderIcons = {}) {
|
|
12
12
|
return {
|
|
13
|
-
primary: options.items ?? deriveNavigation(entries),
|
|
13
|
+
primary: options.items ?? deriveNavigation(entries, folderIcons),
|
|
14
14
|
secondary: options.secondary ?? []
|
|
15
15
|
};
|
|
16
16
|
}
|
|
@@ -21,7 +21,8 @@ function resolveSiteNavigation(source, manifest, language) {
|
|
|
21
21
|
if (!plugin) return null;
|
|
22
22
|
return buildNavigation(
|
|
23
23
|
selectEntriesByLanguage(manifest.discoverableEntries, language),
|
|
24
|
-
plugin.options ?? {}
|
|
24
|
+
plugin.options ?? {},
|
|
25
|
+
manifest.icons?.folders ?? {}
|
|
25
26
|
);
|
|
26
27
|
}
|
|
27
28
|
function validateNavigationOptions(options) {
|
|
@@ -31,11 +32,11 @@ function validateNavigationOptions(options) {
|
|
|
31
32
|
validateItems(options.secondary, "secondary", issues, /* @__PURE__ */ new Set());
|
|
32
33
|
return issues;
|
|
33
34
|
}
|
|
34
|
-
function deriveNavigation(entries) {
|
|
35
|
+
function deriveNavigation(entries, folderIcons) {
|
|
35
36
|
const nodes = entries.map(toNavigationEntry).filter((entry) => Boolean(entry));
|
|
36
|
-
const root = createTreeNode(ROOT_SEGMENT);
|
|
37
|
+
const root = createTreeNode(ROOT_SEGMENT, "");
|
|
37
38
|
for (const node of nodes) addEntry(root, node);
|
|
38
|
-
return buildItems(root);
|
|
39
|
+
return buildItems(root, folderIcons);
|
|
39
40
|
}
|
|
40
41
|
function toNavigationEntry(entry) {
|
|
41
42
|
const path = entry.publicLocation.metadata?.[LOCALIZATION_TRANSLATION_KEY] ?? entry.slug;
|
|
@@ -57,27 +58,28 @@ function addEntry(root, entry) {
|
|
|
57
58
|
const path = entry.isIndex ? entry.segments.slice(0, -1) : entry.segments;
|
|
58
59
|
let node = root;
|
|
59
60
|
for (const segment of path) {
|
|
60
|
-
const child = node.children.get(segment) ?? createTreeNode(segment);
|
|
61
|
+
const child = node.children.get(segment) ?? createTreeNode(segment, node.path ? `${node.path}/${segment}` : segment);
|
|
61
62
|
node.children.set(segment, child);
|
|
62
63
|
node = child;
|
|
63
64
|
}
|
|
64
65
|
if (entry.isIndex) node.indexes.push(entry);
|
|
65
66
|
else node.page = entry;
|
|
66
67
|
}
|
|
67
|
-
function buildItems(node) {
|
|
68
|
-
return [...node.children.values()].toSorted(compareNodes).map(nodeToItem);
|
|
68
|
+
function buildItems(node, folderIcons) {
|
|
69
|
+
return [...node.children.values()].toSorted(compareNodes).map((child) => nodeToItem(child, folderIcons));
|
|
69
70
|
}
|
|
70
|
-
function nodeToItem(node) {
|
|
71
|
+
function nodeToItem(node, folderIcons) {
|
|
71
72
|
const singleIndex = node.indexes.length === 1 ? node.indexes[0] : void 0;
|
|
72
73
|
const primary = singleIndex ?? (node.indexes.length === 0 ? node.page : void 0);
|
|
73
74
|
const extra = node.indexes.length <= 1 ? singleIndex && node.page ? [node.page] : [] : [...node.indexes, ...node.page ? [node.page] : []];
|
|
74
75
|
const children = [
|
|
75
76
|
...extra.map((entry) => toItem(entry)),
|
|
76
|
-
...buildItems(node)
|
|
77
|
+
...buildItems(node, folderIcons)
|
|
77
78
|
];
|
|
78
79
|
if (primary) return toItem(primary, children);
|
|
79
80
|
return {
|
|
80
81
|
label: titleFromSegment(node.segment),
|
|
82
|
+
...folderIcons[node.path] ? { icon: folderIcons[node.path] } : {},
|
|
81
83
|
children
|
|
82
84
|
};
|
|
83
85
|
}
|
|
@@ -85,6 +87,7 @@ function toItem(entry, children = []) {
|
|
|
85
87
|
return {
|
|
86
88
|
label: entry.title,
|
|
87
89
|
href: entry.entry.permalink,
|
|
90
|
+
...entry.entry.icon ? { icon: entry.entry.icon } : {},
|
|
88
91
|
...children.length === 0 ? {} : { children }
|
|
89
92
|
};
|
|
90
93
|
}
|
|
@@ -99,8 +102,8 @@ function compareNodes(a, b) {
|
|
|
99
102
|
if (title !== 0) return title;
|
|
100
103
|
return a.segment.localeCompare(b.segment, "en", { sensitivity: "base" });
|
|
101
104
|
}
|
|
102
|
-
function createTreeNode(segment) {
|
|
103
|
-
return { segment, indexes: [], children: /* @__PURE__ */ new Map() };
|
|
105
|
+
function createTreeNode(segment, path) {
|
|
106
|
+
return { segment, path, indexes: [], children: /* @__PURE__ */ new Map() };
|
|
104
107
|
}
|
|
105
108
|
function titleFromSegment(segment) {
|
|
106
109
|
return segment.replace(/[-_]+/g, " ").replace(/\b\p{L}/gu, (letter) => letter.toLocaleUpperCase("en"));
|
|
@@ -214,7 +217,7 @@ function NavigationItemView(props) {
|
|
|
214
217
|
const active = item.href ? isActive(item.href, path, language) : false;
|
|
215
218
|
const href = item.href ? localizeHref?.(item.href) ?? item.href : void 0;
|
|
216
219
|
return /* @__PURE__ */ jsxs("li", { class: "rb-nav__item", children: [
|
|
217
|
-
href ? /* @__PURE__ */
|
|
220
|
+
href ? /* @__PURE__ */ jsxs(
|
|
218
221
|
"a",
|
|
219
222
|
{
|
|
220
223
|
href,
|
|
@@ -222,9 +225,27 @@ function NavigationItemView(props) {
|
|
|
222
225
|
"aria-current": active ? "page" : void 0,
|
|
223
226
|
target: item.external ? "_blank" : void 0,
|
|
224
227
|
rel: item.external ? "noreferrer" : void 0,
|
|
225
|
-
children:
|
|
228
|
+
children: [
|
|
229
|
+
item.icon ? /* @__PURE__ */ jsx(
|
|
230
|
+
"span",
|
|
231
|
+
{
|
|
232
|
+
class: "rb-nav__icon",
|
|
233
|
+
dangerouslySetInnerHTML: { __html: item.icon.html }
|
|
234
|
+
}
|
|
235
|
+
) : null,
|
|
236
|
+
item.label
|
|
237
|
+
]
|
|
226
238
|
}
|
|
227
|
-
) : /* @__PURE__ */
|
|
239
|
+
) : /* @__PURE__ */ jsxs("span", { class: "rb-nav__label", children: [
|
|
240
|
+
item.icon ? /* @__PURE__ */ jsx(
|
|
241
|
+
"span",
|
|
242
|
+
{
|
|
243
|
+
class: "rb-nav__icon",
|
|
244
|
+
dangerouslySetInnerHTML: { __html: item.icon.html }
|
|
245
|
+
}
|
|
246
|
+
) : null,
|
|
247
|
+
item.label
|
|
248
|
+
] }),
|
|
228
249
|
item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(
|
|
229
250
|
NavigationItems,
|
|
230
251
|
{
|
|
@@ -272,13 +293,11 @@ function navigation(options = {}) {
|
|
|
272
293
|
]
|
|
273
294
|
});
|
|
274
295
|
}
|
|
275
|
-
var navigationPlugin = navigation;
|
|
276
296
|
export {
|
|
277
297
|
MobileSiteNav,
|
|
278
298
|
NAVIGATION_PLUGIN_NAME,
|
|
279
299
|
SiteNav,
|
|
280
300
|
buildNavigation,
|
|
281
301
|
navigation,
|
|
282
|
-
navigationPlugin,
|
|
283
302
|
resolveSiteNavigation
|
|
284
303
|
};
|
package/dist/src/navigation.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { type ConfigValidationIssue, type ContentManifest, type ContentManifestEntry } from "@riebeckite/core";
|
|
1
|
+
import { type ConfigValidationIssue, type ContentIcon, type ContentManifest, type ContentManifestEntry } from "@riebeckite/core";
|
|
2
2
|
import type { NavigationOptions, NavigationSource, SiteNavigation } from "./types.js";
|
|
3
|
+
type FolderIcons = Readonly<Record<string, ContentIcon>>;
|
|
3
4
|
export declare const NAVIGATION_PLUGIN_NAME = "navigation";
|
|
4
|
-
export declare function buildNavigation(entries: readonly ContentManifestEntry[], options?: NavigationOptions): SiteNavigation;
|
|
5
|
+
export declare function buildNavigation(entries: readonly ContentManifestEntry[], options?: NavigationOptions, folderIcons?: FolderIcons): SiteNavigation;
|
|
5
6
|
export declare function resolveSiteNavigation(source: NavigationSource, manifest: ContentManifest, language?: string): SiteNavigation | null;
|
|
6
7
|
export declare function validateNavigationOptions(options: NavigationOptions | undefined): readonly ConfigValidationIssue[];
|
|
8
|
+
export {};
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { ContentManifestEntry } from "@riebeckite/core";
|
|
1
|
+
import type { ContentIcon, ContentManifestEntry } from "@riebeckite/core";
|
|
2
2
|
export type NavigationItem = {
|
|
3
3
|
readonly label: string;
|
|
4
4
|
readonly href?: string;
|
|
5
|
+
readonly icon?: ContentIcon;
|
|
5
6
|
readonly children?: readonly NavigationItem[];
|
|
6
7
|
readonly external?: boolean;
|
|
7
8
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-navigation",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
"./style.css": "./style.css"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"
|
|
19
|
-
"@riebeckite/
|
|
20
|
-
"
|
|
18
|
+
"@riebeckite/core": "0.0.33",
|
|
19
|
+
"@riebeckite/ui": "0.0.33",
|
|
20
|
+
"hono": "^4.12.25"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
23
|
+
"@riebeckite/test": "0.0.33",
|
|
24
|
+
"puppeteer": "^25.16.0",
|
|
25
|
+
"tsx": "^4.22.4"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
package/style.css
CHANGED