react-image-pointer
Version:
A React component for creating interactive images with clickable points and information cards connected by lines. Enhanced with improved modal, better performance, and enhanced interactivity.
2 lines (1 loc) • 8.82 kB
JavaScript
import e,{useEffect as t,useState as r,useRef as i,useCallback as n}from"react";import{jsx as o,jsxs as l}from"react/jsx-runtime";var a={color:void 0,size:void 0,className:void 0,style:void 0,attr:void 0},s=e.createContext&&e.createContext(a),c=["attr","size","title"];function d(e,t){if(null==e)return{};var r,i,n=function(e,t){if(null==e)return{};var r={};for(var i in e)if(Object.prototype.hasOwnProperty.call(e,i)){if(t.indexOf(i)>=0)continue;r[i]=e[i]}return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i<o.length;i++)r=o[i],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}function u(){return u=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},u.apply(this,arguments)}function h(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,i)}return r}function f(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?h(Object(r),!0).forEach(function(t){m(e,t,r[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):h(Object(r)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))})}return e}function m(e,t,r){var i;return(t="symbol"==typeof(i=function(e,t){if("object"!=typeof e||!e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var i=r.call(e,t);if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(t,"string"))?i:i+"")in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function p(t){return t&&t.map((t,r)=>e.createElement(t.tag,f({key:r},t.attr),p(t.child)))}function g(t){return r=>e.createElement(v,u({attr:f({},t.attr)},r),p(t.child))}function v(t){var r=r=>{var i,{attr:n,size:o,title:l}=t,a=d(t,c),s=o||r.size||"1em";return r.className&&(i=r.className),t.className&&(i=(i?i+" ":"")+t.className),e.createElement("svg",u({stroke:"currentColor",fill:"currentColor",strokeWidth:"0"},r.attr,n,a,{className:i,style:f(f({color:t.color||r.color},r.style),t.style),height:s,width:s,xmlns:"http://www.w3.org/2000/svg"}),l&&e.createElement("title",null,l),t.children)};return void 0!==s?e.createElement(s.Consumer,null,e=>r(e)):r(a)}function b(e){return g({attr:{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},child:[{tag:"path",attr:{d:"m21 21-6-6m6 6v-4.8m0 4.8h-4.8"},child:[]},{tag:"path",attr:{d:"M3 16.2V21m0 0h4.8M3 21l6-6"},child:[]},{tag:"path",attr:{d:"M21 7.8V3m0 0h-4.8M21 3l-6 6"},child:[]},{tag:"path",attr:{d:"M3 7.8V3m0 0h4.8M3 3l6 6"},child:[]}]})(e)}function x(e){return g({attr:{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},child:[{tag:"path",attr:{d:"M15 3h6v6"},child:[]},{tag:"path",attr:{d:"M10 14 21 3"},child:[]},{tag:"path",attr:{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"},child:[]}]})(e)}function y(e){return g({attr:{viewBox:"0 0 24 24"},child:[{tag:"path",attr:{fill:"none",d:"M0 0h24v24H0z"},child:[]},{tag:"path",attr:{d:"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"},child:[]}]})(e)}const w=({src:e,onClose:r})=>(t(()=>{const e=e=>{"Escape"===e.key&&r()};return document.addEventListener("keydown",e),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",e),document.body.style.overflow="unset"}},[r]),o("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-75 backdrop-blur-sm",onClick:e=>e.target===e.currentTarget&&r(),children:l("div",{className:"relative max-w-[90vw] max-h-[90vh] overflow-hidden rounded-xl bg-black shadow-2xl",children:[o("button",{className:"absolute top-2 right-2 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-black bg-opacity-50 text-white hover:bg-opacity-75 transition-all duration-200",onClick:r,"aria-label":"Close modal",children:o(y,{size:20})}),o("img",{src:e,alt:"Full size",className:"block max-w-full max-h-full object-contain"})]})})),k=({src:e,maxHeight:a="600px",itemData:s,isDarkMode:c=!1})=>{const[d,u]=r({width:0,height:0}),[h,f]=r({}),[m,p]=r(!1),[g,v]=r(!1),[y,k]=r(""),j=i(null),O=i(null),N=i({}),C=n((e,t,r)=>{if(!t||!O.current||!j.current||0===r.width)return null;const i=t.getBoundingClientRect(),n=O.current.getBoundingClientRect(),o=j.current.getBoundingClientRect(),l=e.x/100*r.width+(o.left-n.left),a=e.y/100*r.height+(o.top-n.top);let c,d;d=i.top+i.height/2-n.top;return c=s.points.indexOf(e)%2==0?i.left-n.left:i.right-n.left,{start:{x:l,y:a},end:{x:c,y:d}}},[s.points]),z=n(()=>{if(d.width>0&&d.height>0&&Object.keys(N.current).length===s.points.length){const e={};s.points.forEach(t=>{if(void 0!==t.x&&void 0!==t.y){const r=N.current[t.id],i=C(t,r,d);null!==i&&(e[t.id]=i)}}),f(e)}},[d,s.points,C]);t(()=>{const e=j.current;if(e){const t=()=>{u({width:e.offsetWidth,height:e.offsetHeight})};return e.addEventListener("load",t),e.complete&&t(),()=>e.removeEventListener("load",t)}},[e]),t(()=>{z()},[z]),t(()=>{const e=()=>{if(j.current){const e={width:j.current.offsetWidth,height:j.current.offsetHeight};u(e)}};return window.addEventListener("resize",e),()=>window.removeEventListener("resize",e)},[]);const E=()=>{k(""),p(!1),v(!1)},P=e=>{k(e),v(!0)},$=(e,t)=>{const r=t%2==0?"rounded-r-md border-l-2 border-[#717171] pl-5":"rounded-l-md border-r-2 border-[#717171] pr-5";return l("div",{id:`info-div-${e.id}`,ref:t=>N.current[e.id]=t,className:`relative z-20 w-full md:w-[300px] ${r} p-2 shadow text-gray-400 text-start ${c?"bg-[rgba(255,255,255,0.1)]":"bg-[rgba(94,75,75,0.1)]"}`,children:[o("h3",{className:"text-lg font-semibold p-1 "+(c?"text-white":"text-gray-800"),children:e.info.title}),o("p",{className:"text-sm p-1.5 "+(c?"text-gray-400":"text-gray-600"),children:e.info.description}),e.link&&o("div",{className:"p-1.5 w-full flex justify-end",children:l("a",{target:"_blank",rel:"noopener noreferrer",href:e.link.url,className:"hover:text-white flex items-center gap-1 transition-colors duration-200 "+(c?"text-gray-300":"text-black"),children:[o(x,{size:18,color:"#717171"}),e.link.title]})}),e.image&&l("div",{className:"relative p-1.5 w-full flex justify-end items-baseline gap-1",children:[o(b,{size:18,color:"#717171",className:"cursor-pointer hover:scale-110 transition-transform duration-200",onClick:()=>P(e.image.src)}),o("img",{src:e.image.src,alt:e.image.alt,className:"rounded-md w-16 h-16 object-cover cursor-pointer hover:scale-105 transition-transform duration-200",onClick:()=>P(e.image.src)})]})]},`info-${e.id}`)};return l("div",{className:"relative flex flex-col items-start justify-center gap-5 font-sans",children:[l("div",{className:"flex flex-col gap-2",children:[o("p",{className:"w-fit py-2 px-4 rounded-xl font-semibold text-xl text-white "+(c?"dark:bg-gray-700":"bg-gray-700"),children:s.details.title}),s.details.description&&o("div",{className:"relative z-20 w-full rounded-l-md items-baseline p-2 text-gray-300 flex flex-row",children:o("p",{className:"px-1 font-light text-1xl "+(c?"text-gray-300":"text-gray-700"),children:s.details.description})})]}),l("div",{ref:O,className:"relative flex flex-col md:flex-row items-start justify-center",children:[o("div",{className:"flex flex-col items-end gap-5 order-2 md:order-1",children:s.points.map((e,t)=>t%2==0?null:$(e,t))}),l("div",{className:"relative flex-shrink-0 order-1 md:order-2",children:[o("img",{ref:j,src:e,alt:"Interactive image",style:{maxHeight:a,cursor:"pointer"},className:"block h-auto w-auto rounded-xl hover:shadow-lg transition-shadow duration-200",onClick:()=>{p(!0)}}),s.points.map(e=>void 0!==e.x&&void 0!==e.y&&o("div",{id:`point-${e.id}`,style:{left:`${e.x}%`,top:`${e.y}%`},className:"absolute z-30 h-3 w-3 border-2 border-white rounded-full bg-red-500 shadow-lg -translate-x-1/2 -translate-y-1/2 hover:scale-125 transition-transform duration-200"},e.id))]}),o("div",{className:"flex flex-col gap-5 order-3 mt-5 md:mt-0",children:s.points.map((e,t)=>t%2==0?$(e,t):null)}),s.points.map(e=>{const t=h[e.id];return t?o("svg",{style:{width:`${O.current?.offsetWidth||"100%"}`,height:`${O.current?.offsetHeight||"100%"}`},className:"pointer-events-none absolute left-0 top-0 z-0",children:o("line",{x1:`${t.start.x}`,y1:`${t.start.y}`,x2:`${t.end.x}`,y2:`${t.end.y}`,stroke:"#eae8e8",strokeWidth:"1.5"})},`line-${e.id}`):null})]}),m&&o(w,{src:e,onClose:E}),g&&o(w,{src:y,onClose:E})]})};export{k as ImagePointer,w as Modal};