Compare commits
No commits in common. "40e3cc9d7efab7f015ba409aee01a81db5b46d2b" and "3b0a212ad46a21b3da180491f41edb676f0432b3" have entirely different histories.
40e3cc9d7e
...
3b0a212ad4
|
@ -21,5 +21,3 @@
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
.eslintcache
|
|
||||||
|
|
15
src/App.js
15
src/App.js
|
@ -1,12 +1,17 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Grommet } from "grommet";
|
import { Counter } from "./features/counter/Counter";
|
||||||
import Core from "./Core";
|
import "./App.css";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<Grommet plain>
|
<div className="App">
|
||||||
<Core />
|
<header className="App-header">
|
||||||
</Grommet>
|
<Counter />
|
||||||
|
<p>
|
||||||
|
Edit <code>src/App.js</code> and save to reload.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
37
src/Core.js
37
src/Core.js
|
@ -1,37 +0,0 @@
|
||||||
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 coreReducer from "../coreSlice";
|
import counterReducer from "../features/counter/counterSlice";
|
||||||
|
|
||||||
export default configureStore({
|
export default configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
core: coreReducer,
|
counter: counterReducer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
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;
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
.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
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
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;
|
|
@ -1,6 +0,0 @@
|
||||||
import Mpd from "./mpd";
|
|
||||||
import Weather from "./weather";
|
|
||||||
export default {
|
|
||||||
mpd: Mpd,
|
|
||||||
weather: Weather,
|
|
||||||
};
|
|
|
@ -1,9 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
function Mpd() {
|
|
||||||
return <p> MPD Plugin </p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mpd.pluginName = "Music Player";
|
|
||||||
|
|
||||||
export default Mpd;
|
|
|
@ -1,9 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
function Weather() {
|
|
||||||
return <p> Weather Plugin </p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
Weather.pluginName = "Weather";
|
|
||||||
|
|
||||||
export default Weather;
|
|
Loading…
Reference in New Issue