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.
- package/LICENSE +21 -0
- package/README.md +903 -0
- package/dist/Client.Index.d.ts +44 -0
- package/dist/Client.Index.js +49 -0
- package/dist/Components/Documents/Button.d.ts +29 -0
- package/dist/Components/Documents/Button.js +11 -0
- package/dist/Components/Documents/Menu.d.ts +102 -0
- package/dist/Components/Documents/Menu.js +238 -0
- package/dist/Components/Documents/SplitButton.d.ts +64 -0
- package/dist/Components/Documents/SplitButton.js +127 -0
- package/dist/Components/Documents/TabButtons.d.ts +62 -0
- package/dist/Components/Documents/TabButtons.js +19 -0
- package/dist/Components/Forms/ApiForm.d.ts +21 -0
- package/dist/Components/Forms/ApiForm.js +99 -0
- package/dist/Components/Forms/Crud.d.ts +17 -0
- package/dist/Components/Forms/Crud.js +556 -0
- package/dist/Components/Forms/CrudParts.d.ts +15 -0
- package/dist/Components/Forms/CrudParts.js +36 -0
- package/dist/Components/Forms/Form.d.ts +52 -0
- package/dist/Components/Forms/Form.js +319 -0
- package/dist/Components/Forms/SubmitForm.d.ts +65 -0
- package/dist/Components/Forms/SubmitForm.js +198 -0
- package/dist/Components/Inputs/Calendar.d.ts +69 -0
- package/dist/Components/Inputs/Calendar.js +258 -0
- package/dist/Components/Inputs/CheckBox.d.ts +53 -0
- package/dist/Components/Inputs/CheckBox.js +87 -0
- package/dist/Components/Inputs/CheckBoxGroup.d.ts +20 -0
- package/dist/Components/Inputs/CheckBoxGroup.js +80 -0
- package/dist/Components/Inputs/ChoiceField.d.ts +122 -0
- package/dist/Components/Inputs/ChoiceField.js +169 -0
- package/dist/Components/Inputs/DatePicker.d.ts +54 -0
- package/dist/Components/Inputs/DatePicker.js +246 -0
- package/dist/Components/Inputs/DateRangePicker.d.ts +62 -0
- package/dist/Components/Inputs/DateRangePicker.js +283 -0
- package/dist/Components/Inputs/DateTimePicker.d.ts +75 -0
- package/dist/Components/Inputs/DateTimePicker.js +399 -0
- package/dist/Components/Inputs/Dropdown.d.ts +117 -0
- package/dist/Components/Inputs/Dropdown.js +839 -0
- package/dist/Components/Inputs/DropdownParts.d.ts +84 -0
- package/dist/Components/Inputs/DropdownParts.js +102 -0
- package/dist/Components/Inputs/EditorShell.d.ts +45 -0
- package/dist/Components/Inputs/EditorShell.js +214 -0
- package/dist/Components/Inputs/Field.d.ts +53 -0
- package/dist/Components/Inputs/Field.js +16 -0
- package/dist/Components/Inputs/GroupForm.d.ts +20 -0
- package/dist/Components/Inputs/GroupForm.js +170 -0
- package/dist/Components/Inputs/InputField.d.ts +59 -0
- package/dist/Components/Inputs/InputField.js +169 -0
- package/dist/Components/Inputs/InputRenderer.d.ts +35 -0
- package/dist/Components/Inputs/InputRenderer.js +99 -0
- package/dist/Components/Inputs/MarkdownEditor.d.ts +67 -0
- package/dist/Components/Inputs/MarkdownEditor.js +289 -0
- package/dist/Components/Inputs/NumberBox.d.ts +53 -0
- package/dist/Components/Inputs/NumberBox.js +228 -0
- package/dist/Components/Inputs/RadioGroup.d.ts +20 -0
- package/dist/Components/Inputs/RadioGroup.js +43 -0
- package/dist/Components/Inputs/ReadOnlyNotice.d.ts +36 -0
- package/dist/Components/Inputs/ReadOnlyNotice.js +116 -0
- package/dist/Components/Inputs/RichTextEditor.d.ts +103 -0
- package/dist/Components/Inputs/RichTextEditor.js +1170 -0
- package/dist/Components/Inputs/RichTextParts.d.ts +63 -0
- package/dist/Components/Inputs/RichTextParts.js +157 -0
- package/dist/Components/Inputs/RowsInput.d.ts +104 -0
- package/dist/Components/Inputs/RowsInput.js +259 -0
- package/dist/Components/Inputs/Slider.d.ts +87 -0
- package/dist/Components/Inputs/Slider.js +216 -0
- package/dist/Components/Inputs/TabularForm.d.ts +23 -0
- package/dist/Components/Inputs/TabularForm.js +283 -0
- package/dist/Components/Inputs/TextArea.d.ts +43 -0
- package/dist/Components/Inputs/TextArea.js +31 -0
- package/dist/Components/Inputs/TextBox.d.ts +52 -0
- package/dist/Components/Inputs/TextBox.js +58 -0
- package/dist/Components/Inputs/TextField.d.ts +31 -0
- package/dist/Components/Inputs/TextField.js +35 -0
- package/dist/Components/Inputs/TimePanel.d.ts +34 -0
- package/dist/Components/Inputs/TimePanel.js +288 -0
- package/dist/Components/Inputs/TimePicker.d.ts +54 -0
- package/dist/Components/Inputs/TimePicker.js +239 -0
- package/dist/Components/Inputs/TreeView.d.ts +160 -0
- package/dist/Components/Inputs/TreeView.js +947 -0
- package/dist/Components/Layouts/MessageBox.d.ts +71 -0
- package/dist/Components/Layouts/MessageBox.js +104 -0
- package/dist/Components/Layouts/Popup.d.ts +92 -0
- package/dist/Components/Layouts/Popup.js +376 -0
- package/dist/Components/Layouts/PopupContext.d.ts +2 -0
- package/dist/Components/Layouts/PopupContext.js +4 -0
- package/dist/Components/Layouts/PopupDock.d.ts +22 -0
- package/dist/Components/Layouts/PopupDock.js +121 -0
- package/dist/Components/Layouts/PopupHost.d.ts +56 -0
- package/dist/Components/Layouts/PopupHost.js +163 -0
- package/dist/Components/Layouts/PopupRules.d.ts +30 -0
- package/dist/Components/Layouts/PopupRules.js +63 -0
- package/dist/Components/Layouts/ThemePicker.d.ts +13 -0
- package/dist/Components/Layouts/ThemePicker.js +22 -0
- package/dist/Components/Layouts/ThemeScript.d.ts +14 -0
- package/dist/Components/Layouts/ThemeScript.js +13 -0
- package/dist/Components/Layouts/ThemeSwitcher.d.ts +13 -0
- package/dist/Components/Layouts/ThemeSwitcher.js +29 -0
- package/dist/Components/Layouts/Toaster.d.ts +76 -0
- package/dist/Components/Layouts/Toaster.js +172 -0
- package/dist/Components/Viewers/DataTable.d.ts +25 -0
- package/dist/Components/Viewers/DataTable.js +542 -0
- package/dist/Components/Viewers/DataTableParts.d.ts +134 -0
- package/dist/Components/Viewers/DataTableParts.js +145 -0
- package/dist/Components/Viewers/MarkdownView.d.ts +26 -0
- package/dist/Components/Viewers/MarkdownView.js +79 -0
- package/dist/Components/Viewers/RichTextView.d.ts +18 -0
- package/dist/Components/Viewers/RichTextView.js +17 -0
- package/dist/Helpers/AnimationHelpers.d.ts +6 -0
- package/dist/Helpers/AnimationHelpers.js +35 -0
- package/dist/Helpers/ApiClient.d.ts +74 -0
- package/dist/Helpers/ApiClient.js +197 -0
- package/dist/Helpers/ApiFormHelpers.d.ts +26 -0
- package/dist/Helpers/ApiFormHelpers.js +56 -0
- package/dist/Helpers/ApiModules.d.ts +135 -0
- package/dist/Helpers/ApiModules.js +194 -0
- package/dist/Helpers/ApiResponses.d.ts +41 -0
- package/dist/Helpers/ApiResponses.js +159 -0
- package/dist/Helpers/ApiRoutes.d.ts +150 -0
- package/dist/Helpers/ApiRoutes.js +191 -0
- package/dist/Helpers/ApiToasts.d.ts +21 -0
- package/dist/Helpers/ApiToasts.js +22 -0
- package/dist/Helpers/ClassHelpers.d.ts +3 -0
- package/dist/Helpers/ClassHelpers.js +4 -0
- package/dist/Helpers/CrudBackend.d.ts +56 -0
- package/dist/Helpers/CrudBackend.js +119 -0
- package/dist/Helpers/CrudHelpers.d.ts +76 -0
- package/dist/Helpers/CrudHelpers.js +202 -0
- package/dist/Helpers/DateHelpers.d.ts +88 -0
- package/dist/Helpers/DateHelpers.js +363 -0
- package/dist/Helpers/DatePreferences.d.ts +14 -0
- package/dist/Helpers/DatePreferences.js +94 -0
- package/dist/Helpers/DateTimeHelpers.d.ts +87 -0
- package/dist/Helpers/DateTimeHelpers.js +286 -0
- package/dist/Helpers/DragHelpers.d.ts +129 -0
- package/dist/Helpers/DragHelpers.js +281 -0
- package/dist/Helpers/DropdownHelpers.d.ts +56 -0
- package/dist/Helpers/DropdownHelpers.js +180 -0
- package/dist/Helpers/FormDrafts.d.ts +18 -0
- package/dist/Helpers/FormDrafts.js +76 -0
- package/dist/Helpers/FormStore.d.ts +231 -0
- package/dist/Helpers/FormStore.js +734 -0
- package/dist/Helpers/InputParamsHelpers.d.ts +79 -0
- package/dist/Helpers/InputParamsHelpers.js +388 -0
- package/dist/Helpers/MarkdownEditing.d.ts +27 -0
- package/dist/Helpers/MarkdownEditing.js +250 -0
- package/dist/Helpers/MarkdownHelpers.d.ts +82 -0
- package/dist/Helpers/MarkdownHelpers.js +775 -0
- package/dist/Helpers/MessageBuilder.d.ts +74 -0
- package/dist/Helpers/MessageBuilder.js +254 -0
- package/dist/Helpers/NepaliCalendar.d.ts +25 -0
- package/dist/Helpers/NepaliCalendar.js +107 -0
- package/dist/Helpers/NumberHelpers.d.ts +71 -0
- package/dist/Helpers/NumberHelpers.js +202 -0
- package/dist/Helpers/Permissions.d.ts +62 -0
- package/dist/Helpers/Permissions.js +133 -0
- package/dist/Helpers/PopoverHelpers.d.ts +43 -0
- package/dist/Helpers/PopoverHelpers.js +91 -0
- package/dist/Helpers/RichTextHelpers.d.ts +77 -0
- package/dist/Helpers/RichTextHelpers.js +1072 -0
- package/dist/Helpers/RowsStore.d.ts +160 -0
- package/dist/Helpers/RowsStore.js +584 -0
- package/dist/Helpers/TableColumns.d.ts +40 -0
- package/dist/Helpers/TableColumns.js +254 -0
- package/dist/Helpers/TableExport.d.ts +30 -0
- package/dist/Helpers/TableExport.js +97 -0
- package/dist/Helpers/TableHelpers.d.ts +102 -0
- package/dist/Helpers/TableHelpers.js +281 -0
- package/dist/Helpers/TableStore.d.ts +142 -0
- package/dist/Helpers/TableStore.js +530 -0
- package/dist/Helpers/ThemeHelpers.d.ts +15 -0
- package/dist/Helpers/ThemeHelpers.js +16 -0
- package/dist/Helpers/TimeHelpers.d.ts +73 -0
- package/dist/Helpers/TimeHelpers.js +253 -0
- package/dist/Helpers/TreeStore.d.ts +188 -0
- package/dist/Helpers/TreeStore.js +577 -0
- package/dist/Helpers/ValidationHelpers.d.ts +124 -0
- package/dist/Helpers/ValidationHelpers.js +301 -0
- package/dist/Interfaces/ApiInterfaces.d.ts +149 -0
- package/dist/Interfaces/ApiInterfaces.js +24 -0
- package/dist/Interfaces/CrudInterfaces.d.ts +171 -0
- package/{src/sso-client.tsx → dist/Interfaces/CrudInterfaces.js} +1 -1
- package/dist/Interfaces/DateInterfaces.d.ts +154 -0
- package/dist/Interfaces/DateInterfaces.js +29 -0
- package/dist/Interfaces/FormInterfaces.d.ts +352 -0
- package/dist/Interfaces/FormInterfaces.js +1 -0
- package/dist/Interfaces/InputInterfaces.d.ts +141 -0
- package/dist/Interfaces/InputInterfaces.js +1 -0
- package/dist/Interfaces/MessageInterfaces.d.ts +111 -0
- package/dist/Interfaces/MessageInterfaces.js +5 -0
- package/dist/Interfaces/TableInterfaces.d.ts +291 -0
- package/dist/Interfaces/TableInterfaces.js +1 -0
- package/dist/Interfaces/ThemeInterfaces.d.ts +17 -0
- package/dist/Interfaces/ThemeInterfaces.js +17 -0
- package/dist/Interfaces/TypeInterfaces.d.ts +4 -0
- package/dist/Interfaces/TypeInterfaces.js +1 -0
- package/dist/Server.Index.d.ts +2 -0
- package/dist/Server.Index.js +5 -0
- package/dist/Services/ApiProxy.d.ts +32 -0
- package/dist/Services/ApiProxy.js +111 -0
- package/dist/Services/BrowserApi.d.ts +78 -0
- package/dist/Services/BrowserApi.js +111 -0
- package/dist/Services/ServerApi.d.ts +49 -0
- package/dist/Services/ServerApi.js +75 -0
- package/dist/Services/ThemeService.d.ts +17 -0
- package/dist/Services/ThemeService.js +83 -0
- package/dist/Shared.Index.d.ts +43 -0
- package/dist/Shared.Index.js +47 -0
- package/package.json +82 -33
- package/src/Styles/Nexus.Base.css +70 -0
- package/src/Styles/Nexus.Button.css +151 -0
- package/src/Styles/Nexus.Calendar.css +275 -0
- package/src/Styles/Nexus.Choice.css +345 -0
- package/src/Styles/Nexus.Crud.css +84 -0
- package/src/Styles/Nexus.DateTime.css +81 -0
- package/src/Styles/Nexus.Dropdown.css +774 -0
- package/src/Styles/Nexus.Editor.css +297 -0
- package/src/Styles/Nexus.Form.css +298 -0
- package/src/Styles/Nexus.Index.css +34 -0
- package/src/Styles/Nexus.Input.css +637 -0
- package/src/Styles/Nexus.Markdown.css +265 -0
- package/src/Styles/Nexus.Menu.css +249 -0
- package/src/Styles/Nexus.Popup.css +731 -0
- package/src/Styles/Nexus.RichText.css +463 -0
- package/src/Styles/Nexus.Rows.css +566 -0
- package/src/Styles/Nexus.Slider.css +278 -0
- package/src/Styles/Nexus.Tab.Buttons.css +364 -0
- package/src/Styles/Nexus.Table.css +1014 -0
- package/src/Styles/Nexus.Theme.Picker.css +159 -0
- package/src/Styles/Nexus.Themes.css +194 -0
- package/src/Styles/Nexus.Time.css +147 -0
- package/src/Styles/Nexus.Toast.css +265 -0
- package/src/Styles/Nexus.Tokens.css +72 -0
- package/src/Styles/Nexus.Tree.css +435 -0
- package/src/api-services/authentication-service.tsx +0 -23
- package/src/api-services/preference-service.tsx +0 -5
- package/src/api-services/system-service.tsx +0 -34
- package/src/client.ts +0 -31
- package/src/components/documents/button.tsx +0 -142
- package/src/components/documents/icon-box.tsx +0 -93
- package/src/components/documents/page-title.tsx +0 -7
- package/src/components/documents/tab-button.tsx +0 -172
- package/src/components/documents/tag.tsx +0 -30
- package/src/components/index.js +0 -0
- package/src/components/inputs/checkbox-input.tsx +0 -66
- package/src/components/inputs/input-box.tsx +0 -48
- package/src/components/inputs/input-element.tsx +0 -65
- package/src/components/inputs/input-form.tsx +0 -164
- package/src/components/inputs/input.tsx +0 -181
- package/src/components/inputs/number-input.tsx +0 -108
- package/src/components/inputs/radiobox-input.tsx +0 -53
- package/src/components/inputs/textarea-input.tsx +0 -47
- package/src/components/inputs/textbox-input.tsx +0 -45
- package/src/components/layouts/global-dialogbox.tsx +0 -431
- package/src/components/layouts/global-layout.tsx +0 -59
- package/src/components/layouts/layout-helpers.tsx +0 -20
- package/src/components/layouts/panels/user-panel.tsx +0 -112
- package/src/components/layouts/utility-menu.tsx +0 -49
- package/src/components/panels/theme-panel.tsx +0 -46
- package/src/helpers/bitwise-helpers.tsx +0 -11
- package/src/helpers/browser-helpers.tsx +0 -156
- package/src/helpers/datasource-helpers.tsx +0 -99
- package/src/helpers/element-helpers.tsx +0 -57
- package/src/helpers/input-helpers.tsx +0 -74
- package/src/helpers/string-helpers.tsx +0 -28
- package/src/helpers/utility-helpers.tsx +0 -45
- package/src/helpers/validation-helpers.tsx +0 -302
- package/src/index.ts +0 -26
- package/src/interface.ts +0 -19
- package/src/interfaces/auth-token-interfaces.tsx +0 -6
- package/src/interfaces/browser-interfaces.tsx +0 -36
- package/src/interfaces/button-interfaces.tsx +0 -63
- package/src/interfaces/datasource-interfaces.tsx +0 -22
- package/src/interfaces/datatable-interfaces.tsx +0 -25
- package/src/interfaces/dialogbox-interfaces.tsx +0 -5
- package/src/interfaces/exception-interfaces.tsx +0 -99
- package/src/interfaces/http-interfaces.tsx +0 -129
- package/src/interfaces/icon-interfaces.tsx +0 -129
- package/src/interfaces/input-interfaces.tsx +0 -410
- package/src/interfaces/layout-interfaces.tsx +0 -191
- package/src/interfaces/menu-interfaces.tsx +0 -49
- package/src/interfaces/message-interfaces.tsx +0 -32
- package/src/interfaces/permission-interfaces.tsx +0 -9
- package/src/interfaces/storage-interfaces.tsx +0 -5
- package/src/interfaces/system-interfaces.tsx +0 -22
- package/src/interfaces/theme-interfaces.tsx +0 -123
- package/src/interfaces/type-interfaces.tsx +0 -28
- package/src/interfaces/user-interfaces.tsx +0 -47
- package/src/nexus-client.tsx +0 -21
- package/src/nexus.environments.tsx +0 -66
- package/src/proxy-api/proxy-backend.tsx +0 -60
- package/src/proxy-api/proxy-constants.tsx +0 -28
- package/src/proxy-api/proxy-helpers.tsx +0 -91
- package/src/proxy-api-client.tsx +0 -1
- package/src/proxy-api-server.tsx +0 -2
- package/src/services/http-client-services.tsx +0 -75
- package/src/services/http-services.tsx +0 -158
- package/src/services/loader-service.tsx +0 -185
- package/src/services/localstorage-service.tsx +0 -119
- package/src/services/message-services.tsx +0 -383
- package/src/services/theme-service.tsx +0 -161
- package/src/services/user-services.tsx +0 -10
- package/src/sso-config/auth-token-validation.ts +0 -16
- package/src/sso-config/callback-route.tsx +0 -35
- package/src/sso-config/cookie-encryption.ts +0 -51
- package/src/sso-config/cookie-helpers.tsx +0 -102
- package/src/sso-config/forgot-password-route.tsx +0 -36
- package/src/sso-config/oauth-callback-state.ts +0 -61
- package/src/sso-config/pkce-helpers.ts +0 -9
- package/src/sso-config/provider-complete-route.tsx +0 -18
- package/src/sso-config/redirect-context-actions.tsx +0 -45
- package/src/sso-config/redirect-context-helpers.tsx +0 -159
- package/src/sso-config/redirect-context-persist.tsx +0 -18
- package/src/sso-config/redirect-context-route.tsx +0 -45
- package/src/sso-config/refresh-route.tsx +0 -23
- package/src/sso-config/sign-in-route.tsx +0 -74
- package/src/sso-config/sign-out-route.tsx +0 -57
- package/src/sso-config/sign-up-route.tsx +0 -57
- package/src/sso-config/sso-backend-flow.ts +0 -38
- package/src/sso-config/sso-complete-route.tsx +0 -50
- package/src/sso-config/sso-config.ts +0 -59
- package/src/sso-config/sso-interfaces.tsx +0 -99
- package/src/sso-config/sso-response-helpers.tsx +0 -20
- package/src/sso-server.tsx +0 -21
- package/src/styles/nexus.animation.css +0 -269
- package/src/styles/nexus.core.css +0 -119
- package/src/styles/nexus.dialog.css +0 -144
- package/src/styles/nexus.icon.css +0 -51
- package/src/styles/nexus.input.css +0 -207
- package/src/styles/nexus.loader.css +0 -11
- package/src/styles/nexus.logic.css +0 -47
- package/src/styles/nexus.utility.css +0 -347
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { DateRange, DateRangeRules, DateRules, DateTimeRules, TimeRules } from "../Interfaces/DateInterfaces.ts";
|
|
2
|
+
import type { CheckRules, NumberRules, OptionValue, SelectionRules, TextRules } from "../Interfaces/InputInterfaces.ts";
|
|
3
|
+
import { type DateTimeFormatOptions } from "./DateTimeHelpers.ts";
|
|
4
|
+
export declare const DEFAULT_FIELD_NAME = "This field";
|
|
5
|
+
/**
|
|
6
|
+
* Common patterns, each with a message that says what to type. Use them as `pattern={TEXT_PATTERNS.email}`.
|
|
7
|
+
* Names and messages accept letters from any language.
|
|
8
|
+
*/
|
|
9
|
+
export declare const TEXT_PATTERNS: {
|
|
10
|
+
readonly email: {
|
|
11
|
+
readonly source: "[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}";
|
|
12
|
+
readonly message: "Enter an email address like name@example.com.";
|
|
13
|
+
};
|
|
14
|
+
readonly username: {
|
|
15
|
+
readonly source: "[A-Za-z0-9]+(?:[._\\-][A-Za-z0-9]+)*";
|
|
16
|
+
readonly message: "Use letters and numbers, with single dots, dashes, or underscores between them.";
|
|
17
|
+
};
|
|
18
|
+
readonly fullName: {
|
|
19
|
+
readonly source: "[\\p{L}\\p{M}]+(?:[ .'\\-]{1,2}[\\p{L}\\p{M}]+)*\\.?";
|
|
20
|
+
readonly message: "Use letters, with spaces, dots, apostrophes, or dashes between words.";
|
|
21
|
+
};
|
|
22
|
+
readonly phone: {
|
|
23
|
+
readonly source: "\\+?(?:[0-9][ \\-]?){6,14}[0-9]";
|
|
24
|
+
readonly message: "Enter a phone number of 7 to 15 digits, like +977 9812345678.";
|
|
25
|
+
};
|
|
26
|
+
readonly mobileNumber: {
|
|
27
|
+
readonly source: "[0-9]{10}";
|
|
28
|
+
readonly message: "Enter a 10-digit mobile number.";
|
|
29
|
+
};
|
|
30
|
+
readonly password: {
|
|
31
|
+
readonly source: "(?=.*\\p{Ll})(?=.*\\p{Lu})(?=.*[0-9])(?=.*[^\\p{L}0-9]).{8,64}";
|
|
32
|
+
readonly message: "Use 8 to 64 characters with an upper and a lower case letter, a number, and a symbol.";
|
|
33
|
+
};
|
|
34
|
+
readonly url: {
|
|
35
|
+
readonly source: "https?://[^\\s/$.?#][^\\s]*";
|
|
36
|
+
readonly message: "Enter a web address starting with http:// or https://.";
|
|
37
|
+
};
|
|
38
|
+
readonly digits: {
|
|
39
|
+
readonly source: "[0-9]+";
|
|
40
|
+
readonly message: "Use digits only.";
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export declare const requiredMessage: (fieldName?: string) => string;
|
|
44
|
+
export declare const minLengthMessage: (fieldName: string, min: number) => string;
|
|
45
|
+
export declare const maxLengthMessage: (fieldName: string, max: number) => string;
|
|
46
|
+
export declare const invalidMessage: (fieldName?: string) => string;
|
|
47
|
+
export declare const notNumberMessage: (fieldName?: string) => string;
|
|
48
|
+
export declare const minValueMessage: (fieldName: string, min: string) => string;
|
|
49
|
+
export declare const maxValueMessage: (fieldName: string, max: string) => string;
|
|
50
|
+
export declare const uncheckedMessage: (fieldName?: string) => string;
|
|
51
|
+
export declare const minChoicesMessage: (fieldName: string, min: number) => string;
|
|
52
|
+
export declare const invalidDateMessage: (fieldName: string | undefined, example: string) => string;
|
|
53
|
+
export declare const minDateMessage: (fieldName: string, min: string) => string;
|
|
54
|
+
export declare const maxDateMessage: (fieldName: string, max: string) => string;
|
|
55
|
+
export declare const invalidDateRangeMessage: (fieldName: string | undefined, example: string) => string;
|
|
56
|
+
export declare const missingEndMessage: (fieldName?: string) => string;
|
|
57
|
+
export declare const missingStartMessage: (fieldName?: string) => string;
|
|
58
|
+
export declare const minStartMessage: (fieldName: string, min: string) => string;
|
|
59
|
+
export declare const maxEndMessage: (fieldName: string, max: string) => string;
|
|
60
|
+
export declare const minDistanceMessage: (fieldName: string, days: number) => string;
|
|
61
|
+
export declare const maxDistanceMessage: (fieldName: string, days: number) => string;
|
|
62
|
+
export declare const invalidTimeMessage: (fieldName: string | undefined, example: string) => string;
|
|
63
|
+
export declare const minTimeMessage: (fieldName: string, min: string) => string;
|
|
64
|
+
export declare const maxTimeMessage: (fieldName: string, max: string) => string;
|
|
65
|
+
export declare const timeRangeMessage: (fieldName: string, min: string, max: string) => string;
|
|
66
|
+
export declare const timeStepMessage: (fieldName: string, step: string, before: string, after: string) => string;
|
|
67
|
+
export declare const invalidDateTimeMessage: (fieldName: string | undefined, example: string) => string;
|
|
68
|
+
export declare const minDateTimeMessage: (fieldName: string, min: string) => string;
|
|
69
|
+
export declare const maxDateTimeMessage: (fieldName: string, max: string) => string;
|
|
70
|
+
export declare const maxChoicesMessage: (fieldName: string, max: number) => string;
|
|
71
|
+
export declare const minRowsMessage: (min: number, item: string, items: string) => string;
|
|
72
|
+
export declare const maxRowsMessage: (max: number, item: string, items: string) => string;
|
|
73
|
+
/** A radio column that one row must have on: "Choose the primary address." */
|
|
74
|
+
export declare const chooseRowMessage: (fieldName: string, item: string) => string;
|
|
75
|
+
/** A value that must differ between rows: "Email is the same as in row 1." */
|
|
76
|
+
export declare const duplicateRowMessage: (fieldName: string, item: string, position: number) => string;
|
|
77
|
+
/**
|
|
78
|
+
* Checks text against its rules and returns the first message, or `null` when the value is valid.
|
|
79
|
+
* Pure and DOM-free, so a form can validate thousands of values on submit, including fields not on screen.
|
|
80
|
+
* The value is trimmed first: " " counts as empty.
|
|
81
|
+
*/
|
|
82
|
+
export declare function validateText(value: string | null | undefined, rules: TextRules, fieldName?: string): string | null;
|
|
83
|
+
/**
|
|
84
|
+
* Checks a number against its rules and returns the first message, or `null` when the value is valid.
|
|
85
|
+
* `null` means an empty field; `NaN` means text that is not a number. Pure and DOM-free, like `validateText`.
|
|
86
|
+
* `format` writes the limits in messages, e.g. `value => formatNumber(value, { grouping: true })` for "at least 1,000".
|
|
87
|
+
*/
|
|
88
|
+
export declare function validateNumber(value: number | null | undefined, rules: NumberRules, fieldName?: string, format?: (value: number) => string): string | null;
|
|
89
|
+
/**
|
|
90
|
+
* Checks a date against its rules and returns the first message, or `null` when valid. The value is an ISO date,
|
|
91
|
+
* "" or `null` for an empty field, or any other text for something typed that is not a date. Pure, like `validateText`.
|
|
92
|
+
* `format` writes dates in messages the way the user sees them, e.g. `iso => formatDate(iso, preferences)`.
|
|
93
|
+
*/
|
|
94
|
+
export declare function validateDate(value: string | null | undefined, rules: DateRules, fieldName?: string, format?: (iso: string) => string): string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Checks a date range against its rules and returns the first message, or `null` when valid. The value is a pair
|
|
97
|
+
* of ISO dates (an end not chosen is `null`), "" or `null` for an empty field, or any other text for something
|
|
98
|
+
* typed that is not a range. A range with one end only is never valid. Pure, like `validateDate`; `format` writes
|
|
99
|
+
* dates in messages the way the user sees them.
|
|
100
|
+
*/
|
|
101
|
+
export declare function validateDateRange(value: DateRange | string | null | undefined, rules: DateRangeRules, fieldName?: string, format?: (iso: string) => string): string | null;
|
|
102
|
+
/**
|
|
103
|
+
* Checks a time against its rules and returns the first message, or `null` when valid. The value is an ISO time,
|
|
104
|
+
* "" or `null` for an empty field, or any other text for something typed that is not a time. Limits are inclusive,
|
|
105
|
+
* and run past midnight when `max` is earlier than `min`; `step` counts seconds from midnight. Pure, like
|
|
106
|
+
* `validateDate`; `format` writes times in messages the way the user sees them, e.g. `iso => formatTime(iso, options)`.
|
|
107
|
+
*/
|
|
108
|
+
export declare function validateTime(value: string | null | undefined, rules: TimeRules, fieldName?: string, format?: (iso: string) => string): string | null;
|
|
109
|
+
/**
|
|
110
|
+
* Checks a date-time against its rules and returns the first message, or `null` when valid. The value is a UTC ISO
|
|
111
|
+
* date-time ("2026-09-17T08:45:00.000Z"), "" or `null` for an empty field, or any other text for something typed that
|
|
112
|
+
* is not a date-time. `min` and `max` compare moments. `minTime`, `maxTime`, and `step` check the time of day the
|
|
113
|
+
* value shows in `options.timeZone` (the browser's by default), to the precision shown: seconds count only when the
|
|
114
|
+
* step has them. Pure, like `validateTime`; `options` are the settings the field is shown with, so messages read
|
|
115
|
+
* like the box.
|
|
116
|
+
*/
|
|
117
|
+
export declare function validateDateTime(value: string | null | undefined, rules: DateTimeRules, fieldName?: string, options?: DateTimeFormatOptions): string | null;
|
|
118
|
+
/**
|
|
119
|
+
* Checks a single checkbox or switch. The field name is optional: a checkbox label is usually a sentence
|
|
120
|
+
* ("I accept the terms"), so without one the message is "Check this box to continue."
|
|
121
|
+
*/
|
|
122
|
+
export declare function validateCheck(checked: boolean, rules: CheckRules, fieldName?: string): string | null;
|
|
123
|
+
/** Checks the values chosen in a checkbox group or a radio group. Pure, like `validateText`. */
|
|
124
|
+
export declare function validateSelection<T extends OptionValue>(values: readonly T[], rules: SelectionRules<T>, fieldName?: string): string | null;
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { addDays, DATE_RANGE_SEPARATOR, diffDays, isIsoDate, todayIsoDate, toIsoDate } from "./DateHelpers.js";
|
|
2
|
+
import { formatDateTime, fromLocalDateTime, isUtcDateTime, timeScriptOf, todayIsoDateIn, toLocalDateTime, toUtcDateTime } from "./DateTimeHelpers.js";
|
|
3
|
+
import { describeTimeStep, formatTime, isIsoTime, isSecondsInRange, normalizeTimeStep, secondsToIsoTime, timeToSeconds, toIsoTime } from "./TimeHelpers.js";
|
|
4
|
+
export const DEFAULT_FIELD_NAME = "This field";
|
|
5
|
+
/**
|
|
6
|
+
* Common patterns, each with a message that says what to type. Use them as `pattern={TEXT_PATTERNS.email}`.
|
|
7
|
+
* Names and messages accept letters from any language.
|
|
8
|
+
*/
|
|
9
|
+
export const TEXT_PATTERNS = {
|
|
10
|
+
email: {
|
|
11
|
+
source: "[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}",
|
|
12
|
+
message: "Enter an email address like name@example.com.",
|
|
13
|
+
},
|
|
14
|
+
username: {
|
|
15
|
+
source: "[A-Za-z0-9]+(?:[._\\-][A-Za-z0-9]+)*",
|
|
16
|
+
message: "Use letters and numbers, with single dots, dashes, or underscores between them.",
|
|
17
|
+
},
|
|
18
|
+
fullName: {
|
|
19
|
+
source: "[\\p{L}\\p{M}]+(?:[ .'\\-]{1,2}[\\p{L}\\p{M}]+)*\\.?",
|
|
20
|
+
message: "Use letters, with spaces, dots, apostrophes, or dashes between words.",
|
|
21
|
+
},
|
|
22
|
+
phone: {
|
|
23
|
+
source: "\\+?(?:[0-9][ \\-]?){6,14}[0-9]",
|
|
24
|
+
message: "Enter a phone number of 7 to 15 digits, like +977 9812345678.",
|
|
25
|
+
},
|
|
26
|
+
mobileNumber: {
|
|
27
|
+
source: "[0-9]{10}",
|
|
28
|
+
message: "Enter a 10-digit mobile number.",
|
|
29
|
+
},
|
|
30
|
+
password: {
|
|
31
|
+
source: "(?=.*\\p{Ll})(?=.*\\p{Lu})(?=.*[0-9])(?=.*[^\\p{L}0-9]).{8,64}",
|
|
32
|
+
message: "Use 8 to 64 characters with an upper and a lower case letter, a number, and a symbol.",
|
|
33
|
+
},
|
|
34
|
+
url: {
|
|
35
|
+
source: "https?://[^\\s/$.?#][^\\s]*",
|
|
36
|
+
message: "Enter a web address starting with http:// or https://.",
|
|
37
|
+
},
|
|
38
|
+
digits: {
|
|
39
|
+
source: "[0-9]+",
|
|
40
|
+
message: "Use digits only.",
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
export const requiredMessage = (fieldName = DEFAULT_FIELD_NAME) => `${fieldName} is required.`;
|
|
44
|
+
export const minLengthMessage = (fieldName, min) => `${fieldName} must be at least ${min} characters.`;
|
|
45
|
+
export const maxLengthMessage = (fieldName, max) => `${fieldName} cannot be longer than ${max} characters.`;
|
|
46
|
+
export const invalidMessage = (fieldName = DEFAULT_FIELD_NAME) => (fieldName === DEFAULT_FIELD_NAME ? "Enter a valid value." : `${fieldName} is not valid.`);
|
|
47
|
+
export const notNumberMessage = (fieldName = DEFAULT_FIELD_NAME) => (fieldName === DEFAULT_FIELD_NAME ? "Enter a number." : `${fieldName} must be a number.`);
|
|
48
|
+
export const minValueMessage = (fieldName, min) => `${fieldName} must be at least ${min}.`;
|
|
49
|
+
export const maxValueMessage = (fieldName, max) => `${fieldName} cannot be more than ${max}.`;
|
|
50
|
+
export const uncheckedMessage = (fieldName) => (fieldName ? `${fieldName} must be checked.` : "Check this box to continue.");
|
|
51
|
+
export const minChoicesMessage = (fieldName, min) => `${fieldName} needs at least ${min} choices.`;
|
|
52
|
+
export const invalidDateMessage = (fieldName = DEFAULT_FIELD_NAME, example) => fieldName === DEFAULT_FIELD_NAME ? `Enter a date like ${example}.` : `${fieldName} must be a date like ${example}.`;
|
|
53
|
+
export const minDateMessage = (fieldName, min) => `${fieldName} must be on or after ${min}.`;
|
|
54
|
+
export const maxDateMessage = (fieldName, max) => `${fieldName} cannot be after ${max}.`;
|
|
55
|
+
export const invalidDateRangeMessage = (fieldName = DEFAULT_FIELD_NAME, example) => fieldName === DEFAULT_FIELD_NAME ? `Enter a range like ${example}.` : `${fieldName} must be a range like ${example}.`;
|
|
56
|
+
export const missingEndMessage = (fieldName = DEFAULT_FIELD_NAME) => `${fieldName} needs an end date.`;
|
|
57
|
+
export const missingStartMessage = (fieldName = DEFAULT_FIELD_NAME) => `${fieldName} needs a start date.`;
|
|
58
|
+
export const minStartMessage = (fieldName, min) => `${fieldName} must start on or after ${min}.`;
|
|
59
|
+
export const maxEndMessage = (fieldName, max) => `${fieldName} cannot end after ${max}.`;
|
|
60
|
+
export const minDistanceMessage = (fieldName, days) => `${fieldName} must be at least ${days} ${days === 1 ? "day" : "days"} long.`;
|
|
61
|
+
export const maxDistanceMessage = (fieldName, days) => `${fieldName} cannot be longer than ${days} ${days === 1 ? "day" : "days"}.`;
|
|
62
|
+
export const invalidTimeMessage = (fieldName = DEFAULT_FIELD_NAME, example) => fieldName === DEFAULT_FIELD_NAME ? `Enter a time like ${example}.` : `${fieldName} must be a time like ${example}.`;
|
|
63
|
+
export const minTimeMessage = (fieldName, min) => `${fieldName} must be ${min} or later.`;
|
|
64
|
+
export const maxTimeMessage = (fieldName, max) => `${fieldName} cannot be later than ${max}.`;
|
|
65
|
+
export const timeRangeMessage = (fieldName, min, max) => `${fieldName} must be between ${min} and ${max}.`;
|
|
66
|
+
export const timeStepMessage = (fieldName, step, before, after) => `${fieldName} must be in steps of ${step}, like ${before} or ${after}.`;
|
|
67
|
+
export const invalidDateTimeMessage = (fieldName = DEFAULT_FIELD_NAME, example) => fieldName === DEFAULT_FIELD_NAME ? `Enter a date and time like ${example}.` : `${fieldName} must be a date and time like ${example}.`;
|
|
68
|
+
export const minDateTimeMessage = (fieldName, min) => `${fieldName} must be ${min} or later.`;
|
|
69
|
+
export const maxDateTimeMessage = (fieldName, max) => `${fieldName} cannot be later than ${max}.`;
|
|
70
|
+
export const maxChoicesMessage = (fieldName, max) => `${fieldName} allows at most ${max} ${max === 1 ? "choice" : "choices"}.`;
|
|
71
|
+
/* Tabular and group forms. `item` and `items` are the row's name in lower case: "address", "addresses". */
|
|
72
|
+
export const minRowsMessage = (min, item, items) => (min <= 1 ? `Add at least one ${item}.` : `Add at least ${min} ${items}.`);
|
|
73
|
+
export const maxRowsMessage = (max, item, items) => (max <= 1 ? `Only one ${item} is allowed.` : `Only ${max} ${items} are allowed.`);
|
|
74
|
+
/** A radio column that one row must have on: "Choose the primary address." */
|
|
75
|
+
export const chooseRowMessage = (fieldName, item) => `Choose the ${fieldName} ${item}.`;
|
|
76
|
+
/** A value that must differ between rows: "Email is the same as in row 1." */
|
|
77
|
+
export const duplicateRowMessage = (fieldName, item, position) => `${fieldName} is the same as in ${item} ${position}.`;
|
|
78
|
+
// Compiled once per source: a large form validates the same few patterns thousands of times.
|
|
79
|
+
const compiledPatterns = new Map();
|
|
80
|
+
function toRegExp(pattern) {
|
|
81
|
+
if (pattern instanceof RegExp) {
|
|
82
|
+
pattern.lastIndex = 0;
|
|
83
|
+
return pattern;
|
|
84
|
+
}
|
|
85
|
+
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
86
|
+
let compiled = compiledPatterns.get(source);
|
|
87
|
+
if (!compiled) {
|
|
88
|
+
// Anchored like the HTML pattern attribute; the "u" flag lets patterns use \p{L} for letters in any language.
|
|
89
|
+
compiled = new RegExp(`^(?:${source})$`, "u");
|
|
90
|
+
compiledPatterns.set(source, compiled);
|
|
91
|
+
}
|
|
92
|
+
return compiled;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Checks text against its rules and returns the first message, or `null` when the value is valid.
|
|
96
|
+
* Pure and DOM-free, so a form can validate thousands of values on submit, including fields not on screen.
|
|
97
|
+
* The value is trimmed first: " " counts as empty.
|
|
98
|
+
*/
|
|
99
|
+
export function validateText(value, rules, fieldName = DEFAULT_FIELD_NAME) {
|
|
100
|
+
const text = (value ?? "").trim();
|
|
101
|
+
if (text) {
|
|
102
|
+
const { minLength, maxLength, pattern } = rules;
|
|
103
|
+
if (minLength && text.length < minLength)
|
|
104
|
+
return minLengthMessage(fieldName, minLength);
|
|
105
|
+
if (maxLength && text.length > maxLength)
|
|
106
|
+
return maxLengthMessage(fieldName, maxLength);
|
|
107
|
+
if (pattern && !toRegExp(pattern).test(text)) {
|
|
108
|
+
return typeof pattern === "object" && !(pattern instanceof RegExp) ? pattern.message : (rules.patternMessage ?? invalidMessage(fieldName));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (rules.required) {
|
|
112
|
+
return requiredMessage(fieldName);
|
|
113
|
+
}
|
|
114
|
+
return rules.validate?.(text) || null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Checks a number against its rules and returns the first message, or `null` when the value is valid.
|
|
118
|
+
* `null` means an empty field; `NaN` means text that is not a number. Pure and DOM-free, like `validateText`.
|
|
119
|
+
* `format` writes the limits in messages, e.g. `value => formatNumber(value, { grouping: true })` for "at least 1,000".
|
|
120
|
+
*/
|
|
121
|
+
export function validateNumber(value, rules, fieldName = DEFAULT_FIELD_NAME, format = String) {
|
|
122
|
+
if (value === null || value === undefined) {
|
|
123
|
+
if (rules.required)
|
|
124
|
+
return requiredMessage(fieldName);
|
|
125
|
+
}
|
|
126
|
+
else if (Number.isNaN(value)) {
|
|
127
|
+
return notNumberMessage(fieldName);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
if (rules.min !== undefined && value < rules.min)
|
|
131
|
+
return minValueMessage(fieldName, format(rules.min));
|
|
132
|
+
if (rules.max !== undefined && value > rules.max)
|
|
133
|
+
return maxValueMessage(fieldName, format(rules.max));
|
|
134
|
+
}
|
|
135
|
+
return rules.validate?.(value ?? null) || null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Checks a date against its rules and returns the first message, or `null` when valid. The value is an ISO date,
|
|
139
|
+
* "" or `null` for an empty field, or any other text for something typed that is not a date. Pure, like `validateText`.
|
|
140
|
+
* `format` writes dates in messages the way the user sees them, e.g. `iso => formatDate(iso, preferences)`.
|
|
141
|
+
*/
|
|
142
|
+
export function validateDate(value, rules, fieldName = DEFAULT_FIELD_NAME, format = iso => iso) {
|
|
143
|
+
if (!value) {
|
|
144
|
+
if (rules.required)
|
|
145
|
+
return requiredMessage(fieldName);
|
|
146
|
+
}
|
|
147
|
+
else if (!isIsoDate(value)) {
|
|
148
|
+
return invalidDateMessage(fieldName, format(todayIsoDate()));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const min = toIsoDate(rules.min);
|
|
152
|
+
if (min && value < min)
|
|
153
|
+
return minDateMessage(fieldName, format(min));
|
|
154
|
+
const max = toIsoDate(rules.max);
|
|
155
|
+
if (max && value > max)
|
|
156
|
+
return maxDateMessage(fieldName, format(max));
|
|
157
|
+
}
|
|
158
|
+
return rules.validate?.(value || null) || null;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Checks a date range against its rules and returns the first message, or `null` when valid. The value is a pair
|
|
162
|
+
* of ISO dates (an end not chosen is `null`), "" or `null` for an empty field, or any other text for something
|
|
163
|
+
* typed that is not a range. A range with one end only is never valid. Pure, like `validateDate`; `format` writes
|
|
164
|
+
* dates in messages the way the user sees them.
|
|
165
|
+
*/
|
|
166
|
+
export function validateDateRange(value, rules, fieldName = DEFAULT_FIELD_NAME, format = iso => iso) {
|
|
167
|
+
if (typeof value === "string" && value) {
|
|
168
|
+
const today = todayIsoDate();
|
|
169
|
+
return invalidDateRangeMessage(fieldName, format(today) + DATE_RANGE_SEPARATOR + format(addDays(today, 3)));
|
|
170
|
+
}
|
|
171
|
+
const [start, end] = value && typeof value !== "string" ? value : [null, null];
|
|
172
|
+
if (!start && !end) {
|
|
173
|
+
if (rules.required)
|
|
174
|
+
return requiredMessage(fieldName);
|
|
175
|
+
}
|
|
176
|
+
else if (!end) {
|
|
177
|
+
return missingEndMessage(fieldName);
|
|
178
|
+
}
|
|
179
|
+
else if (!start) {
|
|
180
|
+
return missingStartMessage(fieldName);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const min = toIsoDate(rules.min);
|
|
184
|
+
if (min && start < min)
|
|
185
|
+
return minStartMessage(fieldName, format(min));
|
|
186
|
+
const max = toIsoDate(rules.max);
|
|
187
|
+
if (max && end > max)
|
|
188
|
+
return maxEndMessage(fieldName, format(max));
|
|
189
|
+
const distance = diffDays(start, end);
|
|
190
|
+
if (rules.minDistance !== undefined && distance < rules.minDistance)
|
|
191
|
+
return minDistanceMessage(fieldName, rules.minDistance);
|
|
192
|
+
if (rules.maxDistance !== undefined && distance > rules.maxDistance)
|
|
193
|
+
return maxDistanceMessage(fieldName, rules.maxDistance);
|
|
194
|
+
}
|
|
195
|
+
return rules.validate?.([start, end]) || null;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Checks a time against its rules and returns the first message, or `null` when valid. The value is an ISO time,
|
|
199
|
+
* "" or `null` for an empty field, or any other text for something typed that is not a time. Limits are inclusive,
|
|
200
|
+
* and run past midnight when `max` is earlier than `min`; `step` counts seconds from midnight. Pure, like
|
|
201
|
+
* `validateDate`; `format` writes times in messages the way the user sees them, e.g. `iso => formatTime(iso, options)`.
|
|
202
|
+
*/
|
|
203
|
+
export function validateTime(value, rules, fieldName = DEFAULT_FIELD_NAME, format = iso => iso) {
|
|
204
|
+
if (!value) {
|
|
205
|
+
if (rules.required)
|
|
206
|
+
return requiredMessage(fieldName);
|
|
207
|
+
}
|
|
208
|
+
else if (!isIsoTime(value)) {
|
|
209
|
+
return invalidTimeMessage(fieldName, format("14:30"));
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
const time = timeToSeconds(value);
|
|
213
|
+
const step = normalizeTimeStep(rules.step);
|
|
214
|
+
// Times in messages have seconds only when the step or the time itself does.
|
|
215
|
+
const write = (seconds) => format(secondsToIsoTime(seconds, step % 60 !== 0 || seconds % 60 !== 0));
|
|
216
|
+
const min = toIsoTime(rules.min, true);
|
|
217
|
+
const max = toIsoTime(rules.max, true);
|
|
218
|
+
const from = min === null ? null : timeToSeconds(min);
|
|
219
|
+
const to = max === null ? null : timeToSeconds(max);
|
|
220
|
+
if (!isSecondsInRange(time, from, to)) {
|
|
221
|
+
if (from !== null && to !== null)
|
|
222
|
+
return timeRangeMessage(fieldName, write(from), write(to));
|
|
223
|
+
return from !== null ? minTimeMessage(fieldName, write(from)) : maxTimeMessage(fieldName, write(to));
|
|
224
|
+
}
|
|
225
|
+
if (time % step !== 0) {
|
|
226
|
+
const before = Math.floor(time / step) * step;
|
|
227
|
+
return timeStepMessage(fieldName, describeTimeStep(step), write(before), write(before + step));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return rules.validate?.(value || null) || null;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Checks a date-time against its rules and returns the first message, or `null` when valid. The value is a UTC ISO
|
|
234
|
+
* date-time ("2026-09-17T08:45:00.000Z"), "" or `null` for an empty field, or any other text for something typed that
|
|
235
|
+
* is not a date-time. `min` and `max` compare moments. `minTime`, `maxTime`, and `step` check the time of day the
|
|
236
|
+
* value shows in `options.timeZone` (the browser's by default), to the precision shown: seconds count only when the
|
|
237
|
+
* step has them. Pure, like `validateTime`; `options` are the settings the field is shown with, so messages read
|
|
238
|
+
* like the box.
|
|
239
|
+
*/
|
|
240
|
+
export function validateDateTime(value, rules, fieldName = DEFAULT_FIELD_NAME, options = {}) {
|
|
241
|
+
const step = normalizeTimeStep(rules.step);
|
|
242
|
+
const seconds = step % 60 !== 0;
|
|
243
|
+
const shown = { ...options, seconds };
|
|
244
|
+
const writeTime = (time) => formatTime(secondsToIsoTime(time, true), { format: options.timeFormat, script: timeScriptOf(options), seconds });
|
|
245
|
+
if (!value) {
|
|
246
|
+
if (rules.required)
|
|
247
|
+
return requiredMessage(fieldName);
|
|
248
|
+
}
|
|
249
|
+
else if (!isUtcDateTime(value)) {
|
|
250
|
+
const example = fromLocalDateTime(todayIsoDateIn(options.timeZone), "14:30", options.timeZone);
|
|
251
|
+
return invalidDateTimeMessage(fieldName, formatDateTime(example, shown));
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const min = toUtcDateTime(rules.min, options.timeZone);
|
|
255
|
+
if (min && value < min)
|
|
256
|
+
return minDateTimeMessage(fieldName, formatDateTime(min, shown));
|
|
257
|
+
const max = toUtcDateTime(rules.max, options.timeZone);
|
|
258
|
+
if (max && value > max)
|
|
259
|
+
return maxDateTimeMessage(fieldName, formatDateTime(max, shown));
|
|
260
|
+
const total = timeToSeconds(toLocalDateTime(value, options.timeZone).time);
|
|
261
|
+
const time = seconds ? total : total - (total % 60);
|
|
262
|
+
const minTime = toIsoTime(rules.minTime, true);
|
|
263
|
+
const maxTime = toIsoTime(rules.maxTime, true);
|
|
264
|
+
const from = minTime === null ? null : timeToSeconds(minTime);
|
|
265
|
+
const to = maxTime === null ? null : timeToSeconds(maxTime);
|
|
266
|
+
if (!isSecondsInRange(time, from, to)) {
|
|
267
|
+
if (from !== null && to !== null)
|
|
268
|
+
return timeRangeMessage(fieldName, writeTime(from), writeTime(to));
|
|
269
|
+
return from !== null ? minTimeMessage(fieldName, writeTime(from)) : maxTimeMessage(fieldName, writeTime(to));
|
|
270
|
+
}
|
|
271
|
+
if (time % step !== 0) {
|
|
272
|
+
const before = Math.floor(time / step) * step;
|
|
273
|
+
return timeStepMessage(fieldName, describeTimeStep(step), writeTime(before), writeTime(before + step));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return rules.validate?.(value || null) || null;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Checks a single checkbox or switch. The field name is optional: a checkbox label is usually a sentence
|
|
280
|
+
* ("I accept the terms"), so without one the message is "Check this box to continue."
|
|
281
|
+
*/
|
|
282
|
+
export function validateCheck(checked, rules, fieldName) {
|
|
283
|
+
if (rules.required && !checked)
|
|
284
|
+
return uncheckedMessage(fieldName);
|
|
285
|
+
return rules.validate?.(checked) || null;
|
|
286
|
+
}
|
|
287
|
+
/** Checks the values chosen in a checkbox group or a radio group. Pure, like `validateText`. */
|
|
288
|
+
export function validateSelection(values, rules, fieldName = DEFAULT_FIELD_NAME) {
|
|
289
|
+
const count = values.length;
|
|
290
|
+
if (count === 0) {
|
|
291
|
+
if (rules.required)
|
|
292
|
+
return requiredMessage(fieldName);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
if (rules.min !== undefined && count < rules.min)
|
|
296
|
+
return minChoicesMessage(fieldName, rules.min);
|
|
297
|
+
if (rules.max !== undefined && count > rules.max)
|
|
298
|
+
return maxChoicesMessage(fieldName, rules.max);
|
|
299
|
+
}
|
|
300
|
+
return rules.validate?.([...values]) || null;
|
|
301
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { MessageAction } from "./MessageInterfaces.ts";
|
|
2
|
+
/** A module's value in an app's own enum: a number (`NexusModule.Identity`) or a string (`"hr"`). */
|
|
3
|
+
export type ApiModuleValue = string | number;
|
|
4
|
+
/**
|
|
5
|
+
* The Nexus backend modules, the modules of `NexusApiModules`. The values are the backend's `EModules` flags, so they
|
|
6
|
+
* can be combined and compared with permissions. Another backend has an enum of its own and its own `ApiModules` class.
|
|
7
|
+
*/
|
|
8
|
+
export declare const NexusModule: {
|
|
9
|
+
/** The app's own API routes (`/api/...`), not a backend module. */
|
|
10
|
+
readonly None: 0;
|
|
11
|
+
readonly Migrations: number;
|
|
12
|
+
readonly Configuration: number;
|
|
13
|
+
readonly Identity: number;
|
|
14
|
+
readonly Enterprise: number;
|
|
15
|
+
readonly Drive: number;
|
|
16
|
+
readonly Message: number;
|
|
17
|
+
readonly Accounting: number;
|
|
18
|
+
readonly Report: number;
|
|
19
|
+
readonly Requisition: number;
|
|
20
|
+
readonly Tenant: number;
|
|
21
|
+
readonly Subscription: number;
|
|
22
|
+
readonly Reservation: number;
|
|
23
|
+
readonly School: number;
|
|
24
|
+
readonly Pharmacy: number;
|
|
25
|
+
readonly Staffing: number;
|
|
26
|
+
readonly Portfolio: number;
|
|
27
|
+
};
|
|
28
|
+
export type NexusModule = (typeof NexusModule)[keyof typeof NexusModule];
|
|
29
|
+
/**
|
|
30
|
+
* Where an app names its module enum for the types, once, next to its `defineApiModules`:
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* export const MODULES = defineApiModules(new HrApiModules());
|
|
34
|
+
* declare module "nexus-shared" {
|
|
35
|
+
* interface ApiRegister { module: HrModule }
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* Every endpoint, call, API form, and CRUD then takes only those modules. Without it, the Nexus modules.
|
|
39
|
+
*/
|
|
40
|
+
export interface ApiRegister {
|
|
41
|
+
}
|
|
42
|
+
/** The app's module type: the one named in `ApiRegister`, else `NexusModule`. */
|
|
43
|
+
export type AppModule = ApiRegister extends {
|
|
44
|
+
module: infer TModule extends ApiModuleValue;
|
|
45
|
+
} ? TModule : NexusModule;
|
|
46
|
+
/** One module of an app's API: its value, and how it is named. */
|
|
47
|
+
export interface ApiModuleInfo<TModule extends ApiModuleValue = ApiModuleValue> {
|
|
48
|
+
module: TModule;
|
|
49
|
+
/** Its name in proxy URLs: `/api/app/reservation/Room/NormalListing`. Letters, digits, dots, dashes, and underscores. */
|
|
50
|
+
key: string;
|
|
51
|
+
/** Its name in messages: "The Reservation API address is not set." */
|
|
52
|
+
label: string;
|
|
53
|
+
/** Where its root URL comes from, such as the environment variable `API_RESERVATION`. Named in messages when it is missing. */
|
|
54
|
+
env?: string;
|
|
55
|
+
}
|
|
56
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
57
|
+
/** `json` sends the body as JSON; `form-data` as fields (files stay files, lists and objects go as JSON text). */
|
|
58
|
+
export type ApiBodyType = "json" | "form-data";
|
|
59
|
+
/** Query values: lists repeat their name, dates go as ISO text, and `null` or `undefined` values are left out. */
|
|
60
|
+
export type ApiQuery = Record<string, string | number | boolean | Date | null | undefined | readonly (string | number | boolean)[]>;
|
|
61
|
+
/**
|
|
62
|
+
* An API: which module serves it, its path under the module's root, and its method. Build them with `apiEndpoint` or
|
|
63
|
+
* `apiController`, once per app, and call them with `api` in the browser or `serverApi` on the server. The module is
|
|
64
|
+
* one of the app's (`AppModule`); its root, through the proxy or not, comes from the app's `ApiModules`.
|
|
65
|
+
*/
|
|
66
|
+
export interface ApiEndpoint<TModule extends ApiModuleValue = AppModule> {
|
|
67
|
+
module: TModule;
|
|
68
|
+
/** Under the module's root, such as "Room/Detail/{id}". A `{name}` is filled from the call's `params`, then its body. */
|
|
69
|
+
path: string;
|
|
70
|
+
method: HttpMethod;
|
|
71
|
+
/** Default `json`. */
|
|
72
|
+
bodyType?: ApiBodyType;
|
|
73
|
+
/** Query values sent with every call; a call's own `query` adds to them. */
|
|
74
|
+
query?: ApiQuery;
|
|
75
|
+
/** What the call does, for its messages. Default: by method (`load` for GET, `delete` for DELETE, else `save`). */
|
|
76
|
+
action?: MessageAction;
|
|
77
|
+
/** What it acts on, for its messages: "room". A call's `subject` ("Room 204") replaces it. */
|
|
78
|
+
subject?: string;
|
|
79
|
+
}
|
|
80
|
+
/** Where a call goes: an endpoint, or a URL ("/api/demo-rooms", "https://…"). */
|
|
81
|
+
export type ApiTarget = ApiEndpoint | string;
|
|
82
|
+
/** A server's answer, read as success or failure. Every call answers with one; it never throws for a failed request. */
|
|
83
|
+
export interface ApiResult<T = unknown> {
|
|
84
|
+
ok: boolean;
|
|
85
|
+
/** The HTTP status, when known. */
|
|
86
|
+
status?: number;
|
|
87
|
+
/** What the server sent back: the record, the list, the saved id. */
|
|
88
|
+
result?: T;
|
|
89
|
+
/** The server's message, or one built for the failure. */
|
|
90
|
+
message?: string;
|
|
91
|
+
/** The server's code for its message, such as "ROOM_TAKEN" (Nexus `messageCode`). */
|
|
92
|
+
messageCode?: string;
|
|
93
|
+
/** The server's error number (Nexus `errorCode`, such as 2003 for an expired token). */
|
|
94
|
+
errorCode?: number;
|
|
95
|
+
/** Messages by field name, as the server names them ("Email", "addresses[1].city"); matched to inputs without case. */
|
|
96
|
+
errors?: Record<string, string>;
|
|
97
|
+
/** Nothing reached the server: offline, or the server is down. */
|
|
98
|
+
network?: boolean;
|
|
99
|
+
/** The server did not answer within the call's `timeout`. */
|
|
100
|
+
timeout?: boolean;
|
|
101
|
+
/** The caller stopped the call through its `signal`. Nothing is shown for it. */
|
|
102
|
+
aborted?: boolean;
|
|
103
|
+
}
|
|
104
|
+
/** A page of a list, as Nexus pagination APIs answer. */
|
|
105
|
+
export interface ApiPage<T = unknown> {
|
|
106
|
+
results: T[];
|
|
107
|
+
/** Every row the user may see. */
|
|
108
|
+
totalCount: number;
|
|
109
|
+
/** The rows that match the search and filters. */
|
|
110
|
+
filterCount: number;
|
|
111
|
+
}
|
|
112
|
+
/** What Nexus pagination APIs take. */
|
|
113
|
+
export interface ApiPageRequest {
|
|
114
|
+
searchKeyword?: string;
|
|
115
|
+
skipCount?: number;
|
|
116
|
+
maxCount?: number;
|
|
117
|
+
sorting?: string;
|
|
118
|
+
sortType?: "asc" | "desc";
|
|
119
|
+
}
|
|
120
|
+
/** Options of one call, on either side. */
|
|
121
|
+
export interface ApiCallOptions {
|
|
122
|
+
/** What is sent. GET sends no body. */
|
|
123
|
+
body?: unknown;
|
|
124
|
+
/** Fills `{name}` in the endpoint's path. Missing names are taken from the body. */
|
|
125
|
+
params?: Record<string, string | number | null | undefined>;
|
|
126
|
+
/** Query values, added to the endpoint's own. */
|
|
127
|
+
query?: ApiQuery;
|
|
128
|
+
/** Replaces the endpoint's method. */
|
|
129
|
+
method?: HttpMethod;
|
|
130
|
+
headers?: Record<string, string>;
|
|
131
|
+
bodyType?: ApiBodyType;
|
|
132
|
+
/** Stops the call. The answer is then `{ ok: false, aborted: true }`, and nothing is shown. */
|
|
133
|
+
signal?: AbortSignal;
|
|
134
|
+
/** Milliseconds to wait for an answer. Default: the client's, 60,000; 0 waits as long as the browser does. */
|
|
135
|
+
timeout?: number;
|
|
136
|
+
/** Tries again after a network failure or a 502, 503, or 504. Default 1 for GET, 0 for the rest (a save must not run twice). */
|
|
137
|
+
retries?: number;
|
|
138
|
+
credentials?: RequestCredentials;
|
|
139
|
+
/** Reads the answer. Default `readApiResponse`: Nexus answers, ASP.NET Core problem details, any JSON by its status. */
|
|
140
|
+
read?: (body: unknown, status: number | undefined) => ApiResult;
|
|
141
|
+
/** What the call does, for its messages. Default: the endpoint's, or by method. */
|
|
142
|
+
action?: MessageAction;
|
|
143
|
+
/** What it acts on, for its messages: "Room 204". Default: the endpoint's. */
|
|
144
|
+
subject?: string;
|
|
145
|
+
/** How many it acts on: 3 → "3 rooms deleted." */
|
|
146
|
+
count?: number;
|
|
147
|
+
/** The plural of `subject` with a `count`, when adding "s" is wrong: "people". */
|
|
148
|
+
plural?: string;
|
|
149
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Nexus backend modules, the modules of `NexusApiModules`. The values are the backend's `EModules` flags, so they
|
|
3
|
+
* can be combined and compared with permissions. Another backend has an enum of its own and its own `ApiModules` class.
|
|
4
|
+
*/
|
|
5
|
+
export const NexusModule = {
|
|
6
|
+
/** The app's own API routes (`/api/...`), not a backend module. */
|
|
7
|
+
None: 0,
|
|
8
|
+
Migrations: 1 << 0,
|
|
9
|
+
Configuration: 1 << 1,
|
|
10
|
+
Identity: 1 << 2,
|
|
11
|
+
Enterprise: 1 << 3,
|
|
12
|
+
Drive: 1 << 4,
|
|
13
|
+
Message: 1 << 5,
|
|
14
|
+
Accounting: 1 << 6,
|
|
15
|
+
Report: 1 << 7,
|
|
16
|
+
Requisition: 1 << 8,
|
|
17
|
+
Tenant: 1 << 9,
|
|
18
|
+
Subscription: 1 << 10,
|
|
19
|
+
Reservation: 1 << 11,
|
|
20
|
+
School: 1 << 12,
|
|
21
|
+
Pharmacy: 1 << 13,
|
|
22
|
+
Staffing: 1 << 14,
|
|
23
|
+
Portfolio: 1 << 15,
|
|
24
|
+
};
|