Image.jsx 3.4 KB

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