Import Multiple images and export array - javascript

Sorry if it seems a bit confusing, I'll try to be as simple and put as much demonstration as possible.
The problem
I have a file that needs to import a bunch of images (for now 65 images). In order not to be a manual process, even when adding a new image in the folder and already being 'automatically detected' I created a function to import everything. However, I need to 'export' this function into an object like this:
const brandsAdds = [
{
label: "brand1",
value: "brand1",
logo: Object,
},
{
label: "brand2",
value: "brand2",
logo: Object,
},
...
];
How am I importing all the images:
// Import all images in folder
function importAll(r) {
let images = {};
r.keys().map((item, index) => {
images[item.replace("./", "").slice(0, -4)] = r(item).default;
});
return images;
}
// Variable for easy access to data
const image = importAll(
require.context("../", false, /\.(png|jpe?g|svg|gif)$/)
);
Demo
Automated process:
https://codesandbox.io/s/frosty-sun-n75j5?file=/pages/index.js
Result:
Print result: what must be converted and how
Manual process:
https://codesandbox.io/s/frosty-sun-n75j5?file=/pages/index_manual.js
Result:
Print result manual process
I'm using next.js and this file that will export an array object will be used in another component. The file is currently over 300 lines long as I'm calling it image by image.
Would there be any way to import all the images by adding them to the array with "label:" and "value:" with the filename and "logo:" with the image path (next.js)?

Related

iterating over data including image in Gatsby

