Image.js 3.0 KB

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