mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
Step 3 of the planned vue->react migration. The five api-docs files
(ApiDocsPage, CodeBlock, EndpointRow, EndpointSection, plus the
data-only endpoints.js) all move to react+ts.
Also introduces components/AppSidebar.tsx — api-docs is the first
authenticated page to need it. AppSidebar.vue stays in place for the
six remaining vue entries (settings, inbounds, clients, xray, nodes,
index); each gets switched to AppSidebar.tsx as its entry migrates.
After the last entry flips, AppSidebar.vue is deleted.
Notable transformations:
* The scroll observer that highlights the active TOC link is a
useEffect keyed on sections — re-registers whenever the visible
set changes (search filter narrows it). Same behaviour as the vue
watchEffect.
* v-html="safeInlineHtml(...)" becomes
dangerouslySetInnerHTML={{ __html: safeInlineHtml(...) }}. The
helper still escapes everything except <code> tags.
* JSON syntax highlighter in CodeBlock is unchanged — pure regex on
the escaped string, then rendered via dangerouslySetInnerHTML.
* endpoints.js stays as JS (allowJs in tsconfig); only the consumer
signatures (Endpoint, Section) are typed at the React boundary.
* AppSidebar reuses pauseAnimationsUntilLeave + useTheme from
step 1. Drawer + Sider keyed off the same localStorage flag
(isSidebarCollapsed) and DOM theme attributes the vue version
uses, so the two stay in sync during coexistence.
107 lines
2.3 KiB
CSS
107 lines
2.3 KiB
CSS
.code-block-wrapper {
|
|
position: relative;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
border: 1px solid rgba(128, 128, 128, 0.15);
|
|
}
|
|
|
|
.code-toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 4px 8px;
|
|
background: rgba(128, 128, 128, 0.06);
|
|
border-bottom: 1px solid rgba(128, 128, 128, 0.1);
|
|
}
|
|
|
|
.lang-badge {
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.5px;
|
|
color: rgba(0, 0, 0, 0.4);
|
|
text-transform: uppercase;
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
}
|
|
|
|
.copy-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 26px;
|
|
height: 26px;
|
|
border: 1px solid rgba(128, 128, 128, 0.15);
|
|
border-radius: 4px;
|
|
background: rgba(255, 255, 255, 0.7);
|
|
color: rgba(0, 0, 0, 0.45);
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
transition: all 0.15s;
|
|
}
|
|
|
|
.copy-btn:hover {
|
|
background: #fff;
|
|
color: #1677ff;
|
|
border-color: #1677ff;
|
|
}
|
|
|
|
.copy-btn.copied {
|
|
background: #52c41a;
|
|
color: #fff;
|
|
border-color: #52c41a;
|
|
}
|
|
|
|
.code-block {
|
|
background: rgba(128, 128, 128, 0.04);
|
|
padding: 10px 12px;
|
|
margin: 0;
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
font-size: 12.5px;
|
|
line-height: 1.6;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
overflow-x: auto;
|
|
border: none;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.json-key { color: #0550ae; }
|
|
.json-string { color: #116329; }
|
|
.json-number { color: #9a6700; }
|
|
.json-boolean { color: #cf222e; }
|
|
.json-null { color: #8250df; }
|
|
|
|
body.dark .code-block-wrapper {
|
|
border-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
body.dark .code-toolbar {
|
|
background: rgba(255, 255, 255, 0.03);
|
|
border-color: rgba(255, 255, 255, 0.06);
|
|
}
|
|
|
|
body.dark .lang-badge {
|
|
color: rgba(255, 255, 255, 0.4);
|
|
}
|
|
|
|
body.dark .code-block {
|
|
background: rgba(255, 255, 255, 0.03);
|
|
color: rgba(255, 255, 255, 0.88);
|
|
}
|
|
|
|
body.dark .json-key { color: #79c0ff; }
|
|
body.dark .json-string { color: #7ee787; }
|
|
body.dark .json-number { color: #d29922; }
|
|
body.dark .json-boolean { color: #ff7b72; }
|
|
body.dark .json-null { color: #d2a8ff; }
|
|
|
|
body.dark .copy-btn {
|
|
background: rgba(255, 255, 255, 0.06);
|
|
color: rgba(255, 255, 255, 0.45);
|
|
border-color: rgba(255, 255, 255, 0.12);
|
|
}
|
|
|
|
body.dark .copy-btn:hover {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
color: #58a6ff;
|
|
border-color: #58a6ff;
|
|
}
|