diff --git a/README.md b/README.md index 0e4d304..985c52f 100644 --- a/README.md +++ b/README.md @@ -81,11 +81,13 @@ Accepts the following props: - **lineStyle:** _\(default solid\)_ The line style as a [css line-style](https://developer.mozilla.org/en-US/docs/Web/CSS/border-style#values) - **lineBorderRadius:** _\(default 5px\)_ The border radius of the Path as a [css border-radius](https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius) - **nodePadding:** _\(default 5px\)_ The left and right padding of every `` as a [css length](https://developer.mozilla.org/en-US/docs/Web/CSS/length) +- **isHorizontal:** _\(default false\)_ View Mode: Vertical or Horizontal ### `TreeNode` - A node in the tree - **label:** _**\(required\)**_ Any react `Node` - **children:** _**\(required\)**_ Any number of `` +- **isHorizontal:** _\(default false\)_ View Mode: Vertical or Horizontal ## Motivation diff --git a/src/components/Tree.tsx b/src/components/Tree.tsx index 4c57c3f..251befc 100644 --- a/src/components/Tree.tsx +++ b/src/components/Tree.tsx @@ -35,6 +35,7 @@ export interface TreeProps { */ nodePadding?: string; children: TreeNodeProps['children']; + isHorizontal?: boolean; } /** @@ -49,6 +50,7 @@ function Tree({ nodePadding = '5px', lineStyle = 'solid', lineBorderRadius = '5px', + isHorizontal = false, }: TreeProps) { return ( ); } diff --git a/src/components/TreeNode.tsx b/src/components/TreeNode.tsx index a622a80..d67c9ce 100644 --- a/src/components/TreeNode.tsx +++ b/src/components/TreeNode.tsx @@ -9,6 +9,7 @@ export interface TreeNodeProps { label: React.ReactNode; className?: string; children?: ReactNode; + isHorizontal?: boolean; } const verticalLine = css` @@ -19,7 +20,7 @@ const verticalLine = css` box-sizing: border-box; `; -const childrenContainer = css` +const childrenContainerVertical = css` display: flex; padding-inline-start: 0; margin: 0; @@ -35,7 +36,7 @@ const childrenContainer = css` } `; -const node = css` +const nodeVertical = css` flex: auto; text-align: center; list-style-type: none; @@ -44,7 +45,7 @@ const node = css` var(--tree-node-padding); `; -const nodeLines = css` +const nodeLinesVertical = css` ::before, ::after { ${verticalLine}; @@ -88,12 +89,128 @@ const nodeLines = css` } `; -function TreeNode({ children, label, className }: TreeNodeProps) { +const horizontalLine = css` + content: ''; + position: absolute; + left: 0; + width: var(--tree-line-height); + box-sizing: border-box; + height: 100%; +`; + +const childrenContainerHorizontal = css` + display: flex; + padding-inline-start: 0; + margin: 0; + padding-left: var(--tree-line-height); + position: relative; + flex-direction: column; + + ::before { + ${horizontalLine}; + top: calc(50% - var(--tree-line-width) / 2); + height: 0; + border-top: var(--tree-line-width) var(--tree-node-line-style) + var(--tree-line-color); + } +`; + +const nodeHorizontal = css` + flex: auto; + text-align: center; + list-style-type: none; + display: flex; + position: relative; + padding: var(--tree-node-padding) 0 var(--tree-node-padding) + var(--tree-line-height); +`; + +const nodeLinesHorizontal = css` + ::before, + ::after { + ${horizontalLine}; + right: 50%; + border-top: 0; + top: 0; + } + ::after { + left: 0; + border-left: var(--tree-line-width) var(--tree-node-line-style) + var(--tree-line-color); + } + + :before { + height: 50%; + border-top: var(--tree-line-width) var(--tree-node-line-style); + top: 50%; + } + + :only-of-type { + padding: 0; + ::after, + :before { + display: none; + } + } + + :first-of-type { + ::before { + border: 0 none; + height: 50%; + } + ::after { + border-radius: var(--tree-line-border-radius) 0 0 0; + top: 50%; + height: 50%; + border-top: var(--tree-line-width) var(--tree-node-line-style) + var(--tree-line-color); + } + } + + :last-of-type { + ::before { + border-left: var(--tree-line-width) var(--tree-node-line-style) + var(--tree-line-color); + border-radius: 0 0 0 var(--tree-line-border-radius); + border-bottom: var(--tree-line-width) var(--tree-node-line-style) + var(--tree-line-color); + border-top: 0; + bottom: 50%; + height: 50%; + top: 0; + } + ::after { + border: 0 none; + height: 50%; + } + } +`; + +function TreeNode({ + children, + label, + className, + isHorizontal = false, +}: TreeNodeProps) { return ( -
  • - {label} +
  • + {label} {React.Children.count(children) > 0 && ( -
      {children}
    +
      + {children} +
    )}
  • ); diff --git a/src/stories/Tree.stories.tsx b/src/stories/Tree.stories.tsx index d19add2..730cbc0 100644 --- a/src/stories/Tree.stories.tsx +++ b/src/stories/Tree.stories.tsx @@ -14,14 +14,14 @@ export default { } as Meta; export const Basic: Story = (args) => ( - + ); Basic.args = { label: 'Root', }; export const Styled: Story = (args) => ( - + ); Styled.args = { @@ -38,6 +38,7 @@ export const StyledNodes: Story = ({ label, ...args }) => ( label={{label}} {...args} children={getNodes(StyledNode)} + isHorizontal={true} /> ); @@ -62,18 +63,24 @@ function StyledNode({ children }: React.PropsWithChildren<{}>) { function getNodes(Label: React.ElementType = 'div') { return [ - Child 1}> - Grand Child} /> + Child 1} isHorizontal={true}> + Grand Child} isHorizontal={true} /> , - Child 2}> - Grand Child}> - Great Grand Child 1} /> - Great Grand Child 2} /> + Child 2} isHorizontal={true}> + Grand Child} isHorizontal={true}> + Great Grand Child 1} + isHorizontal={true} + /> + Great Grand Child 2} + isHorizontal={true} + /> , - Child 3}> - Grand Child 1} /> - Grand Child 2} /> + Child 3} isHorizontal={true}> + Grand Child 1} isHorizontal={true} /> + Grand Child 2} isHorizontal={true} /> , ]; }