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,323 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Fragment } from "react";
4
+ import { iconAlertCircle, iconHistory, iconX, InlineIcon } from "nexus-icons";
5
+ import { memo, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, } from "react";
6
+ import { cx } from "../../Helpers/ClassHelpers.js";
7
+ import { formatDate, toIsoDate, usesDevanagari } from "../../Helpers/DateHelpers.js";
8
+ import { useDatePreferences } from "../../Helpers/DatePreferences.js";
9
+ import { FormStore } from "../../Helpers/FormStore.js";
10
+ import { inputFieldName } from "../../Helpers/InputParamsHelpers.js";
11
+ import { formatTime } from "../../Helpers/TimeHelpers.js";
12
+ import { Button } from "../Documents/Button.js";
13
+ import { hasContent } from "../Inputs/Field.js";
14
+ import { isFirstInvalid } from "../Inputs/InputField.js";
15
+ import { renderInput } from "../Inputs/InputRenderer.js";
16
+ import { firstFocusable } from "../Inputs/RowsInput.js";
17
+ /*
18
+ * Form: a list of input params on a 12-column grid, with the values in a FormStore. Typing renders only the input
19
+ * typed in; an input renders again when a message it shows changes or code sets its value. The form checks nothing on
20
+ * its own: SubmitForm checks on submit, and a plain <form> around a Form blocks its submit while any input, or the form's
21
+ * own rule, has a problem.
22
+ */
23
+ /** Inputs without a box: they line up with the boxes beside them. */
24
+ const CHOICE_TYPES = new Set(["checkbox", "switch", "radio"]);
25
+ /** Inputs taller than one line, for the placeholders shown while the values load. */
26
+ const TALL_TYPES = new Set(["textarea", "markdown", "rich-text", "tree", "tabular", "group", "checkbox-group", "radio-group"]);
27
+ /** What takes focus in an input's cell: a control to type in or pick, before any button of its toolbar. */
28
+ const TYPING_TARGETS = 'input:not([type="hidden"]):not(.nx-visually-hidden):not(:disabled), textarea:not(.nx-visually-hidden):not(:disabled), select:not(:disabled), [contenteditable="true"]';
29
+ const INVALID_CONTROLS = ":is(input, textarea, select):invalid";
30
+ /* Hooks */
31
+ /**
32
+ * A form store for the page to hold: pass it to a form as `form`, then read, watch, set, and submit through it.
33
+ *
34
+ * @example
35
+ * const form = useForm<Employee>();
36
+ * const country = useFormValue<string>(form, "country");
37
+ * <SubmitForm form={form} inputs={inputs} onSubmit={save} />
38
+ */
39
+ export function useForm() {
40
+ const [store] = useState(() => new FormStore());
41
+ return store;
42
+ }
43
+ /** One value of a form; the component renders again only when it changes. */
44
+ export function useFormValue(form, name) {
45
+ const store = form;
46
+ const read = () => store.get(name);
47
+ return useSyncExternalStore(store.subscribeValue, read, read);
48
+ }
49
+ /** Every value of a form; the component renders again on every change, so keep it to small forms or summaries. */
50
+ export function useFormValues(form) {
51
+ const read = () => form.getValue();
52
+ return useSyncExternalStore(form.subscribeValue, read, read);
53
+ }
54
+ /** What the form's frame shows: changed, submitting, problems, the form's message, drafts. */
55
+ export function useFormStatus(form) {
56
+ const store = form;
57
+ return useSyncExternalStore(store.subscribeStatus, store.getStatus, store.getStatus);
58
+ }
59
+ /** The form's own store, or the one given. */
60
+ export function useFormStore(given) {
61
+ const [own] = useState(() => (given ? null : new FormStore()));
62
+ return given ?? own;
63
+ }
64
+ /* Text */
65
+ /** An input's name in the form's messages: its `fieldName`, its label when it is text, or its name. */
66
+ export function formInputLabel(params, name = "") {
67
+ if (!params)
68
+ return name;
69
+ return inputFieldName(params) ?? (typeof params.label === "string" ? params.label : params.name);
70
+ }
71
+ /** The names a list shows: all of them up to one more than `most` ("and 1 more" says less than the name), else the first `most`. */
72
+ export function namesToShow(names, most = 3) {
73
+ return names.length <= most + 1 ? { shown: names, more: 0 } : { shown: names.slice(0, most), more: names.length - most };
74
+ }
75
+ /** Names in a sentence: "A", "A and B", "A, B, and C", "A, B, C, and 3 more". */
76
+ export function listInSentence(names, most = 3) {
77
+ const { shown, more } = namesToShow(names, most);
78
+ if (more)
79
+ return `${shown.join(", ")}, and ${more} more`;
80
+ if (shown.length <= 1)
81
+ return shown[0] ?? "";
82
+ if (shown.length === 2)
83
+ return `${shown[0]} and ${shown[1]}`;
84
+ return `${shown.slice(0, -1).join(", ")}, and ${shown[shown.length - 1]}`;
85
+ }
86
+ /** A moment in the user's time format, with the date in their calendar when it was not today. */
87
+ export function formatFormTime(ms, preferences) {
88
+ const date = new Date(ms);
89
+ const script = usesDevanagari(preferences.calendar, preferences.script) ? "devanagari" : "latin";
90
+ const time = formatTime(date, { format: preferences.timeFormat, script });
91
+ if (toIsoDate(date) === toIsoDate(new Date()))
92
+ return time;
93
+ return `${formatDate(date, preferences)}, ${time}`;
94
+ }
95
+ const spanOf = (params) => Math.max(1, Math.min(12, Math.round(params.span ?? 12)));
96
+ /** A checkbox, switch, or radio that shares a row with labeled boxes: it lines up with their boxes, below their labels. */
97
+ const besideBoxes = (params, span, design) => CHOICE_TYPES.has(params.type) && span < 12 && (design ?? "standard") === "standard";
98
+ /** The input. It starts from the store's value and reports each change; a new stamp mounts it again with a new value. */
99
+ function FieldInput({ store, params, stamp, error, context }) {
100
+ const [initial] = useState(() => store.get(params.name));
101
+ const onValueChange = useCallback((value) => store.input(params.name, value, stamp), [store, params.name, stamp]);
102
+ const own = params;
103
+ const overrides = {
104
+ id: `${context.idPrefix}-${params.name}`,
105
+ defaultValue: initial,
106
+ onValueChange,
107
+ size: own.size ?? context.size,
108
+ readOnly: context.readOnly,
109
+ disabled: context.disabled,
110
+ };
111
+ if (context.design && !own.design)
112
+ overrides.design = context.design;
113
+ if (context.scopeNames && params.type === "radio-group")
114
+ overrides.name = `${context.idPrefix}:${params.name}`;
115
+ // Only a message from the form replaces the input's own.
116
+ if (error !== undefined)
117
+ overrides.error = error;
118
+ return renderInput(params, overrides);
119
+ }
120
+ /** One input's place on the grid. It renders again only when its input must: a message from the form, or a value from code. */
121
+ const FormCell = memo(function FormCell({ store, params, context }) {
122
+ const { name } = params;
123
+ const subscribe = useCallback((listener) => store.subscribeField(name, listener), [store, name]);
124
+ const version = () => store.fieldVersion(name);
125
+ useSyncExternalStore(subscribe, version, version);
126
+ const stamp = store.stamp(name);
127
+ const span = spanOf(params);
128
+ // An input that follows others is asked whether it shows each time it draws: it comes and goes with their values.
129
+ if (params.dependsOn?.length && params.hidden)
130
+ return null;
131
+ // Leaving the input shows the form's own rule message on it, such as "Passwords do not match".
132
+ function onBlur(event) {
133
+ if (event.relatedTarget instanceof Node && event.currentTarget.contains(event.relatedTarget))
134
+ return;
135
+ store.touch(name);
136
+ }
137
+ return (_jsx("div", { className: cx("nx-form__cell", CHOICE_TYPES.has(params.type) && "nx-form__cell--choice"), style: { "--nx-span": span }, "data-span": span, "data-beside": besideBoxes(params, span, context.design) || undefined, "data-nx-field": name, onBlur: onBlur, children: _jsx(FieldInput, { store: store, params: params, stamp: stamp, error: store.shownError(name), context: context }, stamp) }));
138
+ });
139
+ /** A placeholder in an input's shape while the values load. */
140
+ function SkeletonCell({ params, design }) {
141
+ const span = spanOf(params);
142
+ const choice = CHOICE_TYPES.has(params.type);
143
+ const ownDesign = params.design ?? design ?? "standard";
144
+ return (_jsxs("div", { className: cx("nx-form__cell", "nx-form__skeleton", choice && "nx-form__cell--choice"), style: { "--nx-span": span }, "data-span": span, "data-beside": besideBoxes(params, span, design) || undefined, children: [!choice && ownDesign === "standard" && hasContent(params.label) && _jsx("span", { className: "nx-form__skeleton-label" }), _jsx("span", { className: cx("nx-form__skeleton-box", TALL_TYPES.has(params.type) && "nx-form__skeleton-box--tall", choice && "nx-form__skeleton-box--choice") })] }));
145
+ }
146
+ /** The note over a form whose unsaved changes came back from a draft. */
147
+ function DraftNotice({ store, restored, inputs, preferences }) {
148
+ const label = (name) => formInputLabel(inputs.find(params => params.name === name), name);
149
+ const conflicts = restored.conflicts.map(label);
150
+ return (_jsxs("div", { className: "nx-form__notice", role: "status", children: [_jsx(InlineIcon, { icon: iconHistory }), _jsxs("p", { className: "nx-form__notice-text", children: ["Restored your unsaved changes from ", formatFormTime(restored.savedAt, preferences), ": ", listInSentence(restored.names.map(label)), ".", conflicts.length > 0 && (_jsxs("strong", { children: [" ", listInSentence(conflicts), " also changed on the server since; check ", conflicts.length === 1 ? "it" : "them", " before you save."] }))] }), _jsx(Button, { size: "sm", variant: "ghost", hoverTone: "danger", onClick: () => store.discardDraft(), children: "Discard" }), _jsx(Button, { size: "sm", variant: "ghost", icon: _jsx(InlineIcon, { icon: iconX }), "aria-label": "Keep the changes and close this note", title: "Close", onClick: () => store.dismissDraft() })] }));
151
+ }
152
+ /* Form */
153
+ /**
154
+ * Renders a list of input params on a 12-column grid (`span` in each input's params), with the values in a store outside
155
+ * React, so a form of any size types as fast as one input. The same params describe every input, so a CRUD page or a
156
+ * server can build a form from plain objects; a tabular or group input is one entry that holds rows.
157
+ *
158
+ * On its own it suits filters and settings that save as they change (`onValueChange`). `SubmitForm` adds checks on
159
+ * submit and the buttons; `ApiForm` sends the values to a server. Inside a plain `<form>`, each input sends its value
160
+ * under its name, and the form's own rule blocks the submit while it fails.
161
+ *
162
+ * @example
163
+ * <Form
164
+ * inputs={[
165
+ * { type: "text", name: "fullName", label: "Full name", required: true, span: 6 },
166
+ * { type: "email", name: "email", label: "Email", span: 6 },
167
+ * { type: "dropdown", name: "role", label: "Role", options: roles, span: 4 },
168
+ * ]}
169
+ * defaultValue={employee}
170
+ * onValueChange={values => saveFilters(values)}
171
+ * />
172
+ */
173
+ export function Form(props) {
174
+ const { inputs, design, size = "md", readOnly = false, disabled = false, loading = false, draftKey, id, className, style, ref } = props;
175
+ const store = useFormStore(props.form);
176
+ const anyStore = store;
177
+ const preferences = useDatePreferences();
178
+ const idPrefix = useId();
179
+ const rootRef = useRef(null);
180
+ const proxyRef = useRef(null);
181
+ const latest = useRef(props);
182
+ latest.current = props;
183
+ const locked = readOnly || disabled;
184
+ const options = { inputs, validate: props.validate, onFieldChange: props.onFieldChange, context: { preferences }, locked, draftKey };
185
+ // The first render loads the values quietly, so the inputs mount with them; the page hears of it once mounted. A store
186
+ // that already has values (from `useForm`, a form shown again) keeps them.
187
+ if (!store.started) {
188
+ store.configure(options, false);
189
+ store.load(props.defaultValue ?? {}, { draft: !loading, quiet: true });
190
+ }
191
+ useLayoutEffect(() => store.announce(), [store]);
192
+ // Inputs or settings that change what is valid check every value again; callbacks only swap.
193
+ const rulesKey = [inputs, preferences, locked, draftKey];
194
+ const rulesRef = useRef(null);
195
+ useLayoutEffect(() => {
196
+ const previous = rulesRef.current;
197
+ rulesRef.current = rulesKey;
198
+ store.configure(options, !previous || rulesKey.some((item, index) => !Object.is(item, previous[index])));
199
+ });
200
+ useLayoutEffect(() => store.setLoading(loading), [store, loading]);
201
+ useImperativeHandle(ref, () => store, [store]);
202
+ // Radio groups need names of their own when no <form> keeps them apart.
203
+ const [inForm, setInForm] = useState(true);
204
+ useLayoutEffect(() => setInForm(Boolean(rootRef.current?.closest("form"))), []);
205
+ const status = useFormStatus(store);
206
+ /** The inputs on the page that report a problem, and those the values fail, in form order. Fires no events. */
207
+ function findProblems() {
208
+ const root = rootRef.current;
209
+ const invalid = new Map();
210
+ if (root) {
211
+ for (const cell of root.querySelectorAll(":scope > .nx-form__grid > [data-nx-field]")) {
212
+ const controls = cell.querySelectorAll(INVALID_CONTROLS);
213
+ if (controls.length)
214
+ invalid.set(cell.dataset.nxField ?? "", [...controls]);
215
+ }
216
+ }
217
+ const fromValues = new Set(store.problems());
218
+ const names = latest.current.inputs.filter(params => !params.hidden && (invalid.has(params.name) || fromValues.has(params.name))).map(params => params.name);
219
+ return { names, invalid, fromValues };
220
+ }
221
+ function focusInput(name) {
222
+ const root = rootRef.current;
223
+ if (!root)
224
+ return;
225
+ const cell = root.querySelector(`:scope > .nx-form__grid > [data-nx-field${name === undefined ? "" : `="${CSS.escape(name)}"`}]`);
226
+ if (!cell)
227
+ return;
228
+ const target = cell.querySelector(TYPING_TARGETS) ?? firstFocusable(cell);
229
+ target?.focus();
230
+ }
231
+ const view = {
232
+ validate() {
233
+ const { names, invalid, fromValues } = findProblems();
234
+ // A problem the values have and the input on the page does not show: the form shows its message on the input.
235
+ const forced = [...fromValues].filter(name => !invalid.has(name) && store.ruleError(name));
236
+ store.reveal(names, forced);
237
+ // Every input shows its own message, and the first in the form takes focus.
238
+ for (const controls of invalid.values())
239
+ for (const control of controls)
240
+ control.checkValidity();
241
+ const first = names[0];
242
+ // After the inputs' own focus, which a list of rows moves in the next frame.
243
+ if (first && !invalid.has(first))
244
+ requestAnimationFrame(() => focusInput(first));
245
+ return names;
246
+ },
247
+ focus: focusInput,
248
+ };
249
+ useLayoutEffect(() => {
250
+ store.view = view;
251
+ return () => {
252
+ if (store.view === view)
253
+ store.view = null;
254
+ };
255
+ });
256
+ // Page-level listeners: the page's `onValueChange`, the problem list after a submit, and the proxy's validity.
257
+ useEffect(() => {
258
+ let frame = 0;
259
+ const syncProxy = () => {
260
+ const proxy = proxyRef.current;
261
+ if (!proxy)
262
+ return;
263
+ const first = latest.current.inputs.find(params => !params.hidden && store.formRuleError(params.name));
264
+ proxy.setCustomValidity((first ? store.formRuleError(first.name) : store.formRuleMessage) ?? "");
265
+ };
266
+ const recount = () => {
267
+ frame = 0;
268
+ if (store.getStatus().showAll)
269
+ store.setAttention(findProblems().names);
270
+ };
271
+ syncProxy();
272
+ const unsubscribe = store.subscribeValue((values, name) => {
273
+ syncProxy();
274
+ if (store.getStatus().showAll && !frame)
275
+ frame = requestAnimationFrame(recount);
276
+ latest.current.onValueChange?.(values, name);
277
+ });
278
+ return () => {
279
+ unsubscribe();
280
+ cancelAnimationFrame(frame);
281
+ };
282
+ // eslint-disable-next-line react-hooks/exhaustive-deps
283
+ }, [store]);
284
+ // Inside a <form>: a reset brings back the values the form opened with.
285
+ useEffect(() => {
286
+ const form = rootRef.current?.closest("form");
287
+ if (!form)
288
+ return;
289
+ const onReset = () => store.reset();
290
+ form.addEventListener("reset", onReset);
291
+ return () => form.removeEventListener("reset", onReset);
292
+ }, [store]);
293
+ // A draft is written when the page hides or the form leaves, so a change typed just before is kept too.
294
+ useEffect(() => {
295
+ if (!draftKey)
296
+ return;
297
+ const flush = () => store.flushDraft();
298
+ const onVisibility = () => {
299
+ if (document.visibilityState === "hidden")
300
+ flush();
301
+ };
302
+ window.addEventListener("pagehide", flush);
303
+ document.addEventListener("visibilitychange", onVisibility);
304
+ return () => {
305
+ window.removeEventListener("pagehide", flush);
306
+ document.removeEventListener("visibilitychange", onVisibility);
307
+ flush();
308
+ };
309
+ }, [store, draftKey]);
310
+ /** A native submit of the <form> around found the form's own rule failing. */
311
+ function onProxyInvalid(event) {
312
+ event.preventDefault();
313
+ const { names } = findProblems();
314
+ store.reveal(names);
315
+ const first = names.find(name => store.formRuleError(name));
316
+ if (first && isFirstInvalid(event.currentTarget))
317
+ focusInput(first);
318
+ }
319
+ const context = useMemo(() => ({ idPrefix, size, design, readOnly, disabled, scopeNames: !inForm }), [idPrefix, size, design, readOnly, disabled, inForm]);
320
+ // With FormButtons on the page, the form's message is in their status line.
321
+ const ownMessage = status.buttons === 0 ? status.message : null;
322
+ return (_jsxs("div", { ref: rootRef, id: id, className: cx("nx-form", `nx-form--${size}`, className), style: style, "data-nx-form": "", "aria-busy": loading || undefined, children: [status.restored && !loading && _jsx(DraftNotice, { store: anyStore, restored: status.restored, inputs: inputs, preferences: preferences }), _jsx("div", { className: "nx-form__grid", children: inputs.filter(params => params.dependsOn?.length || !params.hidden).map((params, index, visible) => (_jsxs(Fragment, { children: [params.section && (index === 0 || visible[index - 1]?.section !== params.section) && _jsx("h3", { className: "nx-form__section", children: params.section }), loading ? _jsx(SkeletonCell, { params: params, design: design }) : _jsx(FormCell, { store: anyStore, params: params, context: context })] }, params.name))) }), loading && (_jsx("p", { className: "nx-visually-hidden", role: "status", children: "Loading\u2026" })), _jsx("input", { ref: proxyRef, className: "nx-visually-hidden", tabIndex: -1, "aria-hidden": "true", disabled: locked, onInvalid: onProxyInvalid }), ownMessage && (_jsxs("p", { className: "nx-form__message nx-field__message nx-field__message--error", role: "alert", children: [_jsx(InlineIcon, { icon: iconAlertCircle }), _jsx("span", { children: ownMessage })] }))] }));
323
+ }
@@ -0,0 +1,65 @@
1
+ import { type ReactNode } from "react";
2
+ import type { AnyFormStore, FormStore } from "../../Helpers/FormStore.ts";
3
+ import type { FormValues, SubmitFormProps } from "../../Interfaces/FormInterfaces.ts";
4
+ import type { Size } from "../../Interfaces/TypeInterfaces.ts";
5
+ export interface FormButtonsProps<TValues extends FormValues = FormValues> {
6
+ /** The form's store: the one given to the form as `form`. */
7
+ form: FormStore<TValues>;
8
+ /** Default "Save". */
9
+ submitLabel?: ReactNode;
10
+ /** Default: a floppy disk. `null` shows none. */
11
+ submitIcon?: ReactNode | null;
12
+ /** Shows a Cancel button. */
13
+ onCancel?: () => void;
14
+ /** Cancel asks before it drops changed values. Default true. */
15
+ confirmCancel?: boolean;
16
+ /** Default "Cancel". */
17
+ cancelLabel?: ReactNode;
18
+ /** Shows a Reset button. */
19
+ resettable?: boolean;
20
+ /** Default "Reset". */
21
+ resetLabel?: ReactNode;
22
+ /** `end` (default): the status line, then the buttons at the end. `start`: the buttons first. */
23
+ align?: "start" | "end";
24
+ /** Size of the buttons. Default `md`. */
25
+ size?: Size;
26
+ /** Nothing to save: only Cancel shows. */
27
+ readOnly?: boolean;
28
+ className?: string;
29
+ }
30
+ /**
31
+ * Asks "Discard your changes?" when the form has changes. Resolves `true` to go on (nothing changed, or the user chose
32
+ * Discard), `false` to keep editing. Cancel buttons and popups that close a form ask it.
33
+ */
34
+ export declare function confirmDiscard(form: AnyFormStore): Promise<boolean>;
35
+ /**
36
+ * A form's bar: its status line (loading, saving, what needs attention, unsaved changes, saved), Reset, Cancel, and Save.
37
+ * `SubmitForm` renders one; render your own with `buttons={false}` to put the bar elsewhere, such as a popup's footer.
38
+ * Its Save button submits the form from anywhere on the page.
39
+ *
40
+ * @example
41
+ * const form = useForm();
42
+ * <Popup footer={<FormButtons form={form} onCancel={close} />}>
43
+ * <ApiForm form={form} buttons={false} inputs={inputs} api={api} />
44
+ * </Popup>
45
+ */
46
+ export declare function FormButtons<TValues extends FormValues = FormValues>({ form, submitLabel, submitIcon, onCancel, confirmCancel, cancelLabel, resettable, resetLabel, align, size, readOnly, className, }: FormButtonsProps<TValues>): import("react").JSX.Element;
47
+ /**
48
+ * A `Form` that submits: every value is checked on Save (the inputs' own rules, the form's `validate`, rows not on the
49
+ * page), each message shows, and the first problem takes focus; then `onSubmit` gets the values, and while it runs Save
50
+ * waits with a spinner and nothing submits twice. A save makes the values the form's own, so "Unsaved changes" clears;
51
+ * `{ ok: false, errors }` shows messages on their inputs. Cancel asks before it drops changes, the page asks before it
52
+ * closes with unsaved ones, and Ctrl+S or Ctrl+Enter saves from any input.
53
+ *
54
+ * @example
55
+ * <SubmitForm
56
+ * inputs={EMPLOYEE_INPUTS}
57
+ * defaultValue={employee}
58
+ * onSubmit={async values => {
59
+ * const answer = await api.save(values);
60
+ * if (!answer.ok) return { ok: false, errors: answer.errors };
61
+ * }}
62
+ * onCancel={close}
63
+ * />
64
+ */
65
+ export declare function SubmitForm<TValues extends FormValues = FormValues>(props: SubmitFormProps<TValues>): import("react").JSX.Element;
@@ -0,0 +1,198 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { iconAlertCircle, iconCircleCheck, iconDeviceFloppy, iconLoader2, iconRefresh, InlineIcon } from "nexus-icons";
4
+ import { useEffect, useId, useLayoutEffect, useRef } from "react";
5
+ import { errorMessage, isAbortError } from "../../Helpers/ApiResponses.js";
6
+ import { cx } from "../../Helpers/ClassHelpers.js";
7
+ import { useDatePreferences } from "../../Helpers/DatePreferences.js";
8
+ import { Button } from "../Documents/Button.js";
9
+ import { messageBox } from "../Layouts/MessageBox.js";
10
+ import { Form, formatFormTime, formInputLabel, namesToShow, useFormStatus, useFormStore } from "./Form.js";
11
+ /** The problems after a submit, each a button that goes to its input: "2 fields need attention: Email, Phone." */
12
+ function ProblemList({ form, names }) {
13
+ const { shown, more } = namesToShow(names);
14
+ return (_jsxs(_Fragment, { children: [names.length === 1 ? "1 field needs attention: " : `${names.length} fields need attention: `, shown.map((name, index) => (_jsxs("span", { children: [index > 0 && (index === shown.length - 1 && !more ? (shown.length > 2 ? ", and " : " and ") : ", "), _jsx("button", { type: "button", className: "nx-form-status__jump", onClick: () => form.focus(name), children: formInputLabel(form.inputs.find(params => params.name === name), name) })] }, name))), more > 0 && `, and ${more} more`, "."] }));
15
+ }
16
+ /** One line of what the form is doing: loading, saving, what needs attention, unsaved changes, or when it saved. */
17
+ function FormStatusLine({ form, status }) {
18
+ const preferences = useDatePreferences();
19
+ let tone = "muted";
20
+ let content = null;
21
+ if (status.loading) {
22
+ content = (_jsxs(_Fragment, { children: [_jsx(InlineIcon, { icon: iconLoader2, className: "nx-spin" }), _jsx("span", { children: "Loading\u2026" })] }));
23
+ }
24
+ else if (status.submitting) {
25
+ content = _jsx("span", { children: "Saving\u2026" });
26
+ }
27
+ else if (status.message || status.problems.length) {
28
+ tone = "error";
29
+ content = (_jsxs(_Fragment, { children: [_jsx(InlineIcon, { icon: iconAlertCircle }), _jsxs("span", { children: [status.message, status.message && status.problems.length > 0 && " ", status.problems.length > 0 && _jsx(ProblemList, { form: form, names: status.problems })] })] }));
30
+ }
31
+ else if (status.changed) {
32
+ content = _jsx("span", { children: status.draftSavedAt ? "Unsaved changes, kept on this device." : "Unsaved changes." });
33
+ }
34
+ else if (status.savedAt) {
35
+ tone = "success";
36
+ content = (_jsxs(_Fragment, { children: [_jsx(InlineIcon, { icon: iconCircleCheck }), _jsxs("span", { children: ["Saved at ", formatFormTime(status.savedAt, preferences), "."] })] }));
37
+ }
38
+ return (_jsx("p", { className: cx("nx-form-status", `nx-form-status--${tone}`), "aria-live": "polite", children: content }));
39
+ }
40
+ /**
41
+ * Asks "Discard your changes?" when the form has changes. Resolves `true` to go on (nothing changed, or the user chose
42
+ * Discard), `false` to keep editing. Cancel buttons and popups that close a form ask it.
43
+ */
44
+ export async function confirmDiscard(form) {
45
+ if (!form.isChanged())
46
+ return true;
47
+ return messageBox.confirm({
48
+ title: "Discard your changes?",
49
+ message: "What you changed in this form will be lost.",
50
+ tone: "warning",
51
+ confirmLabel: "Discard",
52
+ cancelLabel: "Keep editing",
53
+ });
54
+ }
55
+ /**
56
+ * A form's bar: its status line (loading, saving, what needs attention, unsaved changes, saved), Reset, Cancel, and Save.
57
+ * `SubmitForm` renders one; render your own with `buttons={false}` to put the bar elsewhere, such as a popup's footer.
58
+ * Its Save button submits the form from anywhere on the page.
59
+ *
60
+ * @example
61
+ * const form = useForm();
62
+ * <Popup footer={<FormButtons form={form} onCancel={close} />}>
63
+ * <ApiForm form={form} buttons={false} inputs={inputs} api={api} />
64
+ * </Popup>
65
+ */
66
+ export function FormButtons({ form, submitLabel = "Save", submitIcon, onCancel, confirmCancel = true, cancelLabel = "Cancel", resettable = false, resetLabel = "Reset", align = "end", size = "md", readOnly = false, className, }) {
67
+ const store = form;
68
+ const status = useFormStatus(store);
69
+ useEffect(() => store.addButtons(), [store]);
70
+ async function cancel() {
71
+ if (confirmCancel && !(await confirmDiscard(store)))
72
+ return;
73
+ store.dropDraft();
74
+ onCancel?.();
75
+ }
76
+ const buttonSize = size === "lg" ? "lg" : size === "sm" ? "sm" : "md";
77
+ const actions = [
78
+ resettable && !readOnly && (_jsx(Button, { size: buttonSize, variant: "ghost", icon: _jsx(InlineIcon, { icon: iconRefresh }), disabled: !status.changed || status.submitting || status.loading, onClick: () => store.reset(), children: resetLabel }, "reset")),
79
+ onCancel && (_jsx(Button, { size: buttonSize, onClick: () => void cancel(), children: cancelLabel }, "cancel")),
80
+ !readOnly && (_jsx(Button, { type: "submit", form: status.formId ?? undefined, size: buttonSize, variant: "primary", loading: status.submitting, disabled: status.loading, icon: submitIcon === undefined ? _jsx(InlineIcon, { icon: iconDeviceFloppy }) : (submitIcon ?? undefined), children: submitLabel }, "submit")),
81
+ ].filter(Boolean);
82
+ if (align === "start")
83
+ actions.reverse();
84
+ const line = _jsx(FormStatusLine, { form: store, status: status }, "status");
85
+ const bar = actions.length ? (_jsx("div", { className: "nx-form-buttons__actions", children: actions }, "actions")) : null;
86
+ return _jsx("div", { className: cx("nx-form-buttons", `nx-form-buttons--${align}`, className), children: align === "start" ? [bar, line] : [line, bar] });
87
+ }
88
+ /**
89
+ * A `Form` that submits: every value is checked on Save (the inputs' own rules, the form's `validate`, rows not on the
90
+ * page), each message shows, and the first problem takes focus; then `onSubmit` gets the values, and while it runs Save
91
+ * waits with a spinner and nothing submits twice. A save makes the values the form's own, so "Unsaved changes" clears;
92
+ * `{ ok: false, errors }` shows messages on their inputs. Cancel asks before it drops changes, the page asks before it
93
+ * closes with unsaved ones, and Ctrl+S or Ctrl+Enter saves from any input.
94
+ *
95
+ * @example
96
+ * <SubmitForm
97
+ * inputs={EMPLOYEE_INPUTS}
98
+ * defaultValue={employee}
99
+ * onSubmit={async values => {
100
+ * const answer = await api.save(values);
101
+ * if (!answer.ok) return { ok: false, errors: answer.errors };
102
+ * }}
103
+ * onCancel={close}
104
+ * />
105
+ */
106
+ export function SubmitForm(props) {
107
+ const { onSubmit: _onSubmit, onSaved: _onSaved, onCancel, confirmCancel, submitLabel, submitIcon, cancelLabel, resettable, resetLabel, resetOnSuccess: _resetOnSuccess, buttons = "end", confirmLeave, id, className, style, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...formProps } = props;
108
+ const store = useFormStore(props.form);
109
+ const formRef = useRef(null);
110
+ const generatedId = useId();
111
+ const formId = id ?? `${generatedId}form`;
112
+ const controllerRef = useRef(null);
113
+ const latest = useRef(props);
114
+ latest.current = props;
115
+ const locked = Boolean(props.readOnly || props.disabled);
116
+ useLayoutEffect(() => {
117
+ const submitter = () => formRef.current?.requestSubmit();
118
+ store.submitter = submitter;
119
+ store.setFormId(formId);
120
+ return () => {
121
+ if (store.submitter === submitter)
122
+ store.submitter = null;
123
+ store.setFormId(null);
124
+ };
125
+ }, [store, formId]);
126
+ // A submit still running when the form leaves stops.
127
+ useEffect(() => () => controllerRef.current?.abort(), []);
128
+ // Closing or reloading the page with unsaved changes asks first, unless a draft keeps them.
129
+ const guardLeave = confirmLeave ?? !props.draftKey;
130
+ useEffect(() => {
131
+ if (!guardLeave)
132
+ return;
133
+ const onBeforeUnload = (event) => {
134
+ if (!store.isChanged() || locked)
135
+ return;
136
+ event.preventDefault();
137
+ // Older browsers show the prompt only when this is set.
138
+ event.returnValue = "";
139
+ };
140
+ window.addEventListener("beforeunload", onBeforeUnload);
141
+ return () => window.removeEventListener("beforeunload", onBeforeUnload);
142
+ }, [store, guardLeave, locked]);
143
+ async function submit() {
144
+ const status = store.getStatus();
145
+ if (status.submitting || status.loading || locked)
146
+ return;
147
+ if (!store.validate())
148
+ return;
149
+ const controller = new AbortController();
150
+ controllerRef.current = controller;
151
+ const values = store.getValue();
152
+ const context = { changes: store.getChanges(), initial: store.getInitial(), form: store, signal: controller.signal };
153
+ store.beginSubmit();
154
+ let result;
155
+ try {
156
+ result = await latest.current.onSubmit(values, context);
157
+ }
158
+ catch (error) {
159
+ result = isAbortError(error) ? false : { ok: false, message: errorMessage(error) };
160
+ }
161
+ if (controllerRef.current === controller)
162
+ controllerRef.current = null;
163
+ if (controller.signal.aborted || result === false) {
164
+ store.endSubmit();
165
+ return;
166
+ }
167
+ if (result && typeof result === "object" && result.ok === false) {
168
+ store.endSubmit();
169
+ store.setErrors(result.errors ?? {}, result.message ?? null);
170
+ const first = store.getStatus().problems[0];
171
+ if (first)
172
+ store.focus(first);
173
+ return;
174
+ }
175
+ // Saved.
176
+ if (latest.current.resetOnSuccess) {
177
+ store.reset();
178
+ store.markSaved();
179
+ requestAnimationFrame(() => store.focus());
180
+ }
181
+ else {
182
+ store.commit(values, result && typeof result === "object" && result.ok ? result.values : undefined);
183
+ }
184
+ latest.current.onSaved?.(store.getValue(), store);
185
+ }
186
+ function onKeyDown(event) {
187
+ if (event.defaultPrevented || !(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey)
188
+ return;
189
+ if (event.key.toLowerCase() === "s" || event.key === "Enter") {
190
+ event.preventDefault();
191
+ formRef.current?.requestSubmit();
192
+ }
193
+ }
194
+ return (_jsxs("form", { ref: formRef, id: formId, className: cx("nx-submit-form", className), style: style, noValidate: true, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, onSubmit: event => {
195
+ event.preventDefault();
196
+ void submit();
197
+ }, onKeyDown: onKeyDown, children: [_jsx(Form, { ...formProps, form: store }), buttons !== false && (_jsx(FormButtons, { form: store, align: buttons, submitLabel: submitLabel, submitIcon: submitIcon, onCancel: onCancel, confirmCancel: confirmCancel, cancelLabel: cancelLabel, resettable: resettable, resetLabel: resetLabel, size: formProps.size, readOnly: locked }))] }));
198
+ }
@@ -0,0 +1,69 @@
1
+ import { type CSSProperties, type ReactNode } from "react";
2
+ import type { CalendarDay, CalendarType, DateInput, DateRange, DateScript } from "../../Interfaces/DateInterfaces.ts";
3
+ import type { Size } from "../../Interfaces/TypeInterfaces.ts";
4
+ export type { DateRange } from "../../Interfaces/DateInterfaces.ts";
5
+ export interface CalendarProps {
6
+ /** Picks one day (default) or a start and an end. */
7
+ mode?: "single" | "range";
8
+ /** The chosen day, as an ISO date. With `value`, the calendar is controlled. */
9
+ value?: DateInput;
10
+ defaultValue?: DateInput;
11
+ /** After a day is chosen or cleared, with the ISO date or `null`. */
12
+ onValueChange?: (value: string | null) => void;
13
+ /** Range mode: the chosen range. With `range`, the calendar is controlled. */
14
+ range?: DateRange;
15
+ defaultRange?: DateRange;
16
+ /** Range mode: after the start is chosen (end `null`), after the end is chosen, and after a clear. */
17
+ onRangeChange?: (range: [string | null, string | null]) => void;
18
+ /** After a pick is complete: a day, or the end of a range. A popover closes on this. */
19
+ onSelect?: (value: string) => void;
20
+ /** The calendar shown: `ad` or `bs`. Default: the user's setting. Values are ISO dates in either case. */
21
+ calendar?: CalendarType;
22
+ /** Digits and names of Nepali dates. Default: the user's setting. */
23
+ script?: DateScript;
24
+ /** Earliest day that can be picked. */
25
+ min?: DateInput;
26
+ /** Latest day that can be picked. */
27
+ max?: DateInput;
28
+ /** Days that cannot be picked, such as booked nights. */
29
+ isDateDisabled?: (iso: string) => boolean;
30
+ /** The month shown, as any day in it. With `month`, moving between months goes through `onMonthChange`. */
31
+ month?: DateInput;
32
+ defaultMonth?: DateInput;
33
+ /** After the shown month changes, with its first day. */
34
+ onMonthChange?: (firstDay: string) => void;
35
+ /** Months shown side by side, from the one in `month`: 1 (default) or 2, for ranges. With more than one, outside days are not drawn. */
36
+ months?: number;
37
+ /** First column of the week: 0 Sunday (default), 1 Monday, 6 Saturday. */
38
+ weekStart?: number;
39
+ /** Weekdays drawn as holidays (0 Sunday to 6 Saturday). Default: Saturday in the Nepali calendar, none otherwise. */
40
+ weekendDays?: readonly number[];
41
+ /** Shows the days of the months before and after that fill the first and last rows. Default true. */
42
+ showOutsideDays?: boolean;
43
+ /** Draws the inside of a day, e.g. the number with event markers under it. Default: the day number. */
44
+ renderDay?: (day: CalendarDay) => ReactNode;
45
+ /** Today and Clear buttons under the grid. Default true. */
46
+ footer?: boolean;
47
+ /** The day marked and picked as today, as an ISO date. Default: today where the browser is; a date and time picker showing another time zone passes that zone's day. */
48
+ today?: DateInput;
49
+ /** Default `md`. */
50
+ size?: Size;
51
+ readOnly?: boolean;
52
+ disabled?: boolean;
53
+ /** Focuses the calendar's current day when it mounts. */
54
+ autoFocus?: boolean;
55
+ id?: string;
56
+ className?: string;
57
+ style?: CSSProperties;
58
+ "aria-label"?: string;
59
+ "aria-labelledby"?: string;
60
+ }
61
+ /**
62
+ * A month of days to pick from, in the English (AD) or Nepali (BS) calendar, with month and year menus, arrow-key
63
+ * navigation, a range mode with a live preview, two months side by side with `months`, and a `renderDay` slot for
64
+ * event markers. Values are ISO dates in the Gregorian calendar whatever calendar is shown. It is the panel of
65
+ * `DatePicker` and `DateRangePicker`, and works on its own in a page.
66
+ *
67
+ * @example <Calendar value={date} onValueChange={setDate} min={todayIsoDate()} />
68
+ */
69
+ export declare function Calendar(props: CalendarProps): import("react").JSX.Element;