Is there a way to copy the contents of a static file in Sveltekit into a string? - static

I am trying to build a web app with Sveltekit.
Currently I am working on a simple navigation which should include a text and an icon for each path. I want to generate this dynamically so I am doing this:
<script lang="ts">
export let currentRoute: string;
type Route = { icon: string; name: string; path: string };
const routes: Route[] = [
{ icon: '', name: 'Home', path: '/' },
{ icon: 'icons/servers.svg', name: 'Servers', path: '/servers' }
];
</script>
<nav>
<ul>
{#each routes as route}
<li class:active={currentRoute === route.path}>
<a href={route.path}>
<img src={route.icon} alt="" />
<p>{route.name}</p>
</a>
</li>
{/each}
</ul>
</nav>
This all works fine, but I actually want to change the color of the text and the svg when the corresponding li tag has the class active (which means it is the current route).
As I am using img I cannot change the color directly in css so my thought was to somehow read the static svg files via code and output them to keep everything dynamic.
So the question is: Is there a way to read the contents of each static svg file and just copy them into a variable to keep everything clean?

There are several ways to go around this.
svg as a component
One is to make all svg files components on their own, just name the file Server.svelte and keep the content of the file itself the same. Then include this component and use it as normal. This will inline the svg in your html and you can style it as normal.
I always have
<svg viewBox="0 0 24 24" height="1em" width="1em" fill="currentColor">
this ensures the icon scales with the surrounding text and takes it's color.
(if you don't want to rename the file to .svelte, you can use a plugin to import .svg files as well)
Refer to an included svg
Another technique is to write ```html
This requires you have somewhere else in your html the following
```html
<svg xmlns="http://www.w3.org/2000/svg" style="display: none" viewBox="0 0 24 24">
<defs>
<g id="myicon">
<path d="..." fill="currentColor" />
</g>
</defs>
</svg>
This will do more or less the same as the previous method. How you get all of this included is p to you. One idea would be to make an icons.svelte file with all the icons in it as defenition and include this at the top level. And another component Icon.svelte that sets up the xlink:href block for you so you can simply do <Icon id="myicon" /> in places where you need an icon

Related

Can't play audio using React [duplicate]

I have some problem with my images on my react project. Indeed I always thought that relative path into src attribute was built on the files architecture
Here my files architecture:
components
file1.jsx
file2.jsx
file3.jsx
container
img
js
...
However I realized that the path is built on the url. In one of my component (for example into file1.jsx) I have this:
localhost/details/2
<img src="../img/myImage.png" /> -> works
localhost/details/2/id
<img src="../img/myImage.png" /> -> doesn't work, images are not displayed
How is it possible to solve this problem? I want that in any form of routes handled by react-router, all images can be displayed with the same path.
In create-react-app relative paths for images don't seem to work. Instead, you can import an image:
import logo from './logo.png' // relative path to image
class Nav extends Component {
render() {
return (
<img src={logo} alt={"logo"}/>
)
}
}
If you used create-react-app to create your project then your public folder is accessible. So you need to add your image folder inside the public folder.
public/images/
<img src="/images/logo.png" />
You're using a relative url, which is relative to the current url, not the file system. You could resolve this by using absolute urls
<img src ="http://localhost:3000/details/img/myImage.png" />
But that's not great for when you deploy to www.my-domain.bike, or any other site. Better would be to use a url relative to the root directory of the site
<img src="/details/img/myImage.png" />
With create-react-app there is public folder (with index.html...).
If you place your "myImage.png" there, say under img sub-folder, then you can access them through:
<img src={window.location.origin + '/img/myImage.png'} />
If the image is placed inside the 'src' folder, use the following:
<img src={require('../logo.png')} alt="logo" className="brand-logo"/>
Make an images folder inside src(/src/images) And keep your image in it. Then import this image in your component(use your relative path). Like below-
import imageSrc from './images/image-name.jpg';
And then in your component.
<img title="my-img" src={imageSrc} alt="my-img" />
Another way is to keep images in public folder and import them using relative path. For this make an image folder in public folder and keep your image in it. And then in your component use it like below.
<img title="my-img" src='/images/my-image.jpg' alt="my-img" />
Both method work but first one is recommended because its cleaner way and images are handled by webpack during build time.
Some older answers din't work, others are good but won't explain the theme, in summary:
If image is in 'public' directory
Example: \public\charts\a.png
In html:
<img id="imglogo" src="/charts/logo.svg" />
In JavaScript:
Create image to new img, dynamically:
var img1 = document.createElement("img");
img1.src = 'charts/a.png';
Set image to existing img with id as 'img1', dynamically:
document.getElementById('img1').src = 'charts/a.png';
If image is in 'src' directory:
Example: \src\logo.svg
In JavaScript:
import logo from './logo.svg';
img1.src = logo;
In jsx:
<img src={logo} />
I have used it this way and it worked perfectly
import Product from "../../images/product-icon.png";
import { Icon } from "#material-ui/core";
<Icon>
<img src={Product} style={{ width: "21px", height: "24px" }} />
</Icon>
Adding file-loader npm to webpack.config.js per its official usage instruction like so:
config.module.rules.push(
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {}
}
]
}
);
worked for me.
Import Images in your component
import RecentProjectImage_3 from '../../asset/image/recent-projects/latest_news_3.jpg'
And call the image name on image src={RecentProjectImage_3} as a object
<Img src={RecentProjectImage_3} alt="" />
A friend showed me how to do this as follows:
"./" works when the file requesting the image (e.g., "example.js") is on the same level within the folder tree structure as the folder "images".
Place the logo in your public folder under e.g. public/img/logo.png and then refer to the public folder as %PUBLIC_URL%:
<img src="%PUBLIC_URL%/img/logo.png"/>
The use of %PUBLIC_URL% in the above will be replaced with the URL of the public folder during the build. Only files inside the public folder can be referenced from the HTML.
Unlike "/img/logo.png" or "logo.png", "%PUBLIC_URL%/img/logo.png" will work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running npm run build.
If your page url contains multiple / then in src go back / count minus 1 times.
For example page url http://localhost:3000/play/game/ then src url must be ../your-image-folder/image-name. And your your-image-folder must be in public folder.
I create my app with create-react-app and I use require instruction if I want to change dynamically my image src:
export const MyComponent = () => {
const [myImg, setMyImg] = useState(require('./path/to/my/img'));
const handleClick = () => {
setMyImg(require('./path/to/other/img'));
}
return (
<div>
<img src={myImg} alt='myImg' />
<button onClick={handleClick}>Click me!<button/>
<div>
)
}

