@akshar-technosoft/ui 1.0.24 → 1.1.0

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 (320) hide show
  1. package/README.md +466 -215
  2. package/dist/components/date-helper.cjs.map +1 -1
  3. package/dist/components/date-helper.d.cts +36 -0
  4. package/dist/components/date-helper.d.ts +36 -0
  5. package/dist/components/date-helper.js.map +1 -1
  6. package/dist/components/general.cjs +1 -1
  7. package/dist/components/general.cjs.map +1 -1
  8. package/dist/components/general.d.cts +25 -0
  9. package/dist/components/general.d.ts +25 -0
  10. package/dist/components/general.js +1 -1
  11. package/dist/components/general.js.map +1 -1
  12. package/dist/index.cjs +1 -1
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +817 -28
  15. package/dist/index.d.ts +817 -28
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/lib/utils.cjs.map +1 -1
  19. package/dist/lib/utils.js.map +1 -1
  20. package/package.json +94 -87
  21. package/dist/components/core/error.cjs +0 -3
  22. package/dist/components/core/error.cjs.map +0 -1
  23. package/dist/components/core/error.d.cts +0 -5
  24. package/dist/components/core/error.d.ts +0 -5
  25. package/dist/components/core/error.js +0 -3
  26. package/dist/components/core/error.js.map +0 -1
  27. package/dist/components/core/event-success.cjs +0 -3
  28. package/dist/components/core/event-success.cjs.map +0 -1
  29. package/dist/components/core/event-success.d.cts +0 -5
  30. package/dist/components/core/event-success.d.ts +0 -5
  31. package/dist/components/core/event-success.js +0 -3
  32. package/dist/components/core/event-success.js.map +0 -1
  33. package/dist/components/core/index.cjs +0 -3
  34. package/dist/components/core/index.cjs.map +0 -1
  35. package/dist/components/core/index.d.cts +0 -7
  36. package/dist/components/core/index.d.ts +0 -7
  37. package/dist/components/core/index.js +0 -3
  38. package/dist/components/core/index.js.map +0 -1
  39. package/dist/components/core/loader.cjs +0 -3
  40. package/dist/components/core/loader.cjs.map +0 -1
  41. package/dist/components/core/loader.d.cts +0 -5
  42. package/dist/components/core/loader.d.ts +0 -5
  43. package/dist/components/core/loader.js +0 -3
  44. package/dist/components/core/loader.js.map +0 -1
  45. package/dist/components/core/notfound.cjs +0 -3
  46. package/dist/components/core/notfound.cjs.map +0 -1
  47. package/dist/components/core/notfound.d.cts +0 -5
  48. package/dist/components/core/notfound.d.ts +0 -5
  49. package/dist/components/core/notfound.js +0 -3
  50. package/dist/components/core/notfound.js.map +0 -1
  51. package/dist/components/core/offline-content.cjs +0 -3
  52. package/dist/components/core/offline-content.cjs.map +0 -1
  53. package/dist/components/core/offline-content.d.cts +0 -5
  54. package/dist/components/core/offline-content.d.ts +0 -5
  55. package/dist/components/core/offline-content.js +0 -3
  56. package/dist/components/core/offline-content.js.map +0 -1
  57. package/dist/components/core/unauthorized.cjs +0 -3
  58. package/dist/components/core/unauthorized.cjs.map +0 -1
  59. package/dist/components/core/unauthorized.d.cts +0 -5
  60. package/dist/components/core/unauthorized.d.ts +0 -5
  61. package/dist/components/core/unauthorized.js +0 -3
  62. package/dist/components/core/unauthorized.js.map +0 -1
  63. package/dist/components/data/data-table/data-table-action-bar.cjs +0 -3
  64. package/dist/components/data/data-table/data-table-action-bar.cjs.map +0 -1
  65. package/dist/components/data/data-table/data-table-action-bar.d.cts +0 -26
  66. package/dist/components/data/data-table/data-table-action-bar.d.ts +0 -26
  67. package/dist/components/data/data-table/data-table-action-bar.js +0 -3
  68. package/dist/components/data/data-table/data-table-action-bar.js.map +0 -1
  69. package/dist/components/data/data-table/data-table-column-header.cjs +0 -3
  70. package/dist/components/data/data-table/data-table-column-header.cjs.map +0 -1
  71. package/dist/components/data/data-table/data-table-column-header.d.cts +0 -11
  72. package/dist/components/data/data-table/data-table-column-header.d.ts +0 -11
  73. package/dist/components/data/data-table/data-table-column-header.js +0 -3
  74. package/dist/components/data/data-table/data-table-column-header.js.map +0 -1
  75. package/dist/components/data/data-table/data-table-faceted-filter.cjs +0 -3
  76. package/dist/components/data/data-table/data-table-faceted-filter.cjs.map +0 -1
  77. package/dist/components/data/data-table/data-table-faceted-filter.d.cts +0 -18
  78. package/dist/components/data/data-table/data-table-faceted-filter.d.ts +0 -18
  79. package/dist/components/data/data-table/data-table-faceted-filter.js +0 -3
  80. package/dist/components/data/data-table/data-table-faceted-filter.js.map +0 -1
  81. package/dist/components/data/data-table/data-table-pagination.cjs +0 -3
  82. package/dist/components/data/data-table/data-table-pagination.cjs.map +0 -1
  83. package/dist/components/data/data-table/data-table-pagination.d.cts +0 -12
  84. package/dist/components/data/data-table/data-table-pagination.d.ts +0 -12
  85. package/dist/components/data/data-table/data-table-pagination.js +0 -3
  86. package/dist/components/data/data-table/data-table-pagination.js.map +0 -1
  87. package/dist/components/data/data-table/data-table-toolbar.cjs +0 -3
  88. package/dist/components/data/data-table/data-table-toolbar.cjs.map +0 -1
  89. package/dist/components/data/data-table/data-table-toolbar.d.cts +0 -27
  90. package/dist/components/data/data-table/data-table-toolbar.d.ts +0 -27
  91. package/dist/components/data/data-table/data-table-toolbar.js +0 -3
  92. package/dist/components/data/data-table/data-table-toolbar.js.map +0 -1
  93. package/dist/components/data/data-table/data-table-types.cjs +0 -3
  94. package/dist/components/data/data-table/data-table-types.cjs.map +0 -1
  95. package/dist/components/data/data-table/data-table-types.d.cts +0 -117
  96. package/dist/components/data/data-table/data-table-types.d.ts +0 -117
  97. package/dist/components/data/data-table/data-table-types.js +0 -2
  98. package/dist/components/data/data-table/data-table-types.js.map +0 -1
  99. package/dist/components/data/data-table/data-table-view-options.cjs +0 -3
  100. package/dist/components/data/data-table/data-table-view-options.cjs.map +0 -1
  101. package/dist/components/data/data-table/data-table-view-options.d.cts +0 -9
  102. package/dist/components/data/data-table/data-table-view-options.d.ts +0 -9
  103. package/dist/components/data/data-table/data-table-view-options.js +0 -3
  104. package/dist/components/data/data-table/data-table-view-options.js.map +0 -1
  105. package/dist/components/data/data-table/data-table.cjs +0 -3
  106. package/dist/components/data/data-table/data-table.cjs.map +0 -1
  107. package/dist/components/data/data-table/data-table.d.cts +0 -8
  108. package/dist/components/data/data-table/data-table.d.ts +0 -8
  109. package/dist/components/data/data-table/data-table.js +0 -3
  110. package/dist/components/data/data-table/data-table.js.map +0 -1
  111. package/dist/components/data/data-table/index.cjs +0 -3
  112. package/dist/components/data/data-table/index.cjs.map +0 -1
  113. package/dist/components/data/data-table/index.d.cts +0 -16
  114. package/dist/components/data/data-table/index.d.ts +0 -16
  115. package/dist/components/data/data-table/index.js +0 -3
  116. package/dist/components/data/data-table/index.js.map +0 -1
  117. package/dist/components/data/data-template/data-template-action-bar.cjs +0 -3
  118. package/dist/components/data/data-template/data-template-action-bar.cjs.map +0 -1
  119. package/dist/components/data/data-template/data-template-action-bar.d.cts +0 -14
  120. package/dist/components/data/data-template/data-template-action-bar.d.ts +0 -14
  121. package/dist/components/data/data-template/data-template-action-bar.js +0 -3
  122. package/dist/components/data/data-template/data-template-action-bar.js.map +0 -1
  123. package/dist/components/data/data-template/data-template-pagination.cjs +0 -3
  124. package/dist/components/data/data-template/data-template-pagination.cjs.map +0 -1
  125. package/dist/components/data/data-template/data-template-pagination.d.cts +0 -17
  126. package/dist/components/data/data-template/data-template-pagination.d.ts +0 -17
  127. package/dist/components/data/data-template/data-template-pagination.js +0 -3
  128. package/dist/components/data/data-template/data-template-pagination.js.map +0 -1
  129. package/dist/components/data/data-template/data-template-toolbar.cjs +0 -3
  130. package/dist/components/data/data-template/data-template-toolbar.cjs.map +0 -1
  131. package/dist/components/data/data-template/data-template-toolbar.d.cts +0 -27
  132. package/dist/components/data/data-template/data-template-toolbar.d.ts +0 -27
  133. package/dist/components/data/data-template/data-template-toolbar.js +0 -3
  134. package/dist/components/data/data-template/data-template-toolbar.js.map +0 -1
  135. package/dist/components/data/data-template/data-template-types.cjs +0 -3
  136. package/dist/components/data/data-template/data-template-types.cjs.map +0 -1
  137. package/dist/components/data/data-template/data-template-types.d.cts +0 -107
  138. package/dist/components/data/data-template/data-template-types.d.ts +0 -107
  139. package/dist/components/data/data-template/data-template-types.js +0 -2
  140. package/dist/components/data/data-template/data-template-types.js.map +0 -1
  141. package/dist/components/data/data-template/data-template.cjs +0 -3
  142. package/dist/components/data/data-template/data-template.cjs.map +0 -1
  143. package/dist/components/data/data-template/data-template.d.cts +0 -8
  144. package/dist/components/data/data-template/data-template.d.ts +0 -8
  145. package/dist/components/data/data-template/data-template.js +0 -3
  146. package/dist/components/data/data-template/data-template.js.map +0 -1
  147. package/dist/components/data/data-template/index.cjs +0 -3
  148. package/dist/components/data/data-template/index.cjs.map +0 -1
  149. package/dist/components/data/data-template/index.d.cts +0 -8
  150. package/dist/components/data/data-template/index.d.ts +0 -8
  151. package/dist/components/data/data-template/index.js +0 -3
  152. package/dist/components/data/data-template/index.js.map +0 -1
  153. package/dist/components/helper/date-filter.cjs +0 -3
  154. package/dist/components/helper/date-filter.cjs.map +0 -1
  155. package/dist/components/helper/date-filter.d.cts +0 -14
  156. package/dist/components/helper/date-filter.d.ts +0 -14
  157. package/dist/components/helper/date-filter.js +0 -3
  158. package/dist/components/helper/date-filter.js.map +0 -1
  159. package/dist/components/layouts/app-container.cjs +0 -3
  160. package/dist/components/layouts/app-container.cjs.map +0 -1
  161. package/dist/components/layouts/app-container.d.cts +0 -13
  162. package/dist/components/layouts/app-container.d.ts +0 -13
  163. package/dist/components/layouts/app-container.js +0 -3
  164. package/dist/components/layouts/app-container.js.map +0 -1
  165. package/dist/components/layouts/app-content.cjs +0 -3
  166. package/dist/components/layouts/app-content.cjs.map +0 -1
  167. package/dist/components/layouts/app-content.d.cts +0 -45
  168. package/dist/components/layouts/app-content.d.ts +0 -45
  169. package/dist/components/layouts/app-content.js +0 -3
  170. package/dist/components/layouts/app-content.js.map +0 -1
  171. package/dist/components/layouts/app-footer.cjs +0 -3
  172. package/dist/components/layouts/app-footer.cjs.map +0 -1
  173. package/dist/components/layouts/app-footer.d.cts +0 -18
  174. package/dist/components/layouts/app-footer.d.ts +0 -18
  175. package/dist/components/layouts/app-footer.js +0 -3
  176. package/dist/components/layouts/app-footer.js.map +0 -1
  177. package/dist/components/layouts/app-header.cjs +0 -3
  178. package/dist/components/layouts/app-header.cjs.map +0 -1
  179. package/dist/components/layouts/app-header.d.cts +0 -19
  180. package/dist/components/layouts/app-header.d.ts +0 -19
  181. package/dist/components/layouts/app-header.js +0 -3
  182. package/dist/components/layouts/app-header.js.map +0 -1
  183. package/dist/components/layouts/factories.cjs +0 -3
  184. package/dist/components/layouts/factories.cjs.map +0 -1
  185. package/dist/components/layouts/factories.d.cts +0 -18
  186. package/dist/components/layouts/factories.d.ts +0 -18
  187. package/dist/components/layouts/factories.js +0 -3
  188. package/dist/components/layouts/factories.js.map +0 -1
  189. package/dist/components/layouts/index.cjs +0 -3
  190. package/dist/components/layouts/index.cjs.map +0 -1
  191. package/dist/components/layouts/index.d.cts +0 -8
  192. package/dist/components/layouts/index.d.ts +0 -8
  193. package/dist/components/layouts/index.js +0 -3
  194. package/dist/components/layouts/index.js.map +0 -1
  195. package/dist/components/ui/badge.cjs +0 -3
  196. package/dist/components/ui/badge.cjs.map +0 -1
  197. package/dist/components/ui/badge.d.cts +0 -13
  198. package/dist/components/ui/badge.d.ts +0 -13
  199. package/dist/components/ui/badge.js +0 -3
  200. package/dist/components/ui/badge.js.map +0 -1
  201. package/dist/components/ui/button.cjs +0 -3
  202. package/dist/components/ui/button.cjs.map +0 -1
  203. package/dist/components/ui/button.d.cts +0 -14
  204. package/dist/components/ui/button.d.ts +0 -14
  205. package/dist/components/ui/button.js +0 -3
  206. package/dist/components/ui/button.js.map +0 -1
  207. package/dist/components/ui/calendar.cjs +0 -3
  208. package/dist/components/ui/calendar.cjs.map +0 -1
  209. package/dist/components/ui/calendar.d.cts +0 -13
  210. package/dist/components/ui/calendar.d.ts +0 -13
  211. package/dist/components/ui/calendar.js +0 -3
  212. package/dist/components/ui/calendar.js.map +0 -1
  213. package/dist/components/ui/card.cjs +0 -3
  214. package/dist/components/ui/card.cjs.map +0 -1
  215. package/dist/components/ui/card.d.cts +0 -12
  216. package/dist/components/ui/card.d.ts +0 -12
  217. package/dist/components/ui/card.js +0 -3
  218. package/dist/components/ui/card.js.map +0 -1
  219. package/dist/components/ui/checkbox.cjs +0 -3
  220. package/dist/components/ui/checkbox.cjs.map +0 -1
  221. package/dist/components/ui/checkbox.d.cts +0 -7
  222. package/dist/components/ui/checkbox.d.ts +0 -7
  223. package/dist/components/ui/checkbox.js +0 -3
  224. package/dist/components/ui/checkbox.js.map +0 -1
  225. package/dist/components/ui/command.cjs +0 -3
  226. package/dist/components/ui/command.cjs.map +0 -1
  227. package/dist/components/ui/command.d.cts +0 -20
  228. package/dist/components/ui/command.d.ts +0 -20
  229. package/dist/components/ui/command.js +0 -3
  230. package/dist/components/ui/command.js.map +0 -1
  231. package/dist/components/ui/dialog.cjs +0 -3
  232. package/dist/components/ui/dialog.cjs.map +0 -1
  233. package/dist/components/ui/dialog.d.cts +0 -16
  234. package/dist/components/ui/dialog.d.ts +0 -16
  235. package/dist/components/ui/dialog.js +0 -3
  236. package/dist/components/ui/dialog.js.map +0 -1
  237. package/dist/components/ui/dropdown-menu.cjs +0 -3
  238. package/dist/components/ui/dropdown-menu.cjs.map +0 -1
  239. package/dist/components/ui/dropdown-menu.d.cts +0 -28
  240. package/dist/components/ui/dropdown-menu.d.ts +0 -28
  241. package/dist/components/ui/dropdown-menu.js +0 -3
  242. package/dist/components/ui/dropdown-menu.js.map +0 -1
  243. package/dist/components/ui/input.cjs +0 -3
  244. package/dist/components/ui/input.cjs.map +0 -1
  245. package/dist/components/ui/input.d.cts +0 -6
  246. package/dist/components/ui/input.d.ts +0 -6
  247. package/dist/components/ui/input.js +0 -3
  248. package/dist/components/ui/input.js.map +0 -1
  249. package/dist/components/ui/popover.cjs +0 -3
  250. package/dist/components/ui/popover.cjs.map +0 -1
  251. package/dist/components/ui/popover.d.cts +0 -10
  252. package/dist/components/ui/popover.d.ts +0 -10
  253. package/dist/components/ui/popover.js +0 -3
  254. package/dist/components/ui/popover.js.map +0 -1
  255. package/dist/components/ui/select.cjs +0 -3
  256. package/dist/components/ui/select.cjs.map +0 -1
  257. package/dist/components/ui/select.d.cts +0 -18
  258. package/dist/components/ui/select.d.ts +0 -18
  259. package/dist/components/ui/select.js +0 -3
  260. package/dist/components/ui/select.js.map +0 -1
  261. package/dist/components/ui/separator.cjs +0 -3
  262. package/dist/components/ui/separator.cjs.map +0 -1
  263. package/dist/components/ui/separator.d.cts +0 -7
  264. package/dist/components/ui/separator.d.ts +0 -7
  265. package/dist/components/ui/separator.js +0 -3
  266. package/dist/components/ui/separator.js.map +0 -1
  267. package/dist/components/ui/sheet.cjs +0 -3
  268. package/dist/components/ui/sheet.cjs.map +0 -1
  269. package/dist/components/ui/sheet.d.cts +0 -18
  270. package/dist/components/ui/sheet.d.ts +0 -18
  271. package/dist/components/ui/sheet.js +0 -3
  272. package/dist/components/ui/sheet.js.map +0 -1
  273. package/dist/components/ui/table.cjs +0 -3
  274. package/dist/components/ui/table.cjs.map +0 -1
  275. package/dist/components/ui/table.d.cts +0 -13
  276. package/dist/components/ui/table.d.ts +0 -13
  277. package/dist/components/ui/table.js +0 -3
  278. package/dist/components/ui/table.js.map +0 -1
  279. package/dist/components/ui/toggle.cjs +0 -3
  280. package/dist/components/ui/toggle.cjs.map +0 -1
  281. package/dist/components/ui/toggle.d.cts +0 -16
  282. package/dist/components/ui/toggle.d.ts +0 -16
  283. package/dist/components/ui/toggle.js +0 -3
  284. package/dist/components/ui/toggle.js.map +0 -1
  285. package/dist/components/ui/tooltip.cjs +0 -3
  286. package/dist/components/ui/tooltip.cjs.map +0 -1
  287. package/dist/components/ui/tooltip.d.cts +0 -10
  288. package/dist/components/ui/tooltip.d.ts +0 -10
  289. package/dist/components/ui/tooltip.js +0 -3
  290. package/dist/components/ui/tooltip.js.map +0 -1
  291. package/dist/context/app-meta/context.cjs +0 -3
  292. package/dist/context/app-meta/context.cjs.map +0 -1
  293. package/dist/context/app-meta/context.d.cts +0 -18
  294. package/dist/context/app-meta/context.d.ts +0 -18
  295. package/dist/context/app-meta/context.js +0 -3
  296. package/dist/context/app-meta/context.js.map +0 -1
  297. package/dist/context/app-meta/hooks.cjs +0 -3
  298. package/dist/context/app-meta/hooks.cjs.map +0 -1
  299. package/dist/context/app-meta/hooks.d.cts +0 -10
  300. package/dist/context/app-meta/hooks.d.ts +0 -10
  301. package/dist/context/app-meta/hooks.js +0 -3
  302. package/dist/context/app-meta/hooks.js.map +0 -1
  303. package/dist/context/app-meta/index.cjs +0 -3
  304. package/dist/context/app-meta/index.cjs.map +0 -1
  305. package/dist/context/app-meta/index.d.cts +0 -5
  306. package/dist/context/app-meta/index.d.ts +0 -5
  307. package/dist/context/app-meta/index.js +0 -3
  308. package/dist/context/app-meta/index.js.map +0 -1
  309. package/dist/context/app-meta/types.cjs +0 -3
  310. package/dist/context/app-meta/types.cjs.map +0 -1
  311. package/dist/context/app-meta/types.d.cts +0 -48
  312. package/dist/context/app-meta/types.d.ts +0 -48
  313. package/dist/context/app-meta/types.js +0 -2
  314. package/dist/context/app-meta/types.js.map +0 -1
  315. package/dist/context/index.cjs +0 -3
  316. package/dist/context/index.cjs.map +0 -1
  317. package/dist/context/index.d.cts +0 -5
  318. package/dist/context/index.d.ts +0 -5
  319. package/dist/context/index.js +0 -3
  320. package/dist/context/index.js.map +0 -1
