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,94 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { iconArrowLeft, iconChevronDown, iconPlus, iconUserPlus, iconUsers, iconX, InlineIcon } from "nexus-icons";
4
+ import { useEffect, useMemo, useState } from "react";
5
+ import { chatConversationName, chatScopeKey, pickChatConversation } from "../../Helpers/ChatHelpers.js";
6
+ import { chatDomainLabel } from "../../Helpers/ChatBackend.js";
7
+ import { useChat, useChatPlace } from "../../Helpers/ChatHooks.js";
8
+ import { cx } from "../../Helpers/ClassHelpers.js";
9
+ import { useBrowserTimeZone, useDatePreferences } from "../../Helpers/DatePreferences.js";
10
+ import { Button } from "../Documents/Button.js";
11
+ import { Dropdown } from "../Inputs/Dropdown.js";
12
+ import { ChatConversationList, ChatNewForm } from "./ChatConversations.js";
13
+ import { ChatParticipantList } from "./ChatParts.js";
14
+ import { ChatComposer } from "./ChatComposer.js";
15
+ import { ChatThread } from "./ChatThread.js";
16
+ /** What a record is called when the page does not say: the domain's name, when the app named it, and the record. */
17
+ function defaultTitle(scope) {
18
+ const label = chatDomainLabel(scope.domain);
19
+ return label ? `${label} · #${String(scope.parentId)}` : `Discussion · #${String(scope.parentId)}`;
20
+ }
21
+ /** A record's conversations, drawn in full. */
22
+ export function ChatPanel({ scope, source, sourceFor, place: placeSource, conversations: givenConversations, conversationId: givenConversationId, onConversationChange, title, description, summary, participants, searchParticipants, people, files = false, replies = true, mentions = true, mentionHref, header = true, height, enabled = true, pageSize, pollInterval, onSummary, footer, className, }) {
23
+ // A page that handed over one conversation's source is not asking about any others.
24
+ const single = source !== undefined;
25
+ const place = useChatPlace({ scope, place: placeSource, conversations: givenConversations, enabled: enabled && !single });
26
+ const [view, setView] = useState("thread");
27
+ const [chosenId, setChosenId] = useState(givenConversationId ?? null);
28
+ const [replyTo, setReplyTo] = useState(null);
29
+ const [asideOpen, setAsideOpen] = useState(false);
30
+ const [adding, setAdding] = useState([]);
31
+ useEffect(() => {
32
+ if (givenConversationId !== undefined)
33
+ setChosenId(givenConversationId);
34
+ }, [givenConversationId]);
35
+ // Which conversation is open: the page's, else the one picked out of the list — unread first, then most recent.
36
+ const current = useMemo(() => {
37
+ if (single)
38
+ return undefined;
39
+ const held = place.conversations;
40
+ const chosen = chosenId === null ? undefined : held.find(one => String(one.id) === String(chosenId));
41
+ return chosen ?? pickChatConversation(held);
42
+ }, [single, place.conversations, chosenId]);
43
+ // One conversation's messages: the page's own source, else one made for whichever conversation is open.
44
+ const openSource = current && sourceFor ? sourceFor(current) : undefined;
45
+ const { store, state } = useChat({
46
+ scope,
47
+ source: source ?? openSource,
48
+ ...(current && { conversationId: current.id }),
49
+ pageSize,
50
+ pollInterval,
51
+ summary,
52
+ participants: current?.participants ?? participants,
53
+ enabled: enabled && view === "thread" && (single || current !== undefined),
54
+ });
55
+ const preferences = useDatePreferences();
56
+ const zone = useBrowserTimeZone();
57
+ const time = useMemo(() => ({ ...preferences, ...(zone && { timeZone: zone }) }), [preferences, zone]);
58
+ const search = searchParticipants ?? store.searchParticipants;
59
+ const canAdd = state.features.addParticipants && (current?.type ?? "group") === "group";
60
+ const showPeople = people ?? (state.participants.length > 0 || state.features.participants || searchParticipants !== undefined);
61
+ const many = !single && place.conversations.length > 1;
62
+ // Unsent text belongs to one conversation, so the key holds the record, the reader, and which conversation it is.
63
+ const draftKey = `${chatScopeKey(scope)}:${String(scope.userId)}${current ? `:${String(current.id)}` : ""}`;
64
+ const name = current ? chatConversationName(current, scope.userId) : null;
65
+ // The record's counts reach the page after the render that changed them, never during it.
66
+ useEffect(() => {
67
+ onSummary?.(state.summary);
68
+ }, [onSummary, state.summary]);
69
+ // Once a conversation is open it stays open. Without this the panel would pick again whenever the list changed, and
70
+ // reading this one — which clears its unread count — would hand the panel to whichever conversation is unread next.
71
+ useEffect(() => {
72
+ if (current && chosenId === null)
73
+ setChosenId(current.id);
74
+ }, [current, chosenId]);
75
+ // A conversation's own counts go back into the list, so the switch shows what is waiting in the others. Only once
76
+ // its first page has arrived: a store that has loaded nothing yet holds zeros, which would wipe what the list knows.
77
+ useEffect(() => {
78
+ if (current && state.loaded)
79
+ place.apply(current.id, state.summary);
80
+ }, [current, place, state.loaded, state.summary]);
81
+ function open(conversation) {
82
+ setChosenId(conversation.id);
83
+ setView("thread");
84
+ setAsideOpen(false);
85
+ onConversationChange?.(conversation);
86
+ }
87
+ const style = height === undefined ? undefined : { "--nx-chat-height": typeof height === "number" ? `${height}px` : height };
88
+ const empty = !single && !place.loading && place.conversations.length === 0;
89
+ return (_jsxs("div", { className: cx("nx-chat-panel", asideOpen && "nx-chat-panel--with-aside", className), style: style, children: [header && (_jsxs("header", { className: "nx-chat-panel__bar", children: [view !== "thread" && (_jsx("button", { type: "button", className: "nx-chat-panel__back", onClick: () => setView("thread"), "aria-label": "Back to the conversation", title: "Back to the conversation", children: _jsx(InlineIcon, { icon: iconArrowLeft }) })), _jsxs("div", { className: "nx-chat-panel__heading", children: [view === "list" ? (_jsx("span", { className: "nx-chat-panel__title", children: "Conversations" })) : view === "new" ? (_jsx("span", { className: "nx-chat-panel__title", children: "New chat" })) : many ? (_jsxs("button", { type: "button", className: "nx-chat-panel__switch", onClick: () => setView("list"), "aria-haspopup": "true", children: [_jsx("span", { className: "nx-chat-panel__title", children: name }), _jsx(InlineIcon, { icon: iconChevronDown }), _jsxs("span", { className: "nx-chat-panel__of", children: ["of ", place.conversations.length] })] })) : (_jsx("span", { className: "nx-chat-panel__title", children: name ?? title ?? defaultTitle(scope) })), view === "thread" && description && _jsx("span", { className: "nx-chat-panel__description", children: description })] }), view === "thread" && !single && place.canCreate && !many && (_jsx("button", { type: "button", className: "nx-chat-panel__people", onClick: () => setView("new"), "aria-label": "New chat", title: "New chat", children: _jsx(InlineIcon, { icon: iconPlus }) })), view === "thread" && showPeople && (_jsxs("button", { type: "button", className: cx("nx-chat-panel__people", asideOpen && "is-open"), onClick: () => setAsideOpen(shown => !shown), "aria-pressed": asideOpen, "aria-label": asideOpen ? "Hide the people" : "Show the people", title: asideOpen ? "Hide the people" : "Show the people", children: [_jsx(InlineIcon, { icon: iconUsers }), state.participants.length > 0 && _jsx("span", { children: state.participants.length })] }))] })), _jsxs("div", { className: "nx-chat-panel__body", children: [view === "list" && (_jsx(ChatConversationList, { conversations: place.conversations, currentId: current?.id ?? null, userId: scope.userId, time: time, loading: place.loading, error: place.error, onRetry: () => void place.reload(), onPick: open, onNew: place.canCreate ? () => setView("new") : undefined })), view === "new" && _jsx(ChatNewForm, { place: place, searchParticipants: searchParticipants, onStarted: open, onCancel: () => setView("thread") }), view === "thread" && empty && (_jsx(ChatConversationList, { conversations: [], userId: scope.userId, time: time, loading: place.loading, error: place.error, onRetry: () => void place.reload(), onPick: open, onNew: place.canCreate ? () => setView("new") : undefined, empty: "Nobody has written about this record yet." })), view === "thread" && !empty && (_jsxs(_Fragment, { children: [_jsx(ChatThread, { store: store, state: state, userId: scope.userId, replies: replies, onReply: setReplyTo, marksRead: enabled }), showPeople && asideOpen && (_jsxs("aside", { className: "nx-chat-panel__aside", "aria-label": "People in this conversation", children: [_jsxs("div", { className: "nx-chat-aside__bar", children: [_jsx("span", { children: "People" }), _jsx("button", { type: "button", onClick: () => setAsideOpen(false), "aria-label": "Hide the people", children: _jsx(InlineIcon, { icon: iconX }) })] }), _jsx(ChatParticipantList, { participants: state.participants, userId: scope.userId }), canAdd && (_jsxs("div", { className: "nx-chat-aside__add", children: [_jsx(Dropdown, { multiple: true, name: "nx-chat-add-people", label: "Add people", placeholder: "Search by name", size: "sm", value: adding, valueKey: "id", labelKey: "name", descriptionKey: "role", loadOptions: search, isOptionInactive: (person) => person.inactive === true, onValueChange: (values) => setAdding(values) }), _jsx(Button, { size: "sm", variant: "secondary", icon: _jsx(InlineIcon, { icon: iconUserPlus }), disabled: adding.length === 0, onClick: () => {
90
+ const chosen = adding;
91
+ setAdding([]);
92
+ void store.addParticipants(chosen);
93
+ }, children: "Add" })] }))] }))] }))] }), view === "thread" && !empty && (_jsx(ChatComposer, { store: store, state: state, draftKey: draftKey, files: files, replyTo: replyTo, onCancelReply: () => setReplyTo(null), autoFocus: enabled, mentions: mentions ? state.participants : undefined, mentionHref: mentionHref })), footer && _jsx("div", { className: "nx-chat-panel__footer", children: footer })] }));
94
+ }
@@ -0,0 +1,87 @@
1
+ import { type ReactNode } from "react";
2
+ import { type ChatTimeOptions } from "../../Helpers/ChatHelpers.ts";
3
+ import type { ChatAttachment, ChatId, ChatMessage, ChatParticipant } from "../../Interfaces/ChatInterfaces.ts";
4
+ /** A round photo, or the initials of the name on a tone picked from it — the same look and tones as a dropdown's. */
5
+ export declare function ChatAvatar({ name, src, size }: {
6
+ name: string;
7
+ src?: string | null;
8
+ size?: "sm" | "md";
9
+ }): import("react").JSX.Element;
10
+ /** The day a run of messages was written: "Today", "Yesterday", or the date in the user's calendar. */
11
+ export declare function ChatDayDivider({ label }: {
12
+ label: string;
13
+ }): import("react").JSX.Element;
14
+ /** Where reading stopped last time. It stays where it was put, so it does not slide away as the thread is read. */
15
+ export declare function ChatUnreadLine({ count, label }: {
16
+ count?: number;
17
+ label?: string;
18
+ }): import("react").JSX.Element;
19
+ /**
20
+ * How far a message of this user's has got, in the one place the time sits: a turning spinner while it is on its way,
21
+ * one tick once the server has it, two once it has reached somebody, and two in the accent once it has been read. The
22
+ * mark is the same size in every state, so the line under a message does not shift as it settles.
23
+ */
24
+ export declare function ChatReadMark({ message, participants, userId }: {
25
+ message: ChatMessage;
26
+ participants: readonly ChatParticipant[];
27
+ userId: ChatId;
28
+ }): import("react").JSX.Element;
29
+ /** Files on a message: the name, the size, and a link when the file has a URL. */
30
+ export declare function ChatAttachments({ attachments }: {
31
+ attachments: readonly ChatAttachment[];
32
+ }): import("react").JSX.Element;
33
+ /** The message a reply answers, above the reply itself. */
34
+ export declare function ChatQuote({ message, onOpen }: {
35
+ message: ChatMessage | undefined;
36
+ onOpen?: (id: ChatId) => void;
37
+ }): import("react").JSX.Element;
38
+ /** A note the system wrote, not a person: no side, no avatar, no actions. */
39
+ export declare function ChatSystemNote({ message, time }: {
40
+ message: ChatMessage;
41
+ time: ChatTimeOptions;
42
+ }): import("react").JSX.Element;
43
+ export interface ChatMessageProps {
44
+ message: ChatMessage;
45
+ /** This user wrote it: it sits on the right. */
46
+ mine: boolean;
47
+ /** The first of a run: it carries the author's name. */
48
+ first: boolean;
49
+ /** The last of a run: it carries the time and the read mark. */
50
+ last: boolean;
51
+ time: ChatTimeOptions;
52
+ participants: readonly ChatParticipant[];
53
+ userId: ChatId;
54
+ /** The message this one answers, when it is held. */
55
+ quoted?: ChatMessage;
56
+ onReply?: (message: ChatMessage) => void;
57
+ onRemove?: (message: ChatMessage) => void;
58
+ onRetry?: (clientId: string) => void;
59
+ onDiscard?: (clientId: string) => void;
60
+ onOpenQuoted?: (id: ChatId) => void;
61
+ }
62
+ /**
63
+ * One message. Its own messages sit on the right in the primary tint, everyone else's on the left on a surface. The
64
+ * actions are one row of icon buttons that appear on hover or keyboard focus — and never move the message, so a long
65
+ * thread does not jump under the pointer.
66
+ */
67
+ export declare function ChatMessageItem({ message, mine, first, last, time, participants, userId, quoted, onReply, onRemove, onRetry, onDiscard, onOpenQuoted }: ChatMessageProps): import("react").JSX.Element;
68
+ /** Everyone in a conversation, with what each one is here. */
69
+ export declare function ChatParticipantList({ participants, userId, empty }: {
70
+ participants: readonly ChatParticipant[];
71
+ userId: ChatId;
72
+ empty?: string;
73
+ }): import("react").JSX.Element;
74
+ /** A thread with nothing in it yet. */
75
+ export declare function ChatEmpty({ message, hint }: {
76
+ message?: string;
77
+ hint?: ReactNode;
78
+ }): import("react").JSX.Element;
79
+ /** Something that did not load, with a way to try again. */
80
+ export declare function ChatFailure({ message, onRetry }: {
81
+ message: string;
82
+ onRetry?: () => void;
83
+ }): import("react").JSX.Element;
84
+ /** Messages on their way in. */
85
+ export declare function ChatLoading({ label }: {
86
+ label?: string;
87
+ }): import("react").JSX.Element;
@@ -0,0 +1,100 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { iconAlertCircle, iconCheck, iconChecks, iconCornerUpLeft, iconFile, iconLoader2, iconRefresh, iconTrash, iconX, InlineIcon } from "nexus-icons";
4
+ import { useState } from "react";
5
+ import { cx } from "../../Helpers/ClassHelpers.js";
6
+ import { chatDeliveryState, chatFileSize, chatTimeLabel, sameChatId, CHAT_DELIVERY_LABELS } from "../../Helpers/ChatHelpers.js";
7
+ import { avatarTone, initials } from "../../Helpers/DropdownHelpers.js";
8
+ import { markdownToPlainText } from "../../Helpers/MarkdownHelpers.js";
9
+ import { MarkdownView } from "../Viewers/MarkdownView.js";
10
+ /*
11
+ * The pieces a chat is drawn from: the avatar, one message, the lines that break a thread into days and into read and
12
+ * unread, and the participants. Each one draws what it is given and nothing else — the state is in `ChatStore` — so
13
+ * the same pieces serve the panel, a page-wide chat, and a demo with messages in memory.
14
+ */
15
+ /** A round photo, or the initials of the name on a tone picked from it — the same look and tones as a dropdown's. */
16
+ export function ChatAvatar({ name, src, size = "md" }) {
17
+ const url = typeof src === "string" ? src.trim() : "";
18
+ const [failed, setFailed] = useState(null);
19
+ const photo = url !== "" && failed !== url;
20
+ return (_jsx("span", { className: cx("nx-chat-avatar", size === "sm" && "nx-chat-avatar--sm"), "data-tone": photo ? undefined : avatarTone(name), "aria-hidden": "true", children: photo ? _jsx("img", { src: url, alt: "", loading: "lazy", decoding: "async", onError: () => setFailed(url) }) : initials(name) }));
21
+ }
22
+ /** The day a run of messages was written: "Today", "Yesterday", or the date in the user's calendar. */
23
+ export function ChatDayDivider({ label }) {
24
+ return (_jsx("div", { className: "nx-chat-day", role: "separator", children: _jsx("span", { children: label }) }));
25
+ }
26
+ /** Where reading stopped last time. It stays where it was put, so it does not slide away as the thread is read. */
27
+ export function ChatUnreadLine({ count, label = "New messages" }) {
28
+ return (_jsx("div", { className: "nx-chat-unread-line", role: "separator", children: _jsx("span", { children: count && count > 0 ? `${label} · ${count}` : label }) }));
29
+ }
30
+ /**
31
+ * How far a message of this user's has got, in the one place the time sits: a turning spinner while it is on its way,
32
+ * one tick once the server has it, two once it has reached somebody, and two in the accent once it has been read. The
33
+ * mark is the same size in every state, so the line under a message does not shift as it settles.
34
+ */
35
+ export function ChatReadMark({ message, participants, userId }) {
36
+ const state = chatDeliveryState(message, userId, participants);
37
+ const read = (message.readBy ?? []).filter(id => !sameChatId(id, userId));
38
+ const label = state === "read" && read.length > 1 ? `Read by ${read.length}` : CHAT_DELIVERY_LABELS[state];
39
+ const icon = state === "sending" ? iconLoader2 : state === "failed" ? iconAlertCircle : state === "sent" ? iconCheck : iconChecks;
40
+ // The word is on the wrapper, so it is both the tooltip on hover and the name a screen reader reads for the mark.
41
+ return (_jsx("span", { className: cx("nx-chat-mark", `nx-chat-mark--${state}`, (state === "read" || state === "read-all") && "nx-chat-mark--seen", state === "sending" && "nx-spin"), role: "img", "aria-label": label, title: label, "data-state": state, children: _jsx(InlineIcon, { icon: icon }) }));
42
+ }
43
+ /** Files on a message: the name, the size, and a link when the file has a URL. */
44
+ export function ChatAttachments({ attachments }) {
45
+ return (_jsx("ul", { className: "nx-chat-files", children: attachments.map((file, index) => {
46
+ const size = chatFileSize(file.size);
47
+ const body = (_jsxs(_Fragment, { children: [_jsx(InlineIcon, { icon: iconFile }), _jsx("span", { className: "nx-chat-file__name", children: file.name }), size && _jsx("span", { className: "nx-chat-file__size", children: size })] }));
48
+ return (_jsx("li", { className: "nx-chat-file", children: file.url ? (_jsx("a", { href: file.url, target: "_blank", rel: "noreferrer", children: body })) : (_jsx("span", { children: body })) }, `${String(file.id ?? file.name)}-${index}`));
49
+ }) }));
50
+ }
51
+ /** The message a reply answers, above the reply itself. */
52
+ export function ChatQuote({ message, onOpen }) {
53
+ if (!message)
54
+ return _jsx("div", { className: "nx-chat-quote nx-chat-quote--gone", children: "The message this answers is no longer here." });
55
+ const body = (_jsxs(_Fragment, { children: [_jsx("span", { className: "nx-chat-quote__author", children: message.authorName || "Unknown" }), _jsx("span", { className: "nx-chat-quote__body", children: markdownToPlainText(message.body) })] }));
56
+ if (!onOpen)
57
+ return _jsx("div", { className: "nx-chat-quote", children: body });
58
+ return (_jsx("button", { type: "button", className: "nx-chat-quote", onClick: () => onOpen(message.id), children: body }));
59
+ }
60
+ /** A note the system wrote, not a person: no side, no avatar, no actions. */
61
+ export function ChatSystemNote({ message, time }) {
62
+ return (_jsxs("div", { className: "nx-chat-note", children: [_jsx("span", { children: markdownToPlainText(message.body) }), _jsx("time", { dateTime: message.createdAt, children: chatTimeLabel(message.createdAt, time) })] }));
63
+ }
64
+ /**
65
+ * One message. Its own messages sit on the right in the primary tint, everyone else's on the left on a surface. The
66
+ * actions are one row of icon buttons that appear on hover or keyboard focus — and never move the message, so a long
67
+ * thread does not jump under the pointer.
68
+ */
69
+ export function ChatMessageItem({ message, mine, first, last, time, participants, userId, quoted, onReply, onRemove, onRetry, onDiscard, onOpenQuoted }) {
70
+ const failed = message.status === "failed";
71
+ const waiting = message.status === "sending";
72
+ const actions = [];
73
+ if (onReply && !failed && !waiting)
74
+ actions.push(_jsx(ChatAction, { icon: iconCornerUpLeft, label: "Reply", onClick: () => onReply(message) }, "reply"));
75
+ if (onRemove && mine && !failed && !waiting)
76
+ actions.push(_jsx(ChatAction, { icon: iconTrash, label: "Delete", tone: "danger", onClick: () => onRemove(message) }, "remove"));
77
+ return (_jsxs("div", { className: cx("nx-chat-msg", mine ? "nx-chat-msg--mine" : "nx-chat-msg--theirs", first && "nx-chat-msg--first", last && "nx-chat-msg--last", failed && "nx-chat-msg--failed"), children: [_jsxs("div", { className: "nx-chat-msg__bubble", children: [first && !mine && _jsx("span", { className: "nx-chat-msg__author", children: message.authorName || "Unknown" }), message.replyToId !== null && message.replyToId !== undefined && _jsx(ChatQuote, { message: quoted, onOpen: onOpenQuoted }), message.body && _jsx(MarkdownView, { className: "nx-chat-msg__body", value: message.body }), message.attachments && message.attachments.length > 0 && _jsx(ChatAttachments, { attachments: message.attachments }), _jsxs("span", { className: "nx-chat-msg__meta", children: [message.editedAt && _jsx("span", { className: "nx-chat-msg__edited", children: "edited" }), _jsx("time", { dateTime: message.createdAt, children: chatTimeLabel(message.createdAt, time) }), mine && _jsx(ChatReadMark, { message: message, participants: participants, userId: userId })] }), actions.length > 0 && _jsx("span", { className: "nx-chat-msg__actions", children: actions })] }), failed && (_jsxs("div", { className: "nx-chat-msg__failure", children: [_jsx(InlineIcon, { icon: iconAlertCircle }), _jsx("span", { children: message.error || "Not sent." }), message.clientId && onRetry && (_jsxs("button", { type: "button", onClick: () => onRetry(message.clientId), children: [_jsx(InlineIcon, { icon: iconRefresh }), " Retry"] })), message.clientId && onDiscard && (_jsxs("button", { type: "button", onClick: () => onDiscard(message.clientId), children: [_jsx(InlineIcon, { icon: iconX }), " Discard"] }))] }))] }));
78
+ }
79
+ /** One icon button in a message's action row. */
80
+ function ChatAction({ icon, label, tone, onClick }) {
81
+ return (_jsx("button", { type: "button", className: cx("nx-chat-msg__action", tone === "danger" && "nx-chat-msg__action--danger"), onClick: onClick, "aria-label": label, title: label, children: _jsx(InlineIcon, { icon: icon }) }));
82
+ }
83
+ /** Everyone in a conversation, with what each one is here. */
84
+ export function ChatParticipantList({ participants, userId, empty = "Nobody else yet." }) {
85
+ if (participants.length === 0)
86
+ return _jsx("p", { className: "nx-chat-aside__empty", children: empty });
87
+ return (_jsx("ul", { className: "nx-chat-people", children: participants.map(person => (_jsxs("li", { className: cx("nx-chat-person", person.inactive && "nx-chat-person--inactive"), children: [_jsx(ChatAvatar, { name: person.name, src: person.avatar, size: "sm" }), _jsxs("span", { className: "nx-chat-person__text", children: [_jsxs("span", { className: "nx-chat-person__name", children: [person.name, sameChatId(person.id, userId) && _jsx("span", { className: "nx-chat-person__you", children: "you" })] }), (person.role || person.readOnly) && _jsx("span", { className: "nx-chat-person__role", children: person.readOnly ? [person.role, "reads only"].filter(Boolean).join(" · ") : person.role })] })] }, String(person.id)))) }));
88
+ }
89
+ /** A thread with nothing in it yet. */
90
+ export function ChatEmpty({ message = "No messages yet.", hint }) {
91
+ return (_jsxs("div", { className: "nx-chat-empty", children: [_jsx("p", { children: message }), hint && _jsx("p", { className: "nx-chat-empty__hint", children: hint })] }));
92
+ }
93
+ /** Something that did not load, with a way to try again. */
94
+ export function ChatFailure({ message, onRetry }) {
95
+ return (_jsxs("div", { className: "nx-chat-failure", role: "alert", children: [_jsx(InlineIcon, { icon: iconAlertCircle }), _jsx("span", { children: message }), onRetry && (_jsxs("button", { type: "button", onClick: onRetry, children: [_jsx(InlineIcon, { icon: iconRefresh }), " Try again"] }))] }));
96
+ }
97
+ /** Messages on their way in. */
98
+ export function ChatLoading({ label = "Loading…" }) {
99
+ return (_jsxs("div", { className: "nx-chat-loading", "aria-live": "polite", children: [_jsx(InlineIcon, { icon: iconLoader2, className: "nx-spin" }), _jsx("span", { children: label })] }));
100
+ }
@@ -0,0 +1,22 @@
1
+ import { type ChatTimeOptions } from "../../Helpers/ChatHelpers.ts";
2
+ import type { ChatSnapshot, ChatStore } from "../../Helpers/ChatStore.ts";
3
+ import type { ChatId, ChatMessage } from "../../Interfaces/ChatInterfaces.ts";
4
+ export interface ChatThreadProps {
5
+ store: ChatStore;
6
+ state: ChatSnapshot;
7
+ /** Who is reading: their messages sit on the right. */
8
+ userId: ChatId;
9
+ /** The user's date settings and zone. Default: their own, from local storage. */
10
+ time?: ChatTimeOptions;
11
+ /** A message can be answered. Default true. */
12
+ replies?: boolean;
13
+ /** A reply was started: the panel puts the message in the composer. */
14
+ onReply?: (message: ChatMessage) => void;
15
+ /** Reading the newest message marks the conversation read. Default true. */
16
+ marksRead?: boolean;
17
+ /** Shown when there is not a single message. */
18
+ emptyMessage?: string;
19
+ className?: string;
20
+ }
21
+ /** The messages of a conversation, in days and runs, with the lines that break them up. */
22
+ export declare function ChatThread({ store, state, userId, time, replies, onReply, marksRead, emptyMessage, className }: ChatThreadProps): import("react").JSX.Element;
@@ -0,0 +1,96 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { iconArrowDown, iconMessageCircle, InlineIcon } from "nexus-icons";
4
+ import { Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
5
+ import { cx } from "../../Helpers/ClassHelpers.js";
6
+ import { chatBadgeText, chatMessageKey, groupChatMessages, sameChatId } from "../../Helpers/ChatHelpers.js";
7
+ import { useBrowserTimeZone, useDatePreferences } from "../../Helpers/DatePreferences.js";
8
+ import { Button } from "../Documents/Button.js";
9
+ import { ChatAvatar, ChatDayDivider, ChatEmpty, ChatFailure, ChatLoading, ChatMessageItem, ChatSystemNote, ChatUnreadLine } from "./ChatParts.js";
10
+ /*
11
+ * The thread: the conversation itself, scrolling. It opens at the newest message, keeps itself at the bottom while the
12
+ * reader is already there, and when they are not, it leaves the scroll alone and offers a button down to the newest
13
+ * with the number of messages that arrived. Reaching the top loads the page above and holds the reader's place.
14
+ *
15
+ * Reading marks the conversation read, but only once the newest message is actually on screen: a panel opened and left
16
+ * scrolled up has not been read.
17
+ */
18
+ /** How close to an edge counts as being at it. */
19
+ const EDGE = 32;
20
+ /** How near the top loading the page above starts. */
21
+ const TOP_EDGE = 120;
22
+ /** The messages of a conversation, in days and runs, with the lines that break them up. */
23
+ export function ChatThread({ store, state, userId, time, replies = true, onReply, marksRead = true, emptyMessage, className }) {
24
+ const preferences = useDatePreferences();
25
+ const zone = useBrowserTimeZone();
26
+ const settings = useMemo(() => ({ ...preferences, ...(zone && { timeZone: zone }), ...time }), [preferences, zone, time]);
27
+ const listRef = useRef(null);
28
+ const [atBottom, setAtBottom] = useState(true);
29
+ const [missed, setMissed] = useState(0);
30
+ // What the scroll looked like before a page of older messages arrived, so the reader's place can be given back.
31
+ const anchor = useRef(null);
32
+ const lastKey = useRef(null);
33
+ const count = useRef(0);
34
+ const days = useMemo(() => groupChatMessages(state.messages, { userId, unreadFrom: state.unreadFrom, ...settings }), [state.messages, state.unreadFrom, userId, settings]);
35
+ const byId = useMemo(() => new Map(state.messages.map(message => [String(message.id), message])), [state.messages]);
36
+ const scrollToEnd = useCallback((smooth) => {
37
+ const list = listRef.current;
38
+ if (!list)
39
+ return;
40
+ // Windows with its animations off reports reduced motion, and a smooth scroll there may never run: jump instead.
41
+ const reduced = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
42
+ list.scrollTo({ top: list.scrollHeight, behavior: smooth && !reduced ? "smooth" : "auto" });
43
+ }, []);
44
+ // New messages: stay at the bottom when already there, otherwise hold the place and count what came in.
45
+ useLayoutEffect(() => {
46
+ const list = listRef.current;
47
+ if (!list)
48
+ return;
49
+ if (anchor.current) {
50
+ // A page of older messages: the scroll keeps the message it was on, rather than jumping to the new top.
51
+ list.scrollTop = anchor.current.top + (list.scrollHeight - anchor.current.height);
52
+ anchor.current = null;
53
+ count.current = state.messages.length;
54
+ return;
55
+ }
56
+ const newest = state.messages[state.messages.length - 1];
57
+ const key = newest ? chatMessageKey(newest) : null;
58
+ const grew = state.messages.length > count.current;
59
+ const changed = key !== lastKey.current;
60
+ count.current = state.messages.length;
61
+ lastKey.current = key;
62
+ if (!grew || !changed)
63
+ return;
64
+ if (atBottom || sameChatId(newest?.authorId, userId))
65
+ scrollToEnd(lastKey.current !== null && missed === 0);
66
+ else
67
+ setMissed(value => value + 1);
68
+ // `missed` is read, not depended on: a second message must not re-run the first one's scroll.
69
+ // eslint-disable-next-line react-hooks/exhaustive-deps
70
+ }, [state.messages, atBottom, userId, scrollToEnd]);
71
+ // The conversation counts as read once its newest message has been on screen.
72
+ useEffect(() => {
73
+ if (!marksRead || !atBottom || state.loading || state.summary.unreadCount === 0)
74
+ return;
75
+ void store.markRead();
76
+ }, [marksRead, atBottom, state.loading, state.summary.unreadCount, store]);
77
+ const onScroll = useCallback(() => {
78
+ const list = listRef.current;
79
+ if (!list)
80
+ return;
81
+ const bottom = list.scrollHeight - list.scrollTop - list.clientHeight <= EDGE;
82
+ setAtBottom(bottom);
83
+ if (bottom)
84
+ setMissed(0);
85
+ if (list.scrollTop <= TOP_EDGE && state.hasMore && !state.loadingOlder) {
86
+ anchor.current = { height: list.scrollHeight, top: list.scrollTop };
87
+ void store.loadOlder();
88
+ }
89
+ }, [state.hasMore, state.loadingOlder, store]);
90
+ const quiet = !state.loading && state.messages.length === 0;
91
+ return (_jsxs("div", { className: cx("nx-chat-thread", className), children: [_jsxs("div", { className: "nx-chat-thread__list", ref: listRef, onScroll: onScroll, role: "log", "aria-live": "polite", "aria-relevant": "additions", tabIndex: 0, children: [state.loadingOlder && _jsx(ChatLoading, { label: "Loading earlier messages\u2026" }), !state.loadingOlder && state.hasMore && (_jsx("div", { className: "nx-chat-thread__older", children: _jsx(Button, { size: "sm", variant: "ghost", onClick: () => void store.loadOlder(), children: "Earlier messages" }) })), state.loading && state.messages.length === 0 && _jsx(ChatLoading, { label: "Loading the conversation\u2026" }), state.error && _jsx(ChatFailure, { message: state.error, onRetry: () => void store.refresh() }), quiet && !state.error && _jsx(ChatEmpty, { message: emptyMessage ?? "No messages yet.", hint: _jsxs(_Fragment, { children: [_jsx(InlineIcon, { icon: iconMessageCircle }), " Write the first one below."] }) }), days.map(day => (_jsxs("section", { className: "nx-chat-thread__day", children: [_jsx(ChatDayDivider, { label: day.label }), day.runs.map((run, runIndex) => (_jsxs(Fragment, { children: [run.startsUnread && _jsx(ChatUnreadLine, { count: state.summary.unreadCount }), _jsx("div", { className: cx("nx-chat-run", run.mine && "nx-chat-run--mine", run.system && "nx-chat-run--system"), children: run.system ? (run.messages.map(message => _jsx(ChatSystemNote, { message: message, time: settings }, chatMessageKey(message)))) : (_jsxs(_Fragment, { children: [!run.mine && _jsx(ChatAvatarColumn, { name: run.authorName, src: run.authorAvatar }), _jsx("div", { className: "nx-chat-run__messages", children: run.messages.map((message, index) => (_jsx(ChatMessageItem, { message: message, mine: run.mine, first: index === 0, last: index === run.messages.length - 1, time: settings, participants: state.participants, userId: userId, quoted: message.replyToId ? byId.get(String(message.replyToId)) : undefined, onReply: replies && onReply ? onReply : undefined, onRemove: run.mine && state.features.remove ? message => void store.remove(message.id) : undefined, onRetry: store.retry, onDiscard: store.discard }, chatMessageKey(message)))) })] })) })] }, `${day.date}-${runIndex}`)))] }, day.date)))] }), !atBottom && (_jsxs("button", { type: "button", className: "nx-chat-thread__jump", onClick: () => scrollToEnd(true), "aria-label": missed > 0 ? `${missed} new, go to the newest` : "Go to the newest message", children: [_jsx(InlineIcon, { icon: iconArrowDown }), missed > 0 && _jsx("span", { className: "nx-chat-thread__jump-count", children: chatBadgeText(missed) })] }))] }));
92
+ }
93
+ /** The avatar beside a run of somebody else's messages, kept out of the bubbles so they line up under each other. */
94
+ function ChatAvatarColumn({ name, src }) {
95
+ return (_jsx("div", { className: "nx-chat-run__avatar", children: _jsx(ChatAvatar, { name: name, src: src }) }));
96
+ }
@@ -0,0 +1,29 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from "react";
2
+ import type { Size, Tone } from "../../Interfaces/TypeInterfaces.ts";
3
+ export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
4
+ export type ButtonHoverTone = Exclude<Tone, "neutral">;
5
+ export interface ButtonProps extends ComponentPropsWithRef<"button"> {
6
+ /** Default `secondary`. Use one `primary` per area for the main action. */
7
+ variant?: ButtonVariant;
8
+ /** Default `md`. */
9
+ size?: Size;
10
+ /**
11
+ * Keeps a `secondary` or `ghost` button neutral until hover or keyboard focus, then shows this color
12
+ * on the border, a light background, and the text. For actions that should not shout in a list or table,
13
+ * e.g. `hoverTone="danger"` on Delete.
14
+ */
15
+ hoverTone?: ButtonHoverTone;
16
+ /** Icon before the label, e.g. `<InlineIcon icon={iconPlus} />` or `<Icon name="wifi" />`. */
17
+ icon?: ReactNode;
18
+ /** Icon after the label. */
19
+ iconEnd?: ReactNode;
20
+ /** Shows a spinner in place of `icon` and blocks clicks while an action runs. */
21
+ loading?: boolean;
22
+ /** Fills the width of its container. */
23
+ block?: boolean;
24
+ }
25
+ /**
26
+ * A button. Without children it becomes a square icon button, which then needs an `aria-label`.
27
+ * Has no client state, so it renders in server and client components alike.
28
+ */
29
+ export declare function Button({ variant, size, hoverTone, icon, iconEnd, loading, block, type, disabled, className, children, ...rest }: ButtonProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { InlineIcon, iconLoader2 } from "nexus-icons";
3
+ import { cx } from "../../Helpers/ClassHelpers.js";
4
+ /**
5
+ * A button. Without children it becomes a square icon button, which then needs an `aria-label`.
6
+ * Has no client state, so it renders in server and client components alike.
7
+ */
8
+ export function Button({ variant = "secondary", size = "md", hoverTone, icon, iconEnd, loading = false, block = false, type = "button", disabled, className, children, ...rest }) {
9
+ const hasLabel = children !== undefined && children !== null && children !== false;
10
+ return (_jsxs("button", { type: type, className: cx("nx-btn", `nx-btn--${variant}`, `nx-btn--${size}`, hoverTone && `nx-btn--hover-${hoverTone}`, block && "nx-btn--block", !hasLabel && "nx-btn--icon", className), disabled: disabled || loading, "aria-busy": loading || undefined, ...rest, children: [loading ? _jsx(InlineIcon, { icon: iconLoader2, className: "nx-spin" }) : icon, hasLabel && _jsx("span", { className: "nx-btn__label", children: children }), iconEnd] }));
11
+ }
@@ -0,0 +1,102 @@
1
+ import { type KeyboardEvent, type MouseEvent, type PointerEvent, type ReactElement, type ReactNode, type Ref, type ToggleEvent } from "react";
2
+ import { type PopoverAlign } from "../../Helpers/PopoverHelpers.ts";
3
+ import type { Size } from "../../Interfaces/TypeInterfaces.ts";
4
+ export interface MenuItem<T extends string = string> {
5
+ value: T;
6
+ label: ReactNode;
7
+ /** A second line in muted text: what the action does, or when to use it. */
8
+ description?: ReactNode;
9
+ /** Icon before the label, e.g. `<InlineIcon icon={iconPencil} />`. */
10
+ icon?: ReactNode;
11
+ /** Shown at the end in muted text, e.g. "Ctrl+C". Display only: the page binds the key itself. */
12
+ shortcut?: string;
13
+ /** `danger` draws the item in the danger color, for Delete and the like. */
14
+ tone?: "danger";
15
+ disabled?: boolean;
16
+ /** Renders the item as a link. */
17
+ href?: string;
18
+ /** The link's target, e.g. `_blank`. */
19
+ target?: string;
20
+ /** In a `radio` or `checkbox` menu: the item shows a check mark. */
21
+ checked?: boolean;
22
+ /** Tooltip. */
23
+ title?: string;
24
+ }
25
+ /** A line between groups of items. */
26
+ export interface MenuSeparator {
27
+ separator: true;
28
+ }
29
+ /** A small heading above a group of items. */
30
+ export interface MenuHeading {
31
+ heading: ReactNode;
32
+ }
33
+ export type MenuEntry<T extends string = string> = MenuItem<T> | MenuSeparator | MenuHeading;
34
+ export declare function isMenuItem<T extends string>(entry: MenuEntry<T>): entry is MenuItem<T>;
35
+ /** `menu`: actions. `radio`: one item is checked at a time. `checkbox`: any number of items can be checked. */
36
+ export type MenuKind = "menu" | "radio" | "checkbox";
37
+ export interface MenuPanelProps<T extends string = string> {
38
+ id: string;
39
+ items: readonly MenuEntry<T>[];
40
+ /** Called with the item's value when it is chosen by click, Enter, or Space. */
41
+ onSelect?: (value: T, item: MenuItem<T>) => void;
42
+ /** Default `menu`. */
43
+ kind?: MenuKind;
44
+ /** Default `md`. */
45
+ size?: Size;
46
+ /** Closes the menu once an item is chosen. Default true; `false` suits checkbox menus. */
47
+ closeOnSelect?: boolean;
48
+ className?: string;
49
+ ref: Ref<HTMLDivElement>;
50
+ onToggle?: (event: ToggleEvent<HTMLDivElement>) => void;
51
+ "aria-label"?: string;
52
+ "aria-labelledby"?: string;
53
+ }
54
+ /**
55
+ * The list of a menu inside a native popover. Arrow keys move between items, Home and End jump, a letter jumps to
56
+ * the next item starting with it, Enter or Space chooses, Esc closes, and Tab closes and moves on.
57
+ */
58
+ export declare function MenuPanel<T extends string>({ id, items, onSelect, kind, size, closeOnSelect, className, ref, onToggle, ...labelling }: MenuPanelProps<T>): import("react").JSX.Element;
59
+ /** What `Menu` adds to its trigger. A component used as the trigger must pass these on to its button. */
60
+ export interface MenuTriggerProps {
61
+ ref?: Ref<HTMLButtonElement>;
62
+ id?: string;
63
+ "aria-haspopup"?: "menu";
64
+ "aria-expanded"?: boolean;
65
+ "aria-controls"?: string;
66
+ onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
67
+ onPointerDown?: (event: PointerEvent<HTMLButtonElement>) => void;
68
+ onKeyDown?: (event: KeyboardEvent<HTMLButtonElement>) => void;
69
+ }
70
+ export interface MenuProps<T extends string = string> extends Pick<MenuPanelProps<T>, "items" | "onSelect" | "kind" | "size" | "closeOnSelect" | "className"> {
71
+ /** The button that opens the menu, such as a `Button`. It receives an id, aria attributes, and click and key handlers. */
72
+ children: ReactElement<MenuTriggerProps>;
73
+ /** Which edge of the button the menu lines up with. Default `start`. */
74
+ align?: PopoverAlign;
75
+ /** Name of the menu for screen readers. Default: the button's text. */
76
+ "aria-label"?: string;
77
+ onOpenChange?: (open: boolean) => void;
78
+ }
79
+ /**
80
+ * A dropdown menu: a button that opens a list of actions in a native popover under it (above it when there is no
81
+ * room). Click or Enter opens it, ArrowDown opens it on the first item, Esc closes it and returns focus to the button.
82
+ *
83
+ * @example
84
+ * <Menu items={items} onSelect={value => run(value)}>
85
+ * <Button iconEnd={<InlineIcon icon={iconChevronDown} />}>Actions</Button>
86
+ * </Menu>
87
+ */
88
+ export declare function Menu<T extends string>({ children, align, "aria-label": ariaLabel, onOpenChange, ...panelProps }: MenuProps<T>): import("react").JSX.Element;
89
+ export interface ContextMenuProps<T extends string = string> extends Pick<MenuPanelProps<T>, "items" | "onSelect" | "kind" | "size" | "closeOnSelect" | "className"> {
90
+ /** The content the menu belongs to: a row, a card, a list. */
91
+ children: ReactNode;
92
+ /** Name of the menu for screen readers. Default "Context menu". */
93
+ "aria-label"?: string;
94
+ /** Leaves the browser's own menu in place. */
95
+ disabled?: boolean;
96
+ onOpenChange?: (open: boolean) => void;
97
+ }
98
+ /**
99
+ * A menu that opens where the user right-clicks (or presses Shift+F10) over its content, kept inside the window.
100
+ * The wrapper takes no space of its own (`display: contents`), so it can wrap a table row or a grid cell.
101
+ */
102
+ export declare function ContextMenu<T extends string>({ children, "aria-label": ariaLabel, disabled, onOpenChange, ...panelProps }: ContextMenuProps<T>): import("react").JSX.Element;