@parto-system-design/ui 1.1.5 → 1.1.8
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/AGENTS.md +233 -0
- package/LICENSE +21 -0
- package/README.md +96 -43
- package/dist/chunk-2ACKKPWA.cjs +112 -0
- package/dist/chunk-2ACKKPWA.cjs.map +1 -0
- package/dist/chunk-2UD3LGVX.cjs +316 -0
- package/dist/chunk-2UD3LGVX.cjs.map +1 -0
- package/dist/chunk-3QYYPPFJ.js +269 -0
- package/dist/chunk-3QYYPPFJ.js.map +1 -0
- package/dist/chunk-4SVQNEVH.js +173 -0
- package/dist/chunk-4SVQNEVH.js.map +1 -0
- package/dist/chunk-4WONHORR.cjs +152 -0
- package/dist/chunk-4WONHORR.cjs.map +1 -0
- package/dist/chunk-5HCXH6GS.js +409 -0
- package/dist/chunk-5HCXH6GS.js.map +1 -0
- package/dist/chunk-5JJSRGJD.js +31 -0
- package/dist/chunk-5JJSRGJD.js.map +1 -0
- package/dist/chunk-5K6E4ZSW.cjs +77 -0
- package/dist/chunk-5K6E4ZSW.cjs.map +1 -0
- package/dist/chunk-5NY26ULO.js +89 -0
- package/dist/chunk-5NY26ULO.js.map +1 -0
- package/dist/chunk-7RVPG3LE.cjs +231 -0
- package/dist/chunk-7RVPG3LE.cjs.map +1 -0
- package/dist/chunk-AYEK3WOM.js +207 -0
- package/dist/chunk-AYEK3WOM.js.map +1 -0
- package/dist/chunk-BRMBLIQG.js +53 -0
- package/dist/chunk-BRMBLIQG.js.map +1 -0
- package/dist/chunk-CAJKSTXX.cjs +54 -0
- package/dist/chunk-CAJKSTXX.cjs.map +1 -0
- package/dist/chunk-CKFWMHQU.js +401 -0
- package/dist/chunk-CKFWMHQU.js.map +1 -0
- package/dist/chunk-CV3N3HVK.js +672 -0
- package/dist/chunk-CV3N3HVK.js.map +1 -0
- package/dist/chunk-D2EBLE2B.cjs +220 -0
- package/dist/chunk-D2EBLE2B.cjs.map +1 -0
- package/dist/chunk-GCZ6YATL.js +940 -0
- package/dist/chunk-GCZ6YATL.js.map +1 -0
- package/dist/chunk-GKRAZGDI.cjs +84 -0
- package/dist/chunk-GKRAZGDI.cjs.map +1 -0
- package/dist/chunk-GPYJ66CG.js +45 -0
- package/dist/chunk-GPYJ66CG.js.map +1 -0
- package/dist/chunk-HF6XU5NI.js +84 -0
- package/dist/chunk-HF6XU5NI.js.map +1 -0
- package/dist/chunk-HJPDZOMJ.cjs +87 -0
- package/dist/chunk-HJPDZOMJ.cjs.map +1 -0
- package/dist/chunk-HS3XI3CC.cjs +69 -0
- package/dist/chunk-HS3XI3CC.cjs.map +1 -0
- package/dist/chunk-HUCC3QH5.cjs +53 -0
- package/dist/chunk-HUCC3QH5.cjs.map +1 -0
- package/dist/chunk-HYZ6BQPS.cjs +425 -0
- package/dist/chunk-HYZ6BQPS.cjs.map +1 -0
- package/dist/chunk-ISCSZMYW.cjs +106 -0
- package/dist/chunk-ISCSZMYW.cjs.map +1 -0
- package/dist/chunk-IXFEFIDO.js +82 -0
- package/dist/chunk-IXFEFIDO.js.map +1 -0
- package/dist/chunk-JCJLN437.js +108 -0
- package/dist/chunk-JCJLN437.js.map +1 -0
- package/dist/chunk-JMKNNH63.cjs +982 -0
- package/dist/chunk-JMKNNH63.cjs.map +1 -0
- package/dist/chunk-JUBHQAA2.js +53 -0
- package/dist/chunk-JUBHQAA2.js.map +1 -0
- package/dist/chunk-K6G63EED.cjs +41 -0
- package/dist/chunk-K6G63EED.cjs.map +1 -0
- package/dist/chunk-KCWRCSI7.js +62 -0
- package/dist/chunk-KCWRCSI7.js.map +1 -0
- package/dist/chunk-KYM7NIJO.cjs +433 -0
- package/dist/chunk-KYM7NIJO.cjs.map +1 -0
- package/dist/chunk-L2L5CKC2.js +291 -0
- package/dist/chunk-L2L5CKC2.js.map +1 -0
- package/dist/chunk-M5CHZ5BA.js +124 -0
- package/dist/chunk-M5CHZ5BA.js.map +1 -0
- package/dist/chunk-MEK4RSGC.js +65 -0
- package/dist/chunk-MEK4RSGC.js.map +1 -0
- package/dist/chunk-MEKWH3GS.js +89 -0
- package/dist/chunk-MEKWH3GS.js.map +1 -0
- package/dist/chunk-MFTX2DDQ.js +27 -0
- package/dist/chunk-MFTX2DDQ.js.map +1 -0
- package/dist/chunk-MMC6M35Q.cjs +272 -0
- package/dist/chunk-MMC6M35Q.cjs.map +1 -0
- package/dist/chunk-NMH43BDC.js +130 -0
- package/dist/chunk-NMH43BDC.js.map +1 -0
- package/dist/chunk-NORDUD2T.cjs +135 -0
- package/dist/chunk-NORDUD2T.cjs.map +1 -0
- package/dist/chunk-NV4JOKWL.cjs +197 -0
- package/dist/chunk-NV4JOKWL.cjs.map +1 -0
- package/dist/chunk-O2JG7WY5.cjs +121 -0
- package/dist/chunk-O2JG7WY5.cjs.map +1 -0
- package/dist/chunk-ONO2FTV4.cjs +68 -0
- package/dist/chunk-ONO2FTV4.cjs.map +1 -0
- package/dist/chunk-OQB6HIUL.cjs +108 -0
- package/dist/chunk-OQB6HIUL.cjs.map +1 -0
- package/dist/chunk-OS6CMYAS.cjs +79 -0
- package/dist/chunk-OS6CMYAS.cjs.map +1 -0
- package/dist/chunk-PYURPUTV.js +402 -0
- package/dist/chunk-PYURPUTV.js.map +1 -0
- package/dist/chunk-RJ3HYZ7S.js +44 -0
- package/dist/chunk-RJ3HYZ7S.js.map +1 -0
- package/dist/chunk-RZNRIOLT.js +128 -0
- package/dist/chunk-RZNRIOLT.js.map +1 -0
- package/dist/chunk-S3T2L6NA.js +38 -0
- package/dist/chunk-S3T2L6NA.js.map +1 -0
- package/dist/chunk-S5IPJQZ3.cjs +161 -0
- package/dist/chunk-S5IPJQZ3.cjs.map +1 -0
- package/dist/chunk-SB5DSYR5.js +211 -0
- package/dist/chunk-SB5DSYR5.js.map +1 -0
- package/dist/chunk-SFXV2DUH.js +106 -0
- package/dist/chunk-SFXV2DUH.js.map +1 -0
- package/dist/chunk-SXEPGD4Z.cjs +152 -0
- package/dist/chunk-SXEPGD4Z.cjs.map +1 -0
- package/dist/chunk-SXWSOU3Y.js +89 -0
- package/dist/chunk-SXWSOU3Y.js.map +1 -0
- package/dist/chunk-SZMVOHT7.cjs +107 -0
- package/dist/chunk-SZMVOHT7.cjs.map +1 -0
- package/dist/chunk-TWJXOV4C.js +145 -0
- package/dist/chunk-TWJXOV4C.js.map +1 -0
- package/dist/chunk-U3ADRIVO.cjs +434 -0
- package/dist/chunk-U3ADRIVO.cjs.map +1 -0
- package/dist/chunk-U5FLLCGC.cjs +151 -0
- package/dist/chunk-U5FLLCGC.cjs.map +1 -0
- package/dist/chunk-UOZN45G4.cjs +130 -0
- package/dist/chunk-UOZN45G4.cjs.map +1 -0
- package/dist/chunk-VHLDOG74.cjs +167 -0
- package/dist/chunk-VHLDOG74.cjs.map +1 -0
- package/dist/chunk-YC5KLN6I.js +139 -0
- package/dist/chunk-YC5KLN6I.js.map +1 -0
- package/dist/chunk-YENXXYUV.cjs +111 -0
- package/dist/chunk-YENXXYUV.cjs.map +1 -0
- package/dist/chunk-YFQWC2PW.js +113 -0
- package/dist/chunk-YFQWC2PW.js.map +1 -0
- package/dist/chunk-Z2TY4A75.cjs +700 -0
- package/dist/chunk-Z2TY4A75.cjs.map +1 -0
- package/dist/chunk-Z56O7UEU.cjs +136 -0
- package/dist/chunk-Z56O7UEU.cjs.map +1 -0
- package/dist/chunk-ZBZDR4ZC.js +106 -0
- package/dist/chunk-ZBZDR4ZC.js.map +1 -0
- package/dist/components/charts/PartoAreaChart.cjs +15 -0
- package/dist/components/charts/PartoAreaChart.cjs.map +1 -0
- package/dist/components/charts/PartoAreaChart.d.cts +53 -0
- package/dist/components/charts/PartoAreaChart.d.ts +53 -0
- package/dist/components/charts/PartoAreaChart.js +6 -0
- package/dist/components/charts/PartoAreaChart.js.map +1 -0
- package/dist/components/charts/PartoBarChart.cjs +15 -0
- package/dist/components/charts/PartoBarChart.cjs.map +1 -0
- package/dist/components/charts/PartoBarChart.d.cts +61 -0
- package/dist/components/charts/PartoBarChart.d.ts +61 -0
- package/dist/components/charts/PartoBarChart.js +6 -0
- package/dist/components/charts/PartoBarChart.js.map +1 -0
- package/dist/components/charts/PartoLineChart.cjs +15 -0
- package/dist/components/charts/PartoLineChart.cjs.map +1 -0
- package/dist/components/charts/PartoLineChart.d.cts +57 -0
- package/dist/components/charts/PartoLineChart.d.ts +57 -0
- package/dist/components/charts/PartoLineChart.js +6 -0
- package/dist/components/charts/PartoLineChart.js.map +1 -0
- package/dist/components/charts/PartoPieChart.cjs +15 -0
- package/dist/components/charts/PartoPieChart.cjs.map +1 -0
- package/dist/components/charts/PartoPieChart.d.cts +52 -0
- package/dist/components/charts/PartoPieChart.d.ts +52 -0
- package/dist/components/charts/PartoPieChart.js +6 -0
- package/dist/components/charts/PartoPieChart.js.map +1 -0
- package/dist/components/ui/alert-rule-card.cjs +15 -0
- package/dist/components/ui/alert-rule-card.cjs.map +1 -0
- package/dist/components/ui/alert-rule-card.d.cts +38 -0
- package/dist/components/ui/alert-rule-card.d.ts +38 -0
- package/dist/components/ui/alert-rule-card.js +6 -0
- package/dist/components/ui/alert-rule-card.js.map +1 -0
- package/dist/components/ui/avatar.cjs +21 -0
- package/dist/components/ui/avatar.cjs.map +1 -0
- package/dist/components/ui/avatar.d.cts +18 -0
- package/dist/components/ui/avatar.d.ts +18 -0
- package/dist/components/ui/avatar.js +4 -0
- package/dist/components/ui/avatar.js.map +1 -0
- package/dist/components/ui/badge.cjs +17 -0
- package/dist/components/ui/badge.cjs.map +1 -0
- package/dist/components/ui/badge.d.cts +16 -0
- package/dist/components/ui/badge.d.ts +16 -0
- package/dist/components/ui/badge.js +4 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/button.cjs +18 -0
- package/dist/components/ui/button.cjs.map +1 -0
- package/dist/components/ui/button.d.cts +37 -0
- package/dist/components/ui/button.d.ts +37 -0
- package/dist/components/ui/button.js +5 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/calendar.cjs +15 -0
- package/dist/components/ui/calendar.cjs.map +1 -0
- package/dist/components/ui/calendar.d.cts +17 -0
- package/dist/components/ui/calendar.d.ts +17 -0
- package/dist/components/ui/calendar.js +6 -0
- package/dist/components/ui/calendar.js.map +1 -0
- package/dist/components/ui/card.cjs +37 -0
- package/dist/components/ui/card.cjs.map +1 -0
- package/dist/components/ui/card.d.cts +18 -0
- package/dist/components/ui/card.d.ts +18 -0
- package/dist/components/ui/card.js +4 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/concept-card.cjs +18 -0
- package/dist/components/ui/concept-card.cjs.map +1 -0
- package/dist/components/ui/concept-card.d.cts +5 -0
- package/dist/components/ui/concept-card.d.ts +5 -0
- package/dist/components/ui/concept-card.js +9 -0
- package/dist/components/ui/concept-card.js.map +1 -0
- package/dist/components/ui/data-table.cjs +18 -0
- package/dist/components/ui/data-table.cjs.map +1 -0
- package/dist/components/ui/data-table.d.cts +181 -0
- package/dist/components/ui/data-table.d.ts +181 -0
- package/dist/components/ui/data-table.js +9 -0
- package/dist/components/ui/data-table.js.map +1 -0
- package/dist/components/ui/dialog.cjs +49 -0
- package/dist/components/ui/dialog.cjs.map +1 -0
- package/dist/components/ui/dialog.d.cts +22 -0
- package/dist/components/ui/dialog.d.ts +22 -0
- package/dist/components/ui/dialog.js +4 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/filter-provider.cjs +20 -0
- package/dist/components/ui/filter-provider.cjs.map +1 -0
- package/dist/components/ui/filter-provider.d.cts +49 -0
- package/dist/components/ui/filter-provider.d.ts +49 -0
- package/dist/components/ui/filter-provider.js +3 -0
- package/dist/components/ui/filter-provider.js.map +1 -0
- package/dist/components/ui/input.cjs +22 -0
- package/dist/components/ui/input.cjs.map +1 -0
- package/dist/components/ui/input.d.cts +16 -0
- package/dist/components/ui/input.d.ts +16 -0
- package/dist/components/ui/input.js +5 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/iran-province-heat.cjs +13 -0
- package/dist/components/ui/iran-province-heat.cjs.map +1 -0
- package/dist/components/ui/iran-province-heat.d.cts +64 -0
- package/dist/components/ui/iran-province-heat.d.ts +64 -0
- package/dist/components/ui/iran-province-heat.js +4 -0
- package/dist/components/ui/iran-province-heat.js.map +1 -0
- package/dist/components/ui/page-card.cjs +16 -0
- package/dist/components/ui/page-card.cjs.map +1 -0
- package/dist/components/ui/page-card.d.cts +6 -0
- package/dist/components/ui/page-card.d.ts +6 -0
- package/dist/components/ui/page-card.js +7 -0
- package/dist/components/ui/page-card.js.map +1 -0
- package/dist/components/ui/popover.cjs +25 -0
- package/dist/components/ui/popover.cjs.map +1 -0
- package/dist/components/ui/popover.d.cts +9 -0
- package/dist/components/ui/popover.d.ts +9 -0
- package/dist/components/ui/popover.js +4 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/components/ui/saved-query-card.cjs +15 -0
- package/dist/components/ui/saved-query-card.cjs.map +1 -0
- package/dist/components/ui/saved-query-card.d.cts +41 -0
- package/dist/components/ui/saved-query-card.d.ts +41 -0
- package/dist/components/ui/saved-query-card.js +6 -0
- package/dist/components/ui/saved-query-card.js.map +1 -0
- package/dist/components/ui/separator.cjs +13 -0
- package/dist/components/ui/separator.cjs.map +1 -0
- package/dist/components/ui/separator.d.cts +9 -0
- package/dist/components/ui/separator.d.ts +9 -0
- package/dist/components/ui/separator.js +4 -0
- package/dist/components/ui/separator.js.map +1 -0
- package/dist/components/ui/sheet.cjs +45 -0
- package/dist/components/ui/sheet.cjs.map +1 -0
- package/dist/components/ui/sheet.d.cts +44 -0
- package/dist/components/ui/sheet.d.ts +44 -0
- package/dist/components/ui/sheet.js +4 -0
- package/dist/components/ui/sheet.js.map +1 -0
- package/dist/components/ui/sparkline.cjs +13 -0
- package/dist/components/ui/sparkline.cjs.map +1 -0
- package/dist/components/ui/sparkline.d.cts +36 -0
- package/dist/components/ui/sparkline.d.ts +36 -0
- package/dist/components/ui/sparkline.js +4 -0
- package/dist/components/ui/sparkline.js.map +1 -0
- package/dist/components/ui/tooltip.cjs +25 -0
- package/dist/components/ui/tooltip.cjs.map +1 -0
- package/dist/components/ui/tooltip.d.cts +17 -0
- package/dist/components/ui/tooltip.d.ts +17 -0
- package/dist/components/ui/tooltip.js +4 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/concept-card-CcOBb2Nz.d.ts +83 -0
- package/dist/concept-card-RwPbqJ06.d.cts +83 -0
- package/dist/hooks/use-hotkey-registry.cjs +21 -0
- package/dist/hooks/use-hotkey-registry.cjs.map +1 -0
- package/dist/hooks/use-hotkey-registry.d.cts +65 -0
- package/dist/hooks/use-hotkey-registry.d.ts +65 -0
- package/dist/hooks/use-hotkey-registry.js +4 -0
- package/dist/hooks/use-hotkey-registry.js.map +1 -0
- package/dist/hooks/use-hotkeys.cjs +16 -0
- package/dist/hooks/use-hotkeys.cjs.map +1 -0
- package/dist/hooks/use-hotkeys.d.cts +66 -0
- package/dist/hooks/use-hotkeys.d.ts +66 -0
- package/dist/hooks/use-hotkeys.js +3 -0
- package/dist/hooks/use-hotkeys.js.map +1 -0
- package/dist/i18n-ArS3mqj0.d.ts +344 -0
- package/dist/i18n-CAd9wGOr.d.cts +344 -0
- package/dist/index.cjs +12195 -13224
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +490 -34
- package/dist/index.d.cts +1635 -1263
- package/dist/index.d.ts +1635 -1263
- package/dist/index.js +10693 -12364
- package/dist/index.js.map +1 -1
- package/dist/page-card-CmShVqG-.d.cts +100 -0
- package/dist/page-card-HBn-cy4J.d.ts +100 -0
- package/dist/utils-DlXWmDZ-.d.cts +35 -0
- package/dist/utils-DlXWmDZ-.d.ts +35 -0
- package/package.json +160 -4
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/components/ui/filter-provider.tsx
|
|
5
|
+
var FilterContext = React.createContext(null);
|
|
6
|
+
function FilterProvider({
|
|
7
|
+
initialState,
|
|
8
|
+
state: controlledState,
|
|
9
|
+
onStateChange,
|
|
10
|
+
children
|
|
11
|
+
}) {
|
|
12
|
+
const [internalState, setInternalState] = React.useState(initialState);
|
|
13
|
+
const isControlled = controlledState !== void 0;
|
|
14
|
+
const state = isControlled ? controlledState : internalState;
|
|
15
|
+
const initialRef = React.useRef(initialState);
|
|
16
|
+
const set = React.useCallback(
|
|
17
|
+
(next) => {
|
|
18
|
+
if (!isControlled) setInternalState(next);
|
|
19
|
+
onStateChange?.(next);
|
|
20
|
+
},
|
|
21
|
+
[isControlled, onStateChange]
|
|
22
|
+
);
|
|
23
|
+
const patch = React.useCallback(
|
|
24
|
+
(partial) => {
|
|
25
|
+
const next = { ...state, ...partial };
|
|
26
|
+
if (!isControlled) setInternalState(next);
|
|
27
|
+
onStateChange?.(next);
|
|
28
|
+
},
|
|
29
|
+
[isControlled, onStateChange, state]
|
|
30
|
+
);
|
|
31
|
+
const reset = React.useCallback(() => {
|
|
32
|
+
const next = initialRef.current;
|
|
33
|
+
if (!isControlled) setInternalState(next);
|
|
34
|
+
onStateChange?.(next);
|
|
35
|
+
}, [isControlled, onStateChange]);
|
|
36
|
+
const value = React.useMemo(() => ({ state, set, patch, reset }), [state, set, patch, reset]);
|
|
37
|
+
return /* @__PURE__ */ jsx(FilterContext.Provider, { value, children });
|
|
38
|
+
}
|
|
39
|
+
function useFilterState() {
|
|
40
|
+
const ctx = React.useContext(FilterContext);
|
|
41
|
+
if (!ctx) {
|
|
42
|
+
throw new Error("useFilterState must be used within a <FilterProvider>");
|
|
43
|
+
}
|
|
44
|
+
return ctx;
|
|
45
|
+
}
|
|
46
|
+
function useFilterStateOptional() {
|
|
47
|
+
const ctx = React.useContext(FilterContext);
|
|
48
|
+
return ctx ?? null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { FilterProvider, useFilterState, useFilterStateOptional };
|
|
52
|
+
//# sourceMappingURL=chunk-BRMBLIQG.js.map
|
|
53
|
+
//# sourceMappingURL=chunk-BRMBLIQG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/filter-provider.tsx"],"names":[],"mappings":";;;;AA2BA,IAAM,aAAA,GAAsB,oBAAyC,IAAI,CAAA;AAqBlE,SAAS,cAAA,CAA2C;AAAA,EACzD,YAAA;AAAA,EACA,KAAA,EAAO,eAAA;AAAA,EACP,aAAA;AAAA,EACA;AACF,CAAA,EAA2B;AACzB,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAU,eAAY,YAAY,CAAA;AACxE,EAAA,MAAM,eAAe,eAAA,KAAoB,MAAA;AACzC,EAAA,MAAM,KAAA,GAAQ,eAAe,eAAA,GAAkB,aAAA;AAI/C,EAAA,MAAM,UAAA,GAAmB,aAAO,YAAY,CAAA;AAE5C,EAAA,MAAM,GAAA,GAAY,KAAA,CAAA,WAAA;AAAA,IAChB,CAAC,IAAA,KAAY;AACX,MAAA,IAAI,CAAC,YAAA,EAAc,gBAAA,CAAiB,IAAI,CAAA;AACxC,MAAA,aAAA,GAAgB,IAAI,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,CAAC,cAAc,aAAa;AAAA,GAC9B;AAEA,EAAA,MAAM,KAAA,GAAc,KAAA,CAAA,WAAA;AAAA,IAClB,CAAC,OAAA,KAAwB;AACvB,MAAA,MAAM,IAAA,GAAO,EAAE,GAAG,KAAA,EAAO,GAAG,OAAA,EAAQ;AACpC,MAAA,IAAI,CAAC,YAAA,EAAc,gBAAA,CAAiB,IAAI,CAAA;AACxC,MAAA,aAAA,GAAgB,IAAI,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,CAAC,YAAA,EAAc,aAAA,EAAe,KAAK;AAAA,GACrC;AAEA,EAAA,MAAM,KAAA,GAAc,kBAAY,MAAM;AACpC,IAAA,MAAM,OAAO,UAAA,CAAW,OAAA;AACxB,IAAA,IAAI,CAAC,YAAA,EAAc,gBAAA,CAAiB,IAAI,CAAA;AACxC,IAAA,aAAA,GAAgB,IAAI,CAAA;AAAA,EACtB,CAAA,EAAG,CAAC,YAAA,EAAc,aAAa,CAAC,CAAA;AAEhC,EAAA,MAAM,KAAA,GAAc,KAAA,CAAA,OAAA,CAA+B,OAAO,EAAE,OAAO,GAAA,EAAK,KAAA,EAAO,KAAA,EAAM,CAAA,EAAI,CAAC,KAAA,EAAO,GAAA,EAAK,KAAA,EAAO,KAAK,CAAC,CAAA;AAEnH,EAAA,uBAAO,GAAA,CAAC,aAAA,CAAc,QAAA,EAAd,EAAuB,OAAqC,QAAA,EAAS,CAAA;AAC/E;AAeO,SAAS,cAAA,GAAuF;AACrG,EAAA,MAAM,GAAA,GAAY,iBAAW,aAAa,CAAA;AAC1C,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,EACzE;AACA,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,sBAAA,GAAsG;AACpH,EAAA,MAAM,GAAA,GAAY,iBAAW,aAAa,CAAA;AAC1C,EAAA,OAAQ,GAAA,IAAiC,IAAA;AAC3C","file":"chunk-BRMBLIQG.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\n\n/* -------------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Generic, type-erased filter state. Consumers cast at the `useFilterState`\n * call-site for a typed view. We deliberately do not parameterize the\n * Context itself — a single provider serves the whole tree, and the type\n * lives at the read-site (call as `useFilterState<MyFilters>()`).\n */\nexport type FilterStateShape = Record<string, unknown>\n\nexport interface FilterContextValue<T extends FilterStateShape = FilterStateShape> {\n /** Current filter state. */\n state: T\n /** Replace the entire filter state (full reset to a new shape). */\n set: (next: T) => void\n /** Shallow-merge a partial update. Use this for most edits. */\n patch: (partial: Partial<T>) => void\n /** Reset state to the initial value passed to the Provider. */\n reset: () => void\n}\n\nconst FilterContext = React.createContext<FilterContextValue | null>(null)\n\n/* -------------------------------------------------------------------------- */\n/* Provider */\n/* -------------------------------------------------------------------------- */\n\nexport interface FilterProviderProps<T extends FilterStateShape> {\n /** Initial filter state on first mount. */\n initialState: T\n /**\n * Controlled state. When supplied, `state` becomes a pure projection of\n * this prop and updates flow back through `onStateChange`. Use this when\n * the consumer manages state externally (e.g. via Redux / Zustand /\n * useState in a parent).\n */\n state?: T\n /** Called on every state change in both controlled and uncontrolled modes. */\n onStateChange?: (next: T) => void\n children: React.ReactNode\n}\n\nexport function FilterProvider<T extends FilterStateShape>({\n initialState,\n state: controlledState,\n onStateChange,\n children,\n}: FilterProviderProps<T>) {\n const [internalState, setInternalState] = React.useState<T>(initialState)\n const isControlled = controlledState !== undefined\n const state = isControlled ? controlledState : internalState\n\n // Stable initial-state ref so reset() always returns to the prop's first value\n // even if the prop reference changes between renders.\n const initialRef = React.useRef(initialState)\n\n const set = React.useCallback(\n (next: T) => {\n if (!isControlled) setInternalState(next)\n onStateChange?.(next)\n },\n [isControlled, onStateChange]\n )\n\n const patch = React.useCallback(\n (partial: Partial<T>) => {\n const next = { ...state, ...partial } as T\n if (!isControlled) setInternalState(next)\n onStateChange?.(next)\n },\n [isControlled, onStateChange, state]\n )\n\n const reset = React.useCallback(() => {\n const next = initialRef.current\n if (!isControlled) setInternalState(next)\n onStateChange?.(next)\n }, [isControlled, onStateChange])\n\n const value = React.useMemo<FilterContextValue<T>>(() => ({ state, set, patch, reset }), [state, set, patch, reset])\n\n return <FilterContext.Provider value={value as FilterContextValue}>{children}</FilterContext.Provider>\n}\n\n/* -------------------------------------------------------------------------- */\n/* Hook */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Read + update the filter state from any descendant of `<FilterProvider>`.\n * Cast `T` at the call site to get a typed view of the state shape.\n *\n * @example\n * interface MyFilters { q: string; severity: 'low' | 'high' | null }\n * const { state, patch } = useFilterState<MyFilters>()\n * patch({ q: 'tehran' })\n */\nexport function useFilterState<T extends FilterStateShape = FilterStateShape>(): FilterContextValue<T> {\n const ctx = React.useContext(FilterContext)\n if (!ctx) {\n throw new Error('useFilterState must be used within a <FilterProvider>')\n }\n return ctx as FilterContextValue<T>\n}\n\n/** Internal — used by `useFilterParams` / `useFilterPresets` to opt-in to the context, returning null when absent. */\nexport function useFilterStateOptional<T extends FilterStateShape = FilterStateShape>(): FilterContextValue<T> | null {\n const ctx = React.useContext(FilterContext)\n return (ctx as FilterContextValue<T>) ?? null\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkNV4JOKWL_cjs = require('./chunk-NV4JOKWL.cjs');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var CheckboxPrimitive = require('@radix-ui/react-checkbox');
|
|
6
|
+
var lucideReact = require('lucide-react');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
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
|
+
}
|
|
26
|
+
|
|
27
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
28
|
+
var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimitive);
|
|
29
|
+
|
|
30
|
+
var Checkbox = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
31
|
+
CheckboxPrimitive__namespace.Root,
|
|
32
|
+
{
|
|
33
|
+
ref,
|
|
34
|
+
"data-slot": "checkbox",
|
|
35
|
+
className: chunkNV4JOKWL_cjs.cn(
|
|
36
|
+
"peer border-control bg-control data-[state=checked]:bg-brand data-[state=checked]:text-foreground-contrast data-[state=checked]:border-brand focus-visible:border-brand-default focus-visible:ring-brand-default/50 aria-invalid:ring-destructive/20 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-all duration-150 outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
37
|
+
className
|
|
38
|
+
),
|
|
39
|
+
...props,
|
|
40
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
41
|
+
CheckboxPrimitive__namespace.Indicator,
|
|
42
|
+
{
|
|
43
|
+
"data-slot": "checkbox-indicator",
|
|
44
|
+
className: "grid place-content-center text-current transition-none",
|
|
45
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" })
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
));
|
|
50
|
+
Checkbox.displayName = "Checkbox";
|
|
51
|
+
|
|
52
|
+
exports.Checkbox = Checkbox;
|
|
53
|
+
//# sourceMappingURL=chunk-CAJKSTXX.cjs.map
|
|
54
|
+
//# sourceMappingURL=chunk-CAJKSTXX.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/checkbox.tsx"],"names":["React","jsx","CheckboxPrimitive","cn","CheckIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,IAAM,QAAA,GAAiBA,4BAGrB,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC1BC,cAAA;AAAA,EAAmBC,4BAAA,CAAA,IAAA;AAAA,EAAlB;AAAA,IACC,GAAA;AAAA,IACA,WAAA,EAAU,UAAA;AAAA,IACV,SAAA,EAAWC,oBAAA;AAAA,MACT,ubAAA;AAAA,MACA;AAAA,KACF;AAAA,IACC,GAAG,KAAA;AAAA,IAEJ,QAAA,kBAAAF,cAAA;AAAA,MAAmBC,4BAAA,CAAA,SAAA;AAAA,MAAlB;AAAA,QACC,WAAA,EAAU,oBAAA;AAAA,QACV,SAAA,EAAU,wDAAA;AAAA,QAEV,QAAA,kBAAAD,cAAA,CAACG,qBAAA,EAAA,EAAU,SAAA,EAAU,UAAA,EAAW;AAAA;AAAA;AAClC;AACF,CACD;AAED,QAAA,CAAS,WAAA,GAAc,UAAA","file":"chunk-CAJKSTXX.cjs","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport * as CheckboxPrimitive from '@radix-ui/react-checkbox'\nimport { CheckIcon } from 'lucide-react'\n\nimport { cn } from '@/lib/utils'\n\nconst Checkbox = React.forwardRef<\n React.ElementRef<typeof CheckboxPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <CheckboxPrimitive.Root\n ref={ref}\n data-slot=\"checkbox\"\n className={cn(\n 'peer border-control bg-control data-[state=checked]:bg-brand data-[state=checked]:text-foreground-contrast data-[state=checked]:border-brand focus-visible:border-brand-default focus-visible:ring-brand-default/50 aria-invalid:ring-destructive/20 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-all duration-150 outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"grid place-content-center text-current transition-none\"\n >\n <CheckIcon className=\"size-3.5\" />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n))\n\nCheckbox.displayName = 'Checkbox'\n\nexport { Checkbox }\n"]}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { StatusBadge } from './chunk-MEK4RSGC.js';
|
|
2
|
+
import { Sparkline } from './chunk-YC5KLN6I.js';
|
|
3
|
+
import { Checkbox } from './chunk-5JJSRGJD.js';
|
|
4
|
+
import { SeverityBadge } from './chunk-KCWRCSI7.js';
|
|
5
|
+
import { flowLabels, FLOW_KEYS } from './chunk-CV3N3HVK.js';
|
|
6
|
+
import { cn, convertToLocalNumbers } from './chunk-4SVQNEVH.js';
|
|
7
|
+
import * as React from 'react';
|
|
8
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
function calcPercents(data) {
|
|
11
|
+
const total = FLOW_KEYS.reduce((s, k) => s + (data[k] ?? 0), 0);
|
|
12
|
+
const result = Object.fromEntries(FLOW_KEYS.map((k) => [k, 0]));
|
|
13
|
+
if (total === 0) return result;
|
|
14
|
+
const floats = FLOW_KEYS.filter((k) => (data[k] ?? 0) > 0).map((k) => {
|
|
15
|
+
const v = (data[k] ?? 0) / total * 100;
|
|
16
|
+
return { k, floor: Math.floor(v), rem: v - Math.floor(v) };
|
|
17
|
+
});
|
|
18
|
+
const remaining = 100 - floats.reduce((s, f) => s + f.floor, 0);
|
|
19
|
+
const sorted = [...floats].sort((a, b) => b.rem - a.rem);
|
|
20
|
+
sorted.slice(0, remaining).forEach((f) => f.floor++);
|
|
21
|
+
floats.forEach(({ k, floor }) => result[k] = floor);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
var STACKED_ORDER = [
|
|
25
|
+
"pro-gov",
|
|
26
|
+
"internal-critic",
|
|
27
|
+
"internal-opponent",
|
|
28
|
+
"external-opponent",
|
|
29
|
+
"grey"
|
|
30
|
+
];
|
|
31
|
+
function tokenVar(key) {
|
|
32
|
+
return `--flow-${key}`;
|
|
33
|
+
}
|
|
34
|
+
var FlowDistribution = React.forwardRef(
|
|
35
|
+
({
|
|
36
|
+
className,
|
|
37
|
+
data,
|
|
38
|
+
variant = "stacked",
|
|
39
|
+
showCounts = false,
|
|
40
|
+
showPercent = true,
|
|
41
|
+
locale = "fa",
|
|
42
|
+
isLoading = false,
|
|
43
|
+
...props
|
|
44
|
+
}, ref) => {
|
|
45
|
+
const labels = flowLabels[locale] ?? flowLabels.fa;
|
|
46
|
+
const percents = calcPercents(data);
|
|
47
|
+
const percentSign = locale === "en" ? "%" : "\u066A";
|
|
48
|
+
if (isLoading) {
|
|
49
|
+
return /* @__PURE__ */ jsx("div", { ref, "data-slot": "flow-distribution", className: cn("space-y-2", className), ...props, children: variant === "stacked" ? /* @__PURE__ */ jsx("div", { className: "h-3 w-full rounded-full bg-muted animate-pulse" }) : variant === "compact" ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { className: "h-5 w-20 rounded-full bg-muted animate-pulse" }, i)) }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
50
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-16 rounded bg-muted animate-pulse" }),
|
|
51
|
+
/* @__PURE__ */ jsx("div", { className: "h-2 flex-1 rounded-full bg-muted animate-pulse" }),
|
|
52
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-8 rounded bg-muted animate-pulse" })
|
|
53
|
+
] }, i)) }) });
|
|
54
|
+
}
|
|
55
|
+
if (variant === "stacked") {
|
|
56
|
+
const active = STACKED_ORDER.filter((k) => (data[k] ?? 0) > 0);
|
|
57
|
+
const srLabel = active.map((k) => `${labels[k]} ${convertToLocalNumbers(percents[k], locale)}${percentSign}`).join("\u060C ");
|
|
58
|
+
return /* @__PURE__ */ jsxs("div", { ref, "data-slot": "flow-distribution", className: cn("space-y-2", className), ...props, children: [
|
|
59
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-3 w-full overflow-hidden rounded-full bg-muted", role: "img", "aria-label": srLabel, children: active.map((k) => /* @__PURE__ */ jsx(
|
|
60
|
+
"div",
|
|
61
|
+
{
|
|
62
|
+
className: "transition-all duration-500",
|
|
63
|
+
style: { width: `${percents[k]}%`, backgroundColor: `hsl(var(${tokenVar(k)}))` }
|
|
64
|
+
},
|
|
65
|
+
k
|
|
66
|
+
)) }),
|
|
67
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-x-3 gap-y-1", children: active.map((k) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
68
|
+
/* @__PURE__ */ jsx(
|
|
69
|
+
"span",
|
|
70
|
+
{
|
|
71
|
+
className: "inline-block h-2 w-2 rounded-full",
|
|
72
|
+
style: { backgroundColor: `hsl(var(${tokenVar(k)}))` }
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-foreground-light", children: labels[k] }),
|
|
76
|
+
showPercent && /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium tabular-nums", style: { color: `hsl(var(${tokenVar(k)}))` }, children: [
|
|
77
|
+
convertToLocalNumbers(percents[k], locale),
|
|
78
|
+
percentSign
|
|
79
|
+
] }),
|
|
80
|
+
showCounts && /* @__PURE__ */ jsxs("span", { className: "text-xs text-foreground-muted tabular-nums", children: [
|
|
81
|
+
"(",
|
|
82
|
+
convertToLocalNumbers(data[k] ?? 0, locale),
|
|
83
|
+
")"
|
|
84
|
+
] })
|
|
85
|
+
] }, k)) })
|
|
86
|
+
] });
|
|
87
|
+
}
|
|
88
|
+
if (variant === "compact") {
|
|
89
|
+
const active = FLOW_KEYS.filter((k) => (data[k] ?? 0) > 0).sort((a, b) => (data[b] ?? 0) - (data[a] ?? 0));
|
|
90
|
+
return /* @__PURE__ */ jsx(
|
|
91
|
+
"div",
|
|
92
|
+
{
|
|
93
|
+
ref,
|
|
94
|
+
"data-slot": "flow-distribution",
|
|
95
|
+
className: cn("inline-flex flex-wrap gap-2", className),
|
|
96
|
+
...props,
|
|
97
|
+
children: active.map((k) => /* @__PURE__ */ jsxs(
|
|
98
|
+
"span",
|
|
99
|
+
{
|
|
100
|
+
className: "inline-flex items-center gap-1 rounded-full px-2 py-0.5",
|
|
101
|
+
style: {
|
|
102
|
+
backgroundColor: `hsl(var(${tokenVar(k)}) / 0.12)`,
|
|
103
|
+
color: `hsl(var(${tokenVar(k)}))`
|
|
104
|
+
},
|
|
105
|
+
children: [
|
|
106
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: labels[k] }),
|
|
107
|
+
showPercent && /* @__PURE__ */ jsxs("span", { className: "text-xs font-bold tabular-nums", children: [
|
|
108
|
+
convertToLocalNumbers(percents[k], locale),
|
|
109
|
+
percentSign
|
|
110
|
+
] }),
|
|
111
|
+
showCounts && /* @__PURE__ */ jsxs("span", { className: "text-xs text-foreground-muted tabular-nums", children: [
|
|
112
|
+
"(",
|
|
113
|
+
convertToLocalNumbers(data[k] ?? 0, locale),
|
|
114
|
+
")"
|
|
115
|
+
] })
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
k
|
|
119
|
+
))
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
const sorted = FLOW_KEYS.filter((k) => (data[k] ?? 0) > 0).sort((a, b) => (data[b] ?? 0) - (data[a] ?? 0));
|
|
124
|
+
return /* @__PURE__ */ jsx("div", { ref, "data-slot": "flow-distribution", className: cn("space-y-1.5", className), ...props, children: sorted.map((k) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
125
|
+
/* @__PURE__ */ jsx("span", { className: "w-24 shrink-0 text-xs text-foreground-light text-end", children: labels[k] }),
|
|
126
|
+
/* @__PURE__ */ jsx("div", { className: "relative flex-1 h-2 rounded-full bg-muted overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
127
|
+
"div",
|
|
128
|
+
{
|
|
129
|
+
className: "absolute inset-y-0 start-0 rounded-full transition-all duration-500",
|
|
130
|
+
style: {
|
|
131
|
+
width: `${percents[k]}%`,
|
|
132
|
+
backgroundColor: `hsl(var(${tokenVar(k)}))`
|
|
133
|
+
},
|
|
134
|
+
role: "progressbar",
|
|
135
|
+
"aria-valuenow": percents[k],
|
|
136
|
+
"aria-valuemin": 0,
|
|
137
|
+
"aria-valuemax": 100,
|
|
138
|
+
"aria-label": `${labels[k]}: ${percents[k]}%`
|
|
139
|
+
}
|
|
140
|
+
) }),
|
|
141
|
+
/* @__PURE__ */ jsx(
|
|
142
|
+
"span",
|
|
143
|
+
{
|
|
144
|
+
className: "w-12 shrink-0 text-xs font-medium tabular-nums text-start",
|
|
145
|
+
style: { color: `hsl(var(${tokenVar(k)}))` },
|
|
146
|
+
children: showCounts ? convertToLocalNumbers((data[k] ?? 0).toLocaleString(), locale) : `${convertToLocalNumbers(percents[k], locale)}${percentSign}`
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
] }, k)) });
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
FlowDistribution.displayName = "FlowDistribution";
|
|
153
|
+
var LABELS = {
|
|
154
|
+
fa: { positive: "\u0645\u062B\u0628\u062A", negative: "\u0645\u0646\u0641\u06CC", neutral: "\u062E\u0646\u062B\u06CC", mixed: "\u062A\u0631\u06A9\u06CC\u0628\u06CC" },
|
|
155
|
+
ar: { positive: "\u0625\u064A\u062C\u0627\u0628\u064A", negative: "\u0633\u0644\u0628\u064A", neutral: "\u0645\u062D\u0627\u064A\u062F", mixed: "\u0645\u062E\u062A\u0644\u0637" },
|
|
156
|
+
en: { positive: "Positive", negative: "Negative", neutral: "Neutral", mixed: "Mixed" }
|
|
157
|
+
};
|
|
158
|
+
var SENTIMENT_STYLES = {
|
|
159
|
+
positive: {
|
|
160
|
+
bar: "bg-[hsl(var(--sentiment-positive))]",
|
|
161
|
+
text: "text-[hsl(var(--sentiment-positive))]",
|
|
162
|
+
bg: "bg-[hsl(var(--sentiment-positive)/0.12)]"
|
|
163
|
+
},
|
|
164
|
+
negative: {
|
|
165
|
+
bar: "bg-[hsl(var(--sentiment-negative))]",
|
|
166
|
+
text: "text-[hsl(var(--sentiment-negative))]",
|
|
167
|
+
bg: "bg-[hsl(var(--sentiment-negative)/0.12)]"
|
|
168
|
+
},
|
|
169
|
+
neutral: {
|
|
170
|
+
bar: "bg-[hsl(var(--sentiment-neutral))]",
|
|
171
|
+
text: "text-[hsl(var(--sentiment-neutral))]",
|
|
172
|
+
bg: "bg-[hsl(var(--sentiment-neutral)/0.12)]"
|
|
173
|
+
},
|
|
174
|
+
mixed: {
|
|
175
|
+
bar: "bg-[hsl(var(--sentiment-mixed))]",
|
|
176
|
+
text: "text-[hsl(var(--sentiment-mixed))]",
|
|
177
|
+
bg: "bg-[hsl(var(--sentiment-mixed)/0.12)]"
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
function calcPercents2(data) {
|
|
181
|
+
const keys = ["positive", "negative", "neutral", "mixed"].filter((k) => (data[k] ?? 0) > 0);
|
|
182
|
+
const total = ["positive", "negative", "neutral", "mixed"].reduce((sum, k) => sum + (data[k] ?? 0), 0);
|
|
183
|
+
if (total === 0) return { positive: 0, negative: 0, neutral: 0, mixed: 0 };
|
|
184
|
+
const floats = keys.map((k) => ({ k, v: (data[k] ?? 0) / total * 100 }));
|
|
185
|
+
const floors = floats.map(({ k, v }) => ({ k, floor: Math.floor(v), rem: v - Math.floor(v) }));
|
|
186
|
+
const remaining = 100 - floors.reduce((s, f) => s + f.floor, 0);
|
|
187
|
+
const sorted = [...floors].sort((a, b) => b.rem - a.rem);
|
|
188
|
+
sorted.slice(0, remaining).forEach((f) => f.floor++);
|
|
189
|
+
const result = { positive: 0, negative: 0, neutral: 0, mixed: 0 };
|
|
190
|
+
sorted.forEach(({ k, floor }) => result[k] = floor);
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
var SENTIMENT_ORDER = ["positive", "neutral", "mixed", "negative"];
|
|
194
|
+
var SentimentDistribution = React.forwardRef(
|
|
195
|
+
({
|
|
196
|
+
className,
|
|
197
|
+
data,
|
|
198
|
+
variant = "bars",
|
|
199
|
+
showCounts = false,
|
|
200
|
+
showPercent = true,
|
|
201
|
+
locale = "fa",
|
|
202
|
+
isLoading = false,
|
|
203
|
+
...props
|
|
204
|
+
}, ref) => {
|
|
205
|
+
const labels = LABELS[locale] ?? LABELS.fa;
|
|
206
|
+
const percents = calcPercents2(data);
|
|
207
|
+
const activeOrder = SENTIMENT_ORDER.filter((key) => (data[key] ?? 0) > 0);
|
|
208
|
+
if (isLoading) {
|
|
209
|
+
return /* @__PURE__ */ jsx("div", { ref, "data-slot": "sentiment-distribution", className: cn("space-y-2", className), ...props, children: variant === "stacked" ? /* @__PURE__ */ jsx("div", { className: "h-3 w-full rounded-full bg-muted animate-pulse" }) : variant === "compact" ? /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { className: "h-4 w-16 rounded bg-muted animate-pulse" }, i)) }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
210
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-12 rounded bg-muted animate-pulse" }),
|
|
211
|
+
/* @__PURE__ */ jsx("div", { className: "h-2 flex-1 rounded-full bg-muted animate-pulse" }),
|
|
212
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-8 rounded bg-muted animate-pulse" })
|
|
213
|
+
] }, i)) }) });
|
|
214
|
+
}
|
|
215
|
+
if (variant === "stacked") {
|
|
216
|
+
return /* @__PURE__ */ jsxs("div", { ref, "data-slot": "sentiment-distribution", className: cn("space-y-2", className), ...props, children: [
|
|
217
|
+
/* @__PURE__ */ jsx(
|
|
218
|
+
"div",
|
|
219
|
+
{
|
|
220
|
+
className: "flex h-3 w-full overflow-hidden rounded-full bg-muted",
|
|
221
|
+
role: "img",
|
|
222
|
+
"aria-label": `${labels.positive} ${percents.positive}\u066A\u060C ${labels.negative} ${percents.negative}\u066A\u060C ${labels.neutral} ${percents.neutral}\u066A`,
|
|
223
|
+
children: activeOrder.map((key) => /* @__PURE__ */ jsx(
|
|
224
|
+
"div",
|
|
225
|
+
{
|
|
226
|
+
className: cn("transition-all duration-500", SENTIMENT_STYLES[key].bar),
|
|
227
|
+
style: { width: `${percents[key]}%` }
|
|
228
|
+
},
|
|
229
|
+
key
|
|
230
|
+
))
|
|
231
|
+
}
|
|
232
|
+
),
|
|
233
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-x-3 gap-y-1", children: activeOrder.map((key) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
234
|
+
/* @__PURE__ */ jsx("span", { className: cn("inline-block h-2 w-2 rounded-full", SENTIMENT_STYLES[key].bar) }),
|
|
235
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-foreground-light", children: labels[key] }),
|
|
236
|
+
showPercent && /* @__PURE__ */ jsxs("span", { className: cn("text-xs font-medium", SENTIMENT_STYLES[key].text), children: [
|
|
237
|
+
convertToLocalNumbers(percents[key], locale),
|
|
238
|
+
locale === "en" ? "%" : "\u066A"
|
|
239
|
+
] }),
|
|
240
|
+
showCounts && /* @__PURE__ */ jsxs("span", { className: "text-xs text-foreground-muted", children: [
|
|
241
|
+
"(",
|
|
242
|
+
convertToLocalNumbers(data[key] ?? 0, locale),
|
|
243
|
+
")"
|
|
244
|
+
] })
|
|
245
|
+
] }, key)) })
|
|
246
|
+
] });
|
|
247
|
+
}
|
|
248
|
+
if (variant === "compact") {
|
|
249
|
+
return /* @__PURE__ */ jsx(
|
|
250
|
+
"div",
|
|
251
|
+
{
|
|
252
|
+
ref,
|
|
253
|
+
"data-slot": "sentiment-distribution",
|
|
254
|
+
className: cn("inline-flex flex-wrap gap-2", className),
|
|
255
|
+
...props,
|
|
256
|
+
children: activeOrder.map((key) => /* @__PURE__ */ jsxs(
|
|
257
|
+
"span",
|
|
258
|
+
{
|
|
259
|
+
className: cn("inline-flex items-center gap-1 rounded-full px-2 py-0.5", SENTIMENT_STYLES[key].bg),
|
|
260
|
+
children: [
|
|
261
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-xs font-medium", SENTIMENT_STYLES[key].text), children: labels[key] }),
|
|
262
|
+
showPercent && /* @__PURE__ */ jsxs("span", { className: cn("text-xs font-bold tabular-nums", SENTIMENT_STYLES[key].text), children: [
|
|
263
|
+
convertToLocalNumbers(percents[key], locale),
|
|
264
|
+
locale === "en" ? "%" : "\u066A"
|
|
265
|
+
] }),
|
|
266
|
+
showCounts && /* @__PURE__ */ jsxs("span", { className: "text-xs text-foreground-muted tabular-nums", children: [
|
|
267
|
+
"(",
|
|
268
|
+
convertToLocalNumbers((data[key] ?? 0).toLocaleString(), locale),
|
|
269
|
+
")"
|
|
270
|
+
] })
|
|
271
|
+
]
|
|
272
|
+
},
|
|
273
|
+
key
|
|
274
|
+
))
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return /* @__PURE__ */ jsx("div", { ref, "data-slot": "sentiment-distribution", className: cn("space-y-1.5", className), ...props, children: activeOrder.map((key) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
279
|
+
/* @__PURE__ */ jsx("span", { className: "w-10 shrink-0 text-xs text-foreground-light text-end", children: labels[key] }),
|
|
280
|
+
/* @__PURE__ */ jsx("div", { className: "relative flex-1 h-2 rounded-full bg-muted overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
281
|
+
"div",
|
|
282
|
+
{
|
|
283
|
+
className: cn(
|
|
284
|
+
"absolute inset-y-0 start-0 rounded-full transition-all duration-500",
|
|
285
|
+
SENTIMENT_STYLES[key].bar
|
|
286
|
+
),
|
|
287
|
+
style: { width: `${percents[key]}%` },
|
|
288
|
+
role: "progressbar",
|
|
289
|
+
"aria-valuenow": percents[key],
|
|
290
|
+
"aria-valuemin": 0,
|
|
291
|
+
"aria-valuemax": 100,
|
|
292
|
+
"aria-label": `${labels[key]}: ${percents[key]}%`
|
|
293
|
+
}
|
|
294
|
+
) }),
|
|
295
|
+
/* @__PURE__ */ jsx(
|
|
296
|
+
"span",
|
|
297
|
+
{
|
|
298
|
+
className: cn("w-10 shrink-0 text-xs font-medium tabular-nums text-start", SENTIMENT_STYLES[key].text),
|
|
299
|
+
children: showCounts ? convertToLocalNumbers((data[key] ?? 0).toLocaleString(), locale) : `${convertToLocalNumbers(percents[key], locale)}${locale === "en" ? "%" : "\u066A"}`
|
|
300
|
+
}
|
|
301
|
+
)
|
|
302
|
+
] }, key)) });
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
SentimentDistribution.displayName = "SentimentDistribution";
|
|
306
|
+
var ConceptCard = React.forwardRef(
|
|
307
|
+
({
|
|
308
|
+
className,
|
|
309
|
+
title,
|
|
310
|
+
subtitle,
|
|
311
|
+
status,
|
|
312
|
+
severity,
|
|
313
|
+
flow,
|
|
314
|
+
sentiment,
|
|
315
|
+
trend,
|
|
316
|
+
aiSummary,
|
|
317
|
+
totalCount,
|
|
318
|
+
comparison,
|
|
319
|
+
interactive = false,
|
|
320
|
+
locale = "fa",
|
|
321
|
+
onClick,
|
|
322
|
+
...props
|
|
323
|
+
}, ref) => {
|
|
324
|
+
const isClickable = interactive || !!onClick;
|
|
325
|
+
const handleClick = isClickable ? (e) => {
|
|
326
|
+
const target = e.target;
|
|
327
|
+
if (target && target.closest('[data-slot="concept-card-comparison"]')) return;
|
|
328
|
+
onClick?.(e);
|
|
329
|
+
} : void 0;
|
|
330
|
+
return /* @__PURE__ */ jsxs(
|
|
331
|
+
"div",
|
|
332
|
+
{
|
|
333
|
+
ref,
|
|
334
|
+
"data-slot": "concept-card",
|
|
335
|
+
"data-status": status,
|
|
336
|
+
"data-severity": severity,
|
|
337
|
+
role: isClickable ? "button" : "article",
|
|
338
|
+
tabIndex: isClickable ? 0 : void 0,
|
|
339
|
+
onClick: handleClick,
|
|
340
|
+
onKeyDown: isClickable ? (e) => {
|
|
341
|
+
if ((e.key === "Enter" || e.key === " ") && e.target === e.currentTarget) {
|
|
342
|
+
e.preventDefault();
|
|
343
|
+
e.currentTarget.click();
|
|
344
|
+
}
|
|
345
|
+
} : void 0,
|
|
346
|
+
className: cn(
|
|
347
|
+
"group/concept-card flex flex-col gap-3 rounded-lg border border-border bg-background p-4 transition-colors",
|
|
348
|
+
isClickable && "cursor-pointer hover:border-strong hover:bg-surface-100/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
349
|
+
className
|
|
350
|
+
),
|
|
351
|
+
...props,
|
|
352
|
+
children: [
|
|
353
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
354
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
355
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
356
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground truncate", children: title }),
|
|
357
|
+
status && /* @__PURE__ */ jsx(StatusBadge, { status, size: "sm", animated: true, locale }),
|
|
358
|
+
severity && /* @__PURE__ */ jsx(SeverityBadge, { severity, size: "sm", locale })
|
|
359
|
+
] }),
|
|
360
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-foreground-lighter truncate", children: subtitle })
|
|
361
|
+
] }),
|
|
362
|
+
comparison && /* @__PURE__ */ jsx("div", { "data-slot": "concept-card-comparison", className: "shrink-0", children: /* @__PURE__ */ jsx(
|
|
363
|
+
Checkbox,
|
|
364
|
+
{
|
|
365
|
+
checked: comparison.selected,
|
|
366
|
+
onCheckedChange: (next) => comparison.onSelectionChange(next === true),
|
|
367
|
+
"aria-label": typeof comparison.label === "string" ? comparison.label : locale === "fa" ? "\u0627\u0646\u062A\u062E\u0627\u0628 \u0628\u0631\u0627\u06CC \u0645\u0642\u0627\u06CC\u0633\u0647" : locale === "ar" ? "\u062A\u062D\u062F\u064A\u062F \u0644\u0644\u0645\u0642\u0627\u0631\u0646\u0629" : "Select for comparison"
|
|
368
|
+
}
|
|
369
|
+
) })
|
|
370
|
+
] }),
|
|
371
|
+
aiSummary && /* @__PURE__ */ jsx("p", { className: "text-sm text-foreground leading-relaxed", children: aiSummary }),
|
|
372
|
+
(flow || sentiment) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
373
|
+
flow && /* @__PURE__ */ jsx("div", { "data-slot": "concept-card-flow", children: /* @__PURE__ */ jsx(FlowDistribution, { data: flow, variant: "stacked", showCounts: false, showPercent: true, locale }) }),
|
|
374
|
+
sentiment && /* @__PURE__ */ jsx("div", { "data-slot": "concept-card-sentiment", children: /* @__PURE__ */ jsx(
|
|
375
|
+
SentimentDistribution,
|
|
376
|
+
{
|
|
377
|
+
data: sentiment,
|
|
378
|
+
variant: "stacked",
|
|
379
|
+
showCounts: false,
|
|
380
|
+
showPercent: true,
|
|
381
|
+
locale
|
|
382
|
+
}
|
|
383
|
+
) })
|
|
384
|
+
] }),
|
|
385
|
+
(trend || typeof totalCount === "number") && /* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-3 pt-1 border-t border-border mt-auto", children: [
|
|
386
|
+
trend && trend.length > 0 ? /* @__PURE__ */ jsx("div", { "data-slot": "concept-card-trend", children: /* @__PURE__ */ jsx(Sparkline, { data: trend, variant: "area", width: 120, height: 28 }) }) : /* @__PURE__ */ jsx("span", {}),
|
|
387
|
+
typeof totalCount === "number" && /* @__PURE__ */ jsxs("div", { className: "text-end", children: [
|
|
388
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-foreground-lighter", children: locale === "fa" ? "\u0645\u062C\u0645\u0648\u0639" : locale === "ar" ? "\u0627\u0644\u0625\u062C\u0645\u0627\u0644\u064A" : "Total" }),
|
|
389
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold tabular-nums text-foreground", children: convertToLocalNumbers(totalCount, locale) })
|
|
390
|
+
] })
|
|
391
|
+
] })
|
|
392
|
+
]
|
|
393
|
+
}
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
ConceptCard.displayName = "ConceptCard";
|
|
398
|
+
|
|
399
|
+
export { ConceptCard, FlowDistribution, SentimentDistribution };
|
|
400
|
+
//# sourceMappingURL=chunk-CKFWMHQU.js.map
|
|
401
|
+
//# sourceMappingURL=chunk-CKFWMHQU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/flow-distribution.tsx","../src/components/ui/sentiment-distribution.tsx","../src/components/ui/concept-card.tsx"],"names":["calcPercents","React2","jsx","jsxs","React3"],"mappings":"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","file":"chunk-CKFWMHQU.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { cn, convertToLocalNumbers, type SupportedLocale } from '@/lib/utils'\nimport { flowLabels, FLOW_KEYS, type FlowKey } from '@/lib/i18n'\n\nexport type FlowData = Partial<Record<FlowKey, number>>\n\nexport interface FlowDistributionProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Flow counts (auto-normalized). Missing keys treated as 0. */\n data: FlowData\n /** Display mode */\n variant?: 'bars' | 'stacked' | 'compact'\n /** Show numeric counts alongside percent */\n showCounts?: boolean\n /** Show percentages */\n showPercent?: boolean\n /** Locale */\n locale?: SupportedLocale\n /** Loading skeleton */\n isLoading?: boolean\n}\n\nfunction calcPercents(data: FlowData): Record<FlowKey, number> {\n const total = FLOW_KEYS.reduce((s, k) => s + (data[k] ?? 0), 0)\n const result = Object.fromEntries(FLOW_KEYS.map((k) => [k, 0])) as Record<FlowKey, number>\n if (total === 0) return result\n\n const floats = FLOW_KEYS.filter((k) => (data[k] ?? 0) > 0).map((k) => {\n const v = ((data[k] ?? 0) / total) * 100\n return { k, floor: Math.floor(v), rem: v - Math.floor(v) }\n })\n const remaining = 100 - floats.reduce((s, f) => s + f.floor, 0)\n const sorted = [...floats].sort((a, b) => b.rem - a.rem)\n sorted.slice(0, remaining).forEach((f) => f.floor++)\n floats.forEach(({ k, floor }) => (result[k] = floor))\n return result\n}\n\n/** Visual reading order for stacked bar — aligned → opposed → undefined. */\nconst STACKED_ORDER: readonly FlowKey[] = [\n 'pro-gov',\n 'internal-critic',\n 'internal-opponent',\n 'external-opponent',\n 'grey',\n] as const\n\nfunction tokenVar(key: FlowKey): string {\n return `--flow-${key}`\n}\n\nconst FlowDistribution = React.forwardRef<HTMLDivElement, FlowDistributionProps>(\n (\n {\n className,\n data,\n variant = 'stacked',\n showCounts = false,\n showPercent = true,\n locale = 'fa',\n isLoading = false,\n ...props\n },\n ref\n ) => {\n const labels = flowLabels[locale] ?? flowLabels.fa\n const percents = calcPercents(data)\n const percentSign = locale === 'en' ? '%' : '٪'\n\n if (isLoading) {\n return (\n <div ref={ref} data-slot=\"flow-distribution\" className={cn('space-y-2', className)} {...props}>\n {variant === 'stacked' ? (\n <div className=\"h-3 w-full rounded-full bg-muted animate-pulse\" />\n ) : variant === 'compact' ? (\n <div className=\"flex flex-wrap gap-2\">\n {[0, 1, 2].map((i) => (\n <div key={i} className=\"h-5 w-20 rounded-full bg-muted animate-pulse\" />\n ))}\n </div>\n ) : (\n <div className=\"space-y-2\">\n {[0, 1, 2, 3].map((i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <div className=\"h-3 w-16 rounded bg-muted animate-pulse\" />\n <div className=\"h-2 flex-1 rounded-full bg-muted animate-pulse\" />\n <div className=\"h-3 w-8 rounded bg-muted animate-pulse\" />\n </div>\n ))}\n </div>\n )}\n </div>\n )\n }\n\n if (variant === 'stacked') {\n const active = STACKED_ORDER.filter((k) => (data[k] ?? 0) > 0)\n const srLabel = active\n .map((k) => `${labels[k]} ${convertToLocalNumbers(percents[k], locale)}${percentSign}`)\n .join('، ')\n\n return (\n <div ref={ref} data-slot=\"flow-distribution\" className={cn('space-y-2', className)} {...props}>\n <div className=\"flex h-3 w-full overflow-hidden rounded-full bg-muted\" role=\"img\" aria-label={srLabel}>\n {active.map((k) => (\n <div\n key={k}\n className=\"transition-all duration-500\"\n style={{ width: `${percents[k]}%`, backgroundColor: `hsl(var(${tokenVar(k)}))` }}\n />\n ))}\n </div>\n <div className=\"flex flex-wrap gap-x-3 gap-y-1\">\n {active.map((k) => (\n <span key={k} className=\"inline-flex items-center gap-1\">\n <span\n className=\"inline-block h-2 w-2 rounded-full\"\n style={{ backgroundColor: `hsl(var(${tokenVar(k)}))` }}\n />\n <span className=\"text-xs text-foreground-light\">{labels[k]}</span>\n {showPercent && (\n <span className=\"text-xs font-medium tabular-nums\" style={{ color: `hsl(var(${tokenVar(k)}))` }}>\n {convertToLocalNumbers(percents[k], locale)}\n {percentSign}\n </span>\n )}\n {showCounts && (\n <span className=\"text-xs text-foreground-muted tabular-nums\">\n ({convertToLocalNumbers(data[k] ?? 0, locale)})\n </span>\n )}\n </span>\n ))}\n </div>\n </div>\n )\n }\n\n if (variant === 'compact') {\n const active = FLOW_KEYS.filter((k) => (data[k] ?? 0) > 0).sort((a, b) => (data[b] ?? 0) - (data[a] ?? 0))\n return (\n <div\n ref={ref}\n data-slot=\"flow-distribution\"\n className={cn('inline-flex flex-wrap gap-2', className)}\n {...props}\n >\n {active.map((k) => (\n <span\n key={k}\n className=\"inline-flex items-center gap-1 rounded-full px-2 py-0.5\"\n style={{\n backgroundColor: `hsl(var(${tokenVar(k)}) / 0.12)`,\n color: `hsl(var(${tokenVar(k)}))`,\n }}\n >\n <span className=\"text-xs font-medium\">{labels[k]}</span>\n {showPercent && (\n <span className=\"text-xs font-bold tabular-nums\">\n {convertToLocalNumbers(percents[k], locale)}\n {percentSign}\n </span>\n )}\n {showCounts && (\n <span className=\"text-xs text-foreground-muted tabular-nums\">\n ({convertToLocalNumbers(data[k] ?? 0, locale)})\n </span>\n )}\n </span>\n ))}\n </div>\n )\n }\n\n // bars — row per flow, sorted by value\n const sorted = FLOW_KEYS.filter((k) => (data[k] ?? 0) > 0).sort((a, b) => (data[b] ?? 0) - (data[a] ?? 0))\n\n return (\n <div ref={ref} data-slot=\"flow-distribution\" className={cn('space-y-1.5', className)} {...props}>\n {sorted.map((k) => (\n <div key={k} className=\"flex items-center gap-2\">\n <span className=\"w-24 shrink-0 text-xs text-foreground-light text-end\">{labels[k]}</span>\n <div className=\"relative flex-1 h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"absolute inset-y-0 start-0 rounded-full transition-all duration-500\"\n style={{\n width: `${percents[k]}%`,\n backgroundColor: `hsl(var(${tokenVar(k)}))`,\n }}\n role=\"progressbar\"\n aria-valuenow={percents[k]}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={`${labels[k]}: ${percents[k]}%`}\n />\n </div>\n <span\n className=\"w-12 shrink-0 text-xs font-medium tabular-nums text-start\"\n style={{ color: `hsl(var(${tokenVar(k)}))` }}\n >\n {showCounts\n ? convertToLocalNumbers((data[k] ?? 0).toLocaleString(), locale)\n : `${convertToLocalNumbers(percents[k], locale)}${percentSign}`}\n </span>\n </div>\n ))}\n </div>\n )\n }\n)\n\nFlowDistribution.displayName = 'FlowDistribution'\n\nexport { FlowDistribution }\n","'use client'\n\nimport * as React from 'react'\nimport { cn, convertToLocalNumbers, type SupportedLocale } from '@/lib/utils'\n\nexport interface SentimentData {\n positive: number\n negative: number\n neutral: number\n mixed?: number\n}\n\nexport interface SentimentDistributionProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Sentiment counts (will be auto-normalized to 100%) */\n data: SentimentData\n /** Display mode */\n variant?: 'bars' | 'stacked' | 'compact'\n /** Show numeric count labels */\n showCounts?: boolean\n /** Show percentage labels */\n showPercent?: boolean\n /** Locale for number/label formatting */\n locale?: SupportedLocale\n /** Show loading skeleton */\n isLoading?: boolean\n}\n\nconst LABELS: Record<SupportedLocale, Record<keyof SentimentData, string>> = {\n fa: { positive: 'مثبت', negative: 'منفی', neutral: 'خنثی', mixed: 'ترکیبی' },\n ar: { positive: 'إيجابي', negative: 'سلبي', neutral: 'محايد', mixed: 'مختلط' },\n en: { positive: 'Positive', negative: 'Negative', neutral: 'Neutral', mixed: 'Mixed' },\n}\n\nconst SENTIMENT_STYLES: Record<keyof SentimentData, { bar: string; text: string; bg: string }> = {\n positive: {\n bar: 'bg-[hsl(var(--sentiment-positive))]',\n text: 'text-[hsl(var(--sentiment-positive))]',\n bg: 'bg-[hsl(var(--sentiment-positive)/0.12)]',\n },\n negative: {\n bar: 'bg-[hsl(var(--sentiment-negative))]',\n text: 'text-[hsl(var(--sentiment-negative))]',\n bg: 'bg-[hsl(var(--sentiment-negative)/0.12)]',\n },\n neutral: {\n bar: 'bg-[hsl(var(--sentiment-neutral))]',\n text: 'text-[hsl(var(--sentiment-neutral))]',\n bg: 'bg-[hsl(var(--sentiment-neutral)/0.12)]',\n },\n mixed: {\n bar: 'bg-[hsl(var(--sentiment-mixed))]',\n text: 'text-[hsl(var(--sentiment-mixed))]',\n bg: 'bg-[hsl(var(--sentiment-mixed)/0.12)]',\n },\n}\n\nfunction calcPercents(data: SentimentData): Record<keyof SentimentData, number> {\n const keys = (['positive', 'negative', 'neutral', 'mixed'] as const).filter((k) => (data[k] ?? 0) > 0)\n const total = (['positive', 'negative', 'neutral', 'mixed'] as const).reduce((sum, k) => sum + (data[k] ?? 0), 0)\n if (total === 0) return { positive: 0, negative: 0, neutral: 0, mixed: 0 }\n\n // Largest remainder method — guarantees percentages sum to exactly 100\n const floats = keys.map((k) => ({ k, v: ((data[k] ?? 0) / total) * 100 }))\n const floors = floats.map(({ k, v }) => ({ k, floor: Math.floor(v), rem: v - Math.floor(v) }))\n const remaining = 100 - floors.reduce((s, f) => s + f.floor, 0)\n const sorted = [...floors].sort((a, b) => b.rem - a.rem)\n sorted.slice(0, remaining).forEach((f) => f.floor++)\n\n const result: Record<string, number> = { positive: 0, negative: 0, neutral: 0, mixed: 0 }\n sorted.forEach(({ k, floor }) => (result[k] = floor))\n return result as Record<keyof SentimentData, number>\n}\n\nconst SENTIMENT_ORDER: Array<keyof SentimentData> = ['positive', 'neutral', 'mixed', 'negative']\n\nconst SentimentDistribution = React.forwardRef<HTMLDivElement, SentimentDistributionProps>(\n (\n {\n className,\n data,\n variant = 'bars',\n showCounts = false,\n showPercent = true,\n locale = 'fa',\n isLoading = false,\n ...props\n },\n ref\n ) => {\n const labels = LABELS[locale] ?? LABELS.fa\n const percents = calcPercents(data)\n const activeOrder = SENTIMENT_ORDER.filter((key) => (data[key] ?? 0) > 0)\n\n if (isLoading) {\n return (\n <div ref={ref} data-slot=\"sentiment-distribution\" className={cn('space-y-2', className)} {...props}>\n {variant === 'stacked' ? (\n <div className=\"h-3 w-full rounded-full bg-muted animate-pulse\" />\n ) : variant === 'compact' ? (\n <div className=\"flex gap-2\">\n {[0, 1, 2].map((i) => (\n <div key={i} className=\"h-4 w-16 rounded bg-muted animate-pulse\" />\n ))}\n </div>\n ) : (\n <div className=\"space-y-2\">\n {[0, 1, 2].map((i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <div className=\"h-3 w-12 rounded bg-muted animate-pulse\" />\n <div className=\"h-2 flex-1 rounded-full bg-muted animate-pulse\" />\n <div className=\"h-3 w-8 rounded bg-muted animate-pulse\" />\n </div>\n ))}\n </div>\n )}\n </div>\n )\n }\n\n // Stacked bar variant — a single horizontal bar split by sentiment\n if (variant === 'stacked') {\n return (\n <div ref={ref} data-slot=\"sentiment-distribution\" className={cn('space-y-2', className)} {...props}>\n {/* Stacked bar */}\n <div\n className=\"flex h-3 w-full overflow-hidden rounded-full bg-muted\"\n role=\"img\"\n aria-label={`${labels.positive} ${percents.positive}٪، ${labels.negative} ${percents.negative}٪، ${labels.neutral} ${percents.neutral}٪`}\n >\n {activeOrder.map((key) => (\n <div\n key={key}\n className={cn('transition-all duration-500', SENTIMENT_STYLES[key].bar)}\n style={{ width: `${percents[key]}%` }}\n />\n ))}\n </div>\n\n {/* Legend */}\n <div className=\"flex flex-wrap gap-x-3 gap-y-1\">\n {activeOrder.map((key) => (\n <span key={key} className=\"inline-flex items-center gap-1\">\n <span className={cn('inline-block h-2 w-2 rounded-full', SENTIMENT_STYLES[key].bar)} />\n <span className=\"text-xs text-foreground-light\">{labels[key]}</span>\n {showPercent && (\n <span className={cn('text-xs font-medium', SENTIMENT_STYLES[key].text)}>\n {convertToLocalNumbers(percents[key], locale)}\n {locale === 'en' ? '%' : '٪'}\n </span>\n )}\n {showCounts && (\n <span className=\"text-xs text-foreground-muted\">\n ({convertToLocalNumbers(data[key] ?? 0, locale)})\n </span>\n )}\n </span>\n ))}\n </div>\n </div>\n )\n }\n\n // Compact variant — inline chips with percentage\n if (variant === 'compact') {\n return (\n <div\n ref={ref}\n data-slot=\"sentiment-distribution\"\n className={cn('inline-flex flex-wrap gap-2', className)}\n {...props}\n >\n {activeOrder.map((key) => (\n <span\n key={key}\n className={cn('inline-flex items-center gap-1 rounded-full px-2 py-0.5', SENTIMENT_STYLES[key].bg)}\n >\n <span className={cn('text-xs font-medium', SENTIMENT_STYLES[key].text)}>{labels[key]}</span>\n {showPercent && (\n <span className={cn('text-xs font-bold tabular-nums', SENTIMENT_STYLES[key].text)}>\n {convertToLocalNumbers(percents[key], locale)}\n {locale === 'en' ? '%' : '٪'}\n </span>\n )}\n {showCounts && (\n <span className=\"text-xs text-foreground-muted tabular-nums\">\n ({convertToLocalNumbers((data[key] ?? 0).toLocaleString(), locale)})\n </span>\n )}\n </span>\n ))}\n </div>\n )\n }\n\n // Default — \"bars\" variant: individual rows per sentiment\n return (\n <div ref={ref} data-slot=\"sentiment-distribution\" className={cn('space-y-1.5', className)} {...props}>\n {activeOrder.map((key) => (\n <div key={key} className=\"flex items-center gap-2\">\n {/* Label */}\n <span className=\"w-10 shrink-0 text-xs text-foreground-light text-end\">{labels[key]}</span>\n\n {/* Progress bar */}\n <div className=\"relative flex-1 h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={cn(\n 'absolute inset-y-0 start-0 rounded-full transition-all duration-500',\n SENTIMENT_STYLES[key].bar\n )}\n style={{ width: `${percents[key]}%` }}\n role=\"progressbar\"\n aria-valuenow={percents[key]}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={`${labels[key]}: ${percents[key]}%`}\n />\n </div>\n\n {/* Value */}\n <span\n className={cn('w-10 shrink-0 text-xs font-medium tabular-nums text-start', SENTIMENT_STYLES[key].text)}\n >\n {showCounts\n ? convertToLocalNumbers((data[key] ?? 0).toLocaleString(), locale)\n : `${convertToLocalNumbers(percents[key], locale)}${locale === 'en' ? '%' : '٪'}`}\n </span>\n </div>\n ))}\n </div>\n )\n }\n)\n\nSentimentDistribution.displayName = 'SentimentDistribution'\n\nexport { SentimentDistribution }\n","'use client'\n\nimport * as React from 'react'\nimport { cn, convertToLocalNumbers, type SupportedLocale } from '@/lib/utils'\nimport { StatusBadge } from './status-badge'\nimport { SeverityBadge } from './severity-badge'\nimport { FlowDistribution, type FlowData } from './flow-distribution'\nimport { SentimentDistribution, type SentimentData } from './sentiment-distribution'\nimport { Sparkline } from './sparkline'\nimport { Checkbox } from './checkbox'\nimport type { StatusKey, SeverityKey } from '@/lib/i18n'\n\n/**\n * ConceptCard — افکارسنجی-flavored headline tile for clustering dashboards.\n * Composes: status badge, severity badge, flow composition bar, sentiment\n * distribution, sparkline trend, AI summary snippet, optional comparison\n * checkbox. Built for government dashboards where each \"concept\" needs\n * to fit in a card grid with rich, comparable signals.\n */\n\nexport interface ConceptCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Concept name (e.g., \"افزایش قیمت بنزین\") */\n title: React.ReactNode\n /** Short subtitle (e.g., \"خوشه ۴ — ۱۲۰ پست\") */\n subtitle?: React.ReactNode\n /** Operational status */\n status?: StatusKey\n /** Criticality of the concept (urgent/high/medium/low) */\n severity?: SeverityKey\n /** Flow composition breakdown (5-class political) */\n flow?: FlowData\n /** Sentiment composition (3-class) */\n sentiment?: SentimentData\n /** Trend sparkline values (e.g., last 14 days mention volume) */\n trend?: number[]\n /** AI-generated short summary (1-2 sentences) */\n aiSummary?: React.ReactNode\n /** Total mentions / size — shown as a stat */\n totalCount?: number\n /** Comparison checkbox state. When provided, renders the checkbox in the header */\n comparison?: {\n selected: boolean\n onSelectionChange: (next: boolean) => void\n label?: React.ReactNode\n }\n /** Make the entire card clickable (cursor + hover/focus ring) */\n interactive?: boolean\n /** Locale @default 'fa' */\n locale?: SupportedLocale\n}\n\nconst ConceptCard = React.forwardRef<HTMLDivElement, ConceptCardProps>(\n (\n {\n className,\n title,\n subtitle,\n status,\n severity,\n flow,\n sentiment,\n trend,\n aiSummary,\n totalCount,\n comparison,\n interactive = false,\n locale = 'fa',\n onClick,\n ...props\n },\n ref\n ) => {\n const isClickable = interactive || !!onClick\n const handleClick = isClickable\n ? (e: React.MouseEvent<HTMLDivElement>) => {\n // Ignore clicks on the comparison checkbox subtree\n const target = e.target as Element | null\n if (target && target.closest('[data-slot=\"concept-card-comparison\"]')) return\n onClick?.(e)\n }\n : undefined\n return (\n <div\n ref={ref}\n data-slot=\"concept-card\"\n data-status={status}\n data-severity={severity}\n role={isClickable ? 'button' : 'article'}\n tabIndex={isClickable ? 0 : undefined}\n onClick={handleClick}\n onKeyDown={\n isClickable\n ? (e) => {\n if ((e.key === 'Enter' || e.key === ' ') && e.target === e.currentTarget) {\n e.preventDefault()\n ;(e.currentTarget as HTMLDivElement).click()\n }\n }\n : undefined\n }\n className={cn(\n 'group/concept-card flex flex-col gap-3 rounded-lg border border-border bg-background p-4 transition-colors',\n isClickable &&\n 'cursor-pointer hover:border-strong hover:bg-surface-100/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n className\n )}\n {...props}\n >\n {/* Header row */}\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"text-sm font-semibold text-foreground truncate\">{title}</h3>\n {status && <StatusBadge status={status} size=\"sm\" animated locale={locale} />}\n {severity && <SeverityBadge severity={severity} size=\"sm\" locale={locale} />}\n </div>\n {subtitle && <p className=\"mt-0.5 text-xs text-foreground-lighter truncate\">{subtitle}</p>}\n </div>\n {comparison && (\n <div data-slot=\"concept-card-comparison\" className=\"shrink-0\">\n <Checkbox\n checked={comparison.selected}\n onCheckedChange={(next) => comparison.onSelectionChange(next === true)}\n aria-label={\n typeof comparison.label === 'string'\n ? comparison.label\n : locale === 'fa'\n ? 'انتخاب برای مقایسه'\n : locale === 'ar'\n ? 'تحديد للمقارنة'\n : 'Select for comparison'\n }\n />\n </div>\n )}\n </div>\n\n {/* AI summary */}\n {aiSummary && <p className=\"text-sm text-foreground leading-relaxed\">{aiSummary}</p>}\n\n {/* Composition bars */}\n {(flow || sentiment) && (\n <div className=\"flex flex-col gap-2\">\n {flow && (\n <div data-slot=\"concept-card-flow\">\n <FlowDistribution data={flow} variant=\"stacked\" showCounts={false} showPercent locale={locale} />\n </div>\n )}\n {sentiment && (\n <div data-slot=\"concept-card-sentiment\">\n <SentimentDistribution\n data={sentiment}\n variant=\"stacked\"\n showCounts={false}\n showPercent\n locale={locale}\n />\n </div>\n )}\n </div>\n )}\n\n {/* Footer row — trend + total */}\n {(trend || typeof totalCount === 'number') && (\n <div className=\"flex items-end justify-between gap-3 pt-1 border-t border-border mt-auto\">\n {trend && trend.length > 0 ? (\n <div data-slot=\"concept-card-trend\">\n <Sparkline data={trend} variant=\"area\" width={120} height={28} />\n </div>\n ) : (\n <span />\n )}\n {typeof totalCount === 'number' && (\n <div className=\"text-end\">\n <div className=\"text-xs text-foreground-lighter\">\n {locale === 'fa' ? 'مجموع' : locale === 'ar' ? 'الإجمالي' : 'Total'}\n </div>\n <div className=\"text-sm font-semibold tabular-nums text-foreground\">\n {convertToLocalNumbers(totalCount, locale)}\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n )\n }\n)\nConceptCard.displayName = 'ConceptCard'\n\nexport { ConceptCard }\n"]}
|