I have buttons that are displayed on a css grid. I would like to put arrows in the gaps that will be like a flow chart. If not possible using css grid can think of something that can implement using another technique. This is stackblitz which will show my implementation without the arrows in between.
CodePudding user response:
I would create classes that can be applied to each "step" depending on which way the arrow needs to point (up, down, left, right) and then use an ::after
pseudo selector to create the arrow element on each class, styling as required.
See below.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
grid-gap: 20px;
}
.step {
background-color: blue;
color: white;
padding: 10px;
position: relative;
}
.arrow-right::after {
color: black;
content: '→';
font-size: 20px;
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
}
.arrow-down::after {
color: black;
content: '↓';
font-size: 16px;
position: absolute;
left: 50%;
top: 100%;
transform: translateX(-50%);
}
.arrow-up::after {
color: black;
content: '↑';
font-size: 16px;
position: absolute;
left: 50%;
bottom: 100%;
transform: translateX(-50%);
}
.arrow-left::after {
color: black;
content: '←';
font-size: 20px;
position: absolute;
right: 100%;
top: 50%;
transform: translateY(-50%);
}
<div >
<div >Step X</div>
<div >Step X</div>
<div >Step X</div>
<div >Step X</div>
<div >Step X</div>
<div >Step X</div>
<div >Step X</div>
<div >Step X</div>
<div >Step X</div>
</div>
CodePudding user response:
Another approach is use SVG
Update I forget add window.scrollX and window.scrollY when calculate the paths (just corrected in code and stactblitz)
The idea is put all under a wrapper div
<div #wrapper >
<svg [attr.width]="size.width" [attr.height]="size.height"
xmlns="http://www.w3.org/2000/svg">
<path *ngFor="let path of paths" [attr.d]="path" />
</svg>
<div #bt *ngFor="let item of procDesc; let i = index" >
..your buttons..
</div>
</div>
You get the elements using viewChild and ViewChildren and declare an array of "paths" and an object with the size of the wrapper
@ViewChildren('bt') items:QueryList<ElementRef>
@ViewChild('wrapper') wrapper:ElementRef
paths:string[]=[]
size={width:0,height:0}
Then, when you resize (I use fromEvent rxjs operator in the stackblitz )
ngOnInit()
{
this.subscription=fromEvent(window,'resize').pipe(
startWith(null),
debounceTime(200)
).subscribe(_=>{
setTimeout(()=>{
this.paths=this.createPath()
const rect=this.wrapper.nativeElement.getBoundingClientRect()
this.size={width:rect.width,height:rect.height}
})
})
}
ngOnDestroy(){
this.subscription.unsubscribe()
}
The function createPath is like
createPath()
{
const path:string[]=[]
const add=.5; //if stroke-width is even use add=.5 else use add=0
this.items.forEach((x,i)=>{
if (i)
{
/* replace this lines
const ini=this.items.find((_,index)=>index==i-1)
.nativeElement.getBoundingClientRect()
const fin=x.nativeElement.getBoundingClientRect()
*/
//by
const _ini=this.items.find((_,index)=>index==i-1).nativeElement.getBoundingClientRect()
const _fin=x.nativeElement.getBoundingClientRect()
const ini={width:_ini.width,height:_ini.height,left:_ini.left window.scrollX,top:_ini.top window.scrollY}
const fin={width:_fin.width,height:_fin.height,left:_fin.left window.scrollX,top:_fin.top window.scrollY}
if (ini.top==fin.top)
{
path.push(`M${ini.left ini.width add} ${ini.top ini.height/2 add}
H${fin.left add}
M${fin.left-7-add} ${fin.top fin.height/2-5-add}
L${fin.left add} ${fin.top fin.height/2 add}
M${fin.left-7-add} ${fin.top fin.height/2 5 add}
L${fin.left add} ${fin.top fin.height/2 add}`)
}
else
{
const step=(fin.top-ini.top-ini.height)/2
path.push(`M${ini.left ini.width/2 add} ${ini.top ini.height add}
V${ini.top ini.height step add}
H${fin.left fin.width/2 add}
V${fin.top add}
M${fin.left fin.width/2 5 add} ${fin.top-7 add}
L${fin.left fin.width/2 add} ${fin.top add}
M${fin.left fin.width/2-5-add} ${fin.top-7-add}
L${fin.left fin.width/2 add} ${fin.top}`
)
}
}
})
return path
}