nexus-shared 1.1.20 → 2.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 (332) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +903 -0
  3. package/dist/Client.Index.d.ts +44 -0
  4. package/dist/Client.Index.js +49 -0
  5. package/dist/Components/Documents/Button.d.ts +29 -0
  6. package/dist/Components/Documents/Button.js +11 -0
  7. package/dist/Components/Documents/Menu.d.ts +102 -0
  8. package/dist/Components/Documents/Menu.js +238 -0
  9. package/dist/Components/Documents/SplitButton.d.ts +64 -0
  10. package/dist/Components/Documents/SplitButton.js +127 -0
  11. package/dist/Components/Documents/TabButtons.d.ts +62 -0
  12. package/dist/Components/Documents/TabButtons.js +19 -0
  13. package/dist/Components/Forms/ApiForm.d.ts +21 -0
  14. package/dist/Components/Forms/ApiForm.js +99 -0
  15. package/dist/Components/Forms/Crud.d.ts +17 -0
  16. package/dist/Components/Forms/Crud.js +556 -0
  17. package/dist/Components/Forms/CrudParts.d.ts +15 -0
  18. package/dist/Components/Forms/CrudParts.js +36 -0
  19. package/dist/Components/Forms/Form.d.ts +52 -0
  20. package/dist/Components/Forms/Form.js +319 -0
  21. package/dist/Components/Forms/SubmitForm.d.ts +65 -0
  22. package/dist/Components/Forms/SubmitForm.js +198 -0
  23. package/dist/Components/Inputs/Calendar.d.ts +69 -0
  24. package/dist/Components/Inputs/Calendar.js +258 -0
  25. package/dist/Components/Inputs/CheckBox.d.ts +53 -0
  26. package/dist/Components/Inputs/CheckBox.js +87 -0
  27. package/dist/Components/Inputs/CheckBoxGroup.d.ts +20 -0
  28. package/dist/Components/Inputs/CheckBoxGroup.js +80 -0
  29. package/dist/Components/Inputs/ChoiceField.d.ts +122 -0
  30. package/dist/Components/Inputs/ChoiceField.js +169 -0
  31. package/dist/Components/Inputs/DatePicker.d.ts +54 -0
  32. package/dist/Components/Inputs/DatePicker.js +246 -0
  33. package/dist/Components/Inputs/DateRangePicker.d.ts +62 -0
  34. package/dist/Components/Inputs/DateRangePicker.js +283 -0
  35. package/dist/Components/Inputs/DateTimePicker.d.ts +75 -0
  36. package/dist/Components/Inputs/DateTimePicker.js +399 -0
  37. package/dist/Components/Inputs/Dropdown.d.ts +117 -0
  38. package/dist/Components/Inputs/Dropdown.js +839 -0
  39. package/dist/Components/Inputs/DropdownParts.d.ts +84 -0
  40. package/dist/Components/Inputs/DropdownParts.js +102 -0
  41. package/dist/Components/Inputs/EditorShell.d.ts +45 -0
  42. package/dist/Components/Inputs/EditorShell.js +214 -0
  43. package/dist/Components/Inputs/Field.d.ts +53 -0
  44. package/dist/Components/Inputs/Field.js +16 -0
  45. package/dist/Components/Inputs/GroupForm.d.ts +20 -0
  46. package/dist/Components/Inputs/GroupForm.js +170 -0
  47. package/dist/Components/Inputs/InputField.d.ts +59 -0
  48. package/dist/Components/Inputs/InputField.js +169 -0
  49. package/dist/Components/Inputs/InputRenderer.d.ts +35 -0
  50. package/dist/Components/Inputs/InputRenderer.js +99 -0
  51. package/dist/Components/Inputs/MarkdownEditor.d.ts +67 -0
  52. package/dist/Components/Inputs/MarkdownEditor.js +289 -0
  53. package/dist/Components/Inputs/NumberBox.d.ts +53 -0
  54. package/dist/Components/Inputs/NumberBox.js +228 -0
  55. package/dist/Components/Inputs/RadioGroup.d.ts +20 -0
  56. package/dist/Components/Inputs/RadioGroup.js +43 -0
  57. package/dist/Components/Inputs/ReadOnlyNotice.d.ts +36 -0
  58. package/dist/Components/Inputs/ReadOnlyNotice.js +116 -0
  59. package/dist/Components/Inputs/RichTextEditor.d.ts +103 -0
  60. package/dist/Components/Inputs/RichTextEditor.js +1170 -0
  61. package/dist/Components/Inputs/RichTextParts.d.ts +63 -0
  62. package/dist/Components/Inputs/RichTextParts.js +157 -0
  63. package/dist/Components/Inputs/RowsInput.d.ts +104 -0
  64. package/dist/Components/Inputs/RowsInput.js +259 -0
  65. package/dist/Components/Inputs/Slider.d.ts +87 -0
  66. package/dist/Components/Inputs/Slider.js +216 -0
  67. package/dist/Components/Inputs/TabularForm.d.ts +23 -0
  68. package/dist/Components/Inputs/TabularForm.js +283 -0
  69. package/dist/Components/Inputs/TextArea.d.ts +43 -0
  70. package/dist/Components/Inputs/TextArea.js +31 -0
  71. package/dist/Components/Inputs/TextBox.d.ts +52 -0
  72. package/dist/Components/Inputs/TextBox.js +58 -0
  73. package/dist/Components/Inputs/TextField.d.ts +31 -0
  74. package/dist/Components/Inputs/TextField.js +35 -0
  75. package/dist/Components/Inputs/TimePanel.d.ts +34 -0
  76. package/dist/Components/Inputs/TimePanel.js +288 -0
  77. package/dist/Components/Inputs/TimePicker.d.ts +54 -0
  78. package/dist/Components/Inputs/TimePicker.js +239 -0
  79. package/dist/Components/Inputs/TreeView.d.ts +160 -0
  80. package/dist/Components/Inputs/TreeView.js +947 -0
  81. package/dist/Components/Layouts/MessageBox.d.ts +71 -0
  82. package/dist/Components/Layouts/MessageBox.js +104 -0
  83. package/dist/Components/Layouts/Popup.d.ts +92 -0
  84. package/dist/Components/Layouts/Popup.js +376 -0
  85. package/dist/Components/Layouts/PopupContext.d.ts +2 -0
  86. package/dist/Components/Layouts/PopupContext.js +4 -0
  87. package/dist/Components/Layouts/PopupDock.d.ts +22 -0
  88. package/dist/Components/Layouts/PopupDock.js +121 -0
  89. package/dist/Components/Layouts/PopupHost.d.ts +56 -0
  90. package/dist/Components/Layouts/PopupHost.js +163 -0
  91. package/dist/Components/Layouts/PopupRules.d.ts +30 -0
  92. package/dist/Components/Layouts/PopupRules.js +63 -0
  93. package/dist/Components/Layouts/ThemePicker.d.ts +13 -0
  94. package/dist/Components/Layouts/ThemePicker.js +22 -0
  95. package/dist/Components/Layouts/ThemeScript.d.ts +14 -0
  96. package/dist/Components/Layouts/ThemeScript.js +13 -0
  97. package/dist/Components/Layouts/ThemeSwitcher.d.ts +13 -0
  98. package/dist/Components/Layouts/ThemeSwitcher.js +29 -0
  99. package/dist/Components/Layouts/Toaster.d.ts +76 -0
  100. package/dist/Components/Layouts/Toaster.js +172 -0
  101. package/dist/Components/Viewers/DataTable.d.ts +25 -0
  102. package/dist/Components/Viewers/DataTable.js +542 -0
  103. package/dist/Components/Viewers/DataTableParts.d.ts +134 -0
  104. package/dist/Components/Viewers/DataTableParts.js +145 -0
  105. package/dist/Components/Viewers/MarkdownView.d.ts +26 -0
  106. package/dist/Components/Viewers/MarkdownView.js +79 -0
  107. package/dist/Components/Viewers/RichTextView.d.ts +18 -0
  108. package/dist/Components/Viewers/RichTextView.js +17 -0
  109. package/dist/Helpers/AnimationHelpers.d.ts +6 -0
  110. package/dist/Helpers/AnimationHelpers.js +35 -0
  111. package/dist/Helpers/ApiClient.d.ts +74 -0
  112. package/dist/Helpers/ApiClient.js +197 -0
  113. package/dist/Helpers/ApiFormHelpers.d.ts +26 -0
  114. package/dist/Helpers/ApiFormHelpers.js +56 -0
  115. package/dist/Helpers/ApiModules.d.ts +135 -0
  116. package/dist/Helpers/ApiModules.js +194 -0
  117. package/dist/Helpers/ApiResponses.d.ts +41 -0
  118. package/dist/Helpers/ApiResponses.js +159 -0
  119. package/dist/Helpers/ApiRoutes.d.ts +150 -0
  120. package/dist/Helpers/ApiRoutes.js +191 -0
  121. package/dist/Helpers/ApiToasts.d.ts +21 -0
  122. package/dist/Helpers/ApiToasts.js +22 -0
  123. package/dist/Helpers/ClassHelpers.d.ts +3 -0
  124. package/dist/Helpers/ClassHelpers.js +4 -0
  125. package/dist/Helpers/CrudBackend.d.ts +56 -0
  126. package/dist/Helpers/CrudBackend.js +119 -0
  127. package/dist/Helpers/CrudHelpers.d.ts +76 -0
  128. package/dist/Helpers/CrudHelpers.js +202 -0
  129. package/dist/Helpers/DateHelpers.d.ts +88 -0
  130. package/dist/Helpers/DateHelpers.js +363 -0
  131. package/dist/Helpers/DatePreferences.d.ts +14 -0
  132. package/dist/Helpers/DatePreferences.js +94 -0
  133. package/dist/Helpers/DateTimeHelpers.d.ts +87 -0
  134. package/dist/Helpers/DateTimeHelpers.js +286 -0
  135. package/dist/Helpers/DragHelpers.d.ts +129 -0
  136. package/dist/Helpers/DragHelpers.js +281 -0
  137. package/dist/Helpers/DropdownHelpers.d.ts +56 -0
  138. package/dist/Helpers/DropdownHelpers.js +180 -0
  139. package/dist/Helpers/FormDrafts.d.ts +18 -0
  140. package/dist/Helpers/FormDrafts.js +76 -0
  141. package/dist/Helpers/FormStore.d.ts +231 -0
  142. package/dist/Helpers/FormStore.js +734 -0
  143. package/dist/Helpers/InputParamsHelpers.d.ts +79 -0
  144. package/dist/Helpers/InputParamsHelpers.js +388 -0
  145. package/dist/Helpers/MarkdownEditing.d.ts +27 -0
  146. package/dist/Helpers/MarkdownEditing.js +250 -0
  147. package/dist/Helpers/MarkdownHelpers.d.ts +82 -0
  148. package/dist/Helpers/MarkdownHelpers.js +775 -0
  149. package/dist/Helpers/MessageBuilder.d.ts +74 -0
  150. package/dist/Helpers/MessageBuilder.js +254 -0
  151. package/dist/Helpers/NepaliCalendar.d.ts +25 -0
  152. package/dist/Helpers/NepaliCalendar.js +107 -0
  153. package/dist/Helpers/NumberHelpers.d.ts +71 -0
  154. package/dist/Helpers/NumberHelpers.js +202 -0
  155. package/dist/Helpers/Permissions.d.ts +62 -0
  156. package/dist/Helpers/Permissions.js +133 -0
  157. package/dist/Helpers/PopoverHelpers.d.ts +43 -0
  158. package/dist/Helpers/PopoverHelpers.js +91 -0
  159. package/dist/Helpers/RichTextHelpers.d.ts +77 -0
  160. package/dist/Helpers/RichTextHelpers.js +1072 -0
  161. package/dist/Helpers/RowsStore.d.ts +160 -0
  162. package/dist/Helpers/RowsStore.js +584 -0
  163. package/dist/Helpers/TableColumns.d.ts +40 -0
  164. package/dist/Helpers/TableColumns.js +254 -0
  165. package/dist/Helpers/TableExport.d.ts +30 -0
  166. package/dist/Helpers/TableExport.js +97 -0
  167. package/dist/Helpers/TableHelpers.d.ts +102 -0
  168. package/dist/Helpers/TableHelpers.js +281 -0
  169. package/dist/Helpers/TableStore.d.ts +142 -0
  170. package/dist/Helpers/TableStore.js +530 -0
  171. package/dist/Helpers/ThemeHelpers.d.ts +15 -0
  172. package/dist/Helpers/ThemeHelpers.js +16 -0
  173. package/dist/Helpers/TimeHelpers.d.ts +73 -0
  174. package/dist/Helpers/TimeHelpers.js +253 -0
  175. package/dist/Helpers/TreeStore.d.ts +188 -0
  176. package/dist/Helpers/TreeStore.js +577 -0
  177. package/dist/Helpers/ValidationHelpers.d.ts +124 -0
  178. package/dist/Helpers/ValidationHelpers.js +301 -0
  179. package/dist/Interfaces/ApiInterfaces.d.ts +149 -0
  180. package/dist/Interfaces/ApiInterfaces.js +24 -0
  181. package/dist/Interfaces/CrudInterfaces.d.ts +171 -0
  182. package/{src/sso-client.tsx → dist/Interfaces/CrudInterfaces.js} +1 -1
  183. package/dist/Interfaces/DateInterfaces.d.ts +154 -0
  184. package/dist/Interfaces/DateInterfaces.js +29 -0
  185. package/dist/Interfaces/FormInterfaces.d.ts +352 -0
  186. package/dist/Interfaces/FormInterfaces.js +1 -0
  187. package/dist/Interfaces/InputInterfaces.d.ts +141 -0
  188. package/dist/Interfaces/InputInterfaces.js +1 -0
  189. package/dist/Interfaces/MessageInterfaces.d.ts +111 -0
  190. package/dist/Interfaces/MessageInterfaces.js +5 -0
  191. package/dist/Interfaces/TableInterfaces.d.ts +291 -0
  192. package/dist/Interfaces/TableInterfaces.js +1 -0
  193. package/dist/Interfaces/ThemeInterfaces.d.ts +17 -0
  194. package/dist/Interfaces/ThemeInterfaces.js +17 -0
  195. package/dist/Interfaces/TypeInterfaces.d.ts +4 -0
  196. package/dist/Interfaces/TypeInterfaces.js +1 -0
  197. package/dist/Server.Index.d.ts +2 -0
  198. package/dist/Server.Index.js +5 -0
  199. package/dist/Services/ApiProxy.d.ts +32 -0
  200. package/dist/Services/ApiProxy.js +111 -0
  201. package/dist/Services/BrowserApi.d.ts +78 -0
  202. package/dist/Services/BrowserApi.js +111 -0
  203. package/dist/Services/ServerApi.d.ts +49 -0
  204. package/dist/Services/ServerApi.js +75 -0
  205. package/dist/Services/ThemeService.d.ts +17 -0
  206. package/dist/Services/ThemeService.js +83 -0
  207. package/dist/Shared.Index.d.ts +43 -0
  208. package/dist/Shared.Index.js +47 -0
  209. package/package.json +82 -33
  210. package/src/Styles/Nexus.Base.css +70 -0
  211. package/src/Styles/Nexus.Button.css +151 -0
  212. package/src/Styles/Nexus.Calendar.css +275 -0
  213. package/src/Styles/Nexus.Choice.css +345 -0
  214. package/src/Styles/Nexus.Crud.css +84 -0
  215. package/src/Styles/Nexus.DateTime.css +81 -0
  216. package/src/Styles/Nexus.Dropdown.css +774 -0
  217. package/src/Styles/Nexus.Editor.css +297 -0
  218. package/src/Styles/Nexus.Form.css +298 -0
  219. package/src/Styles/Nexus.Index.css +34 -0
  220. package/src/Styles/Nexus.Input.css +637 -0
  221. package/src/Styles/Nexus.Markdown.css +265 -0
  222. package/src/Styles/Nexus.Menu.css +249 -0
  223. package/src/Styles/Nexus.Popup.css +731 -0
  224. package/src/Styles/Nexus.RichText.css +463 -0
  225. package/src/Styles/Nexus.Rows.css +566 -0
  226. package/src/Styles/Nexus.Slider.css +278 -0
  227. package/src/Styles/Nexus.Tab.Buttons.css +364 -0
  228. package/src/Styles/Nexus.Table.css +1014 -0
  229. package/src/Styles/Nexus.Theme.Picker.css +159 -0
  230. package/src/Styles/Nexus.Themes.css +194 -0
  231. package/src/Styles/Nexus.Time.css +147 -0
  232. package/src/Styles/Nexus.Toast.css +265 -0
  233. package/src/Styles/Nexus.Tokens.css +72 -0
  234. package/src/Styles/Nexus.Tree.css +435 -0
  235. package/src/api-services/authentication-service.tsx +0 -23
  236. package/src/api-services/preference-service.tsx +0 -5
  237. package/src/api-services/system-service.tsx +0 -34
  238. package/src/client.ts +0 -31
  239. package/src/components/documents/button.tsx +0 -142
  240. package/src/components/documents/icon-box.tsx +0 -93
  241. package/src/components/documents/page-title.tsx +0 -7
  242. package/src/components/documents/tab-button.tsx +0 -172
  243. package/src/components/documents/tag.tsx +0 -30
  244. package/src/components/index.js +0 -0
  245. package/src/components/inputs/checkbox-input.tsx +0 -66
  246. package/src/components/inputs/input-box.tsx +0 -48
  247. package/src/components/inputs/input-element.tsx +0 -65
  248. package/src/components/inputs/input-form.tsx +0 -164
  249. package/src/components/inputs/input.tsx +0 -181
  250. package/src/components/inputs/number-input.tsx +0 -108
  251. package/src/components/inputs/radiobox-input.tsx +0 -53
  252. package/src/components/inputs/textarea-input.tsx +0 -47
  253. package/src/components/inputs/textbox-input.tsx +0 -45
  254. package/src/components/layouts/global-dialogbox.tsx +0 -431
  255. package/src/components/layouts/global-layout.tsx +0 -59
  256. package/src/components/layouts/layout-helpers.tsx +0 -20
  257. package/src/components/layouts/panels/user-panel.tsx +0 -112
  258. package/src/components/layouts/utility-menu.tsx +0 -49
  259. package/src/components/panels/theme-panel.tsx +0 -46
  260. package/src/helpers/bitwise-helpers.tsx +0 -11
  261. package/src/helpers/browser-helpers.tsx +0 -156
  262. package/src/helpers/datasource-helpers.tsx +0 -99
  263. package/src/helpers/element-helpers.tsx +0 -57
  264. package/src/helpers/input-helpers.tsx +0 -74
  265. package/src/helpers/string-helpers.tsx +0 -28
  266. package/src/helpers/utility-helpers.tsx +0 -45
  267. package/src/helpers/validation-helpers.tsx +0 -302
  268. package/src/index.ts +0 -26
  269. package/src/interface.ts +0 -19
  270. package/src/interfaces/auth-token-interfaces.tsx +0 -6
  271. package/src/interfaces/browser-interfaces.tsx +0 -36
  272. package/src/interfaces/button-interfaces.tsx +0 -63
  273. package/src/interfaces/datasource-interfaces.tsx +0 -22
  274. package/src/interfaces/datatable-interfaces.tsx +0 -25
  275. package/src/interfaces/dialogbox-interfaces.tsx +0 -5
  276. package/src/interfaces/exception-interfaces.tsx +0 -99
  277. package/src/interfaces/http-interfaces.tsx +0 -129
  278. package/src/interfaces/icon-interfaces.tsx +0 -129
  279. package/src/interfaces/input-interfaces.tsx +0 -410
  280. package/src/interfaces/layout-interfaces.tsx +0 -191
  281. package/src/interfaces/menu-interfaces.tsx +0 -49
  282. package/src/interfaces/message-interfaces.tsx +0 -32
  283. package/src/interfaces/permission-interfaces.tsx +0 -9
  284. package/src/interfaces/storage-interfaces.tsx +0 -5
  285. package/src/interfaces/system-interfaces.tsx +0 -22
  286. package/src/interfaces/theme-interfaces.tsx +0 -123
  287. package/src/interfaces/type-interfaces.tsx +0 -28
  288. package/src/interfaces/user-interfaces.tsx +0 -47
  289. package/src/nexus-client.tsx +0 -21
  290. package/src/nexus.environments.tsx +0 -66
  291. package/src/proxy-api/proxy-backend.tsx +0 -60
  292. package/src/proxy-api/proxy-constants.tsx +0 -28
  293. package/src/proxy-api/proxy-helpers.tsx +0 -91
  294. package/src/proxy-api-client.tsx +0 -1
  295. package/src/proxy-api-server.tsx +0 -2
  296. package/src/services/http-client-services.tsx +0 -75
  297. package/src/services/http-services.tsx +0 -158
  298. package/src/services/loader-service.tsx +0 -185
  299. package/src/services/localstorage-service.tsx +0 -119
  300. package/src/services/message-services.tsx +0 -383
  301. package/src/services/theme-service.tsx +0 -161
  302. package/src/services/user-services.tsx +0 -10
  303. package/src/sso-config/auth-token-validation.ts +0 -16
  304. package/src/sso-config/callback-route.tsx +0 -35
  305. package/src/sso-config/cookie-encryption.ts +0 -51
  306. package/src/sso-config/cookie-helpers.tsx +0 -102
  307. package/src/sso-config/forgot-password-route.tsx +0 -36
  308. package/src/sso-config/oauth-callback-state.ts +0 -61
  309. package/src/sso-config/pkce-helpers.ts +0 -9
  310. package/src/sso-config/provider-complete-route.tsx +0 -18
  311. package/src/sso-config/redirect-context-actions.tsx +0 -45
  312. package/src/sso-config/redirect-context-helpers.tsx +0 -159
  313. package/src/sso-config/redirect-context-persist.tsx +0 -18
  314. package/src/sso-config/redirect-context-route.tsx +0 -45
  315. package/src/sso-config/refresh-route.tsx +0 -23
  316. package/src/sso-config/sign-in-route.tsx +0 -74
  317. package/src/sso-config/sign-out-route.tsx +0 -57
  318. package/src/sso-config/sign-up-route.tsx +0 -57
  319. package/src/sso-config/sso-backend-flow.ts +0 -38
  320. package/src/sso-config/sso-complete-route.tsx +0 -50
  321. package/src/sso-config/sso-config.ts +0 -59
  322. package/src/sso-config/sso-interfaces.tsx +0 -99
  323. package/src/sso-config/sso-response-helpers.tsx +0 -20
  324. package/src/sso-server.tsx +0 -21
  325. package/src/styles/nexus.animation.css +0 -269
  326. package/src/styles/nexus.core.css +0 -119
  327. package/src/styles/nexus.dialog.css +0 -144
  328. package/src/styles/nexus.icon.css +0 -51
  329. package/src/styles/nexus.input.css +0 -207
  330. package/src/styles/nexus.loader.css +0 -11
  331. package/src/styles/nexus.logic.css +0 -47
  332. package/src/styles/nexus.utility.css +0 -347
