pack-test-yanmengs 1.0.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/components.ts ADDED
@@ -0,0 +1,219 @@
1
+ customElements.define(
2
+ "doc-tab",
3
+ class extends HTMLElement {
4
+ constructor() {
5
+ super();
6
+ this.attachShadow({ mode: "open" });
7
+ }
8
+
9
+ set active(visible: boolean) {
10
+ // 使用 style.setProperty 避免覆盖用户原本的 display 逻辑(如果用户也写了 display)
11
+ this.style.display = visible ? "block" : "none";
12
+ }
13
+
14
+ connectedCallback() {
15
+ this.render();
16
+ }
17
+
18
+ render() {
19
+ this.shadowRoot!.innerHTML = `
20
+ <style>
21
+ /* :host 的样式会被外部 style 属性覆盖 */
22
+ :host {
23
+ display: none;
24
+ box-sizing: border-box;
25
+ }
26
+ .content-wrapper {
27
+ padding: 16px;
28
+ }
29
+ </style>
30
+ <div class="content-wrapper">
31
+ <slot></slot>
32
+ </div>
33
+ `;
34
+ }
35
+ },
36
+ );
37
+ customElements.define(
38
+ "doc-tabs",
39
+ class extends HTMLElement {
40
+ constructor() {
41
+ super();
42
+ this.attachShadow({ mode: "open" });
43
+ }
44
+
45
+ // 监听属性变化,如果 active-key 改变,刷新显示
46
+ static get observedAttributes() {
47
+ return ["active-key"];
48
+ }
49
+
50
+ attributeChangedCallback(name: string, oldVal: string, newVal: string) {
51
+ if (name === "active-key" && oldVal !== newVal) {
52
+ this.syncTabs();
53
+ }
54
+ }
55
+
56
+ connectedCallback() {
57
+ this.render();
58
+ this.syncTabs();
59
+ // 监听插槽内容变化(比如动态增删 tab)
60
+ this.shadowRoot!.querySelector("slot")!.addEventListener(
61
+ "slotchange",
62
+ () => this.syncTabs(),
63
+ );
64
+ }
65
+
66
+ private syncTabs() {
67
+ const header = this.shadowRoot!.querySelector(".tabs-nav");
68
+ if (!header) return;
69
+
70
+ const activeKey = this.getAttribute("active-key");
71
+ const children = Array.from(this.querySelectorAll("doc-tab"));
72
+
73
+ header.innerHTML = "";
74
+
75
+ children.forEach((tab: any) => {
76
+ const key = tab.getAttribute("key");
77
+ const title = tab.getAttribute("title") || "Untitled";
78
+ const isActive = key === activeKey;
79
+
80
+ const navItem = document.createElement("div");
81
+ // 这里的 class 是内部组件 UI 专用的
82
+ navItem.className = `nav-item ${isActive ? "active" : ""}`;
83
+ navItem.textContent = title;
84
+ navItem.onclick = () => {
85
+ this.setAttribute("active-key", key);
86
+ };
87
+ header.appendChild(navItem);
88
+
89
+ // 同步子组件显隐
90
+ tab.active = isActive;
91
+ });
92
+ }
93
+
94
+ render() {
95
+ this.shadowRoot!.innerHTML = `
96
+ <style>
97
+ /* 内部基础样式 */
98
+ :host {
99
+ display: block;
100
+ box-sizing: border-box;
101
+ }
102
+ .tabs-nav {
103
+ display: flex;
104
+ background: #fafafa;
105
+ border-bottom: 1px solid #e8e8e8;
106
+ padding: 0 8px;
107
+ }
108
+ .nav-item {
109
+ padding: 10px 16px;
110
+ cursor: pointer;
111
+ font-size: 14px;
112
+ transition: all 0.2s;
113
+ border-bottom: 2px solid transparent;
114
+ color: #666;
115
+ }
116
+ .nav-item.active {
117
+ color: #1890ff;
118
+ border-bottom-color: #1890ff;
119
+ }
120
+ </style>
121
+ <div class="tabs-nav"></div>
122
+ <div class="tabs-body">
123
+ <slot></slot>
124
+ </div>
125
+ `;
126
+ }
127
+ },
128
+ );
129
+
130
+ customElements.define(
131
+ "doc-card",
132
+ class extends HTMLElement {
133
+ constructor() {
134
+ super();
135
+ this.attachShadow({ mode: "open" });
136
+ }
137
+
138
+ static get observedAttributes() {
139
+ return ["title", "bg-color", "slide-color"];
140
+ }
141
+
142
+ attributeChangedCallback() {
143
+ this.render();
144
+ }
145
+
146
+ connectedCallback() {
147
+ this.render();
148
+ }
149
+
150
+ render() {
151
+ const title = this.getAttribute("title") || "Tip";
152
+ // 默认颜色(参考你提供的图片)
153
+ const defaultBg = "#154d24";
154
+ const defaultBorder = "#4ade80";
155
+
156
+ const bgColor = this.getAttribute("bg-color") || defaultBg;
157
+ const borderColor = this.getAttribute("slide-color") || defaultBorder;
158
+
159
+ this.shadowRoot!.innerHTML = `
160
+ <style>
161
+ /* 1. :host 承载用户写的 class 和 style */
162
+ :host {
163
+ display: block;
164
+ margin: 16px 0;
165
+ box-sizing: border-box;
166
+ /* 将颜色存入变量,方便内部引用,也方便用户通过 style="--bg: ..." 覆盖 */
167
+ --internal-bg: ${bgColor};
168
+ --internal-border: ${borderColor};
169
+ }
170
+
171
+ .card-container {
172
+ /* 优先级:用户内联变量 > 属性传值 > 默认值 */
173
+ background-color: var(--doc-card-bg, var(--internal-bg));
174
+ border-left: 4px solid var(--doc-card-border, var(--internal-border));
175
+
176
+ padding: 16px 20px;
177
+ border-radius: 0 4px 4px 0;
178
+ color: #ffffff;
179
+ font-family: sans-serif;
180
+ transition: all 0.3s ease;
181
+ }
182
+
183
+ .card-header {
184
+ display: flex;
185
+ align-items: center;
186
+ font-weight: bold;
187
+ margin-bottom: 8px;
188
+ font-size: 1.1em;
189
+ }
190
+
191
+ .card-icon {
192
+ margin-right: 8px;
193
+ }
194
+
195
+ .card-content {
196
+ line-height: 1.6;
197
+ font-size: 0.95em;
198
+ opacity: 0.9;
199
+ }
200
+
201
+ /* 允许外部样式通过插槽影响内容 */
202
+ ::slotted(*) {
203
+ margin: 0;
204
+ }
205
+ </style>
206
+
207
+ <div class="card-container">
208
+ <div class="card-header">
209
+ <span class="card-icon">💡</span>
210
+ <span class="card-title">${title}</span>
211
+ </div>
212
+ <div class="card-content">
213
+ <slot></slot>
214
+ </div>
215
+ </div>
216
+ `;
217
+ }
218
+ },
219
+ );
package/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ import "./components";
2
+ export * from "./utils";
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "pack-test-yanmengs",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "private": false,
6
+ "main": "index.js",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "author": "",
14
+ "license": "ISC",
15
+ "dependencies": {
16
+ "mdast": "^3.0.0",
17
+ "mdast-util-directive": "^3.1.0",
18
+ "remark-directive": "^4.0.0",
19
+ "remark-parse": "^11.0.0",
20
+ "typescript": "^5.9.3",
21
+ "unified": "^11.0.5"
22
+ },
23
+ "devDependencies": {
24
+ "@types/mdast": "^4.0.4"
25
+ }
26
+ }
Binary file
package/utils.ts ADDED
@@ -0,0 +1,115 @@
1
+ import { unified } from "unified";
2
+ import remarkParse from "remark-parse";
3
+ import remarkDirective from "remark-directive";
4
+ import type { Root, Content, Text, Parent, Code } from "mdast";
5
+ import type { ContainerDirective } from "mdast-util-directive";
6
+
7
+ /**
8
+ * 获取 Markdown AST (抽象语法树)
9
+ */
10
+ function getAST(content: string): Root {
11
+ const processor = unified().use(remarkParse).use(remarkDirective);
12
+ console.log(processor.parse(content), "processor.parse(content)", content);
13
+ return processor.parse(content) as Root;
14
+ }
15
+
16
+ /**
17
+ * 递归转换 AST
18
+ * 将 containerDirective 转换为包含自定义 HTML 标签的文本节点
19
+ */
20
+ const parseAST = (
21
+ ast: Root | Parent,
22
+ map: Map<number, boolean> = new Map(),
23
+ ): Content[] => {
24
+ if (!ast.children) return [];
25
+
26
+ const newChildren: Content[] = [];
27
+
28
+ ast.children.forEach((node: any) => {
29
+ // 判断是否为容器指令 (::: name)
30
+ if (node.type === "containerDirective") {
31
+ // console.log(node, "containerDirective");
32
+ const directiveNode = node as ContainerDirective;
33
+ const column = directiveNode.position?.start.column ?? 0;
34
+ const isClosing = map.has(column);
35
+
36
+ // 1. 构造属性字符串
37
+ const attributes = directiveNode.attributes || {};
38
+ const attrString = Object.entries(attributes)
39
+ .map(([k, v]) => `${k}="${v}"`)
40
+ .join(" ");
41
+
42
+ // 2. 生成标签名
43
+ const tagName = isClosing
44
+ ? `</doc-${directiveNode.name}>`
45
+ : `<doc-${directiveNode.name}${attrString ? " " + attrString : ""}>`;
46
+
47
+ // 3. 创建替换文本节点
48
+ const textNode: Text = {
49
+ type: "text",
50
+ value: tagName,
51
+ };
52
+
53
+ // 4. 状态切换逻辑
54
+ if (isClosing) {
55
+ map.delete(column);
56
+ } else {
57
+ map.set(column, true);
58
+ }
59
+
60
+ newChildren.push(textNode);
61
+
62
+ // 5. 递归处理子节点并将结果展开(平铺化)
63
+ if (directiveNode.children) {
64
+ const processedInner = parseAST(directiveNode, map);
65
+ newChildren.push(...processedInner);
66
+ }
67
+ } else if (node.type === "code") {
68
+ const processedInner = parseAST(getAST(String(node.value)));
69
+ newChildren.push(...processedInner);
70
+ } else {
71
+ // 普通节点递归处理(如果存在子节点)
72
+ if ("children" in node) {
73
+ node.children = parseAST(node as Parent, map) as any;
74
+ }
75
+ newChildren.push(node);
76
+ }
77
+ });
78
+
79
+ return newChildren;
80
+ };
81
+
82
+ /**
83
+ * 将 AST 节点拍平并提取文本内容 (HTML 字符串)
84
+ */
85
+ const parseHtml = (nodes: Content[], currentStr: string = ""): string => {
86
+ let result = currentStr;
87
+
88
+ nodes.forEach((node) => {
89
+ if ("children" in node && node.children) {
90
+ result = parseHtml(node.children as Content[], result);
91
+ } else if ("value" in node) {
92
+ result += node.value + "\n";
93
+ }
94
+ });
95
+
96
+ return result;
97
+ };
98
+ const MarkdownToHtml = (markdownContent: string): string => {
99
+ const ast = getAST(markdownContent);
100
+ const transformedChildren = parseAST(ast);
101
+ return parseHtml(transformedChildren);
102
+ };
103
+ /**
104
+ * 主导出函数
105
+ */
106
+ export const transformMarkdownToHtml = (markdownContent: string): string => {
107
+ let result = MarkdownToHtml(markdownContent);
108
+ // while (result.includes(":::")) {
109
+ // result = MarkdownToHtml(result);
110
+ // console.log(result);
111
+ // }
112
+
113
+ console.log(result);
114
+ return result;
115
+ };