How to register custom icons by reading a SVG file from path?

I am building an application making use of #fluentui/react (version 8.34.7) and I am using create-react-app.
One of the things I would like to do is register custom icons which are in SVG format. Following the example here, I came up with the following code:
registerIcons({
icons: {
'hamburger-icon': (
<svg viewBox="0 0 16 16" role="presentation" focusable="false" aria-hidden="true">
<g>
<path d="M1 4.5v-1h14v1zm0 4v-1h14v1zm0 4v-1h14v1z"></path>
</g>
</svg>
)
}
});
and this works perfectly fine. I can reference hamburger-icon in my code and get the icon to display.
However, the issue with the code above is that I have to include svg file contents in my code. What I would like to do is read the SVG file contents and then use it in registerIcons method. I am not sure how I can do that.
If I do something like:
import Hamburger from 'assets/images/hamburger.svg';
I get a URL.
If I do something like:
import {ReactComponent as HamburgerIcon} from 'assets/images/hamburger.svg';
I get a React component whereas registerIcon method expects a React element.
Is it possible to simply specify a file path of an SVG file and register icon from that?
Well, it was ridiculously simple! All I had to do was this:
import {ReactComponent as HamburgerIcon} from 'assets/images/hamburger.svg';
registerIcons({
icons: {
'hamburger-icon': React.createElement(HamburgerIcon)
}
});
and all worked well.

Next Image not taking class properties

I am using Next.js and next/image to display images, but the CSS is not working inside it. The image is in SVG format and I have placed it in the public folder. I am using Tailwind CSS along with this.
<Image
className="mt-3"
data-testid="close-icon"
src="/CloseIcon.svg"
alt="Close Nav Bar"
height="24"
width="24"
/>
I am not sure why it is not working and it is not being reflected in the browser. Any help will be greatly appreciated!
Before Next.js 12.2
Styling the next/image component's margins this way doesn't work in older Next.js versions. See relevant GitHub discussion for more details.
Internally to the next/image component, the <img> element and the elements that wrap it have inline styles that override certain values passed through className.
As a workaround, you can add a wrapper element and apply the margin styling to it instead.
<div className="mt-3">
<Image
data-testid="close-icon"
src="/CloseIcon.svg"
alt="Close Nav Bar"
height="24"
width="24"
/>
</div>
From Next.js 12.2
You can use the next/future/image component instead. This new component renders a single <img> element without any additional wrappers by default, and is no longer constrained by the wrapper's styles override.
You can enable next/future/image in next.config.js.
module.exports = {
experimental: {
images: {
allowFutureImage: true
}
}
}
From Next.js 13
The next/future/image component has been converted to next/image. Like next/future/image, this component renders a single <img> element and can be styled directly with className/styles.
Juliomalves's answer is right, but I would prefer:
<div className="mt-3" height="24" width="24">
<Image
data-testid="close-icon"
src="/CloseIcon.svg"
alt="Close Nav Bar"
layout="fill"
/>
</div>
You also can use a little cheat:
import {motion} from "framer-motion";
<motion.img
className="mt-3"
data-testid="close-icon"
src="/CloseIcon.svg"
alt="Close Nav Bar"
height="24"
width="24">
Try this:
<div style='width:104px;height:104px;position:relative;'>
<Image
alt='Mountains'
src='/mountains.jpg'
layout='fill'
objectFit='contain'
/>
</div>
More on objectFit Here
Wrapping the image component in a div helps solve the issue for me. I can apply the class name to the div and everything works as it should.

React JSX - Filling an svg with an image