package/README.md CHANGED
@@ -1,215 +1,466 @@
1
- # @akshar-technosoft/ui
2
-
3
- > A complete, type-safe UI component library featuring elegant layouts, data displays, and utility components. Built with TypeScript, Tailwind CSS, and React.
4
-
5
- ---
6
-
7
- ## 🌟 Features
8
-
9
- - **Layout System**: Pre-built, composable layout components (`AppContainer`, `AppHeader`, `AppContent`, `AppFooter`)
10
- - **Type Safety**: Fully typed components with intelligent prop completion
11
- - **Context-Aware**: Built-in app meta management for titles, breadcrumbs, and actions
12
- - **Utility-First**: Includes helpful utilities like `GeneralHelper` for formatting and UI helpers
13
- - **Customizable**: Themeable and extensible with Tailwind CSS
14
-
15
- ---
16
-
17
- ## 📦 Installation
18
-
19
- ```bash
20
- # Using npm
21
- npm install @akshar-technosoft/ui
22
-
23
- # Using yarn
24
- yarn add @akshar-technosoft/ui
25
-
26
- # Using pnpm
27
- pnpm add @akshar-technosoft/ui
28
- ```
29
-
30
- **Peer Dependencies**:
31
- ```bash
32
- npm install react react-dom clsx tailwind-merge lucide-react
33
- ```
34
-
35
- ---
36
-
37
- ## 🚀 Quick Start
38
-
39
- ### 1. Setup App Meta Provider
40
-
41
- ```tsx
42
- // App.tsx
43
- import { AppMetaProvider } from '@tmj/ui';
44
-
45
- function App() {
46
- return (
47
- <AppMetaProvider>
48
- <MyPage />
49
- </AppMetaProvider>
50
- );
51
- }
52
- ```
53
-
54
- ### 2. Use Layout Components
55
-
56
- ```tsx
57
- import {
58
- AppContainer,
59
- AppHeader,
60
- AppContent,
61
- AppFooter
62
- } from '@tmj/ui';
63
-
64
- function DashboardPage() {
65
- return (
66
- <AppContainer>
67
- <AppHeader />
68
- <AppContent>
69
- <h1>Dashboard Content</h1>
70
- </AppContent>
71
- <AppFooter />
72
- </AppContainer>
73
- );
74
- }
75
- ```
76
-
77
- ### 3. Manage Page Metadata
78
-
79
- ```tsx
80
- import { usePageTemplate } from '@tmj/ui/hooks';
81
-
82
- function ProductPage() {
83
- usePageTemplate({
84
- title: 'Product Details',
85
- breadcrumbs: [
86
- { label: 'Home', href: '/' },
87
- { label: 'Products', href: '/products' },
88
- { label: 'Current Product' }
89
- ],
90
- primaryActions: <Button>Edit Product</Button>
91
- });
92
-
93
- return <div>...</div>;
94
- }
95
- ```
96
-
97
- ---
98
-
99
- ## 🧩 Core Components
100
-
101
- ### Layout System
102
-
103
- | Component | Description |
104
- |-----------------|-------------|
105
- | `AppContainer` | Root layout container with spacing and padding controls |
106
- | `AppHeader` | Top navigation bar with title, breadcrumbs, and actions |
107
- | `AppContent` | Flexible content area with multiple layout modes |
108
- | `AppFooter` | Bottom bar with secondary actions and pagination |
109
-
110
- ### Utility Components
111
-
112
- ```tsx
113
- import { GeneralHelper } from '@tmj/ui';
114
-
115
- // Format currency
116
- const price = GeneralHelper.formatINR(1500, { compact: true }); // ₹1.5K
117
-
118
- // Status badge
119
- <GeneralHelper.StatusBadge status="active" variant="outline" />
120
-
121
- // Copy to clipboard
122
- <GeneralHelper.CopyToClipboard text="Copy me!" />
123
- ```
124
-
125
- ---
126
-
127
- ## 🛠️ Advanced Usage
128
-
129
- ### Custom Route Types
130
-
131
- ```tsx
132
- // lib/app-meta.ts
133
- import { createAppMetaContext } from '@tmj/ui/context/app-meta';
134
-
135
- type AppRoutes = '/' | '/dashboard' | '/products/:id';
136
-
137
- const { AppMetaProvider, useAppMeta } = createAppMetaContext<AppRoutes>();
138
-
139
- export { AppMetaProvider, useAppMeta };
140
- ```
141
-
142
- ### Theming
143
-
144
- ```tsx
145
- // tailwind.config.js
146
- module.exports = {
147
- theme: {
148
- extend: {
149
- colors: {
150
- primary: {
151
- DEFAULT: 'var(--color-primary)',
152
- light: 'var(--color-primary-light)',
153
- }
154
- }
155
- }
156
- }
157
- }
158
- ```
159
-
160
- ---
161
-
162
- ## 📚 API Reference
163
-
164
- ### AppMeta Context
165
-
166
- | Hook/Component | Description |
167
- |---------------|-------------|
168
- | `AppMetaProvider` | Provides context for page metadata |
169
- | `useAppMeta()` | Access and update app metadata |
170
- | `usePageTemplate()` | Hook to set page metadata |
171
-
172
- ### GeneralHelper Utilities
173
-
174
- | Method | Description |
175
- |--------|-------------|
176
- | `formatINR()` | Format numbers as Indian Rupees |
177
- | `formatDate()` | Date formatting utilities |
178
- | `toProperCase()` | String case conversion |
179
- | `StatusBadge` | Configurable status indicator |
180
- | `CopyToClipboard` | Clipboard copy component |
181
-
182
- ---
183
-
184
- ## 🎨 Design Philosophy
185
-
186
- 1. **Consistent**: Unified design language across components
187
- 2. **Composable**: Mix and match layout components as needed
188
- 3. **Type-Safe**: Full TypeScript support with intelligent hints
189
- 4. **Performant**: Optimized for fast rendering and updates
190
- 5. **Accessible**: Built with WAI-ARIA standards
191
-
192
- ---
193
-
194
- ## 🛣️ Roadmap
195
-
196
- - [x] Core layout components
197
- - [x] App meta management system
198
- - [x] Data table component
199
- - [x] Data template component
200
- - [] Form components
201
- - [x] Dark mode support
202
- - [] Component playground
203
-
204
- ---
205
-
206
- ## 🤝 Contributing
207
-
208
- We welcome contributions! Please open an issue to discuss your ideas or submit a PR.
209
-
210
- ---
211
-
212
- ## 📄 License
213
-
214
- MIT © [Tirth Jasoliya](https://github.com/tirth-jo7)
215
- ```
1
+ # @akshar-technosoft/ui
2
+
3
+ Type-safe React component library for data-heavy ERP screens: a full-featured `DataTable`, a card/grid `DataTemplate`, a composable page-layout system and utility components. Built on TypeScript, Tailwind CSS v4, TanStack Table v8 and Radix.
4
+
5
+ ```bash
6
+ npm install @akshar-technosoft/ui
7
+ ```
8
+
9
+ Colors ride the app's theme CSS variables (`--sidebar`, `--border`, `--background`…), so the library reskins with the app theme automatically. The consuming app's Tailwind must scan this package (via `@source` or content config) so the classes compile.
10
+
11
+ ## Exports at a glance
12
+
13
+ | Export | What it is |
14
+ |---|---|
15
+ | `DataTable`, `DataTableProps`, `GroupColumnDef` | The data table and its types |
16
+ | `DataTableActionBar`, `DataTableActionBarAction`, `DataTableActionBarSelection` | Floating bulk-action bar for selected rows |
17
+ | `getTableExportView`, `TableExportView`, `TableExportColumn` | Export snapshot builder (see [Export](#export)) |
18
+ | `readPersistedExternal`, `PersistConfig`, `PersistGroupRegistry` | Persist helpers (see [Persist](#persist)) |
19
+ | `DataTemplate`, `DataTemplateProps` + action-bar components | Card/grid sibling of DataTable |
20
+ | `AppContainer`, `AppHeader`, `AppContent`, `AppContentHeader`, `AppContentFooter`, `AppSheet`, `AppFooter` | Page layout system |
21
+ | `GeneralHelper` | Static utils: `formatDate`, `formatINR`, `toProperCase`, `copyToClipboard`, `CopyToClipboard`, `StatusBadge` |
22
+ | `Loader`, `ErrorComponent`, `NotFound`, `Unauthorized`, `OfflineUI`, `CurrencyTransfer` | Full-area state screens |
23
+
24
+ The JSDoc in `data-table-types.ts` is the authoritative per-prop reference; hover any prop in the IDE. This README explains how the pieces fit and the recipes that aren't obvious from types.
25
+
26
+ ---
27
+
28
+ # DataTable
29
+
30
+ A client-side table: hand it `data`, it filters/sorts/paginates in memory. Server-side filtering is opt-in per filter (see [External filters](#external-server-filters)). Everything is plain props — no context, no provider.
31
+
32
+ ## Quick start
33
+
34
+ ```tsx
35
+ <DataTable
36
+ data={items}
37
+ columns={columns}
38
+ settings={{ showToolbar: true }}
39
+ pagination={{}}
40
+ filters={[{ key: "Status", type: "select", placeholder: "Status", options: statusOptions }]}
41
+ getRowId={(row) => String(row.Code)}
42
+ />
43
+ ```
44
+
45
+ ## Columns
46
+
47
+ Columns are TanStack `ColumnDef`s plus extras (`GroupColumnDef`). The three identity fields, and when each is needed:
48
+
49
+ | Field | What it does | When you need it |
50
+ |---|---|---|
51
+ | `accessorKey` | Points at a data field (dot paths like `"Party.Name"` work). Becomes the column id AND the value source for sorting, filtering, global search and export. | The normal case: the column shows one field. |
52
+ | `accessorFn` | Computes the column's value from the whole row. | The cell renders a composite (several fields together) but sorting / global search / export should still see it. |
53
+ | `id` | Explicit identity string. | Required when there is no `accessorKey` (display-only or `accessorFn` columns). It's what filters, aggregations and the Columns menu reference. |
54
+
55
+ `cell` controls **rendering only**. A column with just `id` + `cell` (an actions column, a JSX-composed cell) is invisible to sorting, global search and export — right for actions, usually wrong for composite data cells.
56
+
57
+ **Recipe — composite cell that global search can find.** A cell printing `LotNo`, `SKU` and `Category` together: give the column an `accessorFn` joining them. No filter config needed — global search matches accessor values.
58
+
59
+ ```tsx
60
+ {
61
+ id: "lotInfo",
62
+ accessorFn: (row) => `${row.LotNo} ${row.SKU} ${row.Category}`,
63
+ header: "Lot / SKU",
64
+ cell: ({ row }) => <LotCell data={row.original} />,
65
+ }
66
+ ```
67
+
68
+ Extras on `GroupColumnDef`:
69
+
70
+ - `headerAlign: 'left' | 'center' | 'right'` — header alignment (body cell alignment is your `cell`'s business).
71
+ - `columns: [...]` — nested columns under a grouped header row.
72
+ - `enablePinning: false` — remove this column from the pin menu.
73
+ - `customFooter: { content, align, className }` — a custom footer cell; `content` may be `(table) => ReactNode`. Beats the column's aggregation display.
74
+ - `exportValue` / `exportHeader` — see [Export](#export).
75
+ - `size` — fixed width in px; without it the column auto-sizes. Columns/Aggregations menus derive display names from the column id (`Party_Name` → "Party Name").
76
+
77
+ ## Sorting
78
+
79
+ On by default (`enableSorting`). Clicking a sortable header cycles asc → desc → clear; the header's `⋯` menu offers Sort Ascending / Sort Descending / Clear Sort explicitly (the active one disabled). The active direction shows as an arrow at the header's right edge; the `⋯` button and a faint sort hint appear on hover in a gradient overlay (no space is reserved, no text shifts). Clicks on interactive elements inside custom JSX headers do not sort. The serial "No." column sorts by display index — rarely meaningful.
80
+
81
+ ## Filters — the full story
82
+
83
+ The `filters` prop renders toolbar controls. **Filters live in the toolbar, so nothing renders without `settings.showToolbar: true`** (a dev warning fires) — the most common "my filter is missing" cause.
84
+
85
+ A filter takes one of two shapes:
86
+
87
+ ### 1. Path filter `key` (the common case)
88
+
89
+ `key` is a **dot path into the row data** (validated against your row type). It is NOT a column id, although the two often coincide. What happens depends on whether a column already owns that path:
90
+
91
+ - A column with the same `accessorKey` exists → the filter drives that column. Zero extra setup.
92
+ - No column owns the path → the table **auto-generates a hidden filter-only column** for it. You never touch `columns`.
93
+
94
+ ```tsx
95
+ filters={[
96
+ { key: "PartyDetails.Name", placeholder: "Party" }, // text contains (default)
97
+ { key: "Status", type: "select", placeholder: "Status", options },
98
+ { key: "Date", type: "daterange", placeholder: "Date", externalSearch: true },
99
+ ]}
100
+ ```
101
+
102
+ Hidden filter-only column internals (why they're free): they never render a cell (no render cost), do work only while their filter holds a value, are excluded from global search (no matches on data the user can't see) and from the Columns menu.
103
+
104
+ ### 2. Custom filter — `id` + `filterFn`
105
+
106
+ When the match is not a plain contains on one field — a cell holding an **array of objects**, or one control matching **across several fields** — you own the predicate. `filterFn(row, value)` receives the full row original and returns whether it passes. `id` is any unique string (need not be a real field). It also rides a hidden filter-only column and AND-combines with everything else.
107
+
108
+ ```tsx
109
+ {
110
+ id: "lotNo", type: "text", placeholder: "Lot No",
111
+ filterFn: (row, v) =>
112
+ row.OrderCodeJson.some(l => String(l.LotNo).toLowerCase().includes(String(v).toLowerCase())),
113
+ }
114
+ ```
115
+
116
+ ### Recipe one cell, two different filters
117
+
118
+ A cell shows Party and City together; you want independent Party and City filters. The cell is one column, but filters target **data paths**, not columns — declare two filters and you're done:
119
+
120
+ ```tsx
121
+ filters={[
122
+ { key: "Party.Name", placeholder: "Party" },
123
+ { key: "Party.City", placeholder: "City" },
124
+ ]}
125
+ ```
126
+
127
+ Each rides its own hidden filter-only column (or a real column if one owns the path). They AND-combine. The visible cell is untouched. If the cell should ALSO match in global search, that's the separate `accessorFn` recipe under [Columns](#columns).
128
+
129
+ ### Required vs optional
130
+
131
+ Required: `placeholder`, plus either `key` (path filter) or `id` + `filterFn` (custom filter). Everything else defaults:
132
+
133
+ | Field | Default | Notes |
134
+ |---|---|---|
135
+ | `type` | `"text"` | `text` (case-insensitive contains) · `select` (needs `options`, matched exactly) · `date` / `daterange` (picker, `dateFormat` for display) · `number` |
136
+ | `placement` | `"bar"` | `"menu"` collapses the control into the global search's field picker; while active it shows a removable chip (`settings.showFilterChips`) |
137
+ | `externalSearch` | `false` | Run on the server instead (below) |
138
+ | `width`, `icon` | — | Cosmetic (width = Tailwind class on the control wrapper) |
139
+ | `shared`, `persist` | — / `true` | See [Persist](#persist) |
140
+
141
+ ### External (server) filters
142
+
143
+ `externalSearch: true` = the value is **never applied to in-memory rows**; all staged external values are handed to `onExternalSearch(values)` so you re-query the API. Commit points are the control's natural ones: `select` fires on pick, `date`/`daterange` on Apply, `text` after a 500ms debounce. `values` is a map keyed by filter identity (`key`/`id`).
144
+
145
+ ```tsx
146
+ onExternalSearch={(values) => setSearchFilters(values)} // feed your query params
147
+ ```
148
+
149
+ Local and external filters mix freely on one table: external narrows what you fetch, local narrows the fetched rows.
150
+
151
+ ### Global search & the field picker
152
+
153
+ `enableGlobalFilter` (default on) renders the search box. It matches **accessor columns only** — a `cell`-only column is invisible to it (see the `accessorFn` recipe). It needs `enableFiltering` (the engine) to actually remove rows.
154
+
155
+ When any filter has `placement: 'menu'`, a **field picker** appears on the search box automatically: "All Fields" plus each menu filter. Picking a field swaps the free-text box for that filter's native typed control (select/date/etc.) and clears the free text so the two don't stack. `settings.groupedFilterLabel` renames the "all" label. Menu-placement filters with `enableGlobalFilter` off are unreachable (dev warning).
156
+
157
+ ### Clearing
158
+
159
+ A ghost "Clear Filters" button appears next to the controls whenever anything filters rows (column filters, global search, staged external values); the Settings menu has the same action. Clearing resets local filters, global search text, external values, the field picker AND the persisted snapshot (including this table's shared slots).
160
+
161
+ ## Pagination
162
+
163
+ The `pagination` prop's **presence enables it** — `{}` for defaults, omit to render all rows.
164
+
165
+ ```tsx
166
+ pagination={{ pageSize: 25, pageSizeOptions: [10, 25, 50, -1], showPageNumbers: true }}
167
+ ```
168
+
169
+ Defaults: `pageSize: 10`, `pageSizeOptions: [10, 25, 50, 100, -1]`. `-1` renders "All"; `[]` hides the size selector. `showInfo` (default true) prints "Showing X to Y of Z". `showPageNumbers` (default false) adds numbered buttons with an ellipsis window; otherwise prev/next only.
170
+
171
+ ## Selection
172
+
173
+ `enableRowSelection` (default on) adds the leading checkbox column.
174
+
175
+ - `selectionMode: "multiple"` (default) — checkboxes + a select-all header checkbox (indeterminate when partial; `enableHeaderCheckbox: false` greys it while per-row stays usable). `"single"` — radio, whole row clickable, previous selection cleared; `showCheckbox: false` hides the radio but keeps the row clickable.
176
+ - `isRowSelectable={(row) => boolean}` failing rows grey out, select-all skips them, `onRowSelectionChange` never reports them.
177
+ - `onRowSelectionChange={(rows) => …}` selected row originals on every change.
178
+ - `defaultSelectedRows={(row) => boolean}` uncontrolled prefill, seeded once after the first non-empty data load, selectable rows only; re-seed by remounting with a React `key`.
179
+
180
+ **`getRowId` read this if selection ever "moves" to the wrong rows.** Without it, selection is keyed by array index: any refetch/reorder/insert makes index 2 a different record, and the wrong rows light up. Point it at your record's identity — required for reliable `defaultSelectedRows` too:
181
+
182
+ ```tsx
183
+ getRowId={(row) => String(row.Code)}
184
+ getRowId={(row) => `${row.Code}-${row.Type}`} // unique only as a pair
185
+ ```
186
+
187
+ Bulk actions on selected rows — the action bar via the render-prop. It portals to `document.body`, floats bottom-center, auto-shows while anything is selected, and Escape clears the selection:
188
+
189
+ ```tsx
190
+ <DataTable ...>
191
+ {(table) => (
192
+ <DataTableActionBar table={table}>
193
+ <DataTableActionBarSelection table={table} /> {/* "N selected" + clear */}
194
+ <DataTableActionBarAction tooltip="Issue" isPending={busy} onClick={...}>
195
+ Issue
196
+ </DataTableActionBarAction>
197
+ </DataTableActionBar>
198
+ )}
199
+ </DataTable>
200
+ ```
201
+
202
+ ## Aggregations
203
+
204
+ Parent-controlled footer computations over the **filtered** rows: `sum`, `average`, `min`, `max`, `count`, `unique`, `median`.
205
+
206
+ ```tsx
207
+ const [aggs, setAggs] = useState<AggregationConfigMap<Row>>({
208
+ Qty: { enabled: true, functions: ["sum"] },
209
+ });
210
+
211
+ <DataTable
212
+ aggregations={aggs}
213
+ onAggregationChange={(colId, config) => setAggs(prev => ({ ...prev, [colId]: config }))}
214
+ />
215
+ ```
216
+
217
+ The sticky footer row appears when any config is `enabled`; the serial column's footer cell then shows the total row count. Non-numeric cells are dropped (not coerced to 0). `config.accessor` overrides the value source; `config.format(value, func)` overrides display (default: counts as integers, others 2 decimals). A column's `customFooter` wins over its aggregation display, and the header shows a small calculator icon on aggregated columns.
218
+
219
+ The toolbar's **Aggregations menu** (user-facing toggles, per-column function submenu) renders only with `onAggregationChange` + `showToolbar` + at least one aggregatable column — auto-detected by sampling the first rows for numeric values, or forced with `meta: { aggregatable: true }` on the column.
220
+
221
+ ## Pinning, visibility, resizing
222
+
223
+ - **Pinning** (`enableColumnPinning`, default on): per-column via the header `⋯` menu (Pin Right / Unpin), bulk reset in the Settings menu. Serial and select columns are always pinned left and re-asserted on every change. A pinned column is clamped to its live rendered width so sticky offsets stay true.
224
+ - **Visibility** (`enableColumnVisibility`, default on): the toolbar Columns menu toggles columns (grouped columns listed under their group); the header `⋯` menu offers Hide. Un-hiding lives only in the Columns menu — a hidden column has no header to click. `enableHiding: false` keeps a column out of all of it.
225
+ - **Resizing** (`enableColumnResizing`, default OFF): live drag handles on header right edges. Pinned columns are size-locked — resize first, pin after.
226
+
227
+ ## Persist
228
+
229
+ Opt-in state persistence across unmount/back-navigation:
230
+
231
+ ```tsx
232
+ persist="qc-completed" // shorthand for { key }
233
+ persist={{ key: "qc-completed", group: "inhouse", local: true }}
234
+ ```
235
+
236
+ **Persists:** column filter values, global search text, the field-picker selection, external (server) filter values, pagination (`pagination: false` to skip page state). **Never persists:** selection, sorting, visibility, pinning, sizing.
237
+
238
+ **How it works:** values are stored per **filter identity** (`key`/`id`), not as raw table state, in an in-memory map — a refresh starts clean. `local: true` mirrors to localStorage (`atsui:tbl:v1:*`), surviving refresh; note it's per-browser, not per-login. Restore is synchronous, seeded before first render — no flash of unfiltered data. Dates are revived from ISO strings. Per-filter opt-out: `persist: false` on that filter.
239
+
240
+ **`key` must be app-unique.** Two mounted tables on one key overwrite each other's state (dev warning). One page with two tables = two keys.
241
+
242
+ ### Shared slots — one value across pages
243
+
244
+ `shared: "party"` on a filter publishes its VALUE into a named slot; every table in the same `persist.group` with a filter bound to that slot picks it up — even when the pages use different filter `key`s, or one side is an `externalSearch` filter (values are stored by identity, which is what makes this possible). Search a party on one page; the next page in the group opens already filtered to it. Two tables mounted at once live-sync through the slot.
245
+
246
+ `shared` REQUIRES `persist.group`. Slots never cross groups — "party" in group `inhouse` is not "party" in `sales`. A `shared` filter on a group-less table is inert and warns.
247
+
248
+ ### Typing `group` / `shared` — the registry file
249
+
250
+ Augment `PersistGroupRegistry` once and both become validated unions instead of plain strings:
251
+
252
+ ```ts
253
+ // src/types/table-persist.d.ts
254
+ declare module "@akshar-technosoft/ui" {
255
+ interface PersistGroupRegistry {
256
+ inhouse: "party" | "material";
257
+ sales: "party";
258
+ }
259
+ }
260
+ ```
261
+
262
+ After this `group: "inhose"` is a compile error and `shared` autocompletes.
263
+
264
+ ### The double API call, and how to fix it
265
+
266
+ With persisted **external** filters, mount order is: the page fires its default query → the table replays restored external values through `onExternalSearch` → the page queries AGAIN with filters. Two requests, the first wasted.
267
+
268
+ **Fix A (proper):** seed the page's initial query params from the persisted values, so the FIRST request already carries them:
269
+
270
+ ```tsx
271
+ import { readPersistedExternal } from "@akshar-technosoft/ui";
272
+
273
+ // before — default query fires unfiltered, then re-fires filtered
274
+ const [params, setParams] = useState({ Status: "Pending" });
275
+
276
+ // after — first query is already filtered
277
+ const [params, setParams] = useState(() => ({
278
+ Status: "Pending",
279
+ ...readPersistedExternal("qc-completed"), // same key as the table's persist
280
+ }));
281
+ ```
282
+
283
+ The mount replay then commits the same values the query already used; with React Query the identical query key dedupes, and a manual fetch effect should compare params before refetching.
284
+
285
+ **Fix B (pragmatic):** don't persist that filter — `persist: false` on the external filter. The rest of the table still persists; the server filter starts clean each visit. Right when the double call is cheaper than the wiring.
286
+
287
+ ## Export
288
+
289
+ The table never writes files. It hands you a clean snapshot and the app owns the writer — per-page, fully customizable Excel/CSV builders, no spreadsheet dependency inside the library:
290
+
291
+ ```tsx
292
+ onExport={(data, view) => buildQcExcel(view)} // Settings menu > Export (item appears only when onExport is passed)
293
+
294
+ // or anywhere you hold the table instance (onTableReady / children render-prop):
295
+ const view = getTableExportView(table);
296
+ ```
297
+
298
+ `TableExportView` = `columns` (visible, display+pin order, select/serial excluded) + `rows` (cell values in column order, native types preserved so writers can format real numbers/dates) + `originals` (filtered+sorted row objects). Rows come from the pre-pagination model: filtered and sorted, ALL pages.
299
+
300
+ A column is included when it has an accessor or `exportValue`. Display-only columns are skipped unless `exportValue` supplies a flat value — also the tool for JSX composite cells:
301
+
302
+ ```tsx
303
+ exportValue: (row) => `${row.RefNo} (${GeneralHelper.formatDate(row.RefDate)})`,
304
+ exportHeader: "Reference", // needed only when `header` is JSX
305
+ ```
306
+
307
+ ## Toolbar & the Settings menu
308
+
309
+ `showToolbar` (default OFF) is the largest master switch — it hosts the bar filters + chips + Clear Filters, the global search with field picker, the Aggregations menu, the Columns menu, the Settings menu and Refresh (`onRefresh`, disabled while `loading`). The header's per-column `⋯` menus work without it. The whole toolbar is one wrap row: overflowing controls drop line by line, the right cluster stays right-aligned.
310
+
311
+ `toolbarPosition: 'above-header' | 'below-header'` moves the toolbar inside the card as a sticky band that stacks with the `header` prop (band heights are measured at runtime for correct sticky offsets).
312
+
313
+ Settings (gear) menu: Export (when `onExport` given) · Clear All Sorting · Clear All Filters · Clear Aggregations · Reset All Pinning — each disabled while there's nothing to clear.
314
+
315
+ ## Settings reference
316
+
317
+ Taxonomy: `show*` = a UI element/region is visible · `enable*` = a capability is on · bare adjectives = presentation.
318
+
319
+ | Setting | Default | Notes |
320
+ |---|---|---|
321
+ | `showHeader` | `true` | Off also removes sorting/pin/resize (they live in the header) |
322
+ | `showToolbar` | `false` | Master for search/filters/menus (above) |
323
+ | `showSerialNumbers` | `true` | "No." column — position in current view, not a record id |
324
+ | `showCheckbox` | `true` | Single-mode radio visibility (row stays clickable) |
325
+ | `showFilterChips` | `true` | Chips for active `placement:'menu'` filters |
326
+ | `enableSorting` | `true` | Header click cycle + `⋯` menu |
327
+ | `enableFiltering` | `true` | The engine; off = boxes render but no rows are removed |
328
+ | `enableGlobalFilter` | `true` | Search box (accessor columns only) |
329
+ | `enableColumnVisibility` | `true` | Columns menu + per-column Hide |
330
+ | `enableRowSelection` | `true` | Master for the whole selection group |
331
+ | `enableColumnResizing` | `false` | Live drag handles |
332
+ | `enableColumnPinning` | `true` | Master for pinning |
333
+ | `enableHeaderCheckbox` | `true` | `false` greys select-all only |
334
+ | `fixedCheckboxColumn` | `true` | Locks the select column's pin |
335
+ | `selectionMode` | `"multiple"` | or `"single"` |
336
+ | `striped` / `hoverable` / `compact` / `bordered` | `true` | Presentation |
337
+ | `fullHeight` | `false` | Card flexes to a bounded parent height (pair with `maxHeight`) |
338
+ | `toolbarPosition` | `'default'` | `'above-header'` / `'below-header'` |
339
+ | `groupedFilterLabel` | `"Global"` | Field picker "all" label |
340
+
341
+ Remaining props: `header` (node or `{ content, align, className, showOnEmpty }` — a sticky title band inside the card), `footer` (sticky full-width bottom band), `maxHeight` (scroll container, default `"100%"`), `loading` (overlay), `error` + `onRefresh` (error card with Try Again), `emptyMessage`, `rowClassName={(row) => class}` (status-based row colouring), `onTableReady={(table) => …}` (imperative escape hatch — fires once per table instance), `children={(table) => …}` (render-prop, e.g. the action bar).
342
+
343
+ ## Troubleshooting
344
+
345
+ - **Filter not rendering** → `settings.showToolbar: true` missing (dev warning fires).
346
+ - **Global search can't find a value that's visibly on screen** → that column has no accessor; add `accessorFn`.
347
+ - **A `key` filter does nothing** → the path doesn't match the data shape (it's a data path, not a column id).
348
+ - **Wrong rows selected after a refetch** → pass `getRowId`.
349
+ - **`shared` does nothing** → no `persist.group` on the table (warns).
350
+ - **Two tables fight over saved state** → same `persist.key` (warns).
351
+ - **Field picker missing** → needs `enableGlobalFilter` on AND at least one `placement: 'menu'` filter.
352
+ - **Two API calls on pages with persisted external filters** → see [the double API call](#the-double-api-call-and-how-to-fix-it).
353
+ - **Aggregations menu missing** → needs `onAggregationChange`, `showToolbar`, and a numeric (or `meta.aggregatable`) column.
354
+
355
+ ---
356
+
357
+ # DataTemplate
358
+
359
+ DataTable's sibling for card/grid layouts: same toolbar/search/filter/pagination shell, but YOU render each item. `fields` declares which keys are sortable/searchable; `filters` is a simpler flat-key config. Defaults that differ from DataTable: **`showToolbar` is `true`**, **selection is OFF** (`enableSelection: false`). Grid shape via `settings.gridCols` (`{ default: 3, sm: 1, md: 2, lg: 3, xl: 4, "2xl": 6 }`) and `gap`.
360
+
361
+ ```tsx
362
+ <DataTemplate
363
+ data={products}
364
+ template={(item, index, isSelected, toggleSelect) => (
365
+ <ProductCard item={item} selected={isSelected} onClick={toggleSelect} />
366
+ )}
367
+ fields={[{ key: "Name", label: "Name", searchable: true, sortable: true }]}
368
+ pagination={{}}
369
+ />
370
+ ```
371
+
372
+ `onTemplateReady` / `children` receive a controller (`getSelectedItems`, `selectAll`, `clearSelection`, `getFilteredData`, `refresh`, `exportData`…), and it has its own `DataTemplateActionBar` family.
373
+
374
+ ---
375
+
376
+ # Layout system
377
+
378
+ Zero context, zero factories — plain composable components. The standard page:
379
+
380
+ ```tsx
381
+ <AppContainer>
382
+ <AppHeader title="Quality Check" description="Pending takas" backAction={{ href: "/manufacturing" }}>
383
+ <Button>New Entry</Button> {/* lands right-aligned automatically */}
384
+ </AppHeader>
385
+
386
+ <AppContent> {/* fills remaining height, body scrolls */}
387
+ <AppContentHeader>…pinned band above the scroll…</AppContentHeader>
388
+ <DataTable ... />
389
+ <AppContentFooter>…pinned band below…</AppContentFooter>
390
+ <AppSheet open={open} onOpenChange={setOpen}>…side panel…</AppSheet>
391
+ </AppContent>
392
+
393
+ <AppFooter>
394
+ <span>3 selected</span> {/* leading children sit left */}
395
+ <div className="flex items-center gap-4"> {/* LAST child → pushed right */}
396
+ <Button variant="secondary">Cancel</Button>
397
+ <Button>Save</Button>
398
+ </div>
399
+ </AppFooter>
400
+ </AppContainer>
401
+ ```
402
+
403
+ ## AppContainer
404
+
405
+ Full-height flex column, `gap-2`. Props: `children`, `className`.
406
+
407
+ ## AppHeader
408
+
409
+ Props: `title`, `description`, `backAction: { href?, onClick? }` (back-arrow button; `href` is a typed route once the router registry is augmented), `metadata: [{ label, value }]` (small label/value pairs under the title; falsy values skipped), `children`, `childrenClassName`, `className`.
410
+
411
+ The title block takes only the space it needs. `children` render inside a baked `flex flex-1 items-center justify-end gap-4 h-full` row — content lands right-aligned and vertically centered with no wrapper. Different layout: `childrenClassName="justify-between"` etc. (twMerge — yours wins), or a `<div className="flex-1" />` spacer / `mr-auto` on a child.
412
+
413
+ **Fragment gotcha (header AND footer):** a fragment `<>A B</>` as a direct child melts into multiple DOM children, and positional CSS (`gap`, `last-child`) treats them separately. Wrap grouped elements in a `div`.
414
+
415
+ ## AppContent
416
+
417
+ The page's content slot: fills the remaining space of its flex parent (`flex-auto min-h-0`), rounded `bg-background` card, children scroll inside. Props: `className` (root — `p-0`, `border shadow-sm`, `flex-none` for a content-sized block), `contentClassName` (inner scroll wrapper — has `p-2`; pass `p-0` to drop it, `flex` to make the body a flex row).
418
+
419
+ Three **marker components** work only as DIRECT children — AppContent extracts them and renders the real thing; they render nothing on their own:
420
+
421
+ - `AppContentHeader` / `AppContentFooter` — pinned bands outside the scroll region (`border-b` / `border-t`, own `p-2`, `className` to adjust). For toolbars/summaries that must not scroll away.
422
+ - `AppSheet` — the side panel (one per AppContent, controlled only):
423
+
424
+ ```tsx
425
+ <AppSheet open={open} onOpenChange={setOpen} side="right" className="w-[600px]">
426
+ <PartyDetail … />
427
+ </AppSheet>
428
+ ```
429
+
430
+ | Prop | Default | Notes |
431
+ |---|---|---|
432
+ | `open` / `onOpenChange` | required | Controlled only |
433
+ | `side` | `"right"` | or `"left"` |
434
+ | `overlay` | `true` | Slides over the dimmed body; click outside closes. `overlay={false}` = push mode: the sheet takes its width and the body shrinks, animated both ways. Below 1024px push auto-falls back to overlay — a fixed panel would cramp the main content |
435
+ | `className` | `w-[420px] max-w-full` | Merged (twMerge): override width (`w-1/3`, `w-[600px]`) or add styling (`p-0`). Sheets want a FIXED comfortable width — the MAIN content is the responsive part |
436
+
437
+ ## AppFooter
438
+
439
+ Flex row with `p-2`, `gap-4`, and **the last child pushed to the right edge** (`[&>*:last-child]:ml-auto`) — actions go last, anything before them sits left. Renders nothing without children. Override via `className`.
440
+
441
+ ---
442
+
443
+ # Utilities & core components
444
+
445
+ `GeneralHelper` — static, no setup:
446
+
447
+ ```tsx
448
+ GeneralHelper.formatDate(d) // "07-07-2026" (dd-mm-yyyy default)
449
+ GeneralHelper.formatDate(d, { format: "dd-mmm-yyyy" }) // "07-Jul-2026"; also "iso" | "long" | "short", separator option
450
+ GeneralHelper.formatINR(125000) // "₹ 1,25,000.00"
451
+ GeneralHelper.formatINR(125000, { compact: true }) // "₹ 1.25L" style for >= 1 lakh
452
+ GeneralHelper.toProperCase("mill process") // "Mill Process" (toProperCaseAdvanced handles separators)
453
+ GeneralHelper.copyToClipboard(text) // promise; <GeneralHelper.CopyToClipboard text=... /> for the button
454
+ <GeneralHelper.StatusBadge status="Completed" />
455
+ ```
456
+
457
+ `Loader`, `ErrorComponent`, `NotFound`, `Unauthorized`, `OfflineUI` — full-area state screens. `CurrencyTransfer` — transfer/success display.
458
+
459
+ ---
460
+
461
+ # For maintainers
462
+
463
+ - Behavior reference = the JSDoc in `data-table-types.ts`; keep it and this README in sync when the API moves.
464
+ - Monorepo dev loop: after lib edits `npm run build`, copy `dist/` into the consuming app's `node_modules/@akshar-technosoft/ui/dist/`, restart the TS server.
465
+ - The table renders with `border-separate` (sticky cells drift ~1px under `border-collapse`). Consequence: borders must live on CELLS — a `border-b` on `<tr>` never paints in this mode.
466
+ - Radix dropdown/menu contents are portaled to `body`, but React synthetic events still bubble through the COMPONENT tree — any click-handling wrapper around a menu needs `stopPropagation` on the menu content (the header's click-to-sort learned this the hard way).