forked from M-Labs/web2019
Significantly reduce number of renders
Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
parent
0e60eb9bce
commit
25c4ff970d
|
@ -13,6 +13,7 @@
|
||||||
"@babel/preset-env": "^7.23.2",
|
"@babel/preset-env": "^7.23.2",
|
||||||
"@babel/preset-react": "^7.22.15",
|
"@babel/preset-react": "^7.22.15",
|
||||||
"@hello-pangea/dnd": "^16.3.0",
|
"@hello-pangea/dnd": "^16.3.0",
|
||||||
|
"@uidotdev/usehooks": "^2.4.1",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
"babel-preset-minify": "^0.5.2",
|
"babel-preset-minify": "^0.5.2",
|
||||||
"bootstrap": "^5.3.0",
|
"bootstrap": "^5.3.0",
|
||||||
|
@ -2171,6 +2172,19 @@
|
||||||
"integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==",
|
"integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@uidotdev/usehooks": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18.0.0",
|
||||||
|
"react-dom": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@webassemblyjs/ast": {
|
"node_modules/@webassemblyjs/ast": {
|
||||||
"version": "1.11.6",
|
"version": "1.11.6",
|
||||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
|
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
|
||||||
|
|
|
@ -30,7 +30,8 @@
|
||||||
"webpack": "^5.89.0",
|
"webpack": "^5.89.0",
|
||||||
"webpack-cli": "^5.1.4",
|
"webpack-cli": "^5.1.4",
|
||||||
"json-logic-js": "^2.0.2",
|
"json-logic-js": "^2.0.2",
|
||||||
"zustand": "^4.4.7"
|
"zustand": "^4.4.7",
|
||||||
|
"@uidotdev/usehooks":"^2.4.1"
|
||||||
},
|
},
|
||||||
"babel": {
|
"babel": {
|
||||||
"presets": [
|
"presets": [
|
||||||
|
|
|
@ -2,24 +2,20 @@ import React from 'react';
|
||||||
import {Droppable} from "@hello-pangea/dnd";
|
import {Droppable} from "@hello-pangea/dnd";
|
||||||
import {ProductItem} from "./ProductItem";
|
import {ProductItem} from "./ProductItem";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that renders the backlog in the aside
|
* Component that renders the backlog in the aside
|
||||||
*/
|
*/
|
||||||
export function Backlog() {
|
export function Backlog() {
|
||||||
const {
|
const renderCount = useRenderCount();
|
||||||
data,
|
const data = useShopStore((state) => state.groups);
|
||||||
items,
|
const items = useShopStore((state) => state.cards);
|
||||||
onClickToggleMobileSideMenu,
|
const onClickToggleMobileSideMenu = useShopStore((state) => state.switchSideMenu);
|
||||||
isMobile,
|
const isMobile = useShopStore((state) => state.isMobile);
|
||||||
} = useShopStore(state=> ({
|
|
||||||
data: state.groups,
|
|
||||||
items: state.cards,
|
|
||||||
onClickToggleMobileSideMenu: state.switchSideMenu,
|
|
||||||
isMobile: state.isMobile
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log("Backlog renders: ", renderCount)
|
||||||
const ordered_groups = data.categories.map(groupItem => ({
|
const ordered_groups = data.categories.map(groupItem => ({
|
||||||
name: groupItem.name,
|
name: groupItem.name,
|
||||||
items: groupItem.itemIds.map(itemId => items[itemId])
|
items: groupItem.itemIds.map(itemId => items[itemId])
|
||||||
|
|
|
@ -6,15 +6,21 @@ import {FakePlaceholder} from "./FakePlaceholder";
|
||||||
import {FillExtData} from "./options/utils";
|
import {FillExtData} from "./options/utils";
|
||||||
import {hp_to_slots} from "./count_resources";
|
import {hp_to_slots} from "./count_resources";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
import {useShallow} from "zustand/react/shallow";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays a list of <ProductCartItem>
|
* Component that displays a list of <ProductCartItem>
|
||||||
*/
|
*/
|
||||||
export function Cart({crate_index}) {
|
export function Cart({crate_index}) {
|
||||||
const {crate, crateParams} = useShopStore(state => ({
|
const renderCount = useRenderCount();
|
||||||
crate: state.crates[crate_index],
|
|
||||||
crateParams: state.crateParams
|
const crate = useShopStore(useShallow((state) => state.crates[crate_index]), (a, b) => {
|
||||||
}));
|
return a.items.length === b.items.length && a.occupiedHP === b.occupiedHP && a.crate_mode === b.crate_mode
|
||||||
|
});
|
||||||
|
const crateParams = useShopStore((state) => state.crateParams);
|
||||||
|
|
||||||
|
console.log("Cart renders: ", renderCount)
|
||||||
|
|
||||||
const nbrOccupied = hp_to_slots(crate.occupiedHP);
|
const nbrOccupied = hp_to_slots(crate.occupiedHP);
|
||||||
const nbrSlots = hp_to_slots(crateParams(crate.crate_mode).hp);
|
const nbrSlots = hp_to_slots(crateParams(crate.crate_mode).hp);
|
||||||
|
|
|
@ -3,20 +3,19 @@ import {Cart} from "./Cart";
|
||||||
import {CrateMode} from "./CrateMode";
|
import {CrateMode} from "./CrateMode";
|
||||||
import {CrateWarnings} from "./CrateWarnings";
|
import {CrateWarnings} from "./CrateWarnings";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays the main crate with reminder rules.
|
* Component that displays the main crate with reminder rules.
|
||||||
* It includes <Cart> and rules
|
* It includes <Cart> and rules
|
||||||
*/
|
*/
|
||||||
export function Crate({crate_index}) {
|
export function Crate({crate_index}) {
|
||||||
const {
|
const renderCount = useRenderCount();
|
||||||
onDeleteCrate,
|
const crate = useShopStore((state) => state.crates[crate_index],
|
||||||
crate
|
(a, b) => a.length === b.length);
|
||||||
} = useShopStore(state => ({
|
const onDeleteCrate = useShopStore((state) => state.delCrate);
|
||||||
onDeleteCrate: state.delCrate,
|
|
||||||
crate: state.crates[crate_index]
|
|
||||||
}))
|
|
||||||
|
|
||||||
|
console.log("Crate renders: ", renderCount)
|
||||||
return (
|
return (
|
||||||
<div className="crate">
|
<div className="crate">
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,18 @@ import React from 'react'
|
||||||
import {Accordion} from "react-bootstrap";
|
import {Accordion} from "react-bootstrap";
|
||||||
import {Crate} from "./Crate";
|
import {Crate} from "./Crate";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
export function CrateList() {
|
export function CrateList() {
|
||||||
const {
|
const renderCount = useRenderCount();
|
||||||
crates,
|
|
||||||
active_crate,
|
const crates = useShopStore((state) => state.crates,
|
||||||
onAddCrate,
|
(a, b) => a.length === b.length);
|
||||||
setActiveCrate,
|
const active_crate = useShopStore((state) => state.active_crate);
|
||||||
} = useShopStore(state=> ({
|
const onAddCrate = useShopStore((state) => state.newCrate);
|
||||||
crates: state.crates,
|
const setActiveCrate = useShopStore((state) => state.setActiveCrate);
|
||||||
active_crate: state.active_crate,
|
|
||||||
onAddCrate: state.newCrate,
|
console.log("CrateList renders: ", renderCount)
|
||||||
setActiveCrate: state.setActiveCrate,
|
|
||||||
}));
|
|
||||||
return (
|
return (
|
||||||
<Accordion id="accordion_crates" flush activeKey={active_crate} onSelect={(e) => {
|
<Accordion id="accordion_crates" flush activeKey={active_crate} onSelect={(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, that means that an accordion item was collapsed rather than expanded. e will be non-null when an item is expanded
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays crate modes
|
* Component that displays crate modes
|
||||||
*/
|
*/
|
||||||
export function CrateMode({crate_index}) {
|
export function CrateMode({crate_index}) {
|
||||||
const {modes_order, crate_modes, crate, setMode} = useShopStore(state => ({
|
const renderCount = useRenderCount();
|
||||||
modes_order: state.modes_order,
|
|
||||||
crate_modes: state.crate_modes,
|
const modes_order = useShopStore((state) => state.modes_order);
|
||||||
crate: state.crates[crate_index],
|
const crate_modes = useShopStore((state) => state.crate_modes);
|
||||||
setMode: state.setCrateMode
|
const crate = useShopStore((state) => state.crates[crate_index],
|
||||||
}))
|
(a, b) => a.id === b.id && a.crate_mode === b.crate_mode);
|
||||||
|
const setMode = useShopStore((state) => state.setCrateMode);
|
||||||
|
|
||||||
|
console.log("CrateMode renders: ", renderCount)
|
||||||
return (
|
return (
|
||||||
<div className="crate-mode">
|
<div className="crate-mode">
|
||||||
{modes_order.map((mode_name, _) => (
|
{modes_order.map((mode_name, _) => (
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {LevelUI} from "./warnings";
|
import {LevelUI} from "./warnings";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
|
const compareArrays = (a, b) =>
|
||||||
|
a.length === b.length &&
|
||||||
|
a.every((element, index) => element.id === b[index].id);
|
||||||
|
|
||||||
export function CrateWarnings({crate_index}) {
|
export function CrateWarnings({crate_index}) {
|
||||||
const crate = useShopStore(state => (state.crates[crate_index]))
|
const renderCount = useRenderCount();
|
||||||
const crate_warnings = crate.warnings;
|
const crate_warnings = useShopStore(state => (state.crates[crate_index].warnings), compareArrays)
|
||||||
|
console.log("CrateWarnings renders: ", renderCount)
|
||||||
// TODO UI/colors
|
// TODO UI/colors
|
||||||
return (
|
return (
|
||||||
<div className="crate-info">
|
<div className="crate-info">
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays a placeholder inside crate.
|
* Component that displays a placeholder inside crate.
|
||||||
* Allows to display how it remains space for the current crate.
|
* Allows to display how it remains space for the current crate.
|
||||||
*/
|
*/
|
||||||
export function FakePlaceholder({isDraggingOver, nToDraw}) {
|
export function FakePlaceholder({isDraggingOver, nToDraw}) {
|
||||||
|
const renderCount = useRenderCount();
|
||||||
const fakePlaceholder = [];
|
const fakePlaceholder = [];
|
||||||
|
|
||||||
for (let i = nToDraw; i > 0; i--) {
|
for (let i = nToDraw; i > 0; i--) {
|
||||||
|
@ -17,6 +19,7 @@ export function FakePlaceholder({isDraggingOver, nToDraw}) {
|
||||||
}}></div>
|
}}></div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
//console.log("FakePlaceholder renders: ", renderCount)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {useClickAway} from "./options/useClickAway";
|
||||||
import {Modal} from "react-bootstrap";
|
import {Modal} from "react-bootstrap";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Validation} from "./validate";
|
import {Validation} from "./validate";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
const JSONExample = JSON.stringify({
|
const JSONExample = JSON.stringify({
|
||||||
"items": [{"pn": "1124"}, {"pn": "2118"}, {"pn": "2118"}, {"pn": "2128"}],
|
"items": [{"pn": "1124"}, {"pn": "2118"}, {"pn": "2118"}, {"pn": "2128"}],
|
||||||
|
@ -10,14 +11,16 @@ const JSONExample = JSON.stringify({
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ImportJSON() {
|
export function ImportJSON() {
|
||||||
const {shouldShow, data, loadDescription, updateImportDescription, closeImport, showImport} = useShopStore(state => ({
|
const renderCount = useRenderCount();
|
||||||
shouldShow: state.importShouldOpen,
|
|
||||||
data: state.importValue,
|
const shouldShow = useShopStore((state) => state.importShouldOpen);
|
||||||
loadDescription: state.loadDescription,
|
const data = useShopStore((state) => state.importValue);
|
||||||
updateImportDescription: state.updateImportDescription,
|
const loadDescription = useShopStore((state) => state.loadDescription);
|
||||||
closeImport: state.closeImport,
|
const updateImportDescription = useShopStore((state) => state.updateImportDescription);
|
||||||
showImport: state.openImport,
|
const closeImport = useShopStore((state) => state.closeImport);
|
||||||
}));
|
const showImport = useShopStore((state) => state.openImport);
|
||||||
|
|
||||||
|
console.log("ImportJSON renders: ", renderCount)
|
||||||
|
|
||||||
const ref = useClickAway((e) => {
|
const ref = useClickAway((e) => {
|
||||||
if (e.type === "mousedown") // ignore touchstart
|
if (e.type === "mousedown") // ignore touchstart
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that provides a base layout (aside/main) for the page.
|
* Component that provides a base layout (aside/main) for the page.
|
||||||
|
@ -7,12 +8,13 @@ import {useShopStore} from "./shop_store";
|
||||||
|
|
||||||
|
|
||||||
export function Layout({aside, main}) {
|
export function Layout({aside, main}) {
|
||||||
const {mobileSideMenuShouldOpen, onClickToggleMobileSideMenu, showCardAddedFeedback} = useShopStore((state) => ({
|
const renderCount = useRenderCount();
|
||||||
mobileSideMenuShouldOpen: state.sideMenuIsOpen,
|
|
||||||
onClickToggleMobileSideMenu: state.switchSideMenu,
|
const mobileSideMenuShouldOpen = useShopStore(state => state.sideMenuIsOpen);
|
||||||
showCardAddedFeedback: state.showCardAddedFeedback,
|
const onClickToggleMobileSideMenu = useShopStore(state => state.switchSideMenu);
|
||||||
isMobile: state.isMobile,
|
const showCardAddedFeedback = useShopStore(state => state.showCardAddedFeedback);
|
||||||
}));
|
|
||||||
|
console.log("Layout renders: ", renderCount)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="layout">
|
<div className="layout">
|
||||||
|
|
|
@ -2,31 +2,25 @@ import React from 'react'
|
||||||
import {Validation} from "./validate.js";
|
import {Validation} from "./validate.js";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
import {ShowJSON} from "./ShowJSON";
|
import {ShowJSON} from "./ShowJSON";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Components that renders the form to request quote.
|
* Components that renders the form to request quote.
|
||||||
*/
|
*/
|
||||||
export function OrderForm() {
|
export function OrderForm() {
|
||||||
const {
|
const renderCount = useRenderCount();
|
||||||
email,
|
|
||||||
note,
|
|
||||||
isProcessing,
|
|
||||||
updateEmail,
|
|
||||||
updateNote,
|
|
||||||
resetEmailValidation,
|
|
||||||
submitForm,
|
|
||||||
submitDisabled,
|
|
||||||
} = useShopStore(state => ({
|
|
||||||
email: state.email,
|
|
||||||
note: state.note,
|
|
||||||
isProcessing: state.isProcessing,
|
|
||||||
updateEmail: state.updateEmail,
|
|
||||||
updateNote: state.updateNote,
|
|
||||||
submitForm: state.submitForm,
|
|
||||||
submitDisabled: state.submitDisabled,
|
|
||||||
resetEmailValidation: state.resetEmailValidation
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
const email = useShopStore((state) => state.email);
|
||||||
|
const note = useShopStore((state) => state.note);
|
||||||
|
const isProcessing = useShopStore((state) => state.isProcessing);
|
||||||
|
const updateEmail = useShopStore((state) => state.updateEmail);
|
||||||
|
const updateNote = useShopStore((state) => state.updateNote);
|
||||||
|
const submitForm = useShopStore((state) => state.submitForm);
|
||||||
|
const submitDisabled = useShopStore((state) => state.submitDisabled);
|
||||||
|
const resetEmailValidation = useShopStore((state) => state.resetEmailValidation);
|
||||||
|
|
||||||
|
console.log("OrderForm renders: ", renderCount)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="summary-form">
|
<div className="summary-form">
|
||||||
|
|
|
@ -5,15 +5,19 @@ import {CrateList} from "./CrateList";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
import {ImportJSON} from "./ImportJSON";
|
import {ImportJSON} from "./ImportJSON";
|
||||||
import {RFQFeedback} from "./RFQFeedback";
|
import {RFQFeedback} from "./RFQFeedback";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that renders all things for order.
|
* Component that renders all things for order.
|
||||||
* It acts like-a layout, this component do nothing more.
|
* It acts like-a layout, this component do nothing more.
|
||||||
*/
|
*/
|
||||||
export function OrderPanel({title, description}) {
|
export function OrderPanel({title, description}) {
|
||||||
|
const renderCount = useRenderCount();
|
||||||
const isMobile = useShopStore((state) => state.isMobile);
|
const isMobile = useShopStore((state) => state.isMobile);
|
||||||
const onClickToggleMobileSideMenu = useShopStore((state) => state.switchSideMenu);
|
const onClickToggleMobileSideMenu = useShopStore((state) => state.switchSideMenu);
|
||||||
|
|
||||||
|
console.log("OrderPanel renders: ", renderCount)
|
||||||
|
|
||||||
return (<section className="panel">
|
return (<section className="panel">
|
||||||
|
|
||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
|
|
|
@ -3,38 +3,29 @@ import {SummaryPopup} from "./options/SummaryPopup";
|
||||||
import {formatMoney} from "./utils";
|
import {formatMoney} from "./utils";
|
||||||
import {WarningIndicator} from "./CardWarnings";
|
import {WarningIndicator} from "./CardWarnings";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Components that displays the list of card that are used in the crate.
|
* Components that displays the list of card that are used in the crate.
|
||||||
* It is a summary of purchase
|
* It is a summary of purchase
|
||||||
*/
|
*/
|
||||||
export function OrderSummary() {
|
export function OrderSummary() {
|
||||||
|
const renderCount = useRenderCount();
|
||||||
|
|
||||||
const {
|
const currency = useShopStore((state) => state.currency);
|
||||||
currency,
|
const crates = useShopStore((state) => state.crates);
|
||||||
crates,
|
const total_price = useShopStore((state) => state.totalOrderPrice());
|
||||||
total_price,
|
const crateParams = useShopStore((state) => state.crateParams);
|
||||||
crateParams,
|
const deleteCard = useShopStore((state) => state.deleteCard);
|
||||||
deleteCard,
|
const setHighlight = useShopStore((state) => state.highlightCard);
|
||||||
setHighlight,
|
const resetHighlight = useShopStore((state) => state.highlightReset);
|
||||||
resetHighlight,
|
const highlighted = useShopStore((state) => state.highlighted);
|
||||||
highlighted,
|
const clearAll = useShopStore((state) => state.clearAll);
|
||||||
clearCrate,
|
const clearCrate = useShopStore((state) => state.clearCrate);
|
||||||
delCrate,
|
const delCrate = useShopStore((state) => state.delCrate);
|
||||||
clearAll
|
|
||||||
} = useShopStore(state =>({
|
|
||||||
currency: state.currency,
|
console.log("OrderSummary renders: ", renderCount)
|
||||||
crates: state.crates,
|
|
||||||
total_price: state.totalOrderPrice(),
|
|
||||||
crateParams: state.crateParams,
|
|
||||||
deleteCard: state.deleteCard,
|
|
||||||
setHighlight: state.highlightCard,
|
|
||||||
resetHighlight: state.highlightReset,
|
|
||||||
highlighted: state.highlighted,
|
|
||||||
clearAll: state.clearAll,
|
|
||||||
clearCrate: state.clearCrate,
|
|
||||||
delCrate: state.delCrate,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="summary-price">
|
<div className="summary-price">
|
||||||
|
|
|
@ -5,21 +5,26 @@ import {productStyle} from "./utils";
|
||||||
import {Resources} from "./Resources";
|
import {Resources} from "./Resources";
|
||||||
import {CardWarnings} from "./CardWarnings";
|
import {CardWarnings} from "./CardWarnings";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that renders a product.
|
* Component that renders a product.
|
||||||
* Used in the crate
|
* Used in the crate
|
||||||
*/
|
*/
|
||||||
export function ProductCartItem({card_index, crate_index, ext_data, first, last}) {
|
export function ProductCartItem({card_index, crate_index, ext_data, first, last}) {
|
||||||
const {card, crate, highlighted, setHighlight, removeHighlight, onCardUpdate, onCardRemove} = useShopStore(state => ({
|
const renderCount = useRenderCount();
|
||||||
card: state.crates[crate_index].items[card_index],
|
|
||||||
highlighted: state.crates[crate_index].id === state.highlighted.crate && card_index === state.highlighted.card,
|
|
||||||
crate: state.crates[crate_index],
|
const card = useShopStore((state) => state.crates[crate_index].items[card_index],
|
||||||
setHighlight: state.highlightCard,
|
(a, b) => a.id === b.id && a.show_warnings === b.show_warnings && a.counted_resources === b.counted_resources );
|
||||||
removeHighlight: state.highlightReset,
|
const highlighted = useShopStore((state) => state.crates[crate_index].id === state.highlighted.crate && card_index === state.highlighted.card);
|
||||||
onCardUpdate: state.updateOptions,
|
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||||
onCardRemove: state.deleteCard
|
const setHighlight = useShopStore((state) => state.highlightCard);
|
||||||
}))
|
const removeHighlight = useShopStore((state) => state.highlightReset);
|
||||||
|
const onCardUpdate = useShopStore((state) => state.updateOptions);
|
||||||
|
const onCardRemove = useShopStore((state) => state.deleteCard);
|
||||||
|
|
||||||
|
console.log("ProductCartItem renders: ", renderCount)
|
||||||
|
|
||||||
let options, options_data;
|
let options, options_data;
|
||||||
const warnings = card && card.show_warnings;
|
const warnings = card && card.show_warnings;
|
||||||
|
@ -50,7 +55,7 @@ export function ProductCartItem({card_index, crate_index, ext_data, first, last}
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => setHighlight(crate.id, card_index)}
|
onMouseEnter={() => setHighlight(crate_id, card_index)}
|
||||||
onMouseLeave={removeHighlight}
|
onMouseLeave={removeHighlight}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
@ -79,7 +84,7 @@ export function ProductCartItem({card_index, crate_index, ext_data, first, last}
|
||||||
update: ((outvar, value) => {
|
update: ((outvar, value) => {
|
||||||
// console.log("update", outvar, value, options_data);
|
// console.log("update", outvar, value, options_data);
|
||||||
if (outvar in options_data) options_data[outvar] = value;
|
if (outvar in options_data) options_data[outvar] = value;
|
||||||
onCardUpdate(crate.id, card_index, {[outvar]: value});
|
onCardUpdate(crate_id, card_index, {[outvar]: value});
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -89,8 +94,8 @@ export function ProductCartItem({card_index, crate_index, ext_data, first, last}
|
||||||
<h6>{card.name_number}</h6>
|
<h6>{card.name_number}</h6>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
onMouseEnter={() => setHighlight(crate.id, card_index)}
|
onMouseEnter={() => setHighlight(crate_id, card_index)}
|
||||||
onClick={() => setHighlight(crate.id, card_index)}
|
onClick={() => setHighlight(crate_id, card_index)}
|
||||||
>
|
>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
|
@ -102,7 +107,7 @@ export function ProductCartItem({card_index, crate_index, ext_data, first, last}
|
||||||
<div
|
<div
|
||||||
style={{'display': highlighted ? 'flex' : 'none'}}
|
style={{'display': highlighted ? 'flex' : 'none'}}
|
||||||
className="overlayRemove"
|
className="overlayRemove"
|
||||||
onClick={() => onCardRemove(crate.id, card_index)}>
|
onClick={() => onCardRemove(crate_id, card_index)}>
|
||||||
|
|
||||||
<img src="/images/shop/icon-remove.svg" alt="rm"/>
|
<img src="/images/shop/icon-remove.svg" alt="rm"/>
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,21 @@ import React from 'react';
|
||||||
import {Draggable} from "@hello-pangea/dnd";
|
import {Draggable} from "@hello-pangea/dnd";
|
||||||
import {formatMoney, productStyle} from "./utils";
|
import {formatMoney, productStyle} from "./utils";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that renders a product.
|
* Component that renders a product.
|
||||||
* Used in the aside (e.g backlog of product)
|
* Used in the aside (e.g backlog of product)
|
||||||
*/
|
*/
|
||||||
export function ProductItem({card_index}) {
|
export function ProductItem({card_index}) {
|
||||||
const {card, currency, onAddCard} = useShopStore(state => ({
|
const renderCount = useRenderCount();
|
||||||
card: state.getCardDescription(card_index),
|
|
||||||
currency: state.currency,
|
const getCardDescription = useShopStore((state) => state.getCardDescription);
|
||||||
onAddCard: state.addCardFromBacklog
|
const currency = useShopStore((state) => state.currency);
|
||||||
}));
|
const onAddCard = useShopStore((state) => state.addCardFromBacklog);
|
||||||
|
const card = getCardDescription(card_index);
|
||||||
|
|
||||||
|
console.log("ProductItem renders: ", renderCount)
|
||||||
|
|
||||||
|
|
||||||
const render_specs = (card.specs && card.specs.length > 0 && (
|
const render_specs = (card.specs && card.specs.length > 0 && (
|
||||||
|
|
|
@ -3,13 +3,16 @@ import {useClickAway} from "./options/useClickAway";
|
||||||
import {Modal} from "react-bootstrap";
|
import {Modal} from "react-bootstrap";
|
||||||
import {Validation} from "./validate";
|
import {Validation} from "./validate";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
export function RFQFeedback() {
|
export function RFQFeedback() {
|
||||||
const {closeRFQ, shouldShow, status} = useShopStore(state => ({
|
const renderCount = useRenderCount();
|
||||||
closeRFQ: state.closeRFQFeedback,
|
|
||||||
shouldShow: state.shouldShowRFQFeedback,
|
const closeRFQ = useShopStore((state) => state.closeRFQFeedback);
|
||||||
status: state.processingResult
|
const shouldShow = useShopStore((state) => state.shouldShowRFQFeedback);
|
||||||
}))
|
const status = useShopStore((state) => state.processingResult);
|
||||||
|
|
||||||
|
console.log("RFQFeedback renders: ", renderCount)
|
||||||
|
|
||||||
const ref = useClickAway((e) => {
|
const ref = useClickAway((e) => {
|
||||||
if (e.type === "mousedown") // ignore touchstart
|
if (e.type === "mousedown") // ignore touchstart
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {OverlayTrigger} from "react-bootstrap";
|
import {OverlayTrigger} from "react-bootstrap";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {v4 as uuidv4} from "uuid";
|
import {v4 as uuidv4} from "uuid";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
|
|
||||||
const resourcesWidthStyle = (occupied, max) => {
|
const resourcesWidthStyle = (occupied, max) => {
|
||||||
|
@ -62,6 +63,8 @@ function RenderResources({resources, library}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Resources({resources}) {
|
export function Resources({resources}) {
|
||||||
|
const renderCount = useRenderCount();
|
||||||
|
console.log("Resources renders: ", renderCount)
|
||||||
return (
|
return (
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
placement="top"
|
placement="top"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, {useEffect} from 'react';
|
import React, {useEffect} from 'react';
|
||||||
import {DragDropContext} from "@hello-pangea/dnd";
|
import {DragDropContext} from "@hello-pangea/dnd";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
|
|
||||||
import {Layout} from "./Layout";
|
import {Layout} from "./Layout";
|
||||||
|
@ -12,12 +13,19 @@ import {useShopStore} from "./shop_store";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function Shop() {
|
export function Shop() {
|
||||||
const {addCardFromBacklog, moveCard, deleteCard, cardIndexById} = useShopStore(state => ({
|
const renderCount = useRenderCount();
|
||||||
|
|
||||||
|
const addCardFromBacklog = useShopStore((state) => state.addCardFromBacklog);
|
||||||
|
const moveCard = useShopStore((state) => state.moveCard);
|
||||||
|
const deleteCard = useShopStore((state) => state.deleteCard);
|
||||||
|
const cardIndexById = useShopStore((state) => state.cardIndexById);
|
||||||
|
|
||||||
|
/*const {addCardFromBacklog, moveCard, deleteCard, cardIndexById} = useShopStore(useShallow(state => ({
|
||||||
addCardFromBacklog: state.addCardFromBacklog,
|
addCardFromBacklog: state.addCardFromBacklog,
|
||||||
moveCard: state.moveCard,
|
moveCard: state.moveCard,
|
||||||
deleteCard: state.deleteCard,
|
deleteCard: state.deleteCard,
|
||||||
cardIndexById: state.cardIndexById
|
cardIndexById: state.cardIndexById
|
||||||
}));
|
})));*/
|
||||||
const handleOnDragEnd = (drop_result, _provided) => {
|
const handleOnDragEnd = (drop_result, _provided) => {
|
||||||
if (!drop_result.destination) {
|
if (!drop_result.destination) {
|
||||||
console.warn("No drop destination");
|
console.warn("No drop destination");
|
||||||
|
@ -25,7 +33,7 @@ export function Shop() {
|
||||||
}
|
}
|
||||||
if (drop_result.source.droppableId === "backlog")
|
if (drop_result.source.droppableId === "backlog")
|
||||||
addCardFromBacklog(drop_result.destination.droppableId, drop_result.source.index, drop_result.destination.index);
|
addCardFromBacklog(drop_result.destination.droppableId, drop_result.source.index, drop_result.destination.index);
|
||||||
else if(drop_result.destination.droppableId === "backlog")
|
else if (drop_result.destination.droppableId === "backlog")
|
||||||
deleteCard(drop_result.destination.droppableId, drop_result.destination.index);
|
deleteCard(drop_result.destination.droppableId, drop_result.destination.index);
|
||||||
else
|
else
|
||||||
moveCard(drop_result.source.droppableId, drop_result.source.index, drop_result.destination.droppableId, drop_result.destination.index)
|
moveCard(drop_result.source.droppableId, drop_result.source.index, drop_result.destination.droppableId, drop_result.destination.index)
|
||||||
|
@ -35,6 +43,7 @@ export function Shop() {
|
||||||
addCardFromBacklog(null, [cardIndexById("eem_pwr_mod"), cardIndexById("kasli")], -1, true);
|
addCardFromBacklog(null, [cardIndexById("eem_pwr_mod"), cardIndexById("kasli")], -1, true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
console.log("Shop renders: ", renderCount)
|
||||||
return (
|
return (
|
||||||
<DragDropContext onDragEnd={handleOnDragEnd}>
|
<DragDropContext onDragEnd={handleOnDragEnd}>
|
||||||
<Layout
|
<Layout
|
||||||
|
@ -44,8 +53,14 @@ export function Shop() {
|
||||||
main={(
|
main={(
|
||||||
<OrderPanel
|
<OrderPanel
|
||||||
title="Order hardware"
|
title="Order hardware"
|
||||||
description={(<p className="description">Drag and drop the cards you want into the crate below to see how the combination would look like. Setup card's configuration by tapping at the top of the card, most of the options can be modified after shipment. If you have any issues with this ordering system, or if you need other configurations, email us directly anytime at <a href="mailto:sales@m-labs.hk">sales@m-labs.hk</a>. The price is estimated and must be confirmed by a quote.</p>)}
|
description={(
|
||||||
/>
|
<p className="description">Drag and drop the cards you want into the crate below to see how
|
||||||
|
the combination would look like. Setup card's configuration by tapping at the top of the
|
||||||
|
card, most of the options can be modified after shipment. If you have any issues with
|
||||||
|
this ordering system, or if you need other configurations, email us directly anytime
|
||||||
|
at <a href="mailto:sales@m-labs.hk">sales@m-labs.hk</a>. The price is estimated and must
|
||||||
|
be confirmed by a quote.</p>)}
|
||||||
|
/>
|
||||||
)}>
|
)}>
|
||||||
</Layout>
|
</Layout>
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
|
|
|
@ -2,14 +2,17 @@ import React from "react";
|
||||||
import {Modal} from "react-bootstrap";
|
import {Modal} from "react-bootstrap";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
import {useClickAway} from "./options/useClickAway";
|
import {useClickAway} from "./options/useClickAway";
|
||||||
|
import {useRenderCount} from "@uidotdev/usehooks";
|
||||||
|
|
||||||
export function ShowJSON() {
|
export function ShowJSON() {
|
||||||
const {shouldShow, description, showDescription, closeDescription} = useShopStore(state => ({
|
const renderCount = useRenderCount();
|
||||||
shouldShow: state.shouldShowDescription,
|
|
||||||
description: state.description,
|
const shouldShow = useShopStore((state) => state.shouldShowDescription);
|
||||||
closeDescription: state.closeDescription,
|
const description = useShopStore((state) => state.description);
|
||||||
showDescription: state.showDescription,
|
const closeDescription = useShopStore((state) => state.closeDescription);
|
||||||
}));
|
const showDescription = useShopStore((state) => state.showDescription);
|
||||||
|
|
||||||
|
console.log("ShowJSON renders: ", renderCount)
|
||||||
|
|
||||||
const ref = useClickAway((e) => {
|
const ref = useClickAway((e) => {
|
||||||
if (e.type === "mousedown") // ignore touchstart
|
if (e.type === "mousedown") // ignore touchstart
|
||||||
|
|
Loading…
Reference in New Issue