Spare cards is always last

Also add warning if no cards were added
pull/113/head
火焚 富良 2024-01-05 17:22:53 +08:00 committed by Egor Savkin
parent a589c309cc
commit 963f342c89
9 changed files with 96 additions and 46 deletions

View File

@ -11,6 +11,18 @@
.feedback-add-success { .feedback-add-success {
display: none; display: none;
} }
.feedback-add-failure {
background-color: #c75e5e;
display: block;
position: fixed;
top: 20px;
right: 20px;
padding: 1em;
z-index: 100000;
color: white;
border-radius: 10px;
box-shadow: 0 0 5px 3px;
}
#accordion_categories, #accordion_categories,
#accordion_categories .accordion-header, #accordion_categories .accordion-header,
@ -251,7 +263,7 @@
##Screen = B/w 481px to 767px ##Screen = B/w 481px to 767px
*/ */
@media (min-width: 481px) and (max-width: 767px) { @media (min-width: 481px) and (max-width: 767px) {
.feedback-add-success { .feedback-add-success, .feedback-add-failure {
background-color: green; background-color: green;
display: block; display: block;
position: fixed; position: fixed;
@ -263,6 +275,9 @@
border-radius: 10px; border-radius: 10px;
box-shadow: 0 0 5px 3px; box-shadow: 0 0 5px 3px;
} }
.feedback-add-failure {
background-color: #c75e5e;
}
.dropdown-item { .dropdown-item {
font-size: .75em; font-size: .75em;
@ -459,7 +474,7 @@
##Screen = B/w 320px to 479px ##Screen = B/w 320px to 479px
*/ */
@media (min-width: 320px) and (max-width: 480px) { @media (min-width: 320px) and (max-width: 480px) {
.feedback-add-success { .feedback-add-success, .feedback-add-failure {
background-color: green; background-color: green;
display: block; display: block;
position: fixed; position: fixed;
@ -471,6 +486,9 @@
border-radius: 10px; border-radius: 10px;
box-shadow: 0 0 5px 3px; box-shadow: 0 0 5px 3px;
} }
.feedback-add-failure {
background-color: #c75e5e;
}
.dropdown-item { .dropdown-item {
font-size: .75em; font-size: .75em;

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,5 @@ import React from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { Shop } from "./shop/Shop.jsx" import { Shop } from "./shop/Shop.jsx"
import { data } from "./shop/utils"
createRoot(document.querySelector('#root-shop')).render(<Shop data={data} />); createRoot(document.querySelector('#root-shop')).render(<Shop />);

View File

@ -16,19 +16,21 @@ export function CrateList() {
const onAddCrate = useShopStore((state) => state.newCrate); const onAddCrate = useShopStore((state) => state.newCrate);
const setActiveCrate = useShopStore((state) => state.setActiveCrate); const setActiveCrate = useShopStore((state) => state.setActiveCrate);
const onSelectHandler = (e) => {
// if e === null, that means that an accordion item was collapsed rather than expanded. e will be non-null when an item is expanded
if (e !== null)
setActiveCrate(e);
else
setActiveCrate("")
};
// #!render_count // #!render_count
console.log("CrateList renders: ", renderCount) console.log("CrateList renders: ", renderCount)
return ( return (
<Accordion id="accordion_crates" flush activeKey={active_crate} onSelect={(e) => { <Accordion id="accordion_crates" flush activeKey={active_crate} onSelect={onSelectHandler}>
// if e === null, that means that an accordion item was collapsed rather than expanded. e will be non-null when an item is expanded
if (e !== null)
setActiveCrate(e);
else
setActiveCrate("")
}}>
{crates.map((crate, index) => {crates.map((crate, index) =>
<Accordion.Item eventKey={crate.id} key={"accordion"+crate.id} className="accordion_crates_item"> <Accordion.Item eventKey={crate.id} key={"accordion"+crate.id} className="accordion_crates_item">
<Accordion.Header>Crate #{`${index}`}</Accordion.Header> <Accordion.Header>{crate.name ? crate.name : <>Crate #{`${index}`}</>} </Accordion.Header>
<Accordion.Body> <Accordion.Body>
<Crate crate_index={index}/> <Crate crate_index={index}/>
</Accordion.Body> </Accordion.Body>

View File

@ -19,16 +19,20 @@ export function CrateMode({crate_index}) {
// #!render_count // #!render_count
console.log("CrateMode renders: ", renderCount) console.log("CrateMode renders: ", renderCount)
return ( if (modes_order.includes(crate.crate_mode)) {
<div className="crate-mode"> return (
{modes_order.map((mode_name, _) => ( <div className="crate-mode">
<a {modes_order.map((mode_name, _) => (
key={mode_name} <a
className={(crate.crate_mode === mode_name ? 'active' : '') } key={mode_name}
onClick={() => setMode(crate.id, mode_name)} className={(crate.crate_mode === mode_name ? 'active' : '')}
href="#" onClick={() => setMode(crate.id, mode_name)}
role="button">{crate_modes[mode_name].name}</a> href="#"
))} role="button">{crate_modes[mode_name].name}</a>
</div> ))}
); </div>
);
} else {
return <div className="crate-mode"></div>
}
} }

View File

@ -16,26 +16,31 @@ export function Layout({aside, main}) {
const mobileSideMenuShouldOpen = useShopStore(state => state.sideMenuIsOpen); const mobileSideMenuShouldOpen = useShopStore(state => state.sideMenuIsOpen);
const onClickToggleMobileSideMenu = useShopStore(state => state.switchSideMenu); const onClickToggleMobileSideMenu = useShopStore(state => state.switchSideMenu);
const showCardAddedFeedback = useShopStore(state => state.showCardAddedFeedback); const showCardAddedFeedback = useShopStore(state => state.showCardAddedFeedback);
const showNoDestination = useShopStore(state => state.showNoDestination);
// #!render_count // #!render_count
console.log("Layout renders: ", renderCount) console.log("Layout renders: ", renderCount)
return ( return (
<div className="layout"> <div className="layout">
<aside className={'aside ' + (mobileSideMenuShouldOpen ? 'menu-opened' : '')}>{aside}</aside> <aside className={'aside ' + (mobileSideMenuShouldOpen ? 'menu-opened' : '')}>{aside}</aside>
{mobileSideMenuShouldOpen ? ( {mobileSideMenuShouldOpen ? (
<section className="main" onClick={onClickToggleMobileSideMenu}>{main}</section> <section className="main" onClick={onClickToggleMobileSideMenu}>{main}</section>
) : ( ) : (
<section className="main">{main}</section> <section className="main">{main}</section>
)} )}
{showCardAddedFeedback ? ( {showCardAddedFeedback ? (
<div className="feedback-add-success"> !showNoDestination ?
(<div className="feedback-add-success">
added added
</div> </div>)
) : null} : (<div className="feedback-add-failure">
</div> No cards added: all crates are closed
); </div>)
) : null}
</div>
);
} }

View File

@ -39,6 +39,7 @@ const useLayout = ((set, get) => ({
isMobile: window.deviceIsMobile(), isMobile: window.deviceIsMobile(),
sideMenuIsOpen: false, sideMenuIsOpen: false,
showCardAddedFeedback: false, showCardAddedFeedback: false,
showNoDestination: false,
timerAdded: null, timerAdded: null,
switchSideMenu: () => set(state => ({ switchSideMenu: () => set(state => ({
@ -46,10 +47,18 @@ const useLayout = ((set, get) => ({
})), })),
cardAdded: () => set(state => ({ cardAdded: () => set(state => ({
showCardAddedFeedback: true, showCardAddedFeedback: true,
showNoDestination: false,
timerAdded: (!!state.timerAdded ? clearTimeout(state.timerAdded) : null) || (state.isMobile && setTimeout(() => { timerAdded: (!!state.timerAdded ? clearTimeout(state.timerAdded) : null) || (state.isMobile && setTimeout(() => {
get()._endCardAdded() get()._endCardAdded()
}, 2000)) }, 2000))
})), })),
noDestinationWarning: () => set(state => ({
showCardAddedFeedback: true,
showNoDestination: true,
timerAdded: (!!state.timerAdded ? clearTimeout(state.timerAdded) : null) || (setTimeout(() => {
get()._endCardAdded()
}, 2000))
})),
_endCardAdded: () => set(state => ({ _endCardAdded: () => set(state => ({
showCardAddedFeedback: false, showCardAddedFeedback: false,
timerAdded: !!state.timerAdded ? clearTimeout(state.timerAdded) : null timerAdded: !!state.timerAdded ? clearTimeout(state.timerAdded) : null
@ -221,7 +230,7 @@ const useCart = ((set, get) => ({
active_crate: "crate0", active_crate: "crate0",
_newCrate: (crate_id) => set((state) => ({ _newCrate: (crate_id) => set((state) => ({
crates: state.crates.concat({ crates: state.crates.toSpliced(-1, 0, {
id: crate_id || "crate" + state.crates.length, id: crate_id || "crate" + state.crates.length,
crate_mode: "rack", crate_mode: "rack",
items: [], items: [],
@ -403,7 +412,11 @@ const useCart = ((set, get) => ({
addCardFromBacklog: (crate_to, index_from, index_to, just_mounted) => { addCardFromBacklog: (crate_to, index_from, index_to, just_mounted) => {
const dest = crate_to || get().active_crate; const dest = crate_to || get().active_crate;
if (!dest) return {}; if (!dest) {
console.warn("No destination");
get().noDestinationWarning();
return {};
}
get()._addCardFromBacklog(dest, index_from, index_to) get()._addCardFromBacklog(dest, index_from, index_to)
get().fillExtData(dest); get().fillExtData(dest);
get().fillWarnings(dest); get().fillWarnings(dest);

View File

@ -25,7 +25,7 @@ const shop_data = {
} }
}, },
crateModeOrder: [ crateModeOrder: [
"rack", "desktop", "no_crate" "rack", "desktop"
], ],
items: { items: {
@ -1156,7 +1156,16 @@ const shop_data = {
items: [], items: [],
warnings: [], warnings: [],
occupiedHP: 0, occupiedHP: 0,
}] },
{
id: "spare",
name: "Spare cards",
crate_mode: "no_crate",
items: [],
warnings: [],
occupiedHP: 0,
}
]
}, },
}; };

View File

@ -37,8 +37,8 @@ module.exports = {
resolve: { resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx'], extensions: ['.tsx', '.ts', '.js', '.jsx'],
}, },
devtool: "inline-source-map", //devtool: "inline-source-map",
mode: "development" //mode: "development"
//devtool: false, devtool: false,
//mode: "production" mode: "production"
}; };