namirasoft-site-react 1.4.555 → 1.4.556

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