namirasoft-site-react 1.4.562 → 1.4.564

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.
Files changed (503) hide show
  1. package/.dockerignore +86 -86
  2. package/.env.template +10 -10
  3. package/Dockerfile +18 -18
  4. package/SKILL.md +307 -307
  5. package/config-overrides.js +72 -72
  6. package/dist/App.d.ts +1 -1
  7. package/dist/KeyOperation.d.ts +22 -0
  8. package/dist/KeyOperation.js +76 -0
  9. package/dist/KeyOperation.js.map +1 -0
  10. package/dist/LinkOperation.d.ts +18 -0
  11. package/dist/LinkOperation.js +61 -0
  12. package/dist/LinkOperation.js.map +1 -0
  13. package/dist/NSBoxBuilder.d.ts +25 -25
  14. package/dist/components/NSBanner.d.ts +1 -1
  15. package/dist/components/NSBanner.module.css +47 -47
  16. package/dist/components/NSBarAction.d.ts +1 -1
  17. package/dist/components/NSBarAction.module.css +91 -91
  18. package/dist/components/NSBarAlert.d.ts +1 -1
  19. package/dist/components/NSBarAlert.module.css +79 -79
  20. package/dist/components/NSBarNotification.d.ts +1 -1
  21. package/dist/components/NSBarNotification.module.css +34 -34
  22. package/dist/components/NSBarTitle.d.ts +1 -1
  23. package/dist/components/NSBarTitle.module.css +9 -9
  24. package/dist/components/NSBox.module.css +87 -87
  25. package/dist/components/NSBoxBaseCombo.d.ts +1 -1
  26. package/dist/components/NSBoxBaseCombo.module.css +6 -6
  27. package/dist/components/NSBoxBaseLayout.d.ts +1 -1
  28. package/dist/components/NSBoxBaseLayoutRecursive.d.ts +1 -1
  29. package/dist/components/NSBoxBoolean.d.ts +1 -1
  30. package/dist/components/NSBoxBoolean.module.css +73 -73
  31. package/dist/components/NSBoxBooleans.d.ts +1 -1
  32. package/dist/components/NSBoxColor.d.ts +1 -1
  33. package/dist/components/NSBoxCombo.d.ts +1 -1
  34. package/dist/components/NSBoxDate.d.ts +1 -1
  35. package/dist/components/NSBoxDate.module.css +4 -4
  36. package/dist/components/NSBoxDateRange.d.ts +1 -1
  37. package/dist/components/NSBoxDateRangeBase.d.ts +1 -1
  38. package/dist/components/NSBoxDateRangeBase.module.css +73 -73
  39. package/dist/components/NSBoxDateTime.d.ts +1 -1
  40. package/dist/components/NSBoxDateTime.module.css +4 -4
  41. package/dist/components/NSBoxDateTimeRange.d.ts +1 -1
  42. package/dist/components/NSBoxDouble.d.ts +1 -1
  43. package/dist/components/NSBoxDuration.d.ts +1 -1
  44. package/dist/components/NSBoxDuration.module.css +4 -4
  45. package/dist/components/NSBoxDynamic.d.ts +1 -1
  46. package/dist/components/NSBoxDynamics.d.ts +1 -1
  47. package/dist/components/NSBoxEmail.d.ts +1 -1
  48. package/dist/components/NSBoxEntity.d.ts +1 -1
  49. package/dist/components/NSBoxEnum.d.ts +1 -1
  50. package/dist/components/NSBoxFile.d.ts +1 -1
  51. package/dist/components/NSBoxFile.module.css +10 -10
  52. package/dist/components/NSBoxFilePath.d.ts +1 -1
  53. package/dist/components/NSBoxFont.d.ts +1 -1
  54. package/dist/components/NSBoxIPV4.d.ts +1 -1
  55. package/dist/components/NSBoxIPV4Range.d.ts +1 -1
  56. package/dist/components/NSBoxIPV6.d.ts +1 -1
  57. package/dist/components/NSBoxIPV6Range.d.ts +1 -1
  58. package/dist/components/NSBoxInteger.d.ts +1 -1
  59. package/dist/components/NSBoxInteger.module.css +16 -16
  60. package/dist/components/NSBoxMoney.d.ts +1 -1
  61. package/dist/components/NSBoxOptions.d.ts +1 -1
  62. package/dist/components/NSBoxOptions.module.css +53 -53
  63. package/dist/components/NSBoxPhone.d.ts +1 -1
  64. package/dist/components/NSBoxPhone.module.css +35 -35
  65. package/dist/components/NSBoxRadio.d.ts +1 -1
  66. package/dist/components/NSBoxRadio.module.css +33 -33
  67. package/dist/components/NSBoxSchemaBase.d.ts +1 -1
  68. package/dist/components/NSBoxSchemaBase.module.css +17 -17
  69. package/dist/components/NSBoxSchemaVariable.d.ts +1 -1
  70. package/dist/components/NSBoxSchemaVariable.module.css +16 -16
  71. package/dist/components/NSBoxSearch.d.ts +1 -1
  72. package/dist/components/NSBoxSensitive.d.ts +1 -1
  73. package/dist/components/NSBoxString.d.ts +1 -1
  74. package/dist/components/NSBoxTextArea.d.ts +1 -1
  75. package/dist/components/NSBoxTextArea.module.css +12 -12
  76. package/dist/components/NSBoxTime.d.ts +1 -1
  77. package/dist/components/NSBoxTime.module.css +4 -4
  78. package/dist/components/NSBoxTimeRange.d.ts +1 -1
  79. package/dist/components/NSBoxTimeZone.d.ts +1 -1
  80. package/dist/components/NSBoxURL.d.ts +1 -1
  81. package/dist/components/NSBoxVersion.d.ts +1 -1
  82. package/dist/components/NSButton.module.css +57 -57
  83. package/dist/components/NSButtonBlue.d.ts +1 -1
  84. package/dist/components/NSButtonBlue.module.css +4 -4
  85. package/dist/components/NSButtonGreen.d.ts +1 -1
  86. package/dist/components/NSButtonGreen.module.css +4 -4
  87. package/dist/components/NSButtonRed.d.ts +1 -1
  88. package/dist/components/NSButtonRed.module.css +4 -4
  89. package/dist/components/NSCard.d.ts +1 -1
  90. package/dist/components/NSCard.module.css +113 -113
  91. package/dist/components/NSCardScreenshot.d.ts +1 -1
  92. package/dist/components/NSCardScreenshot.module.css +41 -41
  93. package/dist/components/NSChartBar.d.ts +1 -1
  94. package/dist/components/NSChartBar.js +1 -1
  95. package/dist/components/NSChartBar.js.map +1 -1
  96. package/dist/components/NSChartDoughnut.d.ts +1 -1
  97. package/dist/components/NSChartDoughnut.js +1 -1
  98. package/dist/components/NSChartDoughnut.js.map +1 -1
  99. package/dist/components/NSChartLine.d.ts +1 -1
  100. package/dist/components/NSChartLine.js +1 -1
  101. package/dist/components/NSChartLine.js.map +1 -1
  102. package/dist/components/NSChartPie.d.ts +1 -1
  103. package/dist/components/NSChartPie.js +1 -1
  104. package/dist/components/NSChartPie.js.map +1 -1
  105. package/dist/components/NSChartRange.d.ts +1 -1
  106. package/dist/components/NSColumn.d.ts +1 -1
  107. package/dist/components/NSColumn.module.css +16 -16
  108. package/dist/components/NSCopy.d.ts +1 -1
  109. package/dist/components/NSCopyBox.d.ts +1 -1
  110. package/dist/components/NSCopyBox.module.css +39 -39
  111. package/dist/components/NSDialog.d.ts +1 -1
  112. package/dist/components/NSDialog.module.css +105 -105
  113. package/dist/components/NSDialogBox.d.ts +1 -1
  114. package/dist/components/NSDialogBoxDate.d.ts +1 -1
  115. package/dist/components/NSDialogBoxDateTime.d.ts +1 -1
  116. package/dist/components/NSDialogBoxPhone.d.ts +1 -1
  117. package/dist/components/NSDialogBoxString.d.ts +1 -1
  118. package/dist/components/NSDialogDelete.d.ts +1 -1
  119. package/dist/components/NSDialogInfo.d.ts +1 -1
  120. package/dist/components/NSDialogPageSelection.d.ts +2 -2
  121. package/dist/components/NSDialogPageSelection.module.css +103 -103
  122. package/dist/components/NSDownTimer.d.ts +1 -1
  123. package/dist/components/NSDownTimer.module.css +55 -55
  124. package/dist/components/NSDownload.d.ts +1 -1
  125. package/dist/components/NSDownload.module.css +46 -46
  126. package/dist/components/NSElectronicCard.d.ts +1 -1
  127. package/dist/components/NSElectronicCard.module.css +60 -60
  128. package/dist/components/NSEntityCardBackground.d.ts +1 -1
  129. package/dist/components/NSEntityCardBackground.module.css +27 -27
  130. package/dist/components/NSFilterBox.d.ts +2 -2
  131. package/dist/components/NSFilterBox.module.css +27 -27
  132. package/dist/components/NSFilterBoxItems.d.ts +1 -1
  133. package/dist/components/NSFilterBoxItems.module.css +15 -15
  134. package/dist/components/NSFilterItem.d.ts +1 -1
  135. package/dist/components/NSFilterItem.module.css +24 -24
  136. package/dist/components/NSFooter.d.ts +1 -1
  137. package/dist/components/NSFooter.module.css +140 -140
  138. package/dist/components/NSGauge.d.ts +1 -1
  139. package/dist/components/NSGauge.module.css +67 -67
  140. package/dist/components/NSGaugeDate.d.ts +1 -1
  141. package/dist/components/NSGaugeDate.module.css +57 -57
  142. package/dist/components/NSGaugeNumber.d.ts +1 -1
  143. package/dist/components/NSGaugeNumber.module.css +66 -66
  144. package/dist/components/NSHeader.d.ts +1 -1
  145. package/dist/components/NSHeader.module.css +316 -316
  146. package/dist/components/NSHeaderScreenshot.d.ts +1 -1
  147. package/dist/components/NSHeaderScreenshot.module.css +35 -35
  148. package/dist/components/NSID.d.ts +1 -1
  149. package/dist/components/NSInfo.d.ts +1 -1
  150. package/dist/components/NSLabel.d.ts +1 -1
  151. package/dist/components/NSLabel.module.css +25 -25
  152. package/dist/components/NSLabelErrorNotifier.d.ts +1 -1
  153. package/dist/components/NSLabelErrorNotifier.module.css +2 -2
  154. package/dist/components/NSLabelSensitive.d.ts +1 -1
  155. package/dist/components/NSLabelSensitive.module.css +9 -9
  156. package/dist/components/NSLayout.d.ts +1 -1
  157. package/dist/components/NSLayout.js +1 -1
  158. package/dist/components/NSLayout.js.map +1 -1
  159. package/dist/components/NSLayout.module.css +22 -23
  160. package/dist/components/NSLine.d.ts +1 -1
  161. package/dist/components/NSLine.module.css +15 -15
  162. package/dist/components/NSLineText.d.ts +1 -1
  163. package/dist/components/NSLineText.module.css +38 -38
  164. package/dist/components/NSLink.d.ts +1 -1
  165. package/dist/components/NSLink.module.css +36 -36
  166. package/dist/components/NSLinkBlue.d.ts +1 -1
  167. package/dist/components/NSLinkGreen.d.ts +1 -1
  168. package/dist/components/NSLinkRed.d.ts +1 -1
  169. package/dist/components/NSListGrouped.d.ts +1 -1
  170. package/dist/components/NSListGrouped.module.css +69 -69
  171. package/dist/components/NSListProduct.d.ts +1 -1
  172. package/dist/components/NSLoading.d.ts +1 -1
  173. package/dist/components/NSLoading.module.css +10 -10
  174. package/dist/components/NSMegaMenu.d.ts +1 -1
  175. package/dist/components/NSMegaMenu.module.css +133 -133
  176. package/dist/components/NSMenuAction.d.ts +1 -1
  177. package/dist/components/NSMenuAction.module.css +88 -88
  178. package/dist/components/NSMenuButton.d.ts +1 -1
  179. package/dist/components/NSMenuButton.module.css +44 -44
  180. package/dist/components/NSNoData.d.ts +1 -1
  181. package/dist/components/NSNoData.module.css +11 -11
  182. package/dist/components/NSPagination.d.ts +1 -1
  183. package/dist/components/NSPagination.js +1 -1
  184. package/dist/components/NSPagination.js.map +1 -1
  185. package/dist/components/NSPagination.module.css +87 -87
  186. package/dist/components/NSPanel.d.ts +1 -1
  187. package/dist/components/NSPanel.module.css +19 -19
  188. package/dist/components/NSPanelAccordion.d.ts +1 -1
  189. package/dist/components/NSPanelAccordion.module.css +4 -4
  190. package/dist/components/NSProductSearch.d.ts +1 -1
  191. package/dist/components/NSProductSearch.module.css +73 -73
  192. package/dist/components/NSProgressBar.d.ts +1 -1
  193. package/dist/components/NSProgressBar.module.css +38 -38
  194. package/dist/components/NSRange.d.ts +1 -1
  195. package/dist/components/NSRange.module.css +66 -66
  196. package/dist/components/NSRepeater.d.ts +1 -1
  197. package/dist/components/NSRepeaterNSBoxSchemaVariable.d.ts +1 -1
  198. package/dist/components/NSRepeaterNSTag.d.ts +1 -1
  199. package/dist/components/NSRow.d.ts +1 -1
  200. package/dist/components/NSRow.module.css +17 -17
  201. package/dist/components/NSSection.d.ts +1 -1
  202. package/dist/components/NSSection.module.css +4 -4
  203. package/dist/components/NSSectionCards.d.ts +1 -1
  204. package/dist/components/NSSectionCards.module.css +38 -38
  205. package/dist/components/NSSectionTiles.d.ts +1 -1
  206. package/dist/components/NSSectionTiles.module.css +10 -10
  207. package/dist/components/NSSectionTitle.d.ts +1 -1
  208. package/dist/components/NSSpace.d.ts +1 -1
  209. package/dist/components/NSSplitter.d.ts +1 -1
  210. package/dist/components/NSSplitter.module.css +59 -59
  211. package/dist/components/NSTabPage.d.ts +1 -1
  212. package/dist/components/NSTabPage.module.css +59 -59
  213. package/dist/components/NSTable.d.ts +1 -1
  214. package/dist/components/NSTable.module.css +371 -371
  215. package/dist/components/NSTableChart.d.ts +10 -1
  216. package/dist/components/NSTableChart.js +126 -20
  217. package/dist/components/NSTableChart.js.map +1 -1
  218. package/dist/components/NSTableChart.module.css +119 -119
  219. package/dist/components/NSTag.d.ts +1 -1
  220. package/dist/components/NSTile.d.ts +1 -1
  221. package/dist/components/NSTile.module.css +76 -76
  222. package/dist/components/NSTimelineMonthly.d.ts +1 -1
  223. package/dist/components/NSTimelineMonthly.module.css +71 -71
  224. package/dist/components/NSTitle.d.ts +1 -1
  225. package/dist/components/NSTitle.module.css +15 -15
  226. package/dist/formatter/StringFormatter.d.ts +1 -1
  227. package/dist/main.d.ts +2 -1
  228. package/dist/main.js +2 -1
  229. package/dist/main.js.map +1 -1
  230. package/dist/pages/NSImplementing.d.ts +1 -1
  231. package/dist/pages/NSImplementing.module.css +35 -35
  232. package/dist/pages/NSNotFoundPage.d.ts +1 -1
  233. package/dist/pages/NSNotFoundPage.module.css +18 -18
  234. package/dist/pages/NSUpdating.d.ts +1 -1
  235. package/dist/pages/NSUpdating.module.css +35 -35
  236. package/package.json +86 -86
  237. package/public/index.html +38 -38
  238. package/src/App.css +33 -33
  239. package/src/App.tsx +198 -198
  240. package/src/Color.tsx +10 -10
  241. package/src/CopyToClipboard.ts +6 -6
  242. package/src/EncryptionOperation.ts +98 -98
  243. package/src/KeyOperation.ts +110 -0
  244. package/src/LinkOperation.ts +87 -0
  245. package/src/NSBoxBuilder.tsx +922 -922
  246. package/src/ProductCacheService.ts +15 -15
  247. package/src/Validator.ts +199 -199
  248. package/src/components/INSBox.tsx +7 -7
  249. package/src/components/NSBanner.module.css +47 -47
  250. package/src/components/NSBanner.tsx +80 -80
  251. package/src/components/NSBarAction.module.css +91 -91
  252. package/src/components/NSBarAction.tsx +95 -95
  253. package/src/components/NSBarAlert.module.css +79 -79
  254. package/src/components/NSBarAlert.tsx +35 -35
  255. package/src/components/NSBarNotification.module.css +34 -34
  256. package/src/components/NSBarNotification.tsx +86 -86
  257. package/src/components/NSBarTitle.module.css +9 -9
  258. package/src/components/NSBarTitle.tsx +23 -23
  259. package/src/components/NSBox.module.css +87 -87
  260. package/src/components/NSBox.tsx +19 -19
  261. package/src/components/NSBoxBaseCombo.module.css +6 -6
  262. package/src/components/NSBoxBaseCombo.tsx +488 -488
  263. package/src/components/NSBoxBaseLayout.tsx +163 -163
  264. package/src/components/NSBoxBaseLayoutRecursive.tsx +59 -59
  265. package/src/components/NSBoxBoolean.module.css +73 -73
  266. package/src/components/NSBoxBoolean.tsx +152 -152
  267. package/src/components/NSBoxBooleans.tsx +181 -181
  268. package/src/components/NSBoxColor.tsx +107 -107
  269. package/src/components/NSBoxCombo.tsx +95 -95
  270. package/src/components/NSBoxDate.module.css +4 -4
  271. package/src/components/NSBoxDate.tsx +119 -119
  272. package/src/components/NSBoxDateRange.tsx +88 -88
  273. package/src/components/NSBoxDateRangeBase.module.css +73 -73
  274. package/src/components/NSBoxDateRangeBase.tsx +198 -198
  275. package/src/components/NSBoxDateTime.module.css +4 -4
  276. package/src/components/NSBoxDateTime.tsx +116 -116
  277. package/src/components/NSBoxDateTimeRange.tsx +65 -65
  278. package/src/components/NSBoxDouble.tsx +110 -110
  279. package/src/components/NSBoxDuration.module.css +4 -4
  280. package/src/components/NSBoxDuration.tsx +107 -107
  281. package/src/components/NSBoxDynamic.tsx +77 -77
  282. package/src/components/NSBoxDynamics.tsx +68 -68
  283. package/src/components/NSBoxEmail.tsx +117 -117
  284. package/src/components/NSBoxEntity.tsx +181 -181
  285. package/src/components/NSBoxEnum.tsx +113 -113
  286. package/src/components/NSBoxFile.module.css +10 -10
  287. package/src/components/NSBoxFile.tsx +106 -106
  288. package/src/components/NSBoxFilePath.tsx +106 -106
  289. package/src/components/NSBoxFont.tsx +107 -107
  290. package/src/components/NSBoxIPV4.tsx +107 -107
  291. package/src/components/NSBoxIPV4Range.tsx +107 -107
  292. package/src/components/NSBoxIPV6.tsx +104 -104
  293. package/src/components/NSBoxIPV6Range.tsx +107 -107
  294. package/src/components/NSBoxInteger.module.css +16 -16
  295. package/src/components/NSBoxInteger.tsx +126 -126
  296. package/src/components/NSBoxMoney.tsx +115 -115
  297. package/src/components/NSBoxOptions.module.css +53 -53
  298. package/src/components/NSBoxOptions.tsx +182 -182
  299. package/src/components/NSBoxPhone.module.css +35 -35
  300. package/src/components/NSBoxPhone.tsx +120 -120
  301. package/src/components/NSBoxRadio.module.css +33 -33
  302. package/src/components/NSBoxRadio.tsx +104 -104
  303. package/src/components/NSBoxSchemaBase.module.css +17 -17
  304. package/src/components/NSBoxSchemaBase.tsx +562 -562
  305. package/src/components/NSBoxSchemaVariable.module.css +16 -16
  306. package/src/components/NSBoxSchemaVariable.tsx +263 -263
  307. package/src/components/NSBoxSearch.tsx +112 -112
  308. package/src/components/NSBoxSensitive.tsx +133 -133
  309. package/src/components/NSBoxString.tsx +106 -106
  310. package/src/components/NSBoxTextArea.module.css +12 -12
  311. package/src/components/NSBoxTextArea.tsx +136 -136
  312. package/src/components/NSBoxTime.module.css +4 -4
  313. package/src/components/NSBoxTime.tsx +106 -106
  314. package/src/components/NSBoxTimeRange.tsx +220 -220
  315. package/src/components/NSBoxTimeZone.tsx +122 -122
  316. package/src/components/NSBoxURL.tsx +117 -117
  317. package/src/components/NSBoxVersion.tsx +107 -107
  318. package/src/components/NSButton.module.css +57 -57
  319. package/src/components/NSButton.tsx +74 -74
  320. package/src/components/NSButtonBlue.module.css +4 -4
  321. package/src/components/NSButtonBlue.tsx +29 -29
  322. package/src/components/NSButtonGreen.module.css +4 -4
  323. package/src/components/NSButtonGreen.tsx +29 -29
  324. package/src/components/NSButtonRed.module.css +4 -4
  325. package/src/components/NSButtonRed.tsx +29 -29
  326. package/src/components/NSCard.module.css +113 -113
  327. package/src/components/NSCard.tsx +67 -67
  328. package/src/components/NSCardScreenshot.module.css +41 -41
  329. package/src/components/NSCardScreenshot.tsx +31 -31
  330. package/src/components/NSChartBar.tsx +134 -134
  331. package/src/components/NSChartDoughnut.tsx +114 -114
  332. package/src/components/NSChartLine.tsx +110 -110
  333. package/src/components/NSChartPie.tsx +105 -105
  334. package/src/components/NSChartRange.tsx +14 -14
  335. package/src/components/NSColumn.module.css +16 -16
  336. package/src/components/NSColumn.tsx +24 -24
  337. package/src/components/NSCopy.tsx +58 -58
  338. package/src/components/NSCopyBox.module.css +39 -39
  339. package/src/components/NSCopyBox.tsx +53 -53
  340. package/src/components/NSDialog.module.css +105 -105
  341. package/src/components/NSDialog.tsx +72 -72
  342. package/src/components/NSDialogBox.tsx +38 -38
  343. package/src/components/NSDialogBoxDate.tsx +28 -28
  344. package/src/components/NSDialogBoxDateTime.tsx +28 -28
  345. package/src/components/NSDialogBoxPhone.tsx +28 -28
  346. package/src/components/NSDialogBoxString.tsx +28 -28
  347. package/src/components/NSDialogDelete.tsx +36 -36
  348. package/src/components/NSDialogInfo.tsx +49 -49
  349. package/src/components/NSDialogPageSelection.module.css +103 -103
  350. package/src/components/NSDialogPageSelection.tsx +219 -219
  351. package/src/components/NSDownTimer.module.css +55 -55
  352. package/src/components/NSDownTimer.tsx +91 -91
  353. package/src/components/NSDownload.module.css +46 -46
  354. package/src/components/NSDownload.tsx +69 -69
  355. package/src/components/NSElectronicCard.module.css +60 -60
  356. package/src/components/NSElectronicCard.tsx +46 -46
  357. package/src/components/NSEntityCardBackground.module.css +27 -27
  358. package/src/components/NSEntityCardBackground.tsx +36 -36
  359. package/src/components/NSFilterBox.module.css +27 -27
  360. package/src/components/NSFilterBox.tsx +477 -477
  361. package/src/components/NSFilterBoxItems.module.css +15 -15
  362. package/src/components/NSFilterBoxItems.tsx +52 -52
  363. package/src/components/NSFilterItem.module.css +24 -24
  364. package/src/components/NSFilterItem.tsx +27 -27
  365. package/src/components/NSFooter.module.css +140 -140
  366. package/src/components/NSFooter.tsx +321 -321
  367. package/src/components/NSGauge.module.css +67 -67
  368. package/src/components/NSGauge.tsx +52 -52
  369. package/src/components/NSGaugeDate.module.css +57 -57
  370. package/src/components/NSGaugeDate.tsx +125 -125
  371. package/src/components/NSGaugeNumber.module.css +66 -66
  372. package/src/components/NSGaugeNumber.tsx +90 -90
  373. package/src/components/NSHeader.module.css +316 -316
  374. package/src/components/NSHeader.tsx +334 -334
  375. package/src/components/NSHeaderScreenshot.module.css +35 -35
  376. package/src/components/NSHeaderScreenshot.tsx +39 -39
  377. package/src/components/NSID.tsx +159 -159
  378. package/src/components/NSInfo.tsx +50 -50
  379. package/src/components/NSLabel.module.css +25 -25
  380. package/src/components/NSLabel.tsx +47 -47
  381. package/src/components/NSLabelErrorNotifier.module.css +2 -2
  382. package/src/components/NSLabelErrorNotifier.tsx +35 -35
  383. package/src/components/NSLabelSensitive.module.css +9 -9
  384. package/src/components/NSLabelSensitive.tsx +85 -85
  385. package/src/components/NSLayout.module.css +22 -23
  386. package/src/components/NSLayout.tsx +140 -140
  387. package/src/components/NSLine.module.css +15 -15
  388. package/src/components/NSLine.tsx +13 -13
  389. package/src/components/NSLineText.module.css +38 -38
  390. package/src/components/NSLineText.tsx +17 -17
  391. package/src/components/NSLink.module.css +36 -36
  392. package/src/components/NSLink.tsx +26 -26
  393. package/src/components/NSLinkBlue.tsx +22 -22
  394. package/src/components/NSLinkGreen.tsx +13 -13
  395. package/src/components/NSLinkRed.tsx +13 -13
  396. package/src/components/NSListGrouped.module.css +69 -69
  397. package/src/components/NSListGrouped.tsx +106 -106
  398. package/src/components/NSListProduct.tsx +78 -78
  399. package/src/components/NSLoading.module.css +10 -10
  400. package/src/components/NSLoading.tsx +43 -43
  401. package/src/components/NSMegaMenu.module.css +133 -133
  402. package/src/components/NSMegaMenu.tsx +188 -188
  403. package/src/components/NSMenuAction.module.css +88 -88
  404. package/src/components/NSMenuAction.tsx +106 -106
  405. package/src/components/NSMenuButton.module.css +44 -44
  406. package/src/components/NSMenuButton.tsx +197 -197
  407. package/src/components/NSNoData.module.css +11 -11
  408. package/src/components/NSNoData.tsx +24 -24
  409. package/src/components/NSPagination.module.css +87 -87
  410. package/src/components/NSPagination.tsx +183 -183
  411. package/src/components/NSPanel.module.css +19 -19
  412. package/src/components/NSPanel.tsx +24 -24
  413. package/src/components/NSPanelAccordion.module.css +4 -4
  414. package/src/components/NSPanelAccordion.tsx +51 -51
  415. package/src/components/NSProductSearch.module.css +73 -73
  416. package/src/components/NSProductSearch.tsx +193 -193
  417. package/src/components/NSProgressBar.module.css +38 -38
  418. package/src/components/NSProgressBar.tsx +29 -29
  419. package/src/components/NSRange.module.css +66 -66
  420. package/src/components/NSRange.tsx +83 -83
  421. package/src/components/NSRepeater.tsx +328 -328
  422. package/src/components/NSRepeaterNSBoxSchemaVariable.tsx +94 -94
  423. package/src/components/NSRepeaterNSTag.tsx +86 -86
  424. package/src/components/NSRow.module.css +17 -17
  425. package/src/components/NSRow.tsx +24 -24
  426. package/src/components/NSSection.module.css +4 -4
  427. package/src/components/NSSection.tsx +26 -26
  428. package/src/components/NSSectionCards.module.css +38 -38
  429. package/src/components/NSSectionCards.tsx +51 -51
  430. package/src/components/NSSectionTiles.module.css +10 -10
  431. package/src/components/NSSectionTiles.tsx +25 -25
  432. package/src/components/NSSectionTitle.tsx +19 -19
  433. package/src/components/NSSpace.tsx +28 -28
  434. package/src/components/NSSplitter.module.css +59 -59
  435. package/src/components/NSSplitter.tsx +211 -211
  436. package/src/components/NSTabPage.module.css +59 -59
  437. package/src/components/NSTabPage.tsx +122 -122
  438. package/src/components/NSTable.module.css +371 -371
  439. package/src/components/NSTable.tsx +1115 -1115
  440. package/src/components/NSTableChart.module.css +119 -119
  441. package/src/components/NSTableChart.tsx +732 -587
  442. package/src/components/NSTag.tsx +74 -74
  443. package/src/components/NSTile.module.css +76 -76
  444. package/src/components/NSTile.tsx +27 -27
  445. package/src/components/NSTimelineMonthly.module.css +71 -71
  446. package/src/components/NSTimelineMonthly.tsx +44 -44
  447. package/src/components/NSTitle.module.css +15 -15
  448. package/src/components/NSTitle.tsx +19 -19
  449. package/src/formatter/BackColorFormatter.tsx +23 -23
  450. package/src/formatter/BaseColumnFormatter.ts +18 -18
  451. package/src/formatter/BaseURLImageFormatter.tsx +33 -33
  452. package/src/formatter/BooleanFormatter.ts +22 -22
  453. package/src/formatter/BytesFormatter.tsx +15 -15
  454. package/src/formatter/DateFormatter.ts +22 -22
  455. package/src/formatter/DateTimeFormatter.ts +22 -22
  456. package/src/formatter/DurationFormatter.ts +13 -13
  457. package/src/formatter/EmailFormatter.tsx +21 -21
  458. package/src/formatter/EnumFormatter.ts +13 -13
  459. package/src/formatter/FloatFormatter.ts +23 -23
  460. package/src/formatter/ForeColorFormatter.tsx +24 -24
  461. package/src/formatter/IDFormatter.tsx +53 -53
  462. package/src/formatter/IPFormatter.ts +13 -13
  463. package/src/formatter/IntegerFormatter.ts +23 -23
  464. package/src/formatter/JsonFormatter.tsx +58 -58
  465. package/src/formatter/MoneyFormatter.ts +35 -35
  466. package/src/formatter/PercentFormatter.tsx +39 -39
  467. package/src/formatter/PhoneFormatter.tsx +21 -21
  468. package/src/formatter/SensitiveFormatter.tsx +33 -33
  469. package/src/formatter/StringFormatter.tsx +43 -43
  470. package/src/formatter/TimeFormatter.ts +21 -21
  471. package/src/formatter/URLFormatter.tsx +24 -24
  472. package/src/formatter/UnknowFormatter.ts +18 -18
  473. package/src/index.tsx +7 -7
  474. package/src/main.ts +262 -261
  475. package/src/pages/NSImplementing.module.css +35 -35
  476. package/src/pages/NSImplementing.tsx +32 -32
  477. package/src/pages/NSNotFoundPage.module.css +18 -18
  478. package/src/pages/NSNotFoundPage.tsx +14 -14
  479. package/src/pages/NSUpdating.module.css +35 -35
  480. package/src/pages/NSUpdating.tsx +32 -32
  481. package/src/props/IBackgroundProps.ts +5 -5
  482. package/src/props/IBaseComponentProps.ts +8 -8
  483. package/src/props/IHeaderIconProps.ts +10 -10
  484. package/src/props/IHeaderLeftProps.ts +6 -6
  485. package/src/props/IHeaderRightProps.ts +8 -8
  486. package/src/props/IImageProps.ts +4 -4
  487. package/src/props/ILinkProps.ts +4 -4
  488. package/src/props/IValidationNumberProps.ts +4 -4
  489. package/src/props/IValidationPrecisionProps.ts +3 -3
  490. package/src/props/IValidationProps.ts +9 -9
  491. package/src/props/IValidationRegexProps.ts +4 -4
  492. package/src/props/IValidationStringProps.ts +4 -4
  493. package/src/routing/NSNotifier.ts +114 -114
  494. package/src/routing/NSRouterMaker.tsx +20 -20
  495. package/src/routing/NSRouterMakerComponent.ts +5 -5
  496. package/src/routing/NSRouterMakerProps.ts +5 -5
  497. package/tsconfig.json +43 -43
  498. package/dist/App.css +0 -34
  499. package/dist/ColorOperation.d.ts +0 -27
  500. package/dist/ColorOperation.js +0 -145
  501. package/dist/ColorOperation.js.map +0 -1
  502. package/dist/index.css +0 -6
  503. package/src/ColorOperation.ts +0 -154
