Split Summary so that it rerenders only partially
Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
parent
bf05594813
commit
2bfc16e3c0
@ -17,8 +17,10 @@ export function Cart({crate_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const crate = useShopStore(useShallow((state) => state.crates[crate_index]), (a, b) => {
|
||||
const crate = useShopStore((state) => state.crates[crate_index], (a, b) => {
|
||||
//console.log(a, b)
|
||||
return a.items.length === b.items.length && a.occupiedHP === b.occupiedHP && a.crate_mode === b.crate_mode
|
||||
//return a === b
|
||||
});
|
||||
const crateParams = useShopStore((state) => state.crateParams);
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import {SummaryPopup} from "./options/SummaryPopup";
|
||||
import {formatMoney} from "./utils";
|
||||
import {WarningIndicator} from "./CardWarnings";
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {SummaryCrates} from "./SummaryCrates";
|
||||
import {SummaryTotalPrice} from "./SummaryTotalPrice";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
|
||||
/**
|
||||
* Components that displays the list of card that are used in the crate.
|
||||
* It is a summary of purchase
|
||||
@ -14,18 +14,7 @@ import {useRenderCount} from "@uidotdev/usehooks";
|
||||
export function OrderSummary() {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const currency = useShopStore((state) => state.currency);
|
||||
const crates = useShopStore((state) => state.crates);
|
||||
const total_price = useShopStore((state) => state.totalOrderPrice());
|
||||
const crateParams = useShopStore((state) => state.crateParams);
|
||||
const deleteCard = useShopStore((state) => state.deleteCard);
|
||||
const setHighlight = useShopStore((state) => state.highlightCard);
|
||||
const resetHighlight = useShopStore((state) => state.highlightReset);
|
||||
const highlighted = useShopStore((state) => state.highlighted);
|
||||
const clearAll = useShopStore((state) => state.clearAll);
|
||||
const clearCrate = useShopStore((state) => state.clearCrate);
|
||||
const delCrate = useShopStore((state) => state.delCrate);
|
||||
|
||||
// #!render_count
|
||||
console.log("OrderSummary renders: ", renderCount)
|
||||
@ -47,91 +36,13 @@ export function OrderSummary() {
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{crates.map((crate, _i) => {
|
||||
let crate_type = crateParams(crate.crate_mode);
|
||||
return (
|
||||
<tbody key={"summary_crate_body" + crate.id}>
|
||||
<tr key={"summary_crate_" + crate.id}>
|
||||
<td className="item-card-name">{crate_type.name}</td>
|
||||
<td className="price">
|
||||
<div className="d-inline-flex">
|
||||
{`${currency} ${formatMoney(crate_type.price)}`}
|
||||
|
||||
<button onClick={() => clearCrate(crate.id)}>
|
||||
<img src="/images/shop/icon-clear.svg" alt="empty crate"/>
|
||||
</button>
|
||||
|
||||
<button onClick={() => delCrate(crate.id)}>
|
||||
<img src="/images/shop/icon-remove.svg" alt="remove crate"/>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{crate.items.map((item, index) => {
|
||||
const options = item && item.options;
|
||||
const options_data = item && item.options_data;
|
||||
const warnings = item && item.show_warnings;
|
||||
const selected = crate.id === highlighted.crate && index === highlighted.card;
|
||||
|
||||
return (<tr key={"summary_crate_" + crate.id + item.id}
|
||||
className={`hoverable ${selected ? 'selected' : ''}`}
|
||||
onClick={() => setHighlight(crate.id, index)}
|
||||
onMouseEnter={() => setHighlight(crate.id, index)}
|
||||
onMouseLeave={() => resetHighlight()}>
|
||||
<td className="item-card-name">
|
||||
<span style={{
|
||||
'display': 'inline-block',
|
||||
'width': '16px',
|
||||
}}> </span>
|
||||
<div>{`${item.name_number} ${item.name} ${item.name_codename}`}</div>
|
||||
</td>
|
||||
|
||||
<td className="price">
|
||||
<div className="d-inline-flex align-content-center">
|
||||
{`${currency} ${formatMoney(item.price)}`}
|
||||
|
||||
<button onClick={() => deleteCard(crate.id, index)}>
|
||||
<img src="/images/shop/icon-remove.svg"/>
|
||||
</button>
|
||||
|
||||
<div style={{'width': '45px', 'height': '20px'}}
|
||||
className="d-inline-flex align-content-center align-self-center justify-content-evenly">
|
||||
{(warnings && warnings.length > 0 ? (
|
||||
<WarningIndicator warnings={warnings}/>
|
||||
) : (
|
||||
<span style={{
|
||||
'display': 'inline-block',
|
||||
'width': '20px',
|
||||
}}> </span>
|
||||
))}
|
||||
{((options && options_data) ? (
|
||||
<SummaryPopup id={item.id + "options"} options={options}
|
||||
data={options_data}/>
|
||||
) : (
|
||||
<span style={{
|
||||
'display': 'inline-block',
|
||||
'width': '20px',
|
||||
}}> </span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>);
|
||||
})}
|
||||
</tbody>
|
||||
)
|
||||
})}
|
||||
<SummaryCrates/>
|
||||
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td className="item-card-name">Price estimate</td>
|
||||
<td className="price">
|
||||
<div>
|
||||
{currency} {formatMoney(total_price)}
|
||||
<button style={{'opacity': '0', 'cursor': 'initial'}}>
|
||||
<img src="/images/shop/icon-remove.svg" alt="icon remove"/>
|
||||
</button>
|
||||
</div>
|
||||
<SummaryTotalPrice/>
|
||||
|
||||
<span style={{
|
||||
'display': 'inline-block',
|
||||
|
@ -19,7 +19,10 @@ export function ProductCartItem({card_index, crate_index, ext_data, first, last}
|
||||
|
||||
|
||||
const card = useShopStore((state) => state.crates[crate_index].items[card_index],
|
||||
(a, b) => a.id === b.id && a.show_warnings === b.show_warnings && a.counted_resources === b.counted_resources );
|
||||
(a, b) => {
|
||||
//console.log(a.options_data, b.options_data, a.options_data === b.options_data)
|
||||
return a.id === b.id && a.show_warnings === b.show_warnings && a.counted_resources === b.counted_resources && a.options_data === b.options_data
|
||||
} );
|
||||
const highlighted = useShopStore((state) => state.crates[crate_index].id === state.highlighted.crate && card_index === state.highlighted.card);
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const setHighlight = useShopStore((state) => state.highlightCard);
|
||||
|
30
static/js/shop/SummaryCrate.jsx
Normal file
30
static/js/shop/SummaryCrate.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import {range} from "./utils";
|
||||
import React from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {SummaryCrateHeader} from "./SummaryCrateHeader";
|
||||
import {SummaryCrateCard} from "./SummaryCrateCard";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
export function SummaryCrate({crate_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const crate_len = useShopStore((state) => state.crates[crate_index].items.length);
|
||||
|
||||
// #!render_count
|
||||
console.log("SummaryCrate renders: ", renderCount)
|
||||
|
||||
return (
|
||||
<tbody key={"summary_crate_body" + crate_id}>
|
||||
|
||||
<SummaryCrateHeader crate_index={crate_index}/>
|
||||
|
||||
{range(0, crate_len).map((index, _i) =>
|
||||
<SummaryCrateCard crate_index={crate_index} card_index={index} key={"summary_crate_" + crate_id + "_" +index} />
|
||||
)}
|
||||
</tbody>
|
||||
)
|
||||
}
|
78
static/js/shop/SummaryCrateCard.jsx
Normal file
78
static/js/shop/SummaryCrateCard.jsx
Normal file
@ -0,0 +1,78 @@
|
||||
import {formatMoney} from "./utils";
|
||||
import {WarningIndicator} from "./CardWarnings";
|
||||
import {SummaryPopup} from "./options/SummaryPopup";
|
||||
import React from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
export function SummaryCrateCard({crate_index, card_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const currency = useShopStore((state) => state.currency);
|
||||
const deleteCard = useShopStore((state) => state.deleteCard);
|
||||
const setHighlight = useShopStore((state) => state.highlightCard);
|
||||
const resetHighlight = useShopStore((state) => state.highlightReset);
|
||||
|
||||
const highlighted = useShopStore((state) => state.crates[crate_index].id === state.highlighted.crate && card_index === state.highlighted.card);
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const card = useShopStore((state) => state.crates[crate_index].items[card_index],
|
||||
(a, b) => a.id === b.id && a.options_data === b.options_data && a.show_warnings === b.show_warnings);
|
||||
|
||||
// #!render_count
|
||||
console.log("SummaryCrateCard renders: ", renderCount)
|
||||
|
||||
|
||||
const options = card && card.options;
|
||||
const options_data = card && card.options_data;
|
||||
const warnings = card && card.show_warnings;
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={"summary_crate_" + crate_id + "_" + card_index}
|
||||
className={`hoverable ${highlighted ? 'selected' : ''}`}
|
||||
onClick={() => setHighlight(crate_id, card_index)}
|
||||
onMouseEnter={() => setHighlight(crate_id, card_index)}
|
||||
onMouseLeave={() => resetHighlight()}>
|
||||
<td className="item-card-name">
|
||||
<span style={{
|
||||
'display': 'inline-block',
|
||||
'width': '16px',
|
||||
}}> </span>
|
||||
<div>{`${card.name_number} ${card.name} ${card.name_codename}`}</div>
|
||||
</td>
|
||||
|
||||
<td className="price">
|
||||
<div className="d-inline-flex align-content-center">
|
||||
{`${currency} ${formatMoney(card.price)}`}
|
||||
|
||||
<button onClick={() => deleteCard(crate_id, card_index)}>
|
||||
<img src="/images/shop/icon-remove.svg"/>
|
||||
</button>
|
||||
|
||||
<div style={{'width': '45px', 'height': '20px'}}
|
||||
className="d-inline-flex align-content-center align-self-center justify-content-evenly">
|
||||
{(warnings && warnings.length > 0 ? (
|
||||
<WarningIndicator warnings={warnings}/>
|
||||
) : (
|
||||
<span style={{
|
||||
'display': 'inline-block',
|
||||
'width': '20px',
|
||||
}}> </span>
|
||||
))}
|
||||
{((options && options_data) ? (
|
||||
<SummaryPopup id={card.id + "options"} options={options}
|
||||
data={options_data}/>
|
||||
) : (
|
||||
<span style={{
|
||||
'display': 'inline-block',
|
||||
'width': '20px',
|
||||
}}> </span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>);
|
||||
}
|
43
static/js/shop/SummaryCrateHeader.jsx
Normal file
43
static/js/shop/SummaryCrateHeader.jsx
Normal file
@ -0,0 +1,43 @@
|
||||
import {formatMoney} from "./utils";
|
||||
import React from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
export function SummaryCrateHeader({crate_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const currency = useShopStore((state) => state.currency);
|
||||
const crateParams = useShopStore((state) => state.crateParams);
|
||||
const clearCrate = useShopStore((state) => state.clearCrate);
|
||||
const delCrate = useShopStore((state) => state.delCrate);
|
||||
|
||||
const crate_mode = useShopStore((state) => state.crates[crate_index].crate_mode);
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
|
||||
// #!render_count
|
||||
console.log("SummaryCrateHeader renders: ", renderCount)
|
||||
|
||||
let crate_type = crateParams(crate_mode);
|
||||
|
||||
return (
|
||||
<tr key={"summary_crate_" + crate_id}>
|
||||
<td className="item-card-name">{crate_type.name}</td>
|
||||
<td className="price">
|
||||
<div className="d-inline-flex">
|
||||
{`${currency} ${formatMoney(crate_type.price)}`}
|
||||
|
||||
<button onClick={() => clearCrate(crate_id)}>
|
||||
<img src="/images/shop/icon-clear.svg" alt="empty crate"/>
|
||||
</button>
|
||||
|
||||
<button onClick={() => delCrate(crate_id)}>
|
||||
<img src="/images/shop/icon-remove.svg" alt="remove crate"/>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
25
static/js/shop/SummaryCrates.jsx
Normal file
25
static/js/shop/SummaryCrates.jsx
Normal file
@ -0,0 +1,25 @@
|
||||
import {range} from "./utils";
|
||||
import React from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {SummaryCrate} from "./SummaryCrate";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
export function SummaryCrates() {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const crates_l = useShopStore((state) => state.crates.length);
|
||||
|
||||
// #!render_count
|
||||
console.log("SummaryCrates renders: ", renderCount)
|
||||
|
||||
return (
|
||||
<>
|
||||
{range(0, crates_l).map((index, _i) => {
|
||||
return <SummaryCrate crate_index={index} key={"summary_crate_body_" + index} />
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
17
static/js/shop/SummaryTotalPrice.jsx
Normal file
17
static/js/shop/SummaryTotalPrice.jsx
Normal file
@ -0,0 +1,17 @@
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {formatMoney} from "./utils";
|
||||
import React from "react";
|
||||
|
||||
export function SummaryTotalPrice() {
|
||||
const currency = useShopStore((state) => state.currency);
|
||||
const total_price = useShopStore((state) => state.totalOrderPrice());
|
||||
|
||||
return (
|
||||
<div>
|
||||
{currency} {formatMoney(total_price)}
|
||||
<button style={{'opacity': '0', 'cursor': 'initial'}}>
|
||||
<img src="/images/shop/icon-remove.svg" alt="icon remove"/>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -254,12 +254,14 @@ const useCart = ((set, get) => ({
|
||||
return {
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
if (crate_to === crate_from && crate_to === crate.id) {
|
||||
// TODO fix
|
||||
//const the_card = {...crate[index_from]};
|
||||
let items_copy = Array.from(crate.items);
|
||||
delete items_copy[index_from];
|
||||
console.log(crate_from, index_from, crate_to, index_to, items_copy.toSpliced(index_to+1, 0, the_card).filter((item, _) => !!item))
|
||||
return {
|
||||
...crate,
|
||||
items: items_copy.toSpliced(index_to+1, 0, the_card).filter((item, _) => !!item)
|
||||
items: items_copy.toSpliced(index_to+1, 0, {...the_card}).filter((item, _) => !!item)
|
||||
}
|
||||
} else if (crate_to === crate.id) {
|
||||
return {
|
||||
@ -305,7 +307,12 @@ const useCart = ((set, get) => ({
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
if (crate_id === crate.id) {
|
||||
let itemsCopy = Array.from(crate.items);
|
||||
itemsCopy[index].options_data = {...itemsCopy[index].options_data, ...new_options};
|
||||
itemsCopy[index] = {
|
||||
...itemsCopy[index],
|
||||
options_data: {
|
||||
...itemsCopy[index].options_data,
|
||||
...new_options
|
||||
}};
|
||||
return {
|
||||
...crate,
|
||||
items: itemsCopy
|
||||
|
@ -57,4 +57,9 @@ export function formatMoney(amount, decimalCount = 2, decimal = ".", thousands =
|
||||
} catch (e) {
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const range = (start, end) => {
|
||||
const length = end - start;
|
||||
return Array.from({ length }, (_, i) => start + i);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user