Webpack image loader with dynamic inline background images via CSS? - node.js

I'm using webpack with react, and I usually import and use my images as such:
import CalNotices from 'assets/img/calendar-notices.png';
<img className='img-center' role='presentation' src={CalNotices}/>
that works fine, and when I do production build, those images area neatly put into appropriate folders.
However, at times, I want to use images for backgrounds, and use it as such:
const divStyle = {
backgroundImage: `url(${item.logo})`,
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center'
}
<div className='image-holder' style={divStyle}></div>
with item.logo being an absolute path to the image.
This works fine in dev, but when I do a prod build, those images are obviously missing, since they didn't get picked up by the webpack image loader. How should I approach this?
my image loader config is
{
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)(\?.*)?$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'file',
query: {
name: 'static/media/[name].[ext]'
}
},

Based on your comments (all of the images are in /src/assets/img/logos), you can leverage webpack's require.context functionality to bundle the entire logo folder at build time:
// causes webpack to find and bundle *all* files in logos or one of its subfolders.
const logoContext = require.context("assets/img/logos", true, /\.(png|jpg)$/);
// later...in your render() method
// assumes item.logo is a path relative to the logos folder
// example: "credit-card-xxx.jpg" or "./credit-card-xxx.jpg"
const logoUrl = logoContext.resolve(item.logo);

Related

How to copy files from node_modules with Vite / SvelteKit?

I have a node module dependency for theming content at node_modules/foo-styles.
For most css that I want to pull in, I can simply use an import 'foo-styles/dist/controls/button.css' in the page/component and vite does its magic, shoving the css where it fits best.
For theming however, I need to switch the included css variables file based on active theme in my <svelte:head> , e.g. reference foo-styles/dist/themes/{$theme}/color-variables.css in the markup.
To do so, I assume that vite must copy those files from node_modules/foo-styles to my apps asset directory without import magic, renaming of files etc.
I can only find examples for doing this with webpack or rollup using copy plugins ... is this something that vite can do out of the box? Is there a plugin available? How do you copy assets from node_module dependencies to the bundle?
You can make Vite copy assets and return a URL by adding ?url to the import. With this you then can just dynamically add a link to the <head> with said URL.
E.g.
import themeUrl from 'some-module/css/theme.css?url'
onMount(() => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = themeUrl;
document.head.appendChild(link);
return () => link.remove();
});
If you do not want to manually list all theme imports, you can use Vite's glob import feature. Here the URL has to be written differently as it must start with / or ./:
const styles = import.meta.glob(
'/node_modules/some-module/css/*.css',
{ query: 'url', eager: true },
);
let selectValue = 'some-theme';
$: selectedStyle = `/node_modules/some-module/css/${selectedValue}.css`;
let link = null;
onMount(() => {
link = document.createElement('link');
link.rel = 'stylesheet';
document.head.appendChild(link);
return () => link?.remove();
});
$: if (link) {
const { default: href } = styles[selectedStyle];
link.href = href;
}
You can also use svelte:head, which can be simpler and will add the style reference during SSR, however the order of the stylesheets can be inconsistent as the style may be added before other styles on first load but added after these styles on navigation, so rules may be accidentally overridden.
<script>
const styles = import.meta.glob(
'/node_modules/some-module/css/*.css',
{ query: 'url', eager: true },
);
let selectValue = 'some-theme';
$: href = styles[`/node_modules/some-module/css/${selectedValue}.css`].default;
</script>
<svelte:head>
<link rel="stylesheet" {href} />
</svelte:head>

Cannot use png images from VUE public folder

I have a static asset - png image. I was able to use it like:
<img class="navbar-brand" id="navbar-logo" src="#/assets/logo.png">
But I need it to be possible to replace after deployment. When I put image in public folder and use an absolute path vue application not shows image.
<img class="navbar-brand" id="navbar-logo" src="/logo.png">
The img tag presents on page and browser executes http call for image. The response code is 200 but response body is empty. The image file stored in public folder, which is in root folder of vue application. Actually even if I put in src attribute the name of image which not exist I get a 200 response code, which is strange.
I also have a router in my application and it seems to be working fine, but I not sure can it cause the problem or not:
export default new Router({
mode: 'history',
routes: [
{
path: '/:dataset([^/0-9]+?)',
name: 'introduction',
component: Introduction
},
{
path: '/:dataset([^/0-9]+?)/introduction',
name: 'introduction',
component: Introduction
},
{
path: '/:dataset([^/0-9]+?)/sample-data',
name: 'sample-data',
component: SampleData
},
{
path: '/404',
component: NotFound
},
{
path: '*',
component: NotFound
}
]
})
I run application on my workstation with
npm run dev
The vue version is:
front#1.0.0 /home/pm/git/showroom/front
└── vue#2.6.11
Please help to show image from public folder.

Bind dynamic Vue image src from node_modules

