leetcode-heatmap-track
Version:
A lightweight React library that visualizes LeetCode user activity using a GitHub-style contribution heatmap. It fetches real submission data, supports hover tooltips, and includes a utility to access raw LeetCode statistics—perfect for portfolios and das
3 lines (2 loc) • 14.2 kB
JavaScript
import e,{useState as t,useEffect as n}from"react";function a(e,t,n){return t&&function(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}(e.prototype,t),e}function r(){return r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(e[a]=n[a])}return e},r.apply(this,arguments)}function o(e){return o=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},o(e)}function l(e,t){return l=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},l(e,t)}function i(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function s(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if(!(Symbol.iterator in Object(e))&&"[object Arguments]"!==Object.prototype.toString.call(e))return;var n=[],a=!0,r=!1,o=void 0;try{for(var l,i=e[Symbol.iterator]();!(a=(l=i.next()).done)&&(n.push(l.value),!t||n.length!==t);a=!0);}catch(e){r=!0,o=e}finally{try{a||null==i.return||i.return()}finally{if(r)throw o}}return n}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function u(){}function c(){}c.resetWarningCache=u;var h,p,f=(h=function(e){e.exports=function(){function e(e,t,n,a,r,o){if("SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"!==o){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:c,resetWarningCache:u};return n.PropTypes=n,n}()},h(p={exports:{}},p.exports),p.exports);function y(e,t){if(e.length!==t.length)return!1;for(var n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}var d=864e5;function g(e,t){var n=new Date(e);return n.setDate(n.getDate()+t),n}function m(e){return e instanceof Date?e:new Date(e)}function v(e){for(var t=[],n=0;n<e;n+=1)t.push(n);return t}var b=10,k="react-calendar-heatmap-",D=function(){function t(){var e,n,a,r,l,s,u;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);for(var c=arguments.length,h=new Array(c),p=0;p<c;p++)h[p]=arguments[p];return a=this,r=(e=o(t)).call.apply(e,[this].concat(h)),n=!r||"object"!=typeof r&&"function"!=typeof r?i(a):r,l=i(n),s="getValueCache",u=function(e,t){var n;void 0===t&&(t=y);var a,r=[],o=!1;return function(){for(var l=[],i=0;i<arguments.length;i++)l[i]=arguments[i];return o&&n===this&&t(l,r)||(a=e.apply(this,l),o=!0,n=this,r=l),a}}(function(e){return e.values.reduce(function(e,t){var a=m(t.date);return e[Math.floor((a-n.getStartDateWithEmptyDays())/d)]={value:t,className:n.props.classForValue(t),title:n.props.titleForValue?n.props.titleForValue(t):null,tooltipDataAttrs:n.getTooltipDataAttrsForValue(t)},e},{})}),s in l?Object.defineProperty(l,s,{value:u,enumerable:!0,configurable:!0,writable:!0}):l[s]=u,n}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&l(e,t)}(t,e.Component),a(t,[{key:"getDateDifferenceInDays",value:function(){var e=this.props,t=e.startDate,n=e.numDays;if(n)return console.warn("numDays is a deprecated prop. It will be removed in the next release. Consider using the startDate prop instead."),n;var a=this.getEndDate()-m(t);return Math.ceil(a/d)}},{key:"getSquareSizeWithGutter",value:function(){return b+this.props.gutterSize}},{key:"getMonthLabelSize",value:function(){return this.props.showMonthLabels?this.props.horizontal?14:28:0}},{key:"getWeekdayLabelSize",value:function(){return this.props.showWeekdayLabels?this.props.horizontal?30:15:0}},{key:"getStartDate",value:function(){return g(this.getEndDate(),1-this.getDateDifferenceInDays())}},{key:"getEndDate",value:function(){return e=m(this.props.endDate),new Date(e.getFullYear(),e.getMonth(),e.getDate());var e}},{key:"getStartDateWithEmptyDays",value:function(){return g(this.getStartDate(),-this.getNumEmptyDaysAtStart())}},{key:"getNumEmptyDaysAtStart",value:function(){return this.getStartDate().getDay()}},{key:"getNumEmptyDaysAtEnd",value:function(){return 6-this.getEndDate().getDay()}},{key:"getWeekCount",value:function(){var e=this.getDateDifferenceInDays()+this.getNumEmptyDaysAtStart()+this.getNumEmptyDaysAtEnd();return Math.ceil(e/7)}},{key:"getWeekWidth",value:function(){return 7*this.getSquareSizeWithGutter()}},{key:"getWidth",value:function(){return this.getWeekCount()*this.getSquareSizeWithGutter()-(this.props.gutterSize-this.getWeekdayLabelSize())}},{key:"getHeight",value:function(){return this.getWeekWidth()+(this.getMonthLabelSize()-this.props.gutterSize)+this.getWeekdayLabelSize()}},{key:"getValueForIndex",value:function(e){return this.valueCache[e]?this.valueCache[e].value:null}},{key:"getClassNameForIndex",value:function(e){return this.valueCache[e]?this.valueCache[e].className:this.props.classForValue(null)}},{key:"getTitleForIndex",value:function(e){return this.valueCache[e]?this.valueCache[e].title:this.props.titleForValue?this.props.titleForValue(null):null}},{key:"getTooltipDataAttrsForIndex",value:function(e){return this.valueCache[e]?this.valueCache[e].tooltipDataAttrs:this.getTooltipDataAttrsForValue({date:null,count:null})}},{key:"getTooltipDataAttrsForValue",value:function(e){var t=this.props.tooltipDataAttrs;return"function"==typeof t?t(e):t}},{key:"getTransformForWeek",value:function(e){return this.props.horizontal?"translate(".concat(e*this.getSquareSizeWithGutter(),", 0)"):"translate(0, ".concat(e*this.getSquareSizeWithGutter(),")")}},{key:"getTransformForWeekdayLabels",value:function(){return this.props.horizontal?"translate(".concat(b,", ").concat(this.getMonthLabelSize(),")"):null}},{key:"getTransformForMonthLabels",value:function(){return this.props.horizontal?"translate(".concat(this.getWeekdayLabelSize(),", 0)"):"translate(".concat(this.getWeekWidth()+4,", ").concat(this.getWeekdayLabelSize(),")")}},{key:"getTransformForAllWeeks",value:function(){return this.props.horizontal?"translate(".concat(this.getWeekdayLabelSize(),", ").concat(this.getMonthLabelSize(),")"):"translate(0, ".concat(this.getWeekdayLabelSize(),")")}},{key:"getViewBox",value:function(){return this.props.horizontal?"0 0 ".concat(this.getWidth()," ").concat(this.getHeight()):"0 0 ".concat(this.getHeight()," ").concat(this.getWidth())}},{key:"getSquareCoordinates",value:function(e){return this.props.horizontal?[0,e*this.getSquareSizeWithGutter()]:[e*this.getSquareSizeWithGutter(),0]}},{key:"getWeekdayLabelCoordinates",value:function(e){return this.props.horizontal?[0,(e+1)*b+e*this.props.gutterSize]:[e*b+e*this.props.gutterSize,b]}},{key:"getMonthLabelCoordinates",value:function(e){if(this.props.horizontal)return[e*this.getSquareSizeWithGutter(),this.getMonthLabelSize()-4];return[0,(e+1)*this.getSquareSizeWithGutter()-2]}},{key:"handleClick",value:function(e){this.props.onClick&&this.props.onClick(e)}},{key:"handleMouseOver",value:function(e,t){this.props.onMouseOver&&this.props.onMouseOver(e,t)}},{key:"handleMouseLeave",value:function(e,t){this.props.onMouseLeave&&this.props.onMouseLeave(e,t)}},{key:"renderSquare",value:function(t,n){var a=this;if((n<this.getNumEmptyDaysAtStart()||n>=this.getNumEmptyDaysAtStart()+this.getDateDifferenceInDays())&&!this.props.showOutOfRangeDays)return null;var o=s(this.getSquareCoordinates(t),2),l=o[0],i=o[1],u=this.getValueForIndex(n),c=e.createElement("rect",r({key:n,width:b,height:b,x:l,y:i,className:this.getClassNameForIndex(n),onClick:function(){return a.handleClick(u)},onMouseOver:function(e){return a.handleMouseOver(e,u)},onMouseLeave:function(e){return a.handleMouseLeave(e,u)}},this.getTooltipDataAttrsForIndex(n)),e.createElement("title",null,this.getTitleForIndex(n))),h=this.props.transformDayElement;return h?h(c,u,n):c}},{key:"renderWeek",value:function(t){var n=this;return e.createElement("g",{key:t,transform:this.getTransformForWeek(t),className:"".concat(k,"week")},v(7).map(function(e){return n.renderSquare(e,7*t+e)}))}},{key:"renderAllWeeks",value:function(){var e=this;return v(this.getWeekCount()).map(function(t){return e.renderWeek(t)})}},{key:"renderMonthLabels",value:function(){var t=this;return this.props.showMonthLabels?v(this.getWeekCount()-1).map(function(n){var a=g(t.getStartDateWithEmptyDays(),7*(n+1)),r=s(t.getMonthLabelCoordinates(n),2),o=r[0],l=r[1];return a.getDate()>=1&&a.getDate()<=7?e.createElement("text",{key:n,x:o,y:l,className:"".concat(k,"month-label")},t.props.monthLabels[a.getMonth()]):null}):null}},{key:"renderWeekdayLabels",value:function(){var t=this;return this.props.showWeekdayLabels?this.props.weekdayLabels.map(function(n,a){var r=s(t.getWeekdayLabelCoordinates(a),2),o=r[0],l=r[1],i="".concat(t.props.horizontal?"":"".concat(k,"small-text")," ").concat(k,"weekday-label");return 1&a?e.createElement("text",{key:"".concat(o).concat(l),x:o,y:l,className:i},n):null}):null}},{key:"render",value:function(){return this.valueCache=this.getValueCache(this.props),e.createElement("svg",{className:"react-calendar-heatmap",viewBox:this.getViewBox()},e.createElement("g",{transform:this.getTransformForMonthLabels(),className:"".concat(k,"month-labels")},this.renderMonthLabels()),e.createElement("g",{transform:this.getTransformForAllWeeks(),className:"".concat(k,"all-weeks")},this.renderAllWeeks()),e.createElement("g",{transform:this.getTransformForWeekdayLabels(),className:"".concat(k,"weekday-labels")},this.renderWeekdayLabels()))}}]),t}();function S(e,t){void 0===t&&(t={});var n=t.insertAt;if(e&&"undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css","top"===n&&a.firstChild?a.insertBefore(r,a.firstChild):a.appendChild(r),r.styleSheet?r.styleSheet.cssText=e:r.appendChild(document.createTextNode(e))}}D.propTypes={values:f.arrayOf(f.shape({date:f.oneOfType([f.string,f.number,f.instanceOf(Date)]).isRequired}).isRequired).isRequired,numDays:f.number,startDate:f.oneOfType([f.string,f.number,f.instanceOf(Date)]),endDate:f.oneOfType([f.string,f.number,f.instanceOf(Date)]),gutterSize:f.number,horizontal:f.bool,showMonthLabels:f.bool,showWeekdayLabels:f.bool,showOutOfRangeDays:f.bool,tooltipDataAttrs:f.oneOfType([f.object,f.func]),titleForValue:f.func,classForValue:f.func,monthLabels:f.arrayOf(f.string),weekdayLabels:f.arrayOf(f.string),onClick:f.func,onMouseOver:f.func,onMouseLeave:f.func,transformDayElement:f.func},D.defaultProps={numDays:null,startDate:g(new Date,-200),endDate:new Date,gutterSize:1,horizontal:!0,showMonthLabels:!0,showWeekdayLabels:!1,showOutOfRangeDays:!1,tooltipDataAttrs:null,titleForValue:null,classForValue:function(e){return e?"color-filled":"color-empty"},monthLabels:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],weekdayLabels:["","Mon","","Wed","","Fri",""],onClick:null,onMouseOver:null,onMouseLeave:null,transformDayElement:null};S("/*\n * react-calendar-heatmap styles\n *\n * All of the styles in this file are optional and configurable!\n * The github and gitlab color scales are provided for reference.\n */\n\n.react-calendar-heatmap text {\n font-size: 10px;\n fill: #aaa;\n}\n\n.react-calendar-heatmap .react-calendar-heatmap-small-text {\n font-size: 5px;\n}\n\n.react-calendar-heatmap rect:hover {\n stroke: #555;\n stroke-width: 1px;\n}\n\n/*\n * Default color scale\n */\n\n.react-calendar-heatmap .color-empty {\n fill: #eeeeee;\n}\n\n.react-calendar-heatmap .color-filled {\n fill: #8cc665;\n}\n\n/*\n * Github color scale\n */\n\n.react-calendar-heatmap .color-github-0 {\n fill: #eeeeee;\n}\n.react-calendar-heatmap .color-github-1 {\n fill: #d6e685;\n}\n.react-calendar-heatmap .color-github-2 {\n fill: #8cc665;\n}\n.react-calendar-heatmap .color-github-3 {\n fill: #44a340;\n}\n.react-calendar-heatmap .color-github-4 {\n fill: #1e6823;\n}\n\n/*\n * Gitlab color scale\n */\n\n.react-calendar-heatmap .color-gitlab-0 {\n fill: #ededed;\n}\n.react-calendar-heatmap .color-gitlab-1 {\n fill: #acd5f2;\n}\n.react-calendar-heatmap .color-gitlab-2 {\n fill: #7fa8d1;\n}\n.react-calendar-heatmap .color-gitlab-3 {\n fill: #49729b;\n}\n.react-calendar-heatmap .color-gitlab-4 {\n fill: #254e77;\n}\n");function w({username:a}){const[r,o]=t([]);n(()=>{a&&async function(){try{const e=await fetch(`https://leetcode-stats-api.herokuapp.com/${a}`),t=await e.json();if(!t.submissionCalendar)return;const n=t.submissionCalendar,r=Object.keys(n).map(e=>({date:new Date(1e3*Number(e)).toISOString().slice(0,10),count:n[e]}));o(r)}catch(e){console.error("Heatmap fetch error:",e)}}()},[a]);const l=new Date,i=new Date;i.setFullYear(l.getFullYear()-1);return e.createElement("div",{className:"heatmap-wrapper"},e.createElement("p",{className:"heatmap-title"},"LeetCode Activity Heatmap"),e.createElement(D,{startDate:i,endDate:l,values:r,gutterSize:4,showWeekdayLabels:!0,classForValue:e=>e?e.count>=7?"color-leetcode-4":e.count>=3?"color-leetcode-3":e.count>=2?"color-leetcode-2":"color-leetcode-1":"color-empty",tooltipDataAttrs:e=>{return e?{"data-tooltip":`${e.count} submissions on ${t=e.date,new Date(t).toLocaleDateString("en-US",{day:"numeric",month:"short",year:"numeric"})}`}:null;var t}}))}async function W(e){try{const t=await fetch(`https://leetcode-stats-api.herokuapp.com/${e}`);return await t.json()}catch(e){console.error("Heatmap fetch error:",e)}}S(".color-empty {\r\n fill: #ebedf0;\r\n}\r\n\r\n.color-leetcode-1 {\r\n fill: #9be9a8;\r\n}\r\n\r\n.color-leetcode-2 {\r\n fill: #40c463;\r\n}\r\n\r\n.color-leetcode-3 {\r\n fill: #30a14e;\r\n}\r\n\r\n.color-leetcode-4 {\r\n fill: #216e39;\r\n}\r\n");export{w as LeetCodeHeatmap,W as LeetcodeData};
//# sourceMappingURL=index.esm.js.map