@msom/xbuild
Version:
@msom/xbuild
488 lines (445 loc) • 12.3 kB
JavaScript
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 = 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 = 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