I am trying fill a simple circle SVG with an image in React. The sources I found have not been helpful, for they don't seem to apply to JSX.
Here is the code I currently have:
import React, {Component} from 'react';
import Anime from '../images/Anime.jpeg';
class Circle extends Component {
render() {
return (
<div>
<svg height="300" width="300">
<circle cx="150" cy="150" r="100" stroke="black" stroke-width="3" fill={Anime} />
</svg>
</div>
);
}
}
export default Circle;
My first instinct was to tweak the fill attribute by either replacing the original color ="red" with ={Anime} or =url("../images/Anime.jpeg"), but neither worked.
(I know the path to the Anime image is correct, after testing it by rendering <img src={Anime} alt={Anime}></img> inside the div.
I see people using <defs> and <pattern> tags, as such (referring to this post):
<svg id="graph" width="100%" height="400px">
<!-- pattern -->
<defs>
<pattern id="image" x="0%" y="0%" height="100%" width="100%"
viewBox="0 0 512 512">
<image x="0%" y="0%" width="512" height="512" xlink:href="https://cdn3.iconfinder.com/data/icons/people-professions/512/Baby-512.png"></image>
</pattern>
</defs>
<circle id="sd" class="medium" cx="5%" cy="40%" r="5%" fill="url(#image)" stroke="lightblue" stroke-width="0.5%" />
</svg>
But these tags don't seem to work in React / JSX; I keep getting this error when I try to use them: Namespace tags are not supported by default. React's JSX doesn't support namespace tags. You can turn on the 'throwIfNamespace' flag to bypass this warning.
Is there a way around this? I want to be able to fit the following picture into a simple circle svg with a thick black border around it.
I figured out a way and wanted to post / share it, in case anyone is here, looking at this post and wondering what the (one of the) solution(s) is!
Here is my final code:
import React, {Component} from 'react';
import AnimeCropped from '../images/AnimeCropped.png';
class Circle extends Component {
render() {
return (
<div>
<svg height="670" width="670">
<circle cx="350" cy="350" r="300" stroke="black" stroke-width="5" fill="none" />
<image className='img-circle' xlinkHref={AnimeCropped} x='75.5' y="15" height="670" width="670"/>
</svg>
</div>
);
}
}
export default Circle;
Essentially what I did was:
Crop the photo to be a circle. I could have followed cubrr's recommendation above and clip-path'ed it (which is a great idea!), but instead - I just found and used a photo cropping tool online. I then named the newly-cropped picture "AnimeCropped" and saved it in the same directory.
Imported the picture into the component file and rendered via a <image> tag after the <circle> tag. It wasn't mentioned in my original question above, but I also hypothesized that inserting the image inside the circle might solve the problem. As such:
<svg height="670" width="670">
<circle cx="350" cy="350" r="300" stroke="black" stroke-width="5" fill="none">
<img src={AnimeCropped} alt={AnimeCropped}/>
</circle>
</svg>
But this didn't work.
In my final code, you'll see that the <image> tag comes after the <circle> tag, so technically, it's not really inside the circle, but just overlaying on top of it. Also note that I had to use <image>, not <img>. I'll have to do a bit of research on this, but <img> was giving me an error message.
Also important: In React, xlink:href won't work, but xlinkHref will! This really tripped me off, but I found this helpful post.
Using classNames, I edited the size of the cropped picture in my App.css file.
Configured the x- and y- coordinates so that it somewhat fits perfectly in the middle of the circle.
That's it! Just wanted to post this for closure / help me review what I learned.

Svg clipPath in AngularJS app with HTML Base Element

I use svg clipPath in my AngularJS project. I have a problem with specifying a relative url to the clipPath because i need to use the <base> element in my project.
For example this code works in a project without base, but not in a project with <base href="/">
<svg width="120" height="120"
viewPort="0 0 120 120" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="myClip">
<rect x="10" y="10" width="60" height="60"></rect>
</clipPath>
</defs>
<g clip-path="url(#myClip)">
<circle cx="30" cy="30" r="20"/>
<circle cx="70" cy="70" r="20"/>
</g>
</svg>
How can this be solved? I use ui-router, if that is relevant to the question...
This question is about the same, but the "solution" the OP found was to remove the base which isn't a solution in my case.
Change
<g clip-path="url(#myClip)">
So that you're using an absolute URL + a fragment identifier rather than just a fragment identifier on its own. E.g. url(http://mydomain.com/mypage#myClip)
You may be able to remove some parts e.g. the http and the domain if the base tag matches the absolute URL so /mypage#myClip might work.
In case anyone needs a function to do it here's one in pure JS:
It stores the original clipPath in the element data attribute and each time called, uses document.location.href to use absolute paths for url()
function fixClipPaths(svg, restore) {
Array.prototype.forEach.call(svg.querySelectorAll('*[clip-path]'), function (el) {
var clipUrl = el.getAttribute('clip-path');
if(!el.getAttribute('data-original-clip-path')) {
el.setAttribute('data-original-clip-path', clipUrl);
}
el.setAttribute('clip-path', 'url('+ (!restore ? document.location.href : '') + el.getAttribute('data-original-clip-path').substr(4));
});
}
// usage example
fixClipPaths(document.getElementsByTagName('svg')[0]);

Resources