In Gatsby I would like to iterate over an array, which contains objects. One of the properties of each object would be an image. I would like to be able to use Gatsby Image.
Here is one example of when I'd like to do so: a page on a website with a gallery of images, each image opens a particular associated video when clicked. Perhaps I'd like 20, 50, or even 100+ objects in the array:
const videos = [
{
id: 1,
name: 'Festival 2018',
url: 'https://www.youtube.com',
img: // HOW TO ACHIEVE?
},
// Many more objects
]
videos.map((item) => {
return (
<Img
key={item.id}
fluid= // HOW TO ACHIEVE?
alt={item.name}
onClick={() => openPlayer(item.url)}
/>
)
})
I understand how to query for single images with GraphQL; or how to query multiple images and use aliases; or how to query all images from a folder. But I have't worked out how to achieve my goal. There's probably a better way. Thanks in advance.
To use internal images in gatsby-image you need to allow Gatsby and their transformers and sharps to know where the images are located using the gatsby-source-filesystem. This will create queryable nodes from your images and will allow you to use gatsby-image with them.
Applied to your case, you need to put all images in the same folder, let's say /src/images and:
const path = require(`path`)
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: path.join(__dirname, `src`, `images`),
},
},
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
],
}
There, your JSON object will look like:
const videos = [
{
id: 1,
name: 'Festival 2018',
url: 'https://www.youtube.com',
img: '/relative/path/to/your/image.extension',
extension: 'png'
},
// Many more objects
]
Note: thanks John for the clarification about the extension field. Source: https://stackoverflow.com/a/56012718/13714522
In your case, since you are using a JSON-based source, you will need to add the gatsby-transformer-plugin. The configuration will look like:
module.exports = {
plugins: [
`gatsby-transformer-json`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./src/data/`,
},
},
],
}
Note: assuming that the JSON is placed in /src/data
Also assuming that your JSON file is named data.json, if everything is properly set, Gatsby will create a GraphQL node called allDataJson. Then you only last to create a query (page query or static query) with the following content:
{
allDataJson {
edges {
node {
name
url
id
img {
childImageSharp {
fluid(maxWidth: 1000, quality: 100) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
Note: check your exact query in the localhost:8000/___graphql playground
If your paths are correct, Gatsby will create the childImageSharp node which will allow you to use your own internal images within gatsby-image feature. Since your queried data is stored inside props.data, your final loop should look like:
props.data.allDataJson.edges.node.map((item) => {
return (
<Img
key={item.id}
fluid={item.img.childImageSharp.fluid}
alt={item.name}
onClick={() => openPlayer(item.url)}
/>
)
})

How do you create edgeless graphql element in Gatsby?

The title may be miss leading but I'm not really sure how do I ask this question correctly. Here is the problem: I'd like to query my own API(not created yet so I made placeholder data) for global settings which might change in the future and I will only need to rebuild the website instead of editing it manually, I want to create source node called CmsSettings and pass it to GraphQL (structure similar to site.siteMetadata) but I don't know how can I achieve that. What I achieved so far is to create a source node called allCmsSettings which has my data as an object in nodes array.
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const { createNode } = actions;
const myData = {
key: 123,
app_title: `The foo field of my node`,
...
}
const nodeContent = JSON.stringify(myData);
const nodeMeta = {
id: createNodeId(`my-data${ myData.key }`),
parent: null,
children: [],
internal: {
type: `CmsSettings`,
mediaType: `text/html`,
content: nodeContent,
contentDigest: createContentDigest(myData)
}
}
const node = Object.assign({}, myData, nodeMeta);
createNode(node);
}
Here is the query used to get the data of the source node
allCmsSettings {
edges {
node {
id
app_title
...
}
}
}
Creating a query results in an array of results(which I know is the result of creating source nodes) but I'd like to create that source so that I could query it like this and:
CmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
You get the point. I was browsing the gatsby node API but I can't find how to achieve this.
Thank you for your help
Nevermind, the answer is pretty simple, if you are new to gatsby just like me the sourceNodes export creates 2 graphql fields for you with all prefix and camel case source node. The thing that I wanted to make is already there and is queryable with
cmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
Notice the lowercase letter even though it was declared as CmsSettings. It seems that gatsby really does some magic under the hood.

Storybook conversion to CSF

In the process of converting React Stories to use the advised CSF
I'm wondering how can the following (stories that are generated from looping an array of objects) be converted to CSF?
ps: my real array has over 15 items. this is just an example below
const stories = storiesOf("MyStories", module)
const storyOptions = [{name: "foo", age:21}, {name: "bar", age: 22},{name: "batman", age: 23}, ] //
for (let option of storyOptions) {
stories.add(option.name, () => {
return <MyWrapperComponent option={option} />
})
}
I'm having difficulties refactoring the above to output the same multiple stories as CSF:
Only the first story returns so far:
export const myStories = () => {
for (let option of storyOptions) {
return <MyWrapperComponent option={option} />
}
}
With Storybook 5.x, the latest version available as I'm writing this, it's not possible to iterate over an array and dynamically generate stories with CSF format because the requirements to have working stories are strict:
- a required default export
- one or more named exports
Everything is fine with the default export however there is currently no way to have dynamic named exports because JS importand export have to be statically analyzable - i.e. known before executing the code. Unfortunately, you are stuck with the old syntax for now.

How do I autosave an array in a js file along with some text?

I have a .js file that contains an array that will be imported as data. For example, it will look like this:
const ideasData = [
{
content:"Content",
title: "This Title",
date: new Date()
},
{
content:"content2",
title: "second title",
date: new Date()
}
]
export default ideasData
The export is necessary since I will import the data in another file. I use the data in the program and then will modify it throughout.
import ideasData from "./ideasData";
class Ideas extends Component{
constructor() {
super()
this.state = {
ideas : ideasData
}}}
I want to overwrite the data in the file in a similar format(the array, then the export) every couple seconds as an autosave. So next time I open the webpage it will look like when it was closed.
I put a function autoSave() in the class and then setInterval(this.autoSave, 5000) in the render method before the return. I am not sure how to save the array, this.state.ideas to the ideasData file. Also, I am not sure about putting the setInterval in the render method. I changed it to setInterval(console.log("saved"),5000) and it seems to have only logged that in the console once.
So, we already have your file here:
const ideasData = [
{
content:"Content",
title: "This Title",
date: new Date()
},
{
content:"content2",
title: "second title",
date: new Date()
}
]
export default ideasData
Now, all you have to do is export a set method (in the same file as the code above) such as:
export function update(newData) {
ideasData = newData;
}
You will have to change const to let in order to mutate it, otherwise you can just clear it and push the new data in, or you could create a Class with it's own state. Either way.
Now, when setInterval(this.autoSave, 5000) runs, just import the new update function as well, and call update(this.state.ideas) to update your data cache.

React Native - Dynamic Image Source

I'm trying to loop through the SOURCE array with the map method, but I keep getting this error:
Unknown named module: '../images/one.jpeg'
Anyone know why this is happening? The file path in the require is definitely correct.
var SECTIONS = [
{
title: 'One',
fileName: 'one.jpeg',
},
{
title: 'Two',
fileName: 'two.jpeg',
},
{
title: 'Three',
fileName: 'three.jpeg',
},
{
title: 'Four',
fileName: 'four.jpeg',
},
];
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={require(`../images/${section.fileName}`)}
title={section.title}
/>
))}
I don't think this is possible because react native needs to know what to bundle ahead of time (AFAIK). However, you can require all the files in your array:
var SECTIONS = [
{
title: 'One',
file: require('../images/one.jpeg'),
},
{
title: 'Two',
file: require('../images/two.jpeg'),
},
{
title: 'Three',
file: require('../images/three.jpeg'),
},
{
title: 'Four',
file: require('../images/four.jpeg'),
},
];
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={section.file}
title={section.title}
/>
))}
You can't use dynamic links. The best hack that i found to solve this is this:
var SECTIONS = {
One: {
title: 'One',
file: require('../images/one.jpeg'),
},
Two: {
title: 'Two',
file: require('../images/two.jpeg'),
},
Three: {
title: 'Three',
file: require('../images/three.jpeg'),
},
Four: {
title: 'Four',
file: require('../images/four.jpeg'),
},
};
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={section.file}
title={section.title}
/>
))}
That way, you can just use the files and if you have some kind of dynamic image selection, you can just use something like this
<Image source={SECTIONS[image.type]} />
try opening the file in separate browser using direct URL something like
http://<><>/imgages/one.jpg
You can also do something like this as well:
One working example for displaying dynamic images using react :
Example Click Here
Got a working solution, though not recommended for large images, works perfectly for (a lot of)small images.
Steps:
Convert the icon(s) to base64 string(s).
Create a JSON file with filename as the keys and the base64 strings as values.
(You can also store them to a local database)
e.g.
ImageData.json
{
"icon1": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQ.......==",
"icon2": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQ.......=="
}
3.Import the json file to the place where you require the images dynamically.
e.g.
const imageData = require("./images/ImageData.json")
4: Get/generate the key/filename at runtime. and get the image source.
e.g.
const imageSrc = imageData[keyname]
5: Generate a image dynamically at runtime.
e.g.
<Image style={{ width: 70, height: 70, resizeMode: Image.resizeMode.contain }} source={ uri: imageSrc } />
Done..
Extra..
Written a helper python script to automate the json file creation.
import base64
import os
directory = os.fsencode('.')
with open('ImagesData.json', 'wb') as jsonFile:
jsonFile.write(bytes('{', 'utf-8'))
written = False
for file in os.listdir(directory):
filename = os.fsdecode(file)
if filename.endswith('.png'):
with open(filename, "rb") as image_file:
if written:
jsonFile.write(bytes(',\n','utf-8'))
encoded_string = base64.b64encode(image_file.read())
jsonFile.write(bytes(('"' +filename+ '":'), 'utf-8'))
jsonFile.write(bytes('"data:image/png;base64,', 'utf-8') + encoded_string + bytes('"', 'utf-8'))
written = True
jsonFile.write(bytes('}', 'utf-8'))
Copy the script to the image folder and run the script (requires python 3.6).
A json file will the created with image name as key and base64 string as values.
Copy the file to project and use (You can delete the images after that).
Use the json file as mentioned above.
I had the same problem but my situation was a little different. I had an array of different objects that needed dynamic images. I was already mapping the array, but I needed to match the images to that array based off of name. It was a little hacky, but this is how I went about it.
First, in my parent component I created a function to render a component for my array of objects. I passed the objects data into a prop called "object".
In my case I knew what my data was and I needed to match the corresponding image to the object that was being pulled off of an external api that I was grabbing my data from.
renderObjects() {
return this.state.objects.map(object => (
<ObjectListDetail
key={object.id}
next
props={this.props}
object={object}
/>
));
}
In my ObjectListDetail component, I created a variable called icons, which was another array of objects. This time, I created a name property that would match the object being passed to the component from the parent and then had a second key called source in which I provided the path to the image. It went something like this.
var icons = [
{ name: "BTC", source: Images.btc },
{ name: "ETH", source: Images.eth },
{ name: "ETC", source: Images.etc },
{ name: "ZRX", source: Images.zrx },
{ name: "USDC", source: Images.usdc },
{ name: "LTC", source: Images.ltc },
{ name: "BCH", source: Images.bch },
{ name: "USD", source: Images.usd }
];
NOTE *** I had already required all of my images into a separate file for my entire app and imported them at the top.
I then created a variable called imgSrc and filtered the result to match the name of the object i was passing to the child component.
var imgSrc = icons.filter(
icon => icon.name === props.wallet.name
)
I then created an Image component and in the source requirement I called the result of the filter and pointed it to the source.
<Image source={imgSrc[0].source} />
That is how I achieved dynamic image rendering within my application.
Its probably not the best way to do things, and I am still kinda new at this, but I would love any criticism

Categories