namirasoft-site-react 1.4.575 → 1.4.577

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 (485) 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.css +33 -33
  7. package/dist/App.d.ts +1 -1
  8. package/dist/NSBoxBuilder.d.ts +25 -25
  9. package/dist/components/NSBanner.d.ts +1 -1
  10. package/dist/components/NSBanner.module.css +47 -47
  11. package/dist/components/NSBarAction.d.ts +1 -1
  12. package/dist/components/NSBarAction.module.css +91 -91
  13. package/dist/components/NSBarAlert.d.ts +1 -1
  14. package/dist/components/NSBarAlert.module.css +79 -79
  15. package/dist/components/NSBarNotification.d.ts +1 -1
  16. package/dist/components/NSBarNotification.module.css +34 -34
  17. package/dist/components/NSBarTitle.d.ts +1 -1
  18. package/dist/components/NSBarTitle.module.css +9 -9
  19. package/dist/components/NSBox.module.css +87 -87
  20. package/dist/components/NSBoxBaseCombo.d.ts +1 -1
  21. package/dist/components/NSBoxBaseCombo.module.css +6 -6
  22. package/dist/components/NSBoxBaseLayout.d.ts +1 -1
  23. package/dist/components/NSBoxBaseLayoutRecursive.d.ts +1 -1
  24. package/dist/components/NSBoxBoolean.d.ts +1 -1
  25. package/dist/components/NSBoxBoolean.module.css +73 -73
  26. package/dist/components/NSBoxBooleans.d.ts +1 -1
  27. package/dist/components/NSBoxColor.d.ts +1 -1
  28. package/dist/components/NSBoxCombo.d.ts +1 -1
  29. package/dist/components/NSBoxDate.d.ts +1 -1
  30. package/dist/components/NSBoxDate.module.css +4 -4
  31. package/dist/components/NSBoxDateRange.d.ts +1 -1
  32. package/dist/components/NSBoxDateRangeBase.d.ts +1 -1
  33. package/dist/components/NSBoxDateRangeBase.module.css +73 -73
  34. package/dist/components/NSBoxDateTime.d.ts +1 -1
  35. package/dist/components/NSBoxDateTime.module.css +4 -4
  36. package/dist/components/NSBoxDateTimeRange.d.ts +1 -1
  37. package/dist/components/NSBoxDouble.d.ts +1 -1
  38. package/dist/components/NSBoxDuration.d.ts +1 -1
  39. package/dist/components/NSBoxDuration.module.css +4 -4
  40. package/dist/components/NSBoxDynamic.d.ts +1 -1
  41. package/dist/components/NSBoxDynamics.d.ts +1 -1
  42. package/dist/components/NSBoxEmail.d.ts +1 -1
  43. package/dist/components/NSBoxEntity.d.ts +1 -1
  44. package/dist/components/NSBoxEnum.d.ts +1 -1
  45. package/dist/components/NSBoxFile.d.ts +1 -1
  46. package/dist/components/NSBoxFile.module.css +10 -10
  47. package/dist/components/NSBoxFilePath.d.ts +1 -1
  48. package/dist/components/NSBoxFont.d.ts +1 -1
  49. package/dist/components/NSBoxIPV4.d.ts +1 -1
  50. package/dist/components/NSBoxIPV4Range.d.ts +1 -1
  51. package/dist/components/NSBoxIPV6.d.ts +1 -1
  52. package/dist/components/NSBoxIPV6Range.d.ts +1 -1
  53. package/dist/components/NSBoxInteger.d.ts +1 -1
  54. package/dist/components/NSBoxInteger.module.css +16 -16
  55. package/dist/components/NSBoxMoney.d.ts +1 -1
  56. package/dist/components/NSBoxOptions.d.ts +1 -1
  57. package/dist/components/NSBoxOptions.module.css +53 -53
  58. package/dist/components/NSBoxPhone.d.ts +1 -1
  59. package/dist/components/NSBoxPhone.module.css +35 -35
  60. package/dist/components/NSBoxRadio.d.ts +1 -1
  61. package/dist/components/NSBoxRadio.module.css +33 -33
  62. package/dist/components/NSBoxSchemaBase.d.ts +1 -1
  63. package/dist/components/NSBoxSchemaBase.module.css +17 -17
  64. package/dist/components/NSBoxSchemaVariable.d.ts +1 -1
  65. package/dist/components/NSBoxSchemaVariable.module.css +16 -16
  66. package/dist/components/NSBoxSearch.d.ts +1 -1
  67. package/dist/components/NSBoxSensitive.d.ts +1 -1
  68. package/dist/components/NSBoxString.d.ts +1 -1
  69. package/dist/components/NSBoxTextArea.d.ts +1 -1
  70. package/dist/components/NSBoxTextArea.module.css +12 -12
  71. package/dist/components/NSBoxTime.d.ts +1 -1
  72. package/dist/components/NSBoxTime.module.css +4 -4
  73. package/dist/components/NSBoxTimeRange.d.ts +1 -1
  74. package/dist/components/NSBoxTimeZone.d.ts +1 -1
  75. package/dist/components/NSBoxURL.d.ts +1 -1
  76. package/dist/components/NSBoxVersion.d.ts +1 -1
  77. package/dist/components/NSButton.module.css +57 -57
  78. package/dist/components/NSButtonBlue.d.ts +1 -1
  79. package/dist/components/NSButtonBlue.module.css +4 -4
  80. package/dist/components/NSButtonGreen.d.ts +1 -1
  81. package/dist/components/NSButtonGreen.module.css +4 -4
  82. package/dist/components/NSButtonRed.d.ts +1 -1
  83. package/dist/components/NSButtonRed.module.css +4 -4
  84. package/dist/components/NSCard.d.ts +1 -1
  85. package/dist/components/NSCard.module.css +113 -113
  86. package/dist/components/NSCardScreenshot.d.ts +1 -1
  87. package/dist/components/NSCardScreenshot.module.css +41 -41
  88. package/dist/components/NSChartBar.d.ts +1 -1
  89. package/dist/components/NSChartDoughnut.d.ts +1 -1
  90. package/dist/components/NSChartLine.d.ts +1 -1
  91. package/dist/components/NSChartPie.d.ts +1 -1
  92. package/dist/components/NSChartRange.d.ts +1 -1
  93. package/dist/components/NSColumn.d.ts +1 -1
  94. package/dist/components/NSColumn.module.css +16 -16
  95. package/dist/components/NSCopy.d.ts +6 -3
  96. package/dist/components/NSCopy.js +45 -12
  97. package/dist/components/NSCopy.js.map +1 -1
  98. package/dist/components/NSCopyBox.d.ts +1 -1
  99. package/dist/components/NSCopyBox.module.css +39 -39
  100. package/dist/components/NSDialog.d.ts +1 -1
  101. package/dist/components/NSDialog.module.css +105 -105
  102. package/dist/components/NSDialogBox.d.ts +1 -1
  103. package/dist/components/NSDialogBoxDate.d.ts +1 -1
  104. package/dist/components/NSDialogBoxDateTime.d.ts +1 -1
  105. package/dist/components/NSDialogBoxPhone.d.ts +1 -1
  106. package/dist/components/NSDialogBoxString.d.ts +1 -1
  107. package/dist/components/NSDialogDelete.d.ts +1 -1
  108. package/dist/components/NSDialogInfo.d.ts +1 -1
  109. package/dist/components/NSDialogPageSelection.d.ts +2 -2
  110. package/dist/components/NSDialogPageSelection.module.css +103 -103
  111. package/dist/components/NSDownTimer.d.ts +1 -1
  112. package/dist/components/NSDownTimer.module.css +55 -55
  113. package/dist/components/NSDownload.d.ts +1 -1
  114. package/dist/components/NSDownload.module.css +46 -46
  115. package/dist/components/NSElectronicCard.d.ts +1 -1
  116. package/dist/components/NSElectronicCard.module.css +60 -60
  117. package/dist/components/NSEntityCardBackground.d.ts +1 -1
  118. package/dist/components/NSEntityCardBackground.module.css +27 -27
  119. package/dist/components/NSFilterBox.d.ts +2 -2
  120. package/dist/components/NSFilterBox.module.css +27 -27
  121. package/dist/components/NSFilterBoxItems.d.ts +1 -1
  122. package/dist/components/NSFilterBoxItems.module.css +15 -15
  123. package/dist/components/NSFilterItem.d.ts +1 -1
  124. package/dist/components/NSFilterItem.module.css +24 -24
  125. package/dist/components/NSFooter.d.ts +1 -1
  126. package/dist/components/NSFooter.module.css +140 -140
  127. package/dist/components/NSGauge.d.ts +1 -1
  128. package/dist/components/NSGauge.module.css +67 -67
  129. package/dist/components/NSGaugeDate.d.ts +1 -1
  130. package/dist/components/NSGaugeDate.module.css +57 -57
  131. package/dist/components/NSGaugeNumber.d.ts +1 -1
  132. package/dist/components/NSGaugeNumber.module.css +66 -66
  133. package/dist/components/NSHeader.d.ts +1 -1
  134. package/dist/components/NSHeader.module.css +316 -316
  135. package/dist/components/NSHeaderScreenshot.d.ts +1 -1
  136. package/dist/components/NSHeaderScreenshot.module.css +35 -35
  137. package/dist/components/NSID.d.ts +1 -1
  138. package/dist/components/NSInfo.d.ts +1 -1
  139. package/dist/components/NSLabel.d.ts +1 -1
  140. package/dist/components/NSLabel.module.css +25 -25
  141. package/dist/components/NSLabelErrorNotifier.d.ts +1 -1
  142. package/dist/components/NSLabelErrorNotifier.module.css +2 -2
  143. package/dist/components/NSLabelSensitive.d.ts +2 -1
  144. package/dist/components/NSLabelSensitive.js +13 -3
  145. package/dist/components/NSLabelSensitive.js.map +1 -1
  146. package/dist/components/NSLabelSensitive.module.css +9 -9
  147. package/dist/components/NSLayout.d.ts +1 -1
  148. package/dist/components/NSLayout.module.css +22 -22
  149. package/dist/components/NSLine.d.ts +1 -1
  150. package/dist/components/NSLine.module.css +15 -15
  151. package/dist/components/NSLineText.d.ts +1 -1
  152. package/dist/components/NSLineText.module.css +38 -38
  153. package/dist/components/NSLink.d.ts +1 -1
  154. package/dist/components/NSLink.module.css +36 -36
  155. package/dist/components/NSLinkBlue.d.ts +1 -1
  156. package/dist/components/NSLinkGreen.d.ts +1 -1
  157. package/dist/components/NSLinkRed.d.ts +1 -1
  158. package/dist/components/NSListGrouped.d.ts +1 -1
  159. package/dist/components/NSListGrouped.module.css +69 -69
  160. package/dist/components/NSListProduct.d.ts +1 -1
  161. package/dist/components/NSLoading.d.ts +1 -1
  162. package/dist/components/NSLoading.module.css +10 -10
  163. package/dist/components/NSMegaMenu.d.ts +1 -1
  164. package/dist/components/NSMegaMenu.module.css +133 -133
  165. package/dist/components/NSMenuAction.d.ts +1 -1
  166. package/dist/components/NSMenuAction.module.css +88 -88
  167. package/dist/components/NSMenuButton.d.ts +1 -1
  168. package/dist/components/NSMenuButton.module.css +44 -44
  169. package/dist/components/NSNoData.d.ts +1 -1
  170. package/dist/components/NSNoData.module.css +11 -11
  171. package/dist/components/NSPagination.d.ts +1 -1
  172. package/dist/components/NSPagination.module.css +87 -87
  173. package/dist/components/NSPanel.d.ts +1 -1
  174. package/dist/components/NSPanel.module.css +28 -28
  175. package/dist/components/NSPanelAccordion.d.ts +1 -1
  176. package/dist/components/NSPanelAccordion.module.css +4 -4
  177. package/dist/components/NSProductSearch.d.ts +1 -1
  178. package/dist/components/NSProductSearch.module.css +73 -73
  179. package/dist/components/NSProgressBar.d.ts +1 -1
  180. package/dist/components/NSProgressBar.module.css +38 -38
  181. package/dist/components/NSRange.d.ts +1 -1
  182. package/dist/components/NSRange.module.css +66 -66
  183. package/dist/components/NSRepeater.d.ts +1 -1
  184. package/dist/components/NSRepeaterNSBoxSchemaVariable.d.ts +1 -1
  185. package/dist/components/NSRepeaterNSTag.d.ts +1 -1
  186. package/dist/components/NSRow.d.ts +1 -1
  187. package/dist/components/NSRow.module.css +17 -17
  188. package/dist/components/NSSection.d.ts +1 -1
  189. package/dist/components/NSSection.module.css +4 -4
  190. package/dist/components/NSSectionCards.d.ts +1 -1
  191. package/dist/components/NSSectionCards.module.css +38 -38
  192. package/dist/components/NSSectionTiles.d.ts +1 -1
  193. package/dist/components/NSSectionTiles.module.css +10 -10
  194. package/dist/components/NSSectionTitle.d.ts +1 -1
  195. package/dist/components/NSSpace.d.ts +1 -1
  196. package/dist/components/NSSplitter.d.ts +1 -1
  197. package/dist/components/NSSplitter.module.css +59 -59
  198. package/dist/components/NSTabPage.d.ts +1 -1
  199. package/dist/components/NSTabPage.module.css +59 -59
  200. package/dist/components/NSTable.d.ts +1 -1
  201. package/dist/components/NSTable.module.css +371 -371
  202. package/dist/components/NSTableChart.d.ts +1 -1
  203. package/dist/components/NSTableChart.module.css +119 -119
  204. package/dist/components/NSTag.d.ts +1 -1
  205. package/dist/components/NSTile.d.ts +1 -1
  206. package/dist/components/NSTile.module.css +76 -76
  207. package/dist/components/NSTimelineMonthly.d.ts +1 -1
  208. package/dist/components/NSTimelineMonthly.module.css +71 -71
  209. package/dist/components/NSTitle.d.ts +1 -1
  210. package/dist/components/NSTitle.module.css +15 -15
  211. package/dist/formatter/SensitiveFormatter.d.ts +2 -1
  212. package/dist/formatter/SensitiveFormatter.js +3 -2
  213. package/dist/formatter/SensitiveFormatter.js.map +1 -1
  214. package/dist/formatter/StringFormatter.d.ts +1 -1
  215. package/dist/pages/NSImplementing.d.ts +1 -1
  216. package/dist/pages/NSImplementing.module.css +35 -35
  217. package/dist/pages/NSNotFoundPage.d.ts +1 -1
  218. package/dist/pages/NSNotFoundPage.module.css +18 -18
  219. package/dist/pages/NSUpdating.d.ts +1 -1
  220. package/dist/pages/NSUpdating.module.css +35 -35
  221. package/package.json +86 -86
  222. package/public/index.html +38 -38
  223. package/src/App.css +33 -33
  224. package/src/App.tsx +167 -167
  225. package/src/Color.tsx +10 -10
  226. package/src/CopyToClipboard.ts +6 -6
  227. package/src/EncryptionOperation.ts +98 -98
  228. package/src/KeyOperation.ts +109 -109
  229. package/src/LinkOperation.ts +101 -101
  230. package/src/NSBoxBuilder.tsx +922 -922
  231. package/src/ProductCacheService.ts +15 -15
  232. package/src/Validator.ts +199 -199
  233. package/src/components/INSBox.tsx +7 -7
  234. package/src/components/NSBanner.module.css +47 -47
  235. package/src/components/NSBanner.tsx +80 -80
  236. package/src/components/NSBarAction.module.css +91 -91
  237. package/src/components/NSBarAction.tsx +95 -95
  238. package/src/components/NSBarAlert.module.css +79 -79
  239. package/src/components/NSBarAlert.tsx +35 -35
  240. package/src/components/NSBarNotification.module.css +34 -34
  241. package/src/components/NSBarNotification.tsx +89 -89
  242. package/src/components/NSBarTitle.module.css +9 -9
  243. package/src/components/NSBarTitle.tsx +23 -23
  244. package/src/components/NSBox.module.css +87 -87
  245. package/src/components/NSBox.tsx +19 -19
  246. package/src/components/NSBoxBaseCombo.module.css +6 -6
  247. package/src/components/NSBoxBaseCombo.tsx +488 -488
  248. package/src/components/NSBoxBaseLayout.tsx +163 -163
  249. package/src/components/NSBoxBaseLayoutRecursive.tsx +59 -59
  250. package/src/components/NSBoxBoolean.module.css +73 -73
  251. package/src/components/NSBoxBoolean.tsx +152 -152
  252. package/src/components/NSBoxBooleans.tsx +181 -181
  253. package/src/components/NSBoxColor.tsx +107 -107
  254. package/src/components/NSBoxCombo.tsx +95 -95
  255. package/src/components/NSBoxDate.module.css +4 -4
  256. package/src/components/NSBoxDate.tsx +119 -119
  257. package/src/components/NSBoxDateRange.tsx +88 -88
  258. package/src/components/NSBoxDateRangeBase.module.css +73 -73
  259. package/src/components/NSBoxDateRangeBase.tsx +198 -198
  260. package/src/components/NSBoxDateTime.module.css +4 -4
  261. package/src/components/NSBoxDateTime.tsx +116 -116
  262. package/src/components/NSBoxDateTimeRange.tsx +65 -65
  263. package/src/components/NSBoxDouble.tsx +110 -110
  264. package/src/components/NSBoxDuration.module.css +4 -4
  265. package/src/components/NSBoxDuration.tsx +107 -107
  266. package/src/components/NSBoxDynamic.tsx +77 -77
  267. package/src/components/NSBoxDynamics.tsx +68 -68
  268. package/src/components/NSBoxEmail.tsx +117 -117
  269. package/src/components/NSBoxEntity.tsx +181 -181
  270. package/src/components/NSBoxEnum.tsx +113 -113
  271. package/src/components/NSBoxFile.module.css +10 -10
  272. package/src/components/NSBoxFile.tsx +106 -106
  273. package/src/components/NSBoxFilePath.tsx +106 -106
  274. package/src/components/NSBoxFont.tsx +107 -107
  275. package/src/components/NSBoxIPV4.tsx +107 -107
  276. package/src/components/NSBoxIPV4Range.tsx +107 -107
  277. package/src/components/NSBoxIPV6.tsx +104 -104
  278. package/src/components/NSBoxIPV6Range.tsx +107 -107
  279. package/src/components/NSBoxInteger.module.css +16 -16
  280. package/src/components/NSBoxInteger.tsx +126 -126
  281. package/src/components/NSBoxMoney.tsx +115 -115
  282. package/src/components/NSBoxOptions.module.css +53 -53
  283. package/src/components/NSBoxOptions.tsx +182 -182
  284. package/src/components/NSBoxPhone.module.css +35 -35
  285. package/src/components/NSBoxPhone.tsx +120 -120
  286. package/src/components/NSBoxRadio.module.css +33 -33
  287. package/src/components/NSBoxRadio.tsx +104 -104
  288. package/src/components/NSBoxSchemaBase.module.css +17 -17
  289. package/src/components/NSBoxSchemaBase.tsx +562 -562
  290. package/src/components/NSBoxSchemaVariable.module.css +16 -16
  291. package/src/components/NSBoxSchemaVariable.tsx +263 -263
  292. package/src/components/NSBoxSearch.tsx +112 -112
  293. package/src/components/NSBoxSensitive.tsx +133 -133
  294. package/src/components/NSBoxString.tsx +106 -106
  295. package/src/components/NSBoxTextArea.module.css +12 -12
  296. package/src/components/NSBoxTextArea.tsx +136 -136
  297. package/src/components/NSBoxTime.module.css +4 -4
  298. package/src/components/NSBoxTime.tsx +106 -106
  299. package/src/components/NSBoxTimeRange.tsx +220 -220
  300. package/src/components/NSBoxTimeZone.tsx +122 -122
  301. package/src/components/NSBoxURL.tsx +117 -117
  302. package/src/components/NSBoxVersion.tsx +107 -107
  303. package/src/components/NSButton.module.css +57 -57
  304. package/src/components/NSButton.tsx +74 -74
  305. package/src/components/NSButtonBlue.module.css +4 -4
  306. package/src/components/NSButtonBlue.tsx +29 -29
  307. package/src/components/NSButtonGreen.module.css +4 -4
  308. package/src/components/NSButtonGreen.tsx +29 -29
  309. package/src/components/NSButtonRed.module.css +4 -4
  310. package/src/components/NSButtonRed.tsx +29 -29
  311. package/src/components/NSCard.module.css +113 -113
  312. package/src/components/NSCard.tsx +67 -67
  313. package/src/components/NSCardScreenshot.module.css +41 -41
  314. package/src/components/NSCardScreenshot.tsx +31 -31
  315. package/src/components/NSChartBar.tsx +134 -134
  316. package/src/components/NSChartDoughnut.tsx +114 -114
  317. package/src/components/NSChartLine.tsx +110 -110
  318. package/src/components/NSChartPie.tsx +105 -105
  319. package/src/components/NSChartRange.tsx +14 -14
  320. package/src/components/NSColumn.module.css +16 -16
  321. package/src/components/NSColumn.tsx +24 -24
  322. package/src/components/NSCopy.tsx +95 -58
  323. package/src/components/NSCopyBox.module.css +39 -39
  324. package/src/components/NSCopyBox.tsx +53 -53
  325. package/src/components/NSDialog.module.css +105 -105
  326. package/src/components/NSDialog.tsx +72 -72
  327. package/src/components/NSDialogBox.tsx +38 -38
  328. package/src/components/NSDialogBoxDate.tsx +28 -28
  329. package/src/components/NSDialogBoxDateTime.tsx +28 -28
  330. package/src/components/NSDialogBoxPhone.tsx +28 -28
  331. package/src/components/NSDialogBoxString.tsx +28 -28
  332. package/src/components/NSDialogDelete.tsx +36 -36
  333. package/src/components/NSDialogInfo.tsx +49 -49
  334. package/src/components/NSDialogPageSelection.module.css +103 -103
  335. package/src/components/NSDialogPageSelection.tsx +219 -219
  336. package/src/components/NSDownTimer.module.css +55 -55
  337. package/src/components/NSDownTimer.tsx +91 -91
  338. package/src/components/NSDownload.module.css +46 -46
  339. package/src/components/NSDownload.tsx +69 -69
  340. package/src/components/NSElectronicCard.module.css +60 -60
  341. package/src/components/NSElectronicCard.tsx +46 -46
  342. package/src/components/NSEntityCardBackground.module.css +27 -27
  343. package/src/components/NSEntityCardBackground.tsx +36 -36
  344. package/src/components/NSFilterBox.module.css +27 -27
  345. package/src/components/NSFilterBox.tsx +477 -477
  346. package/src/components/NSFilterBoxItems.module.css +15 -15
  347. package/src/components/NSFilterBoxItems.tsx +52 -52
  348. package/src/components/NSFilterItem.module.css +24 -24
  349. package/src/components/NSFilterItem.tsx +27 -27
  350. package/src/components/NSFooter.module.css +140 -140
  351. package/src/components/NSFooter.tsx +321 -321
  352. package/src/components/NSGauge.module.css +67 -67
  353. package/src/components/NSGauge.tsx +52 -52
  354. package/src/components/NSGaugeDate.module.css +57 -57
  355. package/src/components/NSGaugeDate.tsx +125 -125
  356. package/src/components/NSGaugeNumber.module.css +66 -66
  357. package/src/components/NSGaugeNumber.tsx +90 -90
  358. package/src/components/NSHeader.module.css +316 -316
  359. package/src/components/NSHeader.tsx +334 -334
  360. package/src/components/NSHeaderScreenshot.module.css +35 -35
  361. package/src/components/NSHeaderScreenshot.tsx +39 -39
  362. package/src/components/NSID.tsx +188 -188
  363. package/src/components/NSInfo.tsx +50 -50
  364. package/src/components/NSLabel.module.css +25 -25
  365. package/src/components/NSLabel.tsx +47 -47
  366. package/src/components/NSLabelErrorNotifier.module.css +2 -2
  367. package/src/components/NSLabelErrorNotifier.tsx +35 -35
  368. package/src/components/NSLabelSensitive.module.css +9 -9
  369. package/src/components/NSLabelSensitive.tsx +101 -85
  370. package/src/components/NSLayout.module.css +22 -22
  371. package/src/components/NSLayout.tsx +140 -140
  372. package/src/components/NSLine.module.css +15 -15
  373. package/src/components/NSLine.tsx +13 -13
  374. package/src/components/NSLineText.module.css +38 -38
  375. package/src/components/NSLineText.tsx +17 -17
  376. package/src/components/NSLink.module.css +36 -36
  377. package/src/components/NSLink.tsx +26 -26
  378. package/src/components/NSLinkBlue.tsx +22 -22
  379. package/src/components/NSLinkGreen.tsx +13 -13
  380. package/src/components/NSLinkRed.tsx +13 -13
  381. package/src/components/NSListGrouped.module.css +69 -69
  382. package/src/components/NSListGrouped.tsx +106 -106
  383. package/src/components/NSListProduct.tsx +78 -78
  384. package/src/components/NSLoading.module.css +10 -10
  385. package/src/components/NSLoading.tsx +43 -43
  386. package/src/components/NSMegaMenu.module.css +133 -133
  387. package/src/components/NSMegaMenu.tsx +188 -188
  388. package/src/components/NSMenuAction.module.css +88 -88
  389. package/src/components/NSMenuAction.tsx +106 -106
  390. package/src/components/NSMenuButton.module.css +44 -44
  391. package/src/components/NSMenuButton.tsx +197 -197
  392. package/src/components/NSNoData.module.css +11 -11
  393. package/src/components/NSNoData.tsx +24 -24
  394. package/src/components/NSPagination.module.css +87 -87
  395. package/src/components/NSPagination.tsx +183 -183
  396. package/src/components/NSPanel.module.css +28 -28
  397. package/src/components/NSPanel.tsx +26 -26
  398. package/src/components/NSPanelAccordion.module.css +4 -4
  399. package/src/components/NSPanelAccordion.tsx +51 -51
  400. package/src/components/NSProductSearch.module.css +73 -73
  401. package/src/components/NSProductSearch.tsx +193 -193
  402. package/src/components/NSProgressBar.module.css +38 -38
  403. package/src/components/NSProgressBar.tsx +29 -29
  404. package/src/components/NSRange.module.css +66 -66
  405. package/src/components/NSRange.tsx +83 -83
  406. package/src/components/NSRepeater.tsx +328 -328
  407. package/src/components/NSRepeaterNSBoxSchemaVariable.tsx +94 -94
  408. package/src/components/NSRepeaterNSTag.tsx +86 -86
  409. package/src/components/NSRow.module.css +17 -17
  410. package/src/components/NSRow.tsx +24 -24
  411. package/src/components/NSSection.module.css +4 -4
  412. package/src/components/NSSection.tsx +26 -26
  413. package/src/components/NSSectionCards.module.css +38 -38
  414. package/src/components/NSSectionCards.tsx +51 -51
  415. package/src/components/NSSectionTiles.module.css +10 -10
  416. package/src/components/NSSectionTiles.tsx +25 -25
  417. package/src/components/NSSectionTitle.tsx +19 -19
  418. package/src/components/NSSpace.tsx +28 -28
  419. package/src/components/NSSplitter.module.css +59 -59
  420. package/src/components/NSSplitter.tsx +211 -211
  421. package/src/components/NSTabPage.module.css +59 -59
  422. package/src/components/NSTabPage.tsx +122 -122
  423. package/src/components/NSTable.module.css +371 -371
  424. package/src/components/NSTable.tsx +1109 -1109
  425. package/src/components/NSTableChart.module.css +119 -119
  426. package/src/components/NSTableChart.tsx +732 -732
  427. package/src/components/NSTag.tsx +74 -74
  428. package/src/components/NSTile.module.css +76 -76
  429. package/src/components/NSTile.tsx +27 -27
  430. package/src/components/NSTimelineMonthly.module.css +71 -71
  431. package/src/components/NSTimelineMonthly.tsx +44 -44
  432. package/src/components/NSTitle.module.css +15 -15
  433. package/src/components/NSTitle.tsx +19 -19
  434. package/src/formatter/BackColorFormatter.tsx +23 -23
  435. package/src/formatter/BaseColumnFormatter.ts +18 -18
  436. package/src/formatter/BaseURLImageFormatter.tsx +33 -33
  437. package/src/formatter/BooleanFormatter.ts +22 -22
  438. package/src/formatter/BytesFormatter.tsx +15 -15
  439. package/src/formatter/DateFormatter.ts +22 -22
  440. package/src/formatter/DateTimeFormatter.ts +22 -22
  441. package/src/formatter/DurationFormatter.ts +13 -13
  442. package/src/formatter/EmailFormatter.tsx +21 -21
  443. package/src/formatter/EnumFormatter.ts +13 -13
  444. package/src/formatter/FloatFormatter.ts +23 -23
  445. package/src/formatter/ForeColorFormatter.tsx +24 -24
  446. package/src/formatter/IDFormatter.tsx +53 -53
  447. package/src/formatter/IPFormatter.ts +13 -13
  448. package/src/formatter/IntegerFormatter.ts +23 -23
  449. package/src/formatter/JsonFormatter.tsx +58 -58
  450. package/src/formatter/MoneyFormatter.ts +35 -35
  451. package/src/formatter/PercentFormatter.tsx +39 -39
  452. package/src/formatter/PhoneFormatter.tsx +21 -21
  453. package/src/formatter/SensitiveFormatter.tsx +36 -33
  454. package/src/formatter/StringFormatter.tsx +43 -43
  455. package/src/formatter/TimeFormatter.ts +21 -21
  456. package/src/formatter/URLFormatter.tsx +24 -24
  457. package/src/formatter/UnknowFormatter.ts +18 -18
  458. package/src/index.tsx +7 -7
  459. package/src/main.ts +262 -262
  460. package/src/pages/NSImplementing.module.css +35 -35
  461. package/src/pages/NSImplementing.tsx +32 -32
  462. package/src/pages/NSNotFoundPage.module.css +18 -18
  463. package/src/pages/NSNotFoundPage.tsx +14 -14
  464. package/src/pages/NSUpdating.module.css +35 -35
  465. package/src/pages/NSUpdating.tsx +32 -32
  466. package/src/props/IBackgroundProps.ts +5 -5
  467. package/src/props/IBaseComponentProps.ts +8 -8
  468. package/src/props/IHeaderIconProps.ts +10 -10
  469. package/src/props/IHeaderLeftProps.ts +6 -6
  470. package/src/props/IHeaderRightProps.ts +8 -8
  471. package/src/props/IImageProps.ts +4 -4
  472. package/src/props/ILinkProps.ts +4 -4
  473. package/src/props/IValidationNumberProps.ts +4 -4
  474. package/src/props/IValidationPrecisionProps.ts +3 -3
  475. package/src/props/IValidationProps.ts +9 -9
  476. package/src/props/IValidationRegexProps.ts +4 -4
  477. package/src/props/IValidationStringProps.ts +4 -4
  478. package/src/routing/NSNotifier.ts +114 -114
  479. package/src/routing/NSRouterMaker.tsx +20 -20
  480. package/src/routing/NSRouterMakerComponent.ts +5 -5
  481. package/src/routing/NSRouterMakerProps.ts +5 -5
  482. package/tsconfig.json +43 -43
  483. package/dist/ColorOperation.d.ts +0 -27
  484. package/dist/ColorOperation.js +0 -145
  485. package/dist/ColorOperation.js.map +0 -1
