|
@@ -50,15 +50,15 @@ const RGBInput = () => {
|
|
|
};
|
|
|
|
|
|
// Spoiler
|
|
|
-const Spoiler = ({ header = <h4>+</h4>, open, children }) => {
|
|
|
+const Spoiler = ({ header = <h2>+</h2>, open, children }) => {
|
|
|
const [visible, setVisible] = useState(open);
|
|
|
return (
|
|
|
<div className="Spoiler">
|
|
|
<div className="header" onClick={(e) => setVisible(!visible)}>
|
|
|
- <h3>
|
|
|
+ <div>
|
|
|
{header}
|
|
|
{visible ? "hide" : "show"}
|
|
|
- </h3>
|
|
|
+ </div>
|
|
|
{visible && children}
|
|
|
</div>
|
|
|
</div>
|
|
@@ -158,14 +158,20 @@ const Timer = ({
|
|
|
Hours: {hours.toFixed(3)} | Minutes: {minutes.toFixed(3)} | Seconds:{" "}
|
|
|
{seconds.toFixed(3)}
|
|
|
</h3>
|
|
|
- {onDelete && <button onClick={onDelete}>x</button>}
|
|
|
- <button
|
|
|
+ {onDelete && (
|
|
|
+ <Button variant="danger" className="m-1" onClick={onDelete}>
|
|
|
+ x
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+ <Button
|
|
|
+ variant="primary"
|
|
|
+ className="m-1"
|
|
|
onClick={() => {
|
|
|
setPause(!pause);
|
|
|
}}
|
|
|
>
|
|
|
{pause ? "Continue" : "Pause"}
|
|
|
- </button>
|
|
|
+ </Button>
|
|
|
</>
|
|
|
);
|
|
|
};
|
|
@@ -175,10 +181,28 @@ const Timers = () => {
|
|
|
const [ms, setMS] = useState(1000);
|
|
|
return (
|
|
|
<>
|
|
|
- <button onClick={() => setMS(ms + 100)}>+</button>
|
|
|
+ <Button
|
|
|
+ variant="secondary"
|
|
|
+ className="m-1"
|
|
|
+ onClick={() => setMS(ms + 100)}
|
|
|
+ >
|
|
|
+ +
|
|
|
+ </Button>
|
|
|
{ms}
|
|
|
- <button onClick={() => setMS(ms - 100)}>-</button>
|
|
|
- <button onClick={() => setTimers([Math.random(), ...timers])}>ADD</button>
|
|
|
+ <Button
|
|
|
+ variant="secondary"
|
|
|
+ className="m-1"
|
|
|
+ onClick={() => setMS(ms - 100)}
|
|
|
+ >
|
|
|
+ -
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ variant="success"
|
|
|
+ className="m-1"
|
|
|
+ onClick={() => setTimers([Math.random(), ...timers])}
|
|
|
+ >
|
|
|
+ ADD
|
|
|
+ </Button>
|
|
|
{timers.map((i) => (
|
|
|
<Timer
|
|
|
key={i}
|
|
@@ -225,9 +249,13 @@ const TimerControl = () => {
|
|
|
setSeconds(+e.currentTarget.value ? +e.currentTarget.value : 0)
|
|
|
}
|
|
|
/>{" "}
|
|
|
- <button onClick={() => setStart(!start)}>
|
|
|
+ <Button
|
|
|
+ variant="primary"
|
|
|
+ className="m-1"
|
|
|
+ onClick={() => setStart(!start)}
|
|
|
+ >
|
|
|
{start ? "Hide" : "Start"}
|
|
|
- </button>
|
|
|
+ </Button>
|
|
|
{start && <Timer h={hours} m={minutes} s={seconds} />}
|
|
|
</div>
|
|
|
);
|
|
@@ -333,7 +361,7 @@ const TimerControlContainer = ({ refresh, render: Render }) => {
|
|
|
|
|
|
return (
|
|
|
<div className="TimerControl">
|
|
|
- <label for="Seconds">{seconds} seconds</label>
|
|
|
+ <label htmlFor="Seconds">{seconds} seconds</label>
|
|
|
<input
|
|
|
id="Seconds"
|
|
|
min="0"
|
|
@@ -342,9 +370,13 @@ const TimerControlContainer = ({ refresh, render: Render }) => {
|
|
|
setSeconds(+e.currentTarget.value ? +e.currentTarget.value : 0)
|
|
|
}
|
|
|
/>{" "}
|
|
|
- <button onClick={() => setStart(!start)}>
|
|
|
+ <Button
|
|
|
+ variant="primary"
|
|
|
+ className="m-1"
|
|
|
+ onClick={() => setStart(!start)}
|
|
|
+ >
|
|
|
{start ? "Hide" : "Start"}
|
|
|
- </button>
|
|
|
+ </Button>
|
|
|
{start && <Render seconds={time.toFixed(1)} />}
|
|
|
</div>
|
|
|
);
|
|
@@ -602,8 +634,10 @@ const countries = {
|
|
|
|
|
|
const MySelect = ({ options = countries, value = "YE", onChange }) => (
|
|
|
<select value={value} onChange={(value) => onChange(value.target.value)}>
|
|
|
- {Object.entries(options).map((option) => (
|
|
|
- <option value={option[0]}>{option[1]}</option>
|
|
|
+ {Object.entries(options).map((option, i) => (
|
|
|
+ <option value={option[0]} key={i}>
|
|
|
+ {option[1]}
|
|
|
+ </option>
|
|
|
))}
|
|
|
</select>
|
|
|
);
|
|
@@ -621,6 +655,7 @@ const Dots = ({ active, count, onClick }) => {
|
|
|
: { border: "2px solid black" }
|
|
|
}
|
|
|
onClick={() => onClick(i)}
|
|
|
+ key={i}
|
|
|
>
|
|
|
{i + 1}
|
|
|
</div>
|
|
@@ -666,15 +701,183 @@ const Gallery = ({
|
|
|
);
|
|
|
};
|
|
|
|
|
|
+const PhoneBookEntry = ({
|
|
|
+ data = { name: "", phone: "" },
|
|
|
+ onChange,
|
|
|
+ onMoveUp,
|
|
|
+ onMoveDown,
|
|
|
+ onDelete,
|
|
|
+ onAdd,
|
|
|
+}) => (
|
|
|
+ <>
|
|
|
+ <input
|
|
|
+ className="m-1"
|
|
|
+ value={data.name}
|
|
|
+ type="text"
|
|
|
+ placeholder="Name"
|
|
|
+ onChange={(value) =>
|
|
|
+ onChange({ name: value.target.value, phone: data.phone })
|
|
|
+ }
|
|
|
+ />
|
|
|
+ <input
|
|
|
+ className="m-1"
|
|
|
+ value={data.phone}
|
|
|
+ type="number"
|
|
|
+ placeholder="Phone"
|
|
|
+ onChange={(value) =>
|
|
|
+ onChange({ name: data.name, phone: value.target.value })
|
|
|
+ }
|
|
|
+ />
|
|
|
+ <Button className="m-1" variant="primary" onClick={onMoveUp}>
|
|
|
+ ^
|
|
|
+ </Button>
|
|
|
+ <Button className="m-1" variant="primary" onClick={onMoveDown}>
|
|
|
+ v
|
|
|
+ </Button>
|
|
|
+ <Button className="m-1" variant="danger" onClick={onDelete}>
|
|
|
+ x
|
|
|
+ </Button>
|
|
|
+ <Button className="m-1" variant="warning" onClick={onAdd}>
|
|
|
+ +
|
|
|
+ </Button>
|
|
|
+ <br />
|
|
|
+ </>
|
|
|
+);
|
|
|
+
|
|
|
+const defaultPeople = [
|
|
|
+ { name: "John", phone: "102" },
|
|
|
+ { name: "Paul", phone: "103" },
|
|
|
+];
|
|
|
+
|
|
|
+const PhoneBook = ({ people = defaultPeople, onSave }) => {
|
|
|
+ const [ppl, setPpl] = useState(people);
|
|
|
+ const [keys, setKeys] = useState(people.map(() => Math.random()));
|
|
|
+ useEffect(() => {
|
|
|
+ setPpl(people);
|
|
|
+ }, [people]);
|
|
|
+
|
|
|
+ const onChange = (newData, i) => {
|
|
|
+ const people = [...ppl];
|
|
|
+ people[i] = newData;
|
|
|
+ return setPpl(people);
|
|
|
+ };
|
|
|
+
|
|
|
+ const onDelete = (i) => {
|
|
|
+ setKeys(keys.filter((k, index) => index !== i));
|
|
|
+ setPpl(ppl.filter((p, index) => index !== i));
|
|
|
+ };
|
|
|
+
|
|
|
+ const onAdd = (data, i) => {
|
|
|
+ const people = [...ppl];
|
|
|
+ people.splice(i + 1, 0, data);
|
|
|
+ setPpl(people);
|
|
|
+ const k = [...keys];
|
|
|
+ k.splice(i + 1, 0, Math.random());
|
|
|
+ setKeys(k);
|
|
|
+ };
|
|
|
+
|
|
|
+ const onMoveUp = (i) => {
|
|
|
+ const people = [...ppl];
|
|
|
+ const k = [...keys];
|
|
|
+ let currentP = people[i];
|
|
|
+ let currentK = k[i];
|
|
|
+ let prevP;
|
|
|
+ let prevK;
|
|
|
+ if (people[i - 1]) {
|
|
|
+ prevP = people[i - 1];
|
|
|
+ people[i - 1] = currentP;
|
|
|
+ people[i] = prevP;
|
|
|
+ setPpl(people);
|
|
|
+ prevK = k[i - 1];
|
|
|
+ k[i - 1] = currentK;
|
|
|
+ k[i] = prevK;
|
|
|
+ setKeys(k);
|
|
|
+ } else {
|
|
|
+ people.push(people.shift());
|
|
|
+ setPpl(people);
|
|
|
+ k.push(k.shift());
|
|
|
+ setKeys(k);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const onMoveDown = (i) => {
|
|
|
+ const people = [...ppl];
|
|
|
+ const k = [...keys];
|
|
|
+ let currentP = people[i];
|
|
|
+ let currentK = k[i];
|
|
|
+ let afterP;
|
|
|
+ let afterK;
|
|
|
+ if (people[i + 1]) {
|
|
|
+ afterP = people[i + 1];
|
|
|
+ people[i + 1] = currentP;
|
|
|
+ people[i] = afterP;
|
|
|
+ setPpl(people);
|
|
|
+ afterK = k[i + 1];
|
|
|
+ k[i + 1] = currentK;
|
|
|
+ k[i] = afterK;
|
|
|
+ setKeys(k);
|
|
|
+ } else {
|
|
|
+ people.unshift(people.pop());
|
|
|
+ setPpl(people);
|
|
|
+ k.unshift(k.pop());
|
|
|
+ setKeys(k);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <Button
|
|
|
+ className="m-1"
|
|
|
+ variant="success"
|
|
|
+ onClick={() => {
|
|
|
+ setPpl([{ name: "", phone: "" }, ...ppl]);
|
|
|
+ setKeys([Math.random(), ...keys]);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ Add to start
|
|
|
+ </Button>
|
|
|
+
|
|
|
+ <div className="PhoneBook">
|
|
|
+ {ppl.map((data, i) => (
|
|
|
+ <PhoneBookEntry
|
|
|
+ data={data}
|
|
|
+ onChange={(data) => onChange(data, i)}
|
|
|
+ onDelete={() => onDelete(i)}
|
|
|
+ onAdd={() => onAdd(data, i)}
|
|
|
+ onMoveUp={() => onMoveUp(i)}
|
|
|
+ onMoveDown={() => onMoveDown(i)}
|
|
|
+ key={keys[i]}
|
|
|
+ />
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ <Button
|
|
|
+ className="m-1"
|
|
|
+ variant="dark"
|
|
|
+ onClick={() => {
|
|
|
+ onSave(ppl);
|
|
|
+ onSave(keys);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ Save
|
|
|
+ </Button>
|
|
|
+ </>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
function App() {
|
|
|
const [country, setCountry] = useState("UA");
|
|
|
+ let secondsNow =
|
|
|
+ new Date().getHours() * 3600 +
|
|
|
+ new Date().getMinutes() * 60 +
|
|
|
+ new Date().getSeconds() +
|
|
|
+ new Date().getMilliseconds() / 1000;
|
|
|
return (
|
|
|
<div className="App">
|
|
|
<h3>RGB Input:</h3>
|
|
|
<RGBInput />
|
|
|
|
|
|
<h3>Spoilers:</h3>
|
|
|
- <Spoiler header={<h1>Заголовок</h1>} open>
|
|
|
+ <Spoiler header={<h2>Заголовок</h2>} open>
|
|
|
<h2>Контент 1</h2>
|
|
|
<p>
|
|
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Laudantium
|
|
@@ -719,7 +922,13 @@ function App() {
|
|
|
<TimerControlContainer refresh={100} render={TimerLCD} />
|
|
|
|
|
|
<h3>Watch:</h3>
|
|
|
- <TimerContainer seconds={10} refresh={100} render={Watch} mode="plus" />
|
|
|
+
|
|
|
+ <TimerContainer
|
|
|
+ seconds={secondsNow}
|
|
|
+ refresh={100}
|
|
|
+ render={Watch}
|
|
|
+ mode="plus"
|
|
|
+ />
|
|
|
|
|
|
<h3>MySelect:</h3>
|
|
|
<MySelect
|
|
@@ -729,6 +938,14 @@ function App() {
|
|
|
|
|
|
<h3>Gallery:</h3>
|
|
|
<Gallery />
|
|
|
+
|
|
|
+ <h3>Phones Book:</h3>
|
|
|
+ <PhoneBook
|
|
|
+ onSave={(phones, keys) => {
|
|
|
+ console.log(phones);
|
|
|
+ console.log(keys);
|
|
|
+ }}
|
|
|
+ />
|
|
|
</div>
|
|
|
);
|
|
|
}
|