namirasoft-site-react 1.4.552 → 1.4.554

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