@@ -1,1110 +1,1110 @@
1
- "use client";
2
- import { BaseMetaColumn, BaseMetaTable, IStorageLocal, SortItem } from "namirasoft-core";
3
- import { Component, createRef, ReactNode } from "react";
4
- import { utils, writeFile } from 'xlsx';
5
- import { BaseColumnFormatter } from '../formatter/BaseColumnFormatter';
6
- import { IBaseComponentProps } from "../props/IBaseComponentProps";
7
- import { NSButton, NSButtonProps } from './NSButton';
8
- import { NSDialogInfo } from './NSDialogInfo';
9
- import { NSDialogPageSelection } from './NSDialogPageSelection';
10
- import { NSLoading } from './NSLoading';
11
- import { NSNoData } from './NSNoData';
12
- import { NSPagination } from './NSPagination';
13
- import { NSTableChart } from './NSTableChart';
14
- import Styles from './NSTable.module.css';
15
-
16
- export interface TableInfo
17
- {
18
- name: string;
19
- text: string;
20
- }
21
-
22
- export interface TableColumnInfo
23
- {
24
- table: TableInfo;
25
- index: number;
26
- type: string;
27
- name: string;
28
- text: string;
29
- formatter: BaseColumnFormatter;
30
- group?: string;
31
- defaults?: {
32
- hidden?: boolean;
33
- };
34
- }
35
-
36
- export interface TableRowInfo<RowType>
37
- {
38
- index: number;
39
- value: RowType;
40
- }
41
-
42
- export interface TableCellInfo<RowType>
43
- {
44
- column: TableColumnInfo;
45
- row: TableRowInfo<RowType>;
46
- value: any;
47
- formatted: any;
48
- }
49
-
50
- export interface NSTableProps<RowType> extends IBaseComponentProps
51
- {
52
- name: string;
53
- checkbox: boolean;
54
- columns: TableColumnInfo[];
55
- getRows: (page: number, size: number, sorts: SortItem[]) => Promise<{ rows: RowType[], count: number }>;
56
- getRowKey: (row: TableRowInfo<RowType>) => string;
57
- getRowAttributes?: (row_value: RowType, row_index: number) => { [key: string]: any };
58
- getColumnAttributes?: (column: TableColumnInfo) => { [key: string]: any };
59
- getCellFormattedValue?: (value: any, column: TableColumnInfo, row: TableRowInfo<RowType>, printable: boolean) => any;
60
- onColumnClick?: (e: React.MouseEvent<HTMLTableCellElement, MouseEvent>, column: TableColumnInfo) => void;
61
- onRowClick?: (e: React.MouseEvent<HTMLTableRowElement, MouseEvent>, row: TableRowInfo<RowType>) => void;
62
- onCellClick?: (e: React.MouseEvent<HTMLTableCellElement, MouseEvent>, cell: TableCellInfo<RowType>) => boolean | undefined;
63
- getRowCheckboxDisabled?: (row: TableRowInfo<RowType>) => boolean;
64
- buttons?: NSButtonProps[];
65
- children?: ReactNode;
66
- headerChildren?: ReactNode;
67
- hierarchy?: { id: string, parent: string, group?: string[] };
68
- defaultExpanded?: boolean;
69
- onSelectedIDsChanged?: () => void;
70
- onSortChanged?: (items: SortItem[] | null) => void;
71
- onVisibleColumnsChanged?: (visible_columns: { table: string, column: string }[]) => void;
72
- ui?: {
73
- pagination?: boolean;
74
- button_panel?: boolean;
75
- };
76
- }
77
-
78
- interface NSTableState<RowType>
79
- {
80
- columns: TableColumnInfo[];
81
- visible_columns: { table: string, column: string }[];
82
- totalItems: number;
83
- rows: RowType[] | null;
84
- title?: string;
85
- model: {
86
- show: boolean;
87
- description?: string;
88
- };
89
- selectedIDs: string[];
90
- selectedRows: RowType[];
91
- expandedIDs: string[];
92
- pageSelectionMode: PageSelectionMode;
93
- sortItems: SortItem[] | null;
94
- columnWidths: { [key: string]: number };
95
- fullscreen: boolean;
96
- showChart: boolean;
97
- }
98
-
99
- enum PageSelectionMode
100
- {
101
- Hidden = "Hidden",
102
- Print = "Print",
103
- CSV = "CSV"
104
- }
105
-
106
- export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableState<RowType>>
107
- {
108
- private Table_Ref = createRef<HTMLTableElement>();
109
- NSPagination = createRef<NSPagination>();
110
-
111
- private LastSelectedRowID: number | null = null;
112
- private resizeMoveHandler: ((e: MouseEvent) => void) | null = null;
113
- private resizeUpHandler: (() => void) | null = null;
114
-
115
- constructor(props: NSTableProps<RowType>)
116
- {
117
- super(props);
118
- this.state = {
119
- columns: props.columns,
120
- visible_columns: this.getVisibleColumns(),
121
- rows: null,
122
- totalItems: 0,
123
- model: { show: false },
124
- selectedIDs: [],
125
- selectedRows: [],
126
- expandedIDs: [],
127
- pageSelectionMode: PageSelectionMode.Hidden,
128
- sortItems: this.getSortItems(),
129
- columnWidths: this.loadColumnWidths(),
130
- fullscreen: false,
131
- showChart: false,
132
- };
133
- this.getSortItemsKey = this.getSortItemsKey.bind(this);
134
- this.getSortItems = this.getSortItems.bind(this);
135
- this.setSortItems = this.setSortItems.bind(this);
136
- this.getVisibleColumnsKey = this.getVisibleColumnsKey.bind(this);
137
- this.getVisibleColumns = this.getVisibleColumns.bind(this);
138
- this.setVisibleColumns = this.setVisibleColumns.bind(this);
139
- this.setColumns = this.setColumns.bind(this);
140
- this.setColumns = this.setColumns.bind(this);
141
- this.setRows = this.setRows.bind(this);
142
- this.foreachColumn = this.foreachColumn.bind(this);
143
- this.getColumns = this.getColumns.bind(this);
144
- this.showModal = this.showModal.bind(this);
145
- this.hideModal = this.hideModal.bind(this);
146
- this.onPageChange = this.onPageChange.bind(this);
147
- this.isSomeSelected = this.isSomeSelected.bind(this);
148
- this.toggleAllSelections = this.toggleAllSelections.bind(this);
149
- this.print = this.print.bind(this);
150
- this.exportCSV = this.exportCSV.bind(this);
151
- this.isAllSelected = this.isAllSelected.bind(this);
152
- this.reload = this.reload.bind(this);
153
- this.toggleFullscreen = this.toggleFullscreen.bind(this);
154
- }
155
- private toggleFullscreen()
156
- {
157
- this.setState({ fullscreen: !this.state.fullscreen });
158
- }
159
- private getSortItemsKey(): string
160
- {
161
- return "ns_table_" + this.props.name + "_sort_items";
162
- }
163
- getSortItems(): SortItem[]
164
- {
165
- let storage = new IStorageLocal();
166
- let item = storage.get(this.getSortItemsKey(), "");
167
- try
168
- {
169
- return SortItem.parse(item);
170
- } catch (error)
171
- {
172
- }
173
- return [];
174
- }
175
- setSortItems(items: SortItem[] | null)
176
- {
177
- let storage = new IStorageLocal();
178
- storage.set(this.getSortItemsKey(), SortItem.stringify(items));
179
- this.setState({ sortItems: items }, () =>
180
- {
181
- this.props.onSortChanged?.(items);
182
- this.reload(null, null);
183
- });
184
- }
185
- private getVisibleColumnsKey(): string
186
- {
187
- return "ns_table_" + this.props.name + "_visible_columns";
188
- }
189
- getVisibleColumns(): { table: string, column: string }[]
190
- {
191
- let storage = new IStorageLocal();
192
- let item = storage.get(this.getVisibleColumnsKey(), "");
193
- try
194
- {
195
- return JSON.parse(item);
196
- } catch (error)
197
- {
198
- }
199
- return [];
200
- }
201
- setVisibleColumns(visible_columns: { table: string, column: string }[])
202
- {
203
- let storage = new IStorageLocal();
204
- storage.set(this.getVisibleColumnsKey(), JSON.stringify(visible_columns));
205
- this.setState({ visible_columns }, () =>
206
- {
207
- this.props.onVisibleColumnsChanged?.(visible_columns);
208
- });
209
- }
210
- setColumns(columns: TableColumnInfo[])
211
- {
212
- this.setState({ columns });
213
- }
214
- setRows(rows: RowType[] | null, totalItems: number)
215
- {
216
- this.setState({ rows, totalItems });
217
- }
218
- foreachColumn(visible: boolean | null, handler: (column: TableColumnInfo) => void)
219
- {
220
- let vs = this.getVisibleColumns();
221
- let shouldBeVisible = (c: TableColumnInfo) =>
222
- {
223
- if (vs.length === 0)
224
- return !c.defaults?.hidden;
225
- return vs.filter(v => v.table === c.table.name && v.column === c.name).length > 0;
226
- };
227
- this.state.columns.forEach(c =>
228
- {
229
- if (visible === null || shouldBeVisible(c) === visible)
230
- handler(c);
231
- })
232
- }
233
- getColumns(visible: boolean | null): TableColumnInfo[]
234
- {
235
- let columns: TableColumnInfo[] = [];
236
- this.foreachColumn(visible, c => columns.push(c));
237
- if (visible === true)
238
- {
239
- let vs = this.getVisibleColumns();
240
- if (vs.length > 0)
241
- columns.sort((a, b) =>
242
- {
243
- let ai = vs.findIndex(v => v.table === a.table.name && v.column === a.name);
244
- let bi = vs.findIndex(v => v.table === b.table.name && v.column === b.name);
245
- return ai - bi;
246
- });
247
- }
248
- return columns;
249
- }
250
- private showModal(description: string, title?: string)
251
- {
252
- this.setState({ model: { show: true, description }, title });
253
- }
254
- private hideModal()
255
- {
256
- this.setState({ model: { show: false } });
257
- }
258
- private onPageChange(page: number, size: number)
259
- {
260
- this.reload(page, size);
261
- }
262
- public async reload(page: number | null, size: number | null)
263
- {
264
- this.setRows(null, 0);
265
- if (page === null)
266
- page = this.NSPagination.current?.getCurrentPage() ?? 0;
267
- if (size === null)
268
- size = this.NSPagination.current?.getPageSize() ?? 0;
269
- try
270
- {
271
- let sorts = this.getSortItems();
272
- let res = await this.props.getRows(page, size, sorts);
273
- this.setRows(res.rows, res.count);
274
- } catch (error)
275
- {
276
- }
277
- }
278
- private toggleAllSelections()
279
- {
280
- let selectedIDs: string[] = [];
281
- let selectedRows: RowType[] = [];
282
- this.LastSelectedRowID = null;
283
-
284
- if (this.getSelectedIDs().length === 0)
285
- {
286
- this.state.rows?.forEach((row, rowIndex) =>
287
- {
288
- let rowInfo = { index: rowIndex, value: row };
289
- if (this.props.getRowCheckboxDisabled?.(rowInfo))
290
- return;
291
- let id = this.props.getRowKey(rowInfo);
292
- selectedIDs.push(id);
293
- selectedRows.push(row);
294
- })
295
- }
296
-
297
- this.setSelectedIDs(selectedIDs, selectedRows);
298
- }
299
- print(rows: RowType[])
300
- {
301
- const printWindow = window.open('', '', 'height=500,width=800');
302
- printWindow!.document.write(`<html><head><title>${this.props.name}</title>`);
303
- printWindow!.document.write('<style>table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid black; padding: 8px; text-align: left; }</style>');
304
- printWindow!.document.write('</head><body>');
305
- printWindow!.document.write('<table>');
306
- printWindow!.document.write('<thead><tr>');
307
-
308
- this.foreachColumn(true, column =>
309
- {
310
- printWindow!.document.write('<th>' + column.text + '</th>');
311
- });
312
-
313
- printWindow!.document.write('</tr></thead><tbody>');
314
-
315
- // Add table rows
316
- rows.forEach((rowValue, rowIndex) =>
317
- {
318
- let row = { index: rowIndex, value: rowValue };
319
- printWindow!.document.write('<tr>');
320
- this.foreachColumn(true, column =>
321
- {
322
- let cell = this.getCellTableInfo(column, row, true);
323
- printWindow!.document.write('<td>' + (cell.formatted ?? "") + '</td>');
324
- });
325
- printWindow!.document.write('</tr>');
326
- });
327
-
328
- printWindow!.document.write('</tbody></table>');
329
- printWindow!.document.write('</body></html>');
330
- printWindow!.document.close();
331
- printWindow!.print();
332
- }
333
- exportCSV(rows: RowType[])
334
- {
335
- const worksheet = utils.json_to_sheet(rows || []);
336
- const workbook = utils.book_new();
337
- utils.book_append_sheet(workbook, worksheet, this.props.name);
338
- writeFile(workbook, this.props.name + ".xlsx");
339
- }
340
-
341
- handleSelectRow(row: TableRowInfo<RowType>, selected: boolean, ctrlKey: boolean, shiftKey: boolean)
342
- {
343
- let selectedIDs = this.state.selectedIDs;
344
- let selectedRows = this.state.selectedRows;
345
- let rowID = this.props.getRowKey(row);
346
-
347
- if (shiftKey)
348
- {
349
- selectedRows = this.getRowsInRange(this.LastSelectedRowID ?? 0, row.index);
350
- selectedIDs = selectedRows.map(row => this.props.getRowKey({ index: -1, value: row }));
351
- }
352
- else if (selected)
353
- {
354
- if (this.isMultipleSelected() && !ctrlKey)
355
- {
356
- selectedIDs = [rowID];
357
- selectedRows = [row.value];
358
- this.LastSelectedRowID = row.index;
359
- }
360
- else
361
- {
362
- selectedIDs = selectedIDs.filter(x => x !== rowID);
363
- selectedRows = selectedRows.filter(x => this.props.getRowKey({ index: -1, value: x }) !== rowID);
364
- this.LastSelectedRowID = ctrlKey ? row.index : null;
365
- }
366
- }
367
- else
368
- {
369
- if (ctrlKey)
370
- {
371
- selectedIDs.push(rowID);
372
- selectedRows.push(row.value);
373
- }
374
- else
375
- {
376
- selectedIDs = [rowID];
377
- selectedRows = [row.value];
378
- }
379
- this.LastSelectedRowID = row.index;
380
- }
381
-
382
- this.setSelectedIDs(selectedIDs, selectedRows);
383
- }
384
- getRowsInRange(fromIndex: number, toIndex: number)
385
- {
386
- if (!this.state.rows)
387
- return [];
388
-
389
- let start = Math.min(fromIndex, toIndex);
390
- let end = Math.max(fromIndex, toIndex);
391
-
392
- return this.state.rows?.slice(start, end + 1);
393
- }
394
- getSelectedIDs(): string[]
395
- {
396
- return this.state.selectedIDs;
397
- }
398
- getSelectedRows(): RowType[]
399
- {
400
- return this.state.selectedRows;
401
- }
402
- setSelectedIDs(ids: string[], rows: RowType[], callback?: () => void)
403
- {
404
- this.setState({ selectedIDs: ids, selectedRows: rows }, () =>
405
- {
406
- if (this.props.onSelectedIDsChanged)
407
- this.props.onSelectedIDsChanged();
408
- if (callback)
409
- callback();
410
- });
411
- }
412
- private getGroupKey(row: RowType): string
413
- {
414
- let h = this.props.hierarchy;
415
- if (!h || !h.group || h.group.length === 0)
416
- return "";
417
- return h.group.map(g =>
418
- {
419
- let v = (row as any)[g];
420
- return v === null || v === undefined ? "" : String(v);
421
- }).join("");
422
- }
423
- private buildChildrenMap(): Map<string, RowType[]> | null
424
- {
425
- let h = this.props.hierarchy;
426
- if (!h || !this.state.rows)
427
- return null;
428
- let map = new Map<string, RowType[]>();
429
- for (let r of this.state.rows)
430
- {
431
- let pid = (r as any)[h.parent];
432
- let pidKey = pid === null || pid === undefined ? "" : String(pid);
433
- let key = pidKey + "" + this.getGroupKey(r);
434
- let arr = map.get(key);
435
- if (!arr) { arr = []; map.set(key, arr); }
436
- arr.push(r);
437
- }
438
- return map;
439
- }
440
- private getRowIdValue(row: RowType): string
441
- {
442
- let h = this.props.hierarchy;
443
- if (!h) return "";
444
- let v = (row as any)[h.id];
445
- return v === null || v === undefined ? "" : String(v);
446
- }
447
- private getRowChildLookupKey(row: RowType): string
448
- {
449
- return this.getRowIdValue(row) + "" + this.getGroupKey(row);
450
- }
451
- private isExpanded(rowKey: string): boolean
452
- {
453
- let inList = this.state.expandedIDs.includes(rowKey);
454
- return this.props.defaultExpanded ? !inList : inList;
455
- }
456
- private toggleExpanded(rowKey: string)
457
- {
458
- let ids = this.state.expandedIDs;
459
- if (ids.includes(rowKey))
460
- ids = ids.filter(x => x !== rowKey);
461
- else
462
- ids = [...ids, rowKey];
463
- this.setState({ expandedIDs: ids });
464
- }
465
- private isAllSelected(): boolean
466
- {
467
- if (!this.state.rows || this.state.rows.length === 0)
468
- return false;
469
- const selectableCount = this.state.rows.filter((row, i) =>
470
- !this.props.getRowCheckboxDisabled?.({ index: i, value: row })
471
- ).length;
472
- return selectableCount > 0 && this.state.selectedIDs.length === selectableCount;
473
- }
474
- private isSomeSelected(): boolean
475
- {
476
- return this.state.selectedIDs.length > 0 && !this.isAllSelected();
477
- }
478
- private isMultipleSelected(): boolean
479
- {
480
- return this.state.selectedIDs.length > 1;
481
- }
482
- private getCellTableInfo(column: TableColumnInfo, row: TableRowInfo<RowType>, printable: boolean)
483
- {
484
- let value = (row.value as any)[column.name];
485
- let formatted = this.getCellFormattedValue(value, column, row, printable);
486
- return { column, row, value, formatted };
487
- }
488
- private getCellFormattedValue(value: any, column: TableColumnInfo, row: TableRowInfo<RowType>, printable: boolean)
489
- {
490
- if (this.props.getCellFormattedValue)
491
- return this.props.getCellFormattedValue(value, column, row, printable);
492
- return column.formatter.format(value, column, row, printable);
493
- }
494
- private getColumnWidthsKey(): string
495
- {
496
- return "ns_table_" + this.props.name + "_column_widths";
497
- }
498
- private loadColumnWidths(): { [key: string]: number }
499
- {
500
- let storage = new IStorageLocal();
501
- let item = storage.get(this.getColumnWidthsKey(), "");
502
- try
503
- {
504
- return JSON.parse(item);
505
- }
506
- catch
507
- {
508
- return {};
509
- }
510
- }
511
- private saveColumnWidths(widths: { [key: string]: number })
512
- {
513
- let storage = new IStorageLocal();
514
- storage.set(this.getColumnWidthsKey(), JSON.stringify(widths));
515
- }
516
- private getColumnKey(column: TableColumnInfo): string
517
- {
518
- return column.table.name + "_" + column.name;
519
- }
520
- private getColumnWidth(column: TableColumnInfo): number
521
- {
522
- let key = this.getColumnKey(column);
523
- if (this.state.columnWidths[key] !== undefined)
524
- return this.state.columnWidths[key];
525
- let w = column.formatter.width;
526
- if (typeof w === 'number') return w;
527
- if (typeof w === 'string')
528
- {
529
- let parsed = parseInt(w, 10);
530
- if (!isNaN(parsed)) return parsed;
531
- }
532
- return 120;
533
- }
534
- private getGridTemplateColumns(): string
535
- {
536
- let parts: string[] = [];
537
- if (this.props.checkbox)
538
- parts.push("40px");
539
- this.getColumns(true).forEach(column =>
540
- {
541
- parts.push(this.getColumnWidth(column) + "px");
542
- });
543
- return parts.join(" ");
544
- }
545
- private startResize(e: React.MouseEvent, column: TableColumnInfo)
546
- {
547
- e.preventDefault();
548
- e.stopPropagation();
549
- let key = this.getColumnKey(column);
550
- let startX = e.clientX;
551
- let startWidth = this.getColumnWidth(column);
552
- let currentWidths = { ...this.state.columnWidths };
553
- document.body.style.cursor = 'col-resize';
554
- document.body.style.userSelect = 'none';
555
- const onMouseMove = (ev: MouseEvent) =>
556
- {
557
- let delta = ev.clientX - startX;
558
- let newWidth = Math.max(80, startWidth + delta);
559
- currentWidths = { ...currentWidths, [key]: newWidth };
560
- this.setState({ columnWidths: currentWidths });
561
- };
562
- const onMouseUp = () =>
563
- {
564
- document.body.style.cursor = '';
565
- document.body.style.userSelect = '';
566
- document.removeEventListener('mousemove', onMouseMove);
567
- document.removeEventListener('mouseup', onMouseUp);
568
- this.resizeMoveHandler = null;
569
- this.resizeUpHandler = null;
570
- this.saveColumnWidths(currentWidths);
571
- };
572
- this.resizeMoveHandler = onMouseMove;
573
- this.resizeUpHandler = onMouseUp;
574
- document.addEventListener('mousemove', onMouseMove);
575
- document.addEventListener('mouseup', onMouseUp);
576
- }
577
- override componentDidMount(): void
578
- {
579
- if (!this.NSPagination.current)
580
- this.reload(null, null);
581
- }
582
- override componentWillUnmount(): void
583
- {
584
- if (this.resizeMoveHandler)
585
- document.removeEventListener('mousemove', this.resizeMoveHandler);
586
- if (this.resizeUpHandler)
587
- document.removeEventListener('mouseup', this.resizeUpHandler);
588
- }
589
- override render()
590
- {
591
-
592
- let show_pagination = this.props.ui?.pagination ?? true;
593
- let show_button_panel = this.props.ui?.button_panel ?? true;
594
- let getRowAttributes = (row_value: RowType, row_index: number) =>
595
- {
596
- if (this.props.getRowAttributes)
597
- return this.props.getRowAttributes(row_value, row_index);
598
- return {};
599
- }
600
- let getColumnAttributes = (column: TableColumnInfo) =>
601
- {
602
- if (this.props.getColumnAttributes)
603
- return this.props.getColumnAttributes(column);
604
- return {};
605
- }
606
- let onColumnClick = (e: React.MouseEvent<HTMLTableCellElement, MouseEvent>, column: TableColumnInfo) =>
607
- {
608
- if ((e.target as HTMLElement).tagName !== "SPAN")
609
- return;
610
-
611
- if (this.props.onColumnClick)
612
- this.props.onColumnClick(e, column);
613
- }
614
- let onRowClick = (e: React.MouseEvent<HTMLTableRowElement, MouseEvent>, row: TableRowInfo<RowType>, selected: boolean) =>
615
- {
616
- const target = e.target as HTMLElement;
617
-
618
- if (target.tagName === "TR" || target.tagName === "TD" || target.closest("td")?.dataset.cellType === "checkbox")
619
- this.handleSelectRow(row, selected, e.ctrlKey, e.shiftKey);
620
-
621
- if (this.props.onRowClick)
622
- this.props.onRowClick(e, row);
623
- };
624
- let onCellClick = (e: React.MouseEvent<HTMLTableCellElement, MouseEvent>, cell: TableCellInfo<RowType>) =>
625
- {
626
- if ((e.target as HTMLElement).tagName !== "SPAN")
627
- return;
628
-
629
- let overided = cell.column.formatter.onclick_overrided;
630
- if (this.props.onCellClick)
631
- overided = this.props.onCellClick(e, cell) ?? true;
632
- if (!overided)
633
- this.showModal(cell.formatted, cell.column.text);
634
- };
635
- let gridTemplateColumns = this.getGridTemplateColumns();
636
- let content;
637
- if (this.state.rows === null)
638
- {
639
- content = (
640
- <tr className="d-flex justify-content-center align-items-center p-0">
641
- <td className="w-100 h-100 m-0 mh-100">
642
- <NSLoading />
643
- </td>
644
- </tr>
645
- );
646
- }
647
- else if (this.state.rows.length === 0)
648
- {
649
- content = (
650
- <tr className="d-flex justify-content-center align-items-center p-0">
651
- <td className="w-100 h-100 m-0 mh-100">
652
- <NSNoData lable='No Data' classList={["p-3"]} />
653
- </td>
654
- </tr>
655
- );
656
- }
657
- else
658
- {
659
- let hierarchical = !!this.props.hierarchy;
660
- let childrenMap = this.buildChildrenMap();
661
- let indexOfRow = new Map<RowType, number>();
662
- this.state.rows.forEach((r, i) => indexOfRow.set(r, i));
663
- let INDENT = 16;
664
- let lineColor = "rgba(20, 27, 92, 0.35)";
665
- let renderRow = (rowValue: RowType, rowIndex: number, depth: number, parentKey: string, ancestorHasNext: boolean[], isLastChild: boolean): React.ReactNode[] =>
666
- {
667
- let row = { index: rowIndex, value: rowValue };
668
- let rowKey = this.props.getRowKey(row);
669
- let reactKey = parentKey ? `${parentKey}>${rowKey}` : rowKey;
670
- let isSelected = this.state.selectedIDs.includes(rowKey);
671
- let isLastSelected = this.LastSelectedRowID === row.index;
672
- let isCheckboxDisabled = this.props.getRowCheckboxDisabled?.(row) ?? false;
673
- let children: RowType[] = [];
674
- if (hierarchical && childrenMap)
675
- {
676
- let lookupKey = this.getRowChildLookupKey(rowValue);
677
- children = childrenMap.get(lookupKey) ?? [];
678
- }
679
- let hasChildren = children.length > 0;
680
- let expanded = this.isExpanded(rowKey);
681
- let rowAttrs = getRowAttributes(rowValue, rowIndex);
682
- let nodes: React.ReactNode[] = [];
683
- nodes.push(
684
- <tr
685
- key={reactKey}
686
- {...rowAttrs}
687
- style={{ ...(rowAttrs.style || {}), gridTemplateColumns }}
688
- className={`${isSelected ? Styles.ns_selected : ""} ${isLastSelected ? Styles.ns_last_selected : ""}`.trim()}
689
- onClick={(e) => !isCheckboxDisabled && onRowClick(e, row, isSelected)}
690
- >
691
- {
692
- this.props.checkbox &&
693
- <td data-cell-type="checkbox">
694
- <label
695
- htmlFor={`checkbox_device_${rowKey}`}
696
- className={Styles.ns_check_box_label}
697
- >
698
- <input
699
- id={`checkbox_device_${rowKey}`}
700
- type="checkbox"
701
- checked={isSelected}
702
- disabled={isCheckboxDisabled}
703
- className={Styles.ns_check_box}
704
- onChange={() => { }}
705
- />
706
- </label>
707
- </td>
708
- }
709
- {
710
- this.getColumns(true).map((column, index) =>
711
- {
712
- column.index = index + (this.props.checkbox ? 1 : 0);
713
- let cell = this.getCellTableInfo(column, row, false);
714
- let isFirstDataColumn = index === 0;
715
- return (
716
- <td
717
- key={column.index}
718
- {...getColumnAttributes(column)}
719
- data-label={`${column.text}: `}
720
- width={column.formatter.width}
721
- className=''
722
- style={isFirstDataColumn && hierarchical ? { overflow: "visible" } : undefined}
723
- onClick={(e) =>
724
- {
725
- onColumnClick(e, column);
726
- onCellClick(e, cell);
727
- }}
728
- >
729
- {isFirstDataColumn && hierarchical ? (
730
- <div
731
- style={{
732
- display: "flex",
733
- alignItems: "center",
734
- }}
735
- >
736
- {Array.from({ length: depth }).map((_, i) =>
737
- {
738
- let isLastLevel = i === depth - 1;
739
- let drawVerticalTop = i < depth - 1 ? ancestorHasNext[i] : true;
740
- let drawVerticalBottom = i < depth - 1 ? ancestorHasNext[i] : !isLastChild;
741
- let drawHorizontal = isLastLevel;
742
- return (
743
- <span
744
- key={i}
745
- style={{
746
- position: "relative",
747
- display: "inline-block",
748
- width: INDENT,
749
- alignSelf: "stretch",
750
- flex: "0 0 auto",
751
- }}
752
- >
753
- {drawVerticalTop && (
754
- <span style={{
755
- position: "absolute",
756
- left: INDENT / 2,
757
- top: -24,
758
- height: "calc(50% + 24px)",
759
- width: 1,
760
- background: lineColor,
761
- }} />
762
- )}
763
- {drawVerticalBottom && (
764
- <span style={{
765
- position: "absolute",
766
- left: INDENT / 2,
767
- top: "50%",
768
- height: "calc(50% + 24px)",
769
- width: 1,
770
- background: lineColor,
771
- }} />
772
- )}
773
- {drawHorizontal && (
774
- <span style={{
775
- position: "absolute",
776
- left: INDENT / 2,
777
- top: "50%",
778
- width: INDENT / 2,
779
- height: 1,
780
- background: lineColor,
781
- }} />
782
- )}
783
- </span>
784
- );
785
- })}
786
- <span
787
- data-cell-type="tree-toggle"
788
- style={{
789
- display: "inline-flex",
790
- alignItems: "center",
791
- justifyContent: "center",
792
- marginRight: 6,
793
- }}
794
- onClick={(e) => e.stopPropagation()}
795
- >
796
- {hasChildren ? (
797
- <button
798
- type="button"
799
- onClick={(e) =>
800
- {
801
- e.stopPropagation();
802
- this.toggleExpanded(rowKey);
803
- }}
804
- style={{
805
- width: 18,
806
- height: 18,
807
- lineHeight: "16px",
808
- padding: 0,
809
- border: "1px solid rgba(20, 27, 92, 0.4)",
810
- borderRadius: 3,
811
- background: "#fff",
812
- cursor: "pointer",
813
- fontSize: 14,
814
- fontWeight: "bold",
815
- color: "rgba(20, 27, 92, 1)",
816
- }}
817
- aria-label={expanded ? "Collapse" : "Expand"}
818
- >
819
- {expanded ? "−" : "+"}
820
- </button>
821
- ) : (
822
- <span style={{ display: "inline-block", width: 18, height: 18 }} />
823
- )}
824
- </span>
825
- {cell.formatted ?? ""}
826
- </div>
827
- ) : (
828
- <>
829
- {cell.formatted ?? ""}
830
- </>
831
- )}
832
- </td>
833
- );
834
- })
835
- }
836
- </tr>
837
- );
838
- if (hierarchical && hasChildren && expanded)
839
- {
840
- let childAncestors = [...ancestorHasNext, !isLastChild];
841
- children.forEach((child, ci2) =>
842
- {
843
- let ci = indexOfRow.get(child) ?? -1;
844
- let lastChild = ci2 === children.length - 1;
845
- renderRow(child, ci, depth + 1, reactKey, childAncestors, lastChild).forEach(n => nodes.push(n));
846
- });
847
- }
848
- return nodes;
849
- };
850
- let topLevel: RowType[];
851
- if (hierarchical && this.props.hierarchy)
852
- {
853
- let parentCol = this.props.hierarchy.parent;
854
- let ids = new Set<string>();
855
- this.state.rows.forEach(r => ids.add(this.getRowIdValue(r) + "" + this.getGroupKey(r)));
856
- topLevel = this.state.rows.filter(r =>
857
- {
858
- let pid = (r as any)[parentCol];
859
- let pidKey = pid === null || pid === undefined ? "" : String(pid);
860
- if (pidKey === "")
861
- return true;
862
- return !ids.has(pidKey + "" + this.getGroupKey(r));
863
- });
864
- }
865
- else
866
- {
867
- topLevel = this.state.rows;
868
- }
869
- content = topLevel.flatMap((rowValue, i) => renderRow(rowValue, indexOfRow.get(rowValue) ?? 0, 0, "", [], i === topLevel.length - 1));
870
- }
871
-
872
- let getSortSign = (column: TableColumnInfo) =>
873
- {
874
- if (this.state.sortItems)
875
- {
876
- let c = this.state.sortItems.filter(c => c.table.name === column.table.name && c.column.name === column.name)[0]
877
- if (c)
878
- {
879
- if (c.ascending)
880
- return "↑"
881
- return "↓";
882
- }
883
- }
884
- return "";
885
- };
886
-
887
- let buttonPanel = show_button_panel && (
888
- <div className={Styles.ns_button_panel}>
889
- {
890
- this.state.pageSelectionMode !== PageSelectionMode.Hidden &&
891
- <NSDialogPageSelection<RowType>
892
- icon={
893
- this.state.pageSelectionMode === PageSelectionMode.Print ? "https://static.namirasoft.com/image/concept/print/blue.svg" :
894
- this.state.pageSelectionMode === PageSelectionMode.CSV ? "https://static.namirasoft.com/image/concept/export/blue.svg" : ""
895
- }
896
- current_page={this.NSPagination.current?.getCurrentPage() ?? 1}
897
- current_size={this.NSPagination.current?.getPageSize() ?? 0}
898
- max_page={this.state.totalItems}
899
- getItems={async (page, size, selected) =>
900
- {
901
- if (selected)
902
- {
903
- let rows: RowType[] = [];
904
- if (this.state.rows)
905
- for (let i = 0; i < this.state.rows.length; i++)
906
- {
907
- const row = { index: i, value: this.state.rows[i] };
908
- let id = this.props.getRowKey(row);
909
- if (this.state.selectedIDs.includes(id))
910
- rows.push(row.value);
911
- }
912
- return rows;
913
- }
914
- let res = await this.props.getRows(page, size, this.getSortItems());
915
- return res.rows;
916
- }}
917
- onFinish={async (items: RowType[]) =>
918
- {
919
- if (this.state.pageSelectionMode === PageSelectionMode.Print)
920
- this.print(items);
921
- else if (this.state.pageSelectionMode === PageSelectionMode.CSV)
922
- this.exportCSV(items);
923
- }}
924
- onClose={() => this.setState({ pageSelectionMode: PageSelectionMode.Hidden })}
925
- />
926
- }
927
- {
928
- this.props.buttons?.map((button, index) => (
929
- <NSButton
930
- key={index}
931
- {...button}
932
- classList={[Styles.ns_table_button, ...(button.classList ?? [])]}
933
- />
934
- ))
935
- }
936
- <NSButton
937
- icon={{ src: `https://static.namirasoft.com/image/concept/full-screen/blue.svg` }}
938
- classList={[Styles.ns_table_button, Styles.ns_fullscreen_button]}
939
- onClick={{
940
- action: this.toggleFullscreen,
941
- showLoading: false
942
- }}
943
- attributes={{ title: this.state.fullscreen ? 'Minimize' : 'Maximize' }}
944
- />
945
- <NSButton
946
- attributes={{ title: 'Chart' }}
947
- icon={{ src: "https://static.namirasoft.com/image/concept/chart/blue.svg" }}
948
- onClick={{
949
- action: () =>
950
- {
951
- if (!this.state.rows || this.state.rows.length === 0)
952
- this.showModal(" ", "There is no data to display as a chart.");
953
- else
954
- this.setState({ showChart: true });
955
- },
956
- showLoading: false
957
- }}
958
- classList={[Styles.ns_table_button]}
959
- />
960
- <NSButton
961
- icon={{ src: "https://static.namirasoft.com/image/concept/export/blue.svg" }}
962
- onClick={{
963
- action: () => { this.setState({ pageSelectionMode: PageSelectionMode.CSV }); },
964
- showLoading: false
965
- }}
966
- classList={[Styles.ns_table_button]}
967
- attributes={{ title: 'Export' }}
968
- />
969
- <NSButton
970
- attributes={{ title: 'Print' }}
971
- icon={{ src: "https://static.namirasoft.com/image/concept/print/blue.svg" }}
972
- onClick={{
973
- action: () => { this.setState({ pageSelectionMode: PageSelectionMode.Print }); },
974
- showLoading: false
975
- }}
976
- classList={[Styles.ns_table_button]}
977
- />
978
- <NSButton
979
- attributes={{ title: 'Refresh' }}
980
- icon={{ src: "https://static.namirasoft.com/image/concept/refresh/blue.svg" }}
981
- onClick={{
982
- action: async (onFinished) =>
983
- {
984
- this.reload(null, null).then(onFinished).catch(onFinished);
985
- }
986
- }}
987
- classList={[Styles.ns_table_button]}
988
- style={{ border: '1px solid rgb(3, 119, 255)' }}
989
- />
990
- </div>
991
- );
992
-
993
- return (
994
- <div
995
- id={this.props.id}
996
- className={[...this.props.classList ?? [], Styles.ns_table_wrapper, this.state.fullscreen ? Styles.ns_fullscreen : ""].filter(Boolean).join(" ")}
997
- style={{ ...this.props.style }}
998
- >
999
- {
1000
- ((!this.state.fullscreen && this.props.headerChildren) || show_pagination || buttonPanel) &&
1001
- <div className={Styles.ns_table_topbar}>
1002
- {
1003
- !this.state.fullscreen &&
1004
- this.props.headerChildren
1005
- }
1006
- {
1007
- show_pagination &&
1008
- <NSPagination ref={this.NSPagination} totalItems={this.state.totalItems} onPageChange={this.onPageChange} style={{ marginRight: "auto" }} />
1009
- }
1010
- {buttonPanel}
1011
- </div>
1012
- }
1013
- {
1014
- !this.state.fullscreen && this.props.children &&
1015
- <div className={Styles.ns_table_children}>
1016
- {this.props.children}
1017
- </div>
1018
- }
1019
- <table
1020
- ref={this.Table_Ref}
1021
- className={Styles.ns_table}
1022
- >
1023
- <thead>
1024
- <tr style={{ gridTemplateColumns }}>
1025
- {
1026
- this.props.checkbox &&
1027
- <th data-cell-type="checkbox" className="d-flex">
1028
- <label
1029
- htmlFor='checkbox'
1030
- className={Styles.ns_checkbox_label}
1031
- >
1032
- <input
1033
- id="checkbox"
1034
- type="checkbox"
1035
- className={`${Styles.ns_check_box} ${this.isSomeSelected() ? Styles.ns_indeterminate : ""}`}
1036
- checked={this.getSelectedIDs().length > 0}
1037
- onChange={this.toggleAllSelections}
1038
- ></input>
1039
- </label>
1040
- </th>
1041
- }
1042
- {
1043
- this.getColumns(true).map(column =>
1044
- <th
1045
- key={column.name}
1046
- scope="col"
1047
- onMouseDown={(e) =>
1048
- {
1049
- if ((e.target as HTMLElement).closest('[data-resize-handle]'))
1050
- return;
1051
- let t = new BaseMetaTable(null, column.table.name, column.table.text);
1052
- let c = new BaseMetaColumn(t, column.name, column.text, "", false);
1053
- let items = this.getSortItems();
1054
- if (!items || items.length === 0 || items[0].table.name !== t.name || items[0].column.name !== c.name)
1055
- this.setSortItems([new SortItem(t, c, true)]);
1056
- else
1057
- {
1058
- if (items[0].ascending)
1059
- this.setSortItems([new SortItem(t, c, false)]);
1060
- else
1061
- this.setSortItems(null);
1062
- }
1063
- }}
1064
- style={{ width: this.getColumnWidth(column) }}
1065
- >
1066
- <span className={Styles.ns_th_title}>
1067
- {column.text + " " + getSortSign(column)}
1068
- </span>
1069
- <div
1070
- data-resize-handle="true"
1071
- className={Styles.ns_resize_handle}
1072
- onMouseDown={(e) => this.startResize(e, column)}
1073
- onClick={(e) => {e.stopPropagation()}}
1074
- />
1075
- </th>
1076
- )
1077
- }
1078
- </tr>
1079
- </thead>
1080
-
1081
- <tbody className={Styles.ns_tbody}>
1082
- {content}
1083
- </tbody>
1084
- </table>
1085
-
1086
- {
1087
- this.state.model.show &&
1088
- <NSDialogInfo
1089
- description={this.state.model.description}
1090
- onClose={() => { this.hideModal(); }}
1091
- title={this.state.title}
1092
- show_as_code={true}
1093
- >
1094
- <></>
1095
- </NSDialogInfo>
1096
- }
1097
- {
1098
- this.state.showChart &&
1099
- <NSTableChart<RowType>
1100
- name={this.props.name}
1101
- columns={this.getColumns(true)}
1102
- rows={this.state.rows ?? []}
1103
- selectedRows={this.state.selectedRows}
1104
- onClose={() => this.setState({ showChart: false })}
1105
- />
1106
- }
1107
- </div>
1108
- );
1109
- }
1
+ "use client";
2
+ import { BaseMetaColumn, BaseMetaTable, IStorageLocal, SortItem } from "namirasoft-core";
3
+ import { Component, createRef, ReactNode } from "react";
4
+ import { utils, writeFile } from 'xlsx';
5
+ import { BaseColumnFormatter } from '../formatter/BaseColumnFormatter';
6
+ import { IBaseComponentProps } from "../props/IBaseComponentProps";
7
+ import { NSButton, NSButtonProps } from './NSButton';
8
+ import { NSDialogInfo } from './NSDialogInfo';
9
+ import { NSDialogPageSelection } from './NSDialogPageSelection';
10
+ import { NSLoading } from './NSLoading';
11
+ import { NSNoData } from './NSNoData';
12
+ import { NSPagination } from './NSPagination';
13
+ import { NSTableChart } from './NSTableChart';
14
+ import Styles from './NSTable.module.css';
15
+
16
+ export interface TableInfo
17
+ {
18
+ name: string;
19
+ text: string;
20
+ }
21
+
22
+ export interface TableColumnInfo
23
+ {
24
+ table: TableInfo;
25
+ index: number;
26
+ type: string;
27
+ name: string;
28
+ text: string;
29
+ formatter: BaseColumnFormatter;
30
+ group?: string;
31
+ defaults?: {
32
+ hidden?: boolean;
33
+ };
34
+ }
35
+
36
+ export interface TableRowInfo<RowType>
37
+ {
38
+ index: number;
39
+ value: RowType;
40
+ }
41
+
42
+ export interface TableCellInfo<RowType>
43
+ {
44
+ column: TableColumnInfo;
45
+ row: TableRowInfo<RowType>;
46
+ value: any;
47
+ formatted: any;
48
+ }
49
+
50
+ export interface NSTableProps<RowType> extends IBaseComponentProps
51
+ {
52
+ name: string;
53
+ checkbox: boolean;
54
+ columns: TableColumnInfo[];
55
+ getRows: (page: number, size: number, sorts: SortItem[]) => Promise<{ rows: RowType[], count: number }>;
56
+ getRowKey: (row: TableRowInfo<RowType>) => string;
57
+ getRowAttributes?: (row_value: RowType, row_index: number) => { [key: string]: any };
58
+ getColumnAttributes?: (column: TableColumnInfo) => { [key: string]: any };
59
+ getCellFormattedValue?: (value: any, column: TableColumnInfo, row: TableRowInfo<RowType>, printable: boolean) => any;
60
+ onColumnClick?: (e: React.MouseEvent<HTMLTableCellElement, MouseEvent>, column: TableColumnInfo) => void;
61
+ onRowClick?: (e: React.MouseEvent<HTMLTableRowElement, MouseEvent>, row: TableRowInfo<RowType>) => void;
62
+ onCellClick?: (e: React.MouseEvent<HTMLTableCellElement, MouseEvent>, cell: TableCellInfo<RowType>) => boolean | undefined;
63
+ getRowCheckboxDisabled?: (row: TableRowInfo<RowType>) => boolean;
64
+ buttons?: NSButtonProps[];
65
+ children?: ReactNode;
66
+ headerChildren?: ReactNode;
67
+ hierarchy?: { id: string, parent: string, group?: string[] };
68
+ defaultExpanded?: boolean;
69
+ onSelectedIDsChanged?: () => void;
70
+ onSortChanged?: (items: SortItem[] | null) => void;
71
+ onVisibleColumnsChanged?: (visible_columns: { table: string, column: string }[]) => void;
72
+ ui?: {
73
+ pagination?: boolean;
74
+ button_panel?: boolean;
75
+ };
76
+ }
77
+
78
+ interface NSTableState<RowType>
79
+ {
80
+ columns: TableColumnInfo[];
81
+ visible_columns: { table: string, column: string }[];
82
+ totalItems: number;
83
+ rows: RowType[] | null;
84
+ title?: string;
85
+ model: {
86
+ show: boolean;
87
+ description?: string;
88
+ };
89
+ selectedIDs: string[];
90
+ selectedRows: RowType[];
91
+ expandedIDs: string[];
92
+ pageSelectionMode: PageSelectionMode;
93
+ sortItems: SortItem[] | null;
94
+ columnWidths: { [key: string]: number };
95
+ fullscreen: boolean;
96
+ showChart: boolean;
97
+ }
98
+
99
+ enum PageSelectionMode
100
+ {
101
+ Hidden = "Hidden",
102
+ Print = "Print",
103
+ CSV = "CSV"
104
+ }
105
+
106
+ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableState<RowType>>
107
+ {
108
+ private Table_Ref = createRef<HTMLTableElement>();
109
+ NSPagination = createRef<NSPagination>();
110
+
111
+ private LastSelectedRowID: number | null = null;
112
+ private resizeMoveHandler: ((e: MouseEvent) => void) | null = null;
113
+ private resizeUpHandler: (() => void) | null = null;
114
+
115
+ constructor(props: NSTableProps<RowType>)
116
+ {
117
+ super(props);
118
+ this.state = {
119
+ columns: props.columns,
120
+ visible_columns: this.getVisibleColumns(),
121
+ rows: null,
122
+ totalItems: 0,
123
+ model: { show: false },
124
+ selectedIDs: [],
125
+ selectedRows: [],
126
+ expandedIDs: [],
127
+ pageSelectionMode: PageSelectionMode.Hidden,
128
+ sortItems: this.getSortItems(),
129
+ columnWidths: this.loadColumnWidths(),
130
+ fullscreen: false,
131
+ showChart: false,
132
+ };
133
+ this.getSortItemsKey = this.getSortItemsKey.bind(this);
134
+ this.getSortItems = this.getSortItems.bind(this);
135
+ this.setSortItems = this.setSortItems.bind(this);
136
+ this.getVisibleColumnsKey = this.getVisibleColumnsKey.bind(this);
137
+ this.getVisibleColumns = this.getVisibleColumns.bind(this);
138
+ this.setVisibleColumns = this.setVisibleColumns.bind(this);
139
+ this.setColumns = this.setColumns.bind(this);
140
+ this.setColumns = this.setColumns.bind(this);
141
+ this.setRows = this.setRows.bind(this);
142
+ this.foreachColumn = this.foreachColumn.bind(this);
143
+ this.getColumns = this.getColumns.bind(this);
144
+ this.showModal = this.showModal.bind(this);
145
+ this.hideModal = this.hideModal.bind(this);
146
+ this.onPageChange = this.onPageChange.bind(this);
147
+ this.isSomeSelected = this.isSomeSelected.bind(this);
148
+ this.toggleAllSelections = this.toggleAllSelections.bind(this);
149
+ this.print = this.print.bind(this);
150
+ this.exportCSV = this.exportCSV.bind(this);
151
+ this.isAllSelected = this.isAllSelected.bind(this);
152
+ this.reload = this.reload.bind(this);
153
+ this.toggleFullscreen = this.toggleFullscreen.bind(this);
154
+ }
155
+ private toggleFullscreen()
156
+ {
157
+ this.setState({ fullscreen: !this.state.fullscreen });
158
+ }
159
+ private getSortItemsKey(): string
160
+ {
161
+ return "ns_table_" + this.props.name + "_sort_items";
162
+ }
163
+ getSortItems(): SortItem[]
164
+ {
165
+ let storage = new IStorageLocal();
166
+ let item = storage.get(this.getSortItemsKey(), "");
167
+ try
168
+ {
169
+ return SortItem.parse(item);
170
+ } catch (error)
171
+ {
172
+ }
173
+ return [];
174
+ }
175
+ setSortItems(items: SortItem[] | null)
176
+ {
177
+ let storage = new IStorageLocal();
178
+ storage.set(this.getSortItemsKey(), SortItem.stringify(items));
179
+ this.setState({ sortItems: items }, () =>
180
+ {
181
+ this.props.onSortChanged?.(items);
182
+ this.reload(null, null);
183
+ });
184
+ }
185
+ private getVisibleColumnsKey(): string
186
+ {
187
+ return "ns_table_" + this.props.name + "_visible_columns";
188
+ }
189
+ getVisibleColumns(): { table: string, column: string }[]
190
+ {
191
+ let storage = new IStorageLocal();
192
+ let item = storage.get(this.getVisibleColumnsKey(), "");
193
+ try
194
+ {
195
+ return JSON.parse(item);
196
+ } catch (error)
197
+ {
198
+ }
199
+ return [];
200
+ }
201
+ setVisibleColumns(visible_columns: { table: string, column: string }[])
202
+ {
203
+ let storage = new IStorageLocal();
204
+ storage.set(this.getVisibleColumnsKey(), JSON.stringify(visible_columns));
205
+ this.setState({ visible_columns }, () =>
206
+ {
207
+ this.props.onVisibleColumnsChanged?.(visible_columns);
208
+ });
209
+ }
210
+ setColumns(columns: TableColumnInfo[])
211
+ {
212
+ this.setState({ columns });
213
+ }
214
+ setRows(rows: RowType[] | null, totalItems: number)
215
+ {
216
+ this.setState({ rows, totalItems });
217
+ }
218
+ foreachColumn(visible: boolean | null, handler: (column: TableColumnInfo) => void)
219
+ {
220
+ let vs = this.getVisibleColumns();
221
+ let shouldBeVisible = (c: TableColumnInfo) =>
222
+ {
223
+ if (vs.length === 0)
224
+ return !c.defaults?.hidden;
225
+ return vs.filter(v => v.table === c.table.name && v.column === c.name).length > 0;
226
+ };
227
+ this.state.columns.forEach(c =>
228
+ {
229
+ if (visible === null || shouldBeVisible(c) === visible)
230
+ handler(c);
231
+ })
232
+ }
233
+ getColumns(visible: boolean | null): TableColumnInfo[]
234
+ {
235
+ let columns: TableColumnInfo[] = [];
236
+ this.foreachColumn(visible, c => columns.push(c));
237
+ if (visible === true)
238
+ {
239
+ let vs = this.getVisibleColumns();
240
+ if (vs.length > 0)
241
+ columns.sort((a, b) =>
242
+ {
243
+ let ai = vs.findIndex(v => v.table === a.table.name && v.column === a.name);
244
+ let bi = vs.findIndex(v => v.table === b.table.name && v.column === b.name);
245
+ return ai - bi;
246
+ });
247
+ }
248
+ return columns;
249
+ }
250
+ private showModal(description: string, title?: string)
251
+ {
252
+ this.setState({ model: { show: true, description }, title });
253
+ }
254
+ private hideModal()
255
+ {
256
+ this.setState({ model: { show: false } });
257
+ }
258
+ private onPageChange(page: number, size: number)
259
+ {
260
+ this.reload(page, size);
261
+ }
262
+ public async reload(page: number | null, size: number | null)
263
+ {
264
+ this.setRows(null, 0);
265
+ if (page === null)
266
+ page = this.NSPagination.current?.getCurrentPage() ?? 0;
267
+ if (size === null)
268
+ size = this.NSPagination.current?.getPageSize() ?? 0;
269
+ try
270
+ {
271
+ let sorts = this.getSortItems();
272
+ let res = await this.props.getRows(page, size, sorts);
273
+ this.setRows(res.rows, res.count);
274
+ } catch (error)
275
+ {
276
+ }
277
+ }
278
+ private toggleAllSelections()
279
+ {
280
+ let selectedIDs: string[] = [];
281
+ let selectedRows: RowType[] = [];
282
+ this.LastSelectedRowID = null;
283
+
284
+ if (this.getSelectedIDs().length === 0)
285
+ {
286
+ this.state.rows?.forEach((row, rowIndex) =>
287
+ {
288
+ let rowInfo = { index: rowIndex, value: row };
289
+ if (this.props.getRowCheckboxDisabled?.(rowInfo))
290
+ return;
291
+ let id = this.props.getRowKey(rowInfo);
292
+ selectedIDs.push(id);
293
+ selectedRows.push(row);
294
+ })
295
+ }
296
+
297
+ this.setSelectedIDs(selectedIDs, selectedRows);
298
+ }
299
+ print(rows: RowType[])
300
+ {
301
+ const printWindow = window.open('', '', 'height=500,width=800');
302
+ printWindow!.document.write(`<html><head><title>${this.props.name}</title>`);
303
+ printWindow!.document.write('<style>table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid black; padding: 8px; text-align: left; }</style>');
304
+ printWindow!.document.write('</head><body>');
305
+ printWindow!.document.write('<table>');
306
+ printWindow!.document.write('<thead><tr>');
307
+
308
+ this.foreachColumn(true, column =>
309
+ {
310
+ printWindow!.document.write('<th>' + column.text + '</th>');
311
+ });
312
+
313
+ printWindow!.document.write('</tr></thead><tbody>');
314
+
315
+ // Add table rows
316
+ rows.forEach((rowValue, rowIndex) =>
317
+ {
318
+ let row = { index: rowIndex, value: rowValue };
319
+ printWindow!.document.write('<tr>');
320
+ this.foreachColumn(true, column =>
321
+ {
322
+ let cell = this.getCellTableInfo(column, row, true);
323
+ printWindow!.document.write('<td>' + (cell.formatted ?? "") + '</td>');
324
+ });
325
+ printWindow!.document.write('</tr>');
326
+ });
327
+
328
+ printWindow!.document.write('</tbody></table>');
329
+ printWindow!.document.write('</body></html>');
330
+ printWindow!.document.close();
331
+ printWindow!.print();
332
+ }
333
+ exportCSV(rows: RowType[])
334
+ {
335
+ const worksheet = utils.json_to_sheet(rows || []);
336
+ const workbook = utils.book_new();
337
+ utils.book_append_sheet(workbook, worksheet, this.props.name);
338
+ writeFile(workbook, this.props.name + ".xlsx");
339
+ }
340
+
341
+ handleSelectRow(row: TableRowInfo<RowType>, selected: boolean, ctrlKey: boolean, shiftKey: boolean)
342
+ {
343
+ let selectedIDs = this.state.selectedIDs;
344
+ let selectedRows = this.state.selectedRows;
345
+ let rowID = this.props.getRowKey(row);
346
+
347
+ if (shiftKey)
348
+ {
349
+ selectedRows = this.getRowsInRange(this.LastSelectedRowID ?? 0, row.index);
350
+ selectedIDs = selectedRows.map(row => this.props.getRowKey({ index: -1, value: row }));
351
+ }
352
+ else if (selected)
353
+ {
354
+ if (this.isMultipleSelected() && !ctrlKey)
355
+ {
356
+ selectedIDs = [rowID];
357
+ selectedRows = [row.value];
358
+ this.LastSelectedRowID = row.index;
359
+ }
360
+ else
361
+ {
362
+ selectedIDs = selectedIDs.filter(x => x !== rowID);
363
+ selectedRows = selectedRows.filter(x => this.props.getRowKey({ index: -1, value: x }) !== rowID);
364
+ this.LastSelectedRowID = ctrlKey ? row.index : null;
365
+ }
366
+ }
367
+ else
368
+ {
369
+ if (ctrlKey)
370
+ {
371
+ selectedIDs.push(rowID);
372
+ selectedRows.push(row.value);
373
+ }
374
+ else
375
+ {
376
+ selectedIDs = [rowID];
377
+ selectedRows = [row.value];
378
+ }
379
+ this.LastSelectedRowID = row.index;
380
+ }
381
+
382
+ this.setSelectedIDs(selectedIDs, selectedRows);
383
+ }
384
+ getRowsInRange(fromIndex: number, toIndex: number)
385
+ {
386
+ if (!this.state.rows)
387
+ return [];
388
+
389
+ let start = Math.min(fromIndex, toIndex);
390
+ let end = Math.max(fromIndex, toIndex);
391
+
392
+ return this.state.rows?.slice(start, end + 1);
393
+ }
394
+ getSelectedIDs(): string[]
395
+ {
396
+ return this.state.selectedIDs;
397
+ }
398
+ getSelectedRows(): RowType[]
399
+ {
400
+ return this.state.selectedRows;
401
+ }
402
+ setSelectedIDs(ids: string[], rows: RowType[], callback?: () => void)
403
+ {
404
+ this.setState({ selectedIDs: ids, selectedRows: rows }, () =>
405
+ {
406
+ if (this.props.onSelectedIDsChanged)
407
+ this.props.onSelectedIDsChanged();
408
+ if (callback)
409
+ callback();
410
+ });
411
+ }
412
+ private getGroupKey(row: RowType): string
413
+ {
414
+ let h = this.props.hierarchy;
415
+ if (!h || !h.group || h.group.length === 0)
416
+ return "";
417
+ return h.group.map(g =>
418
+ {
419
+ let v = (row as any)[g];
420
+ return v === null || v === undefined ? "" : String(v);
421
+ }).join("");
422
+ }
423
+ private buildChildrenMap(): Map<string, RowType[]> | null
424
+ {
425
+ let h = this.props.hierarchy;
426
+ if (!h || !this.state.rows)
427
+ return null;
428
+ let map = new Map<string, RowType[]>();
429
+ for (let r of this.state.rows)
430
+ {
431
+ let pid = (r as any)[h.parent];
432
+ let pidKey = pid === null || pid === undefined ? "" : String(pid);
433
+ let key = pidKey + "" + this.getGroupKey(r);
434
+ let arr = map.get(key);
435
+ if (!arr) { arr = []; map.set(key, arr); }
436
+ arr.push(r);
437
+ }
438
+ return map;
439
+ }
440
+ private getRowIdValue(row: RowType): string
441
+ {
442
+ let h = this.props.hierarchy;
443
+ if (!h) return "";
444
+ let v = (row as any)[h.id];
445
+ return v === null || v === undefined ? "" : String(v);
446
+ }
447
+ private getRowChildLookupKey(row: RowType): string
448
+ {
449
+ return this.getRowIdValue(row) + "" + this.getGroupKey(row);
450
+ }
451
+ private isExpanded(rowKey: string): boolean
452
+ {
453
+ let inList = this.state.expandedIDs.includes(rowKey);
454
+ return this.props.defaultExpanded ? !inList : inList;
455
+ }
456
+ private toggleExpanded(rowKey: string)
457
+ {
458
+ let ids = this.state.expandedIDs;
459
+ if (ids.includes(rowKey))
460
+ ids = ids.filter(x => x !== rowKey);
461
+ else
462
+ ids = [...ids, rowKey];
463
+ this.setState({ expandedIDs: ids });
464
+ }
465
+ private isAllSelected(): boolean
466
+ {
467
+ if (!this.state.rows || this.state.rows.length === 0)
468
+ return false;
469
+ const selectableCount = this.state.rows.filter((row, i) =>
470
+ !this.props.getRowCheckboxDisabled?.({ index: i, value: row })
471
+ ).length;
472
+ return selectableCount > 0 && this.state.selectedIDs.length === selectableCount;
473
+ }
474
+ private isSomeSelected(): boolean
475
+ {
476
+ return this.state.selectedIDs.length > 0 && !this.isAllSelected();
477
+ }
478
+ private isMultipleSelected(): boolean
479
+ {
480
+ return this.state.selectedIDs.length > 1;
481
+ }
482
+ private getCellTableInfo(column: TableColumnInfo, row: TableRowInfo<RowType>, printable: boolean)
483
+ {
484
+ let value = (row.value as any)[column.name];
485
+ let formatted = this.getCellFormattedValue(value, column, row, printable);
486
+ return { column, row, value, formatted };
487
+ }
488
+ private getCellFormattedValue(value: any, column: TableColumnInfo, row: TableRowInfo<RowType>, printable: boolean)
489
+ {
490
+ if (this.props.getCellFormattedValue)
491
+ return this.props.getCellFormattedValue(value, column, row, printable);
492
+ return column.formatter.format(value, column, row, printable);
493
+ }
494
+ private getColumnWidthsKey(): string
495
+ {
496
+ return "ns_table_" + this.props.name + "_column_widths";
497
+ }
498
+ private loadColumnWidths(): { [key: string]: number }
499
+ {
500
+ let storage = new IStorageLocal();
501
+ let item = storage.get(this.getColumnWidthsKey(), "");
502
+ try
503
+ {
504
+ return JSON.parse(item);
505
+ }
506
+ catch
507
+ {
508
+ return {};
509
+ }
510
+ }
511
+ private saveColumnWidths(widths: { [key: string]: number })
512
+ {
513
+ let storage = new IStorageLocal();
514
+ storage.set(this.getColumnWidthsKey(), JSON.stringify(widths));
515
+ }
516
+ private getColumnKey(column: TableColumnInfo): string
517
+ {
518
+ return column.table.name + "_" + column.name;
519
+ }
520
+ private getColumnWidth(column: TableColumnInfo): number
521
+ {
522
+ let key = this.getColumnKey(column);
523
+ if (this.state.columnWidths[key] !== undefined)
524
+ return this.state.columnWidths[key];
525
+ let w = column.formatter.width;
526
+ if (typeof w === 'number') return w;
527
+ if (typeof w === 'string')
528
+ {
529
+ let parsed = parseInt(w, 10);
530
+ if (!isNaN(parsed)) return parsed;
531
+ }
532
+ return 120;
533
+ }
534
+ private getGridTemplateColumns(): string
535
+ {
536
+ let parts: string[] = [];
537
+ if (this.props.checkbox)
538
+ parts.push("40px");
539
+ this.getColumns(true).forEach(column =>
540
+ {
541
+ parts.push(this.getColumnWidth(column) + "px");
542
+ });
543
+ return parts.join(" ");
544
+ }
545
+ private startResize(e: React.MouseEvent, column: TableColumnInfo)
546
+ {
547
+ e.preventDefault();
548
+ e.stopPropagation();
549
+ let key = this.getColumnKey(column);
550
+ let startX = e.clientX;
551
+ let startWidth = this.getColumnWidth(column);
552
+ let currentWidths = { ...this.state.columnWidths };
553
+ document.body.style.cursor = 'col-resize';
554
+ document.body.style.userSelect = 'none';
555
+ const onMouseMove = (ev: MouseEvent) =>
556
+ {
557
+ let delta = ev.clientX - startX;
558
+ let newWidth = Math.max(80, startWidth + delta);
559
+ currentWidths = { ...currentWidths, [key]: newWidth };
560
+ this.setState({ columnWidths: currentWidths });
561
+ };
562
+ const onMouseUp = () =>
563
+ {
564
+ document.body.style.cursor = '';
565
+ document.body.style.userSelect = '';
566
+ document.removeEventListener('mousemove', onMouseMove);
567
+ document.removeEventListener('mouseup', onMouseUp);
568
+ this.resizeMoveHandler = null;
569
+ this.resizeUpHandler = null;
570
+ this.saveColumnWidths(currentWidths);
571
+ };
572
+ this.resizeMoveHandler = onMouseMove;
573
+ this.resizeUpHandler = onMouseUp;
574
+ document.addEventListener('mousemove', onMouseMove);
575
+ document.addEventListener('mouseup', onMouseUp);
576
+ }
577
+ override componentDidMount(): void
578
+ {
579
+ if (!this.NSPagination.current)
580
+ this.reload(null, null);
581
+ }
582
+ override componentWillUnmount(): void
583
+ {
584
+ if (this.resizeMoveHandler)
585
+ document.removeEventListener('mousemove', this.resizeMoveHandler);
586
+ if (this.resizeUpHandler)
587
+ document.removeEventListener('mouseup', this.resizeUpHandler);
588
+ }
589
+ override render()
590
+ {
591
+
592
+ let show_pagination = this.props.ui?.pagination ?? true;
593
+ let show_button_panel = this.props.ui?.button_panel ?? true;
594
+ let getRowAttributes = (row_value: RowType, row_index: number) =>
595
+ {
596
+ if (this.props.getRowAttributes)
597
+ return this.props.getRowAttributes(row_value, row_index);
598
+ return {};
599
+ }
600
+ let getColumnAttributes = (column: TableColumnInfo) =>
601
+ {
602
+ if (this.props.getColumnAttributes)
603
+ return this.props.getColumnAttributes(column);
604
+ return {};
605
+ }
606
+ let onColumnClick = (e: React.MouseEvent<HTMLTableCellElement, MouseEvent>, column: TableColumnInfo) =>
607
+ {
608
+ if ((e.target as HTMLElement).tagName !== "SPAN")
609
+ return;
610
+
611
+ if (this.props.onColumnClick)
612
+ this.props.onColumnClick(e, column);
613
+ }
614
+ let onRowClick = (e: React.MouseEvent<HTMLTableRowElement, MouseEvent>, row: TableRowInfo<RowType>, selected: boolean) =>
615
+ {
616
+ const target = e.target as HTMLElement;
617
+
618
+ if (target.tagName === "TR" || target.tagName === "TD" || target.closest("td")?.dataset.cellType === "checkbox")
619
+ this.handleSelectRow(row, selected, e.ctrlKey, e.shiftKey);
620
+
621
+ if (this.props.onRowClick)
622
+ this.props.onRowClick(e, row);
623
+ };
624
+ let onCellClick = (e: React.MouseEvent<HTMLTableCellElement, MouseEvent>, cell: TableCellInfo<RowType>) =>
625
+ {
626
+ if ((e.target as HTMLElement).tagName !== "SPAN")
627
+ return;
628
+
629
+ let overided = cell.column.formatter.onclick_overrided;
630
+ if (this.props.onCellClick)
631
+ overided = this.props.onCellClick(e, cell) ?? true;
632
+ if (!overided)
633
+ this.showModal(cell.formatted, cell.column.text);
634
+ };
635
+ let gridTemplateColumns = this.getGridTemplateColumns();
636
+ let content;
637
+ if (this.state.rows === null)
638
+ {
639
+ content = (
640
+ <tr className="d-flex justify-content-center align-items-center p-0">
641
+ <td className="w-100 h-100 m-0 mh-100">
642
+ <NSLoading />
643
+ </td>
644
+ </tr>
645
+ );
646
+ }
647
+ else if (this.state.rows.length === 0)
648
+ {
649
+ content = (
650
+ <tr className="d-flex justify-content-center align-items-center p-0">
651
+ <td className="w-100 h-100 m-0 mh-100">
652
+ <NSNoData lable='No Data' classList={["p-3"]} />
653
+ </td>
654
+ </tr>
655
+ );
656
+ }
657
+ else
658
+ {
659
+ let hierarchical = !!this.props.hierarchy;
660
+ let childrenMap = this.buildChildrenMap();
661
+ let indexOfRow = new Map<RowType, number>();
662
+ this.state.rows.forEach((r, i) => indexOfRow.set(r, i));
663
+ let INDENT = 16;
664
+ let lineColor = "rgba(20, 27, 92, 0.35)";
665
+ let renderRow = (rowValue: RowType, rowIndex: number, depth: number, parentKey: string, ancestorHasNext: boolean[], isLastChild: boolean): React.ReactNode[] =>
666
+ {
667
+ let row = { index: rowIndex, value: rowValue };
668
+ let rowKey = this.props.getRowKey(row);
669
+ let reactKey = parentKey ? `${parentKey}>${rowKey}` : rowKey;
670
+ let isSelected = this.state.selectedIDs.includes(rowKey);
671
+ let isLastSelected = this.LastSelectedRowID === row.index;
672
+ let isCheckboxDisabled = this.props.getRowCheckboxDisabled?.(row) ?? false;
673
+ let children: RowType[] = [];
674
+ if (hierarchical && childrenMap)
675
+ {
676
+ let lookupKey = this.getRowChildLookupKey(rowValue);
677
+ children = childrenMap.get(lookupKey) ?? [];
678
+ }
679
+ let hasChildren = children.length > 0;
680
+ let expanded = this.isExpanded(rowKey);
681
+ let rowAttrs = getRowAttributes(rowValue, rowIndex);
682
+ let nodes: React.ReactNode[] = [];
683
+ nodes.push(
684
+ <tr
685
+ key={reactKey}
686
+ {...rowAttrs}
687
+ style={{ ...(rowAttrs.style || {}), gridTemplateColumns }}
688
+ className={`${isSelected ? Styles.ns_selected : ""} ${isLastSelected ? Styles.ns_last_selected : ""}`.trim()}
689
+ onClick={(e) => !isCheckboxDisabled && onRowClick(e, row, isSelected)}
690
+ >
691
+ {
692
+ this.props.checkbox &&
693
+ <td data-cell-type="checkbox">
694
+ <label
695
+ htmlFor={`checkbox_device_${rowKey}`}
696
+ className={Styles.ns_check_box_label}
697
+ >
698
+ <input
699
+ id={`checkbox_device_${rowKey}`}
700
+ type="checkbox"
701
+ checked={isSelected}
702
+ disabled={isCheckboxDisabled}
703
+ className={Styles.ns_check_box}
704
+ onChange={() => { }}
705
+ />
706
+ </label>
707
+ </td>
708
+ }
709
+ {
710
+ this.getColumns(true).map((column, index) =>
711
+ {
712
+ column.index = index + (this.props.checkbox ? 1 : 0);
713
+ let cell = this.getCellTableInfo(column, row, false);
714
+ let isFirstDataColumn = index === 0;
715
+ return (
716
+ <td
717
+ key={column.index}
718
+ {...getColumnAttributes(column)}
719
+ data-label={`${column.text}: `}
720
+ width={column.formatter.width}
721
+ className=''
722
+ style={isFirstDataColumn && hierarchical ? { overflow: "visible" } : undefined}
723
+ onClick={(e) =>
724
+ {
725
+ onColumnClick(e, column);
726
+ onCellClick(e, cell);
727
+ }}
728
+ >
729
+ {isFirstDataColumn && hierarchical ? (
730
+ <div
731
+ style={{
732
+ display: "flex",
733
+ alignItems: "center",
734
+ }}
735
+ >
736
+ {Array.from({ length: depth }).map((_, i) =>
737
+ {
738
+ let isLastLevel = i === depth - 1;
739
+ let drawVerticalTop = i < depth - 1 ? ancestorHasNext[i] : true;
740
+ let drawVerticalBottom = i < depth - 1 ? ancestorHasNext[i] : !isLastChild;
741
+ let drawHorizontal = isLastLevel;
742
+ return (
743
+ <span
744
+ key={i}
745
+ style={{
746
+ position: "relative",
747
+ display: "inline-block",
748
+ width: INDENT,
749
+ alignSelf: "stretch",
750
+ flex: "0 0 auto",
751
+ }}
752
+ >
753
+ {drawVerticalTop && (
754
+ <span style={{
755
+ position: "absolute",
756
+ left: INDENT / 2,
757
+ top: -24,
758
+ height: "calc(50% + 24px)",
759
+ width: 1,
760
+ background: lineColor,
761
+ }} />
762
+ )}
763
+ {drawVerticalBottom && (
764
+ <span style={{
765
+ position: "absolute",
766
+ left: INDENT / 2,
767
+ top: "50%",
768
+ height: "calc(50% + 24px)",
769
+ width: 1,
770
+ background: lineColor,
771
+ }} />
772
+ )}
773
+ {drawHorizontal && (
774
+ <span style={{
775
+ position: "absolute",
776
+ left: INDENT / 2,
777
+ top: "50%",
778
+ width: INDENT / 2,
779
+ height: 1,
780
+ background: lineColor,
781
+ }} />
782
+ )}
783
+ </span>
784
+ );
785
+ })}
786
+ <span
787
+ data-cell-type="tree-toggle"
788
+ style={{
789
+ display: "inline-flex",
790
+ alignItems: "center",
791
+ justifyContent: "center",
792
+ marginRight: 6,
793
+ }}
794
+ onClick={(e) => e.stopPropagation()}
795
+ >
796
+ {hasChildren ? (
797
+ <button
798
+ type="button"
799
+ onClick={(e) =>
800
+ {
801
+ e.stopPropagation();
802
+ this.toggleExpanded(rowKey);
803
+ }}
804
+ style={{
805
+ width: 18,
806
+ height: 18,
807
+ lineHeight: "16px",
808
+ padding: 0,
809
+ border: "1px solid rgba(20, 27, 92, 0.4)",
810
+ borderRadius: 3,
811
+ background: "#fff",
812
+ cursor: "pointer",
813
+ fontSize: 14,
814
+ fontWeight: "bold",
815
+ color: "rgba(20, 27, 92, 1)",
816
+ }}
817
+ aria-label={expanded ? "Collapse" : "Expand"}
818
+ >
819
+ {expanded ? "−" : "+"}
820
+ </button>
821
+ ) : (
822
+ <span style={{ display: "inline-block", width: 18, height: 18 }} />
823
+ )}
824
+ </span>
825
+ {cell.formatted ?? ""}
826
+ </div>
827
+ ) : (
828
+ <>
829
+ {cell.formatted ?? ""}
830
+ </>
831
+ )}
832
+ </td>
833
+ );
834
+ })
835
+ }
836
+ </tr>
837
+ );
838
+ if (hierarchical && hasChildren && expanded)
839
+ {
840
+ let childAncestors = [...ancestorHasNext, !isLastChild];
841
+ children.forEach((child, ci2) =>
842
+ {
843
+ let ci = indexOfRow.get(child) ?? -1;
844
+ let lastChild = ci2 === children.length - 1;
845
+ renderRow(child, ci, depth + 1, reactKey, childAncestors, lastChild).forEach(n => nodes.push(n));
846
+ });
847
+ }
848
+ return nodes;
849
+ };
850
+ let topLevel: RowType[];
851
+ if (hierarchical && this.props.hierarchy)
852
+ {
853
+ let parentCol = this.props.hierarchy.parent;
854
+ let ids = new Set<string>();
855
+ this.state.rows.forEach(r => ids.add(this.getRowIdValue(r) + "" + this.getGroupKey(r)));
856
+ topLevel = this.state.rows.filter(r =>
857
+ {
858
+ let pid = (r as any)[parentCol];
859
+ let pidKey = pid === null || pid === undefined ? "" : String(pid);
860
+ if (pidKey === "")
861
+ return true;
862
+ return !ids.has(pidKey + "" + this.getGroupKey(r));
863
+ });
864
+ }
865
+ else
866
+ {
867
+ topLevel = this.state.rows;
868
+ }
869
+ content = topLevel.flatMap((rowValue, i) => renderRow(rowValue, indexOfRow.get(rowValue) ?? 0, 0, "", [], i === topLevel.length - 1));
870
+ }
871
+
872
+ let getSortSign = (column: TableColumnInfo) =>
873
+ {
874
+ if (this.state.sortItems)
875
+ {
876
+ let c = this.state.sortItems.filter(c => c.table.name === column.table.name && c.column.name === column.name)[0]
877
+ if (c)
878
+ {
879
+ if (c.ascending)
880
+ return "↑"
881
+ return "↓";
882
+ }
883
+ }
884
+ return "";
885
+ };
886
+
887
+ let buttonPanel = show_button_panel && (
888
+ <div className={Styles.ns_button_panel}>
889
+ {
890
+ this.state.pageSelectionMode !== PageSelectionMode.Hidden &&
891
+ <NSDialogPageSelection<RowType>
892
+ icon={
893
+ this.state.pageSelectionMode === PageSelectionMode.Print ? "https://static.namirasoft.com/image/concept/print/blue.svg" :
894
+ this.state.pageSelectionMode === PageSelectionMode.CSV ? "https://static.namirasoft.com/image/concept/export/blue.svg" : ""
895
+ }
896
+ current_page={this.NSPagination.current?.getCurrentPage() ?? 1}
897
+ current_size={this.NSPagination.current?.getPageSize() ?? 0}
898
+ max_page={this.state.totalItems}
899
+ getItems={async (page, size, selected) =>
900
+ {
901
+ if (selected)
902
+ {
903
+ let rows: RowType[] = [];
904
+ if (this.state.rows)
905
+ for (let i = 0; i < this.state.rows.length; i++)
906
+ {
907
+ const row = { index: i, value: this.state.rows[i] };
908
+ let id = this.props.getRowKey(row);
909
+ if (this.state.selectedIDs.includes(id))
910
+ rows.push(row.value);
911
+ }
912
+ return rows;
913
+ }
914
+ let res = await this.props.getRows(page, size, this.getSortItems());
915
+ return res.rows;
916
+ }}
917
+ onFinish={async (items: RowType[]) =>
918
+ {
919
+ if (this.state.pageSelectionMode === PageSelectionMode.Print)
920
+ this.print(items);
921
+ else if (this.state.pageSelectionMode === PageSelectionMode.CSV)
922
+ this.exportCSV(items);
923
+ }}
924
+ onClose={() => this.setState({ pageSelectionMode: PageSelectionMode.Hidden })}
925
+ />
926
+ }
927
+ {
928
+ this.props.buttons?.map((button, index) => (
929
+ <NSButton
930
+ key={index}
931
+ {...button}
932
+ classList={[Styles.ns_table_button, ...(button.classList ?? [])]}
933
+ />
934
+ ))
935
+ }
936
+ <NSButton
937
+ icon={{ src: `https://static.namirasoft.com/image/concept/full-screen/blue.svg` }}
938
+ classList={[Styles.ns_table_button, Styles.ns_fullscreen_button]}
939
+ onClick={{
940
+ action: this.toggleFullscreen,
941
+ showLoading: false
942
+ }}
943
+ attributes={{ title: this.state.fullscreen ? 'Minimize' : 'Maximize' }}
944
+ />
945
+ <NSButton
946
+ attributes={{ title: 'Chart' }}
947
+ icon={{ src: "https://static.namirasoft.com/image/concept/chart/blue.svg" }}
948
+ onClick={{
949
+ action: () =>
950
+ {
951
+ if (!this.state.rows || this.state.rows.length === 0)
952
+ this.showModal(" ", "There is no data to display as a chart.");
953
+ else
954
+ this.setState({ showChart: true });
955
+ },
956
+ showLoading: false
957
+ }}
958
+ classList={[Styles.ns_table_button]}
959
+ />
960
+ <NSButton
961
+ icon={{ src: "https://static.namirasoft.com/image/concept/export/blue.svg" }}
962
+ onClick={{
963
+ action: () => { this.setState({ pageSelectionMode: PageSelectionMode.CSV }); },
964
+ showLoading: false
965
+ }}
966
+ classList={[Styles.ns_table_button]}
967
+ attributes={{ title: 'Export' }}
968
+ />
969
+ <NSButton
970
+ attributes={{ title: 'Print' }}
971
+ icon={{ src: "https://static.namirasoft.com/image/concept/print/blue.svg" }}
972
+ onClick={{
973
+ action: () => { this.setState({ pageSelectionMode: PageSelectionMode.Print }); },
974
+ showLoading: false
975
+ }}
976
+ classList={[Styles.ns_table_button]}
977
+ />
978
+ <NSButton
979
+ attributes={{ title: 'Refresh' }}
980
+ icon={{ src: "https://static.namirasoft.com/image/concept/refresh/blue.svg" }}
981
+ onClick={{
982
+ action: async (onFinished) =>
983
+ {
984
+ this.reload(null, null).then(onFinished).catch(onFinished);
985
+ }
986
+ }}
987
+ classList={[Styles.ns_table_button]}
988
+ style={{ border: '1px solid rgb(3, 119, 255)' }}
989
+ />
990
+ </div>
991
+ );
992
+
993
+ return (
994
+ <div
995
+ id={this.props.id}
996
+ className={[...this.props.classList ?? [], Styles.ns_table_wrapper, this.state.fullscreen ? Styles.ns_fullscreen : ""].filter(Boolean).join(" ")}
997
+ style={{ ...this.props.style }}
998
+ >
999
+ {
1000
+ ((!this.state.fullscreen && this.props.headerChildren) || show_pagination || buttonPanel) &&
1001
+ <div className={Styles.ns_table_topbar}>
1002
+ {
1003
+ !this.state.fullscreen &&
1004
+ this.props.headerChildren
1005
+ }
1006
+ {
1007
+ show_pagination &&
1008
+ <NSPagination ref={this.NSPagination} totalItems={this.state.totalItems} onPageChange={this.onPageChange} style={{ marginRight: "auto" }} />
1009
+ }
1010
+ {buttonPanel}
1011
+ </div>
1012
+ }
1013
+ {
1014
+ !this.state.fullscreen && this.props.children &&
1015
+ <div className={Styles.ns_table_children}>
1016
+ {this.props.children}
1017
+ </div>
1018
+ }
1019
+ <table
1020
+ ref={this.Table_Ref}
1021
+ className={Styles.ns_table}
1022
+ >
1023
+ <thead>
1024
+ <tr style={{ gridTemplateColumns }}>
1025
+ {
1026
+ this.props.checkbox &&
1027
+ <th data-cell-type="checkbox" className="d-flex">
1028
+ <label
1029
+ htmlFor='checkbox'
1030
+ className={Styles.ns_checkbox_label}
1031
+ >
1032
+ <input
1033
+ id="checkbox"
1034
+ type="checkbox"
1035
+ className={`${Styles.ns_check_box} ${this.isSomeSelected() ? Styles.ns_indeterminate : ""}`}
1036
+ checked={this.getSelectedIDs().length > 0}
1037
+ onChange={this.toggleAllSelections}
1038
+ ></input>
1039
+ </label>
1040
+ </th>
1041
+ }
1042
+ {
1043
+ this.getColumns(true).map(column =>
1044
+ <th
1045
+ key={column.name}
1046
+ scope="col"
1047
+ onMouseDown={(e) =>
1048
+ {
1049
+ if ((e.target as HTMLElement).closest('[data-resize-handle]'))
1050
+ return;
1051
+ let t = new BaseMetaTable(null, column.table.name, column.table.text);
1052
+ let c = new BaseMetaColumn(t, column.name, column.text, "", false);
1053
+ let items = this.getSortItems();
1054
+ if (!items || items.length === 0 || items[0].table.name !== t.name || items[0].column.name !== c.name)
1055
+ this.setSortItems([new SortItem(t, c, true)]);
1056
+ else
1057
+ {
1058
+ if (items[0].ascending)
1059
+ this.setSortItems([new SortItem(t, c, false)]);
1060
+ else
1061
+ this.setSortItems(null);
1062
+ }
1063
+ }}
1064
+ style={{ width: this.getColumnWidth(column) }}
1065
+ >
1066
+ <span className={Styles.ns_th_title}>
1067
+ {column.text + " " + getSortSign(column)}
1068
+ </span>
1069
+ <div
1070
+ data-resize-handle="true"
1071
+ className={Styles.ns_resize_handle}
1072
+ onMouseDown={(e) => this.startResize(e, column)}
1073
+ onClick={(e) => {e.stopPropagation()}}
1074
+ />
1075
+ </th>
1076
+ )
1077
+ }
1078
+ </tr>
1079
+ </thead>
1080
+
1081
+ <tbody className={Styles.ns_tbody}>
1082
+ {content}
1083
+ </tbody>
1084
+ </table>
1085
+
1086
+ {
1087
+ this.state.model.show &&
1088
+ <NSDialogInfo
1089
+ description={this.state.model.description}
1090
+ onClose={() => { this.hideModal(); }}
1091
+ title={this.state.title}
1092
+ show_as_code={true}
1093
+ >
1094
+ <></>
1095
+ </NSDialogInfo>
1096
+ }
1097
+ {
1098
+ this.state.showChart &&
1099
+ <NSTableChart<RowType>
1100
+ name={this.props.name}
1101
+ columns={this.getColumns(true)}
1102
+ rows={this.state.rows ?? []}
1103
+ selectedRows={this.state.selectedRows}
1104
+ onClose={() => this.setState({ showChart: false })}
1105
+ />
1106
+ }
1107
+ </div>
1108
+ );
1109
+ }
1110
1110
  }