Image.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import React, { memo } from "react";
  2. import { useUsers } from "../users";
  3. import styled from "styled-components";
  4. import eye from "../../images/eye.svg";
  5. const UnflippedFor = styled.div`
  6. position: absolute;
  7. top: -34px;
  8. right: 2px;
  9. color: #555;
  10. font-size: 0.6em;
  11. display: flex;
  12. justify-content: center;
  13. align-items: center;
  14. pointer-events: none;
  15. line-height: 1.5em;
  16. `;
  17. const UnflippedForUser = styled.img`
  18. background-color: ${({ color }) => color};
  19. border-radius: 2px;
  20. padding: 2px;
  21. margin: 2px;
  22. `;
  23. const Label = styled.div`
  24. position: absolute;
  25. top: 1px;
  26. right: 1px;
  27. padding: 0 3px;
  28. background-color: black;
  29. color: white;
  30. border-radius: 0.5em;
  31. opacity: 0.5;
  32. font-size: 0.6em;
  33. line-height: 1.5em;
  34. `;
  35. const Wrapper = styled.div.attrs(({ flippable }) => ({
  36. className: flippable ? "hvr-curl-top-right" : "",
  37. }))`
  38. user-select: none;
  39. position: relative;
  40. line-height: 0em;
  41. //filter: drop-shadow(2px 2px 3px #2225);
  42. `;
  43. const FrontImage = styled.img`
  44. transition: transform 200ms, opacity 200ms;
  45. transform: rotateY(${({ visible }) => (visible ? 0 : 180)}deg);
  46. -webkit-backface-visibility: hidden;
  47. backface-visibility: hidden;
  48. pointer-events: none;
  49. opacity: ${({ visible }) => (visible ? 1 : 0)};
  50. `;
  51. const BackImage = styled(FrontImage)`
  52. position: absolute;
  53. top: 0;
  54. left: 0;
  55. `;
  56. const OverlayImage = styled.img`
  57. pointer-events: none;
  58. position: absolute;
  59. top: 0;
  60. left: 0;
  61. `;
  62. // See https://stackoverflow.com/questions/3680429/click-through-div-to-underlying-elements
  63. // https://developer.mozilla.org/fr/docs/Web/CSS/pointer-events
  64. const Image = ({
  65. width,
  66. height,
  67. content = "/default.png",
  68. backContent,
  69. flipped = false,
  70. unflippedFor,
  71. text,
  72. backText,
  73. overlay,
  74. }) => {
  75. const { currentUser, users } = useUsers();
  76. const size = {};
  77. if (width) {
  78. size.width = width;
  79. }
  80. if (height) {
  81. size.height = height;
  82. }
  83. const unflippedForUsers = React.useMemo(() => {
  84. if (Array.isArray(unflippedFor)) {
  85. return unflippedFor
  86. .filter((userId) => users.find(({ uid }) => userId === uid))
  87. .map((userId) => users.find(({ uid }) => userId === uid));
  88. }
  89. return null;
  90. }, [unflippedFor, users]);
  91. const flippedForMe =
  92. backContent &&
  93. flipped &&
  94. (!Array.isArray(unflippedFor) || !unflippedFor.includes(currentUser.uid));
  95. const flippable = Boolean(backContent);
  96. return (
  97. <Wrapper flippable={flippable}>
  98. <UnflippedFor>
  99. {unflippedForUsers &&
  100. unflippedForUsers.map(({ color, id }) => {
  101. return <UnflippedForUser key={id} src={eye} color={color} />;
  102. })}
  103. </UnflippedFor>
  104. <FrontImage
  105. visible={!flippedForMe}
  106. src={content}
  107. alt=""
  108. draggable={false}
  109. {...size}
  110. />
  111. <BackImage
  112. visible={flippedForMe}
  113. src={backContent}
  114. alt=""
  115. draggable={false}
  116. {...size}
  117. />
  118. {overlay && overlay.content && (
  119. <OverlayImage
  120. src={overlay.content}
  121. draggable={false}
  122. alt=""
  123. {...size}
  124. />
  125. )}
  126. {flippedForMe && backText && <Label>{backText}</Label>}
  127. {(!flippedForMe || !backText) && text && <Label>{text}</Label>}
  128. </Wrapper>
  129. );
  130. };
  131. export default memo(Image);