@stfrigerio/sito-template 0.1.97 → 0.1.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/organisms/charts/MoodChart/MoodChart.d.ts.map +1 -1
- package/dist/components/organisms/charts/QuantifiableHabitsChart/QuantifiableHabitsChart.d.ts.map +1 -1
- package/dist/index.esm.js +539 -175
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +538 -174
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
- package/storybook-static/assets/{ASCIIText.stories-Df73neNa.js → ASCIIText.stories-DSI7g-mB.js} +1 -1
- package/storybook-static/assets/{AllAtoms.stories-BiI0dRwW.js → AllAtoms.stories-JflUIS5e.js} +1 -1
- package/storybook-static/assets/{AnimatedContent.stories-DjkrPhwb.js → AnimatedContent.stories-YR7SgIxb.js} +1 -1
- package/storybook-static/assets/{AnimatedList.stories-BfUnGN7d.js → AnimatedList.stories-GR75O93H.js} +1 -1
- package/storybook-static/assets/{Antigravity.stories-BTD9Xg7T.js → Antigravity.stories-B2MVCdRl.js} +1 -1
- package/storybook-static/assets/{ArrayInput.stories-B9kdIT9a.js → ArrayInput.stories-BI_3D81-.js} +1 -1
- package/storybook-static/assets/{Aurora.stories-BKttBo3z.js → Aurora.stories-C_w705rr.js} +1 -1
- package/storybook-static/assets/{Beams.stories-BT-94qMG.js → Beams.stories-D1L26zBi.js} +1 -1
- package/storybook-static/assets/{BlobCursor.stories-C0fe-xEI.js → BlobCursor.stories-BjywoQ5q.js} +1 -1
- package/storybook-static/assets/{BlurText.stories-CASYZKEu.js → BlurText.stories-CSpGTebR.js} +1 -1
- package/storybook-static/assets/{BooleansHeatmap.stories-CUTRmSWT.js → BooleansHeatmap.stories-CKYZW8W5.js} +1 -1
- package/storybook-static/assets/{BorderGlow.stories-Cbr654fz.js → BorderGlow.stories-BadihHiQ.js} +1 -1
- package/storybook-static/assets/{BubbleMenu.stories-D6s9f9iP.js → BubbleMenu.stories-Up_UUIWc.js} +1 -1
- package/storybook-static/assets/{Button-BFN9LHxy.js → Button-DtiJJxMy.js} +1 -1
- package/storybook-static/assets/{Button.stories--QkKginK.js → Button.stories-BG9uxBwH.js} +1 -1
- package/storybook-static/assets/{Calendar.stories-C6fMirS-.js → Calendar.stories-CUZikQRy.js} +1 -1
- package/storybook-static/assets/{Card-VJ_YQ2Rs.js → Card-BrvmNtM2.js} +1 -1
- package/storybook-static/assets/{Card.stories-BDxNouIx.js → Card.stories-Bf_EtMxA.js} +1 -1
- package/storybook-static/assets/{CardNav.stories-BnhrMkGa.js → CardNav.stories-OiwfVEn3.js} +1 -1
- package/storybook-static/assets/{Carousel.stories-CifGVvYJ.js → Carousel.stories-Gw1aPaNl.js} +1 -1
- package/storybook-static/assets/{Checkbox-BmAUW9fp.js → Checkbox-CJhut6rk.js} +1 -1
- package/storybook-static/assets/{Checkbox.stories-C_UvBcxW.js → Checkbox.stories-tIqU6B-y.js} +1 -1
- package/storybook-static/assets/{ChromaGrid.stories-BUUY24tG.js → ChromaGrid.stories-DbsrxAuC.js} +1 -1
- package/storybook-static/assets/{CircularGallery.stories-BAHtaC5N.js → CircularGallery.stories-0ffqgfyn.js} +1 -1
- package/storybook-static/assets/{CircularText.stories-Dyfa9TGc.js → CircularText.stories-o46bAR53.js} +1 -1
- package/storybook-static/assets/{ClickSpark.stories-DyY7-W0U.js → ClickSpark.stories-Ci-BtpH_.js} +1 -1
- package/storybook-static/assets/{ColorBends.stories-C09fGbbd.js → ColorBends.stories-CAiI7Gmw.js} +1 -1
- package/storybook-static/assets/{CountUp.stories-D319IPzj.js → CountUp.stories-CFiYranp.js} +1 -1
- package/storybook-static/assets/{Counter.stories-DyCug9AH.js → Counter.stories-BfaVu_y6.js} +1 -1
- package/storybook-static/assets/{Crosshair.stories-B0WQ_fE1.js → Crosshair.stories-_Hh1nKQp.js} +1 -1
- package/storybook-static/assets/{Cubes.stories-mKx-JaV9.js → Cubes.stories-DXBS_f8J.js} +1 -1
- package/storybook-static/assets/{CurvedLoop.stories-CPT1TgIj.js → CurvedLoop.stories-jcgMeMsw.js} +1 -1
- package/storybook-static/assets/{DarkVeil.stories-DO4z0Xfp.js → DarkVeil.stories-BwtU9vKO.js} +1 -1
- package/storybook-static/assets/{DateInput-CgcMwqZf.js → DateInput-6Jn_FyvO.js} +1 -1
- package/storybook-static/assets/{DateInput.stories-9DZA97sK.js → DateInput.stories-C_XBRwB3.js} +1 -1
- package/storybook-static/assets/{DecayCard.stories-DGvn_IgJ.js → DecayCard.stories-D6kTH79t.js} +1 -1
- package/storybook-static/assets/{DecryptedText.stories-_ycl9X6g.js → DecryptedText.stories-B4xWEvyF.js} +1 -1
- package/storybook-static/assets/{Dither.stories-B1UXWjuE.js → Dither.stories-DmAOxBWe.js} +1 -1
- package/storybook-static/assets/{Dock.stories--hZxLVBG.js → Dock.stories-AHZywNVf.js} +1 -1
- package/storybook-static/assets/{EditFAB.stories-ujZfcFCB.js → EditFAB.stories-BJ5P85ZN.js} +1 -1
- package/storybook-static/assets/{EvilEye.stories-DbdhTF7k.js → EvilEye.stories-D0CBPfm1.js} +1 -1
- package/storybook-static/assets/{FadeContent.stories-CwCMoiJh.js → FadeContent.stories-D1capsBJ.js} +1 -1
- package/storybook-static/assets/{FaultyTerminal.stories-DIhmC0oV.js → FaultyTerminal.stories-BxqqRf0S.js} +1 -1
- package/storybook-static/assets/{Fbo-X5yWHBm9.js → Fbo-DvyLCJ1Y.js} +1 -1
- package/storybook-static/assets/{FloatingLines.stories-BVLUpCaG.js → FloatingLines.stories-7ze9ddpl.js} +1 -1
- package/storybook-static/assets/{FlowingMenu.stories-Bz2Hm0ud.js → FlowingMenu.stories-BX9PD8x2.js} +1 -1
- package/storybook-static/assets/{FluidGlass.stories-iKAeEvkd.js → FluidGlass.stories-DynD7GVO.js} +1 -1
- package/storybook-static/assets/{Folder.stories-DsIub0M3.js → Folder.stories-DDktLoEO.js} +1 -1
- package/storybook-static/assets/{FuzzyText.stories-oIYU7hRG.js → FuzzyText.stories-D9tRARUX.js} +1 -1
- package/storybook-static/assets/{Galaxy.stories-CjouLZaF.js → Galaxy.stories-aSApGekp.js} +1 -1
- package/storybook-static/assets/{GhostCursor.stories-DCvUCCxa.js → GhostCursor.stories-5l0di532.js} +1 -1
- package/storybook-static/assets/{GlareHover.stories-Djk9iCFM.js → GlareHover.stories-Dc69-LbU.js} +1 -1
- package/storybook-static/assets/{GlassSurface.stories-VZesotzp.js → GlassSurface.stories-DETLyc0H.js} +1 -1
- package/storybook-static/assets/{GlitchText.stories-sn_wSP7G.js → GlitchText.stories-Bzb3ZMSY.js} +1 -1
- package/storybook-static/assets/{GooeyNav.stories-BFC9B_L0.js → GooeyNav.stories-rxdxJjAy.js} +1 -1
- package/storybook-static/assets/{GradientBlinds.stories-z-3FXHpB.js → GradientBlinds.stories-03LkBbo8.js} +1 -1
- package/storybook-static/assets/{GradientText.stories-B9P29Fe5.js → GradientText.stories-CGo9FyNH.js} +1 -1
- package/storybook-static/assets/{Grainient.stories-BxQe6t8A.js → Grainient.stories-CzGAEJr-.js} +1 -1
- package/storybook-static/assets/{GridMotion.stories-B8Qfotg2.js → GridMotion.stories-BzeBDaK8.js} +1 -1
- package/storybook-static/assets/{HabitTimeOfDayChart.stories-DqOwaExY.js → HabitTimeOfDayChart.stories-CbMqUfq0.js} +1 -1
- package/storybook-static/assets/{ImageSlideshow.stories-C7AUfiPQ.js → ImageSlideshow.stories-hoGOc1Gf.js} +1 -1
- package/storybook-static/assets/{Iridescence.stories-CmJNPKZp.js → Iridescence.stories-CQTjQ1lh.js} +1 -1
- package/storybook-static/assets/{LaserFlow.stories-Cj36k-Br.js → LaserFlow.stories-DzkVWskc.js} +1 -1
- package/storybook-static/assets/{LetterGlitch.stories-3n0_SpPW.js → LetterGlitch.stories-0wcxCpnJ.js} +1 -1
- package/storybook-static/assets/{LightPillar.stories-DLDV89_g.js → LightPillar.stories-C3jRtdg9.js} +1 -1
- package/storybook-static/assets/{LightRays.stories-95rx1Jld.js → LightRays.stories-C52kBsnd.js} +1 -1
- package/storybook-static/assets/{Lightning.stories-Ban00VPp.js → Lightning.stories-BFVGqmMh.js} +1 -1
- package/storybook-static/assets/{LineWaves.stories-tYVITNBI.js → LineWaves.stories-DNdwRrSd.js} +1 -1
- package/storybook-static/assets/{LiquidChrome.stories-CRXhS5wG.js → LiquidChrome.stories-DFTBXxI6.js} +1 -1
- package/storybook-static/assets/{LiquidEther.stories-DFcjj4WN.js → LiquidEther.stories-BGYmyfDI.js} +1 -1
- package/storybook-static/assets/{LoadingSpinner-BCSPbqAf.js → LoadingSpinner-D3Kx-V7J.js} +1 -1
- package/storybook-static/assets/{LoadingSpinner.stories-B-3mwj6v.js → LoadingSpinner.stories-CP70BGAb.js} +1 -1
- package/storybook-static/assets/{MagicRings.stories-DxX9Y4fZ.js → MagicRings.stories-BA9xv5mB.js} +1 -1
- package/storybook-static/assets/{Magnet.stories-BtP078KP.js → Magnet.stories-cqBYCb-D.js} +1 -1
- package/storybook-static/assets/{MagnetLines.stories-Boc9ZU8z.js → MagnetLines.stories-QkoPnGKj.js} +1 -1
- package/storybook-static/assets/{Masonry.stories-BZUo993q.js → Masonry.stories-CiGXRf3O.js} +1 -1
- package/storybook-static/assets/{MetaBalls.stories-DXYO9QHZ.js → MetaBalls.stories-3l0pQiKn.js} +1 -1
- package/storybook-static/assets/{MetallicPaint.stories-BSFAAtcE.js → MetallicPaint.stories-DgZJfMr5.js} +1 -1
- package/storybook-static/assets/MoodChart-Cu7ntVIE.css +1 -0
- package/storybook-static/assets/MoodChart.stories-UXaQU4EG.js +40 -0
- package/storybook-static/assets/{MotionConfigContext-cqdgvETc.js → MotionConfigContext-BWFxZgdN.js} +1 -1
- package/storybook-static/assets/{Navbar.stories-TgnWvuhq.js → Navbar.stories-C_Zyfhqr.js} +1 -1
- package/storybook-static/assets/{Noise.stories-COwmBW-s.js → Noise.stories-Cg7CucgT.js} +1 -1
- package/storybook-static/assets/{NumberStepper-BLyqaAZY.js → NumberStepper-DpOu7xmF.js} +1 -1
- package/storybook-static/assets/{NumberStepper.stories-BH--ymZD.js → NumberStepper.stories-CvG9oY5Q.js} +1 -1
- package/storybook-static/assets/{Orb.stories-BouXCqts.js → Orb.stories-BR92tgyW.js} +1 -1
- package/storybook-static/assets/{OrbitImages.stories-DwPbtwmh.js → OrbitImages.stories-D_-2HcAd.js} +1 -1
- package/storybook-static/assets/{PieChart.stories-ThEQ6o4T.js → PieChart.stories-CjtEiSJY.js} +1 -1
- package/storybook-static/assets/{PixelBlast.stories-CXLoQgLR.js → PixelBlast.stories-B6FXuNf4.js} +1 -1
- package/storybook-static/assets/{PixelCard.stories-CwsDhfSZ.js → PixelCard.stories-Dq4CP2Qj.js} +1 -1
- package/storybook-static/assets/{PixelSnow.stories-B78--r-5.js → PixelSnow.stories-BoQSOear.js} +1 -1
- package/storybook-static/assets/{PixelTransition.stories-CQRCg5Dh.js → PixelTransition.stories-CtQQ070z.js} +1 -1
- package/storybook-static/assets/{Plasma.stories-C4nsJ8m0.js → Plasma.stories-D-421jzZ.js} +1 -1
- package/storybook-static/assets/{Prism.stories-CYyW2MhJ.js → Prism.stories-BuibpwdU.js} +1 -1
- package/storybook-static/assets/{PrismaticBurst.stories-DPAVXNq-.js → PrismaticBurst.stories-CwYzdBlM.js} +1 -1
- package/storybook-static/assets/{ProfileCard.stories-GQnHhvBL.js → ProfileCard.stories-Dj4oJCxw.js} +1 -1
- package/storybook-static/assets/QuantifiableHabitsChart-DK3Cp5zY.css +1 -0
- package/storybook-static/assets/QuantifiableHabitsChart.stories-ZY1-WQ1g.js +125 -0
- package/storybook-static/assets/{Radar.stories-BiS3kVBK.js → Radar.stories-DxNmoYV3.js} +1 -1
- package/storybook-static/assets/{RecurrencePicker.stories-AkU87_0U.js → RecurrencePicker.stories-BAicSNhQ.js} +1 -1
- package/storybook-static/assets/{Ribbons.stories-OMZCYkS-.js → Ribbons.stories-usN1kCyI.js} +1 -1
- package/storybook-static/assets/{RippleGrid.stories-f-knJ_dC.js → RippleGrid.stories-nTLGHPV6.js} +1 -1
- package/storybook-static/assets/{RotatingText.stories-Cs5g6eNr.js → RotatingText.stories-8CShs_WJ.js} +1 -1
- package/storybook-static/assets/{ScrollFloat.stories-BkinOlWM.js → ScrollFloat.stories-Dp9YyvHK.js} +1 -1
- package/storybook-static/assets/{ScrollReveal.stories-DAawYSvS.js → ScrollReveal.stories-Dy4JUrqi.js} +1 -1
- package/storybook-static/assets/{ScrollVelocity.stories-DDKvgeRt.js → ScrollVelocity.stories-CUf-OHNp.js} +1 -1
- package/storybook-static/assets/{SearchBar.stories-DontnpSK.js → SearchBar.stories-BKfuRnlK.js} +1 -1
- package/storybook-static/assets/{SearchableDropdown-DOvqXBhT.js → SearchableDropdown-Ctnh_PVq.js} +1 -1
- package/storybook-static/assets/{SearchableDropdown.stories-Ct5wfF8Q.js → SearchableDropdown.stories-1d-goy0G.js} +1 -1
- package/storybook-static/assets/{SelectInput-DTobf0zh.js → SelectInput-C94OHQIl.js} +1 -1
- package/storybook-static/assets/{SelectInput.stories-D65m3xT6.js → SelectInput.stories-DPV5iKcs.js} +1 -1
- package/storybook-static/assets/{ShapeBlur.stories-C2QcmUF6.js → ShapeBlur.stories-CdASN24B.js} +1 -1
- package/storybook-static/assets/{ShapeGrid.stories-B4UpvWuC.js → ShapeGrid.stories-Duzq6qpW.js} +1 -1
- package/storybook-static/assets/{ShinyText.stories-Dx9uB0Ge.js → ShinyText.stories-Dgqps29_.js} +1 -1
- package/storybook-static/assets/{Silk.stories-OqHp151A.js → Silk.stories-DWnaBFZp.js} +1 -1
- package/storybook-static/assets/{SleepChart.stories-EvXrufhV.js → SleepChart.stories-C1n9aK7B.js} +1 -1
- package/storybook-static/assets/{Slider-B49_dS8L.js → Slider-C69QbwVa.js} +1 -1
- package/storybook-static/assets/{Slider.stories-C-WUPilU.js → Slider.stories-DDhTLU2g.js} +1 -1
- package/storybook-static/assets/{SoftAurora.stories-D3FuYkw4.js → SoftAurora.stories-C5OSOqjj.js} +1 -1
- package/storybook-static/assets/{SoundDemo.stories-BgQu6ftq.js → SoundDemo.stories-sb4V9P8H.js} +1 -1
- package/storybook-static/assets/{SplashCursor.stories-DWJpmVM0.js → SplashCursor.stories-PeC0-y78.js} +1 -1
- package/storybook-static/assets/{SpotlightCard.stories-B59YbgvD.js → SpotlightCard.stories-BCCdQhIJ.js} +1 -1
- package/storybook-static/assets/{Stack.stories-C0txqVGC.js → Stack.stories-DJekNsdu.js} +1 -1
- package/storybook-static/assets/{StaggeredMenu.stories-B-EDB5Rd.js → StaggeredMenu.stories-BU-AxZRv.js} +1 -1
- package/storybook-static/assets/{StarBorder.stories-WozhQkt_.js → StarBorder.stories-Dw9LDvMk.js} +1 -1
- package/storybook-static/assets/{SunburstChart.stories-B-FtFWzz.js → SunburstChart.stories-CE4d4r8-.js} +1 -1
- package/storybook-static/assets/{Table.stories-CnuV2Phn.js → Table.stories-IhtnLV5J.js} +1 -1
- package/storybook-static/assets/{Tabs.stories-C6Ixt85v.js → Tabs.stories-F_Q4-xiR.js} +1 -1
- package/storybook-static/assets/{TargetCursor.stories-D05-aNwu.js → TargetCursor.stories-B-Tl1DdA.js} +1 -1
- package/storybook-static/assets/{TextArea-DtQ-6kae.js → TextArea-Bb148SaD.js} +1 -1
- package/storybook-static/assets/{TextArea.stories-DQa3wP_Z.js → TextArea.stories-5-dHoUPL.js} +1 -1
- package/storybook-static/assets/{TextCursor.stories-BJTUllxj.js → TextCursor.stories-O-EIx7Nu.js} +1 -1
- package/storybook-static/assets/{TextInput-ohzWFFuq.js → TextInput-BQ78htqq.js} +1 -1
- package/storybook-static/assets/{TextInput.stories-CIFhZSxb.js → TextInput.stories-Bjjfu_5O.js} +1 -1
- package/storybook-static/assets/{TextPressure.stories-BDbHdoj0.js → TextPressure.stories-BvXWcayn.js} +1 -1
- package/storybook-static/assets/{TextType.stories-Dpp0yM1F.js → TextType.stories-DL92625c.js} +1 -1
- package/storybook-static/assets/{ThemeSwitcher.stories-Ch3eX-Q4.js → ThemeSwitcher.stories-BUUyb12v.js} +1 -1
- package/storybook-static/assets/{Threads.stories-BYlFBArL.js → Threads.stories-C_LDNe0R.js} +1 -1
- package/storybook-static/assets/{TimeInput.stories-ChJb0EcK.js → TimeInput.stories-Tj2LrCtN.js} +1 -1
- package/storybook-static/assets/{Toggle-Er5JazC4.js → Toggle-BgFXbOVy.js} +1 -1
- package/storybook-static/assets/{Toggle.stories-ClF63HwM.js → Toggle.stories-voNeGmVh.js} +1 -1
- package/storybook-static/assets/{ToggleButton-BgyTdkGs.js → ToggleButton-CQLM9369.js} +1 -1
- package/storybook-static/assets/{ToggleButton.stories-D2YQjBPU.js → ToggleButton.stories-CLdG94qe.js} +1 -1
- package/storybook-static/assets/{TrueFocus.stories-B4Qoh1Ek.js → TrueFocus.stories-CEC_mKz7.js} +1 -1
- package/storybook-static/assets/{VariableProximity.stories-DLzbqQ-q.js → VariableProximity.stories-CoB68xuF.js} +1 -1
- package/storybook-static/assets/{Waves.stories-OX3VExu5.js → Waves.stories-Cpn9tv6C.js} +1 -1
- package/storybook-static/assets/{band-BYy0bT_S.js → band-Cq25-Mh7.js} +1 -1
- package/storybook-static/assets/{calendar-3_ekvfk7.js → calendar-B90N4a5g.js} +1 -1
- package/storybook-static/assets/{chart-column-B99l8ykF.js → chart-column-Qn3KVlVm.js} +1 -1
- package/storybook-static/assets/{check-MGuZ-vQU.js → check-BdgefYk0.js} +1 -1
- package/storybook-static/assets/{chevron-down-dMiwJ2Fa.js → chevron-down-rAxqUK0f.js} +1 -1
- package/storybook-static/assets/{chevron-right-BGiouhAQ.js → chevron-right-BQYLOSGD.js} +1 -1
- package/storybook-static/assets/client-Chg03P_H.js +1 -0
- package/storybook-static/assets/{createLucideIcon-He700CuR.js → createLucideIcon-CanksmPJ.js} +1 -1
- package/storybook-static/assets/{download-BKSOfxpe.js → download-BoPCl2gy.js} +1 -1
- package/storybook-static/assets/{folder-LestSoCO.js → folder-B0zTnOfo.js} +1 -1
- package/storybook-static/assets/{iconBase-zJDHXTd9.js → iconBase-jWAd7fUM.js} +1 -1
- package/storybook-static/assets/{iframe-D8qqeeGJ.js → iframe-BTbLpgFM.js} +3 -3
- package/storybook-static/assets/{index-L49vE-ah.js → index-8orVvKs3.js} +1 -1
- package/storybook-static/assets/{index-C-VnSuS_.js → index-D9A3yXPP.js} +1 -1
- package/storybook-static/assets/{index-CJyQ6SwC.js → index-Dnnp7mc9.js} +1 -1
- package/storybook-static/assets/{linear-DhrdWH9x.js → linear-9syG7RFK.js} +1 -1
- package/storybook-static/assets/monotone-B6BFTqpP.js +1 -0
- package/storybook-static/assets/{proxy-1W9qMPm5.js → proxy-DHiOkbBq.js} +1 -1
- package/storybook-static/assets/{react-18-BVZJZnf4.js → react-18-RyhzFaVZ.js} +1 -1
- package/storybook-static/assets/{react-three-fiber.esm-Ccm-0uhF.js → react-three-fiber.esm-DyDueq1t.js} +1 -1
- package/storybook-static/assets/{search-BgqlFsxE.js → search-C2F8ucQI.js} +1 -1
- package/storybook-static/assets/{settings-BXbuThqT.js → settings-B4F4kfld.js} +1 -1
- package/storybook-static/assets/{sun-DzhjLvFL.js → sun-CbGl2Vwt.js} +1 -1
- package/storybook-static/assets/{transform-QOf3tDlt.js → transform-v7fcW145.js} +1 -1
- package/storybook-static/assets/{trash-2-SM79_8re.js → trash-2-CNCXZ0wH.js} +1 -1
- package/storybook-static/assets/{use-animation-frame-sEnyIjfp.js → use-animation-frame-DY1VsI4g.js} +1 -1
- package/storybook-static/assets/{use-in-view-Z5TSuHio.js → use-in-view-Cawxv_UZ.js} +1 -1
- package/storybook-static/assets/{use-motion-value-DQT5SPdA.js → use-motion-value-BKT0PPfP.js} +1 -1
- package/storybook-static/assets/{use-spring-DwAy_p1Y.js → use-spring-CDH8BIpQ.js} +1 -1
- package/storybook-static/assets/{use-transform-C2Oyk60n.js → use-transform-Bj0aHFrd.js} +1 -1
- package/storybook-static/assets/{useSound-CUVgLyES.js → useSound-OlKqgJtL.js} +1 -1
- package/storybook-static/assets/{users-CeDtcuCx.js → users-Oy8C2tFC.js} +1 -1
- package/storybook-static/assets/{x-BoXEIqKt.js → x-CR0CkM0G.js} +1 -1
- package/storybook-static/iframe.html +1 -1
- package/storybook-static/project.json +1 -1
- package/storybook-static/assets/MoodChart-ba3aY_qF.css +0 -1
- package/storybook-static/assets/MoodChart.stories-CnSQKAhp.js +0 -40
- package/storybook-static/assets/QuantifiableHabitsChart-JSu1fVKW.css +0 -1
- package/storybook-static/assets/QuantifiableHabitsChart.stories-DQybQV3G.js +0 -125
- package/storybook-static/assets/client-AUYJ3-v0.js +0 -1
- package/storybook-static/assets/monotone-DouxdRGd.js +0 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { motion, AnimatePresence, useDragControls, useMotionValue, useTransform, animate, LayoutGroup } from 'framer-motion';
|
|
3
|
-
import React, { useRef, useEffect, useCallback, useState, useMemo, createContext, useContext, Fragment as Fragment$1, memo } from 'react';
|
|
3
|
+
import React, { useRef, useEffect, useCallback, useState, useMemo, createContext, useContext, Fragment as Fragment$1, useId, memo } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { X, Calendar as Calendar$1, ChevronDown, Search, Check, Edit, Folder, Users, Book, MessageSquare, UserPlus, Sun, Moon, Info, Github, SquareKanban, ChevronRight, Plus, Loader2, AlertTriangle, Play, Brain, FolderSearch, FilePlus, Pencil, FileText, Terminal, Trash2, GripVertical, Download, Menu, BarChart3, CalendarRange, CalendarDays, Divide, Sigma, ScatterChart, ChevronLeft, Maximize, Share2, Pause } from 'lucide-react';
|
|
6
6
|
import dayjs from 'dayjs';
|
|
@@ -3936,22 +3936,27 @@ const Navbar = ({ items, logo, onItemClick, variant = 'sidebar', isMobile = fals
|
|
|
3936
3936
|
return (jsx(Fragment, { children: jsx(motion.nav, { className: `${styles.navbar} ${styles[`navbar${variant.charAt(0).toUpperCase() + variant.slice(1)}`]} ${className}`, "data-colored-icons": coloredIconsAttr, initial: variant === 'sidebar' ? { x: -300 } : { opacity: 0 }, animate: variant === 'sidebar' ? { x: 0 } : { opacity: 1 }, transition: { type: 'spring', stiffness: 300, damping: 30 }, children: navContent }) }));
|
|
3937
3937
|
};
|
|
3938
3938
|
|
|
3939
|
-
var styles$9 = {"container":"MoodChart-module_container__MB1Vr","chart":"MoodChart-module_chart__4-spu","gridLine":"MoodChart-module_gridLine__YJpZ8","line":"MoodChart-module_line__LfeUX","xAxis":"MoodChart-module_xAxis__SeIWG","yAxis":"MoodChart-module_yAxis__sRjjE","dataPoint":"MoodChart-module_dataPoint__112P1","tooltip":"MoodChart-module_tooltip__vW59y","tooltipHeader":"MoodChart-module_tooltipHeader__U7yvN","tooltipDate":"MoodChart-module_tooltipDate__6Jjeu","tooltipRating":"MoodChart-module_tooltipRating__5A2Yx","ratingValue":"MoodChart-module_ratingValue__7Gpfx","ratingMax":"MoodChart-module_ratingMax__10WcJ","tooltipTags":"MoodChart-module_tooltipTags__FUQrU","tag":"MoodChart-module_tag__QhCh5","tooltipComment":"MoodChart-module_tooltipComment__6driJ"};
|
|
3939
|
+
var styles$9 = {"container":"MoodChart-module_container__MB1Vr","chart":"MoodChart-module_chart__4-spu","gridLine":"MoodChart-module_gridLine__YJpZ8","line":"MoodChart-module_line__LfeUX","area":"MoodChart-module_area__a4BFM","areaStopTop":"MoodChart-module_areaStopTop__KEp49","areaStopBottom":"MoodChart-module_areaStopBottom__cp7Gn","crosshair":"MoodChart-module_crosshair__PHLfW","halo":"MoodChart-module_halo__YQB-7","overlay":"MoodChart-module_overlay__ZwuHi","xAxis":"MoodChart-module_xAxis__SeIWG","yAxis":"MoodChart-module_yAxis__sRjjE","dataPoint":"MoodChart-module_dataPoint__112P1","tooltip":"MoodChart-module_tooltip__vW59y","tooltipHeader":"MoodChart-module_tooltipHeader__U7yvN","tooltipDate":"MoodChart-module_tooltipDate__6Jjeu","tooltipRating":"MoodChart-module_tooltipRating__5A2Yx","ratingValue":"MoodChart-module_ratingValue__7Gpfx","ratingMax":"MoodChart-module_ratingMax__10WcJ","tooltipTags":"MoodChart-module_tooltipTags__FUQrU","tag":"MoodChart-module_tag__QhCh5","tooltipComment":"MoodChart-module_tooltipComment__6driJ"};
|
|
3940
3940
|
|
|
3941
3941
|
const MoodChart = ({ moodData, width = 800, height = 400 }) => {
|
|
3942
3942
|
const svgRef = useRef(null);
|
|
3943
3943
|
const containerRef = useRef(null);
|
|
3944
3944
|
const tooltipRef = useRef(null);
|
|
3945
|
+
const isHoveringTooltipRef = useRef(false);
|
|
3946
|
+
const rawId = useId();
|
|
3947
|
+
const gradientId = useMemo(() => `mood-area-${rawId.replace(/[^a-zA-Z0-9]/g, '_')}`, [rawId]);
|
|
3945
3948
|
const [selectedMood, setSelectedMood] = useState(null);
|
|
3946
3949
|
const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 });
|
|
3947
3950
|
const [isHoveringTooltip, setIsHoveringTooltip] = useState(false);
|
|
3951
|
+
useEffect(() => {
|
|
3952
|
+
isHoveringTooltipRef.current = isHoveringTooltip;
|
|
3953
|
+
}, [isHoveringTooltip]);
|
|
3948
3954
|
const margin = useMemo(() => ({ top: 20, right: 20, bottom: 50, left: 40 }), []);
|
|
3949
3955
|
const chartWidth = width - margin.left - margin.right;
|
|
3950
3956
|
const chartHeight = height - margin.top - margin.bottom;
|
|
3951
3957
|
const processedData = useMemo(() => {
|
|
3952
3958
|
return moodData
|
|
3953
3959
|
.map(mood => {
|
|
3954
|
-
// Parse comma-separated tags
|
|
3955
3960
|
const tags = mood.tag
|
|
3956
3961
|
? mood.tag.split(',').map(t => t.trim()).filter(t => t)
|
|
3957
3962
|
: [];
|
|
@@ -3963,7 +3968,8 @@ const MoodChart = ({ moodData, width = 800, height = 400 }) => {
|
|
|
3963
3968
|
};
|
|
3964
3969
|
})
|
|
3965
3970
|
.filter(mood => !isNaN(mood.date.getTime()) &&
|
|
3966
|
-
isFinite(mood.rating))
|
|
3971
|
+
isFinite(mood.rating))
|
|
3972
|
+
.sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
3967
3973
|
}, [moodData]);
|
|
3968
3974
|
const colorScale = useMemo(() => {
|
|
3969
3975
|
return d3.scaleLinear()
|
|
@@ -3976,6 +3982,13 @@ const MoodChart = ({ moodData, width = 800, height = 400 }) => {
|
|
|
3976
3982
|
return;
|
|
3977
3983
|
const svg = d3.select(svgRef.current);
|
|
3978
3984
|
svg.selectAll('*').remove();
|
|
3985
|
+
const defs = svg.append('defs');
|
|
3986
|
+
const gradient = defs.append('linearGradient')
|
|
3987
|
+
.attr('id', gradientId)
|
|
3988
|
+
.attr('x1', '0%').attr('y1', '0%')
|
|
3989
|
+
.attr('x2', '0%').attr('y2', '100%');
|
|
3990
|
+
gradient.append('stop').attr('offset', '0%').attr('class', styles$9.areaStopTop);
|
|
3991
|
+
gradient.append('stop').attr('offset', '100%').attr('class', styles$9.areaStopBottom);
|
|
3979
3992
|
const g = svg.append('g')
|
|
3980
3993
|
.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
3981
3994
|
const extentDates = d3.extent(processedData, d => d.date);
|
|
@@ -3991,6 +4004,12 @@ const MoodChart = ({ moodData, width = 800, height = 400 }) => {
|
|
|
3991
4004
|
.x(d => xScale(d.date))
|
|
3992
4005
|
.y(d => yScale(d.rating))
|
|
3993
4006
|
.curve(d3.curveMonotoneX);
|
|
4007
|
+
const area = d3.area()
|
|
4008
|
+
.x(d => xScale(d.date))
|
|
4009
|
+
.y0(chartHeight)
|
|
4010
|
+
.y1(d => yScale(d.rating))
|
|
4011
|
+
.curve(d3.curveMonotoneX);
|
|
4012
|
+
// Grid lines (fade in)
|
|
3994
4013
|
g.selectAll('.grid-line-y')
|
|
3995
4014
|
.data(yScale.ticks(5))
|
|
3996
4015
|
.enter().append('line')
|
|
@@ -3998,70 +4017,210 @@ const MoodChart = ({ moodData, width = 800, height = 400 }) => {
|
|
|
3998
4017
|
.attr('x1', 0)
|
|
3999
4018
|
.attr('y1', d => yScale(d))
|
|
4000
4019
|
.attr('x2', chartWidth)
|
|
4001
|
-
.attr('y2', d => yScale(d))
|
|
4002
|
-
|
|
4003
|
-
.
|
|
4004
|
-
.
|
|
4005
|
-
.
|
|
4020
|
+
.attr('y2', d => yScale(d))
|
|
4021
|
+
.style('opacity', 0)
|
|
4022
|
+
.transition()
|
|
4023
|
+
.duration(500)
|
|
4024
|
+
.ease(d3.easeCubicOut)
|
|
4025
|
+
.style('opacity', 1);
|
|
4026
|
+
// Axes (fade in)
|
|
4006
4027
|
g.append('g')
|
|
4007
4028
|
.attr('class', styles$9.xAxis)
|
|
4008
4029
|
.attr('transform', `translate(0,${chartHeight})`)
|
|
4030
|
+
.style('opacity', 0)
|
|
4009
4031
|
.call(d3.axisBottom(xScale)
|
|
4010
|
-
.tickFormat(d => d3.timeFormat('%m/%d')(d)))
|
|
4032
|
+
.tickFormat(d => d3.timeFormat('%m/%d')(d)))
|
|
4033
|
+
.transition()
|
|
4034
|
+
.duration(600)
|
|
4035
|
+
.delay(100)
|
|
4036
|
+
.style('opacity', 1);
|
|
4011
4037
|
g.append('g')
|
|
4012
4038
|
.attr('class', styles$9.yAxis)
|
|
4013
|
-
.
|
|
4014
|
-
|
|
4039
|
+
.style('opacity', 0)
|
|
4040
|
+
.call(d3.axisLeft(yScale))
|
|
4041
|
+
.transition()
|
|
4042
|
+
.duration(600)
|
|
4043
|
+
.delay(100)
|
|
4044
|
+
.style('opacity', 1);
|
|
4045
|
+
// Area fill (fades in after line draws past it)
|
|
4046
|
+
const areaPath = g.append('path')
|
|
4047
|
+
.datum(processedData)
|
|
4048
|
+
.attr('class', styles$9.area)
|
|
4049
|
+
.attr('fill', `url(#${gradientId})`)
|
|
4050
|
+
.attr('d', area)
|
|
4051
|
+
.style('opacity', 0);
|
|
4052
|
+
// Line (draw-in animation via stroke-dasharray)
|
|
4053
|
+
const linePath = g.append('path')
|
|
4054
|
+
.datum(processedData)
|
|
4055
|
+
.attr('class', styles$9.line)
|
|
4056
|
+
.attr('d', line);
|
|
4057
|
+
const lineNode = linePath.node();
|
|
4058
|
+
const totalLength = lineNode && typeof lineNode.getTotalLength === 'function'
|
|
4059
|
+
? lineNode.getTotalLength()
|
|
4060
|
+
: 0;
|
|
4061
|
+
const lineDuration = 1200;
|
|
4062
|
+
if (totalLength > 0) {
|
|
4063
|
+
linePath
|
|
4064
|
+
.attr('stroke-dasharray', `${totalLength} ${totalLength}`)
|
|
4065
|
+
.attr('stroke-dashoffset', totalLength)
|
|
4066
|
+
.transition()
|
|
4067
|
+
.duration(lineDuration)
|
|
4068
|
+
.ease(d3.easeCubicOut)
|
|
4069
|
+
.attr('stroke-dashoffset', 0)
|
|
4070
|
+
.on('end', () => {
|
|
4071
|
+
linePath.attr('stroke-dasharray', null);
|
|
4072
|
+
});
|
|
4073
|
+
}
|
|
4074
|
+
areaPath
|
|
4075
|
+
.transition()
|
|
4076
|
+
.delay(lineDuration * 0.4)
|
|
4077
|
+
.duration(800)
|
|
4078
|
+
.ease(d3.easeCubicOut)
|
|
4079
|
+
.style('opacity', 1);
|
|
4080
|
+
// Crosshair (vertical guide on hover)
|
|
4081
|
+
const crosshair = g.append('line')
|
|
4082
|
+
.attr('class', styles$9.crosshair)
|
|
4083
|
+
.attr('y1', 0)
|
|
4084
|
+
.attr('y2', chartHeight)
|
|
4085
|
+
.style('opacity', 0)
|
|
4086
|
+
.style('pointer-events', 'none');
|
|
4087
|
+
// Pulsing halo behind active point
|
|
4088
|
+
const halo = g.append('circle')
|
|
4089
|
+
.attr('class', styles$9.halo)
|
|
4090
|
+
.attr('r', 0)
|
|
4091
|
+
.style('opacity', 0)
|
|
4092
|
+
.style('pointer-events', 'none');
|
|
4093
|
+
// Data points (staggered entrance keyed to line draw)
|
|
4094
|
+
const minX = xScale(extentDates[0]);
|
|
4095
|
+
const maxX = xScale(extentDates[1]);
|
|
4096
|
+
const xRange = Math.max(1, maxX - minX);
|
|
4097
|
+
const pointSel = g.selectAll('.mood-circle')
|
|
4015
4098
|
.data(processedData)
|
|
4016
4099
|
.enter().append('circle')
|
|
4017
4100
|
.attr('class', styles$9.dataPoint)
|
|
4018
4101
|
.attr('cx', d => xScale(d.date))
|
|
4019
4102
|
.attr('cy', d => yScale(d.rating))
|
|
4020
|
-
.attr('r',
|
|
4103
|
+
.attr('r', 0)
|
|
4021
4104
|
.attr('fill', d => colorScale(d.rating))
|
|
4022
|
-
.style('
|
|
4023
|
-
.
|
|
4105
|
+
.style('opacity', 0)
|
|
4106
|
+
.style('pointer-events', 'none');
|
|
4107
|
+
pointSel.transition()
|
|
4108
|
+
.delay(d => 100 + lineDuration * ((xScale(d.date) - minX) / xRange))
|
|
4109
|
+
.duration(380)
|
|
4110
|
+
.ease(d3.easeBackOut.overshoot(2))
|
|
4111
|
+
.attr('r', 5)
|
|
4112
|
+
.style('opacity', 1);
|
|
4113
|
+
const pointNodes = pointSel.nodes();
|
|
4114
|
+
// Active state lives in mutable refs (closure over redraw)
|
|
4115
|
+
let activeIndex = null;
|
|
4116
|
+
const startHaloPulse = (cx, cy, fill) => {
|
|
4117
|
+
halo
|
|
4118
|
+
.interrupt('halo-pulse')
|
|
4119
|
+
.interrupt('halo-out')
|
|
4120
|
+
.attr('cx', cx)
|
|
4121
|
+
.attr('cy', cy)
|
|
4122
|
+
.attr('fill', fill);
|
|
4123
|
+
const pulseOnce = () => {
|
|
4124
|
+
halo
|
|
4125
|
+
.attr('r', 5)
|
|
4126
|
+
.style('opacity', 0.55)
|
|
4127
|
+
.transition('halo-pulse')
|
|
4128
|
+
.duration(900)
|
|
4129
|
+
.ease(d3.easeCubicOut)
|
|
4130
|
+
.attr('r', 20)
|
|
4131
|
+
.style('opacity', 0)
|
|
4132
|
+
.on('end', pulseOnce);
|
|
4133
|
+
};
|
|
4134
|
+
pulseOnce();
|
|
4135
|
+
};
|
|
4136
|
+
const stopHaloPulse = () => {
|
|
4137
|
+
halo
|
|
4138
|
+
.interrupt('halo-pulse')
|
|
4139
|
+
.transition('halo-out')
|
|
4140
|
+
.duration(180)
|
|
4141
|
+
.style('opacity', 0)
|
|
4142
|
+
.attr('r', 0);
|
|
4143
|
+
};
|
|
4144
|
+
const setActive = (idx, clientX, clientY) => {
|
|
4145
|
+
const d = processedData[idx];
|
|
4146
|
+
if (!d)
|
|
4147
|
+
return;
|
|
4148
|
+
const cx = xScale(d.date);
|
|
4149
|
+
const cy = yScale(d.rating);
|
|
4024
4150
|
const rect = svgRef.current?.getBoundingClientRect();
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
let
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
if (x + estimatedTooltipWidth > chartWidth + margin.left - rightEdgeBuffer) {
|
|
4033
|
-
// Position tooltip to the left of the cursor, but keep it close
|
|
4034
|
-
x = event.clientX - rect.left - estimatedTooltipWidth - 10;
|
|
4035
|
-
// Ensure it doesn't go off the left edge
|
|
4036
|
-
if (x < 0) {
|
|
4151
|
+
if (rect) {
|
|
4152
|
+
let x = clientX - rect.left + 12;
|
|
4153
|
+
let y = clientY - rect.top - 12;
|
|
4154
|
+
const estW = 280;
|
|
4155
|
+
if (x + estW > chartWidth + margin.left - 20) {
|
|
4156
|
+
x = clientX - rect.left - estW - 12;
|
|
4157
|
+
if (x < 0)
|
|
4037
4158
|
x = 10;
|
|
4038
|
-
}
|
|
4039
4159
|
}
|
|
4040
|
-
|
|
4041
|
-
if (y < 0) {
|
|
4160
|
+
if (y < 0)
|
|
4042
4161
|
y = 10;
|
|
4043
|
-
}
|
|
4044
4162
|
setTooltipPosition({ x, y });
|
|
4045
4163
|
}
|
|
4046
4164
|
setSelectedMood(d);
|
|
4047
|
-
|
|
4048
|
-
.
|
|
4049
|
-
.
|
|
4050
|
-
.
|
|
4165
|
+
crosshair
|
|
4166
|
+
.attr('x1', cx).attr('x2', cx)
|
|
4167
|
+
.interrupt('crosshair-fade')
|
|
4168
|
+
.transition('crosshair-fade')
|
|
4169
|
+
.duration(120)
|
|
4170
|
+
.style('opacity', 1);
|
|
4171
|
+
if (activeIndex !== idx) {
|
|
4172
|
+
if (activeIndex !== null) {
|
|
4173
|
+
d3.select(pointNodes[activeIndex])
|
|
4174
|
+
.transition('point-scale')
|
|
4175
|
+
.duration(180)
|
|
4176
|
+
.ease(d3.easeCubicOut)
|
|
4177
|
+
.attr('r', 5);
|
|
4178
|
+
}
|
|
4179
|
+
d3.select(pointNodes[idx])
|
|
4180
|
+
.transition('point-scale')
|
|
4181
|
+
.duration(180)
|
|
4182
|
+
.ease(d3.easeBackOut.overshoot(2))
|
|
4183
|
+
.attr('r', 8);
|
|
4184
|
+
startHaloPulse(cx, cy, colorScale(d.rating));
|
|
4185
|
+
activeIndex = idx;
|
|
4186
|
+
}
|
|
4187
|
+
};
|
|
4188
|
+
const clearActive = () => {
|
|
4189
|
+
crosshair
|
|
4190
|
+
.interrupt('crosshair-fade')
|
|
4191
|
+
.transition('crosshair-fade')
|
|
4192
|
+
.duration(180)
|
|
4193
|
+
.style('opacity', 0);
|
|
4194
|
+
if (activeIndex !== null) {
|
|
4195
|
+
d3.select(pointNodes[activeIndex])
|
|
4196
|
+
.transition('point-scale')
|
|
4197
|
+
.duration(180)
|
|
4198
|
+
.attr('r', 5);
|
|
4199
|
+
activeIndex = null;
|
|
4200
|
+
}
|
|
4201
|
+
stopHaloPulse();
|
|
4202
|
+
setSelectedMood(null);
|
|
4203
|
+
};
|
|
4204
|
+
// Overlay captures mouse for nearest-point lookup (rendered last so it sits on top)
|
|
4205
|
+
const bisect = d3.bisector(d => d.date).center;
|
|
4206
|
+
g.append('rect')
|
|
4207
|
+
.attr('class', styles$9.overlay)
|
|
4208
|
+
.attr('width', chartWidth)
|
|
4209
|
+
.attr('height', chartHeight)
|
|
4210
|
+
.on('mousemove', function (event) {
|
|
4211
|
+
const [mx] = d3.pointer(event, this);
|
|
4212
|
+
const xDate = xScale.invert(mx);
|
|
4213
|
+
const idx = Math.max(0, Math.min(processedData.length - 1, bisect(processedData, xDate)));
|
|
4214
|
+
setActive(idx, event.clientX, event.clientY);
|
|
4051
4215
|
})
|
|
4052
|
-
.on('mouseleave', (
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
setSelectedMood(null);
|
|
4216
|
+
.on('mouseleave', () => {
|
|
4217
|
+
window.setTimeout(() => {
|
|
4218
|
+
if (!isHoveringTooltipRef.current) {
|
|
4219
|
+
clearActive();
|
|
4057
4220
|
}
|
|
4058
|
-
},
|
|
4059
|
-
d3.select(event.currentTarget)
|
|
4060
|
-
.transition()
|
|
4061
|
-
.duration(200)
|
|
4062
|
-
.attr('r', 5);
|
|
4221
|
+
}, 220);
|
|
4063
4222
|
});
|
|
4064
|
-
}, [processedData, chartWidth, chartHeight, colorScale, margin,
|
|
4223
|
+
}, [processedData, chartWidth, chartHeight, colorScale, margin, gradientId]);
|
|
4065
4224
|
// Clean up tooltip on unmount
|
|
4066
4225
|
useEffect(() => {
|
|
4067
4226
|
return () => {
|
|
@@ -4080,9 +4239,8 @@ const MoodChart = ({ moodData, width = 800, height = 400 }) => {
|
|
|
4080
4239
|
}, children: [jsxs("div", { className: styles$9.tooltipHeader, children: [jsx("div", { className: styles$9.tooltipDate, children: selectedMood.date.toLocaleDateString() }), jsxs("div", { className: styles$9.tooltipRating, children: [jsx("span", { className: styles$9.ratingValue, children: selectedMood.rating }), jsx("span", { className: styles$9.ratingMax, children: "/10" })] })] }), selectedMood.tags.length > 0 && (jsx("div", { className: styles$9.tooltipTags, children: selectedMood.tags.map((tag, index) => (jsx("span", { className: styles$9.tag, children: tag }, index))) })), selectedMood.comment && (jsx("div", { className: styles$9.tooltipComment, children: selectedMood.comment }))] }))] }));
|
|
4081
4240
|
};
|
|
4082
4241
|
|
|
4083
|
-
var styles$8 = {"container":"QuantifiableHabitsChart-module_container__X5SBp","controls":"QuantifiableHabitsChart-module_controls__O-ObQ","viewToggle":"QuantifiableHabitsChart-module_viewToggle__24hKA","viewButton":"QuantifiableHabitsChart-module_viewButton__WFU6j","active":"QuantifiableHabitsChart-module_active__Pjqy9","viewIcon":"QuantifiableHabitsChart-module_viewIcon__b2mfk","viewLabel":"QuantifiableHabitsChart-module_viewLabel__9MjCU","aggregationToggle":"QuantifiableHabitsChart-module_aggregationToggle__WnxwB","aggregationButton":"QuantifiableHabitsChart-module_aggregationButton__sGCxX","aggregationIcon":"QuantifiableHabitsChart-module_aggregationIcon__HIaEf","aggregationLabel":"QuantifiableHabitsChart-module_aggregationLabel__Yc-S-","legend":"QuantifiableHabitsChart-module_legend__3Ki7c","compactLegend":"QuantifiableHabitsChart-module_compactLegend__jMpLB","legendItem":"QuantifiableHabitsChart-module_legendItem__Zl7fz","legendEmoji":"QuantifiableHabitsChart-module_legendEmoji__HG9CZ","legendLabel":"QuantifiableHabitsChart-module_legendLabel__H3oFL","inactive":"QuantifiableHabitsChart-module_inactive__TzZC-","legendColor":"QuantifiableHabitsChart-module_legendColor__zbPoV","chart":"QuantifiableHabitsChart-module_chart__FMeA-","gridLine":"QuantifiableHabitsChart-module_gridLine__CTNIq","line":"QuantifiableHabitsChart-module_line__CpYip","xAxis":"QuantifiableHabitsChart-module_xAxis__lbgBG","yAxis":"QuantifiableHabitsChart-module_yAxis__Y6WeV","dataPoint":"QuantifiableHabitsChart-module_dataPoint__s8UMX","tooltip":"QuantifiableHabitsChart-module_tooltip__Fay8N","visible":"QuantifiableHabitsChart-module_visible__-KSJq","tooltipHeader":"QuantifiableHabitsChart-module_tooltipHeader__7Q2up","tooltipEmoji":"QuantifiableHabitsChart-module_tooltipEmoji__atV3T","tooltipDot":"QuantifiableHabitsChart-module_tooltipDot__YbdFh","tooltipInfo":"QuantifiableHabitsChart-module_tooltipInfo__XC7WF","tooltipDate":"QuantifiableHabitsChart-module_tooltipDate__6V6Xi","tooltipValue":"QuantifiableHabitsChart-module_tooltipValue__ldASB"};
|
|
4242
|
+
var styles$8 = {"container":"QuantifiableHabitsChart-module_container__X5SBp","controls":"QuantifiableHabitsChart-module_controls__O-ObQ","viewToggle":"QuantifiableHabitsChart-module_viewToggle__24hKA","viewButton":"QuantifiableHabitsChart-module_viewButton__WFU6j","active":"QuantifiableHabitsChart-module_active__Pjqy9","viewIcon":"QuantifiableHabitsChart-module_viewIcon__b2mfk","viewLabel":"QuantifiableHabitsChart-module_viewLabel__9MjCU","aggregationToggle":"QuantifiableHabitsChart-module_aggregationToggle__WnxwB","aggregationButton":"QuantifiableHabitsChart-module_aggregationButton__sGCxX","aggregationIcon":"QuantifiableHabitsChart-module_aggregationIcon__HIaEf","aggregationLabel":"QuantifiableHabitsChart-module_aggregationLabel__Yc-S-","legend":"QuantifiableHabitsChart-module_legend__3Ki7c","compactLegend":"QuantifiableHabitsChart-module_compactLegend__jMpLB","legendItem":"QuantifiableHabitsChart-module_legendItem__Zl7fz","legendEmoji":"QuantifiableHabitsChart-module_legendEmoji__HG9CZ","legendLabel":"QuantifiableHabitsChart-module_legendLabel__H3oFL","inactive":"QuantifiableHabitsChart-module_inactive__TzZC-","legendColor":"QuantifiableHabitsChart-module_legendColor__zbPoV","chart":"QuantifiableHabitsChart-module_chart__FMeA-","gridLine":"QuantifiableHabitsChart-module_gridLine__CTNIq","line":"QuantifiableHabitsChart-module_line__CpYip","crosshair":"QuantifiableHabitsChart-module_crosshair__7hoFL","halo":"QuantifiableHabitsChart-module_halo__YlkOF","overlay":"QuantifiableHabitsChart-module_overlay__ffeNz","xAxis":"QuantifiableHabitsChart-module_xAxis__lbgBG","yAxis":"QuantifiableHabitsChart-module_yAxis__Y6WeV","dataPoint":"QuantifiableHabitsChart-module_dataPoint__s8UMX","tooltip":"QuantifiableHabitsChart-module_tooltip__Fay8N","visible":"QuantifiableHabitsChart-module_visible__-KSJq","tooltipHeader":"QuantifiableHabitsChart-module_tooltipHeader__7Q2up","tooltipEmoji":"QuantifiableHabitsChart-module_tooltipEmoji__atV3T","tooltipDot":"QuantifiableHabitsChart-module_tooltipDot__YbdFh","tooltipInfo":"QuantifiableHabitsChart-module_tooltipInfo__XC7WF","tooltipDate":"QuantifiableHabitsChart-module_tooltipDate__6V6Xi","tooltipValue":"QuantifiableHabitsChart-module_tooltipValue__ldASB"};
|
|
4084
4243
|
|
|
4085
|
-
// Default colors as fallback
|
|
4086
4244
|
const DEFAULT_HABIT_COLORS$1 = {
|
|
4087
4245
|
'Exercise': '#6BCB77',
|
|
4088
4246
|
'Meditation': '#4D96FF',
|
|
@@ -4093,14 +4251,53 @@ const DEFAULT_HABIT_COLORS$1 = {
|
|
|
4093
4251
|
'Calories': '#FF9F1C',
|
|
4094
4252
|
'Study': '#C774E8'
|
|
4095
4253
|
};
|
|
4254
|
+
// Smoothly interpolate between two SVG path strings by resampling both
|
|
4255
|
+
// at evenly-spaced arc-length positions and tweening corresponding points.
|
|
4256
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
4257
|
+
const pathTween = (oldD, newD, samples = 80) => {
|
|
4258
|
+
const safeOld = oldD && oldD.length > 0 ? oldD : 'M0,0';
|
|
4259
|
+
const safeNew = newD && newD.length > 0 ? newD : 'M0,0';
|
|
4260
|
+
const oldNode = document.createElementNS(SVG_NS, 'path');
|
|
4261
|
+
const newNode = document.createElementNS(SVG_NS, 'path');
|
|
4262
|
+
oldNode.setAttribute('d', safeOld);
|
|
4263
|
+
newNode.setAttribute('d', safeNew);
|
|
4264
|
+
const oldLen = typeof oldNode.getTotalLength === 'function' ? oldNode.getTotalLength() : 0;
|
|
4265
|
+
const newLen = typeof newNode.getTotalLength === 'function' ? newNode.getTotalLength() : 0;
|
|
4266
|
+
if (oldLen === 0 || newLen === 0) {
|
|
4267
|
+
return () => safeNew;
|
|
4268
|
+
}
|
|
4269
|
+
const old = [];
|
|
4270
|
+
const next = [];
|
|
4271
|
+
for (let i = 0; i < samples; i++) {
|
|
4272
|
+
const u = samples === 1 ? 0 : i / (samples - 1);
|
|
4273
|
+
const op = oldNode.getPointAtLength(u * oldLen);
|
|
4274
|
+
const np = newNode.getPointAtLength(u * newLen);
|
|
4275
|
+
old.push([op.x, op.y]);
|
|
4276
|
+
next.push([np.x, np.y]);
|
|
4277
|
+
}
|
|
4278
|
+
return (t) => {
|
|
4279
|
+
let s = '';
|
|
4280
|
+
for (let i = 0; i < samples; i++) {
|
|
4281
|
+
const x = old[i][0] + (next[i][0] - old[i][0]) * t;
|
|
4282
|
+
const y = old[i][1] + (next[i][1] - old[i][1]) * t;
|
|
4283
|
+
s += (i === 0 ? 'M' : 'L') + x + ',' + y;
|
|
4284
|
+
}
|
|
4285
|
+
return s;
|
|
4286
|
+
};
|
|
4287
|
+
};
|
|
4096
4288
|
const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewType = 'daily', periodType = 'month', habitColors: customHabitColors = {}, habitEmojis: customHabitEmojis = {}, onDataPointClick, hideControls = false, compactLegend = false }) => {
|
|
4097
4289
|
const svgRef = useRef(null);
|
|
4098
4290
|
const tooltipRef = useRef(null);
|
|
4291
|
+
const onClickRef = useRef(onDataPointClick);
|
|
4292
|
+
const hasMountedRef = useRef(false);
|
|
4099
4293
|
const [viewType, setViewType] = useState(defaultViewType);
|
|
4100
4294
|
const [activeHabits, setActiveHabits] = useState([]);
|
|
4101
4295
|
const [hoveredHabit, setHoveredHabit] = useState(null);
|
|
4102
4296
|
const [tooltipData, setTooltipData] = useState(null);
|
|
4103
4297
|
const [aggregationMode, setAggregationMode] = useState('average');
|
|
4298
|
+
useEffect(() => {
|
|
4299
|
+
onClickRef.current = onDataPointClick;
|
|
4300
|
+
}, [onDataPointClick]);
|
|
4104
4301
|
const margin = useMemo(() => ({ top: 20, right: 20, bottom: 50, left: 50 }), []);
|
|
4105
4302
|
const chartWidth = width - margin.left - margin.right;
|
|
4106
4303
|
const chartHeight = height - margin.top - margin.bottom;
|
|
@@ -4108,7 +4305,6 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
4108
4305
|
useEffect(() => {
|
|
4109
4306
|
setActiveHabits(habits);
|
|
4110
4307
|
}, [habits]);
|
|
4111
|
-
// Hide tooltip on scroll
|
|
4112
4308
|
useEffect(() => {
|
|
4113
4309
|
const handleScroll = () => {
|
|
4114
4310
|
setTooltipData(null);
|
|
@@ -4135,39 +4331,32 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
4135
4331
|
}
|
|
4136
4332
|
}, [periodType]);
|
|
4137
4333
|
const getColor = useCallback((habit) => {
|
|
4138
|
-
// First check custom colors, then defaults, then generate deterministic color
|
|
4139
4334
|
return customHabitColors[habit] || DEFAULT_HABIT_COLORS$1[habit] || `hsl(${Math.abs(habit.split('').reduce((a, b) => a + b.charCodeAt(0), 0)) % 360}, 70%, 50%)`;
|
|
4140
4335
|
}, [customHabitColors]);
|
|
4141
|
-
// Aggregate data based on view type
|
|
4142
4336
|
const aggregateData = useMemo(() => {
|
|
4143
4337
|
if (viewType === 'daily' || !data.dates.length) {
|
|
4144
4338
|
return data;
|
|
4145
4339
|
}
|
|
4146
4340
|
const aggregated = { dates: [] };
|
|
4147
4341
|
const dateGroups = new Map();
|
|
4148
|
-
// Group dates by period
|
|
4149
4342
|
data.dates.forEach((dateStr, index) => {
|
|
4150
4343
|
const date = new Date(dateStr);
|
|
4151
4344
|
let groupKey;
|
|
4152
4345
|
switch (viewType) {
|
|
4153
4346
|
case 'weekly': {
|
|
4154
|
-
// Get ISO week start (Monday)
|
|
4155
4347
|
const weekStart = new Date(date);
|
|
4156
4348
|
const day = weekStart.getDay();
|
|
4157
4349
|
const diff = weekStart.getDate() - day + (day === 0 ? -6 : 1);
|
|
4158
4350
|
weekStart.setDate(diff);
|
|
4159
|
-
// Use the Monday date as the group key to ensure proper sorting
|
|
4160
4351
|
groupKey = weekStart.toISOString().split('T')[0];
|
|
4161
4352
|
break;
|
|
4162
4353
|
}
|
|
4163
4354
|
case 'monthly':
|
|
4164
|
-
// Use first day of month for consistent sorting
|
|
4165
4355
|
groupKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-01`;
|
|
4166
4356
|
break;
|
|
4167
4357
|
case 'quarterly': {
|
|
4168
4358
|
const quarter = Math.floor(date.getMonth() / 3) + 1;
|
|
4169
4359
|
const quarterStartMonth = (quarter - 1) * 3;
|
|
4170
|
-
// Use first day of quarter for consistent sorting and grouping
|
|
4171
4360
|
const quarterStart = new Date(date.getFullYear(), quarterStartMonth, 1);
|
|
4172
4361
|
groupKey = quarterStart.toISOString().split('T')[0];
|
|
4173
4362
|
break;
|
|
@@ -4180,7 +4369,6 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
4180
4369
|
}
|
|
4181
4370
|
dateGroups.get(groupKey).push(index);
|
|
4182
4371
|
});
|
|
4183
|
-
// Calculate aggregated values - sort dates for proper chronological order
|
|
4184
4372
|
aggregated.dates = Array.from(dateGroups.keys()).sort();
|
|
4185
4373
|
habits.forEach(habit => {
|
|
4186
4374
|
aggregated[habit] = aggregated.dates.map(groupKey => {
|
|
@@ -4190,11 +4378,10 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
4190
4378
|
.filter(v => typeof v === 'number' && !isNaN(v));
|
|
4191
4379
|
if (values.length === 0)
|
|
4192
4380
|
return 0;
|
|
4193
|
-
// Calculate aggregation based on mode
|
|
4194
4381
|
if (aggregationMode === 'sum') {
|
|
4195
4382
|
return Math.round(values.reduce((sum, v) => sum + v, 0));
|
|
4196
4383
|
}
|
|
4197
|
-
else {
|
|
4384
|
+
else {
|
|
4198
4385
|
return Math.round(values.reduce((sum, v) => sum + v, 0) / values.length);
|
|
4199
4386
|
}
|
|
4200
4387
|
});
|
|
@@ -4210,98 +4397,53 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
4210
4397
|
if (!svgRef.current || aggregateData.dates.length === 0)
|
|
4211
4398
|
return;
|
|
4212
4399
|
const svg = d3.select(svgRef.current);
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
.attr('
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4400
|
+
const isFirstRender = !hasMountedRef.current;
|
|
4401
|
+
hasMountedRef.current = true;
|
|
4402
|
+
// Acquire/create persistent root group + sub-groups
|
|
4403
|
+
let root = svg.select('g.qhc-root');
|
|
4404
|
+
if (root.empty()) {
|
|
4405
|
+
root = svg.append('g').attr('class', 'qhc-root');
|
|
4406
|
+
}
|
|
4407
|
+
root.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
4408
|
+
const ensureGroup = (cls) => {
|
|
4409
|
+
let s = root.select(`g.${cls}`);
|
|
4410
|
+
if (s.empty())
|
|
4411
|
+
s = root.append('g').attr('class', cls);
|
|
4412
|
+
return s;
|
|
4413
|
+
};
|
|
4414
|
+
const gridGroup = ensureGroup('qhc-grid');
|
|
4415
|
+
const linesGroup = ensureGroup('qhc-lines');
|
|
4416
|
+
const pointsGroup = ensureGroup('qhc-points');
|
|
4417
|
+
const axesGroup = ensureGroup('qhc-axes');
|
|
4418
|
+
const uiGroup = ensureGroup('qhc-ui');
|
|
4225
4419
|
const dates = aggregateData.dates.map(d => new Date(d));
|
|
4226
4420
|
const xScale = d3.scaleTime()
|
|
4227
4421
|
.domain(d3.extent(dates))
|
|
4228
4422
|
.range([0, chartWidth]);
|
|
4229
|
-
const maxValue = Math.max(...activeHabits.flatMap(habit => aggregateData[habit].filter(v => typeof v === 'number')));
|
|
4423
|
+
const maxValue = Math.max(1, ...activeHabits.flatMap(habit => aggregateData[habit].filter(v => typeof v === 'number')));
|
|
4230
4424
|
const yScale = d3.scaleLinear()
|
|
4231
4425
|
.domain([0, maxValue * 1.1])
|
|
4232
4426
|
.range([chartHeight, 0]);
|
|
4233
|
-
|
|
4234
|
-
.data(yScale.ticks(5))
|
|
4235
|
-
.enter().append('line')
|
|
4236
|
-
.attr('class', styles$8.gridLine)
|
|
4237
|
-
.attr('x1', 0)
|
|
4238
|
-
.attr('y1', d => yScale(d))
|
|
4239
|
-
.attr('x2', chartWidth)
|
|
4240
|
-
.attr('y2', d => yScale(d));
|
|
4241
|
-
const line = d3.line()
|
|
4427
|
+
const lineGen = d3.line()
|
|
4242
4428
|
.x(d => xScale(d[0]))
|
|
4243
4429
|
.y(d => yScale(d[1]))
|
|
4244
4430
|
.curve(d3.curveMonotoneX);
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
.style('cursor', 'pointer')
|
|
4262
|
-
.on('mouseenter', function (_event, d) {
|
|
4263
|
-
const [date, value] = d;
|
|
4264
|
-
const rect = svgRef.current?.getBoundingClientRect();
|
|
4265
|
-
if (rect) {
|
|
4266
|
-
setTooltipData({
|
|
4267
|
-
habit,
|
|
4268
|
-
date: date.toISOString().split('T')[0],
|
|
4269
|
-
value,
|
|
4270
|
-
x: xScale(date) + margin.left + rect.left,
|
|
4271
|
-
y: yScale(value) + margin.top + rect.top
|
|
4272
|
-
});
|
|
4273
|
-
}
|
|
4274
|
-
// Find and enlarge the visible circle
|
|
4275
|
-
d3.select(g.node())
|
|
4276
|
-
.selectAll(`.circle-${habit}-${dates.indexOf(date)}`)
|
|
4277
|
-
.attr('r', 6);
|
|
4278
|
-
})
|
|
4279
|
-
.on('mouseleave', function (_event, _d) {
|
|
4280
|
-
setTooltipData(null);
|
|
4281
|
-
// Reset all circles for this habit
|
|
4282
|
-
d3.select(g.node())
|
|
4283
|
-
.selectAll('[class*="circle-' + habit + '"]')
|
|
4284
|
-
.attr('r', 4);
|
|
4285
|
-
})
|
|
4286
|
-
.on('click', function (_event, d) {
|
|
4287
|
-
const [date, value] = d;
|
|
4288
|
-
if (onDataPointClick) {
|
|
4289
|
-
onDataPointClick(habit, date.toISOString().split('T')[0], value);
|
|
4290
|
-
}
|
|
4291
|
-
});
|
|
4292
|
-
// Add visible circles
|
|
4293
|
-
g.selectAll(`.circle-${habit}`)
|
|
4294
|
-
.data(habitData)
|
|
4295
|
-
.enter().append('circle')
|
|
4296
|
-
.attr('class', (_d, i) => `${styles$8.dataPoint} circle-${habit}-${i}`)
|
|
4297
|
-
.attr('cx', d => xScale(d[0]))
|
|
4298
|
-
.attr('cy', d => yScale(d[1]))
|
|
4299
|
-
.attr('r', 4)
|
|
4300
|
-
.attr('fill', getColor(habit))
|
|
4301
|
-
.attr('opacity', hoveredHabit && hoveredHabit !== habit ? 0.3 : 1)
|
|
4302
|
-
.style('pointer-events', 'none'); // Let the invisible circle handle events
|
|
4303
|
-
});
|
|
4304
|
-
// Helper function to get ISO week number
|
|
4431
|
+
// -------- Grid --------
|
|
4432
|
+
const gridSel = gridGroup.selectAll('line.qhc-grid-line')
|
|
4433
|
+
.data(yScale.ticks(5), d => String(d));
|
|
4434
|
+
gridSel.exit().remove();
|
|
4435
|
+
const gridEnter = gridSel.enter().append('line')
|
|
4436
|
+
.attr('class', `${styles$8.gridLine} qhc-grid-line`)
|
|
4437
|
+
.style('opacity', 0);
|
|
4438
|
+
gridEnter.merge(gridSel)
|
|
4439
|
+
.attr('x1', 0)
|
|
4440
|
+
.attr('x2', chartWidth)
|
|
4441
|
+
.attr('y1', d => yScale(d))
|
|
4442
|
+
.attr('y2', d => yScale(d))
|
|
4443
|
+
.transition()
|
|
4444
|
+
.duration(400)
|
|
4445
|
+
.style('opacity', 1);
|
|
4446
|
+
// -------- Axes --------
|
|
4305
4447
|
const getISOWeek = (date) => {
|
|
4306
4448
|
const d = new Date(date);
|
|
4307
4449
|
d.setHours(0, 0, 0, 0);
|
|
@@ -4309,28 +4451,20 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
4309
4451
|
const week1 = new Date(d.getFullYear(), 0, 4);
|
|
4310
4452
|
return 1 + Math.round(((d.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
|
|
4311
4453
|
};
|
|
4312
|
-
// Format x-axis based on view type
|
|
4313
4454
|
const xAxisFormat = (() => {
|
|
4314
4455
|
switch (viewType) {
|
|
4315
4456
|
case 'daily':
|
|
4316
4457
|
return d3.timeFormat('%m-%d');
|
|
4317
4458
|
case 'weekly':
|
|
4318
|
-
return (d) => {
|
|
4319
|
-
const weekNum = getISOWeek(d);
|
|
4320
|
-
return `W${weekNum.toString().padStart(2, '0')}`;
|
|
4321
|
-
};
|
|
4459
|
+
return (d) => `W${getISOWeek(d).toString().padStart(2, '0')}`;
|
|
4322
4460
|
case 'monthly':
|
|
4323
4461
|
return d3.timeFormat('%b');
|
|
4324
4462
|
case 'quarterly':
|
|
4325
|
-
return (d) => {
|
|
4326
|
-
const quarter = Math.floor(d.getMonth() / 3) + 1;
|
|
4327
|
-
return `Q${quarter}`;
|
|
4328
|
-
};
|
|
4463
|
+
return (d) => `Q${Math.floor(d.getMonth() / 3) + 1}`;
|
|
4329
4464
|
default:
|
|
4330
4465
|
return d3.timeFormat('%m-%d');
|
|
4331
4466
|
}
|
|
4332
4467
|
})();
|
|
4333
|
-
// Determine number of ticks based on view type and data
|
|
4334
4468
|
const tickCount = (() => {
|
|
4335
4469
|
switch (viewType) {
|
|
4336
4470
|
case 'daily':
|
|
@@ -4340,47 +4474,278 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
4340
4474
|
case 'monthly':
|
|
4341
4475
|
return Math.min(12, aggregateData.dates.length);
|
|
4342
4476
|
case 'quarterly':
|
|
4343
|
-
return aggregateData.dates.length;
|
|
4477
|
+
return aggregateData.dates.length;
|
|
4344
4478
|
default:
|
|
4345
4479
|
return undefined;
|
|
4346
4480
|
}
|
|
4347
4481
|
})();
|
|
4348
4482
|
const xAxisGenerator = d3.axisBottom(xScale)
|
|
4349
4483
|
.tickFormat(d => xAxisFormat(d));
|
|
4350
|
-
// Set tick values for quarterly to avoid duplicates
|
|
4351
4484
|
if (viewType === 'quarterly') {
|
|
4352
4485
|
xAxisGenerator.tickValues(dates);
|
|
4353
4486
|
}
|
|
4354
4487
|
else if (tickCount) {
|
|
4355
4488
|
xAxisGenerator.ticks(tickCount);
|
|
4356
4489
|
}
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
.attr('
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4490
|
+
let xAxis = axesGroup.select('g.qhc-x-axis');
|
|
4491
|
+
if (xAxis.empty()) {
|
|
4492
|
+
xAxis = axesGroup.append('g').attr('class', `${styles$8.xAxis} qhc-x-axis`);
|
|
4493
|
+
}
|
|
4494
|
+
xAxis.attr('transform', `translate(0,${chartHeight})`).call(xAxisGenerator);
|
|
4495
|
+
xAxis.selectAll('text')
|
|
4496
|
+
.style('text-anchor', viewType === 'daily' || viewType === 'weekly' ? 'end' : 'middle')
|
|
4497
|
+
.attr('dx', viewType === 'daily' || viewType === 'weekly' ? '-.8em' : '0')
|
|
4498
|
+
.attr('dy', viewType === 'daily' || viewType === 'weekly' ? '.15em' : '.71em')
|
|
4499
|
+
.attr('transform', viewType === 'daily' || viewType === 'weekly' ? 'rotate(-45)' : null);
|
|
4500
|
+
let yAxis = axesGroup.select('g.qhc-y-axis');
|
|
4501
|
+
if (yAxis.empty()) {
|
|
4502
|
+
yAxis = axesGroup.append('g').attr('class', `${styles$8.yAxis} qhc-y-axis`);
|
|
4503
|
+
}
|
|
4504
|
+
yAxis.call(d3.axisLeft(yScale));
|
|
4505
|
+
if (isFirstRender) {
|
|
4506
|
+
xAxis.style('opacity', 0).transition().duration(500).delay(80).style('opacity', 1);
|
|
4507
|
+
yAxis.style('opacity', 0).transition().duration(500).delay(80).style('opacity', 1);
|
|
4508
|
+
}
|
|
4509
|
+
// -------- Lines (data join, morph on update) --------
|
|
4510
|
+
const lineData = activeHabits.map(habit => {
|
|
4511
|
+
const points = dates
|
|
4512
|
+
.map((date, i) => [date, aggregateData[habit][i]])
|
|
4513
|
+
.filter(d => typeof d[1] === 'number');
|
|
4514
|
+
return { habit, color: getColor(habit), points };
|
|
4515
|
+
});
|
|
4516
|
+
const lineSel = linesGroup.selectAll('path.qhc-line')
|
|
4517
|
+
.data(lineData, (d) => d.habit);
|
|
4518
|
+
lineSel.exit()
|
|
4519
|
+
.transition('line-exit')
|
|
4520
|
+
.duration(300)
|
|
4521
|
+
.style('opacity', 0)
|
|
4522
|
+
.remove();
|
|
4523
|
+
const lineEnter = lineSel.enter().append('path')
|
|
4524
|
+
.attr('class', `${styles$8.line} qhc-line`)
|
|
4525
|
+
.attr('data-habit', d => d.habit)
|
|
4526
|
+
.attr('fill', 'none')
|
|
4527
|
+
.attr('stroke', d => d.color)
|
|
4528
|
+
.attr('opacity', 0)
|
|
4529
|
+
.attr('d', d => lineGen(d.points) || '')
|
|
4530
|
+
.each(function () {
|
|
4531
|
+
const node = this;
|
|
4532
|
+
const len = typeof node.getTotalLength === 'function' ? node.getTotalLength() : 0;
|
|
4533
|
+
if (len > 0) {
|
|
4534
|
+
d3.select(node)
|
|
4535
|
+
.attr('stroke-dasharray', `${len} ${len}`)
|
|
4536
|
+
.attr('stroke-dashoffset', len);
|
|
4537
|
+
}
|
|
4538
|
+
});
|
|
4539
|
+
lineEnter.transition('line-enter')
|
|
4540
|
+
.duration(900)
|
|
4541
|
+
.ease(d3.easeCubicOut)
|
|
4542
|
+
.attr('opacity', 1)
|
|
4543
|
+
.attr('stroke-dashoffset', 0)
|
|
4544
|
+
.on('end', function () {
|
|
4545
|
+
d3.select(this).attr('stroke-dasharray', null);
|
|
4546
|
+
});
|
|
4547
|
+
lineSel.transition('line-update')
|
|
4548
|
+
.duration(700)
|
|
4549
|
+
.ease(d3.easeCubicInOut)
|
|
4550
|
+
.attr('stroke', d => d.color)
|
|
4551
|
+
.attrTween('d', function (d) {
|
|
4552
|
+
const oldD = d3.select(this).attr('d') || '';
|
|
4553
|
+
const newD = lineGen(d.points) || '';
|
|
4554
|
+
return pathTween(oldD, newD, 90);
|
|
4555
|
+
})
|
|
4556
|
+
.on('end', function (d) {
|
|
4557
|
+
d3.select(this).attr('d', lineGen(d.points) || '');
|
|
4558
|
+
});
|
|
4559
|
+
// -------- Points --------
|
|
4560
|
+
// Clear and re-add (simpler than join when both habit set + count change).
|
|
4561
|
+
// Hide them during line morph, then spring up from baseline.
|
|
4562
|
+
pointsGroup.selectAll('*').remove();
|
|
4563
|
+
const allPoints = [];
|
|
4564
|
+
activeHabits.forEach(habit => {
|
|
4565
|
+
const color = getColor(habit);
|
|
4566
|
+
dates.forEach((date, i) => {
|
|
4567
|
+
const v = aggregateData[habit][i];
|
|
4568
|
+
if (typeof v === 'number' && !isNaN(v)) {
|
|
4569
|
+
allPoints.push({ habit, color, date, value: v });
|
|
4570
|
+
}
|
|
4571
|
+
});
|
|
4572
|
+
});
|
|
4573
|
+
const minX = xScale(dates[0]);
|
|
4574
|
+
const maxX = xScale(dates[dates.length - 1]);
|
|
4575
|
+
const xRange = Math.max(1, maxX - minX);
|
|
4576
|
+
const pointAppearDelay = isFirstRender ? 100 : 350;
|
|
4577
|
+
const pointAppearTotal = isFirstRender ? 1100 : 600;
|
|
4578
|
+
pointsGroup.selectAll('circle.qhc-point')
|
|
4579
|
+
.data(allPoints)
|
|
4580
|
+
.enter().append('circle')
|
|
4581
|
+
.attr('class', `${styles$8.dataPoint} qhc-point`)
|
|
4582
|
+
.attr('data-habit', d => d.habit)
|
|
4583
|
+
.attr('cx', d => xScale(d.date))
|
|
4584
|
+
.attr('cy', chartHeight)
|
|
4585
|
+
.attr('r', 0)
|
|
4586
|
+
.attr('fill', d => d.color)
|
|
4587
|
+
.style('opacity', 0)
|
|
4588
|
+
.transition('point-enter')
|
|
4589
|
+
.delay(d => pointAppearDelay + pointAppearTotal * ((xScale(d.date) - minX) / xRange))
|
|
4590
|
+
.duration(420)
|
|
4591
|
+
.ease(d3.easeBackOut.overshoot(2))
|
|
4592
|
+
.attr('cy', d => yScale(d.value))
|
|
4593
|
+
.attr('r', 4)
|
|
4594
|
+
.style('opacity', 1);
|
|
4595
|
+
// -------- UI layer (crosshair, halo, overlay) --------
|
|
4596
|
+
uiGroup.selectAll('*').remove();
|
|
4597
|
+
const crosshair = uiGroup.append('line')
|
|
4598
|
+
.attr('class', styles$8.crosshair)
|
|
4599
|
+
.attr('y1', 0)
|
|
4600
|
+
.attr('y2', chartHeight)
|
|
4601
|
+
.style('opacity', 0)
|
|
4602
|
+
.style('pointer-events', 'none');
|
|
4603
|
+
const halo = uiGroup.append('circle')
|
|
4604
|
+
.attr('class', styles$8.halo)
|
|
4605
|
+
.attr('r', 0)
|
|
4606
|
+
.style('opacity', 0)
|
|
4607
|
+
.style('pointer-events', 'none');
|
|
4608
|
+
let activeKey = null;
|
|
4609
|
+
const startHaloPulse = (cx, cy, fill) => {
|
|
4610
|
+
halo.interrupt('halo-pulse').interrupt('halo-out')
|
|
4611
|
+
.attr('cx', cx).attr('cy', cy).attr('fill', fill);
|
|
4612
|
+
const pulseOnce = () => {
|
|
4613
|
+
halo.attr('r', 4).style('opacity', 0.55)
|
|
4614
|
+
.transition('halo-pulse').duration(900).ease(d3.easeCubicOut)
|
|
4615
|
+
.attr('r', 18).style('opacity', 0)
|
|
4616
|
+
.on('end', pulseOnce);
|
|
4617
|
+
};
|
|
4618
|
+
pulseOnce();
|
|
4619
|
+
};
|
|
4620
|
+
const stopHaloPulse = () => {
|
|
4621
|
+
halo.interrupt('halo-pulse')
|
|
4622
|
+
.transition('halo-out').duration(180)
|
|
4623
|
+
.style('opacity', 0).attr('r', 0);
|
|
4624
|
+
};
|
|
4625
|
+
const activeAll = allPoints.map(p => ({
|
|
4626
|
+
habit: p.habit,
|
|
4627
|
+
date: p.date,
|
|
4628
|
+
value: p.value,
|
|
4629
|
+
x: xScale(p.date),
|
|
4630
|
+
y: yScale(p.value)
|
|
4631
|
+
}));
|
|
4632
|
+
const setActive = (pt, clientX, clientY) => {
|
|
4633
|
+
const key = `${pt.habit}|${pt.date.toISOString()}`;
|
|
4634
|
+
const color = getColor(pt.habit);
|
|
4635
|
+
crosshair.attr('x1', pt.x).attr('x2', pt.x)
|
|
4636
|
+
.interrupt('crosshair-fade')
|
|
4637
|
+
.transition('crosshair-fade').duration(120).style('opacity', 1);
|
|
4638
|
+
if (activeKey !== key) {
|
|
4639
|
+
pointsGroup.selectAll('circle.qhc-point')
|
|
4640
|
+
.filter(function () { return d3.select(this).attr('data-active') === 'true'; })
|
|
4641
|
+
.attr('data-active', null)
|
|
4642
|
+
.transition('point-scale').duration(150).attr('r', 4);
|
|
4643
|
+
pointsGroup.selectAll('circle.qhc-point')
|
|
4644
|
+
.filter(function () {
|
|
4645
|
+
const cx = +d3.select(this).attr('cx');
|
|
4646
|
+
const cy = +d3.select(this).attr('cy');
|
|
4647
|
+
const habit = d3.select(this).attr('data-habit');
|
|
4648
|
+
return habit === pt.habit && Math.abs(cx - pt.x) < 0.5 && Math.abs(cy - pt.y) < 0.5;
|
|
4649
|
+
})
|
|
4650
|
+
.attr('data-active', 'true')
|
|
4651
|
+
.transition('point-scale').duration(180).ease(d3.easeBackOut.overshoot(2))
|
|
4652
|
+
.attr('r', 7);
|
|
4653
|
+
startHaloPulse(pt.x, pt.y, color);
|
|
4654
|
+
activeKey = key;
|
|
4655
|
+
}
|
|
4656
|
+
setTooltipData({
|
|
4657
|
+
habit: pt.habit,
|
|
4658
|
+
date: pt.date.toISOString().split('T')[0],
|
|
4659
|
+
value: pt.value,
|
|
4660
|
+
x: clientX,
|
|
4661
|
+
y: clientY
|
|
4662
|
+
});
|
|
4663
|
+
};
|
|
4664
|
+
const clearActive = () => {
|
|
4665
|
+
crosshair.interrupt('crosshair-fade')
|
|
4666
|
+
.transition('crosshair-fade').duration(180).style('opacity', 0);
|
|
4667
|
+
pointsGroup.selectAll('circle.qhc-point')
|
|
4668
|
+
.filter(function () { return d3.select(this).attr('data-active') === 'true'; })
|
|
4669
|
+
.attr('data-active', null)
|
|
4670
|
+
.transition('point-scale').duration(180).attr('r', 4);
|
|
4671
|
+
stopHaloPulse();
|
|
4672
|
+
activeKey = null;
|
|
4673
|
+
setTooltipData(null);
|
|
4674
|
+
};
|
|
4675
|
+
uiGroup.append('rect')
|
|
4676
|
+
.attr('class', styles$8.overlay)
|
|
4677
|
+
.attr('width', chartWidth)
|
|
4678
|
+
.attr('height', chartHeight)
|
|
4679
|
+
.on('mousemove', function (event) {
|
|
4680
|
+
if (activeAll.length === 0)
|
|
4681
|
+
return;
|
|
4682
|
+
const [mx, my] = d3.pointer(event, this);
|
|
4683
|
+
let nearest = activeAll[0];
|
|
4684
|
+
let nearestDist = Infinity;
|
|
4685
|
+
for (const p of activeAll) {
|
|
4686
|
+
const dx = p.x - mx;
|
|
4687
|
+
const dy = p.y - my;
|
|
4688
|
+
const dist = dx * dx + dy * dy;
|
|
4689
|
+
if (dist < nearestDist) {
|
|
4690
|
+
nearestDist = dist;
|
|
4691
|
+
nearest = p;
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
setActive(nearest, event.clientX, event.clientY);
|
|
4695
|
+
})
|
|
4696
|
+
.on('mouseleave', () => {
|
|
4697
|
+
clearActive();
|
|
4698
|
+
})
|
|
4699
|
+
.on('click', function (event) {
|
|
4700
|
+
if (activeAll.length === 0 || !onClickRef.current)
|
|
4701
|
+
return;
|
|
4702
|
+
const [mx, my] = d3.pointer(event, this);
|
|
4703
|
+
let nearest = activeAll[0];
|
|
4704
|
+
let nearestDist = Infinity;
|
|
4705
|
+
for (const p of activeAll) {
|
|
4706
|
+
const dx = p.x - mx;
|
|
4707
|
+
const dy = p.y - my;
|
|
4708
|
+
const dist = dx * dx + dy * dy;
|
|
4709
|
+
if (dist < nearestDist) {
|
|
4710
|
+
nearestDist = dist;
|
|
4711
|
+
nearest = p;
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
onClickRef.current(nearest.habit, nearest.date.toISOString().split('T')[0], nearest.value);
|
|
4715
|
+
});
|
|
4716
|
+
uiGroup.raise();
|
|
4717
|
+
}, [aggregateData, activeHabits, chartWidth, chartHeight, margin, getColor, viewType, width, height]);
|
|
4718
|
+
// Hover dimming: update opacity on existing elements without redrawing.
|
|
4719
|
+
useEffect(() => {
|
|
4720
|
+
if (!svgRef.current)
|
|
4721
|
+
return;
|
|
4722
|
+
const svg = d3.select(svgRef.current);
|
|
4723
|
+
svg.selectAll('path.qhc-line')
|
|
4724
|
+
.each(function () {
|
|
4725
|
+
const habit = this.getAttribute('data-habit');
|
|
4726
|
+
const dim = hoveredHabit && hoveredHabit !== habit;
|
|
4727
|
+
d3.select(this)
|
|
4728
|
+
.transition('hover-dim')
|
|
4729
|
+
.duration(180)
|
|
4730
|
+
.attr('opacity', dim ? 0.25 : 1);
|
|
4731
|
+
});
|
|
4732
|
+
svg.selectAll('circle.qhc-point')
|
|
4733
|
+
.each(function () {
|
|
4734
|
+
const habit = this.getAttribute('data-habit');
|
|
4735
|
+
const dim = hoveredHabit && hoveredHabit !== habit;
|
|
4736
|
+
d3.select(this)
|
|
4737
|
+
.transition('hover-dim')
|
|
4738
|
+
.duration(180)
|
|
4739
|
+
.attr('opacity', dim ? 0.25 : 1);
|
|
4740
|
+
});
|
|
4741
|
+
}, [hoveredHabit]);
|
|
4374
4742
|
const formatTooltipDate = (dateStr, viewType) => {
|
|
4375
4743
|
const date = new Date(dateStr);
|
|
4376
4744
|
switch (viewType) {
|
|
4377
4745
|
case 'daily':
|
|
4378
4746
|
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
4379
|
-
case 'weekly':
|
|
4380
|
-
const weekEnd = new Date(date);
|
|
4381
|
-
weekEnd.setDate(date.getDate() + 6);
|
|
4747
|
+
case 'weekly':
|
|
4382
4748
|
return `Week of ${date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`;
|
|
4383
|
-
}
|
|
4384
4749
|
case 'monthly':
|
|
4385
4750
|
return date.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
|
|
4386
4751
|
case 'quarterly': {
|
|
@@ -4391,7 +4756,6 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
4391
4756
|
return dateStr;
|
|
4392
4757
|
}
|
|
4393
4758
|
};
|
|
4394
|
-
// View type icons and labels
|
|
4395
4759
|
const viewTypeConfig = {
|
|
4396
4760
|
daily: { Icon: Calendar$1, label: 'Daily' },
|
|
4397
4761
|
weekly: { Icon: CalendarDays, label: 'Weekly' },
|