Skip to content

Commit

Permalink
feat(element): flex布局,节点显示隐藏
Browse files Browse the repository at this point in the history
  • Loading branch information
StreakingMan committed Apr 22, 2022
1 parent 0a9f567 commit c2e1b8f
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 10 deletions.
55 changes: 54 additions & 1 deletion components/Element/index.module.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
.element {
//
display: flex;
align-items: center;
gap: var(--row-gap);

.title {
position: relative;
.toggle {
cursor: pointer;
position: absolute;

top: 0;
height: 100%;
opacity: 0;
box-sizing: border-box;
transition: 0.3s;
display: flex;
align-items: center;
justify-content: center;
}
&:hover,
&.closed {
.toggle {
opacity: 1;
}
}
}
.list {
display: flex;
flex-direction: column;
gap: var(--col-gap);
}

&.left {
flex-direction: row-reverse;
.title {
.toggle {
right: 100%;
}
}
.list {
align-items: flex-end;
}
}
&.right {
flex-direction: row;
.title {
.toggle {
left: 100%;
}
}
.list {
align-items: flex-start;
}
}
}
49 changes: 46 additions & 3 deletions components/Element/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,53 @@
import React, { FC } from 'react';
import React, { FC, useState } from 'react';
import classnames from 'classnames';
import style from './index.module.scss';
import { ElementProps } from './interface';
import { LinkIcon, ToggleIcon } from '../icons';

