123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- import React, { memo } from "react";
- import styled, { css } from "styled-components";
- import { useItemActions, useItemInteraction, useWire } from "react-sync-board";
- import { opacify } from "color2k";
- import { media2Url } from "../mediaLibrary";
- import { isItemInsideElement, getItemElement } from "../utils";
- const BagWrapper = styled.div`
- ${({
- width,
- height,
- labelBackgroundColor = "#cccccc33",
- }) => css`
- width: ${width}px;
- height: ${height}px;
- position: relative;
- & .bag__label {
- font-size: 1.5em;
- user-select: none;
- background-color: ${opacify(labelBackgroundColor, 1)};
- position: absolute;
- border-radius: 0.5em;
- color: var(--color-darkGrey);
- }
- & .bag__label.left {
- padding: 1em 0em;
- top: 1em;
- left: -1em;
- letter-spacing: -3px;
- writing-mode: vertical-rl;
- text-orientation: upright;
- }
- & .bag__label.top {
- padding: 0em 1em;
- top: -1em;
- left: 1em;
- }
- `}
- `;
- const BagImage = styled.img`
- transition: opacity 300ms;
- pointer-events: none;
- display: ${({ visible }) => (visible ? 'block' : 'none')};
- `;
- const ItemCounter = styled.div`
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- font-size: ${({ counterSize }) => `${counterSize}px`}
- `;
- const Bag = ({
- families,
- storedItems = [],
- label,
- width,
- height,
- bagImage = "/bag.png",
- emptyBagImage = "/bag_empty.png",
- labelPosition = "left",
- countersize = 50,
- countervisible = true,
- labelBackgroundColor = "#cccccc33",
- id,
- setState,
- }) => {
- const { isMaster } = useWire("board");
- const { register } = useItemInteraction("place");
- const {
- pushItem,
- getItems,
- batchUpdateItems,
- removeItems,
- } = useItemActions();
- const bagRef = React.useRef(null);
- const noFamilies = !Array.isArray(families) || families.length === 0;
- const imageContent = media2Url(bagImage) || "/bag.png";
- const emptyImageContent = media2Url(emptyBagImage) || "/bag_empty.png";
- const size = {};
- if (width) {
- size.width = width;
- }
- if (height) {
- size.height = height;
- }
- const onInsideItem = React.useCallback(
- async (itemIds) => {
- const items = await getItems(itemIds);
- const insideItems = items
- .filter((item) => noFamilies || families.includes(item.groupId))
- .filter(({ id: itemId }) =>
- // Skip the container itself.
- itemId !== id &&
- isItemInsideElement(getItemElement(itemId), bagRef.current)
- )
- .map(({ id }) => id);
- if (!insideItems.length) return;
- const newItems = await getItems(insideItems);
- await removeItems(newItems.map(item => item.id));
- storedItems = newItems.concat(storedItems);
- setState((prevState) => ({
- ...prevState,
- storedItems: storedItems,
- }));
- }, [families, storedItems, id, setState, getItems, removeItems]
- );
- const onExtract = React.useCallback(
- async () => {
- const [bag] = await getItems([id]);
- storedItems = [...storedItems];
- const item = storedItems.shift();
- if (item) {
- setState((prevState) => ({
- ...prevState,
- storedItems: storedItems,
- }));
- await pushItem({
- ...item,
- x: bag.x + bagRef.current.clientWidth / 3.0,
- y: bag.y + bagRef.current.clientHeight / 3.0,
- });
- }
- }, [id, bagRef, storedItems, setState, pushItem]
- );
- const extract = (e) => {
- e.stopPropagation();
- onExtract();
- }
- React.useEffect(() => {
- const unregisterList = [];
- unregisterList.push(register(onInsideItem));
- return () => {
- unregisterList.forEach((callback) => callback());
- };
- }, [onInsideItem, register]);
- return (
- <BagWrapper onDoubleClick={extract} ref={bagRef}
- labelBackgroundColor={labelBackgroundColor}
- width={width}
- height={height}
- >
- {countervisible && (<ItemCounter counterSize={countersize} >{storedItems.length}</ItemCounter>)}
- <BagImage
- visible={storedItems.length}
- src={imageContent}
- alt=""
- draggable={false}
- {...size}
- />
- <BagImage
- visible={!storedItems.length}
- src={emptyImageContent}
- alt=""
- draggable={false}
- {...size}
- />
- <div className={`bag__label ${labelPosition}`}>{label}</div>
- </BagWrapper>
- );
- };
- export default memo(Bag);
|