@@ -0,0 +1,84 @@
1
+ import { type ReactNode } from "react";
2
+ import type { DropdownColumn, DropdownItem } from "../../Interfaces/InputInterfaces.ts";
3
+ /**
4
+ * Why a current value cannot be picked again:
5
+ * - `inactive`: its item is marked inactive, such as a deactivated user.
6
+ * - `unavailable`: its item is no longer among the options.
7
+ * - `unknown`: nothing is known about the value but the value itself.
8
+ */
9
+ export type DropdownValueState = "inactive" | "unavailable" | "unknown";
10
+ export declare const DROPDOWN_STATE_LABELS: Record<DropdownValueState, string>;
11
+ /** The tooltip of a current value that cannot be picked again. */
12
+ export declare const DROPDOWN_STATE_NOTES: Record<DropdownValueState, string>;
13
+ type AnyColumn = DropdownColumn<never>;
14
+ /** A round photo, or the initials of the name on a soft tone when there is no photo or it fails to load. */
15
+ export declare function DropdownAvatar({ src, name }: {
16
+ src: unknown;
17
+ name: string;
18
+ }): import("react").JSX.Element;
19
+ /** A small 4:3 picture, such as a flag or a logo; an empty frame when there is none or it fails to load. */
20
+ export declare function DropdownImage({ src }: {
21
+ src: unknown;
22
+ }): import("react").JSX.Element;
23
+ /** An icon field: a Tabler icon name, or an element such as `<Icon name="wifi" />`. */
24
+ export declare function iconNode(icon: unknown): ReactNode;
25
+ /** Whether an icon field has something to draw. */
26
+ export declare function hasIcon(icon: unknown): boolean;
27
+ /** One cell of a row with columns. `label` names the avatar's initials; `tag` follows the text. */
28
+ export declare function DropdownCell({ column, item, label, tag }: {
29
+ column: AnyColumn;
30
+ item: DropdownItem;
31
+ label: string;
32
+ tag?: ReactNode;
33
+ }): import("react").JSX.Element;
34
+ /** The picture that leads a value in the box or a chip: the first avatar, image, or icon column, or the icon field. */
35
+ export declare function DropdownMedia({ item, label, columns, iconKey }: {
36
+ item: DropdownItem;
37
+ label: string;
38
+ columns?: readonly AnyColumn[];
39
+ iconKey: string;
40
+ }): import("react").JSX.Element | null;
41
+ /** The tag of a value that cannot be picked again. */
42
+ export declare function DropdownTag({ state }: {
43
+ state: DropdownValueState | null;
44
+ }): import("react").JSX.Element | null;
45
+ export interface DropdownOptionProps {
46
+ id: string;
47
+ index: number;
48
+ /** `null` for a value known only by itself. */
49
+ item: DropdownItem | null;
50
+ /** Text of the item's label, or the bare value. */
51
+ label: string;
52
+ /** The label field when it is an element rather than text. */
53
+ labelNode?: ReactNode;
54
+ description?: ReactNode;
55
+ columns?: readonly AnyColumn[];
56
+ /** The column that holds the tag: the label's column, or the last text column. */
57
+ tagColumn: number;
58
+ iconKey: string;
59
+ /** Rows without columns keep an icon slot when any row has an icon, so labels line up. */
60
+ iconSlot: boolean;
61
+ multiple: boolean;
62
+ selected: boolean;
63
+ active: boolean;
64
+ disabled: boolean;
65
+ /** Resolving the item of a bare value: the label shows a placeholder bar. */
66
+ pending: boolean;
67
+ state: DropdownValueState | null;
68
+ }
69
+ /**
70
+ * One row of the list. Memoized, and its events are handled by the list, so moving the highlight or picking re-renders
71
+ * only the rows whose state changed.
72
+ */
73
+ export declare const DropdownOption: import("react").MemoExoticComponent<(props: DropdownOptionProps) => import("react").JSX.Element>;
74
+ export interface DropdownChipProps {
75
+ label: string;
76
+ media?: ReactNode;
77
+ state: DropdownValueState | null;
78
+ pending: boolean;
79
+ /** Without it, the chip has no remove button (read-only, disabled). */
80
+ onRemove?: () => void;
81
+ }
82
+ /** A chosen value in a multiple dropdown's box. */
83
+ export declare function DropdownChip({ label, media, state, pending, onRemove }: DropdownChipProps): import("react").JSX.Element;
84
+ export {};
@@ -0,0 +1,102 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Icon, iconCheck, iconX, InlineIcon } from "nexus-icons";
4
+ import { isValidElement, memo, useState } from "react";
5
+ import { cx } from "../../Helpers/ClassHelpers.js";
6
+ import { avatarTone, initials, isMediaColumn, readField } from "../../Helpers/DropdownHelpers.js";
7
+ export const DROPDOWN_STATE_LABELS = {
8
+ inactive: "Inactive",
9
+ unavailable: "Unavailable",
10
+ unknown: "Not found",
11
+ };
12
+ /** The tooltip of a current value that cannot be picked again. */
13
+ export const DROPDOWN_STATE_NOTES = {
14
+ inactive: "Inactive: kept as the current value. Once removed, it cannot be chosen again.",
15
+ unavailable: "No longer in the list: kept as the current value. Once removed, it cannot be chosen again.",
16
+ unknown: "Not found in the list: kept as the current value. Once removed, it cannot be chosen again.",
17
+ };
18
+ /** A round photo, or the initials of the name on a soft tone when there is no photo or it fails to load. */
19
+ export function DropdownAvatar({ src, name }) {
20
+ const url = typeof src === "string" ? src.trim() : "";
21
+ const [failed, setFailed] = useState(null);
22
+ const photo = url !== "" && failed !== url;
23
+ return (_jsx("span", { className: "nx-dropdown-avatar", "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) }));
24
+ }
25
+ /** A small 4:3 picture, such as a flag or a logo; an empty frame when there is none or it fails to load. */
26
+ export function DropdownImage({ src }) {
27
+ const url = typeof src === "string" ? src.trim() : "";
28
+ const [failed, setFailed] = useState(null);
29
+ if (url === "" || failed === url)
30
+ return _jsx("span", { className: "nx-dropdown-image nx-dropdown-image--empty", "aria-hidden": "true" });
31
+ return _jsx("img", { className: "nx-dropdown-image", src: url, alt: "", loading: "lazy", decoding: "async", onError: () => setFailed(url) });
32
+ }
33
+ /** An icon field: a Tabler icon name, or an element such as `<Icon name="wifi" />`. */
34
+ export function iconNode(icon) {
35
+ if (typeof icon === "string" && icon)
36
+ return _jsx(Icon, { name: icon });
37
+ return isValidElement(icon) ? icon : null;
38
+ }
39
+ /** Whether an icon field has something to draw. */
40
+ export function hasIcon(icon) {
41
+ return (typeof icon === "string" && icon !== "") || isValidElement(icon);
42
+ }
43
+ const alignStyle = (align) => align && align !== "start" ? { justifySelf: align, textAlign: align } : undefined;
44
+ /** One cell of a row with columns. `label` names the avatar's initials; `tag` follows the text. */
45
+ export function DropdownCell({ column, item, label, tag }) {
46
+ const type = column.type ?? "text";
47
+ const style = alignStyle(column.align);
48
+ if (column.render) {
49
+ return (_jsxs("span", { className: cx("nx-dropdown-cell", `nx-dropdown-cell--${type}`), style: style, children: [column.render(item), tag] }));
50
+ }
51
+ const value = readField(item, column.key);
52
+ if (isMediaColumn(column)) {
53
+ return (_jsx("span", { className: "nx-dropdown-cell nx-dropdown-cell--media", style: style, "aria-hidden": "true", children: type === "avatar" ? _jsx(DropdownAvatar, { src: value, name: label }) : type === "image" ? _jsx(DropdownImage, { src: value }) : _jsx("span", { className: "nx-dropdown-icon", children: iconNode(value) }) }));
54
+ }
55
+ const text = value === null || value === undefined ? "" : String(value);
56
+ return (_jsxs("span", { className: cx("nx-dropdown-cell", `nx-dropdown-cell--${type}`), style: style, children: [_jsx("span", { className: "nx-dropdown-cell__text", children: text }), tag] }));
57
+ }
58
+ /** The picture that leads a value in the box or a chip: the first avatar, image, or icon column, or the icon field. */
59
+ export function DropdownMedia({ item, label, columns, iconKey }) {
60
+ if (columns) {
61
+ const column = columns.find(isMediaColumn);
62
+ if (!column)
63
+ return null;
64
+ const value = readField(item, column.key);
65
+ if (column.type === "avatar")
66
+ return _jsx(DropdownAvatar, { src: value, name: label });
67
+ if (column.type === "image")
68
+ return _jsx(DropdownImage, { src: value });
69
+ return hasIcon(value) ? _jsx("span", { className: "nx-dropdown-icon", children: iconNode(value) }) : null;
70
+ }
71
+ const icon = readField(item, iconKey);
72
+ return hasIcon(icon) ? (_jsx("span", { className: "nx-dropdown-icon", "aria-hidden": "true", children: iconNode(icon) })) : null;
73
+ }
74
+ /** The tag of a value that cannot be picked again. */
75
+ export function DropdownTag({ state }) {
76
+ return state ? _jsx("span", { className: "nx-dropdown-tag", children: DROPDOWN_STATE_LABELS[state] }) : null;
77
+ }
78
+ /**
79
+ * One row of the list. Memoized, and its events are handled by the list, so moving the highlight or picking re-renders
80
+ * only the rows whose state changed.
81
+ */
82
+ export const DropdownOption = memo(function DropdownOption(props) {
83
+ const { id, index, item, label, labelNode, description, columns, tagColumn, iconKey, iconSlot, multiple, selected, active, disabled, pending, state } = props;
84
+ const tag = _jsx(DropdownTag, { state: state });
85
+ const text = pending ? _jsx("span", { className: "nx-dropdown-skeleton" }) : (labelNode ?? label);
86
+ let body;
87
+ if (columns && item) {
88
+ body = columns.map((column, columnIndex) => _jsx(DropdownCell, { column: column, item: item, label: label, tag: columnIndex === tagColumn ? tag : null }, columnIndex));
89
+ }
90
+ else if (columns) {
91
+ // A bare value has no fields to fill the columns: its text and tag span them all.
92
+ body = (_jsxs("span", { className: "nx-dropdown-cell nx-dropdown-cell--span", children: [_jsx("span", { className: "nx-dropdown-cell__text", children: text }), tag] }));
93
+ }
94
+ else {
95
+ body = (_jsxs(_Fragment, { children: [iconSlot && (_jsx("span", { className: "nx-dropdown-option__icon", "aria-hidden": "true", children: item && iconNode(readField(item, iconKey)) })), _jsxs("span", { className: "nx-dropdown-option__body", children: [_jsxs("span", { className: "nx-dropdown-option__label", children: [_jsx("span", { className: "nx-dropdown-option__text", children: text }), tag] }), description !== undefined && description !== null && description !== "" && _jsx("span", { className: "nx-dropdown-option__description", children: description })] })] }));
96
+ }
97
+ return (_jsxs("div", { role: "option", id: id, "data-index": index, "data-active": active || undefined, "data-state": state ?? undefined, "aria-selected": selected, "aria-disabled": disabled || undefined, title: state ? DROPDOWN_STATE_NOTES[state] : undefined, className: "nx-dropdown-option", children: [_jsx("span", { className: "nx-dropdown-option__check", "aria-hidden": "true", children: multiple ? _jsx("span", { className: "nx-dropdown-checkbox" }) : selected && _jsx(InlineIcon, { icon: iconCheck }) }), body] }));
98
+ });
99
+ /** A chosen value in a multiple dropdown's box. */
100
+ export function DropdownChip({ label, media, state, pending, onRemove }) {
101
+ return (_jsxs("span", { className: "nx-dropdown-chip", "data-state": state ?? undefined, title: state ? `${label}. ${DROPDOWN_STATE_NOTES[state]}` : label, children: [media, _jsx("span", { className: "nx-dropdown-chip__label", children: pending ? _jsx("span", { className: "nx-dropdown-skeleton" }) : label }), onRemove && (_jsx("button", { type: "button", className: "nx-dropdown-chip__remove", tabIndex: -1, "aria-label": `Remove ${label}`, onMouseDown: event => event.preventDefault(), onClick: onRemove, children: _jsx(InlineIcon, { icon: iconX }) }))] }));
102
+ }
@@ -0,0 +1,45 @@
1
+ import { type CSSProperties, type KeyboardEvent, type ReactNode, type Ref } from "react";
2
+ export interface EditorShellOptions {
3
+ /** Height of the body, in pixels. A double-click on the handle goes back to it. */
4
+ height: number;
5
+ minHeight: number;
6
+ maxHeight: number;
7
+ /** The status bar drags the height. */
8
+ resizable: boolean;
9
+ /** Local storage key that remembers the height the user picks. */
10
+ storageKey?: string;
11
+ /** A button that opens the editor over the whole window. */
12
+ fullscreen: boolean;
13
+ disabled?: boolean;
14
+ /** What takes focus when the editor opens over the window, unless focus is inside it already. */
15
+ focusTarget: (box: HTMLElement) => HTMLElement | null;
16
+ }
17
+ type BodyStyle = CSSProperties & {
18
+ "--nx-editor-height": string;
19
+ };
20
+ /**
21
+ * State and handlers of the editor frame. The height changes by writing a CSS variable, so dragging re-renders
22
+ * nothing; full screen moves the field's box into the top layer as a popover without moving it in the page, so the
23
+ * control inside keeps its undo history and its form.
24
+ */
25
+ export declare function useEditorShell(options: EditorShellOptions): {
26
+ fullscreen: boolean;
27
+ setFullscreen: import("react").Dispatch<import("react").SetStateAction<boolean>>;
28
+ frameRef: import("react").RefObject<HTMLDivElement | null>;
29
+ bodyRef: import("react").RefObject<HTMLDivElement | null>;
30
+ bodyStyle: BodyStyle;
31
+ onFrameKeyDown: (event: KeyboardEvent<HTMLDivElement>) => void;
32
+ moveTool: (event: KeyboardEvent<HTMLDivElement>) => void;
33
+ fullscreenButton: import("react").JSX.Element | null;
34
+ footer: (hint: ReactNode, count: ReactNode) => import("react").JSX.Element;
35
+ };
36
+ /** Word and character counts for the status bar. Editors update them straight in the DOM with `writeEditorCount`. */
37
+ export declare function EditorCount({ ref, text, maxLength }: {
38
+ ref: Ref<HTMLSpanElement>;
39
+ text: string;
40
+ maxLength?: number;
41
+ }): import("react").JSX.Element;
42
+ export declare function writeEditorCount(element: HTMLElement | null, text: string, maxLength?: number): void;
43
+ export declare function readStorage(key: string): string | null;
44
+ export declare function writeStorage(key: string, value: string | null): void;
45
+ export {};
@@ -0,0 +1,214 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { iconArrowsMaximize, iconArrowsMinimize, InlineIcon } from "nexus-icons";
4
+ import { useEffect, useEffectEvent, useLayoutEffect, useRef, useState } from "react";
5
+ /*
6
+ * The frame shared by the Markdown and rich text editors: a bar for views and formatting buttons, a body with a
7
+ * height, a status bar that is also the height handle, and a full-screen view. Class names start with `nx-editor__`
8
+ * (Nexus.Editor.css); each editor adds its own class to the field (`nx-md`, `nx-rte`).
9
+ */
10
+ /** Letters and digits, with the marks of scripts such as Devanagari; an apostrophe inside a word keeps it one word. */
11
+ const WORDS = /[\p{L}\p{N}\p{M}]+(?:['\p{Pf}][\p{L}\p{N}\p{M}]+)*/gu;
12
+ /** How long the height shows after a key moves the handle, in milliseconds. */
13
+ const SIZE_MS = 900;
14
+ /**
15
+ * State and handlers of the editor frame. The height changes by writing a CSS variable, so dragging re-renders
16
+ * nothing; full screen moves the field's box into the top layer as a popover without moving it in the page, so the
17
+ * control inside keeps its undo history and its form.
18
+ */
19
+ export function useEditorShell(options) {
20
+ const { height, minHeight, maxHeight, resizable, storageKey, fullscreen: canFullscreen, disabled } = options;
21
+ const [fullscreen, setFullscreen] = useState(false);
22
+ const frameRef = useRef(null);
23
+ const bodyRef = useRef(null);
24
+ const handleRef = useRef(null);
25
+ const footerRef = useRef(null);
26
+ const heightRef = useRef(height);
27
+ const dragRef = useRef(null);
28
+ const sizeTimerRef = useRef(0);
29
+ const focusInside = useEffectEvent((box) => options.focusTarget(box)?.focus());
30
+ const canResize = resizable && !fullscreen;
31
+ // ---- Height ----
32
+ function setHeight(next, save = false) {
33
+ const value = Math.round(Math.min(Math.max(next, minHeight), Math.max(minHeight, maxHeight)));
34
+ heightRef.current = value;
35
+ bodyRef.current?.style.setProperty("--nx-editor-height", `${value}px`);
36
+ handleRef.current?.setAttribute("aria-valuenow", String(value));
37
+ handleRef.current?.setAttribute("aria-valuetext", `${value} pixels`);
38
+ const size = footerRef.current?.querySelector(".nx-editor__size");
39
+ if (size)
40
+ size.textContent = `${value} px`;
41
+ if (save && storageKey)
42
+ writeStorage(storageKey, String(value));
43
+ }
44
+ // A remembered height applies before the first paint in the browser; the server renders the default.
45
+ useLayoutEffect(() => {
46
+ const stored = storageKey ? Number(readStorage(storageKey)) : 0;
47
+ setHeight(stored > 0 ? stored : height);
48
+ }, [storageKey, height, minHeight, maxHeight]);
49
+ useEffect(() => () => window.clearTimeout(sizeTimerRef.current), []);
50
+ function startResize(event) {
51
+ if (event.button !== 0 || !canResize || disabled)
52
+ return;
53
+ // No text selection and no focus change while dragging.
54
+ event.preventDefault();
55
+ event.currentTarget.setPointerCapture(event.pointerId);
56
+ dragRef.current = { pointer: event.pointerId, y: event.clientY, height: heightRef.current };
57
+ event.currentTarget.setAttribute("data-resizing", "");
58
+ }
59
+ function moveResize(event) {
60
+ const drag = dragRef.current;
61
+ if (drag?.pointer === event.pointerId)
62
+ setHeight(drag.height + event.clientY - drag.y);
63
+ }
64
+ function endResize(event) {
65
+ if (dragRef.current?.pointer !== event.pointerId)
66
+ return;
67
+ dragRef.current = null;
68
+ event.currentTarget.removeAttribute("data-resizing");
69
+ setHeight(heightRef.current, true);
70
+ }
71
+ function resetHeight() {
72
+ if (!canResize || disabled)
73
+ return;
74
+ setHeight(height);
75
+ if (storageKey)
76
+ writeStorage(storageKey, null);
77
+ }
78
+ function resizeWithKeys(event) {
79
+ const step = event.shiftKey ? 100 : 20;
80
+ const current = heightRef.current;
81
+ const next = event.key === "ArrowUp" ? current - step
82
+ : event.key === "ArrowDown" ? current + step
83
+ : event.key === "PageUp" ? current - 200
84
+ : event.key === "PageDown" ? current + 200
85
+ : event.key === "Home" ? minHeight
86
+ : event.key === "End" ? maxHeight
87
+ : null;
88
+ if (next === null)
89
+ return;
90
+ event.preventDefault();
91
+ setHeight(next, true);
92
+ // Shows the height for a moment, as a drag does.
93
+ const footer = footerRef.current;
94
+ footer?.setAttribute("data-resizing", "");
95
+ window.clearTimeout(sizeTimerRef.current);
96
+ sizeTimerRef.current = window.setTimeout(() => {
97
+ if (!dragRef.current)
98
+ footer?.removeAttribute("data-resizing");
99
+ }, SIZE_MS);
100
+ }
101
+ // ---- Full screen ----
102
+ useLayoutEffect(() => {
103
+ const box = frameRef.current?.closest(".nx-field__box");
104
+ if (!fullscreen || !box)
105
+ return;
106
+ box.setAttribute("popover", "manual");
107
+ box.showPopover();
108
+ // Focus goes inside, so Esc closes it even after a click, which leaves focus where it was.
109
+ if (!box.contains(document.activeElement))
110
+ focusInside(box);
111
+ const root = document.documentElement;
112
+ const overflow = root.style.overflow;
113
+ root.style.overflow = "hidden";
114
+ return () => {
115
+ root.style.overflow = overflow;
116
+ if (box.matches(":popover-open"))
117
+ box.hidePopover();
118
+ box.removeAttribute("popover");
119
+ };
120
+ }, [fullscreen]);
121
+ function handleFrameKeyDown(event) {
122
+ // Keys inside a menu or a panel of the bar belong to it.
123
+ if (!fullscreen || event.defaultPrevented || event.target.closest(".nx-popover"))
124
+ return;
125
+ if (event.key === "Escape") {
126
+ event.preventDefault();
127
+ setFullscreen(false);
128
+ }
129
+ else if (event.key === "Tab") {
130
+ // Over the whole window, Tab stays inside the editor instead of moving to the page hidden behind it.
131
+ const stops = [...event.currentTarget.querySelectorAll('button, textarea, [tabindex], [contenteditable="true"]')].filter(element => element.tabIndex >= 0 && !element.disabled && element.getClientRects().length > 0 && !element.closest(".nx-popover"));
132
+ const edge = event.shiftKey ? stops[0] : stops[stops.length - 1];
133
+ if (edge && document.activeElement === edge) {
134
+ event.preventDefault();
135
+ (event.shiftKey ? stops[stops.length - 1] : stops[0])?.focus();
136
+ }
137
+ }
138
+ }
139
+ // ---- Keyboard in the toolbar ----
140
+ /** Arrow keys, Home, and End move between toolbar buttons, which are one tab stop. */
141
+ function moveTool(event) {
142
+ if (event.target.closest(".nx-popover"))
143
+ return;
144
+ const buttons = [...event.currentTarget.querySelectorAll("button:not(:disabled)")].filter(button => !button.closest(".nx-popover"));
145
+ const index = buttons.indexOf(document.activeElement);
146
+ const next = event.key === "ArrowRight" ? index + 1
147
+ : event.key === "ArrowLeft" ? index - 1
148
+ : event.key === "Home" ? 0
149
+ : event.key === "End" ? buttons.length - 1
150
+ : null;
151
+ if (next === null || index < 0)
152
+ return;
153
+ event.preventDefault();
154
+ const target = buttons[(next + buttons.length) % buttons.length];
155
+ for (const button of buttons)
156
+ button.tabIndex = button === target ? 0 : -1;
157
+ target.focus();
158
+ }
159
+ const bodyStyle = { "--nx-editor-height": `${height}px` };
160
+ const fullscreenButton = canFullscreen ? (_jsx("button", { type: "button", className: "nx-editor__tool", "aria-pressed": fullscreen, "aria-label": "Full screen", title: fullscreen ? "Exit full screen (Esc)" : "Full screen", disabled: disabled, onMouseDown: event => event.preventDefault(), onClick: () => setFullscreen(!fullscreen), children: _jsx(InlineIcon, { icon: fullscreen ? iconArrowsMinimize : iconArrowsMaximize }) })) : null;
161
+ /** The status bar: `hint` on the left, the height handle in the middle, `count` on the right. */
162
+ function footer(hint, count) {
163
+ return (_jsxs("div", { ref: footerRef, className: "nx-editor__footer", "data-resizable": canResize || undefined, title: canResize ? "Drag to change the height. Double-click to reset it." : undefined, onPointerDown: startResize, onPointerMove: moveResize, onPointerUp: endResize, onPointerCancel: endResize, onDoubleClick: resetHeight, children: [_jsx("span", { className: "nx-editor__hint", children: hint }), canResize && (_jsxs("div", { ref: handleRef, role: "separator", "aria-orientation": "horizontal", "aria-label": "Editor height", "aria-valuemin": minHeight, "aria-valuemax": Math.max(minHeight, maxHeight), "aria-valuenow": height, "aria-valuetext": `${height} pixels`, tabIndex: disabled ? -1 : 0, className: "nx-editor__handle", onKeyDown: resizeWithKeys, children: [_jsx("span", { className: "nx-editor__grip", "aria-hidden": "true" }), _jsx("span", { className: "nx-editor__size", "aria-hidden": "true", children: `${height} px` })] })), count] }));
164
+ }
165
+ return {
166
+ fullscreen,
167
+ setFullscreen,
168
+ frameRef,
169
+ bodyRef,
170
+ bodyStyle,
171
+ onFrameKeyDown: handleFrameKeyDown,
172
+ moveTool,
173
+ fullscreenButton,
174
+ footer,
175
+ };
176
+ }
177
+ /** Word and character counts for the status bar. Editors update them straight in the DOM with `writeEditorCount`. */
178
+ export function EditorCount({ ref, text, maxLength }) {
179
+ return (_jsxs("span", { ref: ref, className: "nx-editor__count", children: [_jsx("span", { className: "nx-editor__words", children: countWords(text) }), " ", _jsx("span", { children: countCharacters(text, maxLength) })] }));
180
+ }
181
+ export function writeEditorCount(element, text, maxLength) {
182
+ const [words, characters] = element?.children ?? [];
183
+ if (words)
184
+ words.textContent = countWords(text);
185
+ if (characters)
186
+ characters.textContent = countCharacters(text, maxLength);
187
+ }
188
+ function countWords(text) {
189
+ const words = text.match(WORDS)?.length ?? 0;
190
+ return `${words.toLocaleString("en-US")} ${words === 1 ? "word" : "words"},`;
191
+ }
192
+ function countCharacters(text, maxLength) {
193
+ const limit = maxLength ? `/${maxLength.toLocaleString("en-US")}` : "";
194
+ return `${text.length.toLocaleString("en-US")}${limit} ${text.length === 1 && !limit ? "character" : "characters"}`;
195
+ }
196
+ export function readStorage(key) {
197
+ try {
198
+ return localStorage.getItem(key);
199
+ }
200
+ catch {
201
+ return null;
202
+ }
203
+ }
204
+ export function writeStorage(key, value) {
205
+ try {
206
+ if (value === null)
207
+ localStorage.removeItem(key);
208
+ else
209
+ localStorage.setItem(key, value);
210
+ }
211
+ catch {
212
+ // Storage can be blocked; the value then lasts until the page reloads.
213
+ }
214
+ }
@@ -0,0 +1,53 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ import type { InputDesign } from "../../Interfaces/InputInterfaces.ts";
3
+ import type { Size } from "../../Interfaces/TypeInterfaces.ts";
4
+ export interface FieldProps {
5
+ /** Id of the control inside, so the label points at it. */
6
+ controlId: string;
7
+ label?: ReactNode;
8
+ /** Id of the label, for a control that is not an input and names itself with `aria-labelledby`. */
9
+ labelId?: string;
10
+ /** Adds a required mark after the label. The control itself carries `aria-required`. */
11
+ required?: boolean;
12
+ /** Default `standard`. */
13
+ design?: InputDesign;
14
+ /** Default `md`. */
15
+ size?: Size;
16
+ /** Help text under the box. An error takes its place. */
17
+ hint?: ReactNode;
18
+ /** Error message. Marks the field invalid and replaces the hint. */
19
+ error?: ReactNode;
20
+ /** Id of the hint or error line, for the control's `aria-describedby`. */
21
+ messageId?: string;
22
+ /** Decorative icon at the start of the box. */
23
+ icon?: ReactNode;
24
+ /** Decorative icon at the end of the box. */
25
+ iconEnd?: ReactNode;
26
+ /** Text before the control, such as a currency ("Rs") or "https://". Its id is `<controlId>-prefix`, for `aria-describedby`. */
27
+ prefix?: ReactNode;
28
+ /** Text after the control, such as a unit ("kg", "%") or a domain. Its id is `<controlId>-suffix`. */
29
+ suffix?: ReactNode;
30
+ /** Buttons inside the box before the control, such as a minus button. */
31
+ actionsStart?: ReactNode;
32
+ /** Buttons inside the box after the control, such as clear or show password. */
33
+ actions?: ReactNode;
34
+ /** Right side of the line under the box, such as a character count. */
35
+ aside?: ReactNode;
36
+ disabled?: boolean;
37
+ readOnly?: boolean;
38
+ /** Keeps an `outlined` or `inbox` label raised, for a control that shows a placeholder. */
39
+ raiseLabel?: boolean;
40
+ /** For controls taller than one line, such as a text area. */
41
+ multiline?: boolean;
42
+ className?: string;
43
+ style?: CSSProperties;
44
+ /** The control: an input, a text area, or a custom element with the `nx-field__control` class. */
45
+ children: ReactNode;
46
+ }
47
+ /**
48
+ * The frame around every input: label, box, icons, and the hint or error line. It has no state, so it renders
49
+ * anywhere; inputs such as `TextBox` build on it, and so can custom controls.
50
+ */
51
+ export declare function Field({ controlId, label, labelId, required, design, size, hint, error, messageId, icon, iconEnd, prefix, suffix, actionsStart, actions, aside, disabled, readOnly, raiseLabel, multiline, className, style, children, }: FieldProps): import("react").JSX.Element;
52
+ /** Whether a node renders something: not `undefined`, `null`, `false`, or "". */
53
+ export declare const hasContent: (node: ReactNode) => boolean;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { InlineIcon, iconAlertCircle } from "nexus-icons";
3
+ import { cx } from "../../Helpers/ClassHelpers.js";
4
+ /**
5
+ * The frame around every input: label, box, icons, and the hint or error line. It has no state, so it renders
6
+ * anywhere; inputs such as `TextBox` build on it, and so can custom controls.
7
+ */
8
+ export function Field({ controlId, label, labelId, required = false, design = "standard", size = "md", hint, error, messageId, icon, iconEnd, prefix, suffix, actionsStart, actions, aside, disabled = false, readOnly = false, raiseLabel = false, multiline = false, className, style, children, }) {
9
+ const hasLabel = hasContent(label);
10
+ const hasError = hasContent(error);
11
+ const message = hasError ? error : hint;
12
+ const labelElement = hasLabel && (_jsxs("label", { id: labelId, className: "nx-field__label", htmlFor: controlId, children: [label, required && (_jsx("span", { className: "nx-field__required", "aria-hidden": "true", children: "*" }))] }));
13
+ return (_jsxs("div", { className: cx("nx-field", `nx-field--${design}`, `nx-field--${size}`, multiline && "nx-field--multiline", className), "data-invalid": hasError || undefined, "data-disabled": disabled || undefined, "data-readonly": readOnly || undefined, "data-raised": raiseLabel || undefined, style: style, children: [design === "standard" && labelElement, _jsxs("div", { className: "nx-field__box", children: [actionsStart && _jsx("span", { className: "nx-field__actions nx-field__actions--start", children: actionsStart }), icon && (_jsx("span", { className: "nx-field__icon", "aria-hidden": "true", children: icon })), hasContent(prefix) && (_jsx("span", { id: `${controlId}-prefix`, className: "nx-field__affix", children: prefix })), children, design !== "standard" && labelElement, hasContent(suffix) && (_jsx("span", { id: `${controlId}-suffix`, className: "nx-field__affix nx-field__affix--end", children: suffix })), actions && _jsx("span", { className: "nx-field__actions", children: actions }), iconEnd && (_jsx("span", { className: "nx-field__icon nx-field__icon--end", "aria-hidden": "true", children: iconEnd }))] }), (message || aside) && (_jsxs("div", { className: "nx-field__footer", children: [message && (_jsxs("p", { id: messageId, className: cx("nx-field__message", hasError && "nx-field__message--error"), children: [hasError && _jsx(InlineIcon, { icon: iconAlertCircle }), _jsx("span", { children: message })] })), aside && _jsx("span", { className: "nx-field__aside", children: aside })] }))] }));
14
+ }
15
+ /** Whether a node renders something: not `undefined`, `null`, `false`, or "". */
16
+ export const hasContent = (node) => node !== undefined && node !== null && node !== false && node !== "";
@@ -0,0 +1,20 @@
1
+ import type { GroupFormProps, RowValue } from "../../Interfaces/FormInterfaces.ts";
2
+ /**
3
+ * The rows of a `TabularForm` as sections: each row's inputs with their own labels, on a 12-column grid (`span` in
4
+ * each input's params). The same params, value, rules, and form behavior as `TabularForm`; only the layout differs. It
5
+ * suits rows with more or wider inputs than a table row holds, such as addresses, guardians, or work history, when there
6
+ * are a few of them. For hundreds of rows, use `TabularForm`, which renders only the rows in view.
7
+ *
8
+ * A section collapses to one line that lists its first values, and tags the radios it has on ("Primary"). Sections can
9
+ * move, be copied, and be removed (with Undo). A check that finds problems opens their sections and says how many.
10
+ *
11
+ * @example
12
+ * <GroupForm name="addresses" label="Addresses" itemLabel="Address" max={5} inputs={[
13
+ * { type: "dropdown", name: "country", label: "Country", required: true, options: countries, span: 6 },
14
+ * { type: "text", name: "city", label: "City", required: true, span: 6 },
15
+ * { type: "text", name: "zipCode", label: "Zip code", span: 4 },
16
+ * { type: "dropdown", name: "type", label: "Type", options: ADDRESS_TYPES, span: 4 },
17
+ * { type: "radio", name: "isPrimary", label: "Primary address", span: 4, required: true },
18
+ * ]} />
19
+ */
20
+ export declare function GroupForm<TRow extends RowValue = RowValue>(props: GroupFormProps<TRow>): import("react").JSX.Element;