I' am creating a Vue component that shows an SVG image from my node modules based on a given image name or key (given by an API).
If I put the source image directly like ~cryptocurrency-icons/svg/color/eur.svg, the resource loads.
But if I try to compute it with the props or by the mounted method asigning it using :src="imageSource" it does not load.
I'm new to Vue so I don't know why is this happening? Should I use images downloaded in a public directory instead of the NPM package?
<template lang="html">
<img src="~cryptocurrency-icons/svg/color/eur.svg" alt="icon" />
</template>
<script lang="js">
export default {
name: 'crypto-icon',
props: ['currency'],
mounted() {
this.imageSource = `~cryptocurrency-icons/svg/color/${this.currency}.svg`
},
data() {
return {
imageSource: ""
}
},
methods: {
getImageResource(){
return `~cryptocurrency-icons/svg/color/${this.currency}.svg`
}
},
computed: {
}
}
</script>
<style lang="scss" scoped>
.crypto-icon {}
</style>
You are missing the require attribute. Vue Loader, does this automatically when it compiles the <template> blocks in single file components. Try with this:
require(`~cryptocurrency-icons/svg/color/${this.currency}.svg`)
You can read more here.
I've solved it by using a Vue plugin (see vue-cryptoicons), although hatef is right about using require for image resources, using it in directories under node_modules tries to find the full path
~cryptocurrency-icons/svg/color in ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"a7e19d86-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Wallet.vue?vue&type=template&id=7f1455a9&scoped=true&lang=html&
To install it, you can run: npm install --save ~cryptocurrency-icons/svg/color

How can I use a component like react-image-editor for cropping an image in react-admin

I am using React-Admin and I need to use/create a component for cropping an image (profile image) and storing it as a base64-image.
I found #toast-ui/react-image-editor better than other libraries but I have a problem with that. In React-Admin when we use ImageInput like this:
<ImageInput multiple label="Gallery" source="gallery" accept="image/*">
<ImageField source="src" title="title" />
</ImageInput>
the data can be loaded from the source (in Edit mode) and it will be stored there, but when we use other components like what I have mentioned, I need to know how can I pass the data as a source to that. It doesn't matter for me to use this library or any others... but I need one with simple usage.
Actually, my issue is with connecting the new component to the model that react-admin use.
I have recently written such a component, you can find it under the following link:
EditableImageComponent.
I don't use #toast-ui/react-image-editor for this, like you do, but ReactImageCrop, maybe the component will help you anyway. At the moment I have some bugs in the code (after the image is loaded, the crop has to be changed once before it is applied), but otherwise it works quite well so far.
Edit: In order to use this component in your EditView, simply call it like every other Input Component (it is assumed, that your target is called "imagename")
<EditableImage source="imagename" {...props} label="User image (Use the upload button to edit)"/>
I have use #toast-ui/react-image-editor as my image editor. i use modal to edit images in gallery. My simple code using react and bootstrap.
first import react image editor.
import 'tui-image-editor/dist/tui-image-editor.css';
import ImageEditor from '#toast-ui/react-image-editor';
if you use class component add this line.
export default class Example extends Component {
editorRef = React.createRef();
}
or if you use funtional component add this line.
function Example() {
const editorRef = React.createRef();
}
then call react image editor in the component.
<ImageEditor
ref={this.editorRef}
includeUI={{
loadImage: {
path: imagePath,
name: imageName,
},
theme: myTheme,
menu: [
"filter",
"crop",
"flip",
"resize",
"rotate",
"text",
],
initMenu: "filter",
uiSize: {
width: "100%",
height: "600px",
},
menuBarPosition: "left",
}}
cssMaxHeight={500}
cssMaxWidth={700}
selectionStyle={{
cornerSize: 20,
rotatingPointOffset: 70,
}}
usageStatistics={false}
/>

Use different loader in Webpack depending on from where I'm importing

I'm using the svg loader to import SVG icons in my React file.
webpack.config.js:
{ test: /\.svg(\?.*)?$/, loader: 'svg' }
component.js:
import Icon from './icon.svg'
render () {
return <svg {...Icon.attributes} dangerouslySetInnerHTML={{ __html: Icon.content }} />
}
So far I have no issues. I also want to use SVG images in my CSS. If I do this:
.class {
background-image: url('./icon.svg');
}
My final result looks like this:
I would like to use the url loader for my CSS file. I tried this:
.class {
url('url!./icon.svg?name=[path][name].[ext]&limit=1&mimetype=image/svg+xml');
}
In my CSS I get:
This looks like what I want but the image is not displayed and if I open the url in a separate tab I get this:
Which leads me to think that the svg loader is still running.
Is there a way to specify a different loader based on from which file I'm importing?
Add issuer see: webpack.js.org/configuration/module/#rule-issuer
An example:
{
test: /\.svg(\?.*)?$/,
issuer: /\.js$/,
loader: 'svg-inline-loader',
},
{
test: /\.svg/,
issuer: /\.scss$/,
use: {
loader: 'svg-url-loader',
options: {},
},
},
I'm not sure that's exactly what you need; it sounds like you need to trigger webpack's module resolution. By default, the CSS loader treats image URL references as relative (like they would normally be w/CSS). You need to prefix the URL with a '~' to tell it to use webpack's require resolution to find the module.

Resources