Zone.jsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import React from "react";
  2. import { memo } from "react";
  3. import styled, { css } from "styled-components";
  4. import { useItemInteraction } from "react-sync-board";
  5. import { opacify } from "color2k";
  6. import { isItemInsideElement, getItemElement } from "../utils";
  7. import useGameItemActions from "./useGameItemActions";
  8. const ZoneWrapper = styled.div`
  9. ${({
  10. width = 200,
  11. height = 200,
  12. borderColor = "#cccccc33",
  13. borderStyle = "dotted",
  14. backgroundColor = "transparent",
  15. }) => css`
  16. width: ${width}px;
  17. height: ${height}px;
  18. border: 0.5em ${borderStyle} ${borderColor};
  19. background-color: ${backgroundColor};
  20. border-radius: 5px;
  21. position: relative;
  22. & .zone__label {
  23. font-size: 1.5em;
  24. user-select: none;
  25. background-color: ${opacify(borderColor, 1)};
  26. position: absolute;
  27. border-radius: 0.5em;
  28. color: var(--color-darkGrey);
  29. }
  30. & .zone__label.left {
  31. padding: 1em 0em;
  32. top: 1em;
  33. left: -1em;
  34. letter-spacing: -3px;
  35. writing-mode: vertical-rl;
  36. text-orientation: upright;
  37. }
  38. & .zone__label.top {
  39. padding: 0em 1em;
  40. top: -1em;
  41. left: 1em;
  42. }
  43. `}
  44. `;
  45. const Zone = ({
  46. width,
  47. height,
  48. label,
  49. onItem,
  50. borderColor,
  51. borderStyle,
  52. backgroundColor,
  53. labelPosition = "left",
  54. }) => {
  55. const { register } = useItemInteraction("place");
  56. const zoneRef = React.useRef(null);
  57. const { actionMap } = useGameItemActions();
  58. const onInsideItem = React.useCallback(
  59. (itemIds) => {
  60. const insideItems = itemIds.filter((itemId) =>
  61. isItemInsideElement(getItemElement(itemId), zoneRef.current)
  62. );
  63. if (!insideItems.length) return;
  64. const onItemActions = onItem.map((action) => {
  65. if (typeof action === "string") {
  66. return { name: action };
  67. }
  68. return action;
  69. });
  70. onItemActions.forEach(({ name, args }) => {
  71. switch (name) {
  72. case "reveal":
  73. actionMap["reveal"].action(args)(insideItems);
  74. break;
  75. case "hide":
  76. actionMap["hide"].action(args)(insideItems);
  77. break;
  78. case "revealSelf":
  79. actionMap["revealSelf"].action(args)(insideItems);
  80. break;
  81. case "hideSelf":
  82. actionMap["hideSelf"].action(args)(insideItems);
  83. break;
  84. case "stack":
  85. actionMap["stack"].action(args)(insideItems);
  86. break;
  87. }
  88. });
  89. },
  90. [actionMap, onItem]
  91. );
  92. React.useEffect(() => {
  93. const unregisterList = [];
  94. if (onItem?.length) {
  95. unregisterList.push(register(onInsideItem));
  96. }
  97. return () => {
  98. unregisterList.forEach((callback) => callback());
  99. };
  100. }, [onInsideItem, onItem, register]);
  101. return (
  102. <ZoneWrapper
  103. width={width}
  104. height={height}
  105. ref={zoneRef}
  106. borderStyle={borderStyle}
  107. borderColor={borderColor}
  108. backgroundColor={backgroundColor}
  109. >
  110. <div className={`zone__label ${labelPosition}`}>{label}</div>
  111. </ZoneWrapper>
  112. );
  113. };
  114. export default memo(Zone);