Any ideas on how to DRY code this navbar in React? - html

i'm creating a SPA using React and noticed that in the creation of my navbar there is going to be a lot of repetition. What i want is six components like this:
function Navbar() {
return (
<nav className="navbar">
<div className="navbar-items">
<Logo />
<NavbarItem1 />
<NavbarItem2 />
<NavbarItem3 />
<NavbarItem4 />
<NavbarItem5 />
</div>
</nav>
)
}
Now for the repetition part, every component is going to be basically this:
function NavbarItem1() {
return (
<div className="navbar-item">
<button className="navbar-button">
<svg class="navbar-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 80" x="0px" y="0px">
<g>
<path
d="M52,7a5.006,5.006,0,0,0-5-5H7A5.006,5.006,0,0,0,2,7V56H4V7A3,3,0,1,1,8,9.816V7H6V62H48V11.9A5.009,5.009,0,0,0,52,7ZM8,60V12H46V60ZM47,10H10.974a4.9,4.9,0,0,0,0-6H47a3,3,0,0,1,0,6Z" />
<path
d="M57,8a5.006,5.006,0,0,0-5,5V52.236l5,10,5-10V13A5.006,5.006,0,0,0,57,8ZM56,48.586,54.845,47.43,54,48.7V22h2Zm-1.846,3.486,1-1.5L57,52.414l1.845-1.844,1,1.5L57,57.764ZM60,48.7l-.845-1.267L58,48.586V22h2ZM60,20H54V18h6Zm-6-4V13a3,3,0,0,1,6,0v3Z" />
<rect x="12" y="20" width="30" height="2" />
<rect x="12" y="28" width="30" height="2" />
<rect x="12" y="35" width="30" height="2" />
<rect x="12" y="43" width="30" height="2" />
<rect x="12" y="50" width="18" height="2" />
<rect x="36" y="50" width="2" height="2" />
<rect x="32" y="50" width="2" height="2" />
<rect x="40" y="50" width="2" height="2" />
</g>
</svg>
<span class="navbar-text">Item 1</span>
</button>
</div>
)
}
So instead of creating six components who are basically the same, is there a less repetitive way of doing this or is this the normal way people create navbars using React?? Thanks in advance for your answer.

Assuming that the parts that differ between each navbar item are the icon and its text content, you can extract the icons into their own components, then just pass the icon component and the text via props:
// Pass className via props so the NavbarItem can set it
function NoteIcon({ className }) {
return (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 64 80"
x="0px"
y="0px"
>
<g>
<path d="M52,7a5.006,5.006,0,0,0-5-5H7A5.006,5.006,0,0,0,2,7V56H4V7A3,3,0,1,1,8,9.816V7H6V62H48V11.9A5.009,5.009,0,0,0,52,7ZM8,60V12H46V60ZM47,10H10.974a4.9,4.9,0,0,0,0-6H47a3,3,0,0,1,0,6Z" />
<path d="M57,8a5.006,5.006,0,0,0-5,5V52.236l5,10,5-10V13A5.006,5.006,0,0,0,57,8ZM56,48.586,54.845,47.43,54,48.7V22h2Zm-1.846,3.486,1-1.5L57,52.414l1.845-1.844,1,1.5L57,57.764ZM60,48.7l-.845-1.267L58,48.586V22h2ZM60,20H54V18h6Zm-6-4V13a3,3,0,0,1,6,0v3Z" />
<rect x="12" y="20" width="30" height="2" />
<rect x="12" y="28" width="30" height="2" />
<rect x="12" y="35" width="30" height="2" />
<rect x="12" y="43" width="30" height="2" />
<rect x="12" y="50" width="18" height="2" />
<rect x="36" y="50" width="2" height="2" />
<rect x="32" y="50" width="2" height="2" />
<rect x="40" y="50" width="2" height="2" />
</g>
</svg>
);
}
function NavbarItem({ icon: Icon, children }) {
return (
<div className="navbar-item">
<button className="navbar-button">
<Icon className="navbar-icon" />
<span className="navbar-text">{children}</span>
</button>
</div>
);
}
function Navbar() {
return (
<nav className="navbar">
<div className="navbar-items">
<Logo />
{/* Pass the correct icon component, and the text via children */}
<NavbarItem icon={NoteIcon}>Item 1</NavbarItem>
<NavbarItem icon={OtherIcon}>Item 2</NavbarItem>
<NavbarItem icon={AnotherIcon}>Item 3</NavbarItem>
</div>
</nav>
);
}

