nexus-shared 1.1.21 → 3.0.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 (376) hide show
  1. package/CHANGELOG.md +262 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1000 -0
  4. package/dist/Client.Index.d.ts +55 -0
  5. package/dist/Client.Index.js +60 -0
  6. package/dist/Components/Chats/Chat.d.ts +28 -0
  7. package/dist/Components/Chats/Chat.js +19 -0
  8. package/dist/Components/Chats/ChatButton.d.ts +26 -0
  9. package/dist/Components/Chats/ChatButton.js +41 -0
  10. package/dist/Components/Chats/ChatComposer.d.ts +41 -0
  11. package/dist/Components/Chats/ChatComposer.js +177 -0
  12. package/dist/Components/Chats/ChatConversations.d.ts +35 -0
  13. package/dist/Components/Chats/ChatConversations.js +38 -0
  14. package/dist/Components/Chats/ChatPanel.d.ts +66 -0
  15. package/dist/Components/Chats/ChatPanel.js +94 -0
  16. package/dist/Components/Chats/ChatParts.d.ts +87 -0
  17. package/dist/Components/Chats/ChatParts.js +100 -0
  18. package/dist/Components/Chats/ChatThread.d.ts +22 -0
  19. package/dist/Components/Chats/ChatThread.js +96 -0
  20. package/dist/Components/Documents/Button.d.ts +29 -0
  21. package/dist/Components/Documents/Button.js +11 -0
  22. package/dist/Components/Documents/Menu.d.ts +102 -0
  23. package/dist/Components/Documents/Menu.js +242 -0
  24. package/dist/Components/Documents/SplitButton.d.ts +64 -0
  25. package/dist/Components/Documents/SplitButton.js +129 -0
  26. package/dist/Components/Documents/TabButtons.d.ts +80 -0
  27. package/dist/Components/Documents/TabButtons.js +38 -0
  28. package/dist/Components/Forms/ApiForm.d.ts +23 -0
  29. package/dist/Components/Forms/ApiForm.js +100 -0
  30. package/dist/Components/Forms/Crud.d.ts +17 -0
  31. package/dist/Components/Forms/Crud.js +708 -0
  32. package/dist/Components/Forms/CrudParts.d.ts +15 -0
  33. package/dist/Components/Forms/CrudParts.js +36 -0
  34. package/dist/Components/Forms/ExcelImport.d.ts +42 -0
  35. package/dist/Components/Forms/ExcelImport.js +190 -0
  36. package/dist/Components/Forms/Form.d.ts +52 -0
  37. package/dist/Components/Forms/Form.js +323 -0
  38. package/dist/Components/Forms/SubmitForm.d.ts +65 -0
  39. package/dist/Components/Forms/SubmitForm.js +198 -0
  40. package/dist/Components/Inputs/Calendar.d.ts +69 -0
  41. package/dist/Components/Inputs/Calendar.js +258 -0
  42. package/dist/Components/Inputs/CheckBox.d.ts +53 -0
  43. package/dist/Components/Inputs/CheckBox.js +87 -0
  44. package/dist/Components/Inputs/CheckBoxGroup.d.ts +20 -0
  45. package/dist/Components/Inputs/CheckBoxGroup.js +80 -0
  46. package/dist/Components/Inputs/ChoiceField.d.ts +122 -0
  47. package/dist/Components/Inputs/ChoiceField.js +169 -0
  48. package/dist/Components/Inputs/DatePicker.d.ts +54 -0
  49. package/dist/Components/Inputs/DatePicker.js +246 -0
  50. package/dist/Components/Inputs/DateRangePicker.d.ts +62 -0
  51. package/dist/Components/Inputs/DateRangePicker.js +283 -0
  52. package/dist/Components/Inputs/DateTimePicker.d.ts +75 -0
  53. package/dist/Components/Inputs/DateTimePicker.js +400 -0
  54. package/dist/Components/Inputs/Dropdown.d.ts +117 -0
  55. package/dist/Components/Inputs/Dropdown.js +839 -0
  56. package/dist/Components/Inputs/DropdownParts.d.ts +84 -0
  57. package/dist/Components/Inputs/DropdownParts.js +102 -0
  58. package/dist/Components/Inputs/EditorShell.d.ts +45 -0
  59. package/dist/Components/Inputs/EditorShell.js +214 -0
  60. package/dist/Components/Inputs/Field.d.ts +53 -0
  61. package/dist/Components/Inputs/Field.js +16 -0
  62. package/dist/Components/Inputs/GroupForm.d.ts +20 -0
  63. package/dist/Components/Inputs/GroupForm.js +172 -0
  64. package/dist/Components/Inputs/InputField.d.ts +59 -0
  65. package/dist/Components/Inputs/InputField.js +169 -0
  66. package/dist/Components/Inputs/InputRenderer.d.ts +37 -0
  67. package/dist/Components/Inputs/InputRenderer.js +99 -0
  68. package/dist/Components/Inputs/MarkdownEditor.d.ts +67 -0
  69. package/dist/Components/Inputs/MarkdownEditor.js +289 -0
  70. package/dist/Components/Inputs/NumberBox.d.ts +53 -0
  71. package/dist/Components/Inputs/NumberBox.js +228 -0
  72. package/dist/Components/Inputs/RadioGroup.d.ts +20 -0
  73. package/dist/Components/Inputs/RadioGroup.js +43 -0
  74. package/dist/Components/Inputs/ReadOnlyNotice.d.ts +36 -0
  75. package/dist/Components/Inputs/ReadOnlyNotice.js +117 -0
  76. package/dist/Components/Inputs/RichTextEditor.d.ts +103 -0
  77. package/dist/Components/Inputs/RichTextEditor.js +1170 -0
  78. package/dist/Components/Inputs/RichTextParts.d.ts +63 -0
  79. package/dist/Components/Inputs/RichTextParts.js +157 -0
  80. package/dist/Components/Inputs/RowsInput.d.ts +115 -0
  81. package/dist/Components/Inputs/RowsInput.js +308 -0
  82. package/dist/Components/Inputs/Slider.d.ts +87 -0
  83. package/dist/Components/Inputs/Slider.js +216 -0
  84. package/dist/Components/Inputs/TabularForm.d.ts +27 -0
  85. package/dist/Components/Inputs/TabularForm.js +348 -0
  86. package/dist/Components/Inputs/TextArea.d.ts +43 -0
  87. package/dist/Components/Inputs/TextArea.js +31 -0
  88. package/dist/Components/Inputs/TextBox.d.ts +52 -0
  89. package/dist/Components/Inputs/TextBox.js +58 -0
  90. package/dist/Components/Inputs/TextField.d.ts +31 -0
  91. package/dist/Components/Inputs/TextField.js +35 -0
  92. package/dist/Components/Inputs/TimePanel.d.ts +34 -0
  93. package/dist/Components/Inputs/TimePanel.js +288 -0
  94. package/dist/Components/Inputs/TimePicker.d.ts +54 -0
  95. package/dist/Components/Inputs/TimePicker.js +240 -0
  96. package/dist/Components/Inputs/TreeView.d.ts +160 -0
  97. package/dist/Components/Inputs/TreeView.js +947 -0
  98. package/dist/Components/Layouts/MessageBox.d.ts +71 -0
  99. package/dist/Components/Layouts/MessageBox.js +104 -0
  100. package/dist/Components/Layouts/Popup.d.ts +92 -0
  101. package/dist/Components/Layouts/Popup.js +376 -0
  102. package/dist/Components/Layouts/PopupContext.d.ts +2 -0
  103. package/dist/Components/Layouts/PopupContext.js +4 -0
  104. package/dist/Components/Layouts/PopupDock.d.ts +22 -0
  105. package/dist/Components/Layouts/PopupDock.js +121 -0
  106. package/dist/Components/Layouts/PopupHost.d.ts +56 -0
  107. package/dist/Components/Layouts/PopupHost.js +163 -0
  108. package/dist/Components/Layouts/PopupRules.d.ts +30 -0
  109. package/dist/Components/Layouts/PopupRules.js +63 -0
  110. package/dist/Components/Layouts/ThemePicker.d.ts +13 -0
  111. package/dist/Components/Layouts/ThemePicker.js +22 -0
  112. package/dist/Components/Layouts/ThemeScript.d.ts +14 -0
  113. package/dist/Components/Layouts/ThemeScript.js +13 -0
  114. package/dist/Components/Layouts/ThemeSwitcher.d.ts +13 -0
  115. package/dist/Components/Layouts/ThemeSwitcher.js +49 -0
  116. package/dist/Components/Layouts/Toaster.d.ts +76 -0
  117. package/dist/Components/Layouts/Toaster.js +172 -0
  118. package/dist/Components/Viewers/DataTable.d.ts +25 -0
  119. package/dist/Components/Viewers/DataTable.js +596 -0
  120. package/dist/Components/Viewers/DataTableColumns.d.ts +28 -0
  121. package/dist/Components/Viewers/DataTableColumns.js +243 -0
  122. package/dist/Components/Viewers/DataTableParts.d.ts +138 -0
  123. package/dist/Components/Viewers/DataTableParts.js +157 -0
  124. package/dist/Components/Viewers/MarkdownView.d.ts +26 -0
  125. package/dist/Components/Viewers/MarkdownView.js +79 -0
  126. package/dist/Components/Viewers/RichTextView.d.ts +18 -0
  127. package/dist/Components/Viewers/RichTextView.js +17 -0
  128. package/dist/Helpers/AnimationHelpers.d.ts +6 -0
  129. package/dist/Helpers/AnimationHelpers.js +35 -0
  130. package/dist/Helpers/ApiClient.d.ts +96 -0
  131. package/dist/Helpers/ApiClient.js +200 -0
  132. package/dist/Helpers/ApiFormHelpers.d.ts +49 -0
  133. package/dist/Helpers/ApiFormHelpers.js +120 -0
  134. package/dist/Helpers/ApiModules.d.ts +131 -0
  135. package/dist/Helpers/ApiModules.js +195 -0
  136. package/dist/Helpers/ApiResponses.d.ts +51 -0
  137. package/dist/Helpers/ApiResponses.js +250 -0
  138. package/dist/Helpers/ApiRoutes.d.ts +140 -0
  139. package/dist/Helpers/ApiRoutes.js +183 -0
  140. package/dist/Helpers/ApiToasts.d.ts +21 -0
  141. package/dist/Helpers/ApiToasts.js +22 -0
  142. package/dist/Helpers/ChatBackend.d.ts +134 -0
  143. package/dist/Helpers/ChatBackend.js +332 -0
  144. package/dist/Helpers/ChatHelpers.d.ts +189 -0
  145. package/dist/Helpers/ChatHelpers.js +486 -0
  146. package/dist/Helpers/ChatHooks.d.ts +81 -0
  147. package/dist/Helpers/ChatHooks.js +175 -0
  148. package/dist/Helpers/ChatStore.d.ts +132 -0
  149. package/dist/Helpers/ChatStore.js +394 -0
  150. package/dist/Helpers/ChatSummaries.d.ts +39 -0
  151. package/dist/Helpers/ChatSummaries.js +118 -0
  152. package/dist/Helpers/ClassHelpers.d.ts +3 -0
  153. package/dist/Helpers/ClassHelpers.js +4 -0
  154. package/dist/Helpers/CrudBackend.d.ts +98 -0
  155. package/dist/Helpers/CrudBackend.js +214 -0
  156. package/dist/Helpers/CrudHelpers.d.ts +111 -0
  157. package/dist/Helpers/CrudHelpers.js +267 -0
  158. package/dist/Helpers/DateHelpers.d.ts +88 -0
  159. package/dist/Helpers/DateHelpers.js +363 -0
  160. package/dist/Helpers/DatePreferences.d.ts +14 -0
  161. package/dist/Helpers/DatePreferences.js +94 -0
  162. package/dist/Helpers/DateTimeHelpers.d.ts +87 -0
  163. package/dist/Helpers/DateTimeHelpers.js +286 -0
  164. package/dist/Helpers/DragHelpers.d.ts +129 -0
  165. package/dist/Helpers/DragHelpers.js +281 -0
  166. package/dist/Helpers/DropdownHelpers.d.ts +56 -0
  167. package/dist/Helpers/DropdownHelpers.js +180 -0
  168. package/dist/Helpers/ExcelBackend.d.ts +106 -0
  169. package/dist/Helpers/ExcelBackend.js +290 -0
  170. package/dist/Helpers/ExcelHelpers.d.ts +98 -0
  171. package/dist/Helpers/ExcelHelpers.js +302 -0
  172. package/dist/Helpers/FormDrafts.d.ts +18 -0
  173. package/dist/Helpers/FormDrafts.js +76 -0
  174. package/dist/Helpers/FormStore.d.ts +233 -0
  175. package/dist/Helpers/FormStore.js +752 -0
  176. package/dist/Helpers/InputParamsHelpers.d.ts +79 -0
  177. package/dist/Helpers/InputParamsHelpers.js +388 -0
  178. package/dist/Helpers/MarkdownEditing.d.ts +27 -0
  179. package/dist/Helpers/MarkdownEditing.js +250 -0
  180. package/dist/Helpers/MarkdownHelpers.d.ts +82 -0
  181. package/dist/Helpers/MarkdownHelpers.js +775 -0
  182. package/dist/Helpers/MessageBuilder.d.ts +74 -0
  183. package/dist/Helpers/MessageBuilder.js +252 -0
  184. package/dist/Helpers/NepaliCalendar.d.ts +25 -0
  185. package/dist/Helpers/NepaliCalendar.js +107 -0
  186. package/dist/Helpers/NumberHelpers.d.ts +71 -0
  187. package/dist/Helpers/NumberHelpers.js +202 -0
  188. package/dist/Helpers/Permissions.d.ts +62 -0
  189. package/dist/Helpers/Permissions.js +133 -0
  190. package/dist/Helpers/PopoverHelpers.d.ts +68 -0
  191. package/dist/Helpers/PopoverHelpers.js +195 -0
  192. package/dist/Helpers/RichTextHelpers.d.ts +77 -0
  193. package/dist/Helpers/RichTextHelpers.js +1072 -0
  194. package/dist/Helpers/RowsStore.d.ts +166 -0
  195. package/dist/Helpers/RowsStore.js +606 -0
  196. package/dist/Helpers/TableColumns.d.ts +47 -0
  197. package/dist/Helpers/TableColumns.js +282 -0
  198. package/dist/Helpers/TableExport.d.ts +30 -0
  199. package/dist/Helpers/TableExport.js +97 -0
  200. package/dist/Helpers/TableHelpers.d.ts +110 -0
  201. package/dist/Helpers/TableHelpers.js +301 -0
  202. package/dist/Helpers/TableStore.d.ts +142 -0
  203. package/dist/Helpers/TableStore.js +530 -0
  204. package/dist/Helpers/ThemeHelpers.d.ts +15 -0
  205. package/dist/Helpers/ThemeHelpers.js +16 -0
  206. package/dist/Helpers/TimeHelpers.d.ts +73 -0
  207. package/dist/Helpers/TimeHelpers.js +253 -0
  208. package/dist/Helpers/TreeStore.d.ts +188 -0
  209. package/dist/Helpers/TreeStore.js +577 -0
  210. package/dist/Helpers/ValidationHelpers.d.ts +124 -0
  211. package/dist/Helpers/ValidationHelpers.js +301 -0
  212. package/dist/Interfaces/ApiInterfaces.d.ts +174 -0
  213. package/dist/Interfaces/ApiInterfaces.js +30 -0
  214. package/dist/Interfaces/ChatInterfaces.d.ts +240 -0
  215. package/{src/sso-client.tsx → dist/Interfaces/ChatInterfaces.js} +1 -1
  216. package/dist/Interfaces/CrudInterfaces.d.ts +232 -0
  217. package/dist/Interfaces/CrudInterfaces.js +1 -0
  218. package/dist/Interfaces/DateInterfaces.d.ts +154 -0
  219. package/dist/Interfaces/DateInterfaces.js +29 -0
  220. package/dist/Interfaces/ExcelInterfaces.d.ts +215 -0
  221. package/dist/Interfaces/ExcelInterfaces.js +45 -0
  222. package/dist/Interfaces/FormInterfaces.d.ts +402 -0
  223. package/dist/Interfaces/FormInterfaces.js +1 -0
  224. package/dist/Interfaces/InputInterfaces.d.ts +141 -0
  225. package/dist/Interfaces/InputInterfaces.js +1 -0
  226. package/dist/Interfaces/MessageInterfaces.d.ts +111 -0
  227. package/dist/Interfaces/MessageInterfaces.js +5 -0
  228. package/dist/Interfaces/TableInterfaces.d.ts +351 -0
  229. package/dist/Interfaces/TableInterfaces.js +1 -0
  230. package/dist/Interfaces/ThemeInterfaces.d.ts +17 -0
  231. package/dist/Interfaces/ThemeInterfaces.js +17 -0
  232. package/dist/Interfaces/TypeInterfaces.d.ts +4 -0
  233. package/dist/Interfaces/TypeInterfaces.js +1 -0
  234. package/dist/Server.Index.d.ts +2 -0
  235. package/dist/Server.Index.js +5 -0
  236. package/dist/Services/ApiProxy.d.ts +32 -0
  237. package/dist/Services/ApiProxy.js +111 -0
  238. package/dist/Services/BrowserApi.d.ts +95 -0
  239. package/dist/Services/BrowserApi.js +129 -0
  240. package/dist/Services/ChatApi.d.ts +37 -0
  241. package/dist/Services/ChatApi.js +98 -0
  242. package/dist/Services/ExcelApi.d.ts +46 -0
  243. package/dist/Services/ExcelApi.js +196 -0
  244. package/dist/Services/ServerApi.d.ts +54 -0
  245. package/dist/Services/ServerApi.js +78 -0
  246. package/dist/Services/ThemeService.d.ts +17 -0
  247. package/dist/Services/ThemeService.js +83 -0
  248. package/dist/Shared.Index.d.ts +51 -0
  249. package/dist/Shared.Index.js +55 -0
  250. package/package.json +85 -33
  251. package/src/Styles/Nexus.Base.css +70 -0
  252. package/src/Styles/Nexus.Button.css +153 -0
  253. package/src/Styles/Nexus.Calendar.css +275 -0
  254. package/src/Styles/Nexus.Chat.css +1489 -0
  255. package/src/Styles/Nexus.Choice.css +345 -0
  256. package/src/Styles/Nexus.Crud.css +161 -0
  257. package/src/Styles/Nexus.DateTime.css +81 -0
  258. package/src/Styles/Nexus.Dropdown.css +774 -0
  259. package/src/Styles/Nexus.Editor.css +297 -0
  260. package/src/Styles/Nexus.Excel.css +370 -0
  261. package/src/Styles/Nexus.Form.css +321 -0
  262. package/src/Styles/Nexus.Index.css +36 -0
  263. package/src/Styles/Nexus.Input.css +637 -0
  264. package/src/Styles/Nexus.Markdown.css +265 -0
  265. package/src/Styles/Nexus.Menu.css +259 -0
  266. package/src/Styles/Nexus.Popup.css +731 -0
  267. package/src/Styles/Nexus.RichText.css +463 -0
  268. package/src/Styles/Nexus.Rows.css +596 -0
  269. package/src/Styles/Nexus.Slider.css +278 -0
  270. package/src/Styles/Nexus.Tab.Buttons.css +419 -0
  271. package/src/Styles/Nexus.Table.css +1296 -0
  272. package/src/Styles/Nexus.Theme.Picker.css +232 -0
  273. package/src/Styles/Nexus.Themes.css +194 -0
  274. package/src/Styles/Nexus.Time.css +147 -0
  275. package/src/Styles/Nexus.Toast.css +265 -0
  276. package/src/Styles/Nexus.Tokens.css +72 -0
  277. package/src/Styles/Nexus.Tree.css +435 -0
  278. package/src/api-services/authentication-service.tsx +0 -23
  279. package/src/api-services/preference-service.tsx +0 -5
  280. package/src/api-services/system-service.tsx +0 -29
  281. package/src/client.ts +0 -31
  282. package/src/components/documents/button.tsx +0 -133
  283. package/src/components/documents/icon-box.tsx +0 -93
  284. package/src/components/documents/page-title.tsx +0 -7
  285. package/src/components/documents/tab-button.tsx +0 -172
  286. package/src/components/documents/tag.tsx +0 -30
  287. package/src/components/index.js +0 -0
  288. package/src/components/inputs/checkbox-input.tsx +0 -66
  289. package/src/components/inputs/input-box.tsx +0 -48
  290. package/src/components/inputs/input-element.tsx +0 -65
  291. package/src/components/inputs/input-form.tsx +0 -164
  292. package/src/components/inputs/input.tsx +0 -181
  293. package/src/components/inputs/number-input.tsx +0 -108
  294. package/src/components/inputs/radiobox-input.tsx +0 -53
  295. package/src/components/inputs/textarea-input.tsx +0 -47
  296. package/src/components/inputs/textbox-input.tsx +0 -45
  297. package/src/components/layouts/global-dialogbox.tsx +0 -431
  298. package/src/components/layouts/global-layout.tsx +0 -64
  299. package/src/components/layouts/layout-helpers.tsx +0 -20
  300. package/src/components/layouts/panels/user-panel.tsx +0 -112
  301. package/src/components/layouts/utility-menu.tsx +0 -49
  302. package/src/components/panels/theme-panel.tsx +0 -46
  303. package/src/helpers/bitwise-helpers.tsx +0 -11
  304. package/src/helpers/browser-helpers.tsx +0 -156
  305. package/src/helpers/datasource-helpers.tsx +0 -99
  306. package/src/helpers/element-helpers.tsx +0 -123
  307. package/src/helpers/input-helpers.tsx +0 -74
  308. package/src/helpers/string-helpers.tsx +0 -28
  309. package/src/helpers/utility-helpers.tsx +0 -45
  310. package/src/helpers/validation-helpers.tsx +0 -302
  311. package/src/index.ts +0 -26
  312. package/src/interface.ts +0 -19
  313. package/src/interfaces/auth-token-interfaces.tsx +0 -6
  314. package/src/interfaces/browser-interfaces.tsx +0 -36
  315. package/src/interfaces/button-interfaces.tsx +0 -63
  316. package/src/interfaces/datasource-interfaces.tsx +0 -22
  317. package/src/interfaces/datatable-interfaces.tsx +0 -25
  318. package/src/interfaces/dialogbox-interfaces.tsx +0 -5
  319. package/src/interfaces/exception-interfaces.tsx +0 -99
  320. package/src/interfaces/http-interfaces.tsx +0 -129
  321. package/src/interfaces/icon-interfaces.tsx +0 -129
  322. package/src/interfaces/input-interfaces.tsx +0 -410
  323. package/src/interfaces/layout-interfaces.tsx +0 -191
  324. package/src/interfaces/menu-interfaces.tsx +0 -53
  325. package/src/interfaces/message-interfaces.tsx +0 -32
  326. package/src/interfaces/permission-interfaces.tsx +0 -9
  327. package/src/interfaces/storage-interfaces.tsx +0 -5
  328. package/src/interfaces/system-interfaces.tsx +0 -22
  329. package/src/interfaces/theme-interfaces.tsx +0 -123
  330. package/src/interfaces/type-interfaces.tsx +0 -28
  331. package/src/interfaces/user-interfaces.tsx +0 -47
  332. package/src/nexus-client.tsx +0 -21
  333. package/src/nexus.environments.tsx +0 -66
  334. package/src/proxy-api/proxy-backend.tsx +0 -60
  335. package/src/proxy-api/proxy-constants.tsx +0 -28
  336. package/src/proxy-api/proxy-helpers.tsx +0 -91
  337. package/src/proxy-api-client.tsx +0 -1
  338. package/src/proxy-api-server.tsx +0 -2
  339. package/src/services/http-client-services.tsx +0 -75
  340. package/src/services/http-services.tsx +0 -158
  341. package/src/services/loader-service.tsx +0 -185
  342. package/src/services/localstorage-service.tsx +0 -119
  343. package/src/services/message-services.tsx +0 -383
  344. package/src/services/theme-service.tsx +0 -163
  345. package/src/services/user-services.tsx +0 -10
  346. package/src/sso-config/auth-token-validation.ts +0 -16
  347. package/src/sso-config/callback-route.tsx +0 -35
  348. package/src/sso-config/cookie-encryption.ts +0 -51
  349. package/src/sso-config/cookie-helpers.tsx +0 -102
  350. package/src/sso-config/forgot-password-route.tsx +0 -36
  351. package/src/sso-config/oauth-callback-state.ts +0 -61
  352. package/src/sso-config/pkce-helpers.ts +0 -9
  353. package/src/sso-config/provider-complete-route.tsx +0 -18
  354. package/src/sso-config/redirect-context-actions.tsx +0 -45
  355. package/src/sso-config/redirect-context-helpers.tsx +0 -159
  356. package/src/sso-config/redirect-context-persist.tsx +0 -18
  357. package/src/sso-config/redirect-context-route.tsx +0 -45
  358. package/src/sso-config/refresh-route.tsx +0 -23
  359. package/src/sso-config/sign-in-route.tsx +0 -74
  360. package/src/sso-config/sign-out-route.tsx +0 -57
  361. package/src/sso-config/sign-up-route.tsx +0 -57
  362. package/src/sso-config/sso-backend-flow.ts +0 -38
  363. package/src/sso-config/sso-complete-route.tsx +0 -50
  364. package/src/sso-config/sso-config.ts +0 -59
  365. package/src/sso-config/sso-interfaces.tsx +0 -99
  366. package/src/sso-config/sso-response-helpers.tsx +0 -20
  367. package/src/sso-server.tsx +0 -21
  368. package/src/styles/nexus.animation.css +0 -269
  369. package/src/styles/nexus.core.css +0 -119
  370. package/src/styles/nexus.dialog.css +0 -144
  371. package/src/styles/nexus.grid-view.css +0 -42
  372. package/src/styles/nexus.icon.css +0 -51
  373. package/src/styles/nexus.input.css +0 -207
  374. package/src/styles/nexus.loader.css +0 -11
  375. package/src/styles/nexus.logic.css +0 -47
  376. package/src/styles/nexus.utility.css +0 -347