const Element: FC<ElementProps> = () => {
return <div className={classnames(style.element)}>element</div>;
const Element: FC<ElementProps> = ({ data, position, className }) => {
const [closed, setClosed] = useState<boolean>(false);
return (
<div className={classnames(style.element, style[position])}>
<div
className={classnames(
style.title,
className,
closed && style.closed
)}
style={{
cursor: data.url ? 'pointer' : 'default',
}}
onClick={() => data.url && window.open(data.url, '_blank')}
>
{data.url && position === 'left' && LinkIcon}
{data.title}
{!!data.children?.length && (
<div
className={style.toggle}
onClick={(e) => {
e.stopPropagation();
setClosed(!closed);
}}
>
{ToggleIcon(closed)}
</div>
)}
{data.url && position === 'right' && LinkIcon}
</div>
{!!data.children?.length && !closed && (
<div className={classnames(style.list)}>
{data.children.map((ele, idx) => (
<Element
key={idx}
data={ele}
className={className}
position={position}
/>
))}
</div>
)}
</div>
);
};

export default Element;
5 changes: 3 additions & 2 deletions components/Element/interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { SimpleMindProps } from '../SimpleMind/interface';

export interface ElementProps {
className?: string;
children?: React.ReactNode;
data: SimpleMindProps['data'];
position: 'left' | 'right';
}
2 changes: 1 addition & 1 deletion components/Line/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import style from './index.module.scss';
import { LineProps } from './interface';

const Line: FC<LineProps> = () => {
return <div className={classnames(style.line)}>line</div>;
return <div className={classnames(style.line)}></div>;
};

export default Line;
25 changes: 25 additions & 0 deletions components/SimpleMind/index.module.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
.simpleMind {
position: relative;
}

.elementLayer {
display: flex;
align-items: center;
justify-content: center;
gap: var(--row-gap);
.listContainer {
display: flex;
flex-direction: column;
gap: var(--col-gap);
&.left {
align-items: flex-end;
}
&.right {
align-items: flex-start;
}
}
}

.lineLayer {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
87 changes: 85 additions & 2 deletions components/SimpleMind/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,92 @@ import React, { FC } from 'react';
import classnames from 'classnames';
import style from './index.module.scss';
import { SimpleMindProps } from './interface';
import Element from '../Element';
import Line from '../Line';

const SimpleMind: FC<SimpleMindProps> = ({ data }) => {
return <div className={classnames(style.simpleMind)}>{data.title}</div>;
const SimpleMind: FC<SimpleMindProps> = ({
data,
layout = 'doubleSide',
elementClassName,
className,
gap,
}) => {
const allElements = data.children || [];
const leftElements: SimpleMindProps['data'][] = [];
const rightElements: SimpleMindProps['data'][] = [];

if (layout === 'singleSide') {
// 单边布局
rightElements.push(...allElements);
} else {
// 双边布局
if (allElements.length > 3) {
leftElements.push(...allElements.slice(0, allElements.length / 2));
rightElements.push(...allElements.slice(allElements.length / 2));
} else {
rightElements.push(...allElements);
}
}

let rowGap = 24,
colGap = 24;
if (Array.isArray(gap)) {
rowGap = gap[0] ?? 24;
colGap = gap[1] ?? 24;
} else if (gap) {
rowGap = gap;
colGap = gap;
}

return (
<div
style={{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
'--row-gap': `${rowGap}px`,
'--col-gap': `${colGap}px`,
}}
className={classnames(style.simpleMind, className)}
>
<div className={style.lineLayer}>
<Line />
</div>

<div className={style.elementLayer}>
{!!leftElements.length && (
<div
className={classnames(style.listContainer, style.left)}
>
{leftElements.map((ele, idx) => (
<Element
key={`left${idx}`}
position={'left'}
data={ele}
className={elementClassName}
/>
))}
</div>
)}

<div className={classnames(elementClassName)}>{data.title}</div>

{!!rightElements.length && (
<div
className={classnames(style.listContainer, style.right)}
>
{rightElements.map((ele, idx) => (
<Element
key={`right${idx}`}
position={'right'}
data={ele}
className={elementClassName}
/>
))}
</div>
)}
</div>
</div>
);
};

export default SimpleMind;
3 changes: 3 additions & 0 deletions components/SimpleMind/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ interface Data {

export interface SimpleMindProps {
data: Data;
className?: string;
elementClassName?: string;
layout?: 'singleSide' | 'doubleSide';
gap?: number | number[];
}
32 changes: 32 additions & 0 deletions components/icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';

export const LinkIcon = (
<svg
style={{ verticalAlign: 'bottom' }}
viewBox="0 0 1024 1024"
width="18"
height="18"
>
<path d="M314.2144 401.9712a32 32 0 0 1 0 45.2608l-22.8608 22.912a181.504 181.504 0 0 0-4.736 251.776l4.736 4.8896a181.504 181.504 0 0 0 251.776 4.736l4.8896-4.736 22.8864-22.8864a32 32 0 0 1 45.2608 45.2608l-22.8864 22.8864c-94.1056 94.1056-245.5808 95.8464-341.8112 5.2224l-5.376-5.2224c-94.1056-94.1056-95.8464-245.5808-5.2224-341.8112l5.2224-5.376 22.8608-22.8864a32 32 0 0 1 45.2608 0z m120.5504-165.76a245.504 245.504 0 0 1 347.1872 0c94.1056 94.1056 95.8464 245.5808 5.2224 341.8112l-5.2224 5.376-22.8864 22.8864a32 32 0 0 1-45.2608-45.2608l22.8864-22.8864a181.504 181.504 0 0 0 4.736-251.776l-4.736-4.8896a181.504 181.504 0 0 0-251.776-4.736l-4.8896 4.736-22.912 22.8608a32 32 0 0 1-45.2608-45.2352l22.912-22.8864z" />
<path d="M604.5696 413.5936a32 32 0 0 1 2.2016 42.8032l-2.2016 2.432-135.8592 135.8848a32 32 0 0 1-47.4368-42.8288l2.2016-2.432 135.8592-135.8592a32 32 0 0 1 45.2352 0z" />
</svg>
);

export const ToggleIcon = (closed: boolean) => {
return (
<svg width="16" height="16">
<path d="M3 8 L13 8" stroke="currentColor" strokeWidth="1" />
{closed && (
<path d="M8 3 L8 13" stroke="currentColor" strokeWidth="1" />
)}
<circle
cx="8"
cy="8"
r="7"
stroke="currentColor"
fill="transparent"
strokeWidth="1"
/>
</svg>
);
};
9 changes: 9 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.demo2 {
margin-top: 200px;
}

.element {
border: 1px solid gainsboro;
border-radius: 8px;
padding: 8px;
}
8 changes: 7 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import React from 'react';
import SimpleMind from '../components';
import { testData } from './test-data';
import './App.css';

function App() {
return (
<div>
<SimpleMind data={testData} />
<SimpleMind data={testData} gap={[64, 24]} />
<SimpleMind
className={'demo2'}
elementClassName={'element'}
data={{ ...testData, children: testData.children?.slice(0, 3) }}
/>
</div>
);
}
Expand Down
26 changes: 26 additions & 0 deletions src/test-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,30 @@ import { SimpleMindProps } from '../components/SimpleMind/interface';

export const testData: SimpleMindProps['data'] = {
title: 'root',
children: [
{
title: 'element1',
children: [
{ title: 'element1-1' },
{
title: 'element1-2',
children: [
{ title: 'element1-2-1', url: 'https://baidu.com' },
{ title: 'element1-2-2' },
],
},
{
title: 'element1-3',
children: [
{ title: 'element1-3-1', url: 'https://baidu.com' },
{ title: 'element1-3-2' },
],
},
{ title: 'element1-4' },
],
},
{ title: 'element2' },
{ title: 'element3' },
{ title: 'element4' },
],
};

0 comments on commit c2e1b8f

Please sign in to comment.