Related

Why is my SVG icon so blurry on larger screens?

I'm quite new to using SVG icons and I can't seem to figure out why my icon is blurry. Could you help? Thanks
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
<mask id="mask0_12_275" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="12" height="12">
<rect width="12" height="12" fill="url(#pattern0)"/>
</mask>
<g mask="url(#mask0_12_275)">
<rect x="-5" y="-7" width="23" height="21" fill="#C6C6C6"/>
<rect x="-5" y="-7" width="23" height="21" fill="black" fill-opacity="0.2"/>
<rect x="-5" y="-7" width="23" height="21" fill="black" fill-opacity="0.2"/>
</g>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_12_275" transform="scale(0.01)"/>
</pattern>
<image id="image0_12_275" width="100" height="100" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABmJLR0QA/wD/AP+gvaeTAAAD4ElEQVR4nO3dy2tcVRwH8M+Mj9iSNI31gfisVEVLU1QEQS2C26JiN75wIxIEBVFEFy66K4ob3bjxfxB0o7gQXPlCpZhidyrik7aKqRpbGxfniul0xrl35s7MuZPfB36bm5nMOeebOzf3d+8khBBCCCGEEEIIIYQQQgghhBBCCKGqGTyJD7CCtaihaqVYyydwboUcwKX4PINJTGt9VqxxKTMRxljqU132lHaXQJawu8v2UK8b8Vjnxm6BPDT6sYTCw50bWl0e9BtmRz+WIB3o59Zv6BbI2njGEgqnZdDtLStMUASSmQgkMxFIZiKQzEQgmYlAMnN2zd/vBJalPs0yvsO3OIY/iq+v4BR+LZ4zL/1gzOIcbMICLpMacDfgJuwsvj5N4yqlapPsFN7EAzrOOms2hwfxVvGaTR1XZ/VV5Zst445aplXNHhxq4LhGGsghbK1tKtUt4MsGjWvkgdxd4yQGda/mjGvkgWypcwYDmteccZUKZJhub7fnTkLneHMdVy/R7c1ZBJKZCCQzdZ+pT0Iux4xaxB6SmQgkMxFIZqoeQ/7GO3h9yNedwRVS1/SCdbWtqFmpabe5eOxW6S6/ztuT5pw5hzX80rHtd6wW21dxXOruruBIR/2M7/GV1Ake1F14FPfhvLJPKntieByv4RWpbV3GWbgai9iF63BlUZf0eO3c/ISv8Q0O46DUuDwstezLOF+6wfopqc/Vqe86rD+tX8VLuLDEC89jL17Gh1KIVVvRTalV6YbpV7Gvwvrsl/a6gXpZ7+H6Po/djufxEU7WMNGm1ilp79lfYs124N11z+3rBJ7Ve1fahMelvWDSC5FrHcTTel8Ya+EZJd/27uyxfQEv4McMJtyUOoYDuLjiWv+vFh6RDnCTnmBTawXPSb/oDOUqvJ/BhKalPsa1lRJY51b8kMEkpq2OSucoldyDPzMY/LTWX7i/bBi3iE/djiuUvnvK5eLgPc46Kp3L9fRGBoPcaPV2rzD2ZjC4jVr7ugXySQYD26j1haIz8u/1kNtwc7eUwljsxO38F8jS5MYSCkuk3aQlXZDp1W8J43EEF7WlC0gRxuRtw662dAt9yMOetiGaXaF217SlK1ghDzva+py6h7Ha3pYuvoc8bGlL9z6FPGxuSRfap+Gm62lQ9l6vEEII4QwtqR8fMhGfD8lMBJKZCCQzEUhmIpDMRCCZiUAyE4FkJgLJ0KB3252UPq61KC5ykdZgUVqTYT4AO/ATD4x8is31ogkEEv8WqbfdJhBIvE31VuVvLp5Ww3R7m/CnMSZpoHWN37IyE4FkJgLJTASSmQgkMxFIZiKQzPwDFB6i1e0VaQEAAAAASUVORK5CYII="/>
</defs>
</svg>

