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
"use strict";var e=require("react");function t(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 n(){return n=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},n.apply(this,arguments)}function a(e){return a=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},a(e)}function r(e,t){return r=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},r(e,t)}function o(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function l(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 i(){}function s(){}s.resetWarningCache=i;var u,c,h=(u=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:s,resetWarningCache:i};return n.PropTypes=n,n}()},u(c={exports:{}},c.exports),c.exports);function p(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 f=864e5;function y(e,t){var n=new Date(e);return n.setDate(n.getDate()+t),n}function d(e){return e instanceof Date?e:new Date(e)}function g(e){for(var t=[],n=0;n<e;n+=1)t.push(n);return t}var m=10,v="react-calendar-heatmap-",b=function(){function i(){var e,t,n,r,l,s,u;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,i);for(var c=arguments.length,h=new Array(c),y=0;y<c;y++)h[y]=arguments[y];return n=this,r=(e=a(i)).call.apply(e,[this].concat(h)),t=!r||"object"!=typeof r&&"function"!=typeof r?o(n):r,l=o(t),s="getValueCache",u=function(e,t){var n;void 0===t&&(t=p);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,n){var a=d(n.date);return e[Math.floor((a-t.getStartDateWithEmptyDays())/f)]={value:n,className:t.props.classForValue(n),title:t.props.titleForValue?t.props.titleForValue(n):null,tooltipDataAttrs:t.getTooltipDataAttrsForValue(n)},e},{})}),s in l?Object.defineProperty(l,s,{value:u,enumerable:!0,configurable:!0,writable:!0}):l[s]=u,t}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&&r(e,t)}(i,e.Component),t(i,[{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()-d(t);return Math.ceil(a/f)}},{key:"getSquareSizeWithGutter",value:function(){return m+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 y(this.getEndDate(),1-this.getDateDifferenceInDays())}},{key:"getEndDate",value:function(){return e=d(this.props.endDate),new Date(e.getFullYear(),e.getMonth(),e.getDate());var e}},{key:"getStartDateWithEmptyDays",value:function(){return y(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(m,", ").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)*m+e*this.props.gutterSize]:[e*m+e*this.props.gutterSize,m]}},{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,a){var r=this;if((a<this.getNumEmptyDaysAtStart()||a>=this.getNumEmptyDaysAtStart()+this.getDateDifferenceInDays())&&!this.props.showOutOfRangeDays)return null;var o=l(this.getSquareCoordinates(t),2),i=o[0],s=o[1],u=this.getValueForIndex(a),c=e.createElement("rect",n({key:a,width:m,height:m,x:i,y:s,className:this.getClassNameForIndex(a),onClick:function(){return r.handleClick(u)},onMouseOver:function(e){return r.handleMouseOver(e,u)},onMouseLeave:function(e){return r.handleMouseLeave(e,u)}},this.getTooltipDataAttrsForIndex(a)),e.createElement("title",null,this.getTitleForIndex(a))),h=this.props.transformDayElement;return h?h(c,u,a):c}},{key:"renderWeek",value:function(t){var n=this;return e.createElement("g",{key:t,transform:this.getTransformForWeek(t),className:"".concat(v,"week")},g(7).map(function(e){return n.renderSquare(e,7*t+e)}))}},{key:"renderAllWeeks",value:function(){var e=this;return g(this.getWeekCount()).map(function(t){return e.renderWeek(t)})}},{key:"renderMonthLabels",value:function(){var t=this;return this.props.showMonthLabels?g(this.getWeekCount()-1).map(function(n){var a=y(t.getStartDateWithEmptyDays(),7*(n+1)),r=l(t.getMonthLabelCoordinates(n),2),o=r[0],i=r[1];return a.getDate()>=1&&a.getDate()<=7?e.createElement("text",{key:n,x:o,y:i,className:"".concat(v,"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=l(t.getWeekdayLabelCoordinates(a),2),o=r[0],i=r[1],s="".concat(t.props.horizontal?"":"".concat(v,"small-text")," ").concat(v,"weekday-label");return 1&a?e.createElement("text",{key:"".concat(o).concat(i),x:o,y:i,className:s},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(v,"month-labels")},this.renderMonthLabels()),e.createElement("g",{transform:this.getTransformForAllWeeks(),className:"".concat(v,"all-weeks")},this.renderAllWeeks()),e.createElement("g",{transform:this.getTransformForWeekdayLabels(),className:"".concat(v,"weekday-labels")},this.renderWeekdayLabels()))}}]),i}();function k(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))}}b.propTypes={values:h.arrayOf(h.shape({date:h.oneOfType([h.string,h.number,h.instanceOf(Date)]).isRequired}).isRequired).isRequired,numDays:h.number,startDate:h.oneOfType([h.string,h.number,h.instanceOf(Date)]),endDate:h.oneOfType([h.string,h.number,h.instanceOf(Date)]),gutterSize:h.number,horizontal:h.bool,showMonthLabels:h.bool,showWeekdayLabels:h.bool,showOutOfRangeDays:h.bool,tooltipDataAttrs:h.oneOfType([h.object,h.func]),titleForValue:h.func,classForValue:h.func,monthLabels:h.arrayOf(h.string),weekdayLabels:h.arrayOf(h.string),onClick:h.func,onMouseOver:h.func,onMouseLeave:h.func,transformDayElement:h.func},b.defaultProps={numDays:null,startDate:y(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};k("/*\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");k(".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"),exports.LeetCodeHeatmap=function({username:t}){const[n,a]=e.useState([]);e.useEffect(()=>{t&&async function(){try{const e=await fetch(`https://leetcode-stats-api.herokuapp.com/${t}`),n=await e.json();if(!n.submissionCalendar)return;const r=n.submissionCalendar,o=Object.keys(r).map(e=>({date:new Date(1e3*Number(e)).toISOString().slice(0,10),count:r[e]}));a(o)}catch(e){console.error("Heatmap fetch error:",e)}}()},[t]);const r=new Date,o=new Date;return o.setFullYear(r.getFullYear()-1),e.createElement("div",{className:"heatmap-wrapper"},e.createElement("p",{className:"heatmap-title"},"LeetCode Activity Heatmap"),e.createElement(b,{startDate:o,endDate:r,values:n,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}}))},exports.LeetcodeData=async function(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)}};
//# sourceMappingURL=index.cjs.js.map