ftr-timetable
Version:
A versatile timetable component for React
391 lines (349 loc) • 18.9 kB
JavaScript
(function(x,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("react/jsx-runtime"),require("react"),require("date-fns"),require("styled-components")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","date-fns","styled-components"],t):(x=typeof globalThis<"u"?globalThis:x||self,t(x["ftr-timetable"]={},x["react/jsx-runtime"],x.React,x.dateFns,x.styledComponents))})(this,function(x,t,h,m,p){"use strict";const v=h.createContext(void 0),L=({children:e,...o})=>t.jsx(v.Provider,{value:{...o,ref:h.createRef()},children:e}),g=()=>{const e=h.useContext(v);if(e===void 0)throw new Error("useTimeTableContext must be used within the context of TimeTableContext");return e},P=p.div`
position: absolute;
box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.2);
`,T=h.memo(function({date:e}){const{ref:o,displayStyle:a,showTimeMarker:l,startingHour:d,styles:s}=g(),n=h.useMemo(()=>{const i=m.format(new Date(e),"yyyy-MM-dd"),r=m.format(m.subHours(new Date,d),"yyyy-MM-dd");if(i!==r)return-1;const c=m.addHours(m.startOfDay(new Date(e)),d),f=new Date,b=m.differenceInMinutes(f,c);return Math.max(b,0)},[e,d]);return h.useEffect(()=>{var r,c;if(typeof document>"u"||!o.current)return;const i=Math.max(n-180,0);(c=(r=o.current).scrollTo)==null||c.call(r,{top:a==="vertical"?i:0,left:a==="horizontal"?i:0,behavior:"smooth"})},[n,a,o]),n<1||!l?null:t.jsx(P,{style:{backgroundColor:s.timeMarkerColor||"#666",width:a==="horizontal"?"2px":"80%",height:a==="horizontal"?"33.333333%":"2px",left:a==="horizontal"?`${n}px`:0,top:a==="horizontal"?0:`${n}px`}})}),q=p.div`
position: absolute;
width: 100%;
font-size: 0.875rem;
line-height: 1.25rem;
padding: 0 0.5rem;
`,E=p.div`
position: absolute;
height: 100%;
font-size: 0.875rem;
line-height: 1.25rem;
padding: 1px 0;
`,k=p.div`
position: relative;
height: 100%;
padding: 1px;
`,D=p.div`
display: flex;
position: relative;
height: 100%;
color: ${e=>e.$styles.itemTextColor||"inherit"};
background: ${e=>e.$styles.itemBackgroundColor||"#304151"};
cursor: pointer;
border-radius: 5px;
box-shadow: 0 2px 5px -4px rgba(0, 0, 0, 0.4);
&:hover {
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.1);
background: ${e=>e.$styles.itemHoverBackgroundColor||e.$styles.itemBackgroundColor||"#374151"};
}
&.ftr-timetable-item__vertical {
padding: 0.25rem;
flex-direction: column;
.ftr-timetable-item__inner {
top: 62px;
}
}
&.ftr-timetable-item__horizontal {
padding: 0.125rem 0.25rem;
flex-direction: row;
.ftr-timetable-item__inner {
left: 164px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
}
&.item-cancelled {
opacity: 0.5;
.ftr-timetable-item__inner {
text-decoration: line-through;
}
}
.ftr-timetable-item__inner {
position: sticky;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
.ftr-timetable-item__info {
font-size: 10px;
font-weight: normal;
opacity: 0.9;
}
}
`,H=h.memo(function({item:e,eventStartOffset:o,eventSize:a,intersections:l,offset:d}){const{onItemClick:s,renderItem:n,styles:i}=g();return t.jsx(q,{title:e.name,style:{top:`${o}px`,height:`${a}px`,left:`calc(100% / ${l+1} * ${d})`,maxWidth:`calc(100% / ${l+1})`},children:t.jsx(k,{onClick:()=>s==null?void 0:s(e),children:n?n(e):t.jsx(D,{$styles:i,className:"ftr-timetable-item ftr-timetable-item__vertical",style:e.style,children:t.jsxs("div",{className:"ftr-timetable-item__inner",children:[t.jsx("div",{children:e.name}),e.info&&t.jsx("div",{className:"ftr-timetable-item__info",children:e.info})]})})})})}),V=h.memo(function({item:e,eventStartOffset:o,eventSize:a,intersections:l,offset:d}){const{onItemClick:s,renderItem:n,styles:i}=g();return t.jsx(E,{title:e.name,style:{left:`${o}px`,width:`${a}px`,top:`calc(100% / ${l+1} * ${d})`,maxHeight:`calc(100% / ${l+1})`},children:t.jsx(k,{onClick:()=>s==null?void 0:s(e),children:n?n(e):t.jsx(D,{$styles:i,className:`ftr-timetable-item ftr-timetable-item__horizontal${e.cancelled?" item-cancelled":""}${e.className?` ${e.className}`:""}`,style:e.style,children:t.jsxs("div",{className:"ftr-timetable-item__inner",children:[t.jsx("div",{children:e.name}),e.info&&t.jsx("div",{className:"ftr-timetable-item__info",children:e.info})]})})})})}),C=h.memo(function({item:e,intersections:o,offset:a}){const{startingHour:l,numberOfHours:d,selectedDate:s,displayStyle:n}=g(),i=m.differenceInDays(new Date(e.startDate),new Date(s)),r=m.subDays(m.addHours(m.startOfDay(new Date(e.startDate)),l),i),c=new Date(Math.max(new Date(e.startDate).getTime(),new Date(r).getTime())),f=m.differenceInMinutes(c,r),b=m.differenceInMinutes(new Date(e.endDate),c),w=Math.min(b,d*60-f);return n==="vertical"?t.jsx(H,{item:e,intersections:o,offset:a,eventStartOffset:f,eventSize:w}):t.jsx(V,{item:e,intersections:o,offset:a,eventStartOffset:f,eventSize:w})}),z=e=>h.useMemo(()=>e.reduce((l,d,s)=>{const n=new Date(d.startDate).getTime(),i=new Date(d.endDate).getTime();let r=0;const c=l.filter(f=>n>=f.start&&n<f.end||i>=f.start&&i<f.end);for(const f of c){if(f.offset>0){r--;break}if(f.intersections===0){f.intersections+=1;continue}for(const b of l.filter(w=>f.with.includes(w.index)))(n>=b.start&&n<b.end||i>=b.start&&i<b.end)&&(f.intersections+=1)}return[...l,{item:d,start:n,end:i,offset:c.length+r,intersections:c.length,index:s,with:c.map(f=>f.index)}]},[]),[e]),O=p.div`
&::-webkit-scrollbar {
width: 0px;
height: 8px;
background: #1f2937;
}
&::-webkit-scrollbar-thumb {
background: #555;
border-radius: 50%;
border: solid 2px #1f2937;
}
&::-webkit-scrollbar-thumb:hover {
background: hsl(from #888 h s calc(l - 5));
}
position: relative;
overflow: auto;
width: 100%;
max-height: 100%;
max-width: 100vw;
box-sizing: border-box !important;
* {
box-sizing: border-box !important;
}
`,Y=p.div`
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
color: ${e=>e.$styles.dateTextColor||"inherit"};
background: ${e=>e.$styles.backgroundColor||"#1f2937"};
.ftr-timetable-datetime {
display: flex;
flex-direction: row;
position: sticky;
top: 0;
z-index: 3;
background: ${e=>e.$styles.dateBackgroundColor||"#1f2937"};
width: 100%;
height: 44px;
&__date {
position: sticky;
top: 0;
left: 0;
z-index: 2;
background: ${e=>e.$styles.datePickerBackgroundColor||e.$styles.dateBackgroundColor||"#1f2937"};
display: flex;
justify-content: space-between;
width: 10rem;
height: 100%;
flex-shrink: 0;
flex-direction: row;
}
&__select {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
height: 100%;
padding-left: 0.5rem;
border-right: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
border-bottom: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
select {
background: transparent
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='${e=>{var o;return((o=e.$styles.dateTextColor)==null?void 0:o.replace("#","%23"))||"%23fff"}}'><polygon points='0,0 100,0 50,50'/></svg>")
no-repeat calc(100% - 10px) calc(50% + 3px);
background-size: 10px;
color: inherit;
font-family: inherit;
border: none;
outline: none;
font-size: 0.875rem;
line-height: 1.25rem;
width: 100%;
-webkit-appearance: none;
appearance: none;
}
}
&__hours {
display: flex;
flex-direction: row;
position: relative;
border-bottom: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
width: 100%;
height: 100%;
}
&__hour {
display: flex;
flex-direction: column;
justify-content: flex-end;
width: 60px;
padding-left: 0.25rem;
padding-bottom: 0.25rem;
font-size: 0.75rem;
height: 100%;
&:not(:first-child) {
border-left: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
}
}
}
`,A=p.div`
display: flex;
flex-direction: row;
// background-color: ${e=>e.$styles.backgroundColor||"#1f2937"};
height: 60px;
.ftr-timetable-location {
height: 100%;
width: 10rem;
z-index: 2;
position: sticky;
top: 0;
left: 0;
color: ${e=>e.$styles.locationTextColor||"inherit"};
background: ${e=>e.$styles.locationBackgroundColor||"#000"};
&__inner {
display: flex;
align-items: center;
height: 100%;
padding: 0 0.5rem;
font-size: 0.875rem;
line-height: 1.25rem;
border-right: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
border-bottom: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
}
&__name {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
}
.ftr-timetable-location-items {
flex: 1;
position: relative;
height: 100%;
border-bottom: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
// background-color: ${e=>e.$styles.backgroundColor||"#1f2937"};
}
`,G=h.memo(function({location:e}){const{items:o,onLocationClick:a,renderLocation:l,styles:d}=g(),s=o.filter(i=>i.locationId===e.id),n=z(s);return t.jsxs(A,{$styles:d,children:[t.jsx("div",{className:"ftr-timetable-location ftr-timetable-location__horizontal","data-testid":`timetable-location-${e.id}`,title:e.name,onClick:()=>a==null?void 0:a(e),style:e.style,children:l?l(e):t.jsx("div",{className:"ftr-timetable-location__inner",children:t.jsx("div",{className:"ftr-timetable-location__name",children:e.name})})}),t.jsx("div",{className:"ftr-timetable-location-items",children:n.map((i,r)=>t.jsx(C,{item:i.item,intersections:i.intersections,offset:i.offset},`tt_${i.item.id}_${r}`))})]})}),J=({dateChange:e,locations:o,dates:a,hours:l,selectedDate:d})=>{const{ref:s,dateFormat:n,styles:i}=g();return t.jsx(O,{ref:s,"data-testid":"timetable-horizontal",className:"ftr-timetable ftr-timetable__horizontal",style:{height:`${o.length*60+52}px`},children:t.jsxs(Y,{$styles:i,style:{minWidth:`${l.length*60+160}px`},children:[t.jsxs("div",{className:"ftr-timetable-datetime",children:[t.jsx("div",{className:"ftr-timetable-datetime__date",children:t.jsx("div",{className:"ftr-timetable-datetime__select",children:t.jsx("select",{value:d,onChange:r=>e(r.target.value),children:a.map(r=>t.jsx("option",{value:r,children:m.format(new Date(r),n)},r))})})}),t.jsxs("div",{className:"ftr-timetable-datetime__hours",children:[l.map((r,c)=>t.jsx("div",{className:"ftr-timetable-datetime__hour",children:r.display},`hour_${c}`)),t.jsx(T,{date:d})]})]}),o.map((r,c)=>t.jsx(G,{location:r},`location_${c}`))]})})},K=p.div`
&::-webkit-scrollbar {
width: 0px;
height: 8px;
background: #1f2937;
}
&::-webkit-scrollbar-thumb {
background: #555;
border-radius: 50%;
border: solid 2px #1f2937;
}
&::-webkit-scrollbar-thumb:hover {
background: hsl(from #888 h s calc(l - 5));
}
height: 100%;
max-width: 100vw;
position: relative;
overflow: auto;
box-sizing: border-box !important;
* {
box-sizing: border-box !important;
}
`,Q=p.div`
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
transform: translateY(-1px);
color: ${e=>e.$styles.textColor||"#fff"};
.ftr-timetable-datetime {
display: flex;
flex-direction: row;
width: 100%;
&__container {
width: 8rem;
color: ${e=>e.$styles.dateTextColor||"inherit"};
background: ${e=>e.$styles.dateBackgroundColor||"#1f2937"};
position: sticky;
left: 0;
top: 0;
z-index: 3;
}
&__date {
width: 100%;
height: 60px;
background: ${e=>e.$styles.datePickerBackgroundColor||e.$styles.dateBackgroundColor||"#1f2937"};
position: sticky;
top: 0;
z-index: 2;
}
&__select {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
height: 100%;
padding-left: 0.5rem;
border-right: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
border-bottom: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
select {
width: 100%;
background: transparent
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='${e=>{var o;return((o=e.$styles.dateTextColor)==null?void 0:o.replace("#","%23"))||"%23fff"}}'><polygon points='0,0 100,0 50,50'/></svg>")
no-repeat calc(100% - 10px) calc(50% + 3px);
background-size: 10px;
color: inherit;
font-family: inherit;
border: none;
outline: none;
font-size: 0.875rem;
line-height: 1.25rem;
-webkit-appearance: none;
appearance: none;
}
}
&__hours {
position: relative;
z-index: 1;
border-right: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
}
&__hour {
display: flex;
justify-content: flex-end;
height: 60px;
padding-top: 0.25rem;
padding-right: 0.5rem;
position: relative;
z-index: 2;
font-size: 0.75rem;
line-height: 1rem;
&:not(:first-child) {
border-top: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
}
}
}
.ftr-timetable-locations {
display: flex;
flex-direction: row;
flex: 1;
position: relative;
}
`,U=p.div`
display: flex;
flex: 1;
flex-direction: column;
.ftr-timetable-location {
height: 60px;
position: sticky;
top: 0;
z-index: 2;
color: ${e=>e.$styles.locationTextColor||"inherit"};
background: ${e=>e.$styles.locationBackgroundColor||"#000"};
&__inner {
display: flex;
flex-direction: row;
align-items: center;
padding: 0 0.5rem;
font-size: 0.875rem;
line-height: 1.25rem;
height: 100%;
border-right: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
border-bottom: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
}
&__name {
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
}
.ftr-timetable-location-items {
display: flex;
flex: 1;
flex-direction: column;
position: relative;
background: ${e=>e.$styles.backgroundColor||"#1f2937"};
border-right: ${e=>e.$styles.borderStyle||"solid 2px #374151"};
}
`,X=h.memo(function({location:e}){const{items:o,onLocationClick:a,renderLocation:l,styles:d}=g(),s=o.filter(i=>i.locationId===e.id),n=z(s);return t.jsxs(U,{$styles:d,children:[t.jsx("div",{className:"ftr-timetable-location ftr-timetable-location__vertical","data-testid":`timetable-location-${e.id}`,title:e.name,onClick:()=>a==null?void 0:a(e),style:e.style,children:l?l(e):t.jsx("div",{className:"ftr-timetable-location__inner",children:t.jsx("div",{className:"ftr-timetable-location__name",children:e.name})})}),t.jsx("div",{className:"ftr-timetable-location-items",children:n.map((i,r)=>t.jsx(C,{item:i.item,intersections:i.intersections,offset:i.offset},`tt_${i.item.id}_${r}`))})]})}),Z=({dateChange:e,locations:o,dates:a,hours:l,selectedDate:d})=>{const{ref:s,dateFormat:n,styles:i}=g();return t.jsx(K,{ref:s,"data-testid":"timetable-vertical",className:"ftr-timetable ftr-timetable-vertical",children:t.jsx(Q,{$styles:i,children:t.jsxs("div",{className:"ftr-timetable-datetime",style:{minWidth:`${o.length*200}px`},children:[t.jsxs("div",{className:"ftr-timetable-datetime__container",children:[t.jsx("div",{className:"ftr-timetable-datetime__date",children:t.jsx("div",{className:"ftr-timetable-datetime__select",children:t.jsx("select",{value:d,onChange:r=>e(r.target.value),children:a.map(r=>t.jsx("option",{value:r,children:m.format(new Date(r),n)},r))})})}),t.jsxs("div",{className:"ftr-timetable-datetime__hours",children:[l.map((r,c)=>t.jsx("div",{className:"ftr-timetable-datetime__hour",children:r.display},`hour_${c}`)),t.jsx(T,{date:d})]})]}),t.jsx("div",{className:"ftr-timetable-locations",children:o.map((r,c)=>t.jsx(X,{location:r},`location_${c}`))})]})})})},F=(e,o,a,l)=>h.useMemo(()=>{const s=[];if(!(e!=null&&e.length)||!l)return s;const n=m.addHours(m.startOfDay(new Date(l)),o),i=m.addHours(n,a);for(const r of e)(new Date(r.startDate)>=n&&new Date(r.startDate)<i||new Date(r.endDate)>n&&new Date(r.endDate)<=i)&&s.push(r);return s.sort((r,c)=>new Date(r.startDate).getTime()-new Date(c.startDate).getTime())},[l,o,a,e]),R=(e,o)=>h.useMemo(()=>{if(o)return o;const l=e.flatMap(r=>{const c=[];return r.startDate&&c.push(new Date(r.startDate).getTime()),r.endDate&&c.push(new Date(r.endDate).getTime()),c}).sort();if(!l.length)return[];const d=l[0],s=l[l.length-1],n=m.differenceInDays(s,d),i=[];for(let r=0;r<=n;r++){const c=m.format(m.addDays(new Date(d),r),"yyyy-MM-dd");i.push(c)}return i},[e,o]),ee=(e,o)=>h.useMemo(()=>{var d;let l=[];for(const s of e)if((d=s.items)!=null&&d.length)for(const n of s.items.filter(i=>{const r=new Date(i.startDate),c=new Date(i.endDate);return isNaN(r.getTime())||isNaN(c.getTime())?(console.error("Invalid startDate or endDate format",i),!1):!0}))l.push({...n,locationId:s.id});return o!=null&&o.length&&(l=l.concat(o.filter(s=>{const n=new Date(s.startDate),i=new Date(s.endDate);return isNaN(n.getTime())||isNaN(i.getTime())?(console.error("Invalid startDate or endDate format",s),!1):!0}))),l},[o,e]),te=({locations:e,items:o,dates:a,dateFormat:l="eee dd MMMM",onItemClick:d,onLocationClick:s,onDateChange:n,variant:i,renderItem:r,renderLocation:c,startingHour:f=6,numberOfHours:b=24,showTimeMarker:w=!0,styles:N})=>{const re=h.useMemo(()=>({backgroundColor:"#1f2937",dateBackgroundColor:"#1f2937",textColor:"#fff",borderStyle:"solid 2px #374151",...N}),[N]),[y,M]=h.useState(),I=ee(e,o),ie=F(I,f,b,y),_=R(I,a),S=h.useMemo(()=>i||(e.length>1?"horizontal":"vertical"),[i,e]),B=h.useMemo(()=>{const u=[];for(let $=f;$<f+b;$++){const W=$<24?$:$-24;u.push({hour:$,display:`${W<10?"0":""}${W}:00`})}return u},[b,f]);h.useEffect(()=>{M(u=>u||_[0])},[_]),h.useEffect(()=>{y&&(n==null||n(y))},[n,y]);const j=h.useCallback(u=>{M(u)},[]);return y?t.jsx(L,{startingHour:f,numberOfHours:b,onItemClick:d,onLocationClick:s,displayStyle:S,renderItem:r,renderLocation:c,dateFormat:l,selectedDate:y,showTimeMarker:w,items:ie,styles:re,children:S==="horizontal"?t.jsx(J,{dateChange:j,hours:B,locations:e,dates:_,selectedDate:y}):t.jsx(Z,{dateChange:j,hours:B,locations:e,dates:_,selectedDate:y})}):t.jsx(t.Fragment,{})};x.TimeTable=te,Object.defineProperty(x,Symbol.toStringTag,{value:"Module"})});
//# sourceMappingURL=index.umd.js.map