@parto-system-design/ui 1.1.11 → 1.1.17
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.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 +1 -1
- package/dist/components/ui/button.d.ts +1 -1
- 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 +21003 -15720
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +412 -71
- package/dist/index.d.cts +82 -490
- package/dist/index.d.ts +82 -490
- package/dist/index.js +6727 -976
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"tooltip.js"}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/ui/tooltip.tsx"],"names":[],"mappings":";;;;;;AAIO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACCA,IAAM,eAAA,GAAmC,gBAAA,CAAA;AAEzC,IAAM,OAAA,GAA2B,gBAAA,CAAA;AAEjC,IAAM,cAAA,GAAkC,gBAAA,CAAA;AAexC,IAAM,cAAA,GAAuB,KAAA,CAAA,UAAA;AAAA,EAC3B,CAAC,EAAE,SAAA,EAAW,UAAU,SAAA,EAAW,IAAA,GAAO,MAAM,SAAA,GAAY,KAAA,EAAO,UAAA,GAAa,CAAA,EAAG,UAAU,GAAG,KAAA,IAAS,GAAA,qBACvG,GAAA,CAAkB,yBAAjB,EACC,QAAA,kBAAA,IAAA;AAAA,IAAkB,gBAAA,CAAA,OAAA;AAAA,IAAjB;AAAA,MACC,GAAA;AAAA,MACA,WAAA,EAAU,iBAAA;AAAA,MACV,UAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACT,iDAAA;AAAA,QACA,mLAAA;AAAA,QACA,gGAAA;AAAA;AAAA,QAEA,YAAY,SAAA,IAAa,uCAAA;AAAA,QACzB,YAAY,OAAA,IAAW,uDAAA;AAAA,QACvB,YAAY,OAAA,IAAW,uEAAA;AAAA;AAAA,QAEvB,SAAS,IAAA,IAAQ,qBAAA;AAAA,QACjB,SAAS,IAAA,IAAQ,mBAAA;AAAA,QACjB;AAAA,OACF;AAAA,MACC,GAAG,KAAA;AAAA,MAEH,QAAA,EAAA;AAAA,QAAA,QAAA;AAAA,QACA,SAAA,oBACC,GAAA;AAAA,UAAkB,gBAAA,CAAA,KAAA;AAAA,UAAjB;AAAA,YACC,SAAA,EAAW,EAAA;AAAA,cACT,cAAA;AAAA,cACA,YAAY,SAAA,IAAa,kBAAA;AAAA,cACzB,YAAY,OAAA,IAAW,kBAAA;AAAA,cACvB,YAAY,OAAA,IAAW;AAAA,aACzB;AAAA,YACA,KAAA,EAAO,CAAA;AAAA,YACP,MAAA,EAAQ;AAAA;AAAA;AACV;AAAA;AAAA,GAEJ,EACF;AAEJ;AACA,cAAA,CAAe,cAA+B,gBAAA,CAAA,OAAA,CAAQ,WAAA","file":"tooltip.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 * as TooltipPrimitive from '@radix-ui/react-tooltip'\n\nimport { cn } from '@/lib/utils'\n\nconst TooltipProvider = TooltipPrimitive.Provider\n\nconst Tooltip = TooltipPrimitive.Root\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\n/* -------------------------------------------------------------------------- */\n/* TooltipContent */\n/* -------------------------------------------------------------------------- */\n\ninterface TooltipContentProps extends React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> {\n /** Visual variant */\n variant?: 'default' | 'light' | 'error'\n /** Size */\n size?: 'sm' | 'md'\n /** Show arrow/caret pointing to trigger */\n showArrow?: boolean\n}\n\nconst TooltipContent = React.forwardRef<React.ElementRef<typeof TooltipPrimitive.Content>, TooltipContentProps>(\n ({ className, variant = 'default', size = 'sm', showArrow = false, sideOffset = 4, children, ...props }, ref) => (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n ref={ref}\n data-slot=\"tooltip-content\"\n sideOffset={sideOffset}\n className={cn(\n 'z-50 overflow-hidden rounded-md shadow-dropdown',\n 'animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1',\n 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',\n // Variant styles\n variant === 'default' && 'border bg-alternative text-foreground',\n variant === 'light' && 'border bg-surface-100 text-foreground shadow-dropdown',\n variant === 'error' && 'border border-destructive-500 bg-destructive-200 text-destructive-600',\n // Size\n size === 'sm' && 'px-3 py-1.5 text-xs',\n size === 'md' && 'px-4 py-2 text-sm',\n className\n )}\n {...props}\n >\n {children}\n {showArrow && (\n <TooltipPrimitive.Arrow\n className={cn(\n 'fill-current',\n variant === 'default' && 'text-alternative',\n variant === 'light' && 'text-surface-100',\n variant === 'error' && 'text-destructive-200'\n )}\n width={8}\n height={4}\n />\n )}\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n )\n)\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { S as SupportedLocale } from './utils-DlXWmDZ-.cjs';
|
|
3
|
-
import { F as FlowKey, c as StatusKey, d as SeverityKey } from './i18n-
|
|
3
|
+
import { F as FlowKey, c as StatusKey, d as SeverityKey } from './i18n-BfRhV5aw.cjs';
|
|
4
4
|
|
|
5
5
|
type FlowData = Partial<Record<FlowKey, number>>;
|
|
6
6
|
interface FlowDistributionProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -49,7 +49,7 @@ declare const SentimentDistribution: React.ForwardRefExoticComponent<SentimentDi
|
|
|
49
49
|
* to fit in a card grid with rich, comparable signals.
|
|
50
50
|
*/
|
|
51
51
|
interface ConceptCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
52
|
-
/** Concept name (e.g., "
|
|
52
|
+
/** Concept name (e.g., "رونمایی گوشی جدید") */
|
|
53
53
|
title: React.ReactNode;
|
|
54
54
|
/** Short subtitle (e.g., "خوشه ۴ — ۱۲۰ پست") */
|
|
55
55
|
subtitle?: React.ReactNode;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { S as SupportedLocale } from './utils-DlXWmDZ-.js';
|
|
3
|
-
import { F as FlowKey, c as StatusKey, d as SeverityKey } from './i18n-
|
|
3
|
+
import { F as FlowKey, c as StatusKey, d as SeverityKey } from './i18n-ewyqbKM-.js';
|
|
4
4
|
|
|
5
5
|
type FlowData = Partial<Record<FlowKey, number>>;
|
|
6
6
|
interface FlowDistributionProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -49,7 +49,7 @@ declare const SentimentDistribution: React.ForwardRefExoticComponent<SentimentDi
|
|
|
49
49
|
* to fit in a card grid with rich, comparable signals.
|
|
50
50
|
*/
|
|
51
51
|
interface ConceptCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
52
|
-
/** Concept name (e.g., "
|
|
52
|
+
/** Concept name (e.g., "رونمایی گوشی جدید") */
|
|
53
53
|
title: React.ReactNode;
|
|
54
54
|
/** Short subtitle (e.g., "خوشه ۴ — ۱۲۰ پست") */
|
|
55
55
|
subtitle?: React.ReactNode;
|
|
@@ -1,21 +1,208 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
|
-
var
|
|
4
|
-
require('
|
|
4
|
+
var React2 = require('react');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
6
|
|
|
7
|
+
function _interopNamespace(e) {
|
|
8
|
+
if (e && e.__esModule) return e;
|
|
9
|
+
var n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
Object.keys(e).forEach(function (k) {
|
|
12
|
+
if (k !== 'default') {
|
|
13
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return e[k]; }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
6
24
|
|
|
25
|
+
var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
|
|
7
26
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
function isMac() {
|
|
28
|
+
if (typeof navigator === "undefined") return false;
|
|
29
|
+
const ua = navigator.userAgentData?.platform;
|
|
30
|
+
const platform = ua ?? navigator.platform ?? "";
|
|
31
|
+
return /mac|darwin/i.test(platform);
|
|
32
|
+
}
|
|
33
|
+
var SPECIAL_KEY_MAP = {
|
|
34
|
+
" ": "space",
|
|
35
|
+
spacebar: "space",
|
|
36
|
+
esc: "escape",
|
|
37
|
+
del: "delete",
|
|
38
|
+
return: "enter",
|
|
39
|
+
arrowup: "up",
|
|
40
|
+
arrowdown: "down",
|
|
41
|
+
arrowleft: "left",
|
|
42
|
+
arrowright: "right"
|
|
43
|
+
};
|
|
44
|
+
function normalizeKey(raw) {
|
|
45
|
+
const lower = raw.toLowerCase();
|
|
46
|
+
return SPECIAL_KEY_MAP[lower] ?? lower;
|
|
47
|
+
}
|
|
48
|
+
function parseCombo(combo) {
|
|
49
|
+
const parts = combo.toLowerCase().split("+").map((s) => s.trim()).filter(Boolean);
|
|
50
|
+
const result = {
|
|
51
|
+
mod: false,
|
|
52
|
+
ctrl: false,
|
|
53
|
+
cmd: false,
|
|
54
|
+
shift: false,
|
|
55
|
+
alt: false,
|
|
56
|
+
meta: false,
|
|
57
|
+
key: ""
|
|
58
|
+
};
|
|
59
|
+
for (const p of parts) {
|
|
60
|
+
if (p === "mod") result.mod = true;
|
|
61
|
+
else if (p === "ctrl" || p === "control") result.ctrl = true;
|
|
62
|
+
else if (p === "cmd" || p === "command" || p === "win") result.cmd = true;
|
|
63
|
+
else if (p === "shift") result.shift = true;
|
|
64
|
+
else if (p === "alt" || p === "option" || p === "opt") result.alt = true;
|
|
65
|
+
else if (p === "meta") result.meta = true;
|
|
66
|
+
else result.key = normalizeKey(p);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
function matches(parsed, event, mac) {
|
|
71
|
+
const requiredCmd = parsed.cmd || parsed.mod && mac;
|
|
72
|
+
const requiredCtrl = parsed.ctrl || parsed.mod && !mac;
|
|
73
|
+
if (requiredCmd !== event.metaKey) return false;
|
|
74
|
+
if (requiredCtrl !== event.ctrlKey) return false;
|
|
75
|
+
if (parsed.shift !== event.shiftKey) return false;
|
|
76
|
+
if (parsed.alt !== event.altKey) return false;
|
|
77
|
+
if (parsed.meta && !mac && !event.metaKey) return false;
|
|
78
|
+
if (!parsed.key) return true;
|
|
79
|
+
const eventKey = normalizeKey(event.key);
|
|
80
|
+
return eventKey === parsed.key;
|
|
81
|
+
}
|
|
82
|
+
function isEditable(el) {
|
|
83
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
84
|
+
if (el.isContentEditable) return true;
|
|
85
|
+
const tag = el.tagName;
|
|
86
|
+
if (tag === "INPUT") {
|
|
87
|
+
const type = el.type;
|
|
88
|
+
return !["button", "submit", "reset", "checkbox", "radio", "range", "color", "file"].includes(type);
|
|
89
|
+
}
|
|
90
|
+
return tag === "TEXTAREA" || tag === "SELECT";
|
|
91
|
+
}
|
|
92
|
+
function hasModifier(event) {
|
|
93
|
+
return event.metaKey || event.ctrlKey || event.altKey;
|
|
94
|
+
}
|
|
95
|
+
function resolveTarget(target) {
|
|
96
|
+
if (!target) return typeof document !== "undefined" ? document : null;
|
|
97
|
+
if (target instanceof Document) return target;
|
|
98
|
+
if ("current" in target) return target.current ?? null;
|
|
99
|
+
return target;
|
|
100
|
+
}
|
|
101
|
+
function useHotkeys(combo, handler, options = {}) {
|
|
102
|
+
const { preventDefault = true, ignoreWhenTyping = true, enabled = true, target } = options;
|
|
103
|
+
const handlerRef = React2__namespace.useRef(handler);
|
|
104
|
+
React2__namespace.useEffect(() => {
|
|
105
|
+
handlerRef.current = handler;
|
|
106
|
+
}, [handler]);
|
|
107
|
+
React2__namespace.useEffect(() => {
|
|
108
|
+
if (!enabled) return;
|
|
109
|
+
const resolvedTarget = resolveTarget(target);
|
|
110
|
+
if (!resolvedTarget) return;
|
|
111
|
+
const combos = (Array.isArray(combo) ? combo : [combo]).map(parseCombo);
|
|
112
|
+
const mac = isMac();
|
|
113
|
+
const listener = (event) => {
|
|
114
|
+
const keyboardEvent = event;
|
|
115
|
+
const matched = combos.find((parsed) => matches(parsed, keyboardEvent, mac));
|
|
116
|
+
if (!matched) return;
|
|
117
|
+
if (ignoreWhenTyping && isEditable(keyboardEvent.target) && !hasModifier(keyboardEvent)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (preventDefault) keyboardEvent.preventDefault();
|
|
121
|
+
handlerRef.current(keyboardEvent);
|
|
122
|
+
};
|
|
123
|
+
resolvedTarget.addEventListener("keydown", listener);
|
|
124
|
+
return () => {
|
|
125
|
+
resolvedTarget.removeEventListener("keydown", listener);
|
|
126
|
+
};
|
|
127
|
+
}, [Array.isArray(combo) ? combo.join("|") : combo, enabled, ignoreWhenTyping, preventDefault, target]);
|
|
128
|
+
}
|
|
129
|
+
var HotkeyRegisterContext = React2__namespace.createContext(null);
|
|
130
|
+
var HotkeyListContext = React2__namespace.createContext([]);
|
|
131
|
+
function HotkeyProvider({ enabled = true, children }) {
|
|
132
|
+
const [entries, setEntries] = React2__namespace.useState(() => /* @__PURE__ */ new Map());
|
|
133
|
+
const handlersRef = React2__namespace.useRef(/* @__PURE__ */ new Map());
|
|
134
|
+
const register = React2__namespace.useRef((entry, handler) => {
|
|
135
|
+
handlersRef.current.set(entry.id, handler);
|
|
136
|
+
setEntries((prev) => {
|
|
137
|
+
const next = new Map(prev);
|
|
138
|
+
next.set(entry.id, entry);
|
|
139
|
+
return next;
|
|
140
|
+
});
|
|
141
|
+
return () => {
|
|
142
|
+
handlersRef.current.delete(entry.id);
|
|
143
|
+
setEntries((prev) => {
|
|
144
|
+
if (!prev.has(entry.id)) return prev;
|
|
145
|
+
const next = new Map(prev);
|
|
146
|
+
next.delete(entry.id);
|
|
147
|
+
return next;
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
}).current;
|
|
151
|
+
const hotkeys = React2__namespace.useMemo(() => Array.from(entries.values()), [entries]);
|
|
152
|
+
return /* @__PURE__ */ jsxRuntime.jsx(HotkeyRegisterContext.Provider, { value: register, children: /* @__PURE__ */ jsxRuntime.jsxs(HotkeyListContext.Provider, { value: hotkeys, children: [
|
|
153
|
+
children,
|
|
154
|
+
enabled && /* @__PURE__ */ jsxRuntime.jsx(HotkeyDispatcher, { entries: hotkeys, handlersRef })
|
|
155
|
+
] }) });
|
|
156
|
+
}
|
|
157
|
+
function HotkeyDispatcher({
|
|
158
|
+
entries,
|
|
159
|
+
handlersRef
|
|
160
|
+
}) {
|
|
161
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: entries.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(HotkeyEntry, { entry, handlersRef }, entry.id)) });
|
|
162
|
+
}
|
|
163
|
+
function HotkeyEntry({
|
|
164
|
+
entry,
|
|
165
|
+
handlersRef
|
|
166
|
+
}) {
|
|
167
|
+
useHotkeys(
|
|
168
|
+
entry.combo,
|
|
169
|
+
(event) => {
|
|
170
|
+
const handler = handlersRef.current.get(entry.id);
|
|
171
|
+
handler?.(event);
|
|
172
|
+
},
|
|
173
|
+
{ enabled: entry.enabled !== false }
|
|
174
|
+
);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
function useHotkey(id, combo, handler, options = {}) {
|
|
178
|
+
const register = React2__namespace.useContext(HotkeyRegisterContext);
|
|
179
|
+
const handlerRef = React2__namespace.useRef(handler);
|
|
180
|
+
React2__namespace.useEffect(() => {
|
|
181
|
+
handlerRef.current = handler;
|
|
182
|
+
});
|
|
183
|
+
const comboKey = Array.isArray(combo) ? combo.join("|") : combo;
|
|
184
|
+
React2__namespace.useEffect(() => {
|
|
185
|
+
if (!register) return;
|
|
186
|
+
if (!comboKey) return;
|
|
187
|
+
return register(
|
|
188
|
+
{
|
|
189
|
+
id,
|
|
190
|
+
combo,
|
|
191
|
+
description: options.description,
|
|
192
|
+
group: options.group,
|
|
193
|
+
enabled: options.enabled
|
|
194
|
+
},
|
|
195
|
+
(event) => handlerRef.current(event)
|
|
196
|
+
);
|
|
197
|
+
}, [register, id, comboKey, options.description, options.group, options.enabled]);
|
|
198
|
+
}
|
|
199
|
+
function useHotkeyRegistry() {
|
|
200
|
+
const hotkeys = React2__namespace.useContext(HotkeyListContext);
|
|
201
|
+
return { hotkeys };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
exports.HotkeyProvider = HotkeyProvider;
|
|
205
|
+
exports.useHotkey = useHotkey;
|
|
206
|
+
exports.useHotkeyRegistry = useHotkeyRegistry;
|
|
20
207
|
//# sourceMappingURL=use-hotkey-registry.cjs.map
|
|
21
208
|
//# sourceMappingURL=use-hotkey-registry.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"use-hotkey-registry.cjs"}
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/use-hotkeys.ts","../../src/hooks/use-hotkey-registry.tsx"],"names":["React","React2","jsx","jsxs","Fragment"],"mappings":"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","file":"use-hotkey-registry.cjs","sourcesContent":["'use client'\n\nimport * as React from 'react'\n\n/**\n * Hotkey string grammar:\n *\n * <part>[+<part>]*\n *\n * where each <part> is one of:\n * mod — Cmd on macOS, Ctrl on everything else (recommended)\n * ctrl / cmd — explicit, cross-platform tokens\n * shift | alt | meta\n * <single key> — lowercase character, 'space', 'enter', 'escape', 'tab',\n * arrow{up,down,left,right}, 'slash', 'backspace', etc.\n *\n * Examples:\n * 'mod+k' — ⌘K on macOS, CtrlK elsewhere\n * 'mod+shift+p'\n * 'escape'\n * 'ctrl+k' — explicit Ctrl (no Cmd fallback on macOS)\n *\n * Passing an array of strings is treated as an OR — any combo fires the handler.\n */\nexport type HotkeyCombo = string | readonly string[]\n\nexport interface UseHotkeysOptions {\n /**\n * When true, `event.preventDefault()` is called on matched combos.\n * Default: true. Useful to allow the browser's native 'mod+k' (address bar)\n * to keep working — set to false if the handler only does something conditionally.\n */\n preventDefault?: boolean\n /**\n * Skip the handler when focus is inside an editable control (input, textarea,\n * contenteditable). The exception: combos that include a modifier (mod/ctrl/\n * cmd/alt) still fire — they are unambiguous shortcuts, not typing.\n * Default: true.\n */\n ignoreWhenTyping?: boolean\n /**\n * When false, the hook unregisters its listener. Useful for conditionally\n * enabling shortcuts (e.g., only while a panel is open).\n * Default: true.\n */\n enabled?: boolean\n /**\n * DOM target. Defaults to `document`. Pass a ref for scoped shortcuts (e.g.,\n * only when a specific panel has focus within).\n */\n target?: React.RefObject<HTMLElement | null> | HTMLElement | Document | null\n}\n\nfunction isMac(): boolean {\n if (typeof navigator === 'undefined') return false\n // `userAgentData.platform` is the modern API; fall back to platform string.\n // Accept 'darwin' (nodejs tests) and 'MacIntel'/'Mac OS X'.\n const ua = (navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform\n const platform = ua ?? navigator.platform ?? ''\n return /mac|darwin/i.test(platform)\n}\n\nconst SPECIAL_KEY_MAP: Record<string, string> = {\n ' ': 'space',\n spacebar: 'space',\n esc: 'escape',\n del: 'delete',\n return: 'enter',\n arrowup: 'up',\n arrowdown: 'down',\n arrowleft: 'left',\n arrowright: 'right',\n}\n\nfunction normalizeKey(raw: string): string {\n const lower = raw.toLowerCase()\n return SPECIAL_KEY_MAP[lower] ?? lower\n}\n\ntype ParsedCombo = {\n mod: boolean\n ctrl: boolean\n cmd: boolean\n shift: boolean\n alt: boolean\n meta: boolean\n key: string\n}\n\nfunction parseCombo(combo: string): ParsedCombo {\n const parts = combo\n .toLowerCase()\n .split('+')\n .map((s) => s.trim())\n .filter(Boolean)\n const result: ParsedCombo = {\n mod: false,\n ctrl: false,\n cmd: false,\n shift: false,\n alt: false,\n meta: false,\n key: '',\n }\n for (const p of parts) {\n if (p === 'mod') result.mod = true\n else if (p === 'ctrl' || p === 'control') result.ctrl = true\n else if (p === 'cmd' || p === 'command' || p === 'win') result.cmd = true\n else if (p === 'shift') result.shift = true\n else if (p === 'alt' || p === 'option' || p === 'opt') result.alt = true\n else if (p === 'meta') result.meta = true\n else result.key = normalizeKey(p)\n }\n return result\n}\n\nfunction matches(parsed: ParsedCombo, event: KeyboardEvent, mac: boolean): boolean {\n // `mod` resolves to Cmd on macOS, Ctrl elsewhere. Track both sides of the combo.\n const requiredCmd = parsed.cmd || (parsed.mod && mac)\n const requiredCtrl = parsed.ctrl || (parsed.mod && !mac)\n\n if (requiredCmd !== event.metaKey) return false\n if (requiredCtrl !== event.ctrlKey) return false\n if (parsed.shift !== event.shiftKey) return false\n if (parsed.alt !== event.altKey) return false\n // `meta` (plain) aliases Cmd on macOS — avoid double-matching when both mod+meta are specified.\n if (parsed.meta && !mac && !event.metaKey) return false\n\n if (!parsed.key) return true\n const eventKey = normalizeKey(event.key)\n return eventKey === parsed.key\n}\n\nfunction isEditable(el: EventTarget | null): boolean {\n if (!(el instanceof HTMLElement)) return false\n if (el.isContentEditable) return true\n const tag = el.tagName\n if (tag === 'INPUT') {\n const type = (el as HTMLInputElement).type\n // Buttons and checkboxes aren't \"typing\" — don't block.\n return !['button', 'submit', 'reset', 'checkbox', 'radio', 'range', 'color', 'file'].includes(type)\n }\n return tag === 'TEXTAREA' || tag === 'SELECT'\n}\n\nfunction hasModifier(event: KeyboardEvent): boolean {\n return event.metaKey || event.ctrlKey || event.altKey\n}\n\nfunction resolveTarget(target: UseHotkeysOptions['target']): EventTarget | null {\n if (!target) return typeof document !== 'undefined' ? document : null\n if (target instanceof Document) return target\n if ('current' in target) return target.current ?? null\n return target\n}\n\n/**\n * Register a keyboard shortcut. Handler fires when the combo matches, respecting\n * `ignoreWhenTyping` (default true) — except when a modifier is part of the combo,\n * which always wins. Returns nothing; cleanup is automatic.\n *\n * @example\n * useHotkeys('mod+k', () => setOpen((v) => !v))\n * useHotkeys(['escape', 'mod+w'], close, { enabled: open })\n */\nexport function useHotkeys(\n combo: HotkeyCombo,\n handler: (event: KeyboardEvent) => void,\n options: UseHotkeysOptions = {}\n): void {\n const { preventDefault = true, ignoreWhenTyping = true, enabled = true, target } = options\n\n // Keep latest handler in a ref so callers don't need to memoize.\n const handlerRef = React.useRef(handler)\n React.useEffect(() => {\n handlerRef.current = handler\n }, [handler])\n\n React.useEffect(() => {\n if (!enabled) return\n const resolvedTarget = resolveTarget(target)\n if (!resolvedTarget) return\n\n const combos = (Array.isArray(combo) ? combo : [combo]).map(parseCombo)\n const mac = isMac()\n\n const listener = (event: Event) => {\n const keyboardEvent = event as KeyboardEvent\n const matched = combos.find((parsed) => matches(parsed, keyboardEvent, mac))\n if (!matched) return\n\n if (ignoreWhenTyping && isEditable(keyboardEvent.target) && !hasModifier(keyboardEvent)) {\n return\n }\n\n if (preventDefault) keyboardEvent.preventDefault()\n handlerRef.current(keyboardEvent)\n }\n\n resolvedTarget.addEventListener('keydown', listener)\n return () => {\n resolvedTarget.removeEventListener('keydown', listener)\n }\n // `combo` is usually a stable string literal; if callers pass a new array every\n // render we join-stringify to avoid stale closures without surprising rerenders.\n }, [Array.isArray(combo) ? combo.join('|') : combo, enabled, ignoreWhenTyping, preventDefault, target])\n}\n\n/**\n * Render-helper: returns a platform-appropriate display string for a combo.\n * 'mod+k' → '⌘K' on macOS, 'Ctrl+K' elsewhere. Useful for `<CommandShortcut>`.\n */\nexport function formatHotkey(combo: string): string {\n const mac = isMac()\n return combo\n .split('+')\n .map((p) => {\n const t = p.trim().toLowerCase()\n if (t === 'mod') return mac ? '⌘' : 'Ctrl'\n if (t === 'cmd' || t === 'meta') return mac ? '⌘' : 'Win'\n if (t === 'ctrl' || t === 'control') return mac ? '⌃' : 'Ctrl'\n if (t === 'shift') return mac ? '⇧' : 'Shift'\n if (t === 'alt' || t === 'option' || t === 'opt') return mac ? '⌥' : 'Alt'\n if (t === 'enter' || t === 'return') return '↵'\n if (t === 'escape' || t === 'esc') return 'Esc'\n if (t === 'space') return 'Space'\n if (t === 'up') return '↑'\n if (t === 'down') return '↓'\n if (t === 'left') return '←'\n if (t === 'right') return '→'\n return t.length === 1 ? t.toUpperCase() : t\n })\n .join(mac ? '' : '+')\n}\n","'use client'\n\nimport * as React from 'react'\nimport { useHotkeys, type HotkeyCombo } from './use-hotkeys'\n\n/* -------------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------------- */\n\nexport interface RegisteredHotkey {\n /** Stable id — used for de-registration + dedupe. */\n id: string\n /** The combo string(s) — `'mod+k'`, `['escape', 'mod+w']`, etc. */\n combo: HotkeyCombo\n /** Short human-readable description (e.g. \"باز کردن پالت دستور\"). */\n description?: string\n /** Optional grouping label for cheatsheet rendering (\"سراسری\", \"ناوبری\"…). */\n group?: string\n /** When false, the entry is listed but the listener doesn't fire. */\n enabled?: boolean\n}\n\ntype RegisterFn = (entry: RegisteredHotkey, handler: (event: KeyboardEvent) => void) => () => void\n\n/**\n * Two contexts so consumers can depend on a *stable* register fn (never\n * changes for the provider's lifetime) without re-running their effect\n * every time the entries list updates.\n */\nconst HotkeyRegisterContext = React.createContext<RegisterFn | null>(null)\nconst HotkeyListContext = React.createContext<RegisteredHotkey[]>([])\n\n/* -------------------------------------------------------------------------- */\n/* Provider */\n/* -------------------------------------------------------------------------- */\n\nexport interface HotkeyProviderProps {\n /** Globally disable all registered hotkeys (e.g. while a modal owns the page). */\n enabled?: boolean\n children: React.ReactNode\n}\n\n/**\n * Centralised hotkey registry. Wrap your app once; descendants register\n * combos via `useHotkey()` and any UI (CommandPalette, help cheatsheet)\n * can read the full list via `useHotkeyRegistry()`.\n *\n * Compared to scattering `useHotkeys` calls across the tree:\n * - Per-entry mounting is centralized (one place that handles\n * `enabled`, key parsing, ignore-when-typing semantics)\n * - Discoverable: cheatsheets / palettes can list every active combo\n * - De-duplicated: registering the same id twice replaces the entry\n *\n * Optional. `useHotkeys` (the bare hook) continues to work outside a provider.\n */\nexport function HotkeyProvider({ enabled = true, children }: HotkeyProviderProps) {\n const [entries, setEntries] = React.useState<Map<string, RegisteredHotkey>>(() => new Map())\n const handlersRef = React.useRef<Map<string, (event: KeyboardEvent) => void>>(new Map())\n\n // Stable across the provider's lifetime so descendants don't re-register\n // every time the entries list changes.\n const register = React.useRef<RegisterFn>((entry, handler) => {\n handlersRef.current.set(entry.id, handler)\n setEntries((prev) => {\n const next = new Map(prev)\n next.set(entry.id, entry)\n return next\n })\n return () => {\n handlersRef.current.delete(entry.id)\n setEntries((prev) => {\n if (!prev.has(entry.id)) return prev\n const next = new Map(prev)\n next.delete(entry.id)\n return next\n })\n }\n }).current\n\n const hotkeys = React.useMemo(() => Array.from(entries.values()), [entries])\n\n return (\n <HotkeyRegisterContext.Provider value={register}>\n <HotkeyListContext.Provider value={hotkeys}>\n {children}\n {enabled && <HotkeyDispatcher entries={hotkeys} handlersRef={handlersRef} />}\n </HotkeyListContext.Provider>\n </HotkeyRegisterContext.Provider>\n )\n}\n\n/**\n * Internal — one useHotkeys per registered entry, mounted under the\n * provider rather than per-component. Per-entry useHotkeys keeps key\n * parsing and ignoreWhenTyping logic in one tested place.\n */\nfunction HotkeyDispatcher({\n entries,\n handlersRef,\n}: {\n entries: RegisteredHotkey[]\n handlersRef: React.MutableRefObject<Map<string, (event: KeyboardEvent) => void>>\n}) {\n return (\n <>\n {entries.map((entry) => (\n <HotkeyEntry key={entry.id} entry={entry} handlersRef={handlersRef} />\n ))}\n </>\n )\n}\n\nfunction HotkeyEntry({\n entry,\n handlersRef,\n}: {\n entry: RegisteredHotkey\n handlersRef: React.MutableRefObject<Map<string, (event: KeyboardEvent) => void>>\n}) {\n useHotkeys(\n entry.combo,\n (event) => {\n const handler = handlersRef.current.get(entry.id)\n handler?.(event)\n },\n { enabled: entry.enabled !== false }\n )\n return null\n}\n\n/* -------------------------------------------------------------------------- */\n/* Hooks */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Register a hotkey under the active provider. The combo is added to the\n * cheatsheet-readable registry; the handler fires when the combo matches\n * (subject to `useHotkeys` rules around editable targets + modifiers).\n *\n * @example\n * useHotkey('palette', 'mod+k', () => setOpen(true), {\n * description: 'باز کردن پالت دستور',\n * group: 'سراسری',\n * })\n *\n * The first argument is a stable id — use a unique slug per call site so\n * registry updates can dedupe. When called outside a `<HotkeyProvider>`,\n * silently no-ops (so consumers can layer the provider in later without\n * a refactor sweep).\n */\nexport function useHotkey(\n id: string,\n combo: HotkeyCombo,\n handler: (event: KeyboardEvent) => void,\n options: { description?: string; group?: string; enabled?: boolean } = {}\n): void {\n const register = React.useContext(HotkeyRegisterContext)\n\n // Capture the latest handler so callers can pass an inline arrow each\n // render without re-registering the entry.\n const handlerRef = React.useRef(handler)\n React.useEffect(() => {\n handlerRef.current = handler\n })\n\n // Stringify combo for stable dependency tracking.\n const comboKey = Array.isArray(combo) ? combo.join('|') : combo\n\n React.useEffect(() => {\n if (!register) return\n // Skip registration when combo is empty/falsy — lets consumers conditionally\n // disable a hotkey without violating rules of hooks (still call useHotkey\n // unconditionally, but pass `''` to opt out).\n if (!comboKey) return\n return register(\n {\n id,\n combo,\n description: options.description,\n group: options.group,\n enabled: options.enabled,\n },\n (event) => handlerRef.current(event)\n )\n // `combo` (the original value) intentionally elided; comboKey covers it.\n }, [register, id, comboKey, options.description, options.group, options.enabled])\n}\n\n/**\n * Read the active registry. Returns an empty array outside a provider.\n * Useful for CommandPalette items, \"?\" cheatsheet overlays, settings pages.\n */\nexport function useHotkeyRegistry(): { hotkeys: RegisteredHotkey[] } {\n const hotkeys = React.useContext(HotkeyListContext)\n return { hotkeys }\n}\n"]}
|
|
@@ -1,4 +1,184 @@
|
|
|
1
|
-
|
|
2
|
-
import '
|
|
1
|
+
'use client';
|
|
2
|
+
import * as React2 from 'react';
|
|
3
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function isMac() {
|
|
6
|
+
if (typeof navigator === "undefined") return false;
|
|
7
|
+
const ua = navigator.userAgentData?.platform;
|
|
8
|
+
const platform = ua ?? navigator.platform ?? "";
|
|
9
|
+
return /mac|darwin/i.test(platform);
|
|
10
|
+
}
|
|
11
|
+
var SPECIAL_KEY_MAP = {
|
|
12
|
+
" ": "space",
|
|
13
|
+
spacebar: "space",
|
|
14
|
+
esc: "escape",
|
|
15
|
+
del: "delete",
|
|
16
|
+
return: "enter",
|
|
17
|
+
arrowup: "up",
|
|
18
|
+
arrowdown: "down",
|
|
19
|
+
arrowleft: "left",
|
|
20
|
+
arrowright: "right"
|
|
21
|
+
};
|
|
22
|
+
function normalizeKey(raw) {
|
|
23
|
+
const lower = raw.toLowerCase();
|
|
24
|
+
return SPECIAL_KEY_MAP[lower] ?? lower;
|
|
25
|
+
}
|
|
26
|
+
function parseCombo(combo) {
|
|
27
|
+
const parts = combo.toLowerCase().split("+").map((s) => s.trim()).filter(Boolean);
|
|
28
|
+
const result = {
|
|
29
|
+
mod: false,
|
|
30
|
+
ctrl: false,
|
|
31
|
+
cmd: false,
|
|
32
|
+
shift: false,
|
|
33
|
+
alt: false,
|
|
34
|
+
meta: false,
|
|
35
|
+
key: ""
|
|
36
|
+
};
|
|
37
|
+
for (const p of parts) {
|
|
38
|
+
if (p === "mod") result.mod = true;
|
|
39
|
+
else if (p === "ctrl" || p === "control") result.ctrl = true;
|
|
40
|
+
else if (p === "cmd" || p === "command" || p === "win") result.cmd = true;
|
|
41
|
+
else if (p === "shift") result.shift = true;
|
|
42
|
+
else if (p === "alt" || p === "option" || p === "opt") result.alt = true;
|
|
43
|
+
else if (p === "meta") result.meta = true;
|
|
44
|
+
else result.key = normalizeKey(p);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function matches(parsed, event, mac) {
|
|
49
|
+
const requiredCmd = parsed.cmd || parsed.mod && mac;
|
|
50
|
+
const requiredCtrl = parsed.ctrl || parsed.mod && !mac;
|
|
51
|
+
if (requiredCmd !== event.metaKey) return false;
|
|
52
|
+
if (requiredCtrl !== event.ctrlKey) return false;
|
|
53
|
+
if (parsed.shift !== event.shiftKey) return false;
|
|
54
|
+
if (parsed.alt !== event.altKey) return false;
|
|
55
|
+
if (parsed.meta && !mac && !event.metaKey) return false;
|
|
56
|
+
if (!parsed.key) return true;
|
|
57
|
+
const eventKey = normalizeKey(event.key);
|
|
58
|
+
return eventKey === parsed.key;
|
|
59
|
+
}
|
|
60
|
+
function isEditable(el) {
|
|
61
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
62
|
+
if (el.isContentEditable) return true;
|
|
63
|
+
const tag = el.tagName;
|
|
64
|
+
if (tag === "INPUT") {
|
|
65
|
+
const type = el.type;
|
|
66
|
+
return !["button", "submit", "reset", "checkbox", "radio", "range", "color", "file"].includes(type);
|
|
67
|
+
}
|
|
68
|
+
return tag === "TEXTAREA" || tag === "SELECT";
|
|
69
|
+
}
|
|
70
|
+
function hasModifier(event) {
|
|
71
|
+
return event.metaKey || event.ctrlKey || event.altKey;
|
|
72
|
+
}
|
|
73
|
+
function resolveTarget(target) {
|
|
74
|
+
if (!target) return typeof document !== "undefined" ? document : null;
|
|
75
|
+
if (target instanceof Document) return target;
|
|
76
|
+
if ("current" in target) return target.current ?? null;
|
|
77
|
+
return target;
|
|
78
|
+
}
|
|
79
|
+
function useHotkeys(combo, handler, options = {}) {
|
|
80
|
+
const { preventDefault = true, ignoreWhenTyping = true, enabled = true, target } = options;
|
|
81
|
+
const handlerRef = React2.useRef(handler);
|
|
82
|
+
React2.useEffect(() => {
|
|
83
|
+
handlerRef.current = handler;
|
|
84
|
+
}, [handler]);
|
|
85
|
+
React2.useEffect(() => {
|
|
86
|
+
if (!enabled) return;
|
|
87
|
+
const resolvedTarget = resolveTarget(target);
|
|
88
|
+
if (!resolvedTarget) return;
|
|
89
|
+
const combos = (Array.isArray(combo) ? combo : [combo]).map(parseCombo);
|
|
90
|
+
const mac = isMac();
|
|
91
|
+
const listener = (event) => {
|
|
92
|
+
const keyboardEvent = event;
|
|
93
|
+
const matched = combos.find((parsed) => matches(parsed, keyboardEvent, mac));
|
|
94
|
+
if (!matched) return;
|
|
95
|
+
if (ignoreWhenTyping && isEditable(keyboardEvent.target) && !hasModifier(keyboardEvent)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (preventDefault) keyboardEvent.preventDefault();
|
|
99
|
+
handlerRef.current(keyboardEvent);
|
|
100
|
+
};
|
|
101
|
+
resolvedTarget.addEventListener("keydown", listener);
|
|
102
|
+
return () => {
|
|
103
|
+
resolvedTarget.removeEventListener("keydown", listener);
|
|
104
|
+
};
|
|
105
|
+
}, [Array.isArray(combo) ? combo.join("|") : combo, enabled, ignoreWhenTyping, preventDefault, target]);
|
|
106
|
+
}
|
|
107
|
+
var HotkeyRegisterContext = React2.createContext(null);
|
|
108
|
+
var HotkeyListContext = React2.createContext([]);
|
|
109
|
+
function HotkeyProvider({ enabled = true, children }) {
|
|
110
|
+
const [entries, setEntries] = React2.useState(() => /* @__PURE__ */ new Map());
|
|
111
|
+
const handlersRef = React2.useRef(/* @__PURE__ */ new Map());
|
|
112
|
+
const register = React2.useRef((entry, handler) => {
|
|
113
|
+
handlersRef.current.set(entry.id, handler);
|
|
114
|
+
setEntries((prev) => {
|
|
115
|
+
const next = new Map(prev);
|
|
116
|
+
next.set(entry.id, entry);
|
|
117
|
+
return next;
|
|
118
|
+
});
|
|
119
|
+
return () => {
|
|
120
|
+
handlersRef.current.delete(entry.id);
|
|
121
|
+
setEntries((prev) => {
|
|
122
|
+
if (!prev.has(entry.id)) return prev;
|
|
123
|
+
const next = new Map(prev);
|
|
124
|
+
next.delete(entry.id);
|
|
125
|
+
return next;
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
}).current;
|
|
129
|
+
const hotkeys = React2.useMemo(() => Array.from(entries.values()), [entries]);
|
|
130
|
+
return /* @__PURE__ */ jsx(HotkeyRegisterContext.Provider, { value: register, children: /* @__PURE__ */ jsxs(HotkeyListContext.Provider, { value: hotkeys, children: [
|
|
131
|
+
children,
|
|
132
|
+
enabled && /* @__PURE__ */ jsx(HotkeyDispatcher, { entries: hotkeys, handlersRef })
|
|
133
|
+
] }) });
|
|
134
|
+
}
|
|
135
|
+
function HotkeyDispatcher({
|
|
136
|
+
entries,
|
|
137
|
+
handlersRef
|
|
138
|
+
}) {
|
|
139
|
+
return /* @__PURE__ */ jsx(Fragment, { children: entries.map((entry) => /* @__PURE__ */ jsx(HotkeyEntry, { entry, handlersRef }, entry.id)) });
|
|
140
|
+
}
|
|
141
|
+
function HotkeyEntry({
|
|
142
|
+
entry,
|
|
143
|
+
handlersRef
|
|
144
|
+
}) {
|
|
145
|
+
useHotkeys(
|
|
146
|
+
entry.combo,
|
|
147
|
+
(event) => {
|
|
148
|
+
const handler = handlersRef.current.get(entry.id);
|
|
149
|
+
handler?.(event);
|
|
150
|
+
},
|
|
151
|
+
{ enabled: entry.enabled !== false }
|
|
152
|
+
);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
function useHotkey(id, combo, handler, options = {}) {
|
|
156
|
+
const register = React2.useContext(HotkeyRegisterContext);
|
|
157
|
+
const handlerRef = React2.useRef(handler);
|
|
158
|
+
React2.useEffect(() => {
|
|
159
|
+
handlerRef.current = handler;
|
|
160
|
+
});
|
|
161
|
+
const comboKey = Array.isArray(combo) ? combo.join("|") : combo;
|
|
162
|
+
React2.useEffect(() => {
|
|
163
|
+
if (!register) return;
|
|
164
|
+
if (!comboKey) return;
|
|
165
|
+
return register(
|
|
166
|
+
{
|
|
167
|
+
id,
|
|
168
|
+
combo,
|
|
169
|
+
description: options.description,
|
|
170
|
+
group: options.group,
|
|
171
|
+
enabled: options.enabled
|
|
172
|
+
},
|
|
173
|
+
(event) => handlerRef.current(event)
|
|
174
|
+
);
|
|
175
|
+
}, [register, id, comboKey, options.description, options.group, options.enabled]);
|
|
176
|
+
}
|
|
177
|
+
function useHotkeyRegistry() {
|
|
178
|
+
const hotkeys = React2.useContext(HotkeyListContext);
|
|
179
|
+
return { hotkeys };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export { HotkeyProvider, useHotkey, useHotkeyRegistry };
|
|
3
183
|
//# sourceMappingURL=use-hotkey-registry.js.map
|
|
4
184
|
//# sourceMappingURL=use-hotkey-registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"use-hotkey-registry.js"}
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/use-hotkeys.ts","../../src/hooks/use-hotkey-registry.tsx"],"names":["React"],"mappings":"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","file":"use-hotkey-registry.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\n\n/**\n * Hotkey string grammar:\n *\n * <part>[+<part>]*\n *\n * where each <part> is one of:\n * mod — Cmd on macOS, Ctrl on everything else (recommended)\n * ctrl / cmd — explicit, cross-platform tokens\n * shift | alt | meta\n * <single key> — lowercase character, 'space', 'enter', 'escape', 'tab',\n * arrow{up,down,left,right}, 'slash', 'backspace', etc.\n *\n * Examples:\n * 'mod+k' — ⌘K on macOS, CtrlK elsewhere\n * 'mod+shift+p'\n * 'escape'\n * 'ctrl+k' — explicit Ctrl (no Cmd fallback on macOS)\n *\n * Passing an array of strings is treated as an OR — any combo fires the handler.\n */\nexport type HotkeyCombo = string | readonly string[]\n\nexport interface UseHotkeysOptions {\n /**\n * When true, `event.preventDefault()` is called on matched combos.\n * Default: true. Useful to allow the browser's native 'mod+k' (address bar)\n * to keep working — set to false if the handler only does something conditionally.\n */\n preventDefault?: boolean\n /**\n * Skip the handler when focus is inside an editable control (input, textarea,\n * contenteditable). The exception: combos that include a modifier (mod/ctrl/\n * cmd/alt) still fire — they are unambiguous shortcuts, not typing.\n * Default: true.\n */\n ignoreWhenTyping?: boolean\n /**\n * When false, the hook unregisters its listener. Useful for conditionally\n * enabling shortcuts (e.g., only while a panel is open).\n * Default: true.\n */\n enabled?: boolean\n /**\n * DOM target. Defaults to `document`. Pass a ref for scoped shortcuts (e.g.,\n * only when a specific panel has focus within).\n */\n target?: React.RefObject<HTMLElement | null> | HTMLElement | Document | null\n}\n\nfunction isMac(): boolean {\n if (typeof navigator === 'undefined') return false\n // `userAgentData.platform` is the modern API; fall back to platform string.\n // Accept 'darwin' (nodejs tests) and 'MacIntel'/'Mac OS X'.\n const ua = (navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform\n const platform = ua ?? navigator.platform ?? ''\n return /mac|darwin/i.test(platform)\n}\n\nconst SPECIAL_KEY_MAP: Record<string, string> = {\n ' ': 'space',\n spacebar: 'space',\n esc: 'escape',\n del: 'delete',\n return: 'enter',\n arrowup: 'up',\n arrowdown: 'down',\n arrowleft: 'left',\n arrowright: 'right',\n}\n\nfunction normalizeKey(raw: string): string {\n const lower = raw.toLowerCase()\n return SPECIAL_KEY_MAP[lower] ?? lower\n}\n\ntype ParsedCombo = {\n mod: boolean\n ctrl: boolean\n cmd: boolean\n shift: boolean\n alt: boolean\n meta: boolean\n key: string\n}\n\nfunction parseCombo(combo: string): ParsedCombo {\n const parts = combo\n .toLowerCase()\n .split('+')\n .map((s) => s.trim())\n .filter(Boolean)\n const result: ParsedCombo = {\n mod: false,\n ctrl: false,\n cmd: false,\n shift: false,\n alt: false,\n meta: false,\n key: '',\n }\n for (const p of parts) {\n if (p === 'mod') result.mod = true\n else if (p === 'ctrl' || p === 'control') result.ctrl = true\n else if (p === 'cmd' || p === 'command' || p === 'win') result.cmd = true\n else if (p === 'shift') result.shift = true\n else if (p === 'alt' || p === 'option' || p === 'opt') result.alt = true\n else if (p === 'meta') result.meta = true\n else result.key = normalizeKey(p)\n }\n return result\n}\n\nfunction matches(parsed: ParsedCombo, event: KeyboardEvent, mac: boolean): boolean {\n // `mod` resolves to Cmd on macOS, Ctrl elsewhere. Track both sides of the combo.\n const requiredCmd = parsed.cmd || (parsed.mod && mac)\n const requiredCtrl = parsed.ctrl || (parsed.mod && !mac)\n\n if (requiredCmd !== event.metaKey) return false\n if (requiredCtrl !== event.ctrlKey) return false\n if (parsed.shift !== event.shiftKey) return false\n if (parsed.alt !== event.altKey) return false\n // `meta` (plain) aliases Cmd on macOS — avoid double-matching when both mod+meta are specified.\n if (parsed.meta && !mac && !event.metaKey) return false\n\n if (!parsed.key) return true\n const eventKey = normalizeKey(event.key)\n return eventKey === parsed.key\n}\n\nfunction isEditable(el: EventTarget | null): boolean {\n if (!(el instanceof HTMLElement)) return false\n if (el.isContentEditable) return true\n const tag = el.tagName\n if (tag === 'INPUT') {\n const type = (el as HTMLInputElement).type\n // Buttons and checkboxes aren't \"typing\" — don't block.\n return !['button', 'submit', 'reset', 'checkbox', 'radio', 'range', 'color', 'file'].includes(type)\n }\n return tag === 'TEXTAREA' || tag === 'SELECT'\n}\n\nfunction hasModifier(event: KeyboardEvent): boolean {\n return event.metaKey || event.ctrlKey || event.altKey\n}\n\nfunction resolveTarget(target: UseHotkeysOptions['target']): EventTarget | null {\n if (!target) return typeof document !== 'undefined' ? document : null\n if (target instanceof Document) return target\n if ('current' in target) return target.current ?? null\n return target\n}\n\n/**\n * Register a keyboard shortcut. Handler fires when the combo matches, respecting\n * `ignoreWhenTyping` (default true) — except when a modifier is part of the combo,\n * which always wins. Returns nothing; cleanup is automatic.\n *\n * @example\n * useHotkeys('mod+k', () => setOpen((v) => !v))\n * useHotkeys(['escape', 'mod+w'], close, { enabled: open })\n */\nexport function useHotkeys(\n combo: HotkeyCombo,\n handler: (event: KeyboardEvent) => void,\n options: UseHotkeysOptions = {}\n): void {\n const { preventDefault = true, ignoreWhenTyping = true, enabled = true, target } = options\n\n // Keep latest handler in a ref so callers don't need to memoize.\n const handlerRef = React.useRef(handler)\n React.useEffect(() => {\n handlerRef.current = handler\n }, [handler])\n\n React.useEffect(() => {\n if (!enabled) return\n const resolvedTarget = resolveTarget(target)\n if (!resolvedTarget) return\n\n const combos = (Array.isArray(combo) ? combo : [combo]).map(parseCombo)\n const mac = isMac()\n\n const listener = (event: Event) => {\n const keyboardEvent = event as KeyboardEvent\n const matched = combos.find((parsed) => matches(parsed, keyboardEvent, mac))\n if (!matched) return\n\n if (ignoreWhenTyping && isEditable(keyboardEvent.target) && !hasModifier(keyboardEvent)) {\n return\n }\n\n if (preventDefault) keyboardEvent.preventDefault()\n handlerRef.current(keyboardEvent)\n }\n\n resolvedTarget.addEventListener('keydown', listener)\n return () => {\n resolvedTarget.removeEventListener('keydown', listener)\n }\n // `combo` is usually a stable string literal; if callers pass a new array every\n // render we join-stringify to avoid stale closures without surprising rerenders.\n }, [Array.isArray(combo) ? combo.join('|') : combo, enabled, ignoreWhenTyping, preventDefault, target])\n}\n\n/**\n * Render-helper: returns a platform-appropriate display string for a combo.\n * 'mod+k' → '⌘K' on macOS, 'Ctrl+K' elsewhere. Useful for `<CommandShortcut>`.\n */\nexport function formatHotkey(combo: string): string {\n const mac = isMac()\n return combo\n .split('+')\n .map((p) => {\n const t = p.trim().toLowerCase()\n if (t === 'mod') return mac ? '⌘' : 'Ctrl'\n if (t === 'cmd' || t === 'meta') return mac ? '⌘' : 'Win'\n if (t === 'ctrl' || t === 'control') return mac ? '⌃' : 'Ctrl'\n if (t === 'shift') return mac ? '⇧' : 'Shift'\n if (t === 'alt' || t === 'option' || t === 'opt') return mac ? '⌥' : 'Alt'\n if (t === 'enter' || t === 'return') return '↵'\n if (t === 'escape' || t === 'esc') return 'Esc'\n if (t === 'space') return 'Space'\n if (t === 'up') return '↑'\n if (t === 'down') return '↓'\n if (t === 'left') return '←'\n if (t === 'right') return '→'\n return t.length === 1 ? t.toUpperCase() : t\n })\n .join(mac ? '' : '+')\n}\n","'use client'\n\nimport * as React from 'react'\nimport { useHotkeys, type HotkeyCombo } from './use-hotkeys'\n\n/* -------------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------------- */\n\nexport interface RegisteredHotkey {\n /** Stable id — used for de-registration + dedupe. */\n id: string\n /** The combo string(s) — `'mod+k'`, `['escape', 'mod+w']`, etc. */\n combo: HotkeyCombo\n /** Short human-readable description (e.g. \"باز کردن پالت دستور\"). */\n description?: string\n /** Optional grouping label for cheatsheet rendering (\"سراسری\", \"ناوبری\"…). */\n group?: string\n /** When false, the entry is listed but the listener doesn't fire. */\n enabled?: boolean\n}\n\ntype RegisterFn = (entry: RegisteredHotkey, handler: (event: KeyboardEvent) => void) => () => void\n\n/**\n * Two contexts so consumers can depend on a *stable* register fn (never\n * changes for the provider's lifetime) without re-running their effect\n * every time the entries list updates.\n */\nconst HotkeyRegisterContext = React.createContext<RegisterFn | null>(null)\nconst HotkeyListContext = React.createContext<RegisteredHotkey[]>([])\n\n/* -------------------------------------------------------------------------- */\n/* Provider */\n/* -------------------------------------------------------------------------- */\n\nexport interface HotkeyProviderProps {\n /** Globally disable all registered hotkeys (e.g. while a modal owns the page). */\n enabled?: boolean\n children: React.ReactNode\n}\n\n/**\n * Centralised hotkey registry. Wrap your app once; descendants register\n * combos via `useHotkey()` and any UI (CommandPalette, help cheatsheet)\n * can read the full list via `useHotkeyRegistry()`.\n *\n * Compared to scattering `useHotkeys` calls across the tree:\n * - Per-entry mounting is centralized (one place that handles\n * `enabled`, key parsing, ignore-when-typing semantics)\n * - Discoverable: cheatsheets / palettes can list every active combo\n * - De-duplicated: registering the same id twice replaces the entry\n *\n * Optional. `useHotkeys` (the bare hook) continues to work outside a provider.\n */\nexport function HotkeyProvider({ enabled = true, children }: HotkeyProviderProps) {\n const [entries, setEntries] = React.useState<Map<string, RegisteredHotkey>>(() => new Map())\n const handlersRef = React.useRef<Map<string, (event: KeyboardEvent) => void>>(new Map())\n\n // Stable across the provider's lifetime so descendants don't re-register\n // every time the entries list changes.\n const register = React.useRef<RegisterFn>((entry, handler) => {\n handlersRef.current.set(entry.id, handler)\n setEntries((prev) => {\n const next = new Map(prev)\n next.set(entry.id, entry)\n return next\n })\n return () => {\n handlersRef.current.delete(entry.id)\n setEntries((prev) => {\n if (!prev.has(entry.id)) return prev\n const next = new Map(prev)\n next.delete(entry.id)\n return next\n })\n }\n }).current\n\n const hotkeys = React.useMemo(() => Array.from(entries.values()), [entries])\n\n return (\n <HotkeyRegisterContext.Provider value={register}>\n <HotkeyListContext.Provider value={hotkeys}>\n {children}\n {enabled && <HotkeyDispatcher entries={hotkeys} handlersRef={handlersRef} />}\n </HotkeyListContext.Provider>\n </HotkeyRegisterContext.Provider>\n )\n}\n\n/**\n * Internal — one useHotkeys per registered entry, mounted under the\n * provider rather than per-component. Per-entry useHotkeys keeps key\n * parsing and ignoreWhenTyping logic in one tested place.\n */\nfunction HotkeyDispatcher({\n entries,\n handlersRef,\n}: {\n entries: RegisteredHotkey[]\n handlersRef: React.MutableRefObject<Map<string, (event: KeyboardEvent) => void>>\n}) {\n return (\n <>\n {entries.map((entry) => (\n <HotkeyEntry key={entry.id} entry={entry} handlersRef={handlersRef} />\n ))}\n </>\n )\n}\n\nfunction HotkeyEntry({\n entry,\n handlersRef,\n}: {\n entry: RegisteredHotkey\n handlersRef: React.MutableRefObject<Map<string, (event: KeyboardEvent) => void>>\n}) {\n useHotkeys(\n entry.combo,\n (event) => {\n const handler = handlersRef.current.get(entry.id)\n handler?.(event)\n },\n { enabled: entry.enabled !== false }\n )\n return null\n}\n\n/* -------------------------------------------------------------------------- */\n/* Hooks */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Register a hotkey under the active provider. The combo is added to the\n * cheatsheet-readable registry; the handler fires when the combo matches\n * (subject to `useHotkeys` rules around editable targets + modifiers).\n *\n * @example\n * useHotkey('palette', 'mod+k', () => setOpen(true), {\n * description: 'باز کردن پالت دستور',\n * group: 'سراسری',\n * })\n *\n * The first argument is a stable id — use a unique slug per call site so\n * registry updates can dedupe. When called outside a `<HotkeyProvider>`,\n * silently no-ops (so consumers can layer the provider in later without\n * a refactor sweep).\n */\nexport function useHotkey(\n id: string,\n combo: HotkeyCombo,\n handler: (event: KeyboardEvent) => void,\n options: { description?: string; group?: string; enabled?: boolean } = {}\n): void {\n const register = React.useContext(HotkeyRegisterContext)\n\n // Capture the latest handler so callers can pass an inline arrow each\n // render without re-registering the entry.\n const handlerRef = React.useRef(handler)\n React.useEffect(() => {\n handlerRef.current = handler\n })\n\n // Stringify combo for stable dependency tracking.\n const comboKey = Array.isArray(combo) ? combo.join('|') : combo\n\n React.useEffect(() => {\n if (!register) return\n // Skip registration when combo is empty/falsy — lets consumers conditionally\n // disable a hotkey without violating rules of hooks (still call useHotkey\n // unconditionally, but pass `''` to opt out).\n if (!comboKey) return\n return register(\n {\n id,\n combo,\n description: options.description,\n group: options.group,\n enabled: options.enabled,\n },\n (event) => handlerRef.current(event)\n )\n // `combo` (the original value) intentionally elided; comboKey covers it.\n }, [register, id, comboKey, options.description, options.group, options.enabled])\n}\n\n/**\n * Read the active registry. Returns an empty array outside a provider.\n * Useful for CommandPalette items, \"?\" cheatsheet overlays, settings pages.\n */\nexport function useHotkeyRegistry(): { hotkeys: RegisteredHotkey[] } {\n const hotkeys = React.useContext(HotkeyListContext)\n return { hotkeys }\n}\n"]}
|