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,281 @@
|
|
|
1
|
+
import { formatDate } from "./DateHelpers.js";
|
|
2
|
+
import { formatDateTime } from "./DateTimeHelpers.js";
|
|
3
|
+
import { foldSearchText, searchWords } from "./DropdownHelpers.js";
|
|
4
|
+
import { formatNumber } from "./NumberHelpers.js";
|
|
5
|
+
import { formatTime } from "./TimeHelpers.js";
|
|
6
|
+
/*
|
|
7
|
+
* Data table helpers, with no React: reading a column's value, its text, searching, sorting, and paging rows, and the
|
|
8
|
+
* page buttons. The table and a server-side test use the same functions.
|
|
9
|
+
*/
|
|
10
|
+
/** Rows per page when nothing says otherwise. */
|
|
11
|
+
export const DEFAULT_PAGE_SIZE = 10;
|
|
12
|
+
/** Choices of rows per page. */
|
|
13
|
+
export const DEFAULT_PAGE_SIZES = [10, 25, 50, 100];
|
|
14
|
+
/** Most rows a table lets the user select, as the old data table did. */
|
|
15
|
+
export const DEFAULT_MAX_SELECTION = 500;
|
|
16
|
+
/** Reads a field, or a path into the row: `readPath(row, "guest.name")`. */
|
|
17
|
+
export function readPath(row, path) {
|
|
18
|
+
if (row === null || typeof row !== "object")
|
|
19
|
+
return undefined;
|
|
20
|
+
if (path in row)
|
|
21
|
+
return row[path];
|
|
22
|
+
let current = row;
|
|
23
|
+
for (const part of path.split(".")) {
|
|
24
|
+
if (current === null || typeof current !== "object")
|
|
25
|
+
return undefined;
|
|
26
|
+
current = current[part];
|
|
27
|
+
}
|
|
28
|
+
return current;
|
|
29
|
+
}
|
|
30
|
+
/** A row's key as text, so 5 and "5" are the same row. */
|
|
31
|
+
export function rowKeyText(row, rowKey) {
|
|
32
|
+
const key = typeof rowKey === "function" ? rowKey(row) : readPath(row, rowKey);
|
|
33
|
+
return key === undefined || key === null ? "" : String(key);
|
|
34
|
+
}
|
|
35
|
+
/** A column's value in a row. */
|
|
36
|
+
export function columnValue(column, row) {
|
|
37
|
+
return column.value ? column.value(row) : readPath(row, column.key);
|
|
38
|
+
}
|
|
39
|
+
/** The option a value stands for, if the column has options. Values compare as text, so 2 matches "2". */
|
|
40
|
+
export function columnOption(column, value) {
|
|
41
|
+
if (!column.options || value === undefined || value === null)
|
|
42
|
+
return undefined;
|
|
43
|
+
return column.options.find(option => option.value === value || String(option.value) === String(value));
|
|
44
|
+
}
|
|
45
|
+
const isEmpty = (value) => value === undefined || value === null || value === "" || (typeof value === "number" && Number.isNaN(value));
|
|
46
|
+
/** Whether a column lines its values up at the end, as numbers do. */
|
|
47
|
+
export function isNumericColumn(column) {
|
|
48
|
+
return column.format === "number" || column.format === "money" || column.format === "percent";
|
|
49
|
+
}
|
|
50
|
+
/** Where a column's values sit in their cells. */
|
|
51
|
+
export function columnAlign(column) {
|
|
52
|
+
if (column.align)
|
|
53
|
+
return column.align;
|
|
54
|
+
if (isNumericColumn(column))
|
|
55
|
+
return "end";
|
|
56
|
+
return column.format === "boolean" ? "center" : "start";
|
|
57
|
+
}
|
|
58
|
+
function toNumber(value) {
|
|
59
|
+
if (typeof value === "number")
|
|
60
|
+
return Number.isFinite(value) ? value : null;
|
|
61
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
62
|
+
const number = Number(value);
|
|
63
|
+
return Number.isFinite(number) ? number : null;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A value as the cell writes it, in the column's format: "1,250.00", "Rs. 4,500.00", "12.5%", the date and time in the
|
|
69
|
+
* user's settings, "Yes" or "No", an option's label. Empty values give "".
|
|
70
|
+
*/
|
|
71
|
+
export function formatCellText(column, value, options = {}) {
|
|
72
|
+
const option = columnOption(column, value);
|
|
73
|
+
if (option)
|
|
74
|
+
return option.label;
|
|
75
|
+
if (isEmpty(value))
|
|
76
|
+
return "";
|
|
77
|
+
const { prefix = "", suffix = "" } = column;
|
|
78
|
+
switch (column.format) {
|
|
79
|
+
case "number":
|
|
80
|
+
case "money":
|
|
81
|
+
case "percent": {
|
|
82
|
+
const number = toNumber(value);
|
|
83
|
+
if (number === null)
|
|
84
|
+
return String(value);
|
|
85
|
+
const decimals = column.decimals ?? (column.format === "money" ? 2 : undefined);
|
|
86
|
+
const text = formatNumber(number, { decimals, fixedDecimals: column.format === "money" || column.decimals !== undefined, grouping: true });
|
|
87
|
+
return `${prefix}${text}${column.format === "percent" && !suffix ? "%" : suffix}`;
|
|
88
|
+
}
|
|
89
|
+
case "date":
|
|
90
|
+
return formatDate(value, options.preferences) || String(value);
|
|
91
|
+
case "time":
|
|
92
|
+
return formatTime(value, { format: options.preferences?.timeFormat }) || String(value);
|
|
93
|
+
case "date-time":
|
|
94
|
+
return formatDateTime(value, { ...options.preferences, timeZone: options.timeZone }) || String(value);
|
|
95
|
+
case "boolean":
|
|
96
|
+
return value === true || value === "true" || value === 1 ? "Yes" : "No";
|
|
97
|
+
default:
|
|
98
|
+
return `${prefix}${typeof value === "object" ? JSON.stringify(value) : String(value)}${suffix}`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** Text the search looks in: the column's `text`, an option's label, or the value as text (dates as ISO text). */
|
|
102
|
+
export function searchTextOf(column, row) {
|
|
103
|
+
if (column.text)
|
|
104
|
+
return column.text(row);
|
|
105
|
+
const value = columnValue(column, row);
|
|
106
|
+
const option = columnOption(column, value);
|
|
107
|
+
if (option)
|
|
108
|
+
return option.label;
|
|
109
|
+
if (isEmpty(value))
|
|
110
|
+
return "";
|
|
111
|
+
if (column.format === "boolean")
|
|
112
|
+
return formatCellText(column, value);
|
|
113
|
+
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
114
|
+
}
|
|
115
|
+
// Between the texts of two columns, so a search word never matches across them (words have no line breaks).
|
|
116
|
+
const COLUMN_BREAK = String.fromCharCode(10);
|
|
117
|
+
const collator = new Intl.Collator("en", { numeric: true, sensitivity: "base" });
|
|
118
|
+
/** Compares two values of a column for sorting: numbers as numbers, text as a person reads it ("Room 9" before "Room 10"). */
|
|
119
|
+
export function compareCellValues(a, b) {
|
|
120
|
+
const aEmpty = isEmpty(a);
|
|
121
|
+
const bEmpty = isEmpty(b);
|
|
122
|
+
if (aEmpty || bEmpty)
|
|
123
|
+
return aEmpty === bEmpty ? 0 : aEmpty ? 1 : -1;
|
|
124
|
+
if (typeof a === "number" && typeof b === "number")
|
|
125
|
+
return a - b;
|
|
126
|
+
if (typeof a === "boolean" && typeof b === "boolean")
|
|
127
|
+
return a === b ? 0 : a ? -1 : 1;
|
|
128
|
+
return collator.compare(String(a), String(b));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Sorts rows by a column, keeping the order of equal rows. Empty values go last in both directions, so the rows with
|
|
132
|
+
* values stay on the first pages.
|
|
133
|
+
*/
|
|
134
|
+
export function sortRows(rows, column, direction) {
|
|
135
|
+
const sign = direction === "desc" ? -1 : 1;
|
|
136
|
+
const option = (value) => columnOption(column, value)?.label ?? value;
|
|
137
|
+
const keyed = rows.map((row, index) => ({ row, index, value: column.options ? option(columnValue(column, row)) : columnValue(column, row) }));
|
|
138
|
+
keyed.sort((x, y) => {
|
|
139
|
+
const xEmpty = isEmpty(x.value);
|
|
140
|
+
const yEmpty = isEmpty(y.value);
|
|
141
|
+
if (xEmpty || yEmpty)
|
|
142
|
+
return xEmpty === yEmpty ? x.index - y.index : xEmpty ? 1 : -1;
|
|
143
|
+
return compareCellValues(x.value, y.value) * sign || x.index - y.index;
|
|
144
|
+
});
|
|
145
|
+
return keyed.map(entry => entry.row);
|
|
146
|
+
}
|
|
147
|
+
/** How many pages `count` rows take. At least 1. */
|
|
148
|
+
export function pageCount(count, pageSize) {
|
|
149
|
+
return Math.max(1, Math.ceil(count / Math.max(1, pageSize)));
|
|
150
|
+
}
|
|
151
|
+
/** A page number within the pages there are. */
|
|
152
|
+
export function clampPage(page, count, pageSize) {
|
|
153
|
+
return Math.min(Math.max(1, Math.floor(page) || 1), pageCount(count, pageSize));
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The page buttons: the first and last pages, the pages around the current one, and `"gap"` where pages are left out.
|
|
157
|
+
* `pageButtons(6, 24)` gives [1, "gap", 5, 6, 7, "gap", 24]. The list is 7 long at most, so the pager keeps its width.
|
|
158
|
+
*/
|
|
159
|
+
export function pageButtons(page, pages) {
|
|
160
|
+
if (pages <= 7)
|
|
161
|
+
return Array.from({ length: pages }, (_, index) => index + 1);
|
|
162
|
+
if (page <= 4)
|
|
163
|
+
return [1, 2, 3, 4, 5, "gap", pages];
|
|
164
|
+
if (page >= pages - 3)
|
|
165
|
+
return [1, "gap", pages - 4, pages - 3, pages - 2, pages - 1, pages];
|
|
166
|
+
return [1, "gap", page - 1, page, page + 1, "gap", pages];
|
|
167
|
+
}
|
|
168
|
+
/** Reads what a loader answered: a page, or every row. */
|
|
169
|
+
export function readTablePage(answer) {
|
|
170
|
+
if (Array.isArray(answer))
|
|
171
|
+
return { rows: [...answer], total: answer.length, matches: answer.length };
|
|
172
|
+
const page = answer;
|
|
173
|
+
const rows = [...(page?.rows ?? [])];
|
|
174
|
+
const total = Number.isFinite(page?.total) ? Number(page.total) : rows.length;
|
|
175
|
+
const matches = Number.isFinite(page?.matches) ? Number(page.matches) : total;
|
|
176
|
+
return { rows, total, matches };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Searches, sorts, and pages rows the way the table shows them. `texts` caches each row's folded search text, so a
|
|
180
|
+
* search of 10,000 rows looks at each row once per keystroke.
|
|
181
|
+
*/
|
|
182
|
+
export function queryRows(rows, columns, query, texts) {
|
|
183
|
+
let list = rows;
|
|
184
|
+
const words = searchWords(query.search);
|
|
185
|
+
if (words.length) {
|
|
186
|
+
const searched = columns.filter(column => column.searchable !== false);
|
|
187
|
+
list = list.filter(row => {
|
|
188
|
+
let text = texts?.get(row);
|
|
189
|
+
if (text === undefined) {
|
|
190
|
+
text = searched.map(column => foldSearchText(searchTextOf(column, row))).join(COLUMN_BREAK);
|
|
191
|
+
texts?.set(row, text);
|
|
192
|
+
}
|
|
193
|
+
return words.every(word => text.includes(word));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
const sortColumn = query.sort ? columns.find(column => column.key === query.sort.key) : undefined;
|
|
197
|
+
if (sortColumn && query.sort)
|
|
198
|
+
list = sortRows(list, sortColumn, query.sort.direction);
|
|
199
|
+
const page = clampPage(query.page, list.length, query.pageSize);
|
|
200
|
+
const start = (page - 1) * query.pageSize;
|
|
201
|
+
return { rows: list.slice(start, start + query.pageSize), matches: list.length, page };
|
|
202
|
+
}
|
|
203
|
+
/** The next sort after a click on a column's heading: ascending, then descending, then none. */
|
|
204
|
+
export function nextSort(current, key) {
|
|
205
|
+
if (!current || current.key !== key)
|
|
206
|
+
return { key, direction: "asc" };
|
|
207
|
+
return current.direction === "asc" ? { key, direction: "desc" } : null;
|
|
208
|
+
}
|
|
209
|
+
/** A width in CSS: pixels for numbers, the text as it is. */
|
|
210
|
+
export const cssLength = (value) => (typeof value === "number" ? `${value}px` : value);
|
|
211
|
+
/* Columns the user arranged: their order, their widths, and the ones pinned to a side */
|
|
212
|
+
/** The table's own columns in the widths it remembers: the numbers and checkbox column, and the actions. */
|
|
213
|
+
export const TABLE_INDEX_COLUMN = "#index";
|
|
214
|
+
export const TABLE_ACTIONS_COLUMN = "#actions";
|
|
215
|
+
/** Narrowest a column gets while the user drags its edge, in pixels. */
|
|
216
|
+
export const MIN_COLUMN_WIDTH = 56;
|
|
217
|
+
/** Whether a key names one of the table's own columns, not a column of the page's. */
|
|
218
|
+
export const isOwnColumn = (key) => key.startsWith("#");
|
|
219
|
+
/**
|
|
220
|
+
* The columns in the order the user put them: the keys of `order` first, in that order, then any column it does not
|
|
221
|
+
* name, where the columns were given. A key of a column that is gone is left out.
|
|
222
|
+
*/
|
|
223
|
+
export function orderColumns(columns, order) {
|
|
224
|
+
if (!order?.length)
|
|
225
|
+
return [...columns];
|
|
226
|
+
const byKey = new Map(columns.map(column => [column.key, column]));
|
|
227
|
+
const known = [];
|
|
228
|
+
for (const key of order) {
|
|
229
|
+
const column = byKey.get(key);
|
|
230
|
+
if (column) {
|
|
231
|
+
known.push(column);
|
|
232
|
+
byKey.delete(key);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Columns added since the order was saved keep their place among the ones that stayed.
|
|
236
|
+
const rest = columns.filter(column => byKey.has(column.key));
|
|
237
|
+
return order.length && rest.length ? [...known, ...rest] : known.length ? known : [...columns];
|
|
238
|
+
}
|
|
239
|
+
/** The keys with `key` moved to just before `beforeKey`, or to the end when it is null. */
|
|
240
|
+
export function moveColumnKey(keys, key, beforeKey) {
|
|
241
|
+
if (key === beforeKey)
|
|
242
|
+
return [...keys];
|
|
243
|
+
const rest = keys.filter(candidate => candidate !== key);
|
|
244
|
+
const at = beforeKey === null ? rest.length : rest.indexOf(beforeKey);
|
|
245
|
+
if (at < 0)
|
|
246
|
+
return [...keys];
|
|
247
|
+
rest.splice(at, 0, key);
|
|
248
|
+
return rest;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* The columns as the table draws them: in the user's order, with the pinned ones moved to their side (keeping their
|
|
252
|
+
* order among themselves), each with its width, its distance from that edge, and what the user may do with it.
|
|
253
|
+
*/
|
|
254
|
+
export function viewColumns(columns, options = {}) {
|
|
255
|
+
const { order, pinned = {}, widths = {}, offsets = {}, reorderable = true, resizable = true } = options;
|
|
256
|
+
const groups = { left: [], middle: [], right: [] };
|
|
257
|
+
for (const column of orderColumns(columns, order)) {
|
|
258
|
+
const pin = pinned[column.key] ?? column.pinned ?? null;
|
|
259
|
+
groups[pin === "left" ? "left" : pin === "right" ? "right" : "middle"].push({
|
|
260
|
+
column,
|
|
261
|
+
pin,
|
|
262
|
+
offset: offsets[column.key] ?? 0,
|
|
263
|
+
edge: false,
|
|
264
|
+
resizable: resizable && column.resizable !== false,
|
|
265
|
+
movable: reorderable && column.movable !== false,
|
|
266
|
+
width: widths[column.key],
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
// The shadow that says columns scroll under a pinned side belongs to its last column.
|
|
270
|
+
const last = groups.left[groups.left.length - 1];
|
|
271
|
+
if (last)
|
|
272
|
+
last.edge = true;
|
|
273
|
+
const first = groups.right[0];
|
|
274
|
+
if (first)
|
|
275
|
+
first.edge = true;
|
|
276
|
+
return [...groups.left, ...groups.middle, ...groups.right];
|
|
277
|
+
}
|
|
278
|
+
/** Whether the user may pin this column to a side. */
|
|
279
|
+
export function canPinColumn(column, rights) {
|
|
280
|
+
return (rights.pinnable ?? true) && column.pinnable !== false;
|
|
281
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { TableColumn, TableKey, TableLoader, TablePaging, TablePinSide, TableRow, TableSort, TableState } from "../Interfaces/TableInterfaces.ts";
|
|
2
|
+
/** What the table component tells its store on every render. */
|
|
3
|
+
export interface TableStoreOptions<TRow extends TableRow = TableRow> {
|
|
4
|
+
columns: readonly TableColumn<TRow>[];
|
|
5
|
+
rowKey: string | ((row: TRow) => TableKey);
|
|
6
|
+
/** Every row, given by the page. */
|
|
7
|
+
rows?: readonly TRow[];
|
|
8
|
+
load?: TableLoader<TRow>;
|
|
9
|
+
paging: TablePaging;
|
|
10
|
+
maxSelection?: number;
|
|
11
|
+
onSelectionChange?: (rows: TRow[]) => void;
|
|
12
|
+
}
|
|
13
|
+
/** How long a changed or added row stays highlighted. */
|
|
14
|
+
export declare const TABLE_FLASH_MS = 1600;
|
|
15
|
+
export interface TableReloadOptions {
|
|
16
|
+
/** Keeps the rows on screen with a thin bar while the load runs, instead of placeholders. Default true once rows have loaded. */
|
|
17
|
+
quiet?: boolean;
|
|
18
|
+
/** Starts over: the first page, no search, no selection. For another list behind the same table. */
|
|
19
|
+
reset?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface TableUpsertOptions {
|
|
22
|
+
/** Where a new row goes. Default `start`, so the user sees it. A row that is already there stays in its place. */
|
|
23
|
+
at?: "start" | "end";
|
|
24
|
+
/** Highlights the row for a moment. Default true. */
|
|
25
|
+
flash?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare class TableStore<TRow extends TableRow = TableRow> {
|
|
28
|
+
private options;
|
|
29
|
+
private state;
|
|
30
|
+
private listeners;
|
|
31
|
+
/** Every row, when the table has them all. */
|
|
32
|
+
private all;
|
|
33
|
+
/** Whether `all` holds rows given by the page (not loaded). */
|
|
34
|
+
private given;
|
|
35
|
+
private texts;
|
|
36
|
+
private controller;
|
|
37
|
+
private seq;
|
|
38
|
+
private flashTimer;
|
|
39
|
+
private reloadTimer;
|
|
40
|
+
/** A load was stopped by `dispose`. */
|
|
41
|
+
private interrupted;
|
|
42
|
+
constructor(pageSize?: number);
|
|
43
|
+
getState: () => TableState<TRow>;
|
|
44
|
+
subscribe: (listener: () => void) => (() => void);
|
|
45
|
+
get paging(): TablePaging;
|
|
46
|
+
get columns(): readonly TableColumn<TRow>[];
|
|
47
|
+
/** A row's key as text. */
|
|
48
|
+
keyOf(row: TRow): string;
|
|
49
|
+
/** Every row the table has: all of them in a client table, the page's in a server table. */
|
|
50
|
+
getRows(): TRow[];
|
|
51
|
+
/** The row with this key, if the table has it. */
|
|
52
|
+
getRow(key: TableKey): TRow | undefined;
|
|
53
|
+
getSelected(): TRow[];
|
|
54
|
+
/**
|
|
55
|
+
* Every row the table would show for its query, in order: the whole searched and sorted list for a client table, the
|
|
56
|
+
* page a server sent for a server table. An export and a print write these.
|
|
57
|
+
*/
|
|
58
|
+
getViewRows(): TRow[];
|
|
59
|
+
isSelected(row: TRow): boolean;
|
|
60
|
+
/** Takes the component's props. Tells nobody: the component calls it while rendering. */
|
|
61
|
+
configure(options: TableStoreOptions<TRow>): void;
|
|
62
|
+
private set;
|
|
63
|
+
/** Puts back what was saved on this device (page size, sort, columns), before the first load. Tells nobody. */
|
|
64
|
+
restore(saved: {
|
|
65
|
+
pageSize?: number;
|
|
66
|
+
sort?: TableSort | null;
|
|
67
|
+
hidden?: readonly string[];
|
|
68
|
+
order?: readonly string[];
|
|
69
|
+
widths?: Readonly<Record<string, number>>;
|
|
70
|
+
pinned?: Readonly<Record<string, TablePinSide>>;
|
|
71
|
+
}): void;
|
|
72
|
+
/** Shows these rows: every row there is. The table searches, sorts, and pages them. */
|
|
73
|
+
setRows(rows: readonly TRow[]): void;
|
|
74
|
+
/**
|
|
75
|
+
* Loads the rows again: every row for a client table, the page for a server table. A newer load stops an older one,
|
|
76
|
+
* so the table always shows the answer to its latest query. Resolves when the rows are in (or the load failed).
|
|
77
|
+
*/
|
|
78
|
+
reload({ quiet, reset }?: TableReloadOptions): Promise<void>;
|
|
79
|
+
private take;
|
|
80
|
+
private fail;
|
|
81
|
+
/** Searches, sorts, and pages every row (client tables), then tells the table. */
|
|
82
|
+
private view;
|
|
83
|
+
/** Changes the query: a client table shows it at once, a server table loads it. */
|
|
84
|
+
private query;
|
|
85
|
+
setSearch(search: string): void;
|
|
86
|
+
setSort(sort: TableSort | null): void;
|
|
87
|
+
/** Sorts by a column: ascending, then descending, then back to no sort. */
|
|
88
|
+
toggleSort(key: string): void;
|
|
89
|
+
setPage(page: number): void;
|
|
90
|
+
/** Changes the rows per page, keeping the first row of the page in view. */
|
|
91
|
+
setPageSize(pageSize: number): void;
|
|
92
|
+
/**
|
|
93
|
+
* Adds a row, or replaces the row with its key. A new row goes first (a server page grows by one until the next load),
|
|
94
|
+
* and is highlighted for a moment.
|
|
95
|
+
*/
|
|
96
|
+
upsert(row: TRow, { at, flash }?: TableUpsertOptions): void;
|
|
97
|
+
/**
|
|
98
|
+
* Changes fields of the row with this key: an object of new values, or a function of the row. Quiet by default; pass
|
|
99
|
+
* `{ flash: true }` to highlight the row, such as after an action the user took on it.
|
|
100
|
+
*/
|
|
101
|
+
patch(key: TableKey, changes: Partial<TRow> | ((row: TRow) => TRow), { flash }?: Pick<TableUpsertOptions, "flash">): void;
|
|
102
|
+
/**
|
|
103
|
+
* Takes rows out: they moved to another list or were deleted. A server table loads its page again in the background,
|
|
104
|
+
* so the page fills up and the counts stay right.
|
|
105
|
+
*/
|
|
106
|
+
remove(keys: readonly TableKey[]): void;
|
|
107
|
+
/** Selects or unselects rows, up to `maxSelection`. Returns how many rows it could not select. */
|
|
108
|
+
select(rows: readonly TRow[], on: boolean): number;
|
|
109
|
+
/** Selects or unselects every row on the page. */
|
|
110
|
+
selectPage(on: boolean): number;
|
|
111
|
+
clearSelection(): void;
|
|
112
|
+
private replaceSelected;
|
|
113
|
+
private refreshSelected;
|
|
114
|
+
/** A client table forgets selected rows that are gone from its rows. */
|
|
115
|
+
private dropMissingSelection;
|
|
116
|
+
setBusy(keys: readonly TableKey[], on: boolean): void;
|
|
117
|
+
flash(keys: readonly TableKey[]): void;
|
|
118
|
+
setHidden(key: string, hidden: boolean): void;
|
|
119
|
+
/** The order of every column, by key. Keys the columns do not have are kept, in case the column comes back. */
|
|
120
|
+
setOrder(order: readonly string[]): void;
|
|
121
|
+
/** Moves a column to just before another one, or to the end when `beforeKey` is null. */
|
|
122
|
+
moveColumn(key: string, beforeKey: string | null): void;
|
|
123
|
+
/** Sets the width of one column in pixels, or takes the width away (`null`), letting the content size it again. */
|
|
124
|
+
setWidth(key: string, width: number | null): void;
|
|
125
|
+
/** Sets several widths at once: what the table measured when the user started to resize a column. */
|
|
126
|
+
setWidths(widths: Readonly<Record<string, number>>): void;
|
|
127
|
+
/** Back to widths that follow the content. */
|
|
128
|
+
clearWidths(): void;
|
|
129
|
+
/** Keeps a column on the left or the right while the others scroll, or lets it scroll again (`null`). */
|
|
130
|
+
setPinned(key: string, side: TablePinSide | null): void;
|
|
131
|
+
/** Every column back as its params say: the given order, widths from the content, no pins, and the hidden ones. */
|
|
132
|
+
resetColumns(): void;
|
|
133
|
+
/** Whether the user has arranged the columns: an order, a width, a pin, or a hidden column of their own. */
|
|
134
|
+
hasColumnChanges(): boolean;
|
|
135
|
+
/** Stops a load that runs and the timers. The table calls it when it goes away. */
|
|
136
|
+
dispose(): void;
|
|
137
|
+
/**
|
|
138
|
+
* Loads again what `dispose` stopped, when the table comes back: React's Strict Mode unmounts and mounts every table
|
|
139
|
+
* once in development, and a store given as `table` can move to another place on the page.
|
|
140
|
+
*/
|
|
141
|
+
resume(): void;
|
|
142
|
+
}
|