UNPKG

@msom/xbuild

Version:

@msom/xbuild

488 lines (445 loc) 12.3 kB
import { createRequestJson } from "@msom/common"; import { createSingleRef, mountWith } from "@msom/dom"; import { Component, component } from "@msom/component"; import { createReaction, reactive } from "@msom/reaction"; import { jsx, jsxs } from "@msom/dom/jsx-runtime"; //#region src/template/Menu.tsx let FileLikeType = /* @__PURE__ */ function(FileLikeType$1) { FileLikeType$1["File"] = "file"; FileLikeType$1["Directory"] = "directory"; return FileLikeType$1; }({}); function isDirectory(fileLike) { return fileLike.type === FileLikeType.Directory; } /** * 检查目录或其子节点是否包含激活路径 */ function hasActiveChild(node, activePath) { if (node.path === activePath) return true; for (const child of node.children) if (isDirectory(child)) { if (hasActiveChild(child, activePath)) return true; } else if (child.path === activePath) return true; return false; } var Menu = @component("FileMenu", { events: { select: "string" } }) class extends Component { init() { super.init(); this.tree = []; this.showTreeInfo = []; this.onclean(createReaction(() => { return this.tree; }, () => { if (!this.tree) { this.showTreeInfo = reactive([]); return; } const statusMap = /* @__PURE__ */ new Map(); this.showTreeInfo.forEach((node) => { if (isDirectory(node)) statusMap.set(node.path, node.status); }); const processTree = (nodes) => { return nodes.map((node) => { if (isDirectory(node)) { const existingStatus = statusMap.get(node.path); const status = existingStatus || { collapsed: false }; const dirNode = { ...node, status, children: processTree(node.children) }; return dirNode; } return node; }); }; this.showTreeInfo = reactive(processTree(this.tree)); }).exec().disposer()); } toggleDirectoryCollapse(node) { node.status.collapsed = !node.status.collapsed; } setActiveFile(file) { this.emit("select", file); } renderTree(tree, level) { return /* @__PURE__ */ jsx("div", { class: "tree-container", children: tree.map((node) => { const isDir = isDirectory(node); const hasActive = isDir && hasActiveChild(node, this.activePath); const showActive = isDir && hasActive && node.status.collapsed; return /* @__PURE__ */ jsxs("div", { class: `tree-node ${isDir ? "directory" : "file"}`, children: [/* @__PURE__ */ jsx("div", { class: ["node-content", (this.activePath === node.path || showActive) && "active"], children: /* @__PURE__ */ jsxs("div", { class: "node-left", style: `--level: ${level}`, children: [ typeof node.icon === "function" ? node.icon(node) : node.icon || /* @__PURE__ */ jsx("i", { class: `node-icon ${isDir ? "folder-icon" : "file-icon"} ${isDir && node.status.collapsed ? "collapsed" : ""}` }), /* @__PURE__ */ jsx("div", { class: "node-name", onclick: () => { if (isDir) this.toggleDirectoryCollapse(node); else this.setActiveFile(node); }, children: node.name }), isDir && /* @__PURE__ */ jsx("div", { class: "chevron", onclick: () => this.toggleDirectoryCollapse(node), children: /* @__PURE__ */ jsx("i", { class: `chevron-icon ${node.status.collapsed ? "collapsed" : ""}` }) }) ] }) }), isDir && /* @__PURE__ */ jsx("div", { class: `children-container ${node.status.collapsed ? "collapsed" : "expanded"}`, style: `--child-height: ${this.calculateChildHeight(node)}px`, children: this.renderTree(node.children, level + 1) })] }); }) }); } calculateChildHeight(node) { if (node.status.collapsed) return 0; const countChildren = (nodes) => { return nodes.reduce((count, child) => { count++; if (isDirectory(child) && !child.status.collapsed) count += countChildren(child.children); return count; }, 0); }; const childCount = countChildren(node.children); return childCount * 40; } render() { return /* @__PURE__ */ jsx("div", { class: "menu-tree-container", children: this.renderTree(this.showTreeInfo || [], 0) }); } }; function addMenuStyle() { const styleId = "file-menu-style"; if (document.getElementById(styleId)) return; const style = document.createElement("style"); style.id = styleId; style.textContent = ` .menu-tree-container { flex: 1; overflow-y: auto; padding: 12px 0; } /* 自定义滚动条 */ .menu-tree-container::-webkit-scrollbar { width: 8px; } .menu-tree-container::-webkit-scrollbar-track { background: var(--scrollbar-track); } .menu-tree-container::-webkit-scrollbar-thumb { background-color: var(--scrollbar-thumb); border-radius: 4px; } .menu-tree-container::-webkit-scrollbar-thumb:hover { background: #5a5d6e; } /* 树形结构样式 - 使用div重构 */ .tree-container { display: flex; flex-direction: column; } .tree-node { display: flex; flex-direction: column; padding: 0; margin: 0; } .node-content { display: flex; height: 40px; flex-direction: column; position: relative; } .node-left { display: flex; align-items: center; padding: 8px 16px; cursor: pointer; transition: all 0.2s ease; gap: 8px; /* 添加层级缩进 */ padding-left: calc(16px + var(--level) * var(--level-indent)); } .node-left:hover { background-color: var(--hover-bg); } .node-content.active .node-left { background-color: var(--active-bg); } .node-content.active .node-left::before { content: ''; position: absolute; left: 0; top: 0; height: 100%; width: 3px; background-color: var(--accent-color); } .node-icon { display: flex; align-items: center; justify-content: center; color: var(--text-secondary); font-style: normal; position: relative; transition: transform 0.2s ease; } .node-content.active .node-icon { color: var(--accent-color); } /* 简约文件图标 */ .file-icon { width: 14px; height: 16px; } .file-icon::before { content: ''; position: absolute; top: 0; left: 0; width: 12px; height: 14px; background-color: var(--text-secondary); border-radius: 1px; clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 0, 80% 0, 80% 20%, 100% 20%); } .node-content.active .file-icon::before { background-color: var(--accent-color); } /* 简约文件夹图标 */ .folder-icon { width: 16px; height: 14px; } .folder-icon::before { content: ''; position: absolute; top: 0; left: 0; width: 16px; height: 12px; background-color: var(--text-secondary); border-radius: 2px; clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 0, 20% 0, 30% 25%, 70% 25%, 80% 0); } .folder-icon.collapsed::before { background-color: var(--text-secondary); } .node-content.active .folder-icon::before { background-color: var(--accent-color); } .node-name { flex: 1; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; user-select: none; padding: 2px 0; } .chevron { display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; transition: transform 0.2s ease; cursor: pointer; border-radius: 4px; margin-left: -4px; } .chevron:hover { background-color: rgba(100, 108, 255, 0.1); } .chevron-icon { display: inline-block; width: 0; height: 0; border-style: solid; border-width: 4px 0 4px 6px; border-color: transparent transparent transparent var(--text-secondary); transition: transform 0.2s ease; } .chevron-icon.collapsed { transform: rotate(0deg); } .chevron-icon:not(.collapsed) { transform: rotate(90deg); } .children-container { overflow: hidden; transition: height 0.3s ease, opacity 0.2s ease; height: 0; opacity: 0; } .children-container.expanded { height: var(--child-height, auto); opacity: 1; } `; document.head.appendChild(style); } addMenuStyle(); //#endregion //#region src/template/App.tsx var App = @component("App2") class extends Component { render() { return /* @__PURE__ */ jsxs("div", { class: "app-container", children: [/* @__PURE__ */ jsxs("div", { class: "sidebar", children: [/* @__PURE__ */ jsxs("div", { class: "sidebar-header", children: [/* @__PURE__ */ jsx("div", { class: "logo", children: "DEMO" }), /* @__PURE__ */ jsx("div", { class: "title", children: "Demo 列表" })] }), /* @__PURE__ */ jsx(Menu, { $ref: this.menu, activePath: this.activePath, select: (file) => this.handleFileSelect(file), tree: this.tree || [] })] }), /* @__PURE__ */ jsx("div", { class: "content", children: /* @__PURE__ */ jsx("iframe", { $ref: this.iframe, class: "demo-frame" }) })] }); } init() { super.init(); this.activePath = void 0; this.menu = createSingleRef(); this.iframe = createSingleRef(); } handleFileSelect(file) { this.activePath = file.path; requestAnimationFrame(() => { this.iframe.current.src = `/demo?modulePath=${file.path}`; }); } loadFileTree() { return createRequestJson("/demo/file-tree"); } mounted() { this.loadFileTree().then((tree) => { this.tree = tree; }); } }; function addAppStyle() { const styleId = "app-style"; if (document.getElementById(styleId)) return; const style = document.createElement("style"); style.id = styleId; style.textContent = ` /* 暗色主题 */ :root { --bg-dark: #1e1f25; --sidebar-bg: #25262e; --sidebar-header: #1a1b21; --border-color: #383a46; --text-primary: #e2e4e9; --text-secondary: #a2a5b5; --accent-color: #646cff; --hover-bg: #2d2e3a; --active-bg: #343747; --scrollbar-thumb: #4a4d5c; --scrollbar-track: #25262e; --level-indent: 16px; --child-height: 0; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Segoe UI', system-ui, sans-serif; background-color: var(--bg-dark); color: var(--text-primary); height: 100vh; overflow: hidden; } .app-container { display: flex; height: 100vh; } /* 侧边栏样式 */ .sidebar { width: 280px; background-color: var(--sidebar-bg); border-right: 1px solid var(--border-color); display: flex; flex-direction: column; height: 100%; overflow: hidden; } .sidebar-header { padding: 20px 16px; background-color: var(--sidebar-header); border-bottom: 1px solid var(--border-color); display: flex; align-items: center; gap: 12px; } .logo { background-color: var(--accent-color); color: white; width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 16px; } .title { font-size: 18px; font-weight: 600; } /* 右侧内容区样式 */ .content { flex: 1; background-color: var(--bg-dark); position: relative; overflow: hidden; } .demo-frame { width: 100%; height: 100%; border: none; display: block; background: white; } `; document.head.appendChild(style); } addAppStyle(); //#endregion //#region src/template/main.tsx mountWith(() => /* @__PURE__ */ jsx(App, {}), document.getElementById("root")); //#endregion //# sourceMappingURL=main.js.map