Create core structure for the app
This commit is contained in:
parent
3b0a212ad4
commit
90ba2410b9
15
src/App.js
15
src/App.js
|
@ -1,17 +1,12 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Counter } from "./features/counter/Counter";
|
import { Grommet } from "grommet";
|
||||||
import "./App.css";
|
import Core from "./Core";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<Grommet plain>
|
||||||
<header className="App-header">
|
<Core />
|
||||||
<Counter />
|
</Grommet>
|
||||||
<p>
|
|
||||||
Edit <code>src/App.js</code> and save to reload.
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
|
|
||||||
|
import { setPlugin, selectCore } from "./coreSlice";
|
||||||
|
import * as plugins from "./plugins";
|
||||||
|
|
||||||
|
function Core() {
|
||||||
|
const coreState = useSelector(selectCore);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
console.log(plugins.default);
|
||||||
|
const plugin = plugins.default[coreState.plugin];
|
||||||
|
const props = {
|
||||||
|
data: coreState.data,
|
||||||
|
close: () => dispatch(setPlugin(false))
|
||||||
|
};
|
||||||
|
return <>
|
||||||
|
{plugin ? null :
|
||||||
|
<>
|
||||||
|
<p>Welcome to Honda!</p>
|
||||||
|
{Object.keys(plugins.default).map(
|
||||||
|
i => <p
|
||||||
|
onClick={() => dispatch(setPlugin(i))}
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
{plugins.default[i].pluginName}
|
||||||
|
</p>)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{plugin ?
|
||||||
|
React.cloneElement(plugin(), props)
|
||||||
|
:null
|
||||||
|
}
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Core;
|
|
@ -1,8 +1,8 @@
|
||||||
import { configureStore } from "@reduxjs/toolkit";
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
import counterReducer from "../features/counter/counterSlice";
|
import coreReducer from "../coreSlice";
|
||||||
|
|
||||||
export default configureStore({
|
export default configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
counter: counterReducer,
|
core: coreReducer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
export const coreSlice = createSlice({
|
||||||
|
name: "core",
|
||||||
|
initialState: {
|
||||||
|
plugin: false,
|
||||||
|
data: {}
|
||||||
|
},
|
||||||
|
reducers: {
|
||||||
|
setPlugin: (state, action) => ({...state, plugin: action.payload}),
|
||||||
|
setData: (state, action) => ({...state, data: action.payload}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setPlugin, setData } = coreSlice.actions;
|
||||||
|
export const selectCore = state => state.core;
|
||||||
|
export default coreSlice.reducer;
|
|
@ -1,58 +0,0 @@
|
||||||
import React, { useState } from "react";
|
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
|
||||||
import {
|
|
||||||
decrement,
|
|
||||||
increment,
|
|
||||||
incrementByAmount,
|
|
||||||
incrementAsync,
|
|
||||||
selectCount,
|
|
||||||
} from "./counterSlice";
|
|
||||||
import styles from "./Counter.module.css";
|
|
||||||
|
|
||||||
export function Counter() {
|
|
||||||
const count = useSelector(selectCount);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [incrementAmount, setIncrementAmount] = useState("2");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className={styles.row}>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
aria-label="Increment value"
|
|
||||||
onClick={() => dispatch(increment())}
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
<span className={styles.value}>{count}</span>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
aria-label="Decrement value"
|
|
||||||
onClick={() => dispatch(decrement())}
|
|
||||||
>
|
|
||||||
-
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className={styles.row}>
|
|
||||||
<input
|
|
||||||
className={styles.textbox}
|
|
||||||
aria-label="Set increment amount"
|
|
||||||
value={incrementAmount}
|
|
||||||
onChange={e => setIncrementAmount(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
onClick={() => dispatch(incrementByAmount(Number(incrementAmount) || 0))}
|
|
||||||
>
|
|
||||||
Add Amount
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className={styles.asyncButton}
|
|
||||||
onClick={() => dispatch(incrementAsync(Number(incrementAmount) || 0))}
|
|
||||||
>
|
|
||||||
Add Async
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:not(:last-child) {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
font-size: 78px;
|
|
||||||
padding-left: 16px;
|
|
||||||
padding-right: 16px;
|
|
||||||
margin-top: 2px;
|
|
||||||
font-family: 'Courier New', Courier, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
appearance: none;
|
|
||||||
background: none;
|
|
||||||
font-size: 32px;
|
|
||||||
padding-left: 12px;
|
|
||||||
padding-right: 12px;
|
|
||||||
outline: none;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
color: rgb(112, 76, 182);
|
|
||||||
padding-bottom: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: rgba(112, 76, 182, 0.1);
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textbox {
|
|
||||||
font-size: 32px;
|
|
||||||
padding: 2px;
|
|
||||||
width: 64px;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover, .button:focus {
|
|
||||||
border: 2px solid rgba(112, 76, 182, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:active {
|
|
||||||
background-color: rgba(112, 76, 182, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton {
|
|
||||||
composes: button;
|
|
||||||
position: relative;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton:after {
|
|
||||||
content: "";
|
|
||||||
background-color: rgba(112, 76, 182, 0.15);
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
opacity: 0;
|
|
||||||
transition: width 1s linear, opacity 0.5s ease 1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton:active:after {
|
|
||||||
width: 0%;
|
|
||||||
opacity: 1;
|
|
||||||
transition: 0s
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
import { createSlice } from "@reduxjs/toolkit";
|
|
||||||
|
|
||||||
export const counterSlice = createSlice({
|
|
||||||
name: "counter",
|
|
||||||
initialState: {
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
reducers: {
|
|
||||||
increment: state => {
|
|
||||||
// Redux Toolkit allows us to write "mutating" logic in reducers. It
|
|
||||||
// doesn"t actually mutate the state because it uses the Immer library,
|
|
||||||
// which detects changes to a "draft state" and produces a brand new
|
|
||||||
// immutable state based off those changes
|
|
||||||
state.value += 1;
|
|
||||||
},
|
|
||||||
decrement: state => {
|
|
||||||
state.value -= 1;
|
|
||||||
},
|
|
||||||
incrementByAmount: (state, action) => {
|
|
||||||
state.value += action.payload;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
|
|
||||||
|
|
||||||
// The function below is called a thunk and allows us to perform async logic. It
|
|
||||||
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
|
|
||||||
// will call the thunk with the `dispatch` function as the first argument. Async
|
|
||||||
// code can then be executed and other actions can be dispatched
|
|
||||||
export const incrementAsync = amount => dispatch => {
|
|
||||||
setTimeout(() => {
|
|
||||||
dispatch(incrementByAmount(amount));
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
// The function below is called a selector and allows us to select a value from
|
|
||||||
// the state. Selectors can also be defined inline where they"re used instead of
|
|
||||||
// in the slice file. For example: `useSelector((state) => state.counter.value)`
|
|
||||||
export const selectCount = state => state.counter.value;
|
|
||||||
|
|
||||||
export default counterSlice.reducer;
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import Mpd from "./mpd";
|
||||||
|
import Weather from "./weather";
|
||||||
|
export default {
|
||||||
|
mpd: Mpd,
|
||||||
|
weather: Weather,
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function Mpd() {
|
||||||
|
return <p> MPD Plugin </p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mpd.pluginName = "Music Player";
|
||||||
|
|
||||||
|
export default Mpd;
|
|
@ -0,0 +1,9 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function Weather() {
|
||||||
|
return <p> Weather Plugin </p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
Weather.pluginName = "Weather";
|
||||||
|
|
||||||
|
export default Weather;
|
Loading…
Reference in New Issue