Feat: redux for images in tier list
This commit is contained in:
@@ -11,8 +11,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
|
"@reduxjs/toolkit": "^2.5.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1",
|
||||||
|
"react-redux": "^9.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
|
|||||||
43
src/App.tsx
43
src/App.tsx
@@ -1,7 +1,9 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import { TierImage } from './components/Image';
|
import { TierImage } from './components/Image';
|
||||||
import { Tier, TierProps } from './components/Tier';
|
import { Tier, TierProps } from './components/Tier';
|
||||||
|
import { useAppDispatch, useAppSelector } from './hooks';
|
||||||
|
import { addTierImage } from './store';
|
||||||
|
|
||||||
const default_tier_levels: TierProps[] = [
|
const default_tier_levels: TierProps[] = [
|
||||||
{
|
{
|
||||||
@@ -21,17 +23,36 @@ const default_tier_levels: TierProps[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
interface tierImage {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
category: string;
|
||||||
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [images, setImages] = useState<string[]>([]);
|
const images = useAppSelector(state => state.tierImages);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(images);
|
console.log(images);
|
||||||
}, [images]);
|
}, [images]);
|
||||||
|
|
||||||
const uploadBtn = useRef<HTMLInputElement>(null);
|
const uploadBtn = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const clickUpload = () => {
|
const clickUpload = () => {
|
||||||
if (uploadBtn.current) uploadBtn.current.click();
|
if (uploadBtn.current) uploadBtn.current.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getRandomCategoryName = () => {
|
||||||
|
const categoryNames = default_tier_levels.flatMap(category => category.name);
|
||||||
|
return categoryNames[Math.floor(Math.random() * categoryNames.length)];
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAdd = (images: tierImage[]) => {
|
||||||
|
images.forEach(image => {
|
||||||
|
dispatch(addTierImage(image));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='flex flex-col w-full'>
|
<div className='flex flex-col w-full'>
|
||||||
@@ -45,9 +66,11 @@ function App() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-row w-full'>
|
<div className='flex flex-row w-full'>
|
||||||
{images.map((image, index) => (
|
{images
|
||||||
<TierImage image={image} name='test' key={index} />
|
.filter(image => image.category === '')
|
||||||
))}
|
.map((image, index) => (
|
||||||
|
<TierImage image={image.url} name='test' key={index} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className='border border-black w-20 text-center cursor-pointer' onClick={clickUpload}>
|
<div className='border border-black w-20 text-center cursor-pointer' onClick={clickUpload}>
|
||||||
<span>Upload</span>
|
<span>Upload</span>
|
||||||
@@ -60,12 +83,16 @@ function App() {
|
|||||||
onChange={event => {
|
onChange={event => {
|
||||||
console.log(event.target.files);
|
console.log(event.target.files);
|
||||||
if (event.target.files) {
|
if (event.target.files) {
|
||||||
const upload_images: string[] = [];
|
const upload_images: tierImage[] = [];
|
||||||
for (let i = 0; i < event.target.files.length; i++) {
|
for (let i = 0; i < event.target.files.length; i++) {
|
||||||
const url = URL.createObjectURL(event.target.files[i]);
|
const url = URL.createObjectURL(event.target.files[i]);
|
||||||
upload_images.push(url);
|
upload_images.push({
|
||||||
|
url: url,
|
||||||
|
name: event.target.files[i].name.replace('.jpeg', ''),
|
||||||
|
category: getRandomCategoryName(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
setImages(prev => [...prev, ...upload_images]);
|
handleAdd(upload_images);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ interface ImageProps {
|
|||||||
export const TierImage = ({ image, name }: ImageProps) => {
|
export const TierImage = ({ image, name }: ImageProps) => {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-wrap justify-center gap-4 mr-1'>
|
<div className='flex flex-wrap justify-center gap-4 mr-1'>
|
||||||
<div className={`w-[10rem] h-[calc(10rem*1.7)] relative`}>
|
<div className={`w-[calc(20rem*.5625)] h-80 relative`}>
|
||||||
<img src={image} alt={name} className='h-full w-full object-cover' />
|
<img src={image} alt={name} className='h-full w-full object-cover' />
|
||||||
<div className='w-full bg-slate-500 text-white bg-opacity-70 text-center bottom-0 left-0 absolute'>{name}</div>
|
<div className='w-full bg-slate-500 text-white bg-opacity-70 text-center bottom-0 left-0 absolute'>{name}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { useAppSelector } from '../hooks';
|
||||||
|
import { TierImage } from './Image';
|
||||||
|
|
||||||
export interface TierProps {
|
export interface TierProps {
|
||||||
color: 'red' | 'yellow' | 'green';
|
color: 'red' | 'yellow' | 'green';
|
||||||
name: string;
|
name: string;
|
||||||
@@ -6,6 +9,7 @@ export interface TierProps {
|
|||||||
export const Tier = ({ color, name, textColor }: TierProps) => {
|
export const Tier = ({ color, name, textColor }: TierProps) => {
|
||||||
let color_code = '';
|
let color_code = '';
|
||||||
let text_color_code = '';
|
let text_color_code = '';
|
||||||
|
const tierImages = useAppSelector(state => state.tierImages);
|
||||||
switch (color) {
|
switch (color) {
|
||||||
case 'green':
|
case 'green':
|
||||||
color_code = '#00b894';
|
color_code = '#00b894';
|
||||||
@@ -38,7 +42,13 @@ export const Tier = ({ color, name, textColor }: TierProps) => {
|
|||||||
>
|
>
|
||||||
<p>{name}</p>
|
<p>{name}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='image-container flex flex-row gap-1'></div>
|
<div className='image-container flex flex-row gap-1'>
|
||||||
|
{tierImages
|
||||||
|
.filter(image => image.category === name)
|
||||||
|
.map((image, index) => (
|
||||||
|
<TierImage image={image.url} name={image.name} key={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
</>
|
</>
|
||||||
|
|||||||
5
src/hooks.ts
Normal file
5
src/hooks.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { AppDispatch, RootState } from './store';
|
||||||
|
|
||||||
|
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
|
||||||
|
export const useAppSelector = useSelector.withTypes<RootState>();
|
||||||
16
src/main.tsx
16
src/main.tsx
@@ -1,10 +1,14 @@
|
|||||||
import { StrictMode } from 'react'
|
import { StrictMode } from 'react';
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client';
|
||||||
import './index.css'
|
import { Provider } from 'react-redux';
|
||||||
import App from './App.tsx'
|
import App from './App.tsx';
|
||||||
|
import './index.css';
|
||||||
|
import store from './store.ts';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<Provider store={store}>
|
||||||
|
<App />
|
||||||
|
</Provider>
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
);
|
||||||
|
|||||||
33
src/store.ts
Normal file
33
src/store.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
interface tierImage {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
category: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tierImages = createSlice({
|
||||||
|
name: 'images',
|
||||||
|
initialState: [] as tierImage[],
|
||||||
|
reducers: {
|
||||||
|
addTierImage: (state, action: PayloadAction<tierImage>) => {
|
||||||
|
state.push(action.payload);
|
||||||
|
},
|
||||||
|
removeTierImage: (state, action: PayloadAction<string>) => {
|
||||||
|
return state.filter(image => image.name !== action.payload);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { addTierImage, removeTierImage } = tierImages.actions;
|
||||||
|
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: {
|
||||||
|
tierImages: tierImages.reducer,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export type RootState = ReturnType<typeof store.getState>;
|
||||||
|
export type AppDispatch = typeof store.dispatch;
|
||||||
|
|
||||||
|
export default store;
|
||||||
Reference in New Issue
Block a user