namirasoft-site-react 1.5.13 → 1.5.15

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