@@ -0,0 +1,63 @@
1
+ import { type ReactNode, type Ref } from "react";
2
+ /** A color in the editor's palette: the value stored in the HTML, and a lighter one shown in dark themes. */
3
+ export interface RichTextColor {
4
+ name: string;
5
+ /** The stored color, for light pages, emails, and print. `null` removes the color. */
6
+ value: string | null;
7
+ /** How text in this color shows in a dark theme. */
8
+ dark?: string;
9
+ }
10
+ /** Text colors: dark enough for 4.5:1 on white, with a lighter twin for dark themes (Nexus.RichText.css). */
11
+ export declare const RICH_TEXT_COLORS: readonly RichTextColor[];
12
+ /** Highlights: light fills that keep dark text, in every theme. */
13
+ export declare const RICH_TEXT_HIGHLIGHTS: readonly RichTextColor[];
14
+ export interface ToolPanelHandle {
15
+ /** Opens the panel, as a click on its button does. */
16
+ open: () => void;
17
+ close: () => void;
18
+ }
19
+ interface ToolPanelProps {
20
+ label: string;
21
+ icon: ReactNode;
22
+ /** Shortcut for the button's tooltip, e.g. "Ctrl+K". */
23
+ shortcut?: string;
24
+ tabIndex: number;
25
+ /** Called before the panel opens, e.g. to read the selection it acts on. */
26
+ onOpen?: () => void;
27
+ /** The panel's content, rendered only while it is open. */
28
+ children: (close: () => void) => ReactNode;
29
+ className?: string;
30
+ ref?: Ref<ToolPanelHandle>;
31
+ /** `aria-pressed` of the button, e.g. while the caret is in a link. */
32
+ "data-state"?: string;
33
+ }
34
+ /**
35
+ * A toolbar button that opens a panel under it in a native popover. The button keeps the editor's focus and
36
+ * selection on press; the panel takes focus when it opens, and Esc or a press outside closes it and gives focus back.
37
+ */
38
+ export declare function ToolPanel({ label, icon, shortcut, tabIndex, onOpen, children, className, ref, ...rest }: ToolPanelProps): import("react").JSX.Element;
39
+ interface ColorPanelProps {
40
+ /** The colors at the selection, as `#rrggbb`, or `null` for none. */
41
+ color: string | null;
42
+ background: string | null;
43
+ onPick: (kind: "color" | "background", value: string | null) => void;
44
+ }
45
+ /** Text colors and highlights, each a grid of swatches: arrow keys move, Enter or a click picks. */
46
+ export declare function ColorPanel({ color, background, onPick }: ColorPanelProps): import("react").JSX.Element;
47
+ interface LinkPanelProps {
48
+ /** The address of the link at the selection, when there is one. */
49
+ href: string | null;
50
+ /** Asks for the text too: the selection is empty and not in a link. */
51
+ askText: boolean;
52
+ onApply: (url: string, text: string) => void;
53
+ onRemove: () => void;
54
+ onCancel: () => void;
55
+ }
56
+ /** The link box: the address (and the text, when nothing is selected), with Apply, Remove, and Cancel. */
57
+ export declare function LinkPanel({ href, askText, onApply, onRemove, onCancel }: LinkPanelProps): import("react").JSX.Element;
58
+ /**
59
+ * A typed address as a link: "nexus.com" gets https://, an email gets mailto:, a phone number gets tel:. `null` when it
60
+ * is empty or not safe (`javascript:` and the like).
61
+ */
62
+ export declare function normalizeLink(input: string): string | null;
63
+ export {};
@@ -0,0 +1,157 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { iconLinkOff, InlineIcon } from "nexus-icons";
4
+ import { useId, useImperativeHandle, useLayoutEffect, useRef, useState, } from "react";
5
+ import { cx } from "../../Helpers/ClassHelpers.js";
6
+ import { safeUrl } from "../../Helpers/MarkdownHelpers.js";
7
+ import { followAnchor, isPopoverOpen, placeUnderAnchor } from "../../Helpers/PopoverHelpers.js";
8
+ import { Button } from "../Documents/Button.js";
9
+ import { TextBox } from "./TextBox.js";
10
+ /** Text colors: dark enough for 4.5:1 on white, with a lighter twin for dark themes (Nexus.RichText.css). */
11
+ export const RICH_TEXT_COLORS = [
12
+ { name: "Default", value: null },
13
+ { name: "Gray", value: "#4b5563", dark: "#9ca3af" },
14
+ { name: "Red", value: "#b91c1c", dark: "#f87171" },
15
+ { name: "Orange", value: "#9a3412", dark: "#fb923c" },
16
+ { name: "Brown", value: "#92400e", dark: "#fbbf24" },
17
+ { name: "Green", value: "#166534", dark: "#4ade80" },
18
+ { name: "Teal", value: "#0f766e", dark: "#2dd4bf" },
19
+ { name: "Blue", value: "#1d4ed8", dark: "#60a5fa" },
20
+ { name: "Purple", value: "#7e22ce", dark: "#c084fc" },
21
+ { name: "Pink", value: "#be185d", dark: "#f472b6" },
22
+ ];
23
+ /** Highlights: light fills that keep dark text, in every theme. */
24
+ export const RICH_TEXT_HIGHLIGHTS = [
25
+ { name: "None", value: null },
26
+ { name: "Yellow", value: "#fef08a" },
27
+ { name: "Green", value: "#bbf7d0" },
28
+ { name: "Blue", value: "#bfdbfe" },
29
+ { name: "Purple", value: "#e9d5ff" },
30
+ { name: "Pink", value: "#fbcfe8" },
31
+ { name: "Orange", value: "#fed7aa" },
32
+ { name: "Gray", value: "#e5e7eb" },
33
+ ];
34
+ /**
35
+ * A toolbar button that opens a panel under it in a native popover. The button keeps the editor's focus and
36
+ * selection on press; the panel takes focus when it opens, and Esc or a press outside closes it and gives focus back.
37
+ */
38
+ export function ToolPanel({ label, icon, shortcut, tabIndex, onOpen, children, className, ref, ...rest }) {
39
+ const id = useId();
40
+ const [open, setOpen] = useState(false);
41
+ const buttonRef = useRef(null);
42
+ const panelRef = useRef(null);
43
+ // A press on the button while the panel is open closes it (light dismiss); the click that follows must not reopen it.
44
+ const dismissingRef = useRef(false);
45
+ function show() {
46
+ const panel = panelRef.current;
47
+ if (!panel || isPopoverOpen(panel) || typeof panel.showPopover !== "function")
48
+ return;
49
+ onOpen?.();
50
+ // Shown before its content renders: a hidden popover cannot take focus.
51
+ panel.showPopover();
52
+ setOpen(true);
53
+ }
54
+ function close() {
55
+ if (isPopoverOpen(panelRef.current))
56
+ panelRef.current?.hidePopover();
57
+ }
58
+ useImperativeHandle(ref, () => ({ open: show, close }));
59
+ useLayoutEffect(() => {
60
+ const panel = panelRef.current;
61
+ const button = buttonRef.current;
62
+ if (!open || !panel || !button)
63
+ return;
64
+ const place = () => placeUnderAnchor(panel, button.getBoundingClientRect());
65
+ place();
66
+ (panel.querySelector("[data-autofocus]") ?? panel.querySelector("input, button"))?.focus();
67
+ return followAnchor(place);
68
+ }, [open]);
69
+ function onToggle(event) {
70
+ if (event.newState === "closed")
71
+ setOpen(false);
72
+ }
73
+ return (_jsxs(_Fragment, { children: [_jsx("button", { ref: buttonRef, type: "button", className: cx("nx-editor__tool", className), tabIndex: tabIndex, "aria-label": label, "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": id, title: shortcut ? `${label} (${shortcut})` : label, ...rest, onPointerDown: () => {
74
+ dismissingRef.current = isPopoverOpen(panelRef.current);
75
+ },
76
+ // The editor keeps its focus and selection.
77
+ onMouseDown: event => event.preventDefault(), onClick: () => {
78
+ if (dismissingRef.current)
79
+ dismissingRef.current = false;
80
+ else if (isPopoverOpen(panelRef.current))
81
+ close();
82
+ else
83
+ show();
84
+ }, children: icon }), _jsx("div", { ref: panelRef, id: id, popover: "auto", role: "dialog", "aria-label": label, className: "nx-popover nx-rte-panel", onToggle: onToggle, children: open && children(close) })] }));
85
+ }
86
+ /** Text colors and highlights, each a grid of swatches: arrow keys move, Enter or a click picks. */
87
+ export function ColorPanel({ color, background, onPick }) {
88
+ return (_jsxs("div", { className: "nx-rte-colors", children: [_jsx(SwatchGrid, { title: "Text color", kind: "color", colors: RICH_TEXT_COLORS, current: color, onPick: onPick, autofocus: true }), _jsx(SwatchGrid, { title: "Highlight", kind: "background", colors: RICH_TEXT_HIGHLIGHTS, current: background, onPick: onPick })] }));
89
+ }
90
+ const SWATCH_COLUMNS = 5;
91
+ function SwatchGrid({ title, kind, colors, current, onPick, autofocus }) {
92
+ const titleId = useId();
93
+ const selected = Math.max(0, colors.findIndex(color => color.value === current));
94
+ function onKeyDown(event) {
95
+ const buttons = [...event.currentTarget.querySelectorAll("button")];
96
+ const index = buttons.indexOf(document.activeElement);
97
+ const next = event.key === "ArrowRight" ? index + 1
98
+ : event.key === "ArrowLeft" ? index - 1
99
+ : event.key === "ArrowDown" ? index + SWATCH_COLUMNS
100
+ : event.key === "ArrowUp" ? index - SWATCH_COLUMNS
101
+ : event.key === "Home" ? 0
102
+ : event.key === "End" ? buttons.length - 1
103
+ : null;
104
+ if (next === null || index < 0)
105
+ return;
106
+ event.preventDefault();
107
+ const target = buttons[Math.min(Math.max(next, 0), buttons.length - 1)];
108
+ for (const button of buttons)
109
+ button.tabIndex = button === target ? 0 : -1;
110
+ target.focus();
111
+ }
112
+ return (_jsxs("div", { className: "nx-rte-colors__group", children: [_jsx("div", { id: titleId, className: "nx-rte-colors__title", children: title }), _jsx("div", { role: "group", "aria-labelledby": titleId, className: "nx-rte-colors__grid", onKeyDown: onKeyDown, children: colors.map((color, index) => {
113
+ const isSelected = index === selected;
114
+ const style = { "--nx-swatch": color.value ? (color.dark ? `light-dark(${color.value}, ${color.dark})` : color.value) : undefined };
115
+ return (_jsx("button", { type: "button", className: "nx-rte-colors__swatch", "data-kind": kind, "data-none": color.value ? undefined : "", "data-autofocus": autofocus && isSelected ? "" : undefined, "aria-label": color.value ? `${title}: ${color.name}` : color.name === "Default" ? "Default text color" : "No highlight", "aria-pressed": isSelected, title: color.value ? `${color.name} ${color.value}` : color.name, tabIndex: isSelected ? 0 : -1, style: style, onClick: () => onPick(kind, color.value), children: kind === "color" && color.value && _jsx("span", { "aria-hidden": "true", children: "A" }) }, color.name));
116
+ }) })] }));
117
+ }
118
+ /** The link box: the address (and the text, when nothing is selected), with Apply, Remove, and Cancel. */
119
+ export function LinkPanel({ href, askText, onApply, onRemove, onCancel }) {
120
+ const formId = useId();
121
+ const [error, setError] = useState(null);
122
+ const urlRef = useRef(null);
123
+ const textRef = useRef(null);
124
+ function apply(event) {
125
+ event?.preventDefault();
126
+ const url = normalizeLink(urlRef.current?.value ?? "");
127
+ if (!url) {
128
+ setError("Enter a web address, an email, or a phone number.");
129
+ urlRef.current?.focus();
130
+ return;
131
+ }
132
+ onApply(url, textRef.current?.value.trim() ?? "");
133
+ }
134
+ // Enter applies. The inputs belong to no form, so Enter never submits the form around the editor.
135
+ function onKeyDown(event) {
136
+ if (event.key === "Enter" && event.target.tagName === "INPUT")
137
+ apply(event);
138
+ }
139
+ return (_jsxs("div", { className: "nx-rte-link", onKeyDown: onKeyDown, children: [_jsx(TextBox, { ref: urlRef, form: formId, size: "sm", label: "Link address", placeholder: "https://", defaultValue: href ?? "", inputMode: "url", autoComplete: "off", spellCheck: false, error: error, onValueChange: () => setError(null), "data-autofocus": "" }), askText && _jsx(TextBox, { ref: textRef, form: formId, size: "sm", label: "Text", placeholder: "Shown instead of the address", autoComplete: "off" }), _jsxs("div", { className: "nx-rte-link__actions", children: [href && (_jsx(Button, { size: "sm", variant: "ghost", hoverTone: "danger", icon: _jsx(InlineIcon, { icon: iconLinkOff }), onClick: onRemove, children: "Remove" })), _jsx("span", { className: "nx-rte-link__spacer" }), _jsx(Button, { size: "sm", onClick: onCancel, children: "Cancel" }), _jsx(Button, { size: "sm", variant: "primary", onClick: () => apply(), children: "Apply" })] })] }));
140
+ }
141
+ /**
142
+ * A typed address as a link: "nexus.com" gets https://, an email gets mailto:, a phone number gets tel:. `null` when it
143
+ * is empty or not safe (`javascript:` and the like).
144
+ */
145
+ export function normalizeLink(input) {
146
+ const text = input.trim();
147
+ if (!text)
148
+ return null;
149
+ let url = text;
150
+ if (/^[^\s@/:]+@[^\s@/:]+\.[^\s@/:]+$/.test(text))
151
+ url = `mailto:${text}`;
152
+ else if (/^\+?[\d\s().-]{6,}$/.test(text) && /\d{3}/.test(text))
153
+ url = `tel:${text.replace(/[\s().-]/g, "")}`;
154
+ else if (/^(www\.|[a-z0-9-]+(\.[a-z0-9-]+)+([/:?#]|$))/i.test(text) && !/^[a-z][a-z0-9+.-]*:\/\//i.test(text))
155
+ url = `https://${text}`;
156
+ return safeUrl(url);
157
+ }
@@ -0,0 +1,115 @@
1
+ import { type FormEvent, type ReactNode } from "react";
2
+ import { RowsStore, type RowsSnapshot } from "../../Helpers/RowsStore.ts";
3
+ import type { InputParams, RowsInputProps, RowValue } from "../../Interfaces/FormInterfaces.ts";
4
+ import type { InputDesign } from "../../Interfaces/InputInterfaces.ts";
5
+ import type { Size } from "../../Interfaces/TypeInterfaces.ts";
6
+ /** Form names of inputs inside rows that need one (radio and checkbox groups). They are taken out of the form's data. */
7
+ export declare const ROW_INPUT_NAME = "nx-rows:";
8
+ /** Where focus goes in a row once it is on the page: an input by name, the row's first input, or its actions button. */
9
+ export type RowFocusTarget = string | {
10
+ first: true;
11
+ } | {
12
+ actions: true;
13
+ };
14
+ export interface RowsLayout {
15
+ /** Brings a row onto the page if needed and focuses a part of it. */
16
+ focusRow: (key: string, target: RowFocusTarget) => void;
17
+ }
18
+ /**
19
+ * The store behind a tabular or group form, kept in step with the props: rules, a controlled value, the value
20
+ * listener, a parent form's data and reset, and the handle given to `ref`.
21
+ */
22
+ export declare function useRowsInput<TRow extends RowValue>(props: RowsInputProps<TRow>, fallbackItem: string, layoutRef: {
23
+ current: RowsLayout | null;
24
+ }): {
25
+ store: RowsStore<TRow>;
26
+ snapshot: RowsSnapshot;
27
+ proxyRef: import("react").RefObject<HTMLInputElement | null>;
28
+ onInvalid: (event: FormEvent<HTMLInputElement>) => void;
29
+ canAdd: boolean;
30
+ actions: {
31
+ add(values?: Partial<TRow>, index?: number): string;
32
+ remove(key: string): void;
33
+ duplicate(key: string): void;
34
+ move(key: string, delta: number): void;
35
+ undo(): void;
36
+ };
37
+ cellLock: (key: string, name: string, index: number) => string | boolean | null | undefined;
38
+ rowLock: (key: string | null, index: number) => string | boolean | null | undefined;
39
+ cellParams: (key: string, params: InputParams, index: number) => InputParams;
40
+ };
41
+ /** What a layout asks for each cell: the reason it is generated, `true` for no reason, or nothing. */
42
+ export type CellLock = (key: string, name: string, index: number) => string | boolean | null | undefined;
43
+ /** What a layout asks for each cell: its own params, which are the column's unless `cellParams` answers for its row. */
44
+ export type CellParams = (key: string, params: InputParams, index: number) => InputParams;
45
+ /** What a layout asks for each row: the reason nobody may change it, `true` for no reason, or nothing. From `rowReadOnly`. */
46
+ export type RowLock = (key: string | null, index: number) => string | boolean | null | undefined;
47
+ /** The first control of an element that takes focus. */
48
+ export declare function firstFocusable(element: Element | null): HTMLElement | null;
49
+ /** Subscribes a row's component to its row: it renders again only when the row's messages or code-set values change. */
50
+ export declare function useRowVersion(store: RowsStore<RowValue>, key: string): number;
51
+ /** A row's number and name for labels: "row 2", "address 2". */
52
+ export declare function rowName(store: RowsStore<RowValue>, index: number): string;
53
+ /** The text of an input's label, for names built from it. */
54
+ export declare function labelText(params: InputParams): string;
55
+ export interface RowCellProps {
56
+ store: RowsStore<RowValue>;
57
+ rowKey: string;
58
+ params: InputParams;
59
+ index: number;
60
+ error: string | undefined;
61
+ layout: "tabular" | "group";
62
+ idPrefix: string;
63
+ size: Size;
64
+ design?: InputDesign;
65
+ readOnly: boolean;
66
+ disabled: boolean;
67
+ /** This one cell holds a generated value: the reason, `true` for no reason, or nothing. From `cellReadOnly`. */
68
+ lock?: string | boolean | null;
69
+ }
70
+ /**
71
+ * One input of one row. It starts from the store's value when it mounts and writes every change back, so typing renders
72
+ * nothing; a value set from code mounts it again (its key holds the value's revision). In a table the column header is
73
+ * the label, so the input is named "City, row 2" for screen readers and its messages still say "City".
74
+ */
75
+ export declare const RowCell: import("react").MemoExoticComponent<({ store, rowKey, params, index, error, layout, idPrefix, size, design, readOnly, disabled, lock }: RowCellProps) => ReactNode>;
76
+ /** The legend of the rows, with the required mark and, when read-only, a lock. */
77
+ export declare function RowsLegend({ id, label, required, lock }: {
78
+ id: string;
79
+ label: ReactNode;
80
+ required?: boolean;
81
+ lock?: ReactNode;
82
+ }): import("react").JSX.Element | null;
83
+ export interface RowsFooterProps {
84
+ messageId: string;
85
+ store: RowsStore<RowValue>;
86
+ snapshot: RowsSnapshot;
87
+ /** An error from outside, shown first. */
88
+ error?: string | null;
89
+ hint?: ReactNode;
90
+ /** Replaces the hint while it is set: in a table, the message or hint of the input with focus. */
91
+ status?: {
92
+ text: string;
93
+ error: boolean;
94
+ } | null;
95
+ addLabel: string;
96
+ addable: boolean;
97
+ canAdd: boolean;
98
+ max?: number;
99
+ maxMessage: string;
100
+ size: Size;
101
+ onAdd: () => void;
102
+ onUndo: () => void;
103
+ }
104
+ /** The line under the rows: Add, the rows' message or hint (or a removed row with Undo), and the count against `max`. */
105
+ export declare function RowsFooter({ messageId, store, snapshot, error, hint, status, addLabel, addable, canAdd, max, maxMessage, size, onAdd, onUndo }: RowsFooterProps): import("react").JSX.Element;
106
+ /** Ids, names, and read-only feedback for a rows input's frame. */
107
+ export declare function useRowsFrame(props: RowsInputProps<RowValue>, store: RowsStore<RowValue>): {
108
+ id: string;
109
+ legendId: string;
110
+ messageId: string;
111
+ lock: ReactNode;
112
+ addLabel: string;
113
+ emptyMessage: string | number | bigint | boolean | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined>;
114
+ maxMessage: string;
115
+ };
@@ -0,0 +1,308 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { iconAlertCircle, iconArrowBackUp, iconPlus, InlineIcon } from "nexus-icons";
4
+ import { memo, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useRef, useState, useSyncExternalStore, } from "react";
5
+ import { cx } from "../../Helpers/ClassHelpers.js";
6
+ import { useDatePreferences } from "../../Helpers/DatePreferences.js";
7
+ import { inputFieldName } from "../../Helpers/InputParamsHelpers.js";
8
+ import { RowsStore } from "../../Helpers/RowsStore.js";
9
+ import { Button } from "../Documents/Button.js";
10
+ import { hasContent } from "./Field.js";
11
+ import { isFirstInvalid } from "./InputField.js";
12
+ import { renderInput } from "./InputRenderer.js";
13
+ import { useReadOnlyNotice } from "./ReadOnlyNotice.js";
14
+ /*
15
+ * What TabularForm and GroupForm share: the store and its link to the page (the value, a parent form's data, reset, and
16
+ * submit), the cell that renders one input of one row, the legend, and the line under the rows. The two differ only
17
+ * in how they lay the rows out.
18
+ */
19
+ /** Form names of inputs inside rows that need one (radio and checkbox groups). They are taken out of the form's data. */
20
+ export const ROW_INPUT_NAME = "nx-rows:";
21
+ function sameRows(a, b) {
22
+ return a.length === b.length && a.every((row, index) => row === b[index]);
23
+ }
24
+ /**
25
+ * The store behind a tabular or group form, kept in step with the props: rules, a controlled value, the value
26
+ * listener, a parent form's data and reset, and the handle given to `ref`.
27
+ */
28
+ export function useRowsInput(props, fallbackItem, layoutRef) {
29
+ const { inputs, required, min, max, unique, validate, rowKey, newRow, onRowChange, deriveRows, itemLabel, itemLabelPlural, value, defaultValue, name, readOnly = false, disabled = false, ref } = props;
30
+ const preferences = useDatePreferences();
31
+ const options = { inputs, required, min, max, unique, validate, rowKey, newRow, onRowChange, deriveRows, itemLabel, itemLabelPlural, fallbackItem, context: { preferences } };
32
+ const [store] = useState(() => new RowsStore(options, value ?? defaultValue ?? []));
33
+ const snapshot = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
34
+ const proxyRef = useRef(null);
35
+ const latest = useRef({ props, readOnly, disabled });
36
+ latest.current = { props, readOnly, disabled };
37
+ // Rules that change what is valid check every row again; callbacks only swap.
38
+ const rulesKey = [inputs, required, min, max, unique?.join(","), itemLabel, itemLabelPlural, preferences];
39
+ const rulesRef = useRef(rulesKey);
40
+ useLayoutEffect(() => {
41
+ const changed = rulesKey.some((item, index) => !Object.is(item, rulesRef.current[index]));
42
+ rulesRef.current = rulesKey;
43
+ store.configure(options, changed);
44
+ });
45
+ // A controlled value that is not the one the store gave loads anew. The same rows in a new array change nothing.
46
+ useLayoutEffect(() => {
47
+ if (value === undefined || value === store.lastValue || (store.lastValue && sameRows(value, store.lastValue)))
48
+ return;
49
+ store.replace(value);
50
+ }, [store, value]);
51
+ /** The form's submit is blocked while any row has a problem, with the first message; read-only rows are not checked. */
52
+ const syncValidity = useCallback(() => {
53
+ const proxy = proxyRef.current;
54
+ if (!proxy)
55
+ return;
56
+ const invalid = !latest.current.readOnly && store.hasProblems();
57
+ proxy.setCustomValidity(invalid ? (store.firstMessage() ?? "Check the rows.") : "");
58
+ }, [store]);
59
+ useEffect(() => {
60
+ syncValidity();
61
+ return store.subscribeValue(rows => {
62
+ syncValidity();
63
+ latest.current.props.onValueChange?.(rows);
64
+ });
65
+ }, [store, syncValidity]);
66
+ useEffect(syncValidity);
67
+ // Inside a form: the rows go into its data as JSON under `name`, and a reset brings back the rows loaded last.
68
+ useEffect(() => {
69
+ const form = proxyRef.current?.form;
70
+ if (!form)
71
+ return;
72
+ const onFormData = (event) => {
73
+ for (const key of [...event.formData.keys()])
74
+ if (key.startsWith(ROW_INPUT_NAME))
75
+ event.formData.delete(key);
76
+ const fieldName = latest.current.props.name;
77
+ if (fieldName && !latest.current.disabled)
78
+ event.formData.set(fieldName, JSON.stringify(store.getValue()));
79
+ };
80
+ // The reset event comes before the browser restores the fields.
81
+ const onReset = () => setTimeout(() => store.reset());
82
+ form.addEventListener("formdata", onFormData);
83
+ form.addEventListener("reset", onReset);
84
+ return () => {
85
+ form.removeEventListener("formdata", onFormData);
86
+ form.removeEventListener("reset", onReset);
87
+ };
88
+ }, [store, props.form, name]);
89
+ function focusProblem(place) {
90
+ const layout = layoutRef.current;
91
+ if (!place || !layout)
92
+ return;
93
+ if (place.key !== null)
94
+ layout.focusRow(place.key, place.name);
95
+ else
96
+ (addButtonOf(proxyRef.current) ?? proxyRef.current?.closest("fieldset"))?.focus();
97
+ }
98
+ /** A form's submit found the rows invalid: every message shows, and the first problem takes focus. */
99
+ function onInvalid(event) {
100
+ event.preventDefault();
101
+ const place = store.validateAll();
102
+ if (isFirstInvalid(event.currentTarget))
103
+ focusProblem(place);
104
+ }
105
+ const canAdd = !readOnly && !disabled && props.addable !== false && (max === undefined || snapshot.keys.length < max);
106
+ const actions = {
107
+ add(values, index) {
108
+ const key = store.add(values, index);
109
+ layoutRef.current?.focusRow(key, { first: true });
110
+ return key;
111
+ },
112
+ remove(key) {
113
+ store.remove(key);
114
+ // The Undo button takes focus, so the keyboard can bring the row straight back.
115
+ requestAnimationFrame(() => undoButtonOf(proxyRef.current)?.focus());
116
+ },
117
+ duplicate(key) {
118
+ const copy = store.duplicate(key);
119
+ if (copy)
120
+ layoutRef.current?.focusRow(copy, { first: true });
121
+ },
122
+ move(key, delta) {
123
+ store.move(key, store.indexOf(key) + delta);
124
+ layoutRef.current?.focusRow(key, { actions: true });
125
+ },
126
+ undo() {
127
+ const key = store.undoRemove();
128
+ if (key)
129
+ layoutRef.current?.focusRow(key, { first: true });
130
+ },
131
+ };
132
+ useImperativeHandle(ref, () => ({
133
+ getValue: () => store.getValue(),
134
+ setValue: rows => store.replace(rows, { emit: true }),
135
+ add: (values, index) => {
136
+ actions.add(values, index);
137
+ },
138
+ remove: index => {
139
+ const key = store.keyAt(index);
140
+ if (key)
141
+ store.remove(key);
142
+ },
143
+ validate: () => {
144
+ const place = store.validateAll();
145
+ focusProblem(place);
146
+ return place === null;
147
+ },
148
+ focusRow: (index, inputName) => {
149
+ const key = store.keyAt(index);
150
+ if (key)
151
+ layoutRef.current?.focusRow(key, inputName ?? { first: true });
152
+ },
153
+ focus: () => {
154
+ const first = store.keyAt(0);
155
+ if (first)
156
+ layoutRef.current?.focusRow(first, { first: true });
157
+ else
158
+ addButtonOf(proxyRef.current)?.focus();
159
+ },
160
+ }),
161
+ // eslint-disable-next-line react-hooks/exhaustive-deps
162
+ [store]);
163
+ // Stable, so a sheet of thousands of rows does not render again because the parent handed a new function.
164
+ const rowLock = useCallback((key, index) => {
165
+ const ask = latest.current.props.rowReadOnly;
166
+ if (!ask || !key)
167
+ return undefined;
168
+ const row = store.row(key);
169
+ return row ? ask(row, index) : undefined;
170
+ }, [store]);
171
+ const cellLock = useCallback((key, name, index) => {
172
+ // A row nobody may change locks every one of its cells, with the row's reason.
173
+ const whole = rowLock(key, index);
174
+ if (whole)
175
+ return whole;
176
+ const ask = latest.current.props.cellReadOnly;
177
+ if (!ask)
178
+ return undefined;
179
+ const row = store.row(key);
180
+ return row ? ask(row, name, index) : undefined;
181
+ }, [store, rowLock]);
182
+ // A cell's own params, merged once per answer: the same answer gives the same object, so the cell does not draw again.
183
+ const merged = useRef(new WeakMap());
184
+ const cellParams = useCallback((key, params, index) => {
185
+ const ask = latest.current.props.cellParams;
186
+ if (!ask)
187
+ return params;
188
+ const row = store.row(key);
189
+ const own = row ? ask(row, params.name, index) : null;
190
+ if (!own)
191
+ return params;
192
+ let byColumn = merged.current.get(own);
193
+ if (!byColumn)
194
+ merged.current.set(own, (byColumn = new WeakMap()));
195
+ let result = byColumn.get(params);
196
+ if (!result) {
197
+ // Descriptors rather than a spread, so a getter on either side is still read as the cell draws, as it would be
198
+ // on the column's own params; the column keeps its type and its name whatever the answer holds.
199
+ const { type: _type, name: _name, ...rest } = Object.getOwnPropertyDescriptors(own);
200
+ result = Object.defineProperties({}, { ...Object.getOwnPropertyDescriptors(params), ...rest });
201
+ byColumn.set(params, result);
202
+ }
203
+ return result;
204
+ }, [store]);
205
+ return { store, snapshot, proxyRef, onInvalid, canAdd, actions, cellLock, rowLock, cellParams };
206
+ }
207
+ function addButtonOf(proxy) {
208
+ return proxy?.closest(".nx-rows")?.querySelector(":scope > .nx-rows__footer .nx-rows__add:not(:disabled)") ?? null;
209
+ }
210
+ function undoButtonOf(proxy) {
211
+ return proxy?.closest(".nx-rows")?.querySelector(":scope > .nx-rows__footer .nx-rows__undo") ?? null;
212
+ }
213
+ /** The first control of an element that takes focus. */
214
+ export function firstFocusable(element) {
215
+ return (element?.querySelector('input:not([type="hidden"]):not(:disabled):not(.nx-visually-hidden), textarea:not(:disabled), select:not(:disabled), button:not(:disabled), [tabindex]:not([tabindex="-1"])') ?? null);
216
+ }
217
+ /** Subscribes a row's component to its row: it renders again only when the row's messages or code-set values change. */
218
+ export function useRowVersion(store, key) {
219
+ const subscribe = useCallback((listener) => store.subscribeRow(key, listener), [store, key]);
220
+ const read = () => store.version(key);
221
+ return useSyncExternalStore(subscribe, read, read);
222
+ }
223
+ /** A row's number and name for labels: "row 2", "address 2". */
224
+ export function rowName(store, index) {
225
+ return `${store.names.item} ${index + 1}`;
226
+ }
227
+ /** The text of an input's label, for names built from it. */
228
+ export function labelText(params) {
229
+ return typeof params.label === "string" ? params.label : (inputFieldName(params) ?? params.name);
230
+ }
231
+ /**
232
+ * One input of one row. It starts from the store's value when it mounts and writes every change back, so typing renders
233
+ * nothing; a value set from code mounts it again (its key holds the value's revision). In a table the column header is
234
+ * the label, so the input is named "City, row 2" for screen readers and its messages still say "City".
235
+ */
236
+ export const RowCell = memo(function RowCell({ store, rowKey, params, index, error, layout, idPrefix, size, design, readOnly, disabled, lock }) {
237
+ const [initial] = useState(() => store.get(rowKey, params.name));
238
+ const onValueChange = useCallback((next) => store.set(rowKey, params.name, next, "input"), [store, rowKey, params.name]);
239
+ const needsName = params.type === "radio-group" || params.type === "checkbox-group";
240
+ const overrides = {
241
+ id: `${idPrefix}-${rowKey.replace(/[^A-Za-z0-9_-]/g, "_")}-${params.name}`,
242
+ // Nothing goes into a parent form by itself: the rows go in whole, under the rows input's name.
243
+ name: needsName ? `${ROW_INPUT_NAME}${idPrefix}:${rowKey}:${params.name}` : undefined,
244
+ defaultValue: initial,
245
+ onValueChange,
246
+ size: params.size ?? size,
247
+ readOnly,
248
+ disabled,
249
+ };
250
+ // A generated cell: read-only however its params have it, and saying why, since a box that ignores typing without a
251
+ // word reads as broken. Code still writes it — that is what makes it generated rather than merely locked.
252
+ if (lock) {
253
+ overrides.readOnly = true;
254
+ if (typeof lock === "string")
255
+ overrides.readOnlyMessage = lock;
256
+ }
257
+ if (error !== undefined)
258
+ overrides.error = error;
259
+ // A required radio means one row has it on, which the rows check; each radio alone is not required.
260
+ if (params.type === "radio")
261
+ overrides.required = false;
262
+ if (layout === "tabular") {
263
+ overrides.label = undefined;
264
+ overrides.fieldName = inputFieldName(params);
265
+ overrides.design = "standard";
266
+ overrides["aria-label"] = `${labelText(params)}, ${rowName(store, index)}`;
267
+ }
268
+ else if (design && !params.design) {
269
+ overrides.design = design;
270
+ }
271
+ return renderInput(params, overrides);
272
+ });
273
+ /** The legend of the rows, with the required mark and, when read-only, a lock. */
274
+ export function RowsLegend({ id, label, required, lock }) {
275
+ if (!hasContent(label))
276
+ return null;
277
+ return (_jsxs("legend", { id: id, className: "nx-field__label nx-rows__legend", children: [label, required && (_jsx("span", { className: "nx-field__required", "aria-hidden": "true", children: "*" })), lock] }));
278
+ }
279
+ /** The line under the rows: Add, the rows' message or hint (or a removed row with Undo), and the count against `max`. */
280
+ export function RowsFooter({ messageId, store, snapshot, error, hint, status, addLabel, addable, canAdd, max, maxMessage, size, onAdd, onUndo }) {
281
+ const count = snapshot.keys.length;
282
+ const message = error || snapshot.message;
283
+ const invalidRows = !message && snapshot.invalidRows > 0 ? snapshot.invalidRows : 0;
284
+ const shown = status?.text ? status : message ? { text: message, error: true } : invalidRows ? { text: `${invalidRows} ${invalidRows === 1 ? `${store.names.item} needs` : `${store.names.items} need`} attention. ${store.firstMessage() ?? ""}`, error: true } : null;
285
+ const buttonSize = size === "lg" ? "md" : "sm";
286
+ return (_jsxs("div", { className: "nx-rows__footer", children: [addable && (_jsx(Button, { size: buttonSize, className: "nx-rows__add", icon: _jsx(InlineIcon, { icon: iconPlus }), disabled: !canAdd, title: !canAdd && max !== undefined && count >= max ? maxMessage : undefined, onClick: onAdd, children: addLabel })), snapshot.removed ? (_jsxs("p", { className: "nx-rows__message nx-rows__removed", role: "status", children: [_jsxs("span", { children: [store.names.title, " ", snapshot.removed.index + 1, " removed."] }), _jsx(Button, { size: "sm", variant: "ghost", className: "nx-rows__undo", icon: _jsx(InlineIcon, { icon: iconArrowBackUp }), onClick: onUndo, children: "Undo" })] })) : shown || hasContent(hint) ? (_jsxs("p", { id: messageId, className: cx("nx-rows__message", "nx-field__message", shown?.error && "nx-field__message--error"), children: [shown?.error && _jsx(InlineIcon, { icon: iconAlertCircle }), _jsx("span", { children: shown ? shown.text : hint })] })) : null, max !== undefined && (_jsxs("span", { className: "nx-rows__count", "aria-label": `${count} of at most ${max}`, children: [count, "/", max] }))] }));
287
+ }
288
+ /** Ids, names, and read-only feedback for a rows input's frame. */
289
+ export function useRowsFrame(props, store) {
290
+ const generatedId = useId();
291
+ const id = props.id ?? generatedId;
292
+ const { readOnly = false, disabled = false } = props;
293
+ const legendRef = useRef(null);
294
+ const readOnlyNotice = useReadOnlyNotice(readOnly && !disabled, legendRef, props.readOnlyMessage ?? "Read-only: these rows cannot be changed.", {
295
+ lock: "label",
296
+ typing: false,
297
+ });
298
+ const names = store.names;
299
+ return {
300
+ id,
301
+ legendId: `${id}-legend`,
302
+ messageId: `${id}-message`,
303
+ lock: readOnlyNotice.lock,
304
+ addLabel: props.addLabel ?? `Add ${names.item}`,
305
+ emptyMessage: props.emptyMessage ?? `No ${names.items} yet.`,
306
+ maxMessage: props.max === undefined ? "" : `At most ${props.max} ${props.max === 1 ? names.item : names.items}.`,
307
+ };
308
+ }