how do I prevent the feComposite arithmetic svgfilters changing output channel values despite having 0 in the corresponding input channel

When adding an svg box of color value #800000 to an svg box with color value #008000 I find the result is not #808000.
The result turns out to be color #898500.
<feComposite in="b1" in2="b2" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="comp" />
I'm trying to implement an svg filter that will allow me to composite an image from separate color channels. I.e. I have separate red, blue and green shapes that I wish to composite an image from. Only the red channel images should impact the red channel and so on. However, what I find is that adding any red channel color, changes all other channels.
The following svg demonstrates the addition using the svgfilter fecomposite.
<svg xmlns="http://www.w3.org/2000/svg" height="200">
<defs>
<rect id="box1" x="00" y="00" width="40" height="40" fill="#800000" opacity="1" color-rendering="optimizeQuality" shape-rendering="crispEdges"></rect>
<rect id="box2" x="20" y="20" width="40" height="40" fill="#008000" opacity="1" color-rendering="optimizeQuality" shape-rendering="crispEdges"></rect>
<rect id="box3" x="20" y="20" width="20" height="20" fill="#808000" opacity="1" color-rendering="optimizeQuality" shape-rendering="crispEdges"></rect>
<image id="png1" x="00" y="00" href="https://storage.googleapis.com/publitest/Red80.png" height="40" width="40" color-rendering="optimizeQuality" shape-rendering="crispEdges"></image>
<image id="png2" x="20" y="20" href="https://storage.googleapis.com/publitest/Green80.png" height="40" width="40" color-rendering="optimizeQuality" shape-rendering="crispEdges"></image>
</defs>
<text y="12">overlayed data</text>
<g id="twoBox" transform="translate(0,15)">
<use href="#box1" />
<use href="#box2" />
<use x="50" href="#box3" />
</g>
<text y="90">fused data</text>
<filter id="compArith" x="0" y="0" width="256" height="256" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feImage href="#box1" result="b1" />
<feImage href="#box2" result="b2" />
<feComposite in="b1" in2="b2" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="comp" />
</filter>
<g id="fusedBox" transform="translate(0,95)">
<use id="colormap" filter="url(#compArith)" />
</g>
</svg>

How to embed svg into svg?

