优化导航栏样式
This commit is contained in:
parent
6cded9ab51
commit
55a928b545
@ -125,9 +125,6 @@ export default defineConfig({
|
||||
// 启用代码换行
|
||||
wrap: true
|
||||
},
|
||||
remarkPlugins: [
|
||||
[remarkEmoji, { emoticon: false, padded: true }]
|
||||
],
|
||||
rehypePlugins: [
|
||||
[rehypeExternalLinks, { target: '_blank', rel: ['nofollow', 'noopener', 'noreferrer'] }],
|
||||
rehypeCodeBlocks,
|
||||
|
@ -237,8 +237,6 @@ import { VISITED_PLACES } from '@/consts';
|
||||
|
||||
### Mermaid 图表支持
|
||||
|
||||
你可以在 Markdown 文件中使用 Mermaid 语法创建各种图表:
|
||||
|
||||
````markdown
|
||||
```mermaid
|
||||
graph TD;
|
||||
@ -249,20 +247,13 @@ graph TD;
|
||||
```
|
||||
````
|
||||
|
||||
系统将自动渲染这些图表,并支持深色/浅色主题自动适应。
|
||||
|
||||
### 代码块样式优化
|
||||
|
||||
代码块支持多种主题和语言高亮,内置了复制按钮:
|
||||
|
||||
````markdown
|
||||
```javascript
|
||||
// 示例代码
|
||||
function example() {
|
||||
console.log("Hello, world!");
|
||||
}
|
||||
```mermaid
|
||||
graph TD;
|
||||
A[开始] -->|处理数据| B(处理结果);
|
||||
B --> C{判断条件};
|
||||
C -->|条件1| D[结果1];
|
||||
C -->|条件2| E[结果2];
|
||||
```
|
||||
````
|
||||
|
||||
## SEO 优化
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "MDX使用教程"
|
||||
title: "markdown使用教程"
|
||||
date: 2023-03-03
|
||||
tags: []
|
||||
---
|
||||
@ -51,14 +51,6 @@ tags: []
|
||||
|
||||
~~这是删除线文本~~
|
||||
|
||||
#### 1.2.5 下划线文本
|
||||
|
||||
```markdown
|
||||
<u>这是下划线文本</u>
|
||||
```
|
||||
|
||||
<u>这是下划线文本</u>
|
||||
|
||||
### 1.3 列表
|
||||
|
||||
#### 1.3.1 无序列表
|
||||
@ -204,58 +196,3 @@ function greet(user: User): string {
|
||||
<br/>
|
||||
|
||||
---
|
||||
|
||||
### 1.9 表情符号
|
||||
|
||||
|
||||
| 表情名称 | 语法 | 效果 |
|
||||
|:--------|:-----|:-----|
|
||||
| 笑脸 | `:smile:` | :smile: |
|
||||
| 大笑 | `:laughing:` | :laughing: |
|
||||
| 哭泣 | `:cry:` | :cry: |
|
||||
| 心形 | `:heart:` | :heart: |
|
||||
| 火箭 | `:rocket:` | :rocket: |
|
||||
| 星星 | `:star:` | :star: |
|
||||
| 警告 | `:warning:` | :warning: |
|
||||
| 检查标记 | `:white_check_mark:` | :white_check_mark: |
|
||||
|
||||
## 2. HTML/JSX 语法部分
|
||||
|
||||
### 2.1 HTML 标签
|
||||
|
||||
#### 2.1.1 下划线文本
|
||||
|
||||
```mdx
|
||||
<u>这是下划线文本</u>
|
||||
```
|
||||
|
||||
<u>这是下划线文本</u>
|
||||
|
||||
#### 2.1.2 收纳语法
|
||||
|
||||
```mdx
|
||||
<details>
|
||||
<summary>点击展开</summary>
|
||||
|
||||
这里是被收纳的内容。
|
||||
可以包含任何 MDX 格式的内容。
|
||||
|
||||
- 列表项1
|
||||
- 列表项2
|
||||
- 列表项3
|
||||
|
||||
</details>
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>点击展开</summary>
|
||||
|
||||
这里是被收纳的内容。
|
||||
可以包含任何 MDX 格式的内容。
|
||||
|
||||
- 列表项1
|
||||
- 列表项2
|
||||
- 列表项3
|
||||
|
||||
</details>
|
||||
|
@ -180,31 +180,90 @@ function generateTableOfContents(headings: Heading[]) {
|
||||
// 查找最低级别的标题(数值最小)
|
||||
const minDepth = Math.min(...headings.map((h) => h.depth));
|
||||
|
||||
let tocHtml = '<ul class="space-y-2">';
|
||||
// 按照标题层级构建嵌套结构
|
||||
const tocTree: any[] = [];
|
||||
const levelMap: Record<number, any[]> = {};
|
||||
|
||||
headings.forEach((heading) => {
|
||||
// 计算相对缩进,以最小深度为基准
|
||||
const relativeDepth = heading.depth - minDepth;
|
||||
const indent = relativeDepth * 0.75;
|
||||
|
||||
// 基于相对深度而非绝对深度决定样式
|
||||
const isHigherLevel = relativeDepth <= 1; // 仅最高级和次高级标题使用较重的样式
|
||||
// 构建标题项
|
||||
const headingItem = {
|
||||
slug: heading.slug,
|
||||
text: heading.text,
|
||||
depth: relativeDepth,
|
||||
children: [],
|
||||
};
|
||||
|
||||
tocHtml += `<li>
|
||||
<a href="#${heading.slug}"
|
||||
class="block hover:text-primary-600 dark:hover:text-primary-400 duration-50 ${
|
||||
// 更精确地处理嵌套关系
|
||||
if (relativeDepth === 0) {
|
||||
// 顶级标题直接加入到树中
|
||||
tocTree.push(headingItem);
|
||||
levelMap[0] = tocTree;
|
||||
} else {
|
||||
// 查找当前标题的父级
|
||||
let parentDepth = relativeDepth - 1;
|
||||
// 向上查找可能的父级
|
||||
while (parentDepth >= 0 && !levelMap[parentDepth]) {
|
||||
parentDepth--;
|
||||
}
|
||||
|
||||
if (parentDepth >= 0 && levelMap[parentDepth] && levelMap[parentDepth].length > 0) {
|
||||
// 找到父层级,将此标题添加到最近的父标题的子标题数组中
|
||||
const parentItems = levelMap[parentDepth];
|
||||
const parent = parentItems[parentItems.length - 1];
|
||||
parent.children.push(headingItem);
|
||||
|
||||
// 更新当前深度的映射
|
||||
if (!levelMap[relativeDepth]) {
|
||||
levelMap[relativeDepth] = [];
|
||||
}
|
||||
levelMap[relativeDepth].push(headingItem);
|
||||
} else {
|
||||
// 找不到有效父级,作为顶级标题处理
|
||||
tocTree.push(headingItem);
|
||||
levelMap[relativeDepth] = [headingItem];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 递归生成HTML
|
||||
function generateTocHTML(items: any[], level = 0) {
|
||||
if (items.length === 0) return '';
|
||||
|
||||
const isTopLevel = level === 0;
|
||||
let html = `<ul class="space-y-2 toc-list ${isTopLevel ? '' : 'toc-sublist hidden'}" ${level > 0 ? 'aria-expanded="false"' : ''}>`;
|
||||
|
||||
items.forEach(item => {
|
||||
const hasChildren = item.children && item.children.length > 0;
|
||||
const isHigherLevel = item.depth <= 1; // 只有最高级和次高级标题使用较重的样式
|
||||
|
||||
html += `<li class="toc-item" data-depth="${item.depth}">
|
||||
<div class="toc-item-container">
|
||||
<a href="#${item.slug}"
|
||||
class="toc-link block duration-50 ${
|
||||
isHigherLevel
|
||||
? "text-secondary-800 dark:text-secondary-200 font-medium"
|
||||
: "text-secondary-600 dark:text-secondary-400"
|
||||
}"
|
||||
style="padding-left: ${indent}rem;">
|
||||
${heading.text}
|
||||
style="padding-left: ${item.depth * 0.75}rem;">
|
||||
${item.text}
|
||||
</a>
|
||||
${hasChildren ? `<button class="toc-toggle ml-1 p-1 text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400" aria-expanded="false">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>` : ''}
|
||||
</div>
|
||||
${generateTocHTML(item.children, level + 1)}
|
||||
</li>`;
|
||||
});
|
||||
|
||||
tocHtml += "</ul>";
|
||||
return tocHtml;
|
||||
html += '</ul>';
|
||||
return html;
|
||||
}
|
||||
|
||||
return generateTocHTML(tocTree);
|
||||
}
|
||||
|
||||
// 生成目录HTML
|
||||
@ -378,18 +437,14 @@ const tableOfContents = generateTableOfContents(headings);
|
||||
|
||||
<!-- 目录 -->
|
||||
<section
|
||||
class="hidden 2xl:block fixed right-[calc(50%-44rem)] top-20 w-64 z-30"
|
||||
class="hidden 2xl:block"
|
||||
id="toc-panel"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 flex flex-col backdrop-blur-sm bg-opacity-95 dark:bg-opacity-95"
|
||||
>
|
||||
<div
|
||||
class="border-b border-secondary-100 dark:border-gray-700 p-4 pb-3 sticky top-0 bg-white dark:bg-gray-800 bg-opacity-95 dark:bg-opacity-95 backdrop-filter backdrop-blur-sm z-10 rounded-t-xl"
|
||||
>
|
||||
<h3
|
||||
class="font-bold text-primary-700 dark:text-primary-400 flex items-center gap-2"
|
||||
class="panel-header"
|
||||
>
|
||||
<h3>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
@ -409,7 +464,7 @@ const tableOfContents = generateTableOfContents(headings);
|
||||
</div>
|
||||
<div
|
||||
id="toc-content"
|
||||
class="text-sm p-4 pt-2 overflow-y-auto max-h-[calc(100vh-8rem-42px)] scrollbar-thin scrollbar-thumb-primary-200 dark:scrollbar-thumb-primary-800 scrollbar-track-transparent"
|
||||
class="scrollbar-thin scrollbar-thumb-primary-200 dark:scrollbar-thumb-primary-800 scrollbar-track-transparent"
|
||||
set:html={tableOfContents}
|
||||
>
|
||||
<!-- 目录内容在服务端生成 -->
|
||||
@ -742,8 +797,42 @@ const tableOfContents = generateTableOfContents(headings);
|
||||
addListener(window, "resize", checkTocVisibility);
|
||||
checkTocVisibility();
|
||||
|
||||
// 处理目录折叠/展开功能
|
||||
const tocToggles = tocContent.querySelectorAll(".toc-toggle");
|
||||
|
||||
tocToggles.forEach((toggle) => {
|
||||
addListener(toggle, "click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const expanded = toggle.getAttribute("aria-expanded") === "true";
|
||||
toggle.setAttribute("aria-expanded", expanded ? "false" : "true");
|
||||
|
||||
// 更新图标旋转
|
||||
const svg = toggle.querySelector("svg");
|
||||
if (svg) {
|
||||
svg.style.transform = expanded ? "" : "rotate(-180deg)";
|
||||
}
|
||||
|
||||
// 切换子菜单显示状态
|
||||
const listItem = toggle.closest(".toc-item");
|
||||
if (listItem) {
|
||||
const sublist = listItem.querySelector(".toc-sublist");
|
||||
if (sublist) {
|
||||
if (expanded) {
|
||||
sublist.classList.add("hidden");
|
||||
sublist.setAttribute("aria-expanded", "false");
|
||||
} else {
|
||||
sublist.classList.remove("hidden");
|
||||
sublist.setAttribute("aria-expanded", "true");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 处理目录链接点击跳转
|
||||
const tocLinks = tocContent.querySelectorAll("a");
|
||||
const tocLinks = tocContent.querySelectorAll(".toc-link");
|
||||
|
||||
tocLinks.forEach((link) => {
|
||||
addListener(link, "click", (e) => {
|
||||
@ -790,7 +879,8 @@ const tableOfContents = generateTableOfContents(headings);
|
||||
const headings = Array.from(
|
||||
article.querySelectorAll("h1, h2, h3, h4, h5, h6"),
|
||||
);
|
||||
const tocLinks = Array.from(tocContent.querySelectorAll("a"));
|
||||
const tocLinks = Array.from(tocContent.querySelectorAll(".toc-link"));
|
||||
const tocItems = Array.from(tocContent.querySelectorAll(".toc-item"));
|
||||
|
||||
// 清除所有活动状态
|
||||
tocLinks.forEach((link) => {
|
||||
@ -815,7 +905,10 @@ const tableOfContents = generateTableOfContents(headings);
|
||||
}
|
||||
}
|
||||
|
||||
// 高亮当前标题对应的目录项
|
||||
// 记录当前活动的项目和其所有父级
|
||||
const activeItems = new Set();
|
||||
|
||||
// 高亮当前标题对应的目录项并展开父菜单
|
||||
if (currentHeading) {
|
||||
const id = currentHeading.getAttribute("id");
|
||||
if (id) {
|
||||
@ -830,8 +923,34 @@ const tableOfContents = generateTableOfContents(headings);
|
||||
"font-medium",
|
||||
);
|
||||
|
||||
// 可选: 确保当前激活的目录项在可视区域内
|
||||
const tocContainer = tocContent.querySelector("ul");
|
||||
// 展开当前激活项的所有父菜单并收集到活动项集合中
|
||||
let parent = activeLink.closest(".toc-item");
|
||||
while (parent) {
|
||||
// 添加到活动项目集合
|
||||
activeItems.add(parent);
|
||||
|
||||
const parentSublist = parent.querySelector(".toc-sublist");
|
||||
const parentToggle = parent.querySelector(".toc-toggle");
|
||||
|
||||
if (parentSublist && parentSublist.classList.contains("hidden")) {
|
||||
parentSublist.classList.remove("hidden");
|
||||
parentSublist.setAttribute("aria-expanded", "true");
|
||||
|
||||
if (parentToggle) {
|
||||
parentToggle.setAttribute("aria-expanded", "true");
|
||||
const svg = parentToggle.querySelector("svg");
|
||||
if (svg) {
|
||||
svg.style.transform = "rotate(-180deg)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向上查找父级
|
||||
parent = parent.parentElement?.closest(".toc-item");
|
||||
}
|
||||
|
||||
// 确保当前激活的目录项在可视区域内
|
||||
const tocContainer = tocContent;
|
||||
if (tocContainer) {
|
||||
const linkOffsetTop = activeLink.offsetTop;
|
||||
const containerScrollTop = tocContainer.scrollTop;
|
||||
@ -849,6 +968,28 @@ const tableOfContents = generateTableOfContents(headings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭不在当前活动路径上的所有子菜单
|
||||
tocItems.forEach(item => {
|
||||
// 如果不在活动路径上且有子菜单
|
||||
if (!activeItems.has(item)) {
|
||||
const sublist = item.querySelector('.toc-sublist');
|
||||
const toggle = item.querySelector('.toc-toggle');
|
||||
|
||||
if (sublist && !sublist.classList.contains('hidden')) {
|
||||
sublist.classList.add('hidden');
|
||||
sublist.setAttribute('aria-expanded', 'false');
|
||||
|
||||
if (toggle) {
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
const svg = toggle.querySelector('svg');
|
||||
if (svg) {
|
||||
svg.style.transform = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addListener(window, "scroll", () => {
|
||||
@ -861,6 +1002,18 @@ const tableOfContents = generateTableOfContents(headings);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化时收起所有子菜单
|
||||
const topLevelToggles = tocContent.querySelectorAll(".toc-list > .toc-item > .toc-item-container > .toc-toggle");
|
||||
topLevelToggles.forEach(toggle => {
|
||||
toggle.setAttribute("aria-expanded", "false");
|
||||
const sublist = toggle.closest(".toc-item").querySelector(".toc-sublist");
|
||||
if (sublist) {
|
||||
sublist.classList.add("hidden");
|
||||
sublist.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
});
|
||||
|
||||
// 初始更新一次活动标题,确保相关父菜单展开
|
||||
updateActiveHeading();
|
||||
}
|
||||
|
||||
|
@ -220,10 +220,7 @@
|
||||
border-bottom-color: var(--color-primary-400);
|
||||
}
|
||||
|
||||
/* 响应式样式 */
|
||||
@media (max-width: 640px) {
|
||||
/* 移除所有表格相关样式 */
|
||||
}
|
||||
|
||||
|
||||
/* 收纳内容样式 */
|
||||
.details-content {
|
||||
@ -363,3 +360,176 @@
|
||||
background: linear-gradient(to right, var(--color-primary-900/10), var(--color-dark-surface));
|
||||
border-left-color: var(--color-primary-400);
|
||||
}
|
||||
|
||||
/* 目录菜单样式 */
|
||||
/* 目录项样式 */
|
||||
.toc-item-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toc-link {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0.25rem 0;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
/* 黑暗模式文本颜色 */
|
||||
.dark .text-secondary-800,
|
||||
[data-theme="dark"] .text-secondary-800 {
|
||||
color: var(--color-secondary-200);
|
||||
}
|
||||
|
||||
.dark .text-secondary-600,
|
||||
[data-theme="dark"] .text-secondary-600 {
|
||||
color: var(--color-secondary-400);
|
||||
}
|
||||
|
||||
.toc-link:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
color: var(--color-primary-600);
|
||||
}
|
||||
|
||||
.dark .toc-link:hover,
|
||||
[data-theme="dark"] .toc-link:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
color: var(--color-primary-400);
|
||||
}
|
||||
|
||||
.toc-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
min-width: 1.5rem;
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
|
||||
.toc-toggle:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.dark .toc-toggle:hover,
|
||||
[data-theme="dark"] .toc-toggle:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
.toc-sublist.hidden {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toc-sublist:not(.hidden) {
|
||||
max-height: 1000px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 目录面板样式 */
|
||||
#toc-panel {
|
||||
position: fixed;
|
||||
right: calc(50% - 44rem);
|
||||
top: 5rem;
|
||||
width: 16rem;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
#toc-panel > div {
|
||||
background-color: white;
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: 0.75rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
backdrop-filter: blur(4px);
|
||||
background-opacity: 0.95;
|
||||
}
|
||||
|
||||
.dark #toc-panel > div,
|
||||
[data-theme="dark"] #toc-panel > div {
|
||||
background-color: var(--color-gray-800);
|
||||
border-color: var(--color-gray-700);
|
||||
background-opacity: 0.95;
|
||||
}
|
||||
|
||||
#toc-panel .panel-header {
|
||||
border-bottom: 1px solid var(--color-secondary-100);
|
||||
padding: 1rem;
|
||||
padding-bottom: 0.75rem;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
border-top-left-radius: 0.75rem;
|
||||
border-top-right-radius: 0.75rem;
|
||||
background-color: white;
|
||||
background-opacity: 0.95;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.dark #toc-panel .panel-header,
|
||||
[data-theme="dark"] #toc-panel .panel-header {
|
||||
border-bottom-color: var(--color-gray-700);
|
||||
background-color: var(--color-gray-800);
|
||||
}
|
||||
|
||||
#toc-panel h3 {
|
||||
font-weight: 700;
|
||||
color: var(--color-primary-700);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.dark #toc-panel h3,
|
||||
[data-theme="dark"] #toc-panel h3 {
|
||||
color: var(--color-primary-400);
|
||||
}
|
||||
|
||||
#toc-content {
|
||||
font-size: 0.875rem;
|
||||
padding: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 8rem - 42px);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.dark #toc-content,
|
||||
[data-theme="dark"] #toc-content {
|
||||
background-color: var(--color-gray-800);
|
||||
}
|
||||
|
||||
/* 目录列表样式 */
|
||||
.toc-list {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
/* 黑暗模式适配 */
|
||||
.dark .text-secondary-800,
|
||||
[data-theme="dark"] .text-secondary-800 {
|
||||
color: var(--color-secondary-200);
|
||||
}
|
||||
|
||||
.dark .text-secondary-600,
|
||||
[data-theme="dark"] .text-secondary-600 {
|
||||
color: var(--color-secondary-400);
|
||||
}
|
||||
|
||||
.dark .text-primary-600,
|
||||
[data-theme="dark"] .text-primary-600 {
|
||||
color: var(--color-primary-400);
|
||||
}
|
||||
|
||||
/* 扩展目录高亮样式 */
|
||||
.toc-link.text-primary-600 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dark .toc-link.text-primary-600,
|
||||
[data-theme="dark"] .toc-link.text-primary-600 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user