@parto-system-design/ui 1.1.9 → 1.1.16
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/brand/parto-logo.cjs +130 -0
- package/dist/components/brand/parto-logo.cjs.map +1 -0
- package/dist/components/brand/parto-logo.d.cts +38 -0
- package/dist/components/brand/parto-logo.d.ts +38 -0
- package/dist/components/brand/parto-logo.js +108 -0
- package/dist/components/brand/parto-logo.js.map +1 -0
- package/dist/components/charts/PartoAreaChart.cjs +579 -7
- package/dist/components/charts/PartoAreaChart.cjs.map +1 -1
- package/dist/components/charts/PartoAreaChart.js +564 -4
- package/dist/components/charts/PartoAreaChart.js.map +1 -1
- package/dist/components/charts/PartoBarChart.cjs +616 -7
- package/dist/components/charts/PartoBarChart.cjs.map +1 -1
- package/dist/components/charts/PartoBarChart.js +601 -4
- package/dist/components/charts/PartoBarChart.js.map +1 -1
- package/dist/components/charts/PartoLineChart.cjs +584 -7
- package/dist/components/charts/PartoLineChart.cjs.map +1 -1
- package/dist/components/charts/PartoLineChart.js +569 -4
- package/dist/components/charts/PartoLineChart.js.map +1 -1
- package/dist/components/charts/PartoPieChart.cjs +566 -7
- package/dist/components/charts/PartoPieChart.cjs.map +1 -1
- package/dist/components/charts/PartoPieChart.js +551 -4
- package/dist/components/charts/PartoPieChart.js.map +1 -1
- package/dist/components/ui/accordion.cjs +97 -0
- package/dist/components/ui/accordion.cjs.map +1 -0
- package/dist/components/ui/accordion.d.cts +22 -0
- package/dist/components/ui/accordion.d.ts +22 -0
- package/dist/components/ui/accordion.js +72 -0
- package/dist/components/ui/accordion.js.map +1 -0
- package/dist/{chunk-MMC6M35Q.cjs → components/ui/alert-dialog.cjs} +216 -22
- package/dist/components/ui/alert-dialog.cjs.map +1 -0
- package/dist/components/ui/alert-dialog.d.cts +17 -0
- package/dist/components/ui/alert-dialog.d.ts +17 -0
- package/dist/{chunk-3QYYPPFJ.js → components/ui/alert-dialog.js} +175 -10
- package/dist/components/ui/alert-dialog.js.map +1 -0
- package/dist/components/ui/alert-rule-card.cjs +289 -8
- package/dist/components/ui/alert-rule-card.cjs.map +1 -1
- package/dist/components/ui/alert-rule-card.d.cts +1 -1
- package/dist/components/ui/alert-rule-card.d.ts +1 -1
- package/dist/components/ui/alert-rule-card.js +271 -4
- package/dist/components/ui/alert-rule-card.js.map +1 -1
- package/dist/components/ui/alert.cjs +81 -0
- package/dist/components/ui/alert.cjs.map +1 -0
- package/dist/components/ui/alert.d.cts +11 -0
- package/dist/components/ui/alert.d.ts +11 -0
- package/dist/components/ui/alert.js +57 -0
- package/dist/components/ui/alert.js.map +1 -0
- package/dist/components/ui/app-bar.cjs +67 -0
- package/dist/components/ui/app-bar.cjs.map +1 -0
- package/dist/components/ui/app-bar.d.cts +22 -0
- package/dist/components/ui/app-bar.d.ts +22 -0
- package/dist/components/ui/app-bar.js +44 -0
- package/dist/components/ui/app-bar.js.map +1 -0
- package/dist/components/ui/avatar.cjs +66 -14
- package/dist/components/ui/avatar.cjs.map +1 -1
- package/dist/components/ui/avatar.js +47 -2
- package/dist/components/ui/avatar.js.map +1 -1
- package/dist/components/ui/badge.cjs +113 -9
- package/dist/components/ui/badge.cjs.map +1 -1
- package/dist/components/ui/badge.d.cts +1 -1
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/badge.js +96 -2
- package/dist/components/ui/badge.js.map +1 -1
- package/dist/components/ui/breadcrumb.cjs +100 -0
- package/dist/components/ui/breadcrumb.cjs.map +1 -0
- package/dist/components/ui/breadcrumb.d.cts +35 -0
- package/dist/components/ui/breadcrumb.d.ts +35 -0
- package/dist/components/ui/breadcrumb.js +92 -0
- package/dist/components/ui/breadcrumb.js.map +1 -0
- package/dist/components/ui/button.cjs +304 -11
- package/dist/components/ui/button.cjs.map +1 -1
- package/dist/components/ui/button.d.cts +3 -3
- package/dist/components/ui/button.d.ts +3 -3
- package/dist/components/ui/button.js +306 -3
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/calendar.cjs +401 -8
- package/dist/components/ui/calendar.cjs.map +1 -1
- package/dist/components/ui/calendar.js +404 -4
- package/dist/components/ui/calendar.js.map +1 -1
- package/dist/components/ui/card.cjs +154 -30
- package/dist/components/ui/card.cjs.map +1 -1
- package/dist/components/ui/card.js +131 -2
- package/dist/components/ui/card.js.map +1 -1
- package/dist/{chunk-CAJKSTXX.cjs → components/ui/checkbox.cjs} +10 -5
- package/dist/components/ui/checkbox.cjs.map +1 -0
- package/dist/components/ui/checkbox.d.cts +6 -0
- package/dist/components/ui/checkbox.d.ts +6 -0
- package/dist/{chunk-5JJSRGJD.js → components/ui/checkbox.js} +9 -4
- package/dist/components/ui/checkbox.js.map +1 -0
- package/dist/components/ui/concept-card.cjs +728 -11
- package/dist/components/ui/concept-card.cjs.map +1 -1
- package/dist/components/ui/concept-card.d.cts +2 -2
- package/dist/components/ui/concept-card.d.ts +2 -2
- package/dist/components/ui/concept-card.js +710 -7
- package/dist/components/ui/concept-card.js.map +1 -1
- package/dist/components/ui/data-table.cjs +1553 -10
- package/dist/components/ui/data-table.cjs.map +1 -1
- package/dist/components/ui/data-table.js +1537 -7
- package/dist/components/ui/data-table.js.map +1 -1
- package/dist/components/ui/dialog.cjs +119 -42
- package/dist/components/ui/dialog.cjs.map +1 -1
- package/dist/components/ui/dialog.js +92 -2
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/dropdown-menu.cjs +268 -0
- package/dist/components/ui/dropdown-menu.cjs.map +1 -0
- package/dist/components/ui/dropdown-menu.d.cts +73 -0
- package/dist/components/ui/dropdown-menu.d.ts +73 -0
- package/dist/components/ui/dropdown-menu.js +232 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/filter-provider.cjs +70 -13
- package/dist/components/ui/filter-provider.cjs.map +1 -1
- package/dist/components/ui/filter-provider.js +51 -1
- package/dist/components/ui/filter-provider.js.map +1 -1
- package/dist/components/ui/form.cjs +169 -0
- package/dist/components/ui/form.cjs.map +1 -0
- package/dist/components/ui/form.d.cts +46 -0
- package/dist/components/ui/form.d.ts +46 -0
- package/dist/components/ui/form.js +139 -0
- package/dist/components/ui/form.js.map +1 -0
- package/dist/components/ui/input.cjs +135 -15
- package/dist/components/ui/input.cjs.map +1 -1
- package/dist/components/ui/input.d.cts +15 -0
- package/dist/components/ui/input.d.ts +15 -0
- package/dist/components/ui/input.js +116 -3
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/iran-province-heat.cjs +328 -5
- package/dist/components/ui/iran-province-heat.cjs.map +1 -1
- package/dist/components/ui/iran-province-heat.js +312 -2
- package/dist/components/ui/iran-province-heat.js.map +1 -1
- package/dist/components/ui/label.cjs +52 -0
- package/dist/components/ui/label.cjs.map +1 -0
- package/dist/components/ui/label.d.cts +10 -0
- package/dist/components/ui/label.d.ts +10 -0
- package/dist/components/ui/label.js +30 -0
- package/dist/components/ui/label.js.map +1 -0
- package/dist/components/ui/page-card.cjs +627 -8
- package/dist/components/ui/page-card.cjs.map +1 -1
- package/dist/components/ui/page-card.d.cts +3 -2
- package/dist/components/ui/page-card.d.ts +3 -2
- package/dist/components/ui/page-card.js +611 -5
- package/dist/components/ui/page-card.js.map +1 -1
- package/dist/components/ui/page-header.cjs +299 -0
- package/dist/components/ui/page-header.cjs.map +1 -0
- package/dist/components/ui/page-header.d.cts +21 -0
- package/dist/components/ui/page-header.d.ts +21 -0
- package/dist/components/ui/page-header.js +277 -0
- package/dist/components/ui/page-header.js.map +1 -0
- package/dist/components/ui/password-input.cjs +168 -0
- package/dist/components/ui/password-input.cjs.map +1 -0
- package/dist/components/ui/password-input.d.cts +48 -0
- package/dist/components/ui/password-input.d.ts +48 -0
- package/dist/components/ui/password-input.js +146 -0
- package/dist/components/ui/password-input.js.map +1 -0
- package/dist/components/ui/popover.cjs +51 -18
- package/dist/components/ui/popover.cjs.map +1 -1
- package/dist/components/ui/popover.js +30 -2
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/progress.cjs +95 -0
- package/dist/components/ui/progress.cjs.map +1 -0
- package/dist/components/ui/progress.d.cts +18 -0
- package/dist/components/ui/progress.d.ts +18 -0
- package/dist/components/ui/progress.js +72 -0
- package/dist/components/ui/progress.js.map +1 -0
- package/dist/components/ui/radio-card.cjs +84 -0
- package/dist/components/ui/radio-card.cjs.map +1 -0
- package/dist/components/ui/radio-card.d.cts +12 -0
- package/dist/components/ui/radio-card.d.ts +12 -0
- package/dist/components/ui/radio-card.js +58 -0
- package/dist/components/ui/radio-card.js.map +1 -0
- package/dist/components/ui/radio-group.cjs +62 -0
- package/dist/components/ui/radio-group.cjs.map +1 -0
- package/dist/components/ui/radio-group.d.cts +7 -0
- package/dist/components/ui/radio-group.d.ts +7 -0
- package/dist/components/ui/radio-group.js +38 -0
- package/dist/components/ui/radio-group.js.map +1 -0
- package/dist/components/ui/saved-query-card.cjs +409 -7
- package/dist/components/ui/saved-query-card.cjs.map +1 -1
- package/dist/components/ui/saved-query-card.js +394 -4
- package/dist/components/ui/saved-query-card.js.map +1 -1
- package/dist/components/ui/scroll-area.cjs +79 -0
- package/dist/components/ui/scroll-area.cjs.map +1 -0
- package/dist/components/ui/scroll-area.d.cts +14 -0
- package/dist/components/ui/scroll-area.d.ts +14 -0
- package/dist/components/ui/scroll-area.js +56 -0
- package/dist/components/ui/scroll-area.js.map +1 -0
- package/dist/components/ui/select.cjs +242 -0
- package/dist/components/ui/select.cjs.map +1 -0
- package/dist/components/ui/select.d.cts +29 -0
- package/dist/components/ui/select.d.ts +29 -0
- package/dist/components/ui/select.js +210 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/separator.cjs +105 -6
- package/dist/components/ui/separator.cjs.map +1 -1
- package/dist/components/ui/separator.js +87 -2
- package/dist/components/ui/separator.js.map +1 -1
- package/dist/components/ui/sheet.cjs +134 -38
- package/dist/components/ui/sheet.cjs.map +1 -1
- package/dist/components/ui/sheet.js +109 -2
- package/dist/components/ui/sheet.js.map +1 -1
- package/dist/{chunk-D2EBLE2B.cjs → components/ui/skeleton.cjs} +21 -16
- package/dist/components/ui/skeleton.cjs.map +1 -0
- package/dist/components/ui/skeleton.d.cts +90 -0
- package/dist/components/ui/skeleton.d.ts +90 -0
- package/dist/{chunk-SB5DSYR5.js → components/ui/skeleton.js} +8 -3
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/components/ui/slider.cjs +129 -0
- package/dist/components/ui/slider.cjs.map +1 -0
- package/dist/components/ui/slider.d.cts +10 -0
- package/dist/components/ui/slider.d.ts +10 -0
- package/dist/components/ui/slider.js +107 -0
- package/dist/components/ui/slider.js.map +1 -0
- package/dist/components/ui/social-platform-badge.cjs +120 -0
- package/dist/components/ui/social-platform-badge.cjs.map +1 -0
- package/dist/components/ui/social-platform-badge.d.cts +21 -0
- package/dist/components/ui/social-platform-badge.d.ts +21 -0
- package/dist/components/ui/social-platform-badge.js +97 -0
- package/dist/components/ui/social-platform-badge.js.map +1 -0
- package/dist/components/ui/sonner.cjs +51 -0
- package/dist/components/ui/sonner.cjs.map +1 -0
- package/dist/components/ui/sonner.d.cts +13 -0
- package/dist/components/ui/sonner.d.ts +13 -0
- package/dist/components/ui/sonner.js +45 -0
- package/dist/components/ui/sonner.js.map +1 -0
- package/dist/components/ui/sparkline.cjs +159 -6
- package/dist/components/ui/sparkline.cjs.map +1 -1
- package/dist/components/ui/sparkline.js +142 -2
- package/dist/components/ui/sparkline.js.map +1 -1
- package/dist/components/ui/switch.cjs +96 -0
- package/dist/components/ui/switch.cjs.map +1 -0
- package/dist/components/ui/switch.d.cts +13 -0
- package/dist/components/ui/switch.d.ts +13 -0
- package/dist/components/ui/switch.js +73 -0
- package/dist/components/ui/switch.js.map +1 -0
- package/dist/components/ui/table.cjs +188 -0
- package/dist/components/ui/table.cjs.map +1 -0
- package/dist/components/ui/table.d.cts +59 -0
- package/dist/components/ui/table.d.ts +59 -0
- package/dist/components/ui/table.js +178 -0
- package/dist/components/ui/table.js.map +1 -0
- package/dist/components/ui/tabs.cjs +140 -0
- package/dist/components/ui/tabs.cjs.map +1 -0
- package/dist/components/ui/tabs.d.cts +21 -0
- package/dist/components/ui/tabs.d.ts +21 -0
- package/dist/components/ui/tabs.js +115 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/components/ui/textarea.cjs +56 -0
- package/dist/components/ui/textarea.cjs.map +1 -0
- package/dist/components/ui/textarea.d.cts +5 -0
- package/dist/components/ui/textarea.d.ts +5 -0
- package/dist/components/ui/textarea.js +34 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/toggle-group.cjs +123 -0
- package/dist/components/ui/toggle-group.cjs.map +1 -0
- package/dist/components/ui/toggle-group.d.cts +7 -0
- package/dist/components/ui/toggle-group.d.ts +7 -0
- package/dist/components/ui/toggle-group.js +99 -0
- package/dist/components/ui/toggle-group.js.map +1 -0
- package/dist/components/ui/tooltip.cjs +77 -18
- package/dist/components/ui/tooltip.cjs.map +1 -1
- package/dist/components/ui/tooltip.js +56 -2
- package/dist/components/ui/tooltip.js.map +1 -1
- package/dist/{concept-card-RwPbqJ06.d.cts → concept-card-BXra9mr0.d.cts} +2 -2
- package/dist/{concept-card-CcOBb2Nz.d.ts → concept-card-BoJ5gIJD.d.ts} +2 -2
- package/dist/hooks/use-hotkey-registry.cjs +201 -14
- package/dist/hooks/use-hotkey-registry.cjs.map +1 -1
- package/dist/hooks/use-hotkey-registry.js +182 -2
- package/dist/hooks/use-hotkey-registry.js.map +1 -1
- package/dist/hooks/use-hotkeys.cjs +144 -9
- package/dist/hooks/use-hotkeys.cjs.map +1 -1
- package/dist/hooks/use-hotkeys.js +126 -1
- package/dist/hooks/use-hotkeys.js.map +1 -1
- package/dist/{i18n-CAd9wGOr.d.cts → i18n-BfRhV5aw.d.cts} +5 -3
- package/dist/{i18n-ArS3mqj0.d.ts → i18n-ewyqbKM-.d.ts} +5 -3
- package/dist/index.cjs +20940 -15745
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +396 -69
- package/dist/index.d.cts +60 -503
- package/dist/index.d.ts +60 -503
- package/dist/index.js +7716 -2049
- package/dist/index.js.map +1 -1
- package/dist/{page-card-CmShVqG-.d.cts → page-card-C9XXXOVr.d.cts} +3 -20
- package/dist/{page-card-HBn-cy4J.d.ts → page-card-DAnbez_f.d.ts} +3 -20
- package/dist/toggle-group-B8r4LOQw.d.cts +26 -0
- package/dist/toggle-group-B8r4LOQw.d.ts +26 -0
- package/package.json +132 -2
- package/tailwind.config.ts +45 -0
- package/dist/chunk-2ACKKPWA.cjs +0 -112
- package/dist/chunk-2ACKKPWA.cjs.map +0 -1
- package/dist/chunk-2UD3LGVX.cjs +0 -316
- package/dist/chunk-2UD3LGVX.cjs.map +0 -1
- package/dist/chunk-3QYYPPFJ.js.map +0 -1
- package/dist/chunk-4SVQNEVH.js +0 -173
- package/dist/chunk-4SVQNEVH.js.map +0 -1
- package/dist/chunk-4WONHORR.cjs +0 -152
- package/dist/chunk-4WONHORR.cjs.map +0 -1
- package/dist/chunk-5HCXH6GS.js +0 -409
- package/dist/chunk-5HCXH6GS.js.map +0 -1
- package/dist/chunk-5JJSRGJD.js.map +0 -1
- package/dist/chunk-5K6E4ZSW.cjs +0 -77
- package/dist/chunk-5K6E4ZSW.cjs.map +0 -1
- package/dist/chunk-5NY26ULO.js +0 -89
- package/dist/chunk-5NY26ULO.js.map +0 -1
- package/dist/chunk-7RVPG3LE.cjs +0 -231
- package/dist/chunk-7RVPG3LE.cjs.map +0 -1
- package/dist/chunk-AYEK3WOM.js +0 -207
- package/dist/chunk-AYEK3WOM.js.map +0 -1
- package/dist/chunk-BRMBLIQG.js +0 -53
- package/dist/chunk-BRMBLIQG.js.map +0 -1
- package/dist/chunk-CAJKSTXX.cjs.map +0 -1
- package/dist/chunk-CKFWMHQU.js +0 -401
- package/dist/chunk-CKFWMHQU.js.map +0 -1
- package/dist/chunk-CV3N3HVK.js +0 -672
- package/dist/chunk-CV3N3HVK.js.map +0 -1
- package/dist/chunk-D2EBLE2B.cjs.map +0 -1
- package/dist/chunk-GCZ6YATL.js +0 -940
- package/dist/chunk-GCZ6YATL.js.map +0 -1
- package/dist/chunk-GKRAZGDI.cjs +0 -84
- package/dist/chunk-GKRAZGDI.cjs.map +0 -1
- package/dist/chunk-GPYJ66CG.js +0 -45
- package/dist/chunk-GPYJ66CG.js.map +0 -1
- package/dist/chunk-HF6XU5NI.js +0 -84
- package/dist/chunk-HF6XU5NI.js.map +0 -1
- package/dist/chunk-HJPDZOMJ.cjs +0 -87
- package/dist/chunk-HJPDZOMJ.cjs.map +0 -1
- package/dist/chunk-HS3XI3CC.cjs +0 -69
- package/dist/chunk-HS3XI3CC.cjs.map +0 -1
- package/dist/chunk-HUCC3QH5.cjs +0 -53
- package/dist/chunk-HUCC3QH5.cjs.map +0 -1
- package/dist/chunk-HYZ6BQPS.cjs +0 -425
- package/dist/chunk-HYZ6BQPS.cjs.map +0 -1
- package/dist/chunk-ISCSZMYW.cjs +0 -106
- package/dist/chunk-ISCSZMYW.cjs.map +0 -1
- package/dist/chunk-IXFEFIDO.js +0 -82
- package/dist/chunk-IXFEFIDO.js.map +0 -1
- package/dist/chunk-JCJLN437.js +0 -108
- package/dist/chunk-JCJLN437.js.map +0 -1
- package/dist/chunk-JMKNNH63.cjs +0 -982
- package/dist/chunk-JMKNNH63.cjs.map +0 -1
- package/dist/chunk-JUBHQAA2.js +0 -53
- package/dist/chunk-JUBHQAA2.js.map +0 -1
- package/dist/chunk-K6G63EED.cjs +0 -41
- package/dist/chunk-K6G63EED.cjs.map +0 -1
- package/dist/chunk-KCWRCSI7.js +0 -62
- package/dist/chunk-KCWRCSI7.js.map +0 -1
- package/dist/chunk-KYM7NIJO.cjs +0 -433
- package/dist/chunk-KYM7NIJO.cjs.map +0 -1
- package/dist/chunk-L2L5CKC2.js +0 -291
- package/dist/chunk-L2L5CKC2.js.map +0 -1
- package/dist/chunk-M5CHZ5BA.js +0 -124
- package/dist/chunk-M5CHZ5BA.js.map +0 -1
- package/dist/chunk-MEK4RSGC.js +0 -65
- package/dist/chunk-MEK4RSGC.js.map +0 -1
- package/dist/chunk-MEKWH3GS.js +0 -89
- package/dist/chunk-MEKWH3GS.js.map +0 -1
- package/dist/chunk-MFTX2DDQ.js +0 -27
- package/dist/chunk-MFTX2DDQ.js.map +0 -1
- package/dist/chunk-MMC6M35Q.cjs.map +0 -1
- package/dist/chunk-NMH43BDC.js +0 -130
- package/dist/chunk-NMH43BDC.js.map +0 -1
- package/dist/chunk-NORDUD2T.cjs +0 -135
- package/dist/chunk-NORDUD2T.cjs.map +0 -1
- package/dist/chunk-NV4JOKWL.cjs +0 -197
- package/dist/chunk-NV4JOKWL.cjs.map +0 -1
- package/dist/chunk-O2JG7WY5.cjs +0 -121
- package/dist/chunk-O2JG7WY5.cjs.map +0 -1
- package/dist/chunk-ONO2FTV4.cjs +0 -68
- package/dist/chunk-ONO2FTV4.cjs.map +0 -1
- package/dist/chunk-OQB6HIUL.cjs +0 -108
- package/dist/chunk-OQB6HIUL.cjs.map +0 -1
- package/dist/chunk-OS6CMYAS.cjs +0 -79
- package/dist/chunk-OS6CMYAS.cjs.map +0 -1
- package/dist/chunk-PYURPUTV.js +0 -402
- package/dist/chunk-PYURPUTV.js.map +0 -1
- package/dist/chunk-RJ3HYZ7S.js +0 -44
- package/dist/chunk-RJ3HYZ7S.js.map +0 -1
- package/dist/chunk-RZNRIOLT.js +0 -128
- package/dist/chunk-RZNRIOLT.js.map +0 -1
- package/dist/chunk-S3T2L6NA.js +0 -38
- package/dist/chunk-S3T2L6NA.js.map +0 -1
- package/dist/chunk-S5IPJQZ3.cjs +0 -161
- package/dist/chunk-S5IPJQZ3.cjs.map +0 -1
- package/dist/chunk-SB5DSYR5.js.map +0 -1
- package/dist/chunk-SFXV2DUH.js +0 -106
- package/dist/chunk-SFXV2DUH.js.map +0 -1
- package/dist/chunk-SXEPGD4Z.cjs +0 -152
- package/dist/chunk-SXEPGD4Z.cjs.map +0 -1
- package/dist/chunk-SXWSOU3Y.js +0 -89
- package/dist/chunk-SXWSOU3Y.js.map +0 -1
- package/dist/chunk-SZMVOHT7.cjs +0 -107
- package/dist/chunk-SZMVOHT7.cjs.map +0 -1
- package/dist/chunk-TWJXOV4C.js +0 -145
- package/dist/chunk-TWJXOV4C.js.map +0 -1
- package/dist/chunk-U3ADRIVO.cjs +0 -434
- package/dist/chunk-U3ADRIVO.cjs.map +0 -1
- package/dist/chunk-U5FLLCGC.cjs +0 -151
- package/dist/chunk-U5FLLCGC.cjs.map +0 -1
- package/dist/chunk-UOZN45G4.cjs +0 -130
- package/dist/chunk-UOZN45G4.cjs.map +0 -1
- package/dist/chunk-VHLDOG74.cjs +0 -167
- package/dist/chunk-VHLDOG74.cjs.map +0 -1
- package/dist/chunk-YC5KLN6I.js +0 -139
- package/dist/chunk-YC5KLN6I.js.map +0 -1
- package/dist/chunk-YENXXYUV.cjs +0 -111
- package/dist/chunk-YENXXYUV.cjs.map +0 -1
- package/dist/chunk-YFQWC2PW.js +0 -113
- package/dist/chunk-YFQWC2PW.js.map +0 -1
- package/dist/chunk-Z2TY4A75.cjs +0 -700
- package/dist/chunk-Z2TY4A75.cjs.map +0 -1
- package/dist/chunk-Z56O7UEU.cjs +0 -136
- package/dist/chunk-Z56O7UEU.cjs.map +0 -1
- package/dist/chunk-ZBZDR4ZC.js +0 -106
- package/dist/chunk-ZBZDR4ZC.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/ui/social-platform-badge.tsx"],"names":[],"mappings":"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","file":"social-platform-badge.js","sourcesContent":["import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\nimport { formatJalaliDate } from '@/lib/jalali-utils'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\nexport type SupportedLocale = 'fa' | 'ar' | 'en'\n\n/**\n * Convert digits in a string to Persian/Arabic numerals based on locale.\n * @example convertToLocalNumbers('123', 'fa') => '۱۲۳'\n * @example convertToLocalNumbers('123', 'en') => '123'\n */\nexport function convertToLocalNumbers(text: string | number, locale: SupportedLocale): string {\n if (locale === 'fa' || locale === 'ar') {\n const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']\n return String(text).replace(/\\d/g, (digit) => persianDigits[parseInt(digit)])\n }\n return String(text)\n}\n\n/**\n * Format large numbers with locale-aware suffixes (K/M/B).\n * @example formatLargeNumber(1500, 'fa') => '۱.۵ هزار'\n * @example formatLargeNumber(1500, 'en') => '1.5K'\n */\nexport function formatLargeNumber(num: number, locale: SupportedLocale): string {\n if (num >= 1_000_000_000) {\n const formatted = (num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'B' : ' میلیارد')\n }\n if (num >= 1_000_000) {\n const formatted = (num / 1_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'M' : ' میلیون')\n }\n if (num >= 1_000) {\n const formatted = (num / 1_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'K' : ' هزار')\n }\n return convertToLocalNumbers(num.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ','), locale)\n}\n\n/**\n * Format number to Instagram-style short format (English only).\n * @example formatNumber(123456, 'short') => '123K'\n * @example formatNumber(123456, 'exact') => '123,456'\n */\nexport function formatNumber(num: number | undefined, format: 'exact' | 'short' = 'exact'): string {\n if (num === undefined || num === null) return '0'\n\n if (format === 'exact') {\n return num.toLocaleString('en-US')\n }\n\n // Short format (Instagram style)\n if (num >= 1_000_000_000) {\n return `${(num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')}B`\n }\n if (num >= 1_000_000) {\n return `${(num / 1_000_000).toFixed(1).replace(/\\.0$/, '')}M`\n }\n if (num >= 1_000) {\n return `${(num / 1_000).toFixed(1).replace(/\\.0$/, '')}K`\n }\n return num.toString()\n}\n\n/**\n * Format date to relative time with absolute on hover (Persian)\n * @example formatRelativeTime(new Date()) => '۲ ساعت پیش'\n */\nexport function formatRelativeTime(date: Date | string | number): string {\n const now = new Date()\n const then = new Date(date)\n const diffInSeconds = Math.floor((now.getTime() - then.getTime()) / 1000)\n\n if (diffInSeconds < 60) {\n return 'همین الان'\n }\n\n const diffInMinutes = Math.floor(diffInSeconds / 60)\n if (diffInMinutes < 60) {\n return `${convertToLocalNumbers(diffInMinutes, 'fa')} دقیقه پیش`\n }\n\n const diffInHours = Math.floor(diffInMinutes / 60)\n if (diffInHours < 24) {\n return `${convertToLocalNumbers(diffInHours, 'fa')} ساعت پیش`\n }\n\n const diffInDays = Math.floor(diffInHours / 24)\n if (diffInDays < 7) {\n return `${convertToLocalNumbers(diffInDays, 'fa')} روز پیش`\n }\n\n const diffInWeeks = Math.floor(diffInDays / 7)\n if (diffInWeeks < 4) {\n return `${convertToLocalNumbers(diffInWeeks, 'fa')} هفته پیش`\n }\n\n const diffInMonths = Math.floor(diffInDays / 30)\n if (diffInMonths < 12) {\n return `${convertToLocalNumbers(diffInMonths, 'fa')} ماه پیش`\n }\n\n const diffInYears = Math.floor(diffInDays / 365)\n return `${convertToLocalNumbers(diffInYears, 'fa')} سال پیش`\n}\n\n/**\n * Format date to absolute format (Persian / Jalali)\n * Uses date-fns-jalali for accurate Jalali conversion.\n * @example formatAbsoluteTime(new Date()) => '۱۵ دی ۱۴۰۳، ۱۵:۳۰'\n */\nexport function formatAbsoluteTime(date: Date | string | number): string {\n const d = new Date(date)\n return formatJalaliDate(d, 'd MMMM yyyy، HH:mm')\n}\n","'use client'\n\nimport * as React from 'react'\nimport { cva } from 'class-variance-authority'\nimport { cn } from '@/lib/utils'\n\nexport type SocialPlatform =\n | 'instagram'\n | 'twitter'\n | 'tiktok'\n | 'youtube'\n | 'linkedin'\n | 'telegram'\n | 'threads'\n | 'facebook'\n\nexport interface SocialPlatformBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** The social media platform to display */\n platform: SocialPlatform\n /** Size of the badge */\n size?: 'xs' | 'sm' | 'md' | 'lg'\n /** Show the platform name label */\n showLabel?: boolean\n /** Visual style of the badge */\n variant?: 'flat' | 'badge'\n}\n\nconst PLATFORM_META: Record<\n SocialPlatform,\n { label: string; icon: React.FC<{ className?: string; style?: React.CSSProperties }> }\n> = {\n instagram: {\n label: 'Instagram',\n icon: ({ className }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z\" />\n </svg>\n ),\n },\n twitter: {\n label: 'X',\n icon: ({ className }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.737-8.835L1.254 2.25H8.08l4.259 5.63 5.905-5.63zm-1.161 17.52h1.833L7.084 4.126H5.117z\" />\n </svg>\n ),\n },\n tiktok: {\n label: 'TikTok',\n icon: ({ className }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M19.59 6.69a4.83 4.83 0 01-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 01-2.88 2.5 2.89 2.89 0 01-2.89-2.89 2.89 2.89 0 012.89-2.89c.28 0 .54.04.79.1V9.01a6.33 6.33 0 00-.79-.05 6.34 6.34 0 00-6.34 6.34 6.34 6.34 0 006.34 6.34 6.34 6.34 0 006.33-6.34V8.69a8.16 8.16 0 004.77 1.52V6.75a4.85 4.85 0 01-1-.06z\" />\n </svg>\n ),\n },\n youtube: {\n label: 'YouTube',\n icon: ({ className }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M23.498 6.186a3.016 3.016 0 00-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 00.502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 002.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 002.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z\" />\n </svg>\n ),\n },\n linkedin: {\n label: 'LinkedIn',\n icon: ({ className }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z\" />\n </svg>\n ),\n },\n telegram: {\n label: 'Telegram',\n icon: ({ className }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M11.944 0A12 12 0 000 12a12 12 0 0012 12 12 12 0 0012-12A12 12 0 0012 0a12 12 0 00-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 01.171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z\" />\n </svg>\n ),\n },\n threads: {\n label: 'Threads',\n icon: ({ className }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.868 1.205 8.617.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.783 3.631 2.698 6.54 2.717 2.623-.02 4.358-.631 5.8-2.045 1.647-1.613 1.618-3.593 1.09-4.798-.31-.71-.873-1.3-1.634-1.75-.192 1.352-.622 2.446-1.284 3.272-.886 1.102-2.14 1.704-3.73 1.79-1.202.065-2.361-.218-3.259-.801-1.063-.689-1.685-1.749-1.752-2.98-.065-1.19.388-2.285 1.277-3.086.84-.754 2.02-1.164 3.43-1.19.954-.018 1.858.09 2.695.322-.082-.858-.372-1.502-.861-1.903-.529-.434-1.32-.658-2.332-.67-.884.01-1.63.23-2.22.65l-1.11-1.75c.97-.625 2.18-.95 3.6-.97h.022c1.63.02 2.976.497 3.898 1.385.955.92 1.444 2.25 1.51 3.95.23.073.46.149.682.232 1.69.64 2.847 1.635 3.406 2.946.859 2.002.86 5.139-1.773 7.674-1.797 1.742-3.993 2.525-6.935 2.545zm.098-10.02c-.745.01-1.377.186-1.882.523-.443.298-.706.733-.67 1.215.03.564.342 1.017.876 1.275.516.25 1.087.299 1.58.127.684-.24 1.34-.93 1.614-2.366a9.68 9.68 0 00-1.518-.774z\" />\n </svg>\n ),\n },\n facebook: {\n label: 'Facebook',\n icon: ({ className }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.875v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z\" />\n </svg>\n ),\n },\n}\n\nconst socialPlatformBadgeVariants = cva('inline-flex items-center font-medium transition-colors duration-150', {\n variants: {\n size: {\n xs: 'gap-1',\n sm: 'gap-1.5',\n md: 'gap-1.5',\n lg: 'gap-2',\n },\n variant: {\n flat: '',\n badge: 'border border-border bg-surface-100',\n },\n },\n compoundVariants: [\n { variant: 'badge', size: 'xs', className: 'px-1.5 py-0.5 rounded' },\n { variant: 'badge', size: 'sm', className: 'px-2 py-1 rounded' },\n { variant: 'badge', size: 'md', className: 'px-2.5 py-1 rounded-md' },\n { variant: 'badge', size: 'lg', className: 'px-3 py-1.5 rounded-md' },\n ],\n defaultVariants: { size: 'sm', variant: 'flat' },\n})\n\n/** Size-specific classes for child elements (icon, text) */\nconst SIZE_CHILD_CLASSES = {\n xs: { icon: 'size-3', text: 'text-xs' },\n sm: { icon: 'size-4', text: 'text-xs' },\n md: { icon: 'size-5', text: 'text-sm' },\n lg: { icon: 'size-6', text: 'text-base' },\n}\n\nconst SocialPlatformBadge = React.forwardRef<HTMLSpanElement, SocialPlatformBadgeProps>(\n ({ className, platform, size = 'sm', showLabel = false, variant = 'flat', style, ...props }, ref) => {\n const meta = PLATFORM_META[platform]\n const Icon = meta.icon\n const sizeClasses = SIZE_CHILD_CLASSES[size]\n\n return (\n <span\n ref={ref}\n data-slot=\"social-platform-badge\"\n className={cn(socialPlatformBadgeVariants({ size, variant }), className)}\n style={style}\n {...props}\n >\n <Icon className={cn(sizeClasses.icon, 'shrink-0')} style={{ color: `var(--social-platform-${platform})` }} />\n {showLabel && <span className={cn(sizeClasses.text, 'text-foreground-light')}>{meta.label}</span>}\n </span>\n )\n }\n)\n\nSocialPlatformBadge.displayName = 'SocialPlatformBadge'\n\nexport { SocialPlatformBadge, socialPlatformBadgeVariants }\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var sonner = require('sonner');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
var Toaster = ({ className, ...props }) => {
|
|
13
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14
|
+
sonner.Toaster,
|
|
15
|
+
{
|
|
16
|
+
"data-slot": "toaster",
|
|
17
|
+
className: cn("toaster group", className),
|
|
18
|
+
dir: "rtl",
|
|
19
|
+
position: "top-center",
|
|
20
|
+
closeButton: true,
|
|
21
|
+
toastOptions: {
|
|
22
|
+
classNames: {
|
|
23
|
+
toast: "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg font-[var(--font-sans)]",
|
|
24
|
+
description: "group-[.toast]:text-muted-foreground",
|
|
25
|
+
actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
|
26
|
+
cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
|
|
27
|
+
success: "group-[.toaster]:border-[hsl(var(--brand-default)/0.4)] group-[.toaster]:bg-[hsl(var(--brand-default)/0.06)] group-[.toaster]:text-[hsl(var(--brand-600))]",
|
|
28
|
+
error: "group-[.toaster]:border-[hsl(var(--destructive-500)/0.4)] group-[.toaster]:bg-[hsl(var(--destructive-500)/0.06)] group-[.toaster]:text-[hsl(var(--destructive-600))]",
|
|
29
|
+
warning: "group-[.toaster]:border-[hsl(var(--warning-500)/0.4)] group-[.toaster]:bg-[hsl(var(--warning-500)/0.06)] group-[.toaster]:text-[hsl(var(--warning))]",
|
|
30
|
+
// 1.1.16 — `info` toast was pointing at --secondary-default
|
|
31
|
+
// (hue 247° purple, legacy Supabase token). info has its own
|
|
32
|
+
// semantic family now (--info-default, added 1.1.15), so wire
|
|
33
|
+
// the toast to it.
|
|
34
|
+
info: "group-[.toaster]:border-[hsl(var(--info-default)/0.4)] group-[.toaster]:bg-[hsl(var(--info-default)/0.06)] group-[.toaster]:text-[hsl(var(--info-default))]"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
...props
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
Toaster.displayName = "Toaster";
|
|
42
|
+
var SONNER_DEFAULT_DURATION = 4e3;
|
|
43
|
+
|
|
44
|
+
Object.defineProperty(exports, "toast", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
get: function () { return sonner.toast; }
|
|
47
|
+
});
|
|
48
|
+
exports.SONNER_DEFAULT_DURATION = SONNER_DEFAULT_DURATION;
|
|
49
|
+
exports.Toaster = Toaster;
|
|
50
|
+
//# sourceMappingURL=sonner.cjs.map
|
|
51
|
+
//# sourceMappingURL=sonner.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/ui/sonner.tsx"],"names":["twMerge","clsx","jsx","Sonner"],"mappings":";;;;;;;AAIO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAOA,qBAAA,CAAQC,SAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACCA,IAAM,UAAU,CAAC,EAAE,SAAA,EAAW,GAAG,OAAM,KAAyB;AAC9D,EAAA,uBACEC,cAAA;AAAA,IAACC,cAAA;AAAA,IAAA;AAAA,MACC,WAAA,EAAU,SAAA;AAAA,MACV,SAAA,EAAW,EAAA,CAAG,eAAA,EAAiB,SAAS,CAAA;AAAA,MACxC,GAAA,EAAI,KAAA;AAAA,MACJ,QAAA,EAAS,YAAA;AAAA,MACT,WAAA,EAAW,IAAA;AAAA,MACX,YAAA,EAAc;AAAA,QACZ,UAAA,EAAY;AAAA,UACV,KAAA,EACE,+JAAA;AAAA,UACF,WAAA,EAAa,sCAAA;AAAA,UACb,YAAA,EAAc,kEAAA;AAAA,UACd,YAAA,EAAc,8DAAA;AAAA,UACd,OAAA,EACE,4JAAA;AAAA,UACF,KAAA,EACE,sKAAA;AAAA,UACF,OAAA,EACE,sJAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAKF,IAAA,EAAM;AAAA;AACR,OACF;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ;AACA,OAAA,CAAQ,WAAA,GAAc,SAAA;AAIf,IAAM,uBAAA,GAA0B","file":"sonner.cjs","sourcesContent":["import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\nimport { formatJalaliDate } from '@/lib/jalali-utils'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\nexport type SupportedLocale = 'fa' | 'ar' | 'en'\n\n/**\n * Convert digits in a string to Persian/Arabic numerals based on locale.\n * @example convertToLocalNumbers('123', 'fa') => '۱۲۳'\n * @example convertToLocalNumbers('123', 'en') => '123'\n */\nexport function convertToLocalNumbers(text: string | number, locale: SupportedLocale): string {\n if (locale === 'fa' || locale === 'ar') {\n const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']\n return String(text).replace(/\\d/g, (digit) => persianDigits[parseInt(digit)])\n }\n return String(text)\n}\n\n/**\n * Format large numbers with locale-aware suffixes (K/M/B).\n * @example formatLargeNumber(1500, 'fa') => '۱.۵ هزار'\n * @example formatLargeNumber(1500, 'en') => '1.5K'\n */\nexport function formatLargeNumber(num: number, locale: SupportedLocale): string {\n if (num >= 1_000_000_000) {\n const formatted = (num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'B' : ' میلیارد')\n }\n if (num >= 1_000_000) {\n const formatted = (num / 1_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'M' : ' میلیون')\n }\n if (num >= 1_000) {\n const formatted = (num / 1_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'K' : ' هزار')\n }\n return convertToLocalNumbers(num.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ','), locale)\n}\n\n/**\n * Format number to Instagram-style short format (English only).\n * @example formatNumber(123456, 'short') => '123K'\n * @example formatNumber(123456, 'exact') => '123,456'\n */\nexport function formatNumber(num: number | undefined, format: 'exact' | 'short' = 'exact'): string {\n if (num === undefined || num === null) return '0'\n\n if (format === 'exact') {\n return num.toLocaleString('en-US')\n }\n\n // Short format (Instagram style)\n if (num >= 1_000_000_000) {\n return `${(num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')}B`\n }\n if (num >= 1_000_000) {\n return `${(num / 1_000_000).toFixed(1).replace(/\\.0$/, '')}M`\n }\n if (num >= 1_000) {\n return `${(num / 1_000).toFixed(1).replace(/\\.0$/, '')}K`\n }\n return num.toString()\n}\n\n/**\n * Format date to relative time with absolute on hover (Persian)\n * @example formatRelativeTime(new Date()) => '۲ ساعت پیش'\n */\nexport function formatRelativeTime(date: Date | string | number): string {\n const now = new Date()\n const then = new Date(date)\n const diffInSeconds = Math.floor((now.getTime() - then.getTime()) / 1000)\n\n if (diffInSeconds < 60) {\n return 'همین الان'\n }\n\n const diffInMinutes = Math.floor(diffInSeconds / 60)\n if (diffInMinutes < 60) {\n return `${convertToLocalNumbers(diffInMinutes, 'fa')} دقیقه پیش`\n }\n\n const diffInHours = Math.floor(diffInMinutes / 60)\n if (diffInHours < 24) {\n return `${convertToLocalNumbers(diffInHours, 'fa')} ساعت پیش`\n }\n\n const diffInDays = Math.floor(diffInHours / 24)\n if (diffInDays < 7) {\n return `${convertToLocalNumbers(diffInDays, 'fa')} روز پیش`\n }\n\n const diffInWeeks = Math.floor(diffInDays / 7)\n if (diffInWeeks < 4) {\n return `${convertToLocalNumbers(diffInWeeks, 'fa')} هفته پیش`\n }\n\n const diffInMonths = Math.floor(diffInDays / 30)\n if (diffInMonths < 12) {\n return `${convertToLocalNumbers(diffInMonths, 'fa')} ماه پیش`\n }\n\n const diffInYears = Math.floor(diffInDays / 365)\n return `${convertToLocalNumbers(diffInYears, 'fa')} سال پیش`\n}\n\n/**\n * Format date to absolute format (Persian / Jalali)\n * Uses date-fns-jalali for accurate Jalali conversion.\n * @example formatAbsoluteTime(new Date()) => '۱۵ دی ۱۴۰۳، ۱۵:۳۰'\n */\nexport function formatAbsoluteTime(date: Date | string | number): string {\n const d = new Date(date)\n return formatJalaliDate(d, 'd MMMM yyyy، HH:mm')\n}\n","'use client'\n\nimport { Toaster as Sonner, type ToasterProps } from 'sonner'\nimport { cn } from '@/lib/utils'\n\nexport type PartoToasterProps = ToasterProps\n\nconst Toaster = ({ className, ...props }: PartoToasterProps) => {\n return (\n <Sonner\n data-slot=\"toaster\"\n className={cn('toaster group', className)}\n dir=\"rtl\"\n position=\"top-center\"\n closeButton\n toastOptions={{\n classNames: {\n toast:\n 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg font-[var(--font-sans)]',\n description: 'group-[.toast]:text-muted-foreground',\n actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',\n cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',\n success:\n 'group-[.toaster]:border-[hsl(var(--brand-default)/0.4)] group-[.toaster]:bg-[hsl(var(--brand-default)/0.06)] group-[.toaster]:text-[hsl(var(--brand-600))]',\n error:\n 'group-[.toaster]:border-[hsl(var(--destructive-500)/0.4)] group-[.toaster]:bg-[hsl(var(--destructive-500)/0.06)] group-[.toaster]:text-[hsl(var(--destructive-600))]',\n warning:\n 'group-[.toaster]:border-[hsl(var(--warning-500)/0.4)] group-[.toaster]:bg-[hsl(var(--warning-500)/0.06)] group-[.toaster]:text-[hsl(var(--warning))]',\n // 1.1.16 — `info` toast was pointing at --secondary-default\n // (hue 247° purple, legacy Supabase token). info has its own\n // semantic family now (--info-default, added 1.1.15), so wire\n // the toast to it.\n info: 'group-[.toaster]:border-[hsl(var(--info-default)/0.4)] group-[.toaster]:bg-[hsl(var(--info-default)/0.06)] group-[.toaster]:text-[hsl(var(--info-default))]',\n },\n }}\n {...props}\n />\n )\n}\nToaster.displayName = 'Toaster'\n\nexport { Toaster }\nexport { toast } from 'sonner'\nexport const SONNER_DEFAULT_DURATION = 4000\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ToasterProps } from 'sonner';
|
|
3
|
+
export { toast } from 'sonner';
|
|
4
|
+
|
|
5
|
+
type PartoToasterProps = ToasterProps;
|
|
6
|
+
declare const Toaster: {
|
|
7
|
+
({ className, ...props }: PartoToasterProps): react_jsx_runtime.JSX.Element;
|
|
8
|
+
displayName: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
declare const SONNER_DEFAULT_DURATION = 4000;
|
|
12
|
+
|
|
13
|
+
export { type PartoToasterProps, SONNER_DEFAULT_DURATION, Toaster };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ToasterProps } from 'sonner';
|
|
3
|
+
export { toast } from 'sonner';
|
|
4
|
+
|
|
5
|
+
type PartoToasterProps = ToasterProps;
|
|
6
|
+
declare const Toaster: {
|
|
7
|
+
({ className, ...props }: PartoToasterProps): react_jsx_runtime.JSX.Element;
|
|
8
|
+
displayName: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
declare const SONNER_DEFAULT_DURATION = 4000;
|
|
12
|
+
|
|
13
|
+
export { type PartoToasterProps, SONNER_DEFAULT_DURATION, Toaster };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Toaster as Toaster$1 } from 'sonner';
|
|
3
|
+
export { toast } from 'sonner';
|
|
4
|
+
import { clsx } from 'clsx';
|
|
5
|
+
import { twMerge } from 'tailwind-merge';
|
|
6
|
+
import { jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function cn(...inputs) {
|
|
9
|
+
return twMerge(clsx(inputs));
|
|
10
|
+
}
|
|
11
|
+
var Toaster = ({ className, ...props }) => {
|
|
12
|
+
return /* @__PURE__ */ jsx(
|
|
13
|
+
Toaster$1,
|
|
14
|
+
{
|
|
15
|
+
"data-slot": "toaster",
|
|
16
|
+
className: cn("toaster group", className),
|
|
17
|
+
dir: "rtl",
|
|
18
|
+
position: "top-center",
|
|
19
|
+
closeButton: true,
|
|
20
|
+
toastOptions: {
|
|
21
|
+
classNames: {
|
|
22
|
+
toast: "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg font-[var(--font-sans)]",
|
|
23
|
+
description: "group-[.toast]:text-muted-foreground",
|
|
24
|
+
actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
|
25
|
+
cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
|
|
26
|
+
success: "group-[.toaster]:border-[hsl(var(--brand-default)/0.4)] group-[.toaster]:bg-[hsl(var(--brand-default)/0.06)] group-[.toaster]:text-[hsl(var(--brand-600))]",
|
|
27
|
+
error: "group-[.toaster]:border-[hsl(var(--destructive-500)/0.4)] group-[.toaster]:bg-[hsl(var(--destructive-500)/0.06)] group-[.toaster]:text-[hsl(var(--destructive-600))]",
|
|
28
|
+
warning: "group-[.toaster]:border-[hsl(var(--warning-500)/0.4)] group-[.toaster]:bg-[hsl(var(--warning-500)/0.06)] group-[.toaster]:text-[hsl(var(--warning))]",
|
|
29
|
+
// 1.1.16 — `info` toast was pointing at --secondary-default
|
|
30
|
+
// (hue 247° purple, legacy Supabase token). info has its own
|
|
31
|
+
// semantic family now (--info-default, added 1.1.15), so wire
|
|
32
|
+
// the toast to it.
|
|
33
|
+
info: "group-[.toaster]:border-[hsl(var(--info-default)/0.4)] group-[.toaster]:bg-[hsl(var(--info-default)/0.06)] group-[.toaster]:text-[hsl(var(--info-default))]"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
...props
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
Toaster.displayName = "Toaster";
|
|
41
|
+
var SONNER_DEFAULT_DURATION = 4e3;
|
|
42
|
+
|
|
43
|
+
export { SONNER_DEFAULT_DURATION, Toaster };
|
|
44
|
+
//# sourceMappingURL=sonner.js.map
|
|
45
|
+
//# sourceMappingURL=sonner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/ui/sonner.tsx"],"names":["Sonner"],"mappings":";;;;;;AAIO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACCA,IAAM,UAAU,CAAC,EAAE,SAAA,EAAW,GAAG,OAAM,KAAyB;AAC9D,EAAA,uBACE,GAAA;AAAA,IAACA,SAAA;AAAA,IAAA;AAAA,MACC,WAAA,EAAU,SAAA;AAAA,MACV,SAAA,EAAW,EAAA,CAAG,eAAA,EAAiB,SAAS,CAAA;AAAA,MACxC,GAAA,EAAI,KAAA;AAAA,MACJ,QAAA,EAAS,YAAA;AAAA,MACT,WAAA,EAAW,IAAA;AAAA,MACX,YAAA,EAAc;AAAA,QACZ,UAAA,EAAY;AAAA,UACV,KAAA,EACE,+JAAA;AAAA,UACF,WAAA,EAAa,sCAAA;AAAA,UACb,YAAA,EAAc,kEAAA;AAAA,UACd,YAAA,EAAc,8DAAA;AAAA,UACd,OAAA,EACE,4JAAA;AAAA,UACF,KAAA,EACE,sKAAA;AAAA,UACF,OAAA,EACE,sJAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAKF,IAAA,EAAM;AAAA;AACR,OACF;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ;AACA,OAAA,CAAQ,WAAA,GAAc,SAAA;AAIf,IAAM,uBAAA,GAA0B","file":"sonner.js","sourcesContent":["import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\nimport { formatJalaliDate } from '@/lib/jalali-utils'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\nexport type SupportedLocale = 'fa' | 'ar' | 'en'\n\n/**\n * Convert digits in a string to Persian/Arabic numerals based on locale.\n * @example convertToLocalNumbers('123', 'fa') => '۱۲۳'\n * @example convertToLocalNumbers('123', 'en') => '123'\n */\nexport function convertToLocalNumbers(text: string | number, locale: SupportedLocale): string {\n if (locale === 'fa' || locale === 'ar') {\n const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']\n return String(text).replace(/\\d/g, (digit) => persianDigits[parseInt(digit)])\n }\n return String(text)\n}\n\n/**\n * Format large numbers with locale-aware suffixes (K/M/B).\n * @example formatLargeNumber(1500, 'fa') => '۱.۵ هزار'\n * @example formatLargeNumber(1500, 'en') => '1.5K'\n */\nexport function formatLargeNumber(num: number, locale: SupportedLocale): string {\n if (num >= 1_000_000_000) {\n const formatted = (num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'B' : ' میلیارد')\n }\n if (num >= 1_000_000) {\n const formatted = (num / 1_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'M' : ' میلیون')\n }\n if (num >= 1_000) {\n const formatted = (num / 1_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'K' : ' هزار')\n }\n return convertToLocalNumbers(num.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ','), locale)\n}\n\n/**\n * Format number to Instagram-style short format (English only).\n * @example formatNumber(123456, 'short') => '123K'\n * @example formatNumber(123456, 'exact') => '123,456'\n */\nexport function formatNumber(num: number | undefined, format: 'exact' | 'short' = 'exact'): string {\n if (num === undefined || num === null) return '0'\n\n if (format === 'exact') {\n return num.toLocaleString('en-US')\n }\n\n // Short format (Instagram style)\n if (num >= 1_000_000_000) {\n return `${(num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')}B`\n }\n if (num >= 1_000_000) {\n return `${(num / 1_000_000).toFixed(1).replace(/\\.0$/, '')}M`\n }\n if (num >= 1_000) {\n return `${(num / 1_000).toFixed(1).replace(/\\.0$/, '')}K`\n }\n return num.toString()\n}\n\n/**\n * Format date to relative time with absolute on hover (Persian)\n * @example formatRelativeTime(new Date()) => '۲ ساعت پیش'\n */\nexport function formatRelativeTime(date: Date | string | number): string {\n const now = new Date()\n const then = new Date(date)\n const diffInSeconds = Math.floor((now.getTime() - then.getTime()) / 1000)\n\n if (diffInSeconds < 60) {\n return 'همین الان'\n }\n\n const diffInMinutes = Math.floor(diffInSeconds / 60)\n if (diffInMinutes < 60) {\n return `${convertToLocalNumbers(diffInMinutes, 'fa')} دقیقه پیش`\n }\n\n const diffInHours = Math.floor(diffInMinutes / 60)\n if (diffInHours < 24) {\n return `${convertToLocalNumbers(diffInHours, 'fa')} ساعت پیش`\n }\n\n const diffInDays = Math.floor(diffInHours / 24)\n if (diffInDays < 7) {\n return `${convertToLocalNumbers(diffInDays, 'fa')} روز پیش`\n }\n\n const diffInWeeks = Math.floor(diffInDays / 7)\n if (diffInWeeks < 4) {\n return `${convertToLocalNumbers(diffInWeeks, 'fa')} هفته پیش`\n }\n\n const diffInMonths = Math.floor(diffInDays / 30)\n if (diffInMonths < 12) {\n return `${convertToLocalNumbers(diffInMonths, 'fa')} ماه پیش`\n }\n\n const diffInYears = Math.floor(diffInDays / 365)\n return `${convertToLocalNumbers(diffInYears, 'fa')} سال پیش`\n}\n\n/**\n * Format date to absolute format (Persian / Jalali)\n * Uses date-fns-jalali for accurate Jalali conversion.\n * @example formatAbsoluteTime(new Date()) => '۱۵ دی ۱۴۰۳، ۱۵:۳۰'\n */\nexport function formatAbsoluteTime(date: Date | string | number): string {\n const d = new Date(date)\n return formatJalaliDate(d, 'd MMMM yyyy، HH:mm')\n}\n","'use client'\n\nimport { Toaster as Sonner, type ToasterProps } from 'sonner'\nimport { cn } from '@/lib/utils'\n\nexport type PartoToasterProps = ToasterProps\n\nconst Toaster = ({ className, ...props }: PartoToasterProps) => {\n return (\n <Sonner\n data-slot=\"toaster\"\n className={cn('toaster group', className)}\n dir=\"rtl\"\n position=\"top-center\"\n closeButton\n toastOptions={{\n classNames: {\n toast:\n 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg font-[var(--font-sans)]',\n description: 'group-[.toast]:text-muted-foreground',\n actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',\n cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',\n success:\n 'group-[.toaster]:border-[hsl(var(--brand-default)/0.4)] group-[.toaster]:bg-[hsl(var(--brand-default)/0.06)] group-[.toaster]:text-[hsl(var(--brand-600))]',\n error:\n 'group-[.toaster]:border-[hsl(var(--destructive-500)/0.4)] group-[.toaster]:bg-[hsl(var(--destructive-500)/0.06)] group-[.toaster]:text-[hsl(var(--destructive-600))]',\n warning:\n 'group-[.toaster]:border-[hsl(var(--warning-500)/0.4)] group-[.toaster]:bg-[hsl(var(--warning-500)/0.06)] group-[.toaster]:text-[hsl(var(--warning))]',\n // 1.1.16 — `info` toast was pointing at --secondary-default\n // (hue 247° purple, legacy Supabase token). info has its own\n // semantic family now (--info-default, added 1.1.15), so wire\n // the toast to it.\n info: 'group-[.toaster]:border-[hsl(var(--info-default)/0.4)] group-[.toaster]:bg-[hsl(var(--info-default)/0.06)] group-[.toaster]:text-[hsl(var(--info-default))]',\n },\n }}\n {...props}\n />\n )\n}\nToaster.displayName = 'Toaster'\n\nexport { Toaster }\nexport { toast } from 'sonner'\nexport const SONNER_DEFAULT_DURATION = 4000\n"]}
|
|
@@ -1,13 +1,166 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
|
-
var
|
|
4
|
-
require('
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
8
|
|
|
9
|
+
function _interopNamespace(e) {
|
|
10
|
+
if (e && e.__esModule) return e;
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
6
26
|
|
|
27
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
7
28
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
29
|
+
function cn(...inputs) {
|
|
30
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
31
|
+
}
|
|
32
|
+
var TREND_CLASSES = {
|
|
33
|
+
up: "text-[hsl(var(--sentiment-positive))]",
|
|
34
|
+
down: "text-[hsl(var(--sentiment-negative))]",
|
|
35
|
+
neutral: "text-[hsl(var(--sentiment-neutral))]"
|
|
36
|
+
};
|
|
37
|
+
function resolveTrend(data, trend) {
|
|
38
|
+
if (trend !== "auto") return trend;
|
|
39
|
+
if (data.length < 2) return "neutral";
|
|
40
|
+
const first = data[0];
|
|
41
|
+
const last = data[data.length - 1];
|
|
42
|
+
if (last > first) return "up";
|
|
43
|
+
if (last < first) return "down";
|
|
44
|
+
return "neutral";
|
|
45
|
+
}
|
|
46
|
+
var Sparkline = React__namespace.forwardRef(
|
|
47
|
+
({
|
|
48
|
+
className,
|
|
49
|
+
data,
|
|
50
|
+
variant = "line",
|
|
51
|
+
trend = "auto",
|
|
52
|
+
width = 80,
|
|
53
|
+
height = 24,
|
|
54
|
+
strokeWidth = 1.5,
|
|
55
|
+
showEndDot = false,
|
|
56
|
+
padding = 2,
|
|
57
|
+
color,
|
|
58
|
+
barGap = 1,
|
|
59
|
+
"aria-label": ariaLabel,
|
|
60
|
+
...props
|
|
61
|
+
}, ref) => {
|
|
62
|
+
const effectiveTrend = resolveTrend(data, trend);
|
|
63
|
+
const trendColorClass = color ? void 0 : TREND_CLASSES[effectiveTrend];
|
|
64
|
+
if (data.length === 0) {
|
|
65
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
66
|
+
"div",
|
|
67
|
+
{
|
|
68
|
+
ref,
|
|
69
|
+
"data-slot": "sparkline",
|
|
70
|
+
"data-empty": "true",
|
|
71
|
+
"aria-hidden": "true",
|
|
72
|
+
className: cn("inline-block", className),
|
|
73
|
+
style: { width, height },
|
|
74
|
+
...props
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
const min = Math.min(...data);
|
|
79
|
+
const max = Math.max(...data);
|
|
80
|
+
const range = max - min || 1;
|
|
81
|
+
const innerW = width - padding * 2;
|
|
82
|
+
const innerH = height - padding * 2;
|
|
83
|
+
const stepX = data.length > 1 ? innerW / (data.length - 1) : 0;
|
|
84
|
+
const yFor = (v) => padding + innerH - (v - min) / range * innerH;
|
|
85
|
+
const xFor = (i) => padding + i * stepX;
|
|
86
|
+
const points = data.map((v, i) => ({ x: xFor(i), y: yFor(v) }));
|
|
87
|
+
const linePath = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(2)} ${p.y.toFixed(2)}`).join(" ");
|
|
88
|
+
const areaPath = `${linePath} L${points[points.length - 1].x.toFixed(2)} ${(height - padding).toFixed(
|
|
89
|
+
2
|
|
90
|
+
)} L${points[0].x.toFixed(2)} ${(height - padding).toFixed(2)} Z`;
|
|
91
|
+
const stroke = color ?? "currentColor";
|
|
92
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
ref,
|
|
96
|
+
"data-slot": "sparkline",
|
|
97
|
+
"data-variant": variant,
|
|
98
|
+
"data-trend": effectiveTrend,
|
|
99
|
+
role: ariaLabel ? "img" : void 0,
|
|
100
|
+
"aria-label": ariaLabel,
|
|
101
|
+
"aria-hidden": ariaLabel ? void 0 : true,
|
|
102
|
+
className: cn("inline-block align-middle", trendColorClass, className),
|
|
103
|
+
style: { width, height },
|
|
104
|
+
...props,
|
|
105
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
106
|
+
"svg",
|
|
107
|
+
{
|
|
108
|
+
width,
|
|
109
|
+
height,
|
|
110
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
111
|
+
preserveAspectRatio: "none",
|
|
112
|
+
focusable: "false",
|
|
113
|
+
children: [
|
|
114
|
+
variant === "area" && /* @__PURE__ */ jsxRuntime.jsx("path", { d: areaPath, fill: stroke, opacity: 0.18 }),
|
|
115
|
+
(variant === "line" || variant === "area") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
116
|
+
"path",
|
|
117
|
+
{
|
|
118
|
+
d: linePath,
|
|
119
|
+
fill: "none",
|
|
120
|
+
stroke,
|
|
121
|
+
strokeWidth,
|
|
122
|
+
strokeLinecap: "round",
|
|
123
|
+
strokeLinejoin: "round",
|
|
124
|
+
vectorEffect: "non-scaling-stroke"
|
|
125
|
+
}
|
|
126
|
+
),
|
|
127
|
+
variant === "bars" && data.map((v, i) => {
|
|
128
|
+
const gap = barGap;
|
|
129
|
+
const barW = Math.max(0, stepX - gap);
|
|
130
|
+
const y = yFor(v);
|
|
131
|
+
const barH = Math.max(1, height - padding - y);
|
|
132
|
+
const x = xFor(i) - barW / 2;
|
|
133
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
134
|
+
"rect",
|
|
135
|
+
{
|
|
136
|
+
x: x.toFixed(2),
|
|
137
|
+
y: y.toFixed(2),
|
|
138
|
+
width: barW.toFixed(2),
|
|
139
|
+
height: barH.toFixed(2),
|
|
140
|
+
rx: Math.min(1, barW / 3),
|
|
141
|
+
fill: stroke
|
|
142
|
+
},
|
|
143
|
+
i
|
|
144
|
+
);
|
|
145
|
+
}),
|
|
146
|
+
showEndDot && variant !== "bars" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
147
|
+
"circle",
|
|
148
|
+
{
|
|
149
|
+
cx: points[points.length - 1].x.toFixed(2),
|
|
150
|
+
cy: points[points.length - 1].y.toFixed(2),
|
|
151
|
+
r: strokeWidth + 0.5,
|
|
152
|
+
fill: stroke
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
Sparkline.displayName = "Sparkline";
|
|
163
|
+
|
|
164
|
+
exports.Sparkline = Sparkline;
|
|
12
165
|
//# sourceMappingURL=sparkline.cjs.map
|
|
13
166
|
//# sourceMappingURL=sparkline.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"sparkline.cjs"}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/ui/sparkline.tsx"],"names":["twMerge","clsx","React","jsx","jsxs"],"mappings":"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","file":"sparkline.cjs","sourcesContent":["import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\nimport { formatJalaliDate } from '@/lib/jalali-utils'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\nexport type SupportedLocale = 'fa' | 'ar' | 'en'\n\n/**\n * Convert digits in a string to Persian/Arabic numerals based on locale.\n * @example convertToLocalNumbers('123', 'fa') => '۱۲۳'\n * @example convertToLocalNumbers('123', 'en') => '123'\n */\nexport function convertToLocalNumbers(text: string | number, locale: SupportedLocale): string {\n if (locale === 'fa' || locale === 'ar') {\n const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']\n return String(text).replace(/\\d/g, (digit) => persianDigits[parseInt(digit)])\n }\n return String(text)\n}\n\n/**\n * Format large numbers with locale-aware suffixes (K/M/B).\n * @example formatLargeNumber(1500, 'fa') => '۱.۵ هزار'\n * @example formatLargeNumber(1500, 'en') => '1.5K'\n */\nexport function formatLargeNumber(num: number, locale: SupportedLocale): string {\n if (num >= 1_000_000_000) {\n const formatted = (num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'B' : ' میلیارد')\n }\n if (num >= 1_000_000) {\n const formatted = (num / 1_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'M' : ' میلیون')\n }\n if (num >= 1_000) {\n const formatted = (num / 1_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'K' : ' هزار')\n }\n return convertToLocalNumbers(num.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ','), locale)\n}\n\n/**\n * Format number to Instagram-style short format (English only).\n * @example formatNumber(123456, 'short') => '123K'\n * @example formatNumber(123456, 'exact') => '123,456'\n */\nexport function formatNumber(num: number | undefined, format: 'exact' | 'short' = 'exact'): string {\n if (num === undefined || num === null) return '0'\n\n if (format === 'exact') {\n return num.toLocaleString('en-US')\n }\n\n // Short format (Instagram style)\n if (num >= 1_000_000_000) {\n return `${(num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')}B`\n }\n if (num >= 1_000_000) {\n return `${(num / 1_000_000).toFixed(1).replace(/\\.0$/, '')}M`\n }\n if (num >= 1_000) {\n return `${(num / 1_000).toFixed(1).replace(/\\.0$/, '')}K`\n }\n return num.toString()\n}\n\n/**\n * Format date to relative time with absolute on hover (Persian)\n * @example formatRelativeTime(new Date()) => '۲ ساعت پیش'\n */\nexport function formatRelativeTime(date: Date | string | number): string {\n const now = new Date()\n const then = new Date(date)\n const diffInSeconds = Math.floor((now.getTime() - then.getTime()) / 1000)\n\n if (diffInSeconds < 60) {\n return 'همین الان'\n }\n\n const diffInMinutes = Math.floor(diffInSeconds / 60)\n if (diffInMinutes < 60) {\n return `${convertToLocalNumbers(diffInMinutes, 'fa')} دقیقه پیش`\n }\n\n const diffInHours = Math.floor(diffInMinutes / 60)\n if (diffInHours < 24) {\n return `${convertToLocalNumbers(diffInHours, 'fa')} ساعت پیش`\n }\n\n const diffInDays = Math.floor(diffInHours / 24)\n if (diffInDays < 7) {\n return `${convertToLocalNumbers(diffInDays, 'fa')} روز پیش`\n }\n\n const diffInWeeks = Math.floor(diffInDays / 7)\n if (diffInWeeks < 4) {\n return `${convertToLocalNumbers(diffInWeeks, 'fa')} هفته پیش`\n }\n\n const diffInMonths = Math.floor(diffInDays / 30)\n if (diffInMonths < 12) {\n return `${convertToLocalNumbers(diffInMonths, 'fa')} ماه پیش`\n }\n\n const diffInYears = Math.floor(diffInDays / 365)\n return `${convertToLocalNumbers(diffInYears, 'fa')} سال پیش`\n}\n\n/**\n * Format date to absolute format (Persian / Jalali)\n * Uses date-fns-jalali for accurate Jalali conversion.\n * @example formatAbsoluteTime(new Date()) => '۱۵ دی ۱۴۰۳، ۱۵:۳۰'\n */\nexport function formatAbsoluteTime(date: Date | string | number): string {\n const d = new Date(date)\n return formatJalaliDate(d, 'd MMMM yyyy، HH:mm')\n}\n","'use client'\n\nimport * as React from 'react'\nimport { cn } from '@/lib/utils'\n\nexport type SparklineTrend = 'up' | 'down' | 'neutral' | 'auto'\nexport type SparklineVariant = 'line' | 'area' | 'bars'\n\nexport interface SparklineProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Array of numeric data points. Must have at least 2 items to render line/area. */\n data: number[]\n /** Visual style @default \"line\" */\n variant?: SparklineVariant\n /**\n * Trend colour. `\"auto\"` compares first and last point:\n * last > first → up, last < first → down, equal → neutral.\n * Pass an explicit value to override.\n * @default \"auto\"\n */\n trend?: SparklineTrend\n /** Width in px. Also used as SVG viewBox width — output scales to parent when CSS overrides. @default 80 */\n width?: number\n /** Height in px @default 24 */\n height?: number\n /** Line / bar stroke width @default 1.5 */\n strokeWidth?: number\n /** Show a filled dot at the last point */\n showEndDot?: boolean\n /** Padding inside the viewBox so strokes/dots are not clipped @default 2 */\n padding?: number\n /** Override color with any CSS value (e.g. `\"hsl(var(--brand))\"`). Takes precedence over `trend`. */\n color?: string\n /** For bars variant — gap between bars in user units @default 1 */\n barGap?: number\n /** aria-label for screen readers — describe the trend / metric */\n 'aria-label'?: string\n}\n\nconst TREND_CLASSES: Record<Exclude<SparklineTrend, 'auto'>, string> = {\n up: 'text-[hsl(var(--sentiment-positive))]',\n down: 'text-[hsl(var(--sentiment-negative))]',\n neutral: 'text-[hsl(var(--sentiment-neutral))]',\n}\n\nfunction resolveTrend(data: number[], trend: SparklineTrend): Exclude<SparklineTrend, 'auto'> {\n if (trend !== 'auto') return trend\n if (data.length < 2) return 'neutral'\n const first = data[0]\n const last = data[data.length - 1]\n if (last > first) return 'up'\n if (last < first) return 'down'\n return 'neutral'\n}\n\nconst Sparkline = React.forwardRef<HTMLDivElement, SparklineProps>(\n (\n {\n className,\n data,\n variant = 'line',\n trend = 'auto',\n width = 80,\n height = 24,\n strokeWidth = 1.5,\n showEndDot = false,\n padding = 2,\n color,\n barGap = 1,\n 'aria-label': ariaLabel,\n ...props\n },\n ref\n ) => {\n const effectiveTrend = resolveTrend(data, trend)\n const trendColorClass = color ? undefined : TREND_CLASSES[effectiveTrend]\n\n if (data.length === 0) {\n return (\n <div\n ref={ref}\n data-slot=\"sparkline\"\n data-empty=\"true\"\n aria-hidden=\"true\"\n className={cn('inline-block', className)}\n style={{ width, height }}\n {...props}\n />\n )\n }\n\n const min = Math.min(...data)\n const max = Math.max(...data)\n const range = max - min || 1\n const innerW = width - padding * 2\n const innerH = height - padding * 2\n const stepX = data.length > 1 ? innerW / (data.length - 1) : 0\n const yFor = (v: number) => padding + innerH - ((v - min) / range) * innerH\n const xFor = (i: number) => padding + i * stepX\n\n const points = data.map((v, i) => ({ x: xFor(i), y: yFor(v) }))\n const linePath = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x.toFixed(2)} ${p.y.toFixed(2)}`).join(' ')\n const areaPath = `${linePath} L${points[points.length - 1].x.toFixed(2)} ${(height - padding).toFixed(\n 2\n )} L${points[0].x.toFixed(2)} ${(height - padding).toFixed(2)} Z`\n\n const stroke = color ?? 'currentColor'\n\n return (\n <div\n ref={ref}\n data-slot=\"sparkline\"\n data-variant={variant}\n data-trend={effectiveTrend}\n role={ariaLabel ? 'img' : undefined}\n aria-label={ariaLabel}\n aria-hidden={ariaLabel ? undefined : true}\n className={cn('inline-block align-middle', trendColorClass, className)}\n style={{ width, height }}\n {...props}\n >\n <svg\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"none\"\n focusable=\"false\"\n >\n {variant === 'area' && <path d={areaPath} fill={stroke} opacity={0.18} />}\n {(variant === 'line' || variant === 'area') && (\n <path\n d={linePath}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n vectorEffect=\"non-scaling-stroke\"\n />\n )}\n {variant === 'bars' &&\n data.map((v, i) => {\n const gap = barGap\n const barW = Math.max(0, stepX - gap)\n const y = yFor(v)\n const barH = Math.max(1, height - padding - y)\n const x = xFor(i) - barW / 2\n return (\n <rect\n key={i}\n x={x.toFixed(2)}\n y={y.toFixed(2)}\n width={barW.toFixed(2)}\n height={barH.toFixed(2)}\n rx={Math.min(1, barW / 3)}\n fill={stroke}\n />\n )\n })}\n {showEndDot && variant !== 'bars' && (\n <circle\n cx={points[points.length - 1].x.toFixed(2)}\n cy={points[points.length - 1].y.toFixed(2)}\n r={strokeWidth + 0.5}\n fill={stroke}\n />\n )}\n </svg>\n </div>\n )\n }\n)\nSparkline.displayName = 'Sparkline'\n\nexport { Sparkline }\n"]}
|
|
@@ -1,4 +1,144 @@
|
|
|
1
|
-
|
|
2
|
-
import '
|
|
1
|
+
'use client';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
var TREND_CLASSES = {
|
|
11
|
+
up: "text-[hsl(var(--sentiment-positive))]",
|
|
12
|
+
down: "text-[hsl(var(--sentiment-negative))]",
|
|
13
|
+
neutral: "text-[hsl(var(--sentiment-neutral))]"
|
|
14
|
+
};
|
|
15
|
+
function resolveTrend(data, trend) {
|
|
16
|
+
if (trend !== "auto") return trend;
|
|
17
|
+
if (data.length < 2) return "neutral";
|
|
18
|
+
const first = data[0];
|
|
19
|
+
const last = data[data.length - 1];
|
|
20
|
+
if (last > first) return "up";
|
|
21
|
+
if (last < first) return "down";
|
|
22
|
+
return "neutral";
|
|
23
|
+
}
|
|
24
|
+
var Sparkline = React.forwardRef(
|
|
25
|
+
({
|
|
26
|
+
className,
|
|
27
|
+
data,
|
|
28
|
+
variant = "line",
|
|
29
|
+
trend = "auto",
|
|
30
|
+
width = 80,
|
|
31
|
+
height = 24,
|
|
32
|
+
strokeWidth = 1.5,
|
|
33
|
+
showEndDot = false,
|
|
34
|
+
padding = 2,
|
|
35
|
+
color,
|
|
36
|
+
barGap = 1,
|
|
37
|
+
"aria-label": ariaLabel,
|
|
38
|
+
...props
|
|
39
|
+
}, ref) => {
|
|
40
|
+
const effectiveTrend = resolveTrend(data, trend);
|
|
41
|
+
const trendColorClass = color ? void 0 : TREND_CLASSES[effectiveTrend];
|
|
42
|
+
if (data.length === 0) {
|
|
43
|
+
return /* @__PURE__ */ jsx(
|
|
44
|
+
"div",
|
|
45
|
+
{
|
|
46
|
+
ref,
|
|
47
|
+
"data-slot": "sparkline",
|
|
48
|
+
"data-empty": "true",
|
|
49
|
+
"aria-hidden": "true",
|
|
50
|
+
className: cn("inline-block", className),
|
|
51
|
+
style: { width, height },
|
|
52
|
+
...props
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const min = Math.min(...data);
|
|
57
|
+
const max = Math.max(...data);
|
|
58
|
+
const range = max - min || 1;
|
|
59
|
+
const innerW = width - padding * 2;
|
|
60
|
+
const innerH = height - padding * 2;
|
|
61
|
+
const stepX = data.length > 1 ? innerW / (data.length - 1) : 0;
|
|
62
|
+
const yFor = (v) => padding + innerH - (v - min) / range * innerH;
|
|
63
|
+
const xFor = (i) => padding + i * stepX;
|
|
64
|
+
const points = data.map((v, i) => ({ x: xFor(i), y: yFor(v) }));
|
|
65
|
+
const linePath = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(2)} ${p.y.toFixed(2)}`).join(" ");
|
|
66
|
+
const areaPath = `${linePath} L${points[points.length - 1].x.toFixed(2)} ${(height - padding).toFixed(
|
|
67
|
+
2
|
|
68
|
+
)} L${points[0].x.toFixed(2)} ${(height - padding).toFixed(2)} Z`;
|
|
69
|
+
const stroke = color ?? "currentColor";
|
|
70
|
+
return /* @__PURE__ */ jsx(
|
|
71
|
+
"div",
|
|
72
|
+
{
|
|
73
|
+
ref,
|
|
74
|
+
"data-slot": "sparkline",
|
|
75
|
+
"data-variant": variant,
|
|
76
|
+
"data-trend": effectiveTrend,
|
|
77
|
+
role: ariaLabel ? "img" : void 0,
|
|
78
|
+
"aria-label": ariaLabel,
|
|
79
|
+
"aria-hidden": ariaLabel ? void 0 : true,
|
|
80
|
+
className: cn("inline-block align-middle", trendColorClass, className),
|
|
81
|
+
style: { width, height },
|
|
82
|
+
...props,
|
|
83
|
+
children: /* @__PURE__ */ jsxs(
|
|
84
|
+
"svg",
|
|
85
|
+
{
|
|
86
|
+
width,
|
|
87
|
+
height,
|
|
88
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
89
|
+
preserveAspectRatio: "none",
|
|
90
|
+
focusable: "false",
|
|
91
|
+
children: [
|
|
92
|
+
variant === "area" && /* @__PURE__ */ jsx("path", { d: areaPath, fill: stroke, opacity: 0.18 }),
|
|
93
|
+
(variant === "line" || variant === "area") && /* @__PURE__ */ jsx(
|
|
94
|
+
"path",
|
|
95
|
+
{
|
|
96
|
+
d: linePath,
|
|
97
|
+
fill: "none",
|
|
98
|
+
stroke,
|
|
99
|
+
strokeWidth,
|
|
100
|
+
strokeLinecap: "round",
|
|
101
|
+
strokeLinejoin: "round",
|
|
102
|
+
vectorEffect: "non-scaling-stroke"
|
|
103
|
+
}
|
|
104
|
+
),
|
|
105
|
+
variant === "bars" && data.map((v, i) => {
|
|
106
|
+
const gap = barGap;
|
|
107
|
+
const barW = Math.max(0, stepX - gap);
|
|
108
|
+
const y = yFor(v);
|
|
109
|
+
const barH = Math.max(1, height - padding - y);
|
|
110
|
+
const x = xFor(i) - barW / 2;
|
|
111
|
+
return /* @__PURE__ */ jsx(
|
|
112
|
+
"rect",
|
|
113
|
+
{
|
|
114
|
+
x: x.toFixed(2),
|
|
115
|
+
y: y.toFixed(2),
|
|
116
|
+
width: barW.toFixed(2),
|
|
117
|
+
height: barH.toFixed(2),
|
|
118
|
+
rx: Math.min(1, barW / 3),
|
|
119
|
+
fill: stroke
|
|
120
|
+
},
|
|
121
|
+
i
|
|
122
|
+
);
|
|
123
|
+
}),
|
|
124
|
+
showEndDot && variant !== "bars" && /* @__PURE__ */ jsx(
|
|
125
|
+
"circle",
|
|
126
|
+
{
|
|
127
|
+
cx: points[points.length - 1].x.toFixed(2),
|
|
128
|
+
cy: points[points.length - 1].y.toFixed(2),
|
|
129
|
+
r: strokeWidth + 0.5,
|
|
130
|
+
fill: stroke
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
Sparkline.displayName = "Sparkline";
|
|
141
|
+
|
|
142
|
+
export { Sparkline };
|
|
3
143
|
//# sourceMappingURL=sparkline.js.map
|
|
4
144
|
//# sourceMappingURL=sparkline.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"sparkline.js"}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/ui/sparkline.tsx"],"names":[],"mappings":"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","file":"sparkline.js","sourcesContent":["import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\nimport { formatJalaliDate } from '@/lib/jalali-utils'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\nexport type SupportedLocale = 'fa' | 'ar' | 'en'\n\n/**\n * Convert digits in a string to Persian/Arabic numerals based on locale.\n * @example convertToLocalNumbers('123', 'fa') => '۱۲۳'\n * @example convertToLocalNumbers('123', 'en') => '123'\n */\nexport function convertToLocalNumbers(text: string | number, locale: SupportedLocale): string {\n if (locale === 'fa' || locale === 'ar') {\n const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']\n return String(text).replace(/\\d/g, (digit) => persianDigits[parseInt(digit)])\n }\n return String(text)\n}\n\n/**\n * Format large numbers with locale-aware suffixes (K/M/B).\n * @example formatLargeNumber(1500, 'fa') => '۱.۵ هزار'\n * @example formatLargeNumber(1500, 'en') => '1.5K'\n */\nexport function formatLargeNumber(num: number, locale: SupportedLocale): string {\n if (num >= 1_000_000_000) {\n const formatted = (num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'B' : ' میلیارد')\n }\n if (num >= 1_000_000) {\n const formatted = (num / 1_000_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'M' : ' میلیون')\n }\n if (num >= 1_000) {\n const formatted = (num / 1_000).toFixed(1).replace(/\\.0$/, '')\n return convertToLocalNumbers(formatted, locale) + (locale === 'en' ? 'K' : ' هزار')\n }\n return convertToLocalNumbers(num.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ','), locale)\n}\n\n/**\n * Format number to Instagram-style short format (English only).\n * @example formatNumber(123456, 'short') => '123K'\n * @example formatNumber(123456, 'exact') => '123,456'\n */\nexport function formatNumber(num: number | undefined, format: 'exact' | 'short' = 'exact'): string {\n if (num === undefined || num === null) return '0'\n\n if (format === 'exact') {\n return num.toLocaleString('en-US')\n }\n\n // Short format (Instagram style)\n if (num >= 1_000_000_000) {\n return `${(num / 1_000_000_000).toFixed(1).replace(/\\.0$/, '')}B`\n }\n if (num >= 1_000_000) {\n return `${(num / 1_000_000).toFixed(1).replace(/\\.0$/, '')}M`\n }\n if (num >= 1_000) {\n return `${(num / 1_000).toFixed(1).replace(/\\.0$/, '')}K`\n }\n return num.toString()\n}\n\n/**\n * Format date to relative time with absolute on hover (Persian)\n * @example formatRelativeTime(new Date()) => '۲ ساعت پیش'\n */\nexport function formatRelativeTime(date: Date | string | number): string {\n const now = new Date()\n const then = new Date(date)\n const diffInSeconds = Math.floor((now.getTime() - then.getTime()) / 1000)\n\n if (diffInSeconds < 60) {\n return 'همین الان'\n }\n\n const diffInMinutes = Math.floor(diffInSeconds / 60)\n if (diffInMinutes < 60) {\n return `${convertToLocalNumbers(diffInMinutes, 'fa')} دقیقه پیش`\n }\n\n const diffInHours = Math.floor(diffInMinutes / 60)\n if (diffInHours < 24) {\n return `${convertToLocalNumbers(diffInHours, 'fa')} ساعت پیش`\n }\n\n const diffInDays = Math.floor(diffInHours / 24)\n if (diffInDays < 7) {\n return `${convertToLocalNumbers(diffInDays, 'fa')} روز پیش`\n }\n\n const diffInWeeks = Math.floor(diffInDays / 7)\n if (diffInWeeks < 4) {\n return `${convertToLocalNumbers(diffInWeeks, 'fa')} هفته پیش`\n }\n\n const diffInMonths = Math.floor(diffInDays / 30)\n if (diffInMonths < 12) {\n return `${convertToLocalNumbers(diffInMonths, 'fa')} ماه پیش`\n }\n\n const diffInYears = Math.floor(diffInDays / 365)\n return `${convertToLocalNumbers(diffInYears, 'fa')} سال پیش`\n}\n\n/**\n * Format date to absolute format (Persian / Jalali)\n * Uses date-fns-jalali for accurate Jalali conversion.\n * @example formatAbsoluteTime(new Date()) => '۱۵ دی ۱۴۰۳، ۱۵:۳۰'\n */\nexport function formatAbsoluteTime(date: Date | string | number): string {\n const d = new Date(date)\n return formatJalaliDate(d, 'd MMMM yyyy، HH:mm')\n}\n","'use client'\n\nimport * as React from 'react'\nimport { cn } from '@/lib/utils'\n\nexport type SparklineTrend = 'up' | 'down' | 'neutral' | 'auto'\nexport type SparklineVariant = 'line' | 'area' | 'bars'\n\nexport interface SparklineProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Array of numeric data points. Must have at least 2 items to render line/area. */\n data: number[]\n /** Visual style @default \"line\" */\n variant?: SparklineVariant\n /**\n * Trend colour. `\"auto\"` compares first and last point:\n * last > first → up, last < first → down, equal → neutral.\n * Pass an explicit value to override.\n * @default \"auto\"\n */\n trend?: SparklineTrend\n /** Width in px. Also used as SVG viewBox width — output scales to parent when CSS overrides. @default 80 */\n width?: number\n /** Height in px @default 24 */\n height?: number\n /** Line / bar stroke width @default 1.5 */\n strokeWidth?: number\n /** Show a filled dot at the last point */\n showEndDot?: boolean\n /** Padding inside the viewBox so strokes/dots are not clipped @default 2 */\n padding?: number\n /** Override color with any CSS value (e.g. `\"hsl(var(--brand))\"`). Takes precedence over `trend`. */\n color?: string\n /** For bars variant — gap between bars in user units @default 1 */\n barGap?: number\n /** aria-label for screen readers — describe the trend / metric */\n 'aria-label'?: string\n}\n\nconst TREND_CLASSES: Record<Exclude<SparklineTrend, 'auto'>, string> = {\n up: 'text-[hsl(var(--sentiment-positive))]',\n down: 'text-[hsl(var(--sentiment-negative))]',\n neutral: 'text-[hsl(var(--sentiment-neutral))]',\n}\n\nfunction resolveTrend(data: number[], trend: SparklineTrend): Exclude<SparklineTrend, 'auto'> {\n if (trend !== 'auto') return trend\n if (data.length < 2) return 'neutral'\n const first = data[0]\n const last = data[data.length - 1]\n if (last > first) return 'up'\n if (last < first) return 'down'\n return 'neutral'\n}\n\nconst Sparkline = React.forwardRef<HTMLDivElement, SparklineProps>(\n (\n {\n className,\n data,\n variant = 'line',\n trend = 'auto',\n width = 80,\n height = 24,\n strokeWidth = 1.5,\n showEndDot = false,\n padding = 2,\n color,\n barGap = 1,\n 'aria-label': ariaLabel,\n ...props\n },\n ref\n ) => {\n const effectiveTrend = resolveTrend(data, trend)\n const trendColorClass = color ? undefined : TREND_CLASSES[effectiveTrend]\n\n if (data.length === 0) {\n return (\n <div\n ref={ref}\n data-slot=\"sparkline\"\n data-empty=\"true\"\n aria-hidden=\"true\"\n className={cn('inline-block', className)}\n style={{ width, height }}\n {...props}\n />\n )\n }\n\n const min = Math.min(...data)\n const max = Math.max(...data)\n const range = max - min || 1\n const innerW = width - padding * 2\n const innerH = height - padding * 2\n const stepX = data.length > 1 ? innerW / (data.length - 1) : 0\n const yFor = (v: number) => padding + innerH - ((v - min) / range) * innerH\n const xFor = (i: number) => padding + i * stepX\n\n const points = data.map((v, i) => ({ x: xFor(i), y: yFor(v) }))\n const linePath = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x.toFixed(2)} ${p.y.toFixed(2)}`).join(' ')\n const areaPath = `${linePath} L${points[points.length - 1].x.toFixed(2)} ${(height - padding).toFixed(\n 2\n )} L${points[0].x.toFixed(2)} ${(height - padding).toFixed(2)} Z`\n\n const stroke = color ?? 'currentColor'\n\n return (\n <div\n ref={ref}\n data-slot=\"sparkline\"\n data-variant={variant}\n data-trend={effectiveTrend}\n role={ariaLabel ? 'img' : undefined}\n aria-label={ariaLabel}\n aria-hidden={ariaLabel ? undefined : true}\n className={cn('inline-block align-middle', trendColorClass, className)}\n style={{ width, height }}\n {...props}\n >\n <svg\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"none\"\n focusable=\"false\"\n >\n {variant === 'area' && <path d={areaPath} fill={stroke} opacity={0.18} />}\n {(variant === 'line' || variant === 'area') && (\n <path\n d={linePath}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n vectorEffect=\"non-scaling-stroke\"\n />\n )}\n {variant === 'bars' &&\n data.map((v, i) => {\n const gap = barGap\n const barW = Math.max(0, stepX - gap)\n const y = yFor(v)\n const barH = Math.max(1, height - padding - y)\n const x = xFor(i) - barW / 2\n return (\n <rect\n key={i}\n x={x.toFixed(2)}\n y={y.toFixed(2)}\n width={barW.toFixed(2)}\n height={barH.toFixed(2)}\n rx={Math.min(1, barW / 3)}\n fill={stroke}\n />\n )\n })}\n {showEndDot && variant !== 'bars' && (\n <circle\n cx={points[points.length - 1].x.toFixed(2)}\n cy={points[points.length - 1].y.toFixed(2)}\n r={strokeWidth + 0.5}\n fill={stroke}\n />\n )}\n </svg>\n </div>\n )\n }\n)\nSparkline.displayName = 'Sparkline'\n\nexport { Sparkline }\n"]}
|