namirasoft-site-react 1.4.567 → 1.4.568

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