Search code examples
cssangularsassflexboxresponsive-design

Extra space is getting added before text - Angular tree view CSS


I am creating this nested tree view component in Angular. All the stylings are working fine but for the nested tree first element extra space is getting added before the text which i am not able to figure out. Here is the stackblitz for the project that i am working on Stackblitz

enter image description here

tree.component.css

.open-btn {
  float: left;
  transform: rotate(90deg);
  position: relative;
  left: -30px;
  z-index: 10;
}

.close-btn {
  float: left;
  left: -30px;
  position: relative;
  z-index: 10;
}

.tree li {
  list-style-type: none;
  margin: 10px;
  position: relative;
}

.arrow-btn {
  width: 18px;
  height: 18px;
}

.tree li::before {
  content: '';
  position: absolute;
  top: -7px;
  left: -20px;
  border-left: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  border-radius: 0 0 0 0px;
  width: 20px;
  height: 15px;
}

.tree li::after {
  position: absolute;
  content: '';
  top: 8px;
  left: -20px;
  border-left: 1px solid #ccc;
  border-top: 1px solid #ccc;
  border-radius: 0px 0 0 0;
  width: 20px;
  height: 100%;
}

.tree li:last-child::after {
  display: none;
}

.tree li:last-child:before {
  border-radius: 0 0 0 5px;
}

ul.tree > li:first-child::before {
  border-left: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
}

.label-container {
  display: inline-block;
  /* background-color: rgb(123, 212, 239); */
}

tree.component.ts

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { TreeNode } from './tree-mock';
@Component({
  selector: 'app-tree',
  templateUrl: './tree.component.html',
  styleUrls: ['./tree.component.css'],
})
export class TreeComponent {
  @Input() tree: any;
  @Output() selectedValue = new EventEmitter<any>();

  toggleChild(node) {
    this.selectedValue.emit(node);
    node.showChildren = !node.showChildren;
    node.isOpen = !node.isOpen;
  }

  /* Events are not bubbled up so emitting the parent event on <app-tree>
   * when one of the child emits an event - this will create a new EventEmitter per child.
   */
  emitOnChildClicked(node) {
    this.selectedValue.emit(node);
  }
}

tree.component.html

<ul *ngIf="tree" class="tree">
  <li *ngFor="let node of tree">
    <div class="label-container" (click)="toggleChild(node)">
      <span *ngIf="node.children != 0">
        <img
          src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Icons8_flat_document.svg/512px-Icons8_flat_document.svg.png"
          class="arrow-btn"
          [ngClass]="
            !node.children ? 'doc' : node.isOpen ? 'open-btn' : 'close-btn'
          "
        />
      </span>
      <span> {{ node.name }} </span>
    </div>
    <app-tree
      *ngIf="node.showChildren"
      (selectedValue)="emitOnChildClicked($event)"
      [tree]="node.children"
    ></app-tree>
  </li>
</ul>

tree-mock.ts

export interface TreeNode {
  name: string;
  showChildren: boolean;
  children: any[];
}

export const NODES: TreeNode[] = [
    {
        name: 'Africa',
        showChildren: false,
        children:[
            {
                name : 'Algeria',
                showChildren: false,
                children:[
                    {
                        name : 'Algeris',
                        showChildren: false,
                        children:[]
                    },
                    {
                        name : 'Algeria child 2',
                        showChildren: false,
                        children:[
                            
                        ]
                    },
                ]
            },
            {
                name : 'Angola',
                showChildren: false,
                children:[]
            },
            {
                name : 'Benin',
                showChildren: false,
                children:[]
            },

        ]
     },
     {
        name: 'Asia',
        showChildren: false,
        children:[
            {
                name : 'Afghanistan',
                showChildren: false,
                children:[
                    {
                        name : 'Kabul',
                        showChildren: false,
                        children:[]
                    }
                ]
            },
            {
                name : 'Armenia',
                showChildren: false,
                children:[]
            },
            {
                name : 'Azerbaijan',
                showChildren: false,
                children:[]
            },

        ]
     },
     {
        name: 'Europe',
        showChildren: false,
        children:[
            {
                name : 'Romania',
                showChildren: false,
                children:[
                    {
                        name : 'Bucuresti',
                        showChildren: false,
                        children:[]
                    }
                ]
            },
            {
                name : 'Hungary',
                showChildren: false,
                children:[]
            },
            {
                name : 'Benin',
                showChildren: false,
                children:[]
            },
        ]
     },
     {
        name: 'North America',
        showChildren: false,
        children: []
     }


]

Solution

  • The space is from the arrow/close button. Change its position from relative to absolute.

    .close-btn {
      float: left;
      left: -30px;
      /* position: relative; */
      position: absolute;
      z-index: 10;
    }
    

    Additionally, the float: left declaration is unneeded with position: absolute.