for-humanity 0.1.0
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/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/cli.css +962 -0
- package/dist/cli.js +1241 -0
- package/dist/client.js +5 -0
- package/package.json +92 -0
- package/src/app.tsx +57 -0
- package/src/asset/favicon.svg +5 -0
- package/src/asset/font/architects-daughter/LICENSE.txt +93 -0
- package/src/asset/font/architects-daughter/architects-daughter-latin-400-normal.woff2 +0 -0
- package/src/asset/font/architects-daughter/architects-daughter.css +10 -0
- package/src/asset/font/jetbrains-mono/LICENSE +93 -0
- package/src/asset/font/jetbrains-mono/jetbrains-mono-latin-wght-normal.woff2 +0 -0
- package/src/asset/font/jetbrains-mono/jetbrains-mono.css +11 -0
- package/src/asset/font/pretendard/LICENSE.txt +94 -0
- package/src/asset/font/pretendard/pretendardvariable-dynamic-subset.css +1008 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.0.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.1.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.10.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.11.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.12.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.13.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.14.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.15.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.16.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.17.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.18.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.19.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.2.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.20.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.21.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.22.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.23.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.24.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.25.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.26.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.27.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.28.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.29.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.3.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.30.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.31.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.32.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.33.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.34.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.35.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.36.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.37.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.38.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.39.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.4.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.40.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.41.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.42.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.43.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.44.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.45.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.46.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.47.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.48.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.49.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.5.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.50.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.51.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.52.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.53.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.54.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.55.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.56.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.57.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.58.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.59.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.6.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.60.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.61.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.62.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.63.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.64.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.65.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.66.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.67.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.68.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.69.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.7.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.70.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.71.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.72.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.73.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.74.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.75.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.76.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.77.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.78.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.79.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.8.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.80.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.81.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.82.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.83.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.84.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.85.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.86.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.87.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.88.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.89.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.9.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.90.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.91.woff2 +0 -0
- package/src/cli.ts +162 -0
- package/src/client.ts +322 -0
- package/src/component/widget/prose/_constant/heading-highlight.ts +4 -0
- package/src/component/widget/prose/_constant/section.ts +7 -0
- package/src/component/widget/prose/_constant/status.ts +9 -0
- package/src/component/widget/prose/_function/has-block-content.ts +30 -0
- package/src/component/widget/prose/_function/rehype-head.ts +31 -0
- package/src/component/widget/prose/_function/rehype-sections/_to-section-headings.ts +20 -0
- package/src/component/widget/prose/_function/rehype-sections/rehype-sections.ts +21 -0
- package/src/component/widget/prose/_function/rehype-tables.ts +22 -0
- package/src/component/widget/prose/_function/remark-blocks.ts +112 -0
- package/src/component/widget/prose/_function/remark-flow.ts +26 -0
- package/src/component/widget/prose/_function/remark-links.ts +33 -0
- package/src/component/widget/prose/_function/remark-parts.ts +26 -0
- package/src/component/widget/prose/_function/remark-status.ts +59 -0
- package/src/component/widget/prose/_function/remark-swatch.ts +16 -0
- package/src/component/widget/prose/_function/remark-unknown-directives.ts +25 -0
- package/src/component/widget/prose/_type/doc-section.ts +9 -0
- package/src/component/widget/prose/wg-prose.css +381 -0
- package/src/component/widget/prose/wg-prose.tsx +43 -0
- package/src/component/widget/shell/_constant/cursor-face.ts +9 -0
- package/src/component/widget/shell/_constant/navigation.ts +4 -0
- package/src/component/widget/shell/_constant/reading-line.ts +4 -0
- package/src/component/widget/shell/_function/bind-mobile-navigation.ts +140 -0
- package/src/component/widget/shell/_function/to-toc-groups.ts +38 -0
- package/src/component/widget/shell/_type/doc-outline.ts +16 -0
- package/src/component/widget/shell/_type/toc-group.ts +39 -0
- package/src/component/widget/shell/_wg-shell-nav.css +134 -0
- package/src/component/widget/shell/_wg-shell-nav.tsx +75 -0
- package/src/component/widget/shell/_wg-shell-toc.css +268 -0
- package/src/component/widget/shell/_wg-shell-toc.tsx +57 -0
- package/src/component/widget/shell/wg-shell.css +261 -0
- package/src/component/widget/shell/wg-shell.tsx +147 -0
- package/src/constant/asset.ts +48 -0
- package/src/constant/cli.ts +19 -0
- package/src/constant/copy.ts +108 -0
- package/src/constant/doc.ts +13 -0
- package/src/constant/font.ts +39 -0
- package/src/constant/locale.ts +9 -0
- package/src/constant/site.ts +9 -0
- package/src/constant/status.ts +15 -0
- package/src/content/create-processor.ts +59 -0
- package/src/content/read-docs.test.ts +242 -0
- package/src/content/read-docs.ts +75 -0
- package/src/content/read-home.ts +32 -0
- package/src/content/rehype-heading-ids.ts +34 -0
- package/src/content/remark-report.ts +15 -0
- package/src/content/to-doc-groups.ts +18 -0
- package/src/dev.ts +79 -0
- package/src/env.d.ts +6 -0
- package/src/page/doc/pg-doc.tsx +35 -0
- package/src/page/home/pg-home.tsx +40 -0
- package/src/style/base.css +28 -0
- package/src/style/token.css +162 -0
- package/src/type/doc-content.ts +15 -0
- package/src/type/doc-data.ts +33 -0
- package/src/type/doc-file-data.ts +21 -0
- package/src/type/doc-heading.ts +17 -0
- package/src/type/doc.ts +16 -0
- package/src/type/site-assets.ts +17 -0
- package/src/type/site-config.ts +55 -0
- package/src/type/vfile-data.d.ts +10 -0
- package/src/util/dom/find-hash-target.ts +18 -0
- package/src/util/dom/reveal-hash-target.ts +15 -0
- package/src/util/error/to-error-message.ts +10 -0
- package/src/util/font/to-font-css.test.ts +41 -0
- package/src/util/font/to-font-css.ts +28 -0
- package/src/util/mermaid/render-flow.js +321 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {escape as escapeHtml, escapeRegExp} from "es-toolkit/string";
|
|
2
|
+
import type {Root} from "mdast";
|
|
3
|
+
import {findAndReplace, type RegExpMatchObject} from "mdast-util-find-and-replace";
|
|
4
|
+
import type {VFile} from "vfile";
|
|
5
|
+
import {status_candidate_pattern, status_date_pattern} from "@/component/widget/prose/_constant/status";
|
|
6
|
+
import {copy_warn_unknown_status} from "@/constant/copy";
|
|
7
|
+
import type {SiteConfig} from "@/type/site-config";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 설정 문구의 상태 표지 변환 · 괄호 제거 · 제목·링크 제외
|
|
11
|
+
* 미등록 날짜 문구는 파일 경고 · remark-report에서 출력
|
|
12
|
+
*/
|
|
13
|
+
export const remarkStatus = (options: {status: SiteConfig["status"]}) => {
|
|
14
|
+
const statuses = options.status.toSorted((a, b) => b.phrase.length - a.phrase.length);
|
|
15
|
+
const phrases = statuses.map((item) => [escapeRegExp(item.phrase), ...(item.date ? [`(?: ${status_date_pattern})?`] : [])].join("")).join("|");
|
|
16
|
+
const pattern = new RegExp(String.raw`\((${phrases})\)|(${phrases})`, "g");
|
|
17
|
+
const ignore = ["link", "linkReference", "heading"];
|
|
18
|
+
|
|
19
|
+
return (tree: Root, file: VFile) => {
|
|
20
|
+
findAndReplace(
|
|
21
|
+
tree,
|
|
22
|
+
[
|
|
23
|
+
pattern,
|
|
24
|
+
(_match: string, inParens: string | undefined, bare: string | undefined) => {
|
|
25
|
+
const text = inParens ?? bare;
|
|
26
|
+
const item = statuses.find((status) => text?.startsWith(status.phrase));
|
|
27
|
+
|
|
28
|
+
// findAndReplace의 false: 원문 유지
|
|
29
|
+
if (text === undefined || item === undefined) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
type: "html",
|
|
35
|
+
value: `<span class="wg_prose__pill wg_prose__pill--${item.kind}">${escapeHtml(text)}</span>`,
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
{ignore},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
findAndReplace(
|
|
43
|
+
tree,
|
|
44
|
+
[
|
|
45
|
+
status_candidate_pattern,
|
|
46
|
+
(match: string, info: RegExpMatchObject) => {
|
|
47
|
+
// 치환된 텍스트에 position 부재 · 위치가 남은 가까운 조상 기준
|
|
48
|
+
file.message(
|
|
49
|
+
`${copy_warn_unknown_status}: ${match}`,
|
|
50
|
+
info.stack.findLast((node) => node.position !== undefined),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return false;
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
{ignore},
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type {Root} from "mdast";
|
|
2
|
+
import {visit} from "unist-util-visit";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 인라인 hex 색 값의 칩 표시
|
|
6
|
+
* 문서별 색은 --wg-prose-chip-color로 전달 · 고정 스타일시트 값 불가
|
|
7
|
+
*/
|
|
8
|
+
export const remarkSwatch = () => (tree: Root) => {
|
|
9
|
+
visit(tree, "inlineCode", (node) => {
|
|
10
|
+
if (/^#[0-9a-f]{3,8}$/i.test(node.value.trim())) {
|
|
11
|
+
node.data = {
|
|
12
|
+
hProperties: {className: ["wg_prose__color"], style: `--wg-prose-chip-color: ${node.value.trim()}`},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type {Root} from "mdast";
|
|
2
|
+
import {SKIP, visit} from "unist-util-visit";
|
|
3
|
+
import type {VFile} from "vfile";
|
|
4
|
+
import {copy_error_unknown_directive} from "@/constant/copy";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* hName 없는 블록 지시문은 작성 오류
|
|
8
|
+
* textDirective는 원문 복원 · 예:이것 같은 일반 문장의 오인 방지
|
|
9
|
+
*/
|
|
10
|
+
export const remarkUnknownDirectives = () => (tree: Root, file: VFile) => {
|
|
11
|
+
visit(tree, (node, index, parent) => {
|
|
12
|
+
if (node.type === "textDirective" && parent !== undefined && index !== undefined) {
|
|
13
|
+
parent.children[index] = {
|
|
14
|
+
type: "text",
|
|
15
|
+
value: String(file.value).slice(node.position?.start.offset, node.position?.end.offset),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return SKIP;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if ((node.type === "leafDirective" || node.type === "containerDirective") && node.data?.hName === undefined) {
|
|
22
|
+
file.fail(`${copy_error_unknown_directive}: ${node.name}`, node);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
};
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Markdown 본문과 생성 부품의 표현
|
|
3
|
+
* 요소 선택자는 소유 root 안에 한정
|
|
4
|
+
*/
|
|
5
|
+
.wg_prose__root {
|
|
6
|
+
/* stylelint-disable selector-disallowed-list -- Markdown 생성 태그 영역 */
|
|
7
|
+
& h1 {
|
|
8
|
+
margin: 0 0 var(--app-space-stack);
|
|
9
|
+
color: var(--app-color-text-strong);
|
|
10
|
+
font-size: var(--app-font-size-title);
|
|
11
|
+
font-weight: var(--app-font-weight-title);
|
|
12
|
+
line-height: var(--app-line-height-title);
|
|
13
|
+
letter-spacing: var(--app-letter-spacing-title);
|
|
14
|
+
text-wrap: balance;
|
|
15
|
+
|
|
16
|
+
/* 문서 탐색 라벨과 같은 글자 위쪽 기준 · 글꼴 크기별 내부 여백 제거 */
|
|
17
|
+
text-box: trim-start cap alphabetic;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
& p {
|
|
21
|
+
margin: 0 0 var(--app-space-stack);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
& img {
|
|
25
|
+
max-width: 100%;
|
|
26
|
+
height: auto;
|
|
27
|
+
vertical-align: middle;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* 제목 아래 첫 문단 · 소개 크기 */
|
|
31
|
+
& h1 + p {
|
|
32
|
+
font-size: var(--app-font-size-lead);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
& h2 {
|
|
36
|
+
margin: var(--app-space-section) 0 var(--app-space-stack);
|
|
37
|
+
padding-top: var(--app-space-block);
|
|
38
|
+
border-top: 1px dotted var(--app-color-border);
|
|
39
|
+
color: var(--app-color-text-strong);
|
|
40
|
+
font-size: var(--app-font-size-section);
|
|
41
|
+
font-weight: var(--app-font-weight-strong);
|
|
42
|
+
line-height: var(--app-line-height-heading);
|
|
43
|
+
letter-spacing: var(--app-letter-spacing-heading);
|
|
44
|
+
text-wrap: balance;
|
|
45
|
+
scroll-margin-top: var(--app-space-anchor);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* 머리 아래 첫 절의 중복 구분선 제거 */
|
|
49
|
+
& .wg_prose__head + h2 {
|
|
50
|
+
margin-top: var(--app-space-group);
|
|
51
|
+
padding-top: 0;
|
|
52
|
+
border-top: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* 가름 아래 절의 중복 구분선 제거 */
|
|
56
|
+
& .wg_prose__part + h2 {
|
|
57
|
+
margin-top: var(--app-space-block);
|
|
58
|
+
padding-top: 0;
|
|
59
|
+
border-top: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
& h3 {
|
|
63
|
+
margin: var(--app-space-group) 0 var(--app-space-inline);
|
|
64
|
+
color: var(--app-color-text-strong);
|
|
65
|
+
font-size: var(--app-font-size-lead);
|
|
66
|
+
font-weight: var(--app-font-weight-strong);
|
|
67
|
+
line-height: var(--app-line-height-tight);
|
|
68
|
+
scroll-margin-top: var(--app-space-anchor);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
& h4 {
|
|
72
|
+
margin: var(--app-space-group) 0 6px;
|
|
73
|
+
color: var(--app-color-text-strong);
|
|
74
|
+
font-size: var(--app-font-size-body);
|
|
75
|
+
font-weight: var(--app-font-weight-strong);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
& h5 {
|
|
79
|
+
margin: var(--app-space-block) 0 6px;
|
|
80
|
+
color: var(--app-color-text-muted);
|
|
81
|
+
font-size: var(--app-font-size-dense);
|
|
82
|
+
font-weight: var(--app-font-weight-strong);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
& a {
|
|
86
|
+
color: var(--app-color-link);
|
|
87
|
+
text-decoration: none;
|
|
88
|
+
text-underline-offset: 2px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
& a:visited {
|
|
92
|
+
color: var(--app-color-link-visited);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
& a:hover {
|
|
96
|
+
color: var(--app-color-link-hover);
|
|
97
|
+
text-decoration: underline;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
& a:focus-visible {
|
|
101
|
+
border-radius: var(--app-radius-mark);
|
|
102
|
+
outline: var(--app-outline-focus);
|
|
103
|
+
outline-offset: var(--app-outline-focus-offset);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
& strong {
|
|
107
|
+
color: var(--app-color-text-strong);
|
|
108
|
+
font-weight: var(--app-font-weight-strong);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
& ul {
|
|
112
|
+
margin: 0 0 var(--app-space-stack);
|
|
113
|
+
padding-left: 20px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
& ol {
|
|
117
|
+
margin: 0 0 var(--app-space-stack);
|
|
118
|
+
padding-left: 22px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
& li {
|
|
122
|
+
margin-bottom: 4px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
& li > ul {
|
|
126
|
+
margin: 4px 0 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
& li > ol {
|
|
130
|
+
margin: 4px 0 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* JetBrains Mono 합자 비활성 · >=, != 원문 구분 */
|
|
134
|
+
& code {
|
|
135
|
+
padding: 1px 3px;
|
|
136
|
+
border-radius: var(--app-radius-inline);
|
|
137
|
+
background: var(--app-color-code);
|
|
138
|
+
color: var(--app-color-text-strong);
|
|
139
|
+
font-family: var(--app-font-mono);
|
|
140
|
+
font-size: 1em;
|
|
141
|
+
font-variant-ligatures: none;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* 코드 블록 · Shiki의 --app-code-* 참조 */
|
|
145
|
+
& pre {
|
|
146
|
+
overflow-x: auto;
|
|
147
|
+
margin: var(--app-space-block) 0;
|
|
148
|
+
padding: var(--app-space-stack) var(--app-space-block);
|
|
149
|
+
border: 1px solid var(--app-color-border);
|
|
150
|
+
border-radius: var(--app-radius-box);
|
|
151
|
+
background: var(--app-color-code);
|
|
152
|
+
font-family: var(--app-font-mono);
|
|
153
|
+
font-size: var(--app-font-size-detail);
|
|
154
|
+
font-variant-ligatures: none;
|
|
155
|
+
line-height: var(--app-line-height-code);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
& pre code {
|
|
159
|
+
padding: 0;
|
|
160
|
+
background: none;
|
|
161
|
+
color: inherit;
|
|
162
|
+
font-size: inherit;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* 인용과 첫머리 안내 */
|
|
166
|
+
& blockquote {
|
|
167
|
+
margin: var(--app-space-block) 0;
|
|
168
|
+
padding: 0 0 0 var(--app-space-block);
|
|
169
|
+
border-left: 1px solid var(--app-color-border);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
& blockquote > :last-child {
|
|
173
|
+
margin-bottom: 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
& table {
|
|
177
|
+
width: 100%;
|
|
178
|
+
border-collapse: collapse;
|
|
179
|
+
font-size: var(--app-font-size-dense);
|
|
180
|
+
font-variant-numeric: tabular-nums;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
& th {
|
|
184
|
+
padding: var(--app-space-inline) var(--app-space-stack);
|
|
185
|
+
border-bottom: 1px dotted var(--app-color-border);
|
|
186
|
+
color: var(--app-color-text-strong);
|
|
187
|
+
font-weight: var(--app-font-weight-strong);
|
|
188
|
+
text-align: left;
|
|
189
|
+
vertical-align: bottom;
|
|
190
|
+
white-space: nowrap;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
& td {
|
|
194
|
+
padding: var(--app-space-inline) var(--app-space-stack);
|
|
195
|
+
border-bottom: 1px solid var(--app-color-border-soft);
|
|
196
|
+
text-align: left;
|
|
197
|
+
vertical-align: top;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
& tbody tr:last-child td {
|
|
201
|
+
border-bottom: 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* 표의 읽는 행 표시 */
|
|
205
|
+
& tbody tr:hover td {
|
|
206
|
+
background: var(--app-color-accent-soft);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
& hr {
|
|
210
|
+
margin: var(--app-space-section) 0;
|
|
211
|
+
border: 0;
|
|
212
|
+
border-top: 1px dotted var(--app-color-border);
|
|
213
|
+
}
|
|
214
|
+
/* stylelint-enable selector-disallowed-list */
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* 공통 문서 머리 · 제목·소개 */
|
|
218
|
+
.wg_prose__head {
|
|
219
|
+
margin-bottom: var(--app-space-inline);
|
|
220
|
+
padding-bottom: var(--app-space-block);
|
|
221
|
+
border-bottom: 1px dotted var(--app-color-border);
|
|
222
|
+
|
|
223
|
+
/* stylelint-disable-next-line selector-disallowed-list -- Markdown 생성 머리 */
|
|
224
|
+
& > :last-child {
|
|
225
|
+
margin-bottom: 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* stylelint-disable selector-disallowed-list -- README의 가운데 정렬 HTML 소개 */
|
|
229
|
+
& [align="center"] {
|
|
230
|
+
padding: var(--app-space-group) 0;
|
|
231
|
+
text-align: center;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
& [align="center"] > h1 {
|
|
235
|
+
margin-block: var(--app-space-group);
|
|
236
|
+
font-size: 24px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
& [align="center"] > p {
|
|
240
|
+
margin-bottom: var(--app-space-block);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
& [align="center"] > img {
|
|
244
|
+
width: 72px;
|
|
245
|
+
image-rendering: pixelated;
|
|
246
|
+
}
|
|
247
|
+
/* stylelint-enable selector-disallowed-list */
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* 제목 글자만 강조 · 여러 줄에서도 각 줄을 칠하고 문서 배치는 유지 */
|
|
251
|
+
.wg_prose__headingText {
|
|
252
|
+
box-decoration-break: clone;
|
|
253
|
+
|
|
254
|
+
/* 줄바꿈된 글자 뒤의 바탕은 opacity 레이어로 분리할 수 없어 배경색만 전환 */
|
|
255
|
+
transition: background-color var(--app-motion-duration-highlight-fade) var(--app-motion-easing-out);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.wg_prose__headingText--highlighted {
|
|
259
|
+
background-color: var(--app-color-anchor-highlight);
|
|
260
|
+
transition: none;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* ::part 가름의 공통 표현 */
|
|
264
|
+
.wg_prose__part {
|
|
265
|
+
margin-top: var(--app-space-section);
|
|
266
|
+
padding-top: var(--app-space-stack);
|
|
267
|
+
border-top: 1px dotted var(--app-color-border);
|
|
268
|
+
color: var(--app-color-text-strong);
|
|
269
|
+
font-size: var(--app-font-size-part);
|
|
270
|
+
font-weight: var(--app-font-weight-strong);
|
|
271
|
+
line-height: var(--app-line-height-heading);
|
|
272
|
+
letter-spacing: var(--app-letter-spacing-heading);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* 넓은 표의 내부 가로 스크롤 */
|
|
276
|
+
.wg_prose__table {
|
|
277
|
+
overflow-x: auto;
|
|
278
|
+
margin: var(--app-space-block) 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* 상태 표지 · 확인 초록, 미확인 amber */
|
|
282
|
+
.wg_prose__pill {
|
|
283
|
+
display: inline-block;
|
|
284
|
+
padding: 0 5px;
|
|
285
|
+
border-radius: var(--app-radius-inline);
|
|
286
|
+
font-size: var(--app-font-size-detail);
|
|
287
|
+
font-weight: var(--app-font-weight-strong);
|
|
288
|
+
line-height: 1.6;
|
|
289
|
+
white-space: nowrap;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.wg_prose__pill--verified {
|
|
293
|
+
background: var(--app-color-verified-soft);
|
|
294
|
+
color: var(--app-color-verified);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.wg_prose__pill--unverified {
|
|
298
|
+
background: var(--app-color-unverified-soft);
|
|
299
|
+
color: var(--app-color-unverified);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* 인라인 hex 색 칩 · remark-swatch의 CSS 변수 */
|
|
303
|
+
.wg_prose__color {
|
|
304
|
+
/* 색 칩과 값 사이 줄바꿈 방지 */
|
|
305
|
+
white-space: nowrap;
|
|
306
|
+
|
|
307
|
+
&::before {
|
|
308
|
+
display: inline-block;
|
|
309
|
+
width: 0.8em;
|
|
310
|
+
height: 0.8em;
|
|
311
|
+
margin-right: 5px;
|
|
312
|
+
border: 1px solid var(--app-color-border);
|
|
313
|
+
border-radius: var(--app-radius-mark);
|
|
314
|
+
background: var(--wg-prose-chip-color, transparent);
|
|
315
|
+
content: "";
|
|
316
|
+
vertical-align: -0.05em;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* 빌드 시 생성한 격자 SVG 흐름도 */
|
|
321
|
+
.wg_prose__flow {
|
|
322
|
+
overflow-x: auto;
|
|
323
|
+
margin: var(--app-space-block) 0;
|
|
324
|
+
padding: var(--app-space-block) 0;
|
|
325
|
+
|
|
326
|
+
/* stylelint-disable-next-line selector-disallowed-list -- 외부 렌더러 생성 SVG */
|
|
327
|
+
& svg {
|
|
328
|
+
display: block;
|
|
329
|
+
max-width: 100%;
|
|
330
|
+
height: auto;
|
|
331
|
+
margin: 0 auto;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* 보충 설명 · 본문과 분리되는 제목·왼쪽 선 */
|
|
336
|
+
.wg_prose__note {
|
|
337
|
+
margin: var(--app-space-block) 0;
|
|
338
|
+
padding-left: var(--app-space-block);
|
|
339
|
+
border-left: 2px solid var(--app-color-border);
|
|
340
|
+
|
|
341
|
+
& > :last-child {
|
|
342
|
+
margin-bottom: 0;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.wg_prose__noteTitle {
|
|
347
|
+
display: block;
|
|
348
|
+
margin-bottom: var(--app-space-inline);
|
|
349
|
+
color: var(--app-color-text-strong);
|
|
350
|
+
font-weight: var(--app-font-weight-strong);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* 구현 상세 · 펼침 상태와 marker는 native Details 소유 */
|
|
354
|
+
.wg_prose__details {
|
|
355
|
+
margin: var(--app-space-block) 0;
|
|
356
|
+
padding-bottom: var(--app-space-stack);
|
|
357
|
+
border-top: 1px dotted var(--app-color-border);
|
|
358
|
+
border-bottom: 1px dotted var(--app-color-border);
|
|
359
|
+
|
|
360
|
+
& > :last-child {
|
|
361
|
+
margin-bottom: 0;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.wg_prose__detailsSummary {
|
|
366
|
+
padding: var(--app-space-inline) 0;
|
|
367
|
+
color: var(--app-color-link);
|
|
368
|
+
font-weight: var(--app-font-weight-strong);
|
|
369
|
+
cursor: pointer;
|
|
370
|
+
|
|
371
|
+
&:hover {
|
|
372
|
+
color: var(--app-color-link-hover);
|
|
373
|
+
text-decoration: underline;
|
|
374
|
+
text-underline-offset: 2px;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
&:focus-visible {
|
|
378
|
+
outline: var(--app-outline-focus);
|
|
379
|
+
outline-offset: var(--app-outline-focus-offset);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import "./wg-prose.css";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Markdown HTML과 본문 부품의 공통 표현 입력
|
|
6
|
+
* remark·rehype 플러그인은 _function 소유
|
|
7
|
+
*/
|
|
8
|
+
export interface WgProseProps {
|
|
9
|
+
/**
|
|
10
|
+
* 머리를 포함한 렌더링 완료 HTML
|
|
11
|
+
*/
|
|
12
|
+
html?: string;
|
|
13
|
+
/**
|
|
14
|
+
* README가 없을 때의 안내 머리
|
|
15
|
+
*/
|
|
16
|
+
head?: {
|
|
17
|
+
/**
|
|
18
|
+
* 문서 h1 제목
|
|
19
|
+
*/
|
|
20
|
+
title: string;
|
|
21
|
+
/**
|
|
22
|
+
* 제목 아래 소개 · 선택
|
|
23
|
+
*/
|
|
24
|
+
lead?: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const WgProse = (props: WgProseProps) => {
|
|
29
|
+
if (props.html !== undefined) {
|
|
30
|
+
return <article className={clsx("wg_prose__root")} dangerouslySetInnerHTML={{__html: props.html}} />;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<article className={clsx("wg_prose__root")}>
|
|
35
|
+
{props.head !== undefined && (
|
|
36
|
+
<header className={clsx("wg_prose__head")}>
|
|
37
|
+
<h1>{props.head.title}</h1>
|
|
38
|
+
{props.head.lead !== undefined && <p>{props.head.lead}</p>}
|
|
39
|
+
</header>
|
|
40
|
+
)}
|
|
41
|
+
</article>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import {navigation_mobile_query} from "@/component/widget/shell/_constant/navigation";
|
|
2
|
+
import {revealHashTarget} from "@/util/dom/reveal-hash-target";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 같은 문서 탐색을 데스크톱 사이드바와 모바일 모달 사이에서 이동
|
|
6
|
+
* 닫기·크기 변경에서 스크롤과 포커스 복원 · 초기화 실패 시 기본 목록 유지
|
|
7
|
+
*/
|
|
8
|
+
export const bindMobileNavigation = () => {
|
|
9
|
+
const navigation = document.querySelector<HTMLElement>("[data-navigation]");
|
|
10
|
+
const dialog = document.querySelector<HTMLDialogElement>("[data-navigation-drawer]");
|
|
11
|
+
const content = document.querySelector<HTMLElement>("[data-navigation-content]");
|
|
12
|
+
const openButton = document.querySelector<HTMLButtonElement>("[data-navigation-open]");
|
|
13
|
+
const closeButton = document.querySelector<HTMLButtonElement>("[data-navigation-close]");
|
|
14
|
+
const sidebar = navigation?.parentElement;
|
|
15
|
+
|
|
16
|
+
if (!navigation || !dialog || !content || !openButton || !closeButton || !sidebar) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const mobile = matchMedia(navigation_mobile_query);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Escape 등 네이티브 닫기에서도 배경 스크롤과 버튼의 상태 복원
|
|
24
|
+
*/
|
|
25
|
+
const handleClose = () => {
|
|
26
|
+
// 닫기 직후 다시 연 모달에 이전 close 이벤트가 개입하지 않도록 함
|
|
27
|
+
if (dialog.open) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
document.body.classList.remove("wg_shell__root--drawerOpen");
|
|
32
|
+
openButton.setAttribute("aria-expanded", "false");
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 기본 링크 이동보다 먼저 잠금 해제 · 네이티브 close가 열기 버튼으로 포커스 복원
|
|
37
|
+
*/
|
|
38
|
+
const closeNavigation = () => {
|
|
39
|
+
dialog.close();
|
|
40
|
+
handleClose();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 네이티브 모달로 외부 포커스 차단 · 모바일에서만 열기
|
|
45
|
+
*/
|
|
46
|
+
const handleOpen = () => {
|
|
47
|
+
if (!mobile.matches || dialog.open) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
dialog.showModal();
|
|
52
|
+
document.body.classList.add("wg_shell__root--drawerOpen");
|
|
53
|
+
openButton.setAttribute("aria-expanded", "true");
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 대화상자 바깥 배경 클릭만 닫기 · 내부의 빈 공간은 유지
|
|
58
|
+
*/
|
|
59
|
+
const handleBackdropClick = (event: MouseEvent) => {
|
|
60
|
+
const bounds = dialog.getBoundingClientRect();
|
|
61
|
+
|
|
62
|
+
if (event.target === dialog && (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom)) {
|
|
63
|
+
closeNavigation();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 링크의 기본 이동은 유지 · 목차는 닫은 뒤 실제 제목에 키보드 위치 전달
|
|
69
|
+
*/
|
|
70
|
+
const handleNavigationClick = (event: MouseEvent) => {
|
|
71
|
+
if (
|
|
72
|
+
!dialog.open ||
|
|
73
|
+
event.defaultPrevented ||
|
|
74
|
+
event.button !== 0 ||
|
|
75
|
+
event.metaKey ||
|
|
76
|
+
event.ctrlKey ||
|
|
77
|
+
event.shiftKey ||
|
|
78
|
+
event.altKey ||
|
|
79
|
+
!(event.target instanceof Element)
|
|
80
|
+
) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const link = event.target.closest("a[href]");
|
|
85
|
+
|
|
86
|
+
if (!(link instanceof HTMLAnchorElement) || (link.target !== "" && link.target !== "_self") || link.hasAttribute("download")) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
closeNavigation();
|
|
91
|
+
|
|
92
|
+
if (link.origin === location.origin && link.pathname === location.pathname && link.search === location.search && link.hash) {
|
|
93
|
+
const target = revealHashTarget(link.hash);
|
|
94
|
+
|
|
95
|
+
if (target) {
|
|
96
|
+
target.tabIndex = -1;
|
|
97
|
+
target.focus({preventScroll: true});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 반응형 전환에도 목록 중복 없음 · 열린 모달을 데스크톱으로 가져가기 전에 닫기
|
|
104
|
+
*/
|
|
105
|
+
const handleLayoutChange = () => {
|
|
106
|
+
const focused = document.activeElement;
|
|
107
|
+
|
|
108
|
+
if (mobile.matches) {
|
|
109
|
+
openButton.classList.add("wg_shell__menuToggle--visible");
|
|
110
|
+
content.append(navigation);
|
|
111
|
+
|
|
112
|
+
if (focused instanceof HTMLElement && navigation.contains(focused)) {
|
|
113
|
+
openButton.focus({preventScroll: true});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
closeNavigation();
|
|
120
|
+
sidebar.append(navigation);
|
|
121
|
+
|
|
122
|
+
if (focused instanceof HTMLElement && navigation.contains(focused)) {
|
|
123
|
+
focused.focus({preventScroll: true});
|
|
124
|
+
} else if (focused === openButton || (focused instanceof HTMLElement && dialog.contains(focused))) {
|
|
125
|
+
sidebar.querySelector<HTMLAnchorElement>(".wg_shell__brand")?.focus({preventScroll: true});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// CSS의 폭 조건이 먼저 버튼을 숨겨 포커스가 사라지지 않도록 복원 후 숨김
|
|
129
|
+
openButton.classList.remove("wg_shell__menuToggle--visible");
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
openButton.addEventListener("click", handleOpen);
|
|
133
|
+
closeButton.addEventListener("click", closeNavigation);
|
|
134
|
+
dialog.addEventListener("close", handleClose);
|
|
135
|
+
dialog.addEventListener("click", handleBackdropClick);
|
|
136
|
+
navigation.addEventListener("click", handleNavigationClick);
|
|
137
|
+
mobile.addEventListener("change", handleLayoutChange);
|
|
138
|
+
handleLayoutChange();
|
|
139
|
+
sidebar.classList.add("wg_shell__sidebar--navigationReady");
|
|
140
|
+
};
|