I have to perform this task in which i have to embed one svg into another svg.
As I am new to SVG, I have done the polygon and rectangle part but while combining these two I am facing issues. I am attaching my work up to which I have completed.
Don't have a bunch of SVGs. Instead you should just put all your shapes into one big SVG.
Below is a quick example. But remember, you might find it easier to just use one of the many available vector drawing apps to create your SVG.
<!DOCTYPE html>
<html>
<body>
<svg width="550" height="400">
<g>
<rect width="300" height="30" style="fill:rgb(255,0,0);" />
<text x="10" y="20" fill="white">Fatality</text>
</g>
<g transform="translate(0,40)">
<rect width="300" height="30" style="fill:rgb(255,0,0);" />
<text x="10" y="20" fill="white">Lost Time Incidents</text>
</g>
<g transform="translate(0,80)">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="10" y="20" fill="white">Restricted Work Cases</text>
</g>
<g transform="translate(0,120)">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="10" y="20" fill="white">Medical Treatment Cases</text>
</g>
<g transform="translate(0,160)">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="10" y="20" fill="white">First Aid Cases</text>
</g>
<g transform="translate(0,200)">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="10" y="20" fill="white">RTA Incident</text>
</g>
<g transform="translate(0,240)">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="10" y="20" fill="white">Environment Incident</text>
</g>
<g transform="translate(0,280)">
<rect width="300" height="30" style="fill:rgb(102,204,0);" />
<text x="10" y="20" fill="white">Near Miss</text>
</g>
<g transform="translate(0,320)">
<rect width="300" height="30" style="fill:rgb(102,204,0);" />
<text x="10" y="20" fill="white">Unsafe Acts & Conditions</text>
</g>
<g transform="translate(0,360)">
<rect width="300" height="30" style="fill:rgb(102,178,255);" />
<text x="10" y="20" fill="white">Man Hours</text>
</g>
<polygon points="350,0, 550,400, 150,400" style="fill:white;stroke:black;stroke-width:1" />
</svg>
In order to embed one svg into another svg you have to use it the same way you would use a <symbol> element.
const SVG_NS = 'http://www.w3.org/2000/svg';
let colors = [
"rgb(255,128,0)",
"rgb(255,128,0)",
"rgb(255,128,0)",
"rgb(255,128,0)",
"rgb(255,128,0)",
"rgb(255,128,0)",
"rgb(255,128,0)",
"rgb(102,204,0)",
"rgb(102,204,0)",
"rgb(102,178,255)"
];
let angle = Math.atan2(215,430);
let n = 0;
let offset = 10;// distance between the border of the triangle and the start of the rectangle
for(let y = 40; y < 430; y+= 40){
let halfW = Math.tan(angle)*y - offset;
let o = {
x:430/2 - halfW,
y: y,
width: 2*halfW,
height: 30,
fill:colors[n]
}
drawRect(o, polys);
n++;
}
function drawRect(o, parent) {
let rect = document.createElementNS(SVG_NS, 'rect');
for (var name in o) {
if (o.hasOwnProperty(name)) {
rect.setAttributeNS(null, name, o[name]);
}
}
parent.appendChild(rect);
return rect;
}
svg{max-width:100vh}
<svg viewBox="0 0 600 600" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="400" height="30" y="40">
<rect width="300" height="30" style="fill:rgb(255,0,0);" />
<text x="0" y="15" fill="white">Fatality</text>
</svg>
<svg width="400" height="30" y="80">
<rect width="300" height="30" style="fill:rgb(255,0,0);" />
<text x="0" y="15" fill="white">Lost Time Incidents</text>
</svg>
<svg width="400" height="30" y="120">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="0" y="15" fill="white">Restricted Work Cases</text>
</svg>
<svg width="400" height="30" y="160">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="0" y="15" fill="white">Medical Treatment Cases</text>
</svg>
<svg width="400" height="30" y="200">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="0" y="15" fill="white">First Aid Cases</text>
</svg>
<svg width="400" height="30" y="240">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="0" y="15" fill="white">RTA Incident</text>
</svg>
<svg width="400" height="30" y="280">
<rect width="300" height="30" style="fill:rgb(255,128,0);" />
<text x="0" y="15" fill="white">Environment Incident</text>
</svg>
<svg width="400" height="30" y="320">
<rect width="300" height="30" style="fill:rgb(102,204,0);" />
<text x="0" y="15" fill="white">Near Miss</text>
</svg>
<svg width="400" height="30" y="360">
<rect width="300" height="30" style="fill:rgb(102,204,0);" />
<text x="0" y="15" fill="white">Unsafe Acts & Conditions</text>
</svg>
<svg width="400" height="30" y="400">
<rect width="300" height="30" style="fill:rgb(102,178,255);" />
<text x="0" y="15" fill="white">Man Hours</text>
</svg>
<svg id="polys" height="430" width="430" viewBox="0 0 430 430" x="160" >
<polygon points="215,0, 0,430 430,430 215,0" style="fill:white;stroke:red;stroke-width:1" />
</svg>
</svg>

Keep <text> element scaled inside <svg> with viewBox