@@ -1,588 +1,733 @@
1
- "use client";
2
- import { IStorageLocal } from "namirasoft-core";
3
- import { Component, createRef } from "react";
4
- import { IBaseComponentProps } from "../props/IBaseComponentProps";
5
- import { NSBoxBoolean } from "./NSBoxBoolean";
6
- import { NSBoxCombo } from "./NSBoxCombo";
7
- import { NSBoxOptions } from "./NSBoxOptions";
8
- import { NSButton } from "./NSButton";
9
- import { NSButtonBlue } from "./NSButtonBlue";
10
- import { NSChartBar, NSChartBarMode } from "./NSChartBar";
11
- import { NSChartDoughnut } from "./NSChartDoughnut";
12
- import { NSChartLine } from "./NSChartLine";
13
- import { NSColumn } from "./NSColumn";
14
- import { NSNoData } from "./NSNoData";
15
- import { TableColumnInfo } from "./NSTable";
16
- import Styles from "./NSTableChart.module.css";
17
-
18
- export type NSTableChartType = "bar" | "hbar" | "line" | "area" | "doughnut";
19
- export type NSTableChartRowSource = "page" | "selected";
20
- export type NSTableChartAggregate = "sum" | "average" | "count" | "min" | "max";
21
-
22
- const CHART_TYPES: { type: NSTableChartType, text: string }[] = [
23
- { type: "bar", text: "Bar" },
24
- { type: "hbar", text: "Horizontal Bar" },
25
- { type: "line", text: "Line" },
26
- { type: "area", text: "Area" },
27
- { type: "doughnut", text: "Doughnut" },
28
- ];
29
-
30
- const BAR_MODES: { mode: NSChartBarMode, text: string }[] = [
31
- { mode: "grouped", text: "Grouped" },
32
- { mode: "stacked", text: "Stacked" },
33
- { mode: "percent", text: "Percent" },
34
- ];
35
-
36
- const AGGREGATES: { value: NSTableChartAggregate, text: string }[] = [
37
- { value: "sum", text: "Sum" },
38
- { value: "average", text: "Average" },
39
- { value: "count", text: "Count" },
40
- { value: "min", text: "Min" },
41
- { value: "max", text: "Max" },
42
- ];
43
-
44
- const NUMERIC_TYPES = ["number", "integer", "int", "float", "double", "decimal", "long", "money", "percent", "percentage", "bytes", "duration"];
45
-
46
- export interface NSTableChartProps<RowType> extends IBaseComponentProps
47
- {
48
- name: string;
49
- columns: TableColumnInfo[];
50
- rows: RowType[];
51
- selectedRows: RowType[];
52
- onClose: () => void;
53
- }
54
-
55
- interface NSTableChartState
56
- {
57
- chartType: NSTableChartType;
58
- barMode: NSChartBarMode;
59
- aggregate: NSTableChartAggregate;
60
- rowSource: NSTableChartRowSource;
61
- xColumn: string;
62
- yColumns: string[];
63
- }
64
-
65
- interface NSTableChartSettings
66
- {
67
- chartType?: NSTableChartType;
68
- barMode?: NSChartBarMode;
69
- aggregate?: NSTableChartAggregate;
70
- rowSource?: NSTableChartRowSource;
71
- xColumn?: string;
72
- yColumns?: string[];
73
- }
74
-
75
- export class NSTableChart<RowType> extends Component<NSTableChartProps<RowType>, NSTableChartState>
76
- {
77
- private chartContainer = createRef<HTMLDivElement>();
78
- private previousBodyOverflow = "";
79
- private Storage = new IStorageLocal();
80
-
81
- constructor(props: NSTableChartProps<RowType>)
82
- {
83
- super(props);
84
- let numeric = this.getNumericColumns();
85
- let nonNumeric = props.columns.filter(column => !this.isNumericColumn(column));
86
- let defaultX = (nonNumeric[0] ?? props.columns[0])?.name ?? "";
87
- let defaultY = numeric.length > 0 ? [numeric[0].name] : [];
88
-
89
- let saved = this.loadSettings();
90
- let chartType: NSTableChartType = CHART_TYPES.some(item => item.type === saved?.chartType) ? saved!.chartType! : "bar";
91
- let barMode: NSChartBarMode = BAR_MODES.some(item => item.mode === saved?.barMode) ? saved!.barMode! : "grouped";
92
- let aggregate: NSTableChartAggregate = AGGREGATES.some(item => item.value === saved?.aggregate) ? saved!.aggregate! : "sum";
93
- let rowSource: NSTableChartRowSource = saved?.rowSource === "selected" ? "selected" : "page";
94
- let xColumn = (saved?.xColumn && this.getColumn(saved.xColumn)) ? saved.xColumn : defaultX;
95
- let savedY = (saved?.yColumns ?? []).filter((name: string) => numeric.some(column => column.name === name));
96
- let yColumns = savedY.length > 0 ? savedY : defaultY;
97
- if (chartType === "doughnut" && yColumns.length > 1)
98
- yColumns = [yColumns[0]];
99
-
100
- this.state = {
101
- chartType,
102
- barMode,
103
- aggregate,
104
- rowSource,
105
- xColumn,
106
- yColumns,
107
- };
108
- this.download = this.download.bind(this);
109
- }
110
-
111
- override componentDidMount()
112
- {
113
- this.previousBodyOverflow = document.body.style.overflow;
114
- document.body.style.overflow = "hidden";
115
- }
116
-
117
- override componentWillUnmount()
118
- {
119
- document.body.style.overflow = this.previousBodyOverflow;
120
- }
121
-
122
- override componentDidUpdate(_prevProps: NSTableChartProps<RowType>, prevState: NSTableChartState)
123
- {
124
- if (
125
- prevState.chartType !== this.state.chartType ||
126
- prevState.barMode !== this.state.barMode ||
127
- prevState.aggregate !== this.state.aggregate ||
128
- prevState.rowSource !== this.state.rowSource ||
129
- prevState.xColumn !== this.state.xColumn ||
130
- prevState.yColumns !== this.state.yColumns
131
- )
132
- this.saveSettings();
133
- }
134
-
135
- private settingsKey(): string
136
- {
137
- return `ns_table_chart_${this.props.name}`;
138
- }
139
-
140
- private loadSettings(): NSTableChartSettings | null
141
- {
142
- try
143
- {
144
- let raw = this.Storage.get(this.settingsKey(), "");
145
- return raw ? JSON.parse(raw) as NSTableChartSettings : null;
146
- }
147
- catch (e)
148
- {
149
- return null;
150
- }
151
- }
152
-
153
- private saveSettings(): void
154
- {
155
- try
156
- {
157
- let { chartType, barMode, aggregate, rowSource, xColumn, yColumns } = this.state;
158
- this.Storage.set(this.settingsKey(), JSON.stringify({ chartType, barMode, aggregate, rowSource, xColumn, yColumns }));
159
- }
160
- catch (e) { }
161
- }
162
-
163
- private isNumericColumn(column: TableColumnInfo): boolean
164
- {
165
- return NUMERIC_TYPES.includes((column.type ?? "").toLowerCase());
166
- }
167
-
168
- private getNumericColumns(): TableColumnInfo[]
169
- {
170
- return this.props.columns.filter(column => this.isNumericColumn(column));
171
- }
172
-
173
- private getColumn(name: string): TableColumnInfo | undefined
174
- {
175
- return this.props.columns.find(column => column.name === name);
176
- }
177
-
178
- private getTableTitle(): string
179
- {
180
- return this.props.columns[0]?.table?.text ?? this.props.name;
181
- }
182
-
183
- private getActiveRows(): RowType[]
184
- {
185
- return this.state.rowSource === "selected" ? this.props.selectedRows : this.props.rows;
186
- }
187
-
188
- private toNumber(value: any): number
189
- {
190
- let n = Number(value);
191
- return isFinite(n) ? n : 0;
192
- }
193
-
194
- private labelOf(row: RowType, column: TableColumnInfo | undefined): string
195
- {
196
- if (!column)
197
- return "";
198
- let value = (row as any)[column.name];
199
- if (value === null || value === undefined)
200
- return "";
201
- return String(value);
202
- }
203
-
204
- private aggregate(rows: RowType[], xCol: TableColumnInfo | undefined, yCols: TableColumnInfo[]): { labels: string[], series: number[][] }
205
- {
206
- let labels: string[] = [];
207
- let indexByLabel = new Map<string, number>();
208
- let stats: { sum: number, count: number, min: number, max: number }[][] = yCols.map(() => []);
209
- rows.forEach(row =>
210
- {
211
- let label = this.labelOf(row, xCol);
212
- let index = indexByLabel.get(label);
213
- if (index === undefined)
214
- {
215
- index = labels.length;
216
- indexByLabel.set(label, index);
217
- labels.push(label);
218
- stats.forEach(serie => serie[index as number] = { sum: 0, count: 0, min: Infinity, max: -Infinity });
219
- }
220
- yCols.forEach((column, s) =>
221
- {
222
- let raw = (row as any)[column.name];
223
- if (raw === null || raw === undefined || raw === "")
224
- return;
225
- let value = this.toNumber(raw);
226
- let cell = stats[s][index as number];
227
- cell.sum += value;
228
- cell.count += 1;
229
- cell.min = Math.min(cell.min, value);
230
- cell.max = Math.max(cell.max, value);
231
- });
232
- });
233
-
234
- let series = stats.map(serie => serie.map(cell => this.applyAggregate(cell)));
235
-
236
- let order = labels.map((_, index) => index);
237
- order.sort((a, b) => this.compareLabels(labels[a], labels[b], xCol));
238
- return {
239
- labels: order.map(index => labels[index]),
240
- series: series.map(serie => order.map(index => serie[index])),
241
- };
242
- }
243
-
244
- private applyAggregate(cell: { sum: number, count: number, min: number, max: number }): number
245
- {
246
- switch (this.state.aggregate)
247
- {
248
- case "sum":
249
- return cell.sum;
250
- case "count":
251
- return cell.count;
252
- case "average":
253
- return cell.count ? cell.sum / cell.count : 0;
254
- case "min":
255
- return cell.count ? cell.min : 0;
256
- case "max":
257
- return cell.count ? cell.max : 0;
258
- default:
259
- return cell.sum;
260
- }
261
- }
262
-
263
- private formatValueFor(yCols: TableColumnInfo[]): (value: number) => string
264
- {
265
- let aggregate = this.state.aggregate;
266
- let formatter = yCols[0]?.formatter;
267
- return (value: number) =>
268
- {
269
- if (aggregate === "count")
270
- return Math.round(value).toLocaleString();
271
- try
272
- {
273
- let out = (formatter as any)?.format(value);
274
- if (typeof out === "string")
275
- return out;
276
- }
277
- catch (e) { }
278
- return value.toLocaleString();
279
- };
280
- }
281
-
282
- private compareLabels(a: string, b: string, xCol: TableColumnInfo | undefined): number
283
- {
284
- if (xCol && this.isNumericColumn(xCol))
285
- return this.toNumber(a) - this.toNumber(b);
286
- if (xCol && /(date|time)/.test((xCol.type ?? "").toLowerCase()))
287
- {
288
- let da = Date.parse(a);
289
- let db = Date.parse(b);
290
- if (!isNaN(da) && !isNaN(db))
291
- return da - db;
292
- }
293
- return a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" });
294
- }
295
-
296
- private download()
297
- {
298
- let container = this.chartContainer.current;
299
- if (!container)
300
- return;
301
- let canvas = container.querySelector("canvas");
302
- if (!canvas)
303
- return;
304
- let link = document.createElement("a");
305
- link.href = canvas.toDataURL("image/png");
306
- link.download = this.props.name + "_chart.png";
307
- link.click();
308
- }
309
-
310
- private renderChart()
311
- {
312
- let { chartType, xColumn, yColumns } = this.state;
313
- let rows = this.getActiveRows();
314
-
315
- let xCol = this.getColumn(xColumn);
316
- let yCols = yColumns.map(name => this.getColumn(name)).filter(Boolean) as TableColumnInfo[];
317
-
318
- if (rows.length === 0)
319
- return <NSNoData lable={this.state.rowSource === "selected" ? "No selected rows" : "No Data"} />;
320
- if (yCols.length === 0)
321
- return <div className={Styles.ns_chart_message}>Select at least one numeric series for the Y-axis.</div>;
322
-
323
- let { labels, series } = this.aggregate(rows, xCol, yCols);
324
-
325
- let dataset = yCols.map((column, s) => ({ label: column.text, data: series[s] }));
326
- let formatValue = this.formatValueFor(yCols);
327
-
328
- if (chartType === "bar" || chartType === "hbar")
329
- {
330
- let max = 0;
331
- labels.forEach((_, index) =>
332
- {
333
- if (this.state.barMode === "stacked")
334
- {
335
- let sum = 0;
336
- dataset.forEach(item => sum += item.data[index] ?? 0);
337
- max = Math.max(max, sum);
338
- }
339
- else
340
- dataset.forEach(item => max = Math.max(max, item.data[index] ?? 0));
341
- });
342
- max = max <= 0 ? 1 : Math.ceil(max * 1.1);
343
- return (
344
- <NSChartBar
345
- labels={labels}
346
- dataset={dataset}
347
- range={{ min: 0, max }}
348
- mode={this.state.barMode}
349
- horizontal={chartType === "hbar"}
350
- xTitle={xCol?.text}
351
- formatValue={formatValue}
352
- yGrid
353
- style={{ width: "100%" }}
354
- />
355
- );
356
- }
357
-
358
- if (chartType === "line" || chartType === "area")
359
- {
360
- let max = 0;
361
- dataset.forEach(item => item.data.forEach(value => max = Math.max(max, value)));
362
- max = max <= 0 ? 1 : Math.ceil(max * 1.1);
363
- return (
364
- <NSChartLine
365
- labels={labels}
366
- dataset={dataset}
367
- range={{ min: 0, max }}
368
- area={chartType === "area"}
369
- xTitle={xCol?.text}
370
- formatValue={formatValue}
371
- yGrid
372
- style={{ width: "100%" }}
373
- />
374
- );
375
- }
376
-
377
- let datas = series[0];
378
-
379
- return (
380
- <NSChartDoughnut
381
- title={yCols[0]?.text ?? ""}
382
- labels={labels}
383
- datas={datas}
384
- labelTitle={xCol?.text}
385
- valueTitle={yCols[0]?.text}
386
- formatValue={formatValue}
387
- style={{ width: "100%", height: "100%", display: "flex", justifyContent: "center" }}
388
- />
389
- );
390
- }
391
-
392
- override render()
393
- {
394
- let { chartType, barMode, xColumn, yColumns } = this.state;
395
- let numericColumns = this.getNumericColumns();
396
- let isSingleSeries = chartType === "doughnut";
397
-
398
- return (
399
- <div
400
- id={this.props.id}
401
- className={[Styles.ns_chart_overlay, ...(this.props.classList ?? [])].join(" ")}
402
- style={this.props.style}
403
- >
404
- <div className={Styles.ns_chart_modal}>
405
- <div className={Styles.ns_chart_header}>
406
- <span className={Styles.ns_chart_title}>
407
- Chart{this.getTableTitle() ? ` - ${this.getTableTitle()}` : ""}
408
- </span>
409
- <NSButton
410
- icon={{ src: "https://static.namirasoft.com/image/concept/close/blue.svg", alt: "close" }}
411
- onClick={{
412
- action: this.props.onClose,
413
- showLoading: false
414
- }}
415
- style={{ width: 24, height: 24, boxShadow: "none" }}
416
- />
417
- </div>
418
- <div className={Styles.ns_chart_body}>
419
- <div className={Styles.ns_chart_canvas} ref={this.chartContainer}>
420
- {this.renderChart()}
421
- </div>
422
- <NSColumn classList={[Styles.ns_chart_panel]}>
423
- <NSBoxCombo
424
- title="Chart Type"
425
- required={false}
426
- multiple={false}
427
- searchable={false}
428
- clearable={false}
429
- reloadable={false}
430
- defaultValue={chartType}
431
- getOptions={() => CHART_TYPES.map(item => ({ value: item.type, title: item.text }))}
432
- onChanged={(box) =>
433
- {
434
- let value = box.getValue(false) as string | null;
435
- if (!value)
436
- return;
437
- let nextType = value as NSTableChartType;
438
- let nextY = nextType === "doughnut" && yColumns.length > 1
439
- ? [yColumns[0]]
440
- : yColumns;
441
- this.setState({ chartType: nextType, yColumns: nextY });
442
- }}
443
- menu={{
444
- builtin: {
445
- copy: { enabled: false, getValue: () => "" },
446
- delete: { enabled: false, onDelete: () => { } },
447
- },
448
- items: []
449
- }}
450
- />
451
-
452
- {
453
- (chartType === "bar" || chartType === "hbar") && yColumns.length > 1 &&
454
- <NSBoxOptions
455
- key="bar-mode"
456
- title="Bar Mode"
457
- multiple={false}
458
- required={false}
459
- defaultValue={barMode}
460
- options={BAR_MODES.map(item => ({ value: item.mode, title: item.text }))}
461
- onChanged={(box) =>
462
- {
463
- let value = box.getValue(false) as string | null;
464
- if (value)
465
- this.setState({ barMode: value as NSChartBarMode });
466
- }}
467
- menu={{
468
- builtin: {
469
- copy: { enabled: false, getValue: () => "" },
470
- delete: { enabled: false, onDelete: () => { } },
471
- },
472
- items: []
473
- }}
474
- input={{ style: { minHeight: "auto", border: "none", padding: "8px 0 0" } }}
475
- />
476
- }
477
-
478
- <NSBoxCombo
479
- title="Label (X-Axis)"
480
- multiple={false}
481
- required={false}
482
- reloadable={false}
483
- clearable={false}
484
- defaultValue={xColumn}
485
- getOptions={() => this.props.columns.map(column => ({ value: column.name, title: column.text }))}
486
- onChanged={(box) =>
487
- {
488
- let value = box.getValue(false) as string | null;
489
- if (value)
490
- this.setState({ xColumn: value });
491
- }}
492
- menu={{
493
- builtin: {
494
- copy: { enabled: false, getValue: () => "" },
495
- delete: { enabled: false, onDelete: () => { } },
496
- },
497
- items: []
498
- }}
499
- />
500
-
501
- <NSBoxCombo
502
- key={isSingleSeries ? "y-single" : "y-multi"}
503
- title={"Series (Y-Axis)"}
504
- multiple={!isSingleSeries}
505
- required={false}
506
- clearable={false}
507
- reloadable={false}
508
- defaultValue={isSingleSeries ? yColumns[0] : yColumns}
509
- getOptions={() => numericColumns.map(column => ({ value: column.name, title: column.text }))}
510
- onChanged={(box) =>
511
- {
512
- let value = box.getValue(false);
513
- let next = Array.isArray(value) ? value : (value ? [value] : []);
514
- this.setState({ yColumns: next });
515
- }}
516
- menu={{
517
- builtin: {
518
- copy: { enabled: false, getValue: () => "" },
519
- delete: { enabled: false, onDelete: () => { } },
520
- },
521
- items: []
522
- }}
523
- />
524
-
525
- <NSBoxOptions
526
- title="Aggregate"
527
- multiple={false}
528
- required={false}
529
- defaultValue={this.state.aggregate}
530
- options={AGGREGATES.map(item => ({ value: item.value, title: item.text }))}
531
- onChanged={(box) =>
532
- {
533
- let value = box.getValue(false) as string | null;
534
- if (value)
535
- this.setState({ aggregate: value as NSTableChartAggregate });
536
- }}
537
- menu={{
538
- builtin: {
539
- copy: { enabled: false, getValue: () => "" },
540
- delete: { enabled: false, onDelete: () => { } },
541
- },
542
- items: []
543
- }}
544
- input={{ style: { border: "none", padding: "8px 0 0" } }}
545
- />
546
-
547
- {
548
- numericColumns.length === 0 &&
549
- <div className={Styles.ns_chart_hint}>No numeric columns available.</div>
550
- }
551
-
552
- {
553
- isSingleSeries &&
554
- <div className={Styles.ns_chart_hint}>Doughnut charts use a single series.</div>
555
- }
556
-
557
- <NSBoxBoolean
558
- title="Only selected rows "
559
- required={false}
560
- defaultValue={false}
561
- hideHeader={true}
562
- onChanged={(box) =>
563
- {
564
- let nextSource: NSTableChartRowSource = box.getValue(false) === true ? "selected" : "page";
565
- if (nextSource !== this.state.rowSource)
566
- this.setState({ rowSource: nextSource });
567
- }}
568
- />
569
-
570
- <NSButtonBlue
571
- title="Download PNG"
572
- onClick={{
573
- action: (onFinished) =>
574
- {
575
- this.download();
576
- onFinished();
577
- },
578
- showLoading: false,
579
- }}
580
- style={{ minHeight: 48, marginTop: "auto" }}
581
- />
582
- </NSColumn>
583
- </div>
584
- </div>
585
- </div>
586
- );
587
- }
1
+ "use client";
2
+ import { IStorageLocal, TimeOperation } from "namirasoft-core";
3
+ import { Component, createRef } from "react";
4
+ import { IBaseComponentProps } from "../props/IBaseComponentProps";
5
+ import { NSBoxBoolean } from "./NSBoxBoolean";
6
+ import { NSBoxCombo } from "./NSBoxCombo";
7
+ import { NSBoxOptions } from "./NSBoxOptions";
8
+ import { NSButton } from "./NSButton";
9
+ import { NSButtonBlue } from "./NSButtonBlue";
10
+ import { NSChartBar, NSChartBarMode } from "./NSChartBar";
11
+ import { NSChartDoughnut } from "./NSChartDoughnut";
12
+ import { NSChartLine } from "./NSChartLine";
13
+ import { NSColumn } from "./NSColumn";
14
+ import { NSNoData } from "./NSNoData";
15
+ import { TableColumnInfo } from "./NSTable";
16
+ import Styles from "./NSTableChart.module.css";
17
+
18
+ export type NSTableChartType = "bar" | "hbar" | "line" | "area" | "doughnut";
19
+ export type NSTableChartRowSource = "page" | "selected";
20
+ export type NSTableChartAggregate = "sum" | "average" | "count" | "min" | "max";
21
+ export type NSTableChartDateGroup = "exact" | "hourly" | "daily" | "weekly" | "monthly" | "yearly";
22
+
23
+ const CHART_TYPES: { type: NSTableChartType, text: string }[] = [
24
+ { type: "bar", text: "Bar" },
25
+ { type: "hbar", text: "Horizontal Bar" },
26
+ { type: "line", text: "Line" },
27
+ { type: "area", text: "Area" },
28
+ { type: "doughnut", text: "Doughnut" },
29
+ ];
30
+
31
+ const BAR_MODES: { mode: NSChartBarMode, text: string }[] = [
32
+ { mode: "grouped", text: "Grouped" },
33
+ { mode: "stacked", text: "Stacked" },
34
+ { mode: "percent", text: "Percent" },
35
+ ];
36
+
37
+ const AGGREGATES: { value: NSTableChartAggregate, text: string }[] = [
38
+ { value: "sum", text: "Sum" },
39
+ { value: "average", text: "Average" },
40
+ { value: "count", text: "Count" },
41
+ { value: "min", text: "Min" },
42
+ { value: "max", text: "Max" },
43
+ ];
44
+
45
+ const DATE_GROUPS: { value: NSTableChartDateGroup, text: string }[] = [
46
+ { value: "exact", text: "Exact" },
47
+ { value: "hourly", text: "Hourly" },
48
+ { value: "daily", text: "Daily" },
49
+ { value: "weekly", text: "Weekly" },
50
+ { value: "monthly", text: "Monthly" },
51
+ { value: "yearly", text: "Yearly" },
52
+ ];
53
+
54
+ const NUMERIC_TYPES = ["number", "integer", "int", "float", "double", "decimal", "long", "money", "percent", "percentage", "bytes", "duration"];
55
+
56
+ export interface NSTableChartProps<RowType> extends IBaseComponentProps
57
+ {
58
+ name: string;
59
+ columns: TableColumnInfo[];
60
+ rows: RowType[];
61
+ selectedRows: RowType[];
62
+ onClose: () => void;
63
+ }
64
+
65
+ interface NSTableChartState
66
+ {
67
+ chartType: NSTableChartType;
68
+ barMode: NSChartBarMode;
69
+ aggregate: NSTableChartAggregate;
70
+ dateGroup: NSTableChartDateGroup;
71
+ rowSource: NSTableChartRowSource;
72
+ xColumn: string;
73
+ yColumns: string[];
74
+ }
75
+
76
+ interface NSTableChartSettings
77
+ {
78
+ chartType?: NSTableChartType;
79
+ barMode?: NSChartBarMode;
80
+ aggregate?: NSTableChartAggregate;
81
+ dateGroup?: NSTableChartDateGroup;
82
+ rowSource?: NSTableChartRowSource;
83
+ xColumn?: string;
84
+ yColumns?: string[];
85
+ }
86
+
87
+ export class NSTableChart<RowType> extends Component<NSTableChartProps<RowType>, NSTableChartState>
88
+ {
89
+ private chartContainer = createRef<HTMLDivElement>();
90
+ private previousBodyOverflow = "";
91
+ private Storage = new IStorageLocal();
92
+
93
+ constructor(props: NSTableChartProps<RowType>)
94
+ {
95
+ super(props);
96
+ let numeric = this.getNumericColumns();
97
+ let nonNumeric = props.columns.filter(column => !this.isNumericColumn(column));
98
+ let defaultX = (nonNumeric[0] ?? props.columns[0])?.name ?? "";
99
+ let defaultY = numeric.length > 0 ? [numeric[0].name] : [];
100
+
101
+ let saved = this.loadSettings();
102
+ let chartType: NSTableChartType = CHART_TYPES.some(item => item.type === saved?.chartType) ? saved!.chartType! : "bar";
103
+ let barMode: NSChartBarMode = BAR_MODES.some(item => item.mode === saved?.barMode) ? saved!.barMode! : "grouped";
104
+ let aggregate: NSTableChartAggregate = AGGREGATES.some(item => item.value === saved?.aggregate) ? saved!.aggregate! : "sum";
105
+ let dateGroup: NSTableChartDateGroup = DATE_GROUPS.some(item => item.value === saved?.dateGroup) ? saved!.dateGroup! : "exact";
106
+ let rowSource: NSTableChartRowSource = saved?.rowSource === "selected" ? "selected" : "page";
107
+ let xColumn = (saved?.xColumn && this.getColumn(saved.xColumn)) ? saved.xColumn : defaultX;
108
+ let savedY = (saved?.yColumns ?? []).filter((name: string) => numeric.some(column => column.name === name));
109
+ let yColumns = savedY.length > 0 ? savedY : defaultY;
110
+ if (chartType === "doughnut" && yColumns.length > 1)
111
+ yColumns = [yColumns[0]];
112
+
113
+ this.state = {
114
+ chartType,
115
+ barMode,
116
+ aggregate,
117
+ dateGroup,
118
+ rowSource,
119
+ xColumn,
120
+ yColumns,
121
+ };
122
+ this.download = this.download.bind(this);
123
+ }
124
+
125
+ override componentDidMount()
126
+ {
127
+ this.previousBodyOverflow = document.body.style.overflow;
128
+ document.body.style.overflow = "hidden";
129
+ }
130
+
131
+ override componentWillUnmount()
132
+ {
133
+ document.body.style.overflow = this.previousBodyOverflow;
134
+ }
135
+
136
+ override componentDidUpdate(_prevProps: NSTableChartProps<RowType>, prevState: NSTableChartState)
137
+ {
138
+ if (
139
+ prevState.chartType !== this.state.chartType ||
140
+ prevState.barMode !== this.state.barMode ||
141
+ prevState.aggregate !== this.state.aggregate ||
142
+ prevState.dateGroup !== this.state.dateGroup ||
143
+ prevState.rowSource !== this.state.rowSource ||
144
+ prevState.xColumn !== this.state.xColumn ||
145
+ prevState.yColumns !== this.state.yColumns
146
+ )
147
+ this.saveSettings();
148
+ }
149
+
150
+ private settingsKey(): string
151
+ {
152
+ return `ns_table_chart_${this.props.name}`;
153
+ }
154
+
155
+ private loadSettings(): NSTableChartSettings | null
156
+ {
157
+ try
158
+ {
159
+ let raw = this.Storage.get(this.settingsKey(), "");
160
+ return raw ? JSON.parse(raw) as NSTableChartSettings : null;
161
+ }
162
+ catch (e)
163
+ {
164
+ return null;
165
+ }
166
+ }
167
+
168
+ private saveSettings(): void
169
+ {
170
+ try
171
+ {
172
+ let { chartType, barMode, aggregate, dateGroup, rowSource, xColumn, yColumns } = this.state;
173
+ this.Storage.set(this.settingsKey(), JSON.stringify({ chartType, barMode, aggregate, dateGroup, rowSource, xColumn, yColumns }));
174
+ }
175
+ catch (e) { }
176
+ }
177
+
178
+ private isNumericColumn(column: TableColumnInfo): boolean
179
+ {
180
+ return NUMERIC_TYPES.includes((column.type ?? "").toLowerCase());
181
+ }
182
+
183
+ private isDateColumn(column: TableColumnInfo | undefined): boolean
184
+ {
185
+ return !!column && /date/.test((column.type ?? "").toLowerCase());
186
+ }
187
+
188
+ private isDateTimeColumn(column: TableColumnInfo | undefined): boolean
189
+ {
190
+ return !!column && (column.type ?? "").toLowerCase().includes("datetime");
191
+ }
192
+
193
+ private dateBucket(date: Date, group: NSTableChartDateGroup): Date
194
+ {
195
+ switch (group)
196
+ {
197
+ case "hourly": return TimeOperation.beginningOfHour(date);
198
+ case "daily": return TimeOperation.beginningOfDay(date);
199
+ case "weekly":
200
+ {
201
+ let start = TimeOperation.beginningOfDay(date);
202
+ let offset = (start.getDay() + 6) % 7;
203
+ return TimeOperation.daysAgo(offset, start);
204
+ }
205
+ case "monthly": return TimeOperation.beginningOfMonth(date);
206
+ case "yearly": return TimeOperation.beginningOfYear(date);
207
+ default: return date;
208
+ }
209
+ }
210
+
211
+ private formatBucket(date: Date, group: NSTableChartDateGroup): string
212
+ {
213
+ switch (group)
214
+ {
215
+ case "hourly": return TimeOperation.format(date, "YYYY/MM/DD HH:00");
216
+ case "daily": return TimeOperation.format(date, "YYYY/MM/DD");
217
+ case "weekly":
218
+ {
219
+ let { year, week } = this.isoWeek(date);
220
+ return `${year}-W${String(week).padStart(2, "0")}`;
221
+ }
222
+ case "monthly": return TimeOperation.format(date, "YYYY/MM");
223
+ case "yearly": return TimeOperation.format(date, "YYYY");
224
+ default: return TimeOperation.format(date, "YYYY/MM/DD HH:mm:ss");
225
+ }
226
+ }
227
+
228
+ private isoWeek(monday: Date): { year: number, week: number }
229
+ {
230
+ let thursday = new Date(monday.getTime());
231
+ thursday.setDate(thursday.getDate() + 3);
232
+ let year = thursday.getFullYear();
233
+ let firstThursday = new Date(year, 0, 4);
234
+ let firstThursdayOffset = (firstThursday.getDay() + 6) % 7;
235
+ firstThursday.setDate(firstThursday.getDate() - firstThursdayOffset + 3);
236
+ let week = 1 + Math.round((thursday.getTime() - firstThursday.getTime()) / (7 * 86400000));
237
+ return { year, week };
238
+ }
239
+
240
+ private getLabelParts(row: RowType, xCol: TableColumnInfo | undefined): { key: string, display: string }
241
+ {
242
+ if (this.isDateColumn(xCol) && this.state.dateGroup !== "exact")
243
+ {
244
+ let raw = (row as any)[xCol!.name];
245
+ let date = new Date(raw);
246
+ if (raw && !isNaN(date.getTime()))
247
+ {
248
+ let bucket = this.dateBucket(date, this.state.dateGroup);
249
+ return { key: String(bucket.getTime()), display: this.formatBucket(bucket, this.state.dateGroup) };
250
+ }
251
+ return { key: "", display: "" };
252
+ }
253
+ return { key: this.labelOf(row, xCol), display: this.formatLabel(row, xCol) };
254
+ }
255
+
256
+ private getNumericColumns(): TableColumnInfo[]
257
+ {
258
+ return this.props.columns.filter(column => this.isNumericColumn(column));
259
+ }
260
+
261
+ private getColumn(name: string): TableColumnInfo | undefined
262
+ {
263
+ return this.props.columns.find(column => column.name === name);
264
+ }
265
+
266
+ private getTableTitle(): string
267
+ {
268
+ return this.props.columns[0]?.table?.text ?? this.props.name;
269
+ }
270
+
271
+ private getActiveRows(): RowType[]
272
+ {
273
+ return this.state.rowSource === "selected" ? this.props.selectedRows : this.props.rows;
274
+ }
275
+
276
+ private toNumber(value: any): number
277
+ {
278
+ let n = Number(value);
279
+ return isFinite(n) ? n : 0;
280
+ }
281
+
282
+ private labelOf(row: RowType, column: TableColumnInfo | undefined): string
283
+ {
284
+ if (!column)
285
+ return "";
286
+ let value = (row as any)[column.name];
287
+ if (value === null || value === undefined)
288
+ return "";
289
+ return String(value);
290
+ }
291
+
292
+ private aggregate(rows: RowType[], xCol: TableColumnInfo | undefined, yCols: TableColumnInfo[]): { labels: string[], series: number[][] }
293
+ {
294
+ let keys: string[] = [];
295
+ let displays: string[] = [];
296
+ let indexByKey = new Map<string, number>();
297
+ let stats: { sum: number, count: number, min: number, max: number }[][] = yCols.map(() => []);
298
+ rows.forEach(row =>
299
+ {
300
+ let { key, display } = this.getLabelParts(row, xCol);
301
+ let index = indexByKey.get(key);
302
+ if (index === undefined)
303
+ {
304
+ index = keys.length;
305
+ indexByKey.set(key, index);
306
+ keys.push(key);
307
+ displays.push(display);
308
+ stats.forEach(serie => serie[index as number] = { sum: 0, count: 0, min: Infinity, max: -Infinity });
309
+ }
310
+ yCols.forEach((column, s) =>
311
+ {
312
+ let raw = (row as any)[column.name];
313
+ if (raw === null || raw === undefined || raw === "")
314
+ return;
315
+ let value = this.toNumber(raw);
316
+ let cell = stats[s][index as number];
317
+ cell.sum += value;
318
+ cell.count += 1;
319
+ cell.min = Math.min(cell.min, value);
320
+ cell.max = Math.max(cell.max, value);
321
+ });
322
+ });
323
+
324
+ let series = stats.map(serie => serie.map(cell => this.applyAggregate(cell)));
325
+
326
+ let order = keys.map((_, index) => index);
327
+ order.sort((a, b) => this.compareLabels(keys[a], keys[b], xCol));
328
+ return {
329
+ labels: order.map(index => displays[index]),
330
+ series: series.map(serie => order.map(index => serie[index])),
331
+ };
332
+ }
333
+
334
+ private formatLabel(row: RowType, column: TableColumnInfo | undefined): string
335
+ {
336
+ if (!column)
337
+ return "";
338
+ let value = (row as any)[column.name];
339
+ if (value === null || value === undefined || value === "")
340
+ return "";
341
+ try
342
+ {
343
+ let out = (column.formatter as any)?.format(value);
344
+ if (typeof out === "string")
345
+ return out;
346
+ }
347
+ catch (e)
348
+ {
349
+ }
350
+ return String(value);
351
+ }
352
+
353
+ private applyAggregate(cell: { sum: number, count: number, min: number, max: number }): number
354
+ {
355
+ switch (this.state.aggregate)
356
+ {
357
+ case "sum":
358
+ return cell.sum;
359
+ case "count":
360
+ return cell.count;
361
+ case "average":
362
+ return cell.count ? cell.sum / cell.count : 0;
363
+ case "min":
364
+ return cell.count ? cell.min : 0;
365
+ case "max":
366
+ return cell.count ? cell.max : 0;
367
+ default:
368
+ return cell.sum;
369
+ }
370
+ }
371
+
372
+ private formatValueFor(yCols: TableColumnInfo[]): (value: number) => string
373
+ {
374
+ let aggregate = this.state.aggregate;
375
+ let formatter = yCols[0]?.formatter;
376
+ return (value: number) =>
377
+ {
378
+ if (aggregate === "count")
379
+ return Math.round(value).toLocaleString();
380
+ try
381
+ {
382
+ let out = (formatter as any)?.format(value);
383
+ if (typeof out === "string")
384
+ return out;
385
+ }
386
+ catch (e) { }
387
+ return value.toLocaleString();
388
+ };
389
+ }
390
+
391
+ private compareLabels(a: string, b: string, xCol: TableColumnInfo | undefined): number
392
+ {
393
+ if (xCol && this.isNumericColumn(xCol))
394
+ return this.toNumber(a) - this.toNumber(b);
395
+ if (xCol && /(date|time)/.test((xCol.type ?? "").toLowerCase()))
396
+ {
397
+ let da = Date.parse(a);
398
+ let db = Date.parse(b);
399
+ if (!isNaN(da) && !isNaN(db))
400
+ return da - db;
401
+ }
402
+ return a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" });
403
+ }
404
+
405
+ private download()
406
+ {
407
+ let container = this.chartContainer.current;
408
+ if (!container)
409
+ return;
410
+ let canvas = container.querySelector("canvas");
411
+ if (!canvas)
412
+ return;
413
+ let link = document.createElement("a");
414
+ link.href = canvas.toDataURL("image/png");
415
+ link.download = this.props.name + "_chart.png";
416
+ link.click();
417
+ }
418
+
419
+ private renderChart()
420
+ {
421
+ let { chartType, xColumn, yColumns } = this.state;
422
+ let xCol = this.getColumn(xColumn);
423
+ let yCols = yColumns.map(name => this.getColumn(name)).filter(Boolean) as TableColumnInfo[];
424
+
425
+ let rows = this.getActiveRows();
426
+
427
+ if (rows.length === 0)
428
+ return <NSNoData lable={this.state.rowSource === "selected" ? "No selected rows" : "No Data"} />;
429
+ if (yCols.length === 0)
430
+ return <div className={Styles.ns_chart_message}>Select at least one numeric series for the Y-axis.</div>;
431
+
432
+ let { labels, series } = this.aggregate(rows, xCol, yCols);
433
+
434
+ let dataset = yCols.map((column, s) => ({ label: column.text, data: series[s] }));
435
+ let formatValue = this.formatValueFor(yCols);
436
+
437
+ if (chartType === "bar" || chartType === "hbar")
438
+ {
439
+ let mode: NSChartBarMode = yCols.length > 1 ? this.state.barMode : "grouped";
440
+ let max = 0;
441
+ labels.forEach((_, index) =>
442
+ {
443
+ if (mode === "stacked")
444
+ {
445
+ let sum = 0;
446
+ dataset.forEach(item => sum += item.data[index] ?? 0);
447
+ max = Math.max(max, sum);
448
+ }
449
+ else
450
+ dataset.forEach(item => max = Math.max(max, item.data[index] ?? 0));
451
+ });
452
+ max = max <= 0 ? 1 : Math.ceil(max * 1.1);
453
+ return (
454
+ <NSChartBar
455
+ labels={labels}
456
+ dataset={dataset}
457
+ range={{ min: 0, max }}
458
+ mode={mode}
459
+ horizontal={chartType === "hbar"}
460
+ xTitle={xCol?.text}
461
+ formatValue={formatValue}
462
+ yGrid
463
+ style={{ width: "100%" }}
464
+ />
465
+ );
466
+ }
467
+
468
+ if (chartType === "line" || chartType === "area")
469
+ {
470
+ let max = 0;
471
+ dataset.forEach(item => item.data.forEach(value => max = Math.max(max, value)));
472
+ max = max <= 0 ? 1 : Math.ceil(max * 1.1);
473
+ return (
474
+ <NSChartLine
475
+ labels={labels}
476
+ dataset={dataset}
477
+ range={{ min: 0, max }}
478
+ area={chartType === "area"}
479
+ xTitle={xCol?.text}
480
+ formatValue={formatValue}
481
+ yGrid
482
+ style={{ width: "100%" }}
483
+ />
484
+ );
485
+ }
486
+
487
+ let datas = series[0];
488
+
489
+ return (
490
+ <NSChartDoughnut
491
+ title={yCols[0]?.text ?? ""}
492
+ labels={labels}
493
+ datas={datas}
494
+ labelTitle={xCol?.text}
495
+ valueTitle={yCols[0]?.text}
496
+ formatValue={formatValue}
497
+ style={{ width: "100%", height: "100%", display: "flex", justifyContent: "center" }}
498
+ />
499
+ );
500
+ }
501
+
502
+ override render()
503
+ {
504
+ let { chartType, barMode, dateGroup, xColumn, yColumns } = this.state;
505
+ let numericColumns = this.getNumericColumns();
506
+ let isSingleSeries = chartType === "doughnut";
507
+ let xCol = this.getColumn(xColumn);
508
+ let xColIsDate = this.isDateColumn(xCol);
509
+ let xColIsDateTime = this.isDateTimeColumn(xCol);
510
+ let dateGroupOptions = DATE_GROUPS.filter(item => item.value !== "hourly" || xColIsDateTime);
511
+
512
+ let labelAxisTitle = chartType === "doughnut" ? "Category" : chartType === "hbar" ? "Label (Y-Axis)" : "Label (X-Axis)";
513
+ let seriesAxisTitle = chartType === "doughnut" ? "Value" : chartType === "hbar" ? "Series (X-Axis)" : "Series (Y-Axis)";
514
+
515
+ return (
516
+ <div
517
+ id={this.props.id}
518
+ className={[Styles.ns_chart_overlay, ...(this.props.classList ?? [])].join(" ")}
519
+ style={this.props.style}
520
+ >
521
+ <div className={Styles.ns_chart_modal}>
522
+ <div className={Styles.ns_chart_header}>
523
+ <span className={Styles.ns_chart_title}>
524
+ Chart{this.getTableTitle() ? ` - ${this.getTableTitle()}` : ""}
525
+ </span>
526
+ <NSButton
527
+ icon={{ src: "https://static.namirasoft.com/image/concept/close/blue.svg", alt: "close" }}
528
+ onClick={{
529
+ action: this.props.onClose,
530
+ showLoading: false
531
+ }}
532
+ style={{ width: 24, height: 24, boxShadow: "none" }}
533
+ />
534
+ </div>
535
+ <div className={Styles.ns_chart_body}>
536
+ <div className={Styles.ns_chart_canvas} ref={this.chartContainer}>
537
+ {this.renderChart()}
538
+ </div>
539
+ <NSColumn classList={[Styles.ns_chart_panel]}>
540
+ <NSBoxCombo
541
+ title="Chart Type"
542
+ required={false}
543
+ multiple={false}
544
+ searchable={false}
545
+ clearable={false}
546
+ reloadable={false}
547
+ defaultValue={chartType}
548
+ getOptions={() => CHART_TYPES.map(item => ({ value: item.type, title: item.text }))}
549
+ onChanged={(box) =>
550
+ {
551
+ let value = box.getValue(false) as string | null;
552
+ if (!value)
553
+ return;
554
+ let nextType = value as NSTableChartType;
555
+ let nextY = nextType === "doughnut" && yColumns.length > 1
556
+ ? [yColumns[0]]
557
+ : yColumns;
558
+ this.setState({ chartType: nextType, yColumns: nextY });
559
+ }}
560
+ menu={{
561
+ builtin: {
562
+ copy: { enabled: false, getValue: () => "" },
563
+ delete: { enabled: false, onDelete: () => { } },
564
+ },
565
+ items: []
566
+ }}
567
+ />
568
+
569
+ {
570
+ (chartType === "bar" || chartType === "hbar") && yColumns.length > 1 &&
571
+ <NSBoxOptions
572
+ key="bar-mode"
573
+ title=""
574
+ hideHeader={true}
575
+ multiple={false}
576
+ required={false}
577
+ defaultValue={barMode}
578
+ options={BAR_MODES.map(item => ({ value: item.mode, title: item.text }))}
579
+ onChanged={(box) =>
580
+ {
581
+ let value = box.getValue(false) as string | null;
582
+ if (value)
583
+ this.setState({ barMode: value as NSChartBarMode });
584
+ }}
585
+ menu={{
586
+ builtin: {
587
+ copy: { enabled: false, getValue: () => "" },
588
+ delete: { enabled: false, onDelete: () => { } },
589
+ },
590
+ items: []
591
+ }}
592
+ input={{ style: { minHeight: "auto", border: "none", padding: "0" } }}
593
+ />
594
+ }
595
+
596
+ <NSBoxCombo
597
+ title={labelAxisTitle}
598
+ multiple={false}
599
+ required={false}
600
+ reloadable={false}
601
+ clearable={false}
602
+ defaultValue={xColumn}
603
+ getOptions={() => this.props.columns.map(column => ({ value: column.name, title: column.text }))}
604
+ onChanged={(box) =>
605
+ {
606
+ let value = box.getValue(false) as string | null;
607
+ if (value)
608
+ this.setState({ xColumn: value });
609
+ }}
610
+ menu={{
611
+ builtin: {
612
+ copy: { enabled: false, getValue: () => "" },
613
+ delete: { enabled: false, onDelete: () => { } },
614
+ },
615
+ items: []
616
+ }}
617
+ />
618
+
619
+ {
620
+ xColIsDate &&
621
+ <NSBoxOptions
622
+ key="date-group"
623
+ title=""
624
+ hideHeader={true}
625
+ multiple={false}
626
+ required={false}
627
+ defaultValue={dateGroup}
628
+ options={dateGroupOptions.map(item => ({ value: item.value, title: item.text }))}
629
+ onChanged={(box) =>
630
+ {
631
+ let value = box.getValue(false) as string | null;
632
+ if (value)
633
+ this.setState({ dateGroup: value as NSTableChartDateGroup });
634
+ }}
635
+ menu={{
636
+ builtin: {
637
+ copy: { enabled: false, getValue: () => "" },
638
+ delete: { enabled: false, onDelete: () => { } },
639
+ },
640
+ items: []
641
+ }}
642
+ input={{ style: { minHeight: "auto", border: "none", padding: "0" } }}
643
+ />
644
+ }
645
+
646
+ <NSBoxCombo
647
+ key={isSingleSeries ? "y-single" : "y-multi"}
648
+ title={seriesAxisTitle}
649
+ multiple={!isSingleSeries}
650
+ required={false}
651
+ clearable={false}
652
+ reloadable={false}
653
+ defaultValue={isSingleSeries ? yColumns[0] : yColumns}
654
+ getOptions={() => numericColumns.map(column => ({ value: column.name, title: column.text }))}
655
+ onChanged={(box) =>
656
+ {
657
+ let value = box.getValue(false);
658
+ let next = Array.isArray(value) ? value : (value ? [value] : []);
659
+ this.setState({ yColumns: next });
660
+ }}
661
+ menu={{
662
+ builtin: {
663
+ copy: { enabled: false, getValue: () => "" },
664
+ delete: { enabled: false, onDelete: () => { } },
665
+ },
666
+ items: []
667
+ }}
668
+ />
669
+
670
+ <NSBoxOptions
671
+ title="Aggregate"
672
+ multiple={false}
673
+ required={false}
674
+ defaultValue={this.state.aggregate}
675
+ options={AGGREGATES.map(item => ({ value: item.value, title: item.text }))}
676
+ onChanged={(box) =>
677
+ {
678
+ let value = box.getValue(false) as string | null;
679
+ if (value)
680
+ this.setState({ aggregate: value as NSTableChartAggregate });
681
+ }}
682
+ menu={{
683
+ builtin: {
684
+ copy: { enabled: false, getValue: () => "" },
685
+ delete: { enabled: false, onDelete: () => { } },
686
+ },
687
+ items: []
688
+ }}
689
+ input={{ style: { border: "none", padding: "8px 0 0" } }}
690
+ />
691
+
692
+ {
693
+ numericColumns.length === 0 &&
694
+ <div className={Styles.ns_chart_hint}>No numeric columns available.</div>
695
+ }
696
+
697
+ {
698
+ isSingleSeries &&
699
+ <div className={Styles.ns_chart_hint}>Doughnut charts use a single series.</div>
700
+ }
701
+
702
+ <NSBoxBoolean
703
+ title="Only selected rows "
704
+ required={false}
705
+ defaultValue={false}
706
+ hideHeader={true}
707
+ onChanged={(box) =>
708
+ {
709
+ let nextSource: NSTableChartRowSource = box.getValue(false) === true ? "selected" : "page";
710
+ if (nextSource !== this.state.rowSource)
711
+ this.setState({ rowSource: nextSource });
712
+ }}
713
+ />
714
+
715
+ <NSButtonBlue
716
+ title="Download PNG"
717
+ onClick={{
718
+ action: (onFinished) =>
719
+ {
720
+ this.download();
721
+ onFinished();
722
+ },
723
+ showLoading: false,
724
+ }}
725
+ style={{ minHeight: 48, marginTop: "auto" }}
726
+ />
727
+ </NSColumn>
728
+ </div>
729
+ </div>
730
+ </div>
731
+ );
732
+ }
588
733
  }