mirror of
https://github.com/ratiger/obsidian-banyan.git
synced 2026-07-22 05:49:34 +00:00
feat: 实现自己的热力图组件,并换用
This commit is contained in:
parent
e689976543
commit
e9f8b027fa
7 changed files with 537 additions and 544 deletions
539
package-lock.json
generated
539
package-lock.json
generated
|
|
@ -9,7 +9,6 @@
|
|||
"version": "1.4.4",
|
||||
"license": "GPL-3.0-only",
|
||||
"dependencies": {
|
||||
"cal-heatmap": "^4.2.4",
|
||||
"react": "^19.1.0",
|
||||
"react-calendar-heatmap": "^1.10.0",
|
||||
"react-dom": "^19.1.0",
|
||||
|
|
@ -1747,20 +1746,6 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@observablehq/plot": {
|
||||
"version": "0.6.17",
|
||||
"resolved": "https://registry.npmmirror.com/@observablehq/plot/-/plot-0.6.17.tgz",
|
||||
"integrity": "sha512-/qaXP/7mc4MUS0s4cPPFASDRjtsWp85/TbfsciqDgU1HwYixbSbbytNuInD8AcTYC3xaxACgVX06agdfQy9W+g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3": "^7.9.0",
|
||||
"interval-tree-1d": "^1.0.0",
|
||||
"isoformat": "^0.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
|
|
@ -1785,16 +1770,6 @@
|
|||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.34.35",
|
||||
"resolved": "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.34.35.tgz",
|
||||
|
|
@ -2721,12 +2696,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/binary-search-bounds": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz",
|
||||
"integrity": "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
|
|
@ -2827,23 +2796,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cal-heatmap": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/cal-heatmap/-/cal-heatmap-4.2.4.tgz",
|
||||
"integrity": "sha512-TTNoQTRxHXrttOEbkraKv9vy2VpfQIwVLQJTlAfcBusQK9qrBL/UBO+WloAxv2yrR+P8URA2cuXEdc5iztER9g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@observablehq/plot": "^0.6.0",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-fetch": "^3.0.1",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-transition": "^3.0.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"eventemitter3": "^5.0.0",
|
||||
"lodash-es": "^4.17.21"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
|
||||
|
|
@ -3034,15 +2986,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
|
@ -3079,413 +3022,6 @@
|
|||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/d3": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3/-/d3-7.9.0.tgz",
|
||||
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "3",
|
||||
"d3-axis": "3",
|
||||
"d3-brush": "3",
|
||||
"d3-chord": "3",
|
||||
"d3-color": "3",
|
||||
"d3-contour": "4",
|
||||
"d3-delaunay": "6",
|
||||
"d3-dispatch": "3",
|
||||
"d3-drag": "3",
|
||||
"d3-dsv": "3",
|
||||
"d3-ease": "3",
|
||||
"d3-fetch": "3",
|
||||
"d3-force": "3",
|
||||
"d3-format": "3",
|
||||
"d3-geo": "3",
|
||||
"d3-hierarchy": "3",
|
||||
"d3-interpolate": "3",
|
||||
"d3-path": "3",
|
||||
"d3-polygon": "3",
|
||||
"d3-quadtree": "3",
|
||||
"d3-random": "3",
|
||||
"d3-scale": "4",
|
||||
"d3-scale-chromatic": "3",
|
||||
"d3-selection": "3",
|
||||
"d3-shape": "3",
|
||||
"d3-time": "3",
|
||||
"d3-time-format": "4",
|
||||
"d3-timer": "3",
|
||||
"d3-transition": "3",
|
||||
"d3-zoom": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-axis": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-axis/-/d3-axis-3.0.0.tgz",
|
||||
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-brush": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-brush/-/d3-brush-3.0.0.tgz",
|
||||
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "3",
|
||||
"d3-transition": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-chord": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-chord/-/d3-chord-3.0.1.tgz",
|
||||
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-path": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-contour": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-contour/-/d3-contour-4.0.2.tgz",
|
||||
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "^3.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-delaunay": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"delaunator": "5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dispatch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-drag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-selection": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dsv": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
||||
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"commander": "7",
|
||||
"iconv-lite": "0.6",
|
||||
"rw": "1"
|
||||
},
|
||||
"bin": {
|
||||
"csv2json": "bin/dsv2json.js",
|
||||
"csv2tsv": "bin/dsv2dsv.js",
|
||||
"dsv2dsv": "bin/dsv2dsv.js",
|
||||
"dsv2json": "bin/dsv2json.js",
|
||||
"json2csv": "bin/json2dsv.js",
|
||||
"json2dsv": "bin/json2dsv.js",
|
||||
"json2tsv": "bin/json2dsv.js",
|
||||
"tsv2csv": "bin/dsv2dsv.js",
|
||||
"tsv2json": "bin/dsv2json.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-fetch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-fetch/-/d3-fetch-3.0.1.tgz",
|
||||
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-dsv": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-force": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-force/-/d3-force-3.0.0.tgz",
|
||||
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-quadtree": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.0.tgz",
|
||||
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-geo": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-geo/-/d3-geo-3.1.1.tgz",
|
||||
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "2.5.0 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-hierarchy": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
|
||||
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-polygon": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-polygon/-/d3-polygon-3.0.1.tgz",
|
||||
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-quadtree": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
|
||||
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-random": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-random/-/d3-random-3.0.1.tgz",
|
||||
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale-chromatic": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-interpolate": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-selection": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-path": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-transition": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-ease": "1 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"d3-selection": "2 - 3"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-zoom": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "2 - 3",
|
||||
"d3-transition": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
|
||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.0.tgz",
|
||||
|
|
@ -3537,15 +3073,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/delaunator": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/delaunator/-/delaunator-5.0.1.tgz",
|
||||
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"robust-predicates": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-newline": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/detect-newline/-/detect-newline-3.1.0.tgz",
|
||||
|
|
@ -3950,12 +3477,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/execa": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz",
|
||||
|
|
@ -4420,18 +3941,6 @@
|
|||
"node": ">=10.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz",
|
||||
|
|
@ -4509,24 +4018,6 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz",
|
||||
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/interval-tree-1d": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/interval-tree-1d/-/interval-tree-1d-1.0.4.tgz",
|
||||
"integrity": "sha512-wY8QJH+6wNI0uh4pDQzMvl+478Qh7Rl4qLmqiluxALlNvl+I+o5x38Pw3/z7mDPTPS1dQalZJXsmbvxx5gclhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"binary-search-bounds": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
|
|
@ -4618,12 +4109,6 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/isoformat": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/isoformat/-/isoformat-0.2.1.tgz",
|
||||
"integrity": "sha512-tFLRAygk9NqrRPhJSnNGh7g7oaVWDwR0wKh/GM2LgmPa50Eg4UfyaCO4I8k6EqJHl1/uh2RAD6g06n5ygEnrjQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/istanbul-lib-coverage": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
|
||||
|
|
@ -5460,12 +4945,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
|
|
@ -6267,12 +5746,6 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/robust-predicates": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.2.tgz",
|
||||
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
|
|
@ -6297,18 +5770,6 @@
|
|||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/rw/-/rw-1.3.3.tgz",
|
||||
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.26.0.tgz",
|
||||
|
|
|
|||
|
|
@ -30,11 +30,10 @@
|
|||
"typescript": "4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"cal-heatmap": "^4.2.4",
|
||||
"react": "^19.1.0",
|
||||
"react-calendar-heatmap": "^1.10.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-tooltip": "^5.28.1",
|
||||
"zustand": "^5.0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
- 筛选某一天时,筛选条件显示当天日期,而不是日期范围。
|
||||
- 筛选结果中的标签,现在可以点击。
|
||||
- 更改筛选方案现在会把条件同步到搜索框,现在可以基于当前筛选方案进行个性搜索。
|
||||
|
||||
- 更换热力图组件,现在可以查看所有日期的情况。
|
||||
|
||||
|
||||
### 1.4.4
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Heatmap } from 'src/pages/sidebar/heatmap/Heatmap';
|
||||
import { CustomHeatmap } from 'src/pages/sidebar/heatmap/CustomHeatmap';
|
||||
import { FilterSchemesInfo } from './filterScheme/FilterSchemesInfo';
|
||||
import { ViewSchemesInfo } from './viewScheme/ViewSchemesInfo';
|
||||
import { RandomReviewInfo } from './randomReview/RandomReviewInfo';
|
||||
|
|
@ -22,7 +22,7 @@ export const SidebarContent = () => {
|
|||
return (
|
||||
<div className="sidebar-content">
|
||||
<StatisticsInfo />
|
||||
<Heatmap onCickDate={handleClickDate} />
|
||||
<CustomHeatmap onCickDate={handleClickDate} />
|
||||
<div className="sidebar-margin-top" />
|
||||
<SidebarButton leftIconName="settings"
|
||||
label={i18n.t('setting_panel')}
|
||||
|
|
|
|||
316
src/pages/sidebar/heatmap/CustomHeatmap.tsx
Normal file
316
src/pages/sidebar/heatmap/CustomHeatmap.tsx
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
import moment from 'moment';
|
||||
import { useMemo, useRef, useEffect, useState } from 'react';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import { SortType } from 'src/models/Enum';
|
||||
import { FileInfo } from 'src/models/FileInfo';
|
||||
import { useCombineStore } from 'src/store';
|
||||
import { i18n } from 'src/utils/i18n';
|
||||
|
||||
export type HeatmapData = {
|
||||
date: string,
|
||||
count: number,
|
||||
}
|
||||
|
||||
export const CustomHeatmap = ({
|
||||
onCickDate,
|
||||
cellSize = 14,
|
||||
gapSize = 6,
|
||||
fontSize = 14,
|
||||
}: {
|
||||
onCickDate: (date: string) => void,
|
||||
cellSize?: number,
|
||||
gapSize?: number,
|
||||
fontSize?: number,
|
||||
}) => {
|
||||
const sortType = useCombineStore((state) => state.appData.sortType);
|
||||
const allFiles = useCombineStore((state) => state.allFiles);
|
||||
const firstUseDateStr = useCombineStore((state) => state.appData.firstUseDate);
|
||||
|
||||
const values = useMemo(() => getHeatmapValues(allFiles, sortType), [allFiles, sortType]);
|
||||
|
||||
const valueMap = useMemo(() => {
|
||||
const map = new Map<string, number>();
|
||||
values.forEach(v => {
|
||||
map.set(v.date, v.count);
|
||||
});
|
||||
return map;
|
||||
}, [values]);
|
||||
|
||||
const today = useMemo(() => {
|
||||
const d = new Date();
|
||||
d.setHours(0, 0, 0, 0);
|
||||
return d;
|
||||
}, []);
|
||||
|
||||
const earliestDate = useMemo(() => {
|
||||
// Default to 1 year ago (52 weeks ago)
|
||||
let d = new Date(today);
|
||||
d.setFullYear(today.getFullYear() - 1);
|
||||
|
||||
// Find the earliest file date in values
|
||||
if (values && values.length > 0) {
|
||||
values.forEach(v => {
|
||||
const dateVal = new Date(v.date);
|
||||
if (!isNaN(dateVal.getTime()) && dateVal < d) {
|
||||
d = dateVal;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check first use date
|
||||
if (firstUseDateStr) {
|
||||
const fud = new Date(firstUseDateStr);
|
||||
if (!isNaN(fud.getTime()) && fud < d) {
|
||||
d = fud;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}, [values, firstUseDateStr, today]);
|
||||
|
||||
// Align to Sunday of start week
|
||||
const startOfWeek = useMemo(() => {
|
||||
const d = new Date(earliestDate);
|
||||
const day = d.getDay();
|
||||
d.setDate(d.getDate() - day);
|
||||
d.setHours(0, 0, 0, 0);
|
||||
return d;
|
||||
}, [earliestDate]);
|
||||
|
||||
// Align to Saturday of end week
|
||||
const endOfWeek = useMemo(() => {
|
||||
const d = new Date(today);
|
||||
const day = d.getDay();
|
||||
d.setDate(d.getDate() + (6 - day));
|
||||
d.setHours(23, 59, 59, 999);
|
||||
return d;
|
||||
}, [today]);
|
||||
|
||||
// Generate weeks array
|
||||
const weeks = useMemo(() => {
|
||||
const result: {
|
||||
date: Date;
|
||||
dateStr: string;
|
||||
count: number;
|
||||
level: number;
|
||||
isToday: boolean;
|
||||
isFuture: boolean;
|
||||
tooltipText: string;
|
||||
}[][] = [];
|
||||
|
||||
const currentDay = new Date(startOfWeek);
|
||||
let currentWeek: typeof result[0] = [];
|
||||
|
||||
const getDayLevel = (count: number) => {
|
||||
if (count === 0) return 0;
|
||||
const numPerLevel = 4;
|
||||
const numOflevels = 3;
|
||||
return Math.min(numOflevels, Math.ceil(count / numPerLevel));
|
||||
};
|
||||
|
||||
const todayStr = moment(today).format('YYYY-MM-DD');
|
||||
|
||||
while (currentDay <= endOfWeek) {
|
||||
const dateStr = moment(currentDay).format('YYYY-MM-DD');
|
||||
const count = valueMap.get(dateStr) || 0;
|
||||
const level = getDayLevel(count);
|
||||
const isToday = dateStr === todayStr;
|
||||
const isFuture = currentDay > today;
|
||||
|
||||
const activityText = (sortType === 'created' || sortType === 'earliestCreated')
|
||||
? i18n.t('notes_created_at')
|
||||
: i18n.t('notes_modified_at');
|
||||
const tooltipText = count > 0
|
||||
? `${count} ${activityText} ${dateStr}`
|
||||
: `${i18n.t('empty_note_result')} ${dateStr}`;
|
||||
|
||||
currentWeek.push({
|
||||
date: new Date(currentDay),
|
||||
dateStr,
|
||||
count,
|
||||
level: isFuture ? 0 : level,
|
||||
isToday,
|
||||
isFuture,
|
||||
tooltipText,
|
||||
});
|
||||
|
||||
if (currentWeek.length === 7) {
|
||||
result.push(currentWeek);
|
||||
currentWeek = [];
|
||||
}
|
||||
|
||||
currentDay.setDate(currentDay.getDate() + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [startOfWeek, endOfWeek, valueMap, today, sortType]);
|
||||
|
||||
// Precalculate which week columns should render a month label to avoid collisions
|
||||
const labelIndices = useMemo(() => {
|
||||
const indices = new Set<number>();
|
||||
let lastLabelIndex = -10;
|
||||
|
||||
for (let i = 0; i < weeks.length; i++) {
|
||||
const week = weeks[i];
|
||||
const prevWeek = weeks[i - 1];
|
||||
const isNewMonth = i === 0 || week[0].date.getMonth() !== prevWeek[0].date.getMonth();
|
||||
|
||||
if (isNewMonth) {
|
||||
// If it's the very first column, check if the next month is too close (within 3 columns)
|
||||
if (i === 0) {
|
||||
let nextMonthIndex = -1;
|
||||
for (let j = 1; j < weeks.length; j++) {
|
||||
if (weeks[j][0].date.getMonth() !== week[0].date.getMonth()) {
|
||||
nextMonthIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Skip the very first month label if it's too short, prioritizing the next month's clean render
|
||||
if (nextMonthIndex !== -1 && nextMonthIndex < 3) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Spacing check: Only render month label if it's at least 3 columns away from the last rendered label
|
||||
if (i - lastLabelIndex >= 3) {
|
||||
indices.add(i);
|
||||
lastLabelIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
}, [weeks]);
|
||||
|
||||
// Find the first index that displays a month label
|
||||
const firstLabelIndex = useMemo(() => {
|
||||
let min = Infinity;
|
||||
labelIndices.forEach(idx => {
|
||||
if (idx < min) min = idx;
|
||||
});
|
||||
return min === Infinity ? -1 : min;
|
||||
}, [labelIndices]);
|
||||
|
||||
// Format the month label: January displays ONLY the year, first label displays Year + Month
|
||||
const getMonthLabelText = (date: Date, isFirst: boolean) => {
|
||||
const monthIndex = date.getMonth();
|
||||
const monthName = i18n.t(`month${monthIndex + 1}` as any);
|
||||
const year = date.getFullYear();
|
||||
const isJanuary = monthIndex === 0;
|
||||
|
||||
if (isJanuary) {
|
||||
return `${year}`;
|
||||
}
|
||||
|
||||
if (isFirst) {
|
||||
return `${monthName}`;
|
||||
}
|
||||
return monthName;
|
||||
};
|
||||
|
||||
// Scroll control ref
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const hasScrolledRef = useRef(false);
|
||||
|
||||
// Initial scroll to today (far right)
|
||||
useEffect(() => {
|
||||
if (containerRef.current && !hasScrolledRef.current && weeks.length > 0) {
|
||||
containerRef.current.scrollLeft = containerRef.current.scrollWidth;
|
||||
hasScrolledRef.current = true;
|
||||
}
|
||||
}, [weeks.length]);
|
||||
|
||||
// Mouse drag-to-scroll panning state
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [startX, setStartX] = useState(0);
|
||||
const [scrollLeftState, setScrollLeftState] = useState(0);
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
setIsDragging(true);
|
||||
setStartX(e.pageX - containerRef.current.offsetLeft);
|
||||
setScrollLeftState(containerRef.current.scrollLeft);
|
||||
};
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent) => {
|
||||
if (!isDragging || !containerRef.current) return;
|
||||
e.preventDefault();
|
||||
const x = e.pageX - containerRef.current.offsetLeft;
|
||||
// Sensitivity factor: 1.5
|
||||
const walk = (x - startX) * 1.5;
|
||||
containerRef.current.scrollLeft = scrollLeftState - walk;
|
||||
};
|
||||
|
||||
const handleMouseUpOrLeave = () => {
|
||||
setIsDragging(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="custom-heatmap-container">
|
||||
|
||||
{/* Scrollable Grid */}
|
||||
<div
|
||||
className={`custom-heatmap-scroll-container ${isDragging ? 'is-dragging' : ''}`}
|
||||
ref={containerRef}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseUp={handleMouseUpOrLeave}
|
||||
onMouseLeave={handleMouseUpOrLeave}
|
||||
>
|
||||
<div className="custom-heatmap-content-wrapper">
|
||||
{/* Month Labels Row */}
|
||||
<div className="custom-heatmap-months-row" style={{ gap: `${gapSize}px` }}>
|
||||
{weeks.map((week, index) => {
|
||||
const showLabel = labelIndices.has(index);
|
||||
const isFirst = index === firstLabelIndex;
|
||||
return (
|
||||
<div key={index} className="custom-heatmap-month-label-col" style={{ width: `${cellSize}px` }}>
|
||||
{showLabel && (
|
||||
<span className="custom-heatmap-month-label" style={{ fontSize: `${fontSize}px` }}>
|
||||
{getMonthLabelText(week[0].date, isFirst)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Day Grid Row */}
|
||||
<div className="custom-heatmap-grid-row" style={{ gap: `${gapSize}px` }}>
|
||||
{weeks.map((week, index) => (
|
||||
<div key={index} className="custom-heatmap-week-column" style={{ gap: `${gapSize}px` }}>
|
||||
{week.map((day) => (
|
||||
<div
|
||||
key={day.dateStr}
|
||||
className={`custom-heatmap-day-cell color-scale-${day.level} ${day.isToday ? 'is-today' : ''} ${day.isFuture ? 'is-future' : ''}`}
|
||||
style={{ width: `${cellSize}px`, height: `${cellSize}px` }}
|
||||
data-tooltip-id="custom-heatmap-tooltip"
|
||||
data-tooltip-content={day.tooltipText}
|
||||
onClick={() => !day.isFuture && onCickDate(day.dateStr)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tooltip id="custom-heatmap-tooltip" className="custom-heatmap-tooltip" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const getHeatmapValues = (fileInfos: FileInfo[], sortType: SortType) => {
|
||||
const valueMap = fileInfos
|
||||
.map(f => f.file.stat)
|
||||
.map(stat => {
|
||||
const timestamp = (sortType === 'created' || sortType === 'earliestCreated') ? stat.ctime : stat.mtime;
|
||||
return moment(timestamp).format('YYYY-MM-DD');
|
||||
})
|
||||
.reduce<Map<string, number>>(
|
||||
(pre, cur) => pre.set(cur, pre.has(cur) ? pre.get(cur)! + 1 : 1),
|
||||
new Map<string, number>());
|
||||
return Array
|
||||
.from(valueMap.entries())
|
||||
.map(([key, value]) => {
|
||||
return { date: key, count: value };
|
||||
});
|
||||
};
|
||||
216
src/pages/sidebar/heatmap/custom-heatmap.css
Normal file
216
src/pages/sidebar/heatmap/custom-heatmap.css
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/* Container for the whole Heatmap widget */
|
||||
.custom-heatmap-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Header style containing title and controls */
|
||||
.custom-heatmap-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.custom-heatmap-title {
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.custom-heatmap-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
/* Small elegant navigation buttons */
|
||||
.custom-heatmap-control-btn {
|
||||
background: var(--background-secondary);
|
||||
border: 1px solid var(--border-color, var(--background-modifier-border));
|
||||
color: var(--text-normal);
|
||||
border-radius: 4px;
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 0.15s ease, border-color 0.15s ease;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.custom-heatmap-control-btn:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
.custom-heatmap-control-btn svg {
|
||||
stroke: var(--text-muted);
|
||||
}
|
||||
|
||||
.custom-heatmap-control-btn:hover svg {
|
||||
stroke: var(--text-normal);
|
||||
}
|
||||
|
||||
/* Scrollable area */
|
||||
.custom-heatmap-scroll-container {
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--scrollbar-thumb-bg, var(--background-modifier-border)) transparent;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.custom-heatmap-scroll-container.is-dragging {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* We hide standard scrollbars on Webkit browsers for a cleaner look */
|
||||
.custom-heatmap-scroll-container::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.custom-heatmap-scroll-container::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.custom-heatmap-scroll-container::-webkit-scrollbar-thumb {
|
||||
background-color: var(--scrollbar-thumb-bg, var(--background-modifier-border));
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Wrapper to hold row sizes together */
|
||||
.custom-heatmap-content-wrapper {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
padding: 4px;
|
||||
min-width: max-content;
|
||||
}
|
||||
|
||||
/* Month labels row layout */
|
||||
.custom-heatmap-months-row {
|
||||
display: flex;
|
||||
height: 14px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.custom-heatmap-month-label-col {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.custom-heatmap-month-label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* Days grid layout */
|
||||
.custom-heatmap-grid-row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.custom-heatmap-week-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Day cell styling */
|
||||
.custom-heatmap-day-cell {
|
||||
border-radius: 2px;
|
||||
transition: transform 0.1s ease, box-shadow 0.15s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Prevent clicking future days */
|
||||
.custom-heatmap-day-cell.is-future {
|
||||
cursor: default;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* Highlight today with a subtle border/outline */
|
||||
.custom-heatmap-day-cell.is-today {
|
||||
outline: 1.5px solid var(--color-accent);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
/* Hover effects for interactive cells */
|
||||
.custom-heatmap-day-cell:not(.is-future):hover {
|
||||
transform: scale(1.3);
|
||||
z-index: 10;
|
||||
box-shadow: 0 0 6px var(--color-accent);
|
||||
}
|
||||
|
||||
/* Color scales mapped exactly as requested */
|
||||
.custom-heatmap-day-cell.color-scale-0 {
|
||||
background-color: var(--background-primary-alt);
|
||||
}
|
||||
|
||||
.custom-heatmap-day-cell.color-scale-1 {
|
||||
background-color: color-mix(in srgb, var(--color-accent) 33%, var(--background-primary-alt) 66%);
|
||||
}
|
||||
|
||||
.custom-heatmap-day-cell.color-scale-2 {
|
||||
background-color: color-mix(in srgb, var(--color-accent) 66%, var(--background-primary-alt) 33%);
|
||||
}
|
||||
|
||||
.custom-heatmap-day-cell.color-scale-3 {
|
||||
background-color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* Footer / Legend */
|
||||
.custom-heatmap-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-top: 6px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.custom-heatmap-legend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.custom-heatmap-legend-text {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.custom-heatmap-legend-cell {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.custom-heatmap-legend-cell.color-scale-0 {
|
||||
background-color: var(--background-primary-alt);
|
||||
}
|
||||
|
||||
.custom-heatmap-legend-cell.color-scale-1 {
|
||||
background-color: color-mix(in srgb, var(--color-accent) 33%, var(--background-primary-alt) 66%);
|
||||
}
|
||||
|
||||
.custom-heatmap-legend-cell.color-scale-2 {
|
||||
background-color: color-mix(in srgb, var(--color-accent) 66%, var(--background-primary-alt) 33%);
|
||||
}
|
||||
|
||||
.custom-heatmap-legend-cell.color-scale-3 {
|
||||
background-color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* Tooltip layering */
|
||||
.custom-heatmap-tooltip {
|
||||
z-index: 2001;
|
||||
font-size: 10px !important;
|
||||
padding: 4px 8px !important;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
@import 'src/pages/sidebar/sidebar.css';
|
||||
@import 'src/pages/sidebar/heatmap/heatmap.css';
|
||||
@import 'src/pages/sidebar/heatmap/custom-heatmap.css';
|
||||
@import 'src/pages/sidebar/filterScheme/filterScheme.css';
|
||||
@import 'src/pages/sidebar/viewScheme/viewScheme.css';
|
||||
@import 'src/pages/sidebar/randomReview/randomReview.css';
|
||||
|
|
|
|||
Loading…
Reference in a new issue