I'm trying to put some text as labels inside some scaled elements, and the text is too big to fit in the container. What can I do here?
<div class="t_container">
<div class="t_x" style="position: relative;">
<svg position="absolute" viewBox="0 0 6 1" preserveAspectRatio="none">
<g>
<rect x="0" y="0" width="1" height="0.4"><title>Nov-21</title></rect>
<text x="0.5" y="0.5" fill="red">A<text>
</g>
<rect x="1" y="0" width="1" height="1"><title>Nov-22</title></rect>
<rect x="2" y="0" width="1" height="1"><title>Nov-23</title></rect>
<rect x="3" y="0" width="1" height="1"><title>Nov-24</title></rect>
<rect x="4" y="0" width="1" height="1"><title>Nov-25</title></rect>
<rect x="5" y="0" width="1" height="1"><title>Nov-26</title></rect></svg>
</div>
Here is a codepen with the result.
You have a very small custom viewport="0 0 6 1" size. 6px - width, 1px - height, so the font can not be displayed with such parameters.
I increased the size of the viewBox 100 times viewBox="0 0 600 100"
Squares for clarity painted in different colors. You can change their coloring according to your choice.
The text is placed inside the squares. I hope that's exactly what you wanted when you used the command
<title> Nov-24 </ title> inside the squares.
But the command <title> in SVG is a system tooltip, the information from which appears when you hover the cursor.
The size of the tooltip and its font can not be changed, so I added in the squares more tags <text> ... </ text>, the parameters of which you can change.
<div class="t_container">
<div class="t_x" style="position: relative;">
<svg position="absolute" viewBox="0 0 600 100" >
<g>
<rect x="0" y="0" width="100" height="40"><title>Nov-21</title></rect>
<text x="35" y="75" font-size="36" fill="red">A</text>
</g>
<rect x="100" y="0" width="100" height="100" fill="orange">
<title>Nov-22</title></rect>
<text x="125" y="55" font-size="18" fill="white">Nov-22</text>
<rect x="200" y="0" width="100" height="100" fill="orangered">
<title>Nov-23</title></rect>
<text x="225" y="55" font-size="18" fill="white">Nov-23</text>
<rect x="300" y="0" width="100" height="100" fill="green">
<title>Nov-24</title></rect>
<text x="325" y="55" font-size="18" fill="white">Nov-24</text>
<rect x="400" y="0" width="100" height="100" fill="dodgerblue">
<title>Nov-25</title></rect>
<text x="425" y="55" font-size="18" fill="white">Nov-25</text>
<rect x="500" y="0" width="100" height="100" fill="yellowgreen">
<title>Nov-26</title></rect>
<text x="525" y="55" font-size="18" fill="white">Nov-26</text>
</svg>
</div>

Moving a button in the middle of an SVG?

What would be the best way to move a playbutton in the middle of an SVG?
Is there anyone who can help me do this?
All the code is provided below. It's telling me to add more details, but that's about it. I put all the code below.
https://jsfiddle.net/cy8v6657/1/
<svg width="238" height="238" viewBox="0 0 238 238">
<rect x="0" y="0" width="238" height="238" fill="blue" />
<rect x="7" y="7" width="224" height="224" fill="black" />
<rect x="14" y="14" width="210" height="210" fill="red" />
<rect x="21" y="21" width="196" height="196" fill="black" />
<rect x="28" y="28" width="182" height="182" fill="yellow" />
<rect x="35" y="35" width="168" height="168" fill="black" />
<rect x="42" y="42" width="154" height="154" fill="orange" />
<rect x="49" y="49" width="140" height="140" fill="black" />
<rect x="56" y="56" width="126" height="126" fill="lime" />
<rect x="63" y="63" width="112" height="112" fill="black" />
<rect x="70" y="70" width="98" height="98" fill="teal" />
<rect x="77" y="77" width="84" height="84" fill="black" />
<rect x="84" y="84" width="70" height="70" fill="silver" />
<rect x="91" y="91" width="56" height="56" fill="black" />
<rect x="98" y="98" width="42" height="42" fill="#1155cc" />
<rect x="105" y="105" width="28" height="28" fill="black" />
<rect x="112" y="112" width="14" height="14" fill="gold" />
</svg>
<button id="playButton2" style="width:50px; height:50px; border:none; cursor: pointer; background-color:transparent; background-image: url('https://i.imgur.com/A445IfJ.png')" onclick="
var player = document.getElementById('player');
player.volume=1.0;
var button = document.getElementById('playButton2');
if (player.paused) {
playButton2.style.backgroundImage = 'url(\'https://i.imgur.com/qg4rg7Z.png\')';
player.play();
} else {
playButton2.style.backgroundImage = 'url(\'https://i.imgur.com/A445IfJ.png\')';
player.pause();
}">
</button>
<audio id="player" preload="none" style="display:none;">
<source src='' type='audio/mpeg'/>
</audio>
Place both - your svg and your button, inside a wrapper div.
Give position relative to this wrapper div.
Center the button using absolute positioning inside the wrapper div.
#svg-wrapper{
position: relative;
width: 238px;
height: 238px;
}
#svg-wrapper #playButton2{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
<div id="svg-wrapper">
<svg width="238" height="238" viewBox="0 0 238 238">
<rect x="0" y="0" width="238" height="238" fill="blue" />
<rect x="7" y="7" width="224" height="224" fill="black" />
<rect x="14" y="14" width="210" height="210" fill="red" />
<rect x="21" y="21" width="196" height="196" fill="black" />
<rect x="28" y="28" width="182" height="182" fill="yellow" />
<rect x="35" y="35" width="168" height="168" fill="black" />
<rect x="42" y="42" width="154" height="154" fill="orange" />
<rect x="49" y="49" width="140" height="140" fill="black" />
<rect x="56" y="56" width="126" height="126" fill="lime" />
<rect x="63" y="63" width="112" height="112" fill="black" />
<rect x="70" y="70" width="98" height="98" fill="teal" />
<rect x="77" y="77" width="84" height="84" fill="black" />
<rect x="84" y="84" width="70" height="70" fill="silver" />
<rect x="91" y="91" width="56" height="56" fill="black" />
<rect x="98" y="98" width="42" height="42" fill="#1155cc" />
<rect x="105" y="105" width="28" height="28" fill="black" />
<rect x="112" y="112" width="14" height="14" fill="gold" />
</svg>
<button id="playButton2" style="width:50px; height:50px; border:none; cursor: pointer; background-color:transparent; background-image: url('https://i.imgur.com/A445IfJ.png')" onclick="
var player = document.getElementById('player');
player.volume=1.0;
var button = document.getElementById('playButton2');
if (player.paused) {
playButton2.style.backgroundImage = 'url(\'https://i.imgur.com/qg4rg7Z.png\')';
player.play();
} else {
playButton2.style.backgroundImage = 'url(\'https://i.imgur.com/A445IfJ.png\')';
player.pause();
}">
</button>
</div><!--#svg-wrapper-->
<audio id="player" preload="none" style="display:none;">
<source src='' type='audio/mpeg'/>
</audio>
Update
If you cannot use external stylesheet for some reason as you mentioned in comments, you can use the same styles inline.
Change following lines:
<div id="svg-wrapper" style="position: relative;width: 238px;height: 238px;">
<button id="playButton2" style="position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);width:50px; height:50px; border:none; cursor: pointer; background-color:transparent; background-image: url('https://i.imgur.com/A445IfJ.png')" onclick="
var player = document.getElementById('player');
player.volume=1.0;
var button = document.getElementById('playButton2');
if (player.paused) {
playButton2.style.backgroundImage = 'url(\'https://i.imgur.com/qg4rg7Z.png\')';
player.play();
} else {
playButton2.style.backgroundImage = 'url(\'https://i.imgur.com/A445IfJ.png\')';
player.pause();
}">
</button>