nexus-shared 3.0.0 → 3.2.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/CHANGELOG.md +442 -0
- package/README.md +1123 -1000
- package/dist/Client.Index.d.ts +6 -0
- package/dist/Client.Index.js +6 -0
- package/dist/Components/Chats/ChatParts.d.ts +2 -2
- package/dist/Components/Chats/ChatParts.js +3 -7
- package/dist/Components/Chats/ChatThread.js +3 -3
- package/dist/Components/Documents/Button.d.ts +6 -1
- package/dist/Components/Documents/Button.js +2 -2
- package/dist/Components/Documents/Menu.js +21 -9
- package/dist/Components/Documents/SplitButton.d.ts +3 -1
- package/dist/Components/Documents/SplitButton.js +7 -5
- package/dist/Components/Documents/TabButtons.d.ts +5 -2
- package/dist/Components/Documents/TabButtons.js +2 -2
- package/dist/Components/Forms/ApiForm.d.ts +1 -1
- package/dist/Components/Forms/ApiForm.js +1 -1
- package/dist/Components/Forms/Crud.js +486 -206
- package/dist/Components/Forms/CrudHistory.d.ts +66 -0
- package/dist/Components/Forms/CrudHistory.js +140 -0
- package/dist/Components/Forms/CrudParts.d.ts +5 -1
- package/dist/Components/Forms/CrudParts.js +38 -5
- package/dist/Components/Forms/ExcelImport.d.ts +23 -7
- package/dist/Components/Forms/ExcelImport.js +189 -60
- package/dist/Components/Forms/Form.js +9 -14
- package/dist/Components/Forms/SubmitForm.d.ts +11 -2
- package/dist/Components/Forms/SubmitForm.js +23 -12
- package/dist/Components/Inputs/CodeBox.d.ts +69 -0
- package/dist/Components/Inputs/CodeBox.js +316 -0
- package/dist/Components/Inputs/DatePicker.js +4 -1
- package/dist/Components/Inputs/DateRangePicker.js +4 -1
- package/dist/Components/Inputs/DateTimePicker.js +4 -1
- package/dist/Components/Inputs/Dropdown.js +4 -1
- package/dist/Components/Inputs/DropdownParts.d.ts +1 -1
- package/dist/Components/Inputs/DropdownParts.js +4 -6
- package/dist/Components/Inputs/InputRenderer.js +3 -0
- package/dist/Components/Inputs/NumberBox.js +26 -2
- package/dist/Components/Inputs/RowsInput.js +2 -2
- package/dist/Components/Inputs/Slider.js +4 -1
- package/dist/Components/Inputs/TabularForm.js +42 -10
- package/dist/Components/Inputs/TextArea.js +4 -1
- package/dist/Components/Inputs/TextBox.js +4 -1
- package/dist/Components/Inputs/TimePanel.js +4 -3
- package/dist/Components/Inputs/TimePicker.js +4 -1
- package/dist/Components/Inputs/TreeView.d.ts +55 -5
- package/dist/Components/Inputs/TreeView.js +372 -23
- package/dist/Components/Layouts/Popup.js +1 -4
- package/dist/Components/Layouts/ThemePicker.d.ts +21 -3
- package/dist/Components/Layouts/ThemePicker.js +89 -6
- package/dist/Components/Layouts/ThemeScript.d.ts +4 -2
- package/dist/Components/Layouts/ThemeScript.js +4 -2
- package/dist/Components/Layouts/ThemeSwitcher.d.ts +6 -3
- package/dist/Components/Layouts/ThemeSwitcher.js +9 -8
- package/dist/Components/Viewers/Avatar.d.ts +21 -0
- package/dist/Components/Viewers/Avatar.js +21 -0
- package/dist/Components/Viewers/DataTable.js +252 -62
- package/dist/Components/Viewers/DataTableColumns.d.ts +7 -3
- package/dist/Components/Viewers/DataTableColumns.js +25 -68
- package/dist/Components/Viewers/DataTableFilters.d.ts +21 -0
- package/dist/Components/Viewers/DataTableFilters.js +68 -0
- package/dist/Components/Viewers/DataTableParts.d.ts +28 -10
- package/dist/Components/Viewers/DataTableParts.js +42 -13
- package/dist/Helpers/AnimationHelpers.d.ts +11 -0
- package/dist/Helpers/AnimationHelpers.js +20 -2
- package/dist/Helpers/ApiModules.d.ts +3 -34
- package/dist/Helpers/ApiModules.js +3 -3
- package/dist/Helpers/ApiRoutes.d.ts +68 -26
- package/dist/Helpers/ApiRoutes.js +31 -27
- package/dist/Helpers/ArrayHelpers.d.ts +0 -0
- package/dist/Helpers/ArrayHelpers.js +2 -0
- package/dist/Helpers/CodeHelpers.d.ts +97 -0
- package/dist/Helpers/CodeHelpers.js +332 -0
- package/dist/Helpers/CrudBackend.d.ts +101 -8
- package/dist/Helpers/CrudBackend.js +172 -11
- package/dist/Helpers/CrudColumns.d.ts +51 -0
- package/dist/Helpers/CrudColumns.js +69 -0
- package/dist/Helpers/CrudHelpers.d.ts +50 -5
- package/dist/Helpers/CrudHelpers.js +109 -12
- package/dist/Helpers/ExcelBackend.d.ts +3 -3
- package/dist/Helpers/ExcelBackend.js +8 -2
- package/dist/Helpers/ExcelHelpers.d.ts +36 -8
- package/dist/Helpers/ExcelHelpers.js +55 -13
- package/dist/Helpers/FormStore.d.ts +9 -5
- package/dist/Helpers/FormStore.js +25 -5
- package/dist/Helpers/InputParamsHelpers.js +8 -0
- package/dist/Helpers/MessageBuilder.js +3 -0
- package/dist/Helpers/NexusHelpers.d.ts +0 -0
- package/dist/Helpers/NexusHelpers.js +1 -0
- package/dist/Helpers/NumberHelpers.d.ts +7 -1
- package/dist/Helpers/NumberHelpers.js +41 -3
- package/dist/Helpers/PopoverHelpers.d.ts +17 -5
- package/dist/Helpers/PopoverHelpers.js +52 -17
- package/dist/Helpers/PopoverHooks.d.ts +24 -0
- package/dist/Helpers/PopoverHooks.js +62 -0
- package/dist/Helpers/RequestCache.d.ts +35 -0
- package/dist/Helpers/RequestCache.js +113 -0
- package/dist/Helpers/RowsStore.d.ts +7 -2
- package/dist/Helpers/RowsStore.js +15 -3
- package/dist/Helpers/TableFilters.d.ts +25 -0
- package/dist/Helpers/TableFilters.js +162 -0
- package/dist/Helpers/TableHelpers.d.ts +7 -4
- package/dist/Helpers/TableHelpers.js +15 -6
- package/dist/Helpers/TableJoins.d.ts +70 -0
- package/dist/Helpers/TableJoins.js +250 -0
- package/dist/Helpers/TableStore.d.ts +16 -3
- package/dist/Helpers/TableStore.js +25 -5
- package/dist/Helpers/ThemeHelpers.d.ts +21 -4
- package/dist/Helpers/ThemeHelpers.js +46 -9
- package/dist/Helpers/TreeStore.d.ts +225 -11
- package/dist/Helpers/TreeStore.js +683 -90
- package/dist/Helpers/UserPreferences.d.ts +22 -0
- package/dist/Helpers/UserPreferences.js +116 -0
- package/dist/Interfaces/CrudInterfaces.d.ts +67 -10
- package/dist/Interfaces/ExcelInterfaces.d.ts +12 -0
- package/dist/Interfaces/FormInterfaces.d.ts +37 -6
- package/dist/Interfaces/InputInterfaces.d.ts +24 -0
- package/dist/Interfaces/MessageInterfaces.d.ts +1 -1
- package/dist/Interfaces/PreferenceInterfaces.d.ts +51 -0
- package/dist/Interfaces/PreferenceInterfaces.js +25 -0
- package/dist/Interfaces/TableInterfaces.d.ts +145 -16
- package/dist/Interfaces/ThemeInterfaces.d.ts +25 -0
- package/dist/Interfaces/ThemeInterfaces.js +15 -0
- package/dist/Services/ApiProxy.d.ts +36 -1
- package/dist/Services/ApiProxy.js +182 -52
- package/dist/Services/ApiProxyRules.d.ts +203 -0
- package/dist/Services/ApiProxyRules.js +426 -0
- package/dist/Services/ExcelApi.d.ts +3 -1
- package/dist/Services/ExcelApi.js +1 -1
- package/dist/Services/JoinSources.d.ts +48 -0
- package/dist/Services/JoinSources.js +74 -0
- package/dist/Services/ServerApi.d.ts +13 -2
- package/dist/Services/ServerApi.js +8 -3
- package/dist/Services/ThemeService.d.ts +18 -2
- package/dist/Services/ThemeService.js +80 -16
- package/dist/Shared.Index.d.ts +6 -0
- package/dist/Shared.Index.js +6 -0
- package/package.json +4 -2
- package/src/Styles/Nexus.Avatar.css +79 -0
- package/src/Styles/Nexus.Base.css +51 -4
- package/src/Styles/Nexus.Button.css +27 -0
- package/src/Styles/Nexus.Calendar.css +4 -6
- package/src/Styles/Nexus.Chat.css +5 -51
- package/src/Styles/Nexus.Choice.css +4 -6
- package/src/Styles/Nexus.Crud.css +16 -9
- package/src/Styles/Nexus.Dropdown.css +12 -61
- package/src/Styles/Nexus.Editor.css +4 -6
- package/src/Styles/Nexus.Excel.css +34 -15
- package/src/Styles/Nexus.Form.css +25 -67
- package/src/Styles/Nexus.Index.css +1 -0
- package/src/Styles/Nexus.Input.css +794 -637
- package/src/Styles/Nexus.Markdown.css +0 -28
- package/src/Styles/Nexus.Menu.css +8 -30
- package/src/Styles/Nexus.Popup.css +22 -29
- package/src/Styles/Nexus.RichText.css +2 -32
- package/src/Styles/Nexus.Rows.css +51 -10
- package/src/Styles/Nexus.Slider.css +3 -5
- package/src/Styles/Nexus.Tab.Buttons.css +20 -7
- package/src/Styles/Nexus.Table.css +1387 -1296
- package/src/Styles/Nexus.Theme.Picker.css +342 -232
- package/src/Styles/Nexus.Time.css +9 -12
- package/src/Styles/Nexus.Toast.css +6 -8
- package/src/Styles/Nexus.Tokens.css +17 -0
- package/src/Styles/Nexus.Tree.css +76 -11
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,448 @@ Notable changes to `nexus-shared`. The **values** of exported constants count as
|
|
|
4
4
|
Nexus backend's own numbers, and a value that differs from the backend by one does not fail loudly — it silently
|
|
5
5
|
means the wrong thing on every record and every permission check.
|
|
6
6
|
|
|
7
|
+
## 3.2.0
|
|
8
|
+
|
|
9
|
+
**New features, no code break.** Every export of `3.1.0` is still exported (99 are new: 72 from `nexus-shared`, 16
|
|
10
|
+
from `nexus-shared/client`, 11 from `nexus-shared/server`), and no exported constant changed a value: six only gained
|
|
11
|
+
keys or entries (`API_ACTIONS`, `CRUD_ROW_ACTIONS`, `CRUD_MASS_ACTIONS`, `MESSAGE_ACTION_NAMES`,
|
|
12
|
+
`DEFAULT_USER_PREFERENCES`, `PREFERENCE_STORAGE_KEYS`). Both were checked against the published `3.1.0`: its exports
|
|
13
|
+
by the TypeScript checker, its constants by importing both builds. The owner chose the number on 2026-10-10. It needs
|
|
14
|
+
`nexus-icons` `^1.2.0`, the first with `iconStarOff`. What can move for an app:
|
|
15
|
+
|
|
16
|
+
- **A CRUD built from `apiController` reads `BookmarkListing` and writes `SwitchBookmarks`**, the Nexus backend's
|
|
17
|
+
names since 2026-10-07, in place of `PinListing` / `Pin`. See "Pins and favourites from one call" below.
|
|
18
|
+
- **Stylesheet overrides** of the pinned section's classes and of the outlined field's raised label (both below).
|
|
19
|
+
- **`TablePinnedHead` is deprecated**: `TableSectionHead` takes the section's icon. The old name still renders the
|
|
20
|
+
pinned section's head, with the pin icon and the new classes, so an import of it keeps building.
|
|
21
|
+
|
|
22
|
+
### A tree whose children load when they are opened
|
|
23
|
+
|
|
24
|
+
The owner, 2026-10-09: administrative regions in two versions, a data table and a tree, both paged on the server; the
|
|
25
|
+
tree "when a node is expanded, calls the API, gets the info and caches it". The data table already paged on the
|
|
26
|
+
server, so the change is the tree's. Added only; a tree without `loadChildren` works as before, with one fix it shares.
|
|
27
|
+
|
|
28
|
+
- **`TreeView` `loadChildren`** (`nexus-shared/client`): asks for an item's children when it is first expanded, a page
|
|
29
|
+
at a time - `{ parent, parentValue, skip, take, signal }` in, `{ items, total?, hasMore? }` out, at once or in a
|
|
30
|
+
promise - and for the top level too when `items` is left out. The children go under the item asked for, whatever
|
|
31
|
+
their own parent field says. New params `childPageSize` (default 50), `hasChildrenKey` (a boolean or a count such as
|
|
32
|
+
`childCount`; without it an item shows a chevron until its first page answers, and loses it when that is empty),
|
|
33
|
+
`cacheKey`, `cacheLifetime` and `partialMessage`; `items` is optional now.
|
|
34
|
+
- **Status rows**, rows of the windowed list like the others, reached with the arrows and pressed with Enter or Space:
|
|
35
|
+
a loading row while a page comes (the parent `aria-busy`, a polite announcement), "Could not load the items." /
|
|
36
|
+
"Could not load more items." with Retry (the Dropdown's words), and **Load more** with how many are left when the
|
|
37
|
+
total is known. Focus on one of them follows the page to its first new item. New classes `.nx-tree__row--status`,
|
|
38
|
+
`--loading`, `--error`, `--more`, `.nx-tree__status-icon`, `.nx-tree__status-text`, `.nx-tree__retry`.
|
|
39
|
+
- **What is loaded stays**: collapse and expand again asks nothing. With `cacheKey`, pages are kept in `treePages`, a
|
|
40
|
+
`RequestCache` shared by every tree on the page, for 5 minutes: a remount or a second tree asks nothing, and a request
|
|
41
|
+
already on its way is shared. A failure is not kept, so Retry asks again.
|
|
42
|
+
- **`refresh(value?)`** on the handle drops the kept pages (all, or an item's and everything under it) and loads again
|
|
43
|
+
only what is on screen - the top level and the open items with rows in view; the rest load fresh when next opened or
|
|
44
|
+
scrolled to. What is drawn stays until the fresh pages replace it, and an item still there keeps what is open under it.
|
|
45
|
+
- **Checking, with `cascade`**: an item whose items are not all loaded (`TreeNode.partial`) cannot be checked as a
|
|
46
|
+
whole - a check stands for everything under it, and the value can only hold items that were loaded, so it would
|
|
47
|
+
silently leave the rest out. A click says so (`partialMessage`); it shows mixed when something loaded under it is
|
|
48
|
+
checked, never checked; Select all checks the loaded parts. A saved value whose item is not loaded yet stays in the
|
|
49
|
+
value and checks the item when it comes. Without `cascade` nothing changes.
|
|
50
|
+
- **Nothing asks in bulk**: Expand all, `*` and `defaultExpanded` open loaded items only.
|
|
51
|
+
- **`TreeStore`**: options `lazy`, `lazyRoots`, `hasChildrenKey`; `canExpand`, `find`, `pagesOf`, `beginLoad`,
|
|
52
|
+
`receivePage`, `failLoad`, `replacePages`, `markStale`, `rootPages`, and `insertMany`, a batch insert that updates
|
|
53
|
+
counts along one path (a page of 50 into 100,000 items: 0.3 ms); `TreeNode.pages`, `.partial`, `.status`. Helpers
|
|
54
|
+
`treePages`, `treePagesKey`, `treePageKey`, `expireTreePages`, `treeHasChildren`, `treeStatusKey`, and the types
|
|
55
|
+
`TreeChildrenRequest`, `TreeChildrenPage`, `TreeChildPages`, `TreeStatusKind`.
|
|
56
|
+
- **`RequestCache`** (`Helpers/RequestCache.ts`, `nexus-shared`): answers kept by key for a lifetime, a request on its
|
|
57
|
+
way shared, a failure not kept, `drop` by key prefix or test; `REQUEST_CACHE_LIFETIME`, `isPromiseLike`.
|
|
58
|
+
- **Fix, every tree**: a node's `uid` counted from one counter for the whole page, so a page rendered on a server that
|
|
59
|
+
had rendered trees before numbered its rows differently from the browser, and React reported a hydration mismatch
|
|
60
|
+
in development (production does not check attributes). It is counted per store now.
|
|
61
|
+
- **Fix, every tree: a scroll that takes the focused row out of view keeps its position** (nexus.web, the same
|
|
62
|
+
evening: after a client-side navigation a jump to the end came back up to 1,120px, and a wheel step left Load more
|
|
63
|
+
out of view). Chrome lays the page out inside the removal of a focused element, in the middle of React's commit,
|
|
64
|
+
when the leaving rows are gone and the new ones not yet in, and clamped or anchored the scroll position to that short
|
|
65
|
+
list. Focus now moves from a row to the tree's viewport in the event that moves the window, before the row leaves
|
|
66
|
+
(a name box being edited lets go without saving, as when its row unmounted), and `.nx-tree__list` gets a `min-height`
|
|
67
|
+
of all its rows, so a layout at any moment of a commit sees its full height.
|
|
68
|
+
|
|
69
|
+
### Focus rings shown whole
|
|
70
|
+
|
|
71
|
+
The owner, 2026-10-08: the theme panel cut the focus ring of its last example input (bottom) and of the date-format
|
|
72
|
+
dropdown (left), and a focused outlined input's raised label sat across its own ring. No export changed; what a
|
|
73
|
+
consumer may notice:
|
|
74
|
+
|
|
75
|
+
- **`.nx-popover__scroll` keeps 4px of room** on its top, bottom and left for focus rings, with a matching negative
|
|
76
|
+
margin so nothing moves, and `scroll-padding` of 4px. Every panel that scrolls through it gets it: the theme panel,
|
|
77
|
+
menus, and the table's Columns and Filters panels. A panel's `--nx-popover-pad` must be 4px or more. Where the
|
|
78
|
+
scroller is its panel's first or last child, as a menu's list is, its left corner follows the panel's curve (the
|
|
79
|
+
inner radius less that room: 9px in a menu), so a scrolled item's highlight never paints over the rounded border.
|
|
80
|
+
- **A focused outlined field** (`design="outlined"`) draws a 2px accent outline over its border instead of the soft
|
|
81
|
+
ring outside it, so the raised label sits in a clean gap. The raised label is placed with `top: -0.6em` instead of a
|
|
82
|
+
transform, and its backing is now the label's `::before`: it starts exactly on the box's edge at every screen scale
|
|
83
|
+
and paints nothing above it. The gradient it replaces painted a strip of the field's colour above read-only and
|
|
84
|
+
disabled boxes and in dark popovers. An app that styles the outlined label's `::before` overrides that backing. The
|
|
85
|
+
standard and inbox designs are unchanged.
|
|
86
|
+
|
|
87
|
+
### The API proxy hides the backend
|
|
88
|
+
|
|
89
|
+
The owner, 2026-10-08: nexus.web's network tab showed `POST /api/app/configuration/Region/MassRecover` answer
|
|
90
|
+
`307 Temporary Redirect` with the backend's own https address in it, which the browser then called directly, and
|
|
91
|
+
asked for a proxy behind which the app looks like the backend itself. `createApiProxy({ token })` is called as before
|
|
92
|
+
and no export is removed; what a route may notice:
|
|
93
|
+
|
|
94
|
+
- **No redirect reaches the browser** (was: passed on, its Location rewritten only on the backend's own host). One that
|
|
95
|
+
stays inside the module - the root's origin, or its host moved to https, under the root's path - is followed on the
|
|
96
|
+
server as `fetch` follows it, up to 3; any other answers 502 `BACKEND_REDIRECT_REFUSED` or `BACKEND_REDIRECT_LIMIT`.
|
|
97
|
+
- **Response headers by an allowlist** (was: all but the hop headers): `DEFAULT_PROXY_RESPONSE_HEADERS` and the Nexus
|
|
98
|
+
backend's `X-Nexus-*`. A header an app read through the proxy (`X-Request-Id`, `X-Total-Count`) must now be named in
|
|
99
|
+
`responseHeaders`; Server, X-Powered-By, X-AspNet-*, HSTS, CORS, Location, Link and Set-Cookie never pass (Set-Cookie
|
|
100
|
+
with `passCookies`). Every answer gets `X-Content-Type-Options: nosniff`, and `Cache-Control: private, no-store`
|
|
101
|
+
when the backend sent none.
|
|
102
|
+
- **Error pages are replaced** with the failure envelope and the backend's status (`BACKEND_ERROR_PAGE`): HTML, text,
|
|
103
|
+
stack traces, and JSON that repeats the user's token. JSON refusals pass as they came.
|
|
104
|
+
- **Refusals carry `errorCode` and `errorMessages`** beside `message` and `messageCode`, and say nothing about the
|
|
105
|
+
server: an unset module answers "The School API is not set up on this server." (was: "... is not set (API_SCHOOL).",
|
|
106
|
+
which named a setting; the log has it now), and a module the browser calls directly "... is not available at this
|
|
107
|
+
address." New: 403 `CROSS_SITE_REFUSED`, 413 `REQUEST_TOO_LARGE`, 400 `REQUEST_UNREADABLE`.
|
|
108
|
+
- **The request**: the browser's X-Forwarded-*, Forwarded, X-Real-IP, method and path overrides, `Expect` and Next.js's
|
|
109
|
+
own headers never go on; the proxy writes X-Forwarded-For (the last address the app received, and only ever an IPv4
|
|
110
|
+
or IPv6 address) and X-Forwarded-Host and -Proto (the app's configured origin, else the request's). A module whose
|
|
111
|
+
root is a path on the app is called at this server's own host and port, over http (was: the request URL's protocol,
|
|
112
|
+
which Next.js takes from X-Forwarded-Proto), and never at the configured origin or anywhere a request header names.
|
|
113
|
+
- **`configureServerApi({ origin })` must not trust the Host header.** `serverApi` calls a relative root at that origin,
|
|
114
|
+
so one built from a request's Host lets a client pick an address on the server's machine. The template's
|
|
115
|
+
`api-setup.ts` now takes the Host only as `localhost:<PORT>` or `127.0.0.1:<PORT>` (Next.js sets `PORT` to the port
|
|
116
|
+
it listens on); an app with a copy of the older one, which took any localhost port, should change it the same way.
|
|
117
|
+
- **A server call can say who the client is.** `lastForwardedAddress` (which now takes anything with a `get`, Next.js's
|
|
118
|
+
`headers()` included) and `readIpAddress` are exported from `nexus-shared/server`: the client's address as the proxy
|
|
119
|
+
reads it, an IP address or nothing. The template's `api-setup.ts` uses the first in `configureServerApi({ headers })`
|
|
120
|
+
to give Identity calls, and only them, `X-Forwarded-For` and the browser's `User-Agent`: sign-in leaves from the
|
|
121
|
+
server, and Identity's rate limit counts per address. An app that signs in through `serverApi` should do the same,
|
|
122
|
+
for Identity's calls only, since reading the request makes a page render per request.
|
|
123
|
+
- **New options** `responseHeaders`, `passCookies`, `maxBodySize` (100 MB), `maxRedirects`, `clientAddress`,
|
|
124
|
+
`allowCrossSite`, `failure`, `onProblem`; **new exports** `DEFAULT_PROXY_RESPONSE_HEADERS`, `DEFAULT_PROXY_BODY_LIMIT`,
|
|
125
|
+
`DEFAULT_PROXY_REDIRECTS`, `PROXY_REFUSALS`, `nexusFailure`, `lastForwardedAddress`, `readIpAddress` and the types
|
|
126
|
+
`ApiProxyFailure`, `ApiProxyProblem`, `ApiProxyCode` (`nexus-shared/server`), and `serverAppOrigin`.
|
|
127
|
+
- **A backend on a development certificate**: the proxy now calls the backend's https address itself, and Node.js
|
|
128
|
+
trusts ASP.NET Core's development certificate only when started with `NODE_USE_SYSTEM_CA=1` (or
|
|
129
|
+
`NODE_EXTRA_CA_CERTS`). Without it such a module answers 502 `BACKEND_UNREACHABLE`, and the server's log says why.
|
|
130
|
+
|
|
131
|
+
### The code box
|
|
132
|
+
|
|
133
|
+
The owner, 2026-10-08: one shared input for codes typed a box to a character - one-time codes, SSNs, and more - as
|
|
134
|
+
dynamic as possible (numbers, strings, hex). New exports, nothing removed:
|
|
135
|
+
|
|
136
|
+
- **`CodeBox`** (`nexus-shared/client`): one real input lying unseen over a box per character, so autofill
|
|
137
|
+
(`autoComplete="one-time-code"`), a paste and a screen reader see one field. `length` or `groups`, a `separator`
|
|
138
|
+
outside the value, `characters` (`digits`, `letters`, `alphanumeric`, `hex`) or an `allowed` one-character pattern,
|
|
139
|
+
`letterCase`, `masked`, `slotPlaceholder`, `block`, `onComplete`, and the field shell's label, hint, error, sizes and
|
|
140
|
+
read-only note.
|
|
141
|
+
- **`type: "code"`** in `InputParams` (`CodeInputParams`): `renderInput`, `validateInput`, `normalizeInputValue` and
|
|
142
|
+
`formatInputValue` know it.
|
|
143
|
+
- **One typing model** for every way text arrives - a key, a phone's keyboard or its suggestion, an input method, a
|
|
144
|
+
paste, autofill: a whole code replaces everything; a character replaces a selected box, or goes into the box with the
|
|
145
|
+
ring - inserted while the code has an empty box, so a box Backspace removed takes the right digit back, and over that
|
|
146
|
+
box once the code is full. ← → Home End select box by box, and a full code's caret selects its last box. An input
|
|
147
|
+
method's word goes in once, when it commits. A pasted message gives its one code ("Your 2-step code is 482913" is
|
|
148
|
+
482913; "482 913" and "123-45-6789" are read whole), or nothing when it holds more characters than boxes and no one
|
|
149
|
+
code. `onComplete` runs once per code, and again once the boxes were short - by typing, a form's reset, or the page
|
|
150
|
+
emptying a controlled `value`.
|
|
151
|
+
- **A saved code is never cut**: `value`, `defaultValue` and a form's loaded record (`normalizeInputValue`) lose only
|
|
152
|
+
separators, case and a hex "#". A code the boxes cannot hold is kept, shown invalid at once, and a form refuses to
|
|
153
|
+
save it. The over-long message names the count: "The code is 6 digits long, not 7."
|
|
154
|
+
- **What a screen reader hears** is what the boxes show: "9 digits, in groups of 3, 2 and 4", "8 digits: DD/MM/YYYY".
|
|
155
|
+
A read-only code shows the accent border on every box while it has focus.
|
|
156
|
+
- **Styles**: a box without `block` keeps its own width (`.nx-code:not([data-block]) .nx-code__slot`), about as wide
|
|
157
|
+
as it is tall, and the boxes shrink alike on a narrow screen.
|
|
158
|
+
- **Helpers and types** (`nexus-shared`): `validateCode`, `formatCode`, `codeGroups`, `codeLength`, `loadCode`,
|
|
159
|
+
`codeFits`, `describeCode`, `readCodeText`, `typeCode`, `settleCode`, `moveCode`, `codeAfterInput`,
|
|
160
|
+
`CODE_CHARACTER_PATTERNS`, `DEFAULT_CODE_LENGTH`, `CodeCharacters`, `CodeRules`, `CodeShape`, `CodeEdit`, `CodeText`,
|
|
161
|
+
`CodeMove`; `normalizeCode` stays exported, though the box itself now loads a code with `loadCode`.
|
|
162
|
+
|
|
163
|
+
### Another record's name, joined by the database or by the table
|
|
164
|
+
|
|
165
|
+
Added only; nothing an app uses changes (2026-10-08, the owner: "some columns we joined into DB, some frontend").
|
|
166
|
+
|
|
167
|
+
- **A column whose value is another record's** (`TableColumn.join`): the row holds the key (`from`), and the table asks
|
|
168
|
+
the source once for every key its rows hold - a Nexus controller's `findListing`, or a `TableJoinSource` of the
|
|
169
|
+
page's own - then shows the record's field, searched, sorted, exported and shown in the details like any column. A key
|
|
170
|
+
with no record shows "Not found". `crudColumns.clientJoin(SUB_REGIONS, { key: "subRegionName", label: "Sub-region",
|
|
171
|
+
from: "subRegionId" })` is the Nexus form.
|
|
172
|
+
- **One cache for the page** (`tableJoins`, `Helpers/TableJoins.ts`): keys asked for in the same moment by every table and
|
|
173
|
+
picker go in one call per source; a call already out is waited for; answers are kept 5 minutes (`JOIN_LIFETIME`) and an
|
|
174
|
+
aged one is still drawn while it is asked again; a failed call is not repeated for 30 seconds. A Reload of a table, and
|
|
175
|
+
a `Crud` write on the controller looked up, mark its answers old (`expireLookups`).
|
|
176
|
+
- **A short tag after a value** (`TableColumn.tag`), and the one every reference to a record gets when that record is no
|
|
177
|
+
longer available: `CrudBackend.unavailableTag(status)` - Nexus: "Trashed" (warning) from 40, "Deleted" (danger) from 60,
|
|
178
|
+
none for a live record. `crudColumns.dbJoin({ key: "regionName", label: "Region", status: "regionRecordStatus" })` reads
|
|
179
|
+
it from a status the database joined into the row; `clientJoin` from the record it looked up. New class
|
|
180
|
+
`.nx-table__tag--after`.
|
|
181
|
+
- **A picker of a controller's usable records** (`crudPicker(REGIONS, { labelKey: "regionName" })`, `nexus-shared/client`):
|
|
182
|
+
its `ActiveListing` read once for the page and searched in the browser, a saved value it no longer offers found through
|
|
183
|
+
the same cached `findListing`, tagged and never offered again. `CrudBackend.isPickable(record)` - Nexus: under 30.
|
|
184
|
+
- `TableStore.refreshValues()` searches and sorts again when values changed without the rows; `CrudEndpoints` takes the
|
|
185
|
+
`findListing` and `activeListing` an `apiController` already has.
|
|
186
|
+
|
|
187
|
+
### Pins and favourites from one call
|
|
188
|
+
|
|
189
|
+
Two things move for an app:
|
|
190
|
+
|
|
191
|
+
- **A CRUD built from `apiController` now reads `BookmarkListing` and writes `SwitchBookmarks` / `MassSwitchBookmarks`**,
|
|
192
|
+
the Nexus backend's names since 2026-10-07. An app whose backend still answers only `PinListing` / `Pin` / `MassPin`
|
|
193
|
+
passes its own endpoints without `bookmarks`, `bookmark` and `mass.bookmark`, and keeps the pin-only behaviour.
|
|
194
|
+
- **The section classes are renamed** (an app's override of an old name stops matching):
|
|
195
|
+
|
|
196
|
+
| Was | Now |
|
|
197
|
+
|---|---|
|
|
198
|
+
| `tbody.nx-table__pins` | `tbody.nx-table__section[data-section="pins"]` (and `"favorites"`) |
|
|
199
|
+
| `.nx-table__pin-row`, `__pin-cell`, `__pin-toggle`, `__pin-caret`, `__pin-icon`, `__pin-title`, `__pin-count`, `__pin-loading` | `.nx-table__section-row`, `__section-cell`, `__section-toggle`, `__section-caret`, `__section-icon`, `__section-title`, `__section-count`, `__section-loading` |
|
|
200
|
+
| `.nx-table__pin-mark` | `.nx-table__mark`, inside `.nx-table__marks` |
|
|
201
|
+
| `data-pinned` on a section's row, `data-table-pins` on its toggle | gone: `data-section` on the section |
|
|
202
|
+
| saved `pinsOpen` | `openSections` (an old `pinsOpen: true` still opens the pinned section) |
|
|
203
|
+
|
|
204
|
+
What is new (the owner's rules, `Project-Skills/README.RULES.md`, "Rules for Favorite or Pin"):
|
|
205
|
+
|
|
206
|
+
- **Favourites beside pins, from one call.** `CrudEndpoints.bookmarks` (`Room/BookmarkListing`) answers every record
|
|
207
|
+
this user marked, each with its bits (`bookmarkTypes`: 1 pinned, 2 favourite); `Crud` splits them into a **Pinned**
|
|
208
|
+
and a **Favourites** section above the active list, each folding on its own and remembered. The lists' own rows need
|
|
209
|
+
not carry marks: every record's marks come from that call.
|
|
210
|
+
- **The marks show where a row's number is**, one under the other: a pinned favourite wears the pin above the star, in
|
|
211
|
+
the list and in both sections, with no column of its own (the owner, 2026-10-08). `DataTable` takes
|
|
212
|
+
`rowMarks(row)`; a section's row with none wears the section's icon.
|
|
213
|
+
- **Directed switches, never a toggle.** Pin and **Add to favourites** / **Remove from favourites** on a row send the
|
|
214
|
+
record's whole set (`bookmark`, `?bookmarkTypes=3`, built by `CrudBackend.marksRequest`); on a selection they send
|
|
215
|
+
one bit and the way (`mass.bookmark`, `?bookmarkTypes=2&isAdding=true`, `massMarksRequest`), only the records it
|
|
216
|
+
is for, and a selection is offered only the ways it can still go - both, when it is mixed. Bits the package does not
|
|
217
|
+
know are kept. While a whole-set backend's marks are not read yet, the actions wait and say why.
|
|
218
|
+
- **A limit each:** `configureCrud({ maxFavorites })`, the same as `maxPins` unless set (`markLimit`). Nothing is ever
|
|
219
|
+
taken off to meet a lowered limit.
|
|
220
|
+
- **No reload:** a switch's answer (`answeredMarks`, or the ids a mass switch answers) is written into the sections,
|
|
221
|
+
the table and every list kept. A record trashed leaves the sections and keeps its marks; recovered, it comes back.
|
|
222
|
+
- `DataTable` takes `sections` (`TableRowSection`: key, label, icon, rows, loading, max); `pinnedRows` and the other
|
|
223
|
+
`pinned*` props stay, as one section with the key `pins`. Select by **Favourites** / **Not favourites**. The message
|
|
224
|
+
builder knows `favorite` and `unfavorite` ("3 guests added to the favourites."). New helpers: `CRUD_MARKS`,
|
|
225
|
+
`CRUD_MARK_BITS`, `hasMark`, `withMark`, `markLimit`, `markDirections`, `markedRecords`, `patchMarkedRecords`,
|
|
226
|
+
`dropMarkedRows`, `crudMassEndpoint`.
|
|
227
|
+
- **Needs `nexus-icons` with `iconStarOff`**, which is new in its source (not on npm): release `nexus-icons` with a
|
|
228
|
+
raised version first, and raise this package's `nexus-icons` range to it.
|
|
229
|
+
|
|
230
|
+
### A CRUD page passes params and the CRUD does the rest (2026-10-08)
|
|
231
|
+
|
|
232
|
+
The owner: *"we just pass params no logic, due to we have 1000+ similar page"*. What the Regions page did itself is
|
|
233
|
+
now `Crud`'s, so a page is its controller, columns and inputs:
|
|
234
|
+
|
|
235
|
+
- **`apiController(module, controller, subject, options)`**: `commands: "switch"` (the default, the Nexus backend since
|
|
236
|
+
2026-10-07: `SwitchFlag`, `MassSwitchFlag`, `BookmarkListing`, `SwitchBookmarks`, `MassSwitchBookmarks`) or
|
|
237
|
+
`"toggle"` (`Flag`, `MassFlag`, `PinListing`, `Pin`, `MassPin`), and `without` for what a backend lacks (`"pages"`,
|
|
238
|
+
`"audit"`, `"flag"`, `"marks"`, `"mass.description"`, ...). **The return type now marks those members optional**
|
|
239
|
+
(`pages`, `pagination`, `flag`, `audit`, the marks, the mass actions): a caller that reads one adds `!` or `?.`.
|
|
240
|
+
`MODULES.controller` takes the same options.
|
|
241
|
+
- **A directed flag.** Flag and Unflag send the way (`CrudBackend.flagRequest`, Nexus `?isFlagged=`); a selection is
|
|
242
|
+
offered Flag and Unflag only as it can still go, sends only the records each is for, and says "the other N already
|
|
243
|
+
were" when the answer lists fewer. `NexusCrudBackend.isFlagged` is the band 25-29, and `canFlag` keeps Flag off a
|
|
244
|
+
pending record (30-39). The old "Flag / unflag" toggle of a mixed selection is gone.
|
|
245
|
+
- **History built in.** With an `audit` endpoint the person may call, every row offers **History** and the details
|
|
246
|
+
**View history**: `AuditHistory` and `openAuditHistory` (moved from nexus.web), its words from
|
|
247
|
+
`CrudBackend.auditWords`.
|
|
248
|
+
- **Presets:** `crudColumns.status()`, `.description()` (a `markdown` column: text in the table, formatted in the
|
|
249
|
+
details), `.added()`, `.changed()`, and `crudInputs.description()` (Markdown, 1000 characters, one hint for every
|
|
250
|
+
model). `TableColumn.markdown` is new.
|
|
251
|
+
- **A Create answered with the payload** (no status, so no list can place it) is read back once through `details`.
|
|
252
|
+
- **The export names the list on screen** (`CrudBackend.exportFilters`, Nexus `{ flag: 50 | 52 | 54 | 55 }`).
|
|
253
|
+
### The theme panel: grouped, twice as wide, and the input design (2026-10-08)
|
|
254
|
+
|
|
255
|
+
- **Input design is a quick preference** (`UserPreferences.inputDesign`, key `nx-input-design`): Standard, Outlined or
|
|
256
|
+
Inbox for every labelled input whose page and form name none (`useInputDesign`). An input with a design of its own
|
|
257
|
+
(a grid's cells) keeps it, and one with no label (a table's search box) stays standard: it has no label to place.
|
|
258
|
+
- **`ThemeSwitcher`'s panel is 680px wide** (was 340), its settings in named sections two to a row - Theme across the
|
|
259
|
+
top, then Appearance, Language and region, Dates and times, Numbers, Tables, and Forms (the input design, with a box
|
|
260
|
+
to try it) - and one under the other on a narrow window. `ThemePicker`'s card group is named Colors now, under Theme.
|
|
261
|
+
|
|
262
|
+
- **`Crud` takes `backend`**, for an app whose pages sit on two backends. `CrudBackend` gains `canFlag`, `flagRequest`,
|
|
263
|
+
`exportFilters`, `statusText` and `auditWords`, each with a default, so an app's own backend class still compiles.
|
|
264
|
+
|
|
265
|
+
## 3.1.0
|
|
266
|
+
|
|
267
|
+
**New features, no code break.** Every export of `3.0.0` is still exported (49 are new: 33 from `nexus-shared`, 16 from
|
|
268
|
+
`nexus-shared/client`), and no exported constant changed its value, so `^3.0.0` takes it. The owner chose the number
|
|
269
|
+
on 2026-10-08. What can move for an app is its **stylesheet overrides**: these class names and custom properties were
|
|
270
|
+
renamed or dropped, and an override of the old name no longer applies.
|
|
271
|
+
|
|
272
|
+
| Was | Now |
|
|
273
|
+
|---|---|
|
|
274
|
+
| `.nx-columns__head`, `__title`, `__count`, `__foot` | `.nx-table-panel__head`, `__title`, `__count`, `__foot` |
|
|
275
|
+
| `.nx-table-panel__body` | `.nx-popover__scroll` |
|
|
276
|
+
| `.nx-chat-avatar`, `--nx-chat-avatar` | `.nx-avatar`, `--nx-avatar-size` |
|
|
277
|
+
| `.nx-table__frame`'s border | on `.nx-table` |
|
|
278
|
+
| `--nx-field-scrollbar`, `--nx-field-scrollbar-hover`, `--nx-dropdown-scrollbar`, `--nx-time-scrollbar` | gone: one thin bar everywhere |
|
|
279
|
+
| `.nx-form__skeleton*` | gone: a loading form shows its real inputs, disabled |
|
|
280
|
+
| `.nx-excel__refused` | gone: the import's report lists are `.nx-excel__problems` |
|
|
281
|
+
|
|
282
|
+
- **Bulk insert, and rows added to an import (the owner, 2026-10-07).** `ExcelImport` and `openExcelImport` take
|
|
283
|
+
`mode: "bulk"`: the import with no file, its new rows' editor opening with one empty row for the person to type
|
|
284
|
+
into, sent to the same `Excel/Import` with the same checks, a problem then naming its row ("Row 3 · Region name").
|
|
285
|
+
`DataTable` offers it as **Bulk insert** beside **Import from a spreadsheet**, and only beside an Add, as it is an Add
|
|
286
|
+
of many rows (`TableExcel.bulk: false` leaves it out). A sheet with a required list cannot be typed in - a list's key
|
|
287
|
+
is picked in the spreadsheet - and the panel says so; its file review takes no added rows either. Any other file's
|
|
288
|
+
new rows take Add, Insert and Duplicate: an added row is numbered after every row the file had
|
|
289
|
+
(`excelSheetRows(rows, columns, after)`), and one left empty is dropped, not refused. The new rows no longer fold
|
|
290
|
+
away (folding unmounted the editor and lost every edit), the footer counts what will be sent, and the review is inert
|
|
291
|
+
while an import is out.
|
|
292
|
+
- **Nothing typed is lost unasked.** Closing the window, or Start over, over rows typed or changed in it asks first
|
|
293
|
+
(`ExcelImportHandle.unsavedRows`); picking the entry again while its window is minimized brings that window back
|
|
294
|
+
rather than a fresh one. An untouched date or time is no edit: `toInputValue` reads them as their inputs do.
|
|
295
|
+
- **An import that wrote nothing keeps its rows** and lists under them each refusal, by row, in the backend's words:
|
|
296
|
+
`readExcelResponse` keeps a refusal's report (`result`, as XLS_X044 sends it) instead of dropping it, so the panel no
|
|
297
|
+
longer says only "No Region was added". Refusals read in row order.
|
|
298
|
+
- `ExcelEditorRow`'s three own fields are optional, as an added row has none; new helpers `excelRowNumber`,
|
|
299
|
+
`hasExcelValues` and `isExcelRowTyped`, and `excelProblemPlace(problem, byRow)`. `RowsInputHandle.remove(index,
|
|
300
|
+
{ undo: false })` drops a row with no Undo offered, and keeps the person's own last Undo where it was.
|
|
301
|
+
- **A list shows what an import wrote.** After an import, `DataTable` runs `excel.onImported` first and reads its rows
|
|
302
|
+
after it: a `Crud` drops its cached lists there, and the reload used to be asked before, so on a page just opened it
|
|
303
|
+
was answered from the cache and the imported rows stayed off the screen until a reload.
|
|
304
|
+
- **What imported records start as.** `ExcelSettings` gains `recordStatuses` (`ExcelRecordStatus`: `value`, `label`)
|
|
305
|
+
and `defaultRecordStatus`; when a model offers any, the import shows "New records start as", the default picked,
|
|
306
|
+
and sends the choice (`importExcelRows(..., { recordStatus })`; `ExcelBackend.importRequest(rows, recordStatus?)`,
|
|
307
|
+
the Nexus one sending `{ rows, recordStatus }`). The report says "3 cities added as In review".
|
|
308
|
+
- **`Crud`, from the owner's review on 2026-10-07.** The active list is labelled **Active**, not "All" - it leaves out
|
|
309
|
+
the trash and the deleted. **Recover asks first** ("Recover Asia?", "It goes back to the active list."), one record
|
|
310
|
+
or many, like trash and delete. Recover and Move back to trash wear the back arrow (`iconArrowBackUp`): the
|
|
311
|
+
clock-shaped restore icon read as History beside a page's history action.
|
|
312
|
+
- **`details.actions`**: buttons of the page's own in the details popup's footer, before Edit and Close
|
|
313
|
+
(`CrudDetailsAction`: `id`, `label`, `icon`, `endpoint`, `hidden`, `onSelect(row, popup)`). One that names an
|
|
314
|
+
`endpoint` shows only to a user who may call it, by the rule every built-in action follows.
|
|
315
|
+
- **The spreadsheet import, as the owner asked on 2026-10-07.** Once a file is picked, the steps and the template and
|
|
316
|
+
file buttons give way to the file's name; another file is a Start over (now a bordered button) or a fresh popup. The
|
|
317
|
+
rows already there are read-only and never sent, and the footer says they are skipped. The new rows have Remove and
|
|
318
|
+
Lock at their end. After an import the review is gone: the panel shows what was added, what was not written and why,
|
|
319
|
+
and what was skipped and why. The `nx-excel__refused` list is gone with it; the report's lists are
|
|
320
|
+
`nx-excel__problems` blocks.
|
|
321
|
+
- **`TabularForm` `actionsAt="end"` and `lockable`.** The row's actions in a column fixed at the right edge, as a data
|
|
322
|
+
table has them: Remove and Lock as buttons of their own, anything else in a menu after them. A locked row's inputs
|
|
323
|
+
are read-only ("Locked: unlock the row to change it.") and it cannot be removed; the locks are the screen's, never
|
|
324
|
+
the value's.
|
|
325
|
+
- **A tabular form no longer jumps when a cell is clicked.** The line under the table, which holds the hint or the
|
|
326
|
+
message of the input with focus, keeps its height whether or not it says anything.
|
|
327
|
+
- **A slow answer lands in the list the user moved to.** `Crud` writes an action's answer into the table only when
|
|
328
|
+
the table still holds the list the action was asked from, fully loaded. Otherwise (the user switched lists while
|
|
329
|
+
the request was out, or a list is still loading) it patches every cached list and reads the one on screen again from
|
|
330
|
+
them, stopping a load that may have been asked before the change. Before, a record trashed from the active list and
|
|
331
|
+
answered after a switch to the trash was written over by the stale trash list and showed in neither list until the
|
|
332
|
+
page was opened again. The same rule covers `handle.apply`, Add, Edit and the read-back after an Add; a Create that
|
|
333
|
+
answers no key now loads the list instead of showing the cached one.
|
|
334
|
+
- **The theme panel is a quick preference panel.** Under the theme, `ThemeSwitcher` now shows `PreferencesPicker`
|
|
335
|
+
(`preferences={false}` leaves it out): Language and region (interface language, calendar), Dates and times (date
|
|
336
|
+
picker language, clock, display date format: the existing `DatePreferences`), Numbers (number scale, thousands
|
|
337
|
+
separator) and Tables (default page size, a row click opens the record). New: `UserPreferences`,
|
|
338
|
+
`readUserPreferences`, `setUserPreferences`, `useUserPreferences`, `subscribeUserPreferences`, `useNumberLocale`,
|
|
339
|
+
`DEFAULT_USER_PREFERENCES`, `PREFERENCE_STORAGE_KEYS` (`nx-language`, `nx-number-scale`, `nx-thousands`,
|
|
340
|
+
`nx-page-size`, `nx-row-click`), `InterfaceLanguage`, `ThousandsSeparator`, and `ENumberScales` (the core's enum, by
|
|
341
|
+
member value).
|
|
342
|
+
- **Numbers follow the person's settings.** `numberLocale(scale, separator)` gives `en-US`/`en-IN` or a key such as
|
|
343
|
+
`"en-IN|.|,"` that `formatNumber`, `parseNumber` and `getNumberSymbols` read. `NumberBox` and `Slider` use it when
|
|
344
|
+
they have no `locale` (they defaulted to `en-US`, which is still the setting's default), and so do a `DataTable`'s and
|
|
345
|
+
`CrudDetails`' number cells (`CellTextOptions.locale`, new). A number box rewrites its text when the setting changes.
|
|
346
|
+
- **`DataTable` `pageSize` and `openOnClick` give way to the person's choice once it is made**: the default page size
|
|
347
|
+
(when the table offers it) and the row click. A page size picked in a table with a `stateKey` still wins, and that
|
|
348
|
+
table now saves a size only when it differs from the one it opens with, so a later default reaches it.
|
|
349
|
+
- **Animations: On, Off or System, chosen in the theme panel.** `ThemeScript` now writes `data-nx-motion`
|
|
350
|
+
(`reduce` or `full`) on `<html>` before the first paint, from the visitor's choice (`data-nx-animations`, saved as
|
|
351
|
+
`nx-animations`) and the system's `prefers-reduced-motion`, and keeps it current when the system setting changes.
|
|
352
|
+
**Every stylesheet reads that attribute instead of the media query**, and so does script motion (the popup's
|
|
353
|
+
minimize and maximize, smooth scrolls). On animates on a machine whose operating system turned animations off; Off
|
|
354
|
+
never animates. An app that does not render `ThemeScript` animates whatever the system says. New: `AnimationMode`,
|
|
355
|
+
`setAnimations`, `motionFor`, `REDUCED_MOTION_QUERY`, the `ANIMATIONS_*` and `MOTION_ATTRIBUTE` constants, and
|
|
356
|
+
`useTheme()`'s `animations`, `motion` and `setAnimations`.
|
|
357
|
+
- **Density: Compact or Cozy.** `data-nx-density` on `<html>` (`nx-density`; `setDensity`, `useTheme().density`).
|
|
358
|
+
Cozy makes `--nx-control-sm/md/lg` 32/38/44 and the new `--nx-row` 38; compact is exactly the old sizes. Table rows
|
|
359
|
+
and headers, menu items, dropdown options and the Columns panel's lines are now sized from `--nx-row`, so they
|
|
360
|
+
follow it. `ThemeScript` takes `defaultDensity`.
|
|
361
|
+
- **Scale grid, development builds only**: Off, px (a line every 4px, solid every 48px) or mm (millimetres, solid every
|
|
362
|
+
centimetre), drawn over the window on `[data-nx-grid] body::after`. `ThemeScript`'s `grid` and `ThemePicker`'s
|
|
363
|
+
`showGrid` default to `isDevelopmentBuild()`, so a production build neither shows the section nor applies a saved
|
|
364
|
+
grid. New: `ScaleGrid`, `setScaleGrid`, `isDevelopmentBuild`.
|
|
365
|
+
- **`ThemePicker` / `ThemeSwitcher`** add Density, Animations and Scale grid under Mode and Theme (`showDensity`,
|
|
366
|
+
`showAnimations`, `showGrid`).
|
|
367
|
+
- **Popups fade in and out** (160ms in, 120ms out), with nothing else moving: the slide and scale are gone. **Every
|
|
368
|
+
popover panel fades** in and out in 120ms through one rule on `.nx-popover`; the menu's, the table panels' and the
|
|
369
|
+
dropdown's own opening animations are gone. A closing panel takes no clicks, and nothing waits for a fade.
|
|
370
|
+
- **Popover panels fit the window.** `placeUnderAnchor` and `placeAtPoint` pick the side that fits, or the one with
|
|
371
|
+
more room, and write that room as `--nx-popover-room`; a panel that marks its scrolling part `.nx-popover__scroll`
|
|
372
|
+
is never taller than it. That part reaches the panel's right wall, so its scroll bar sits on the wall, and carries
|
|
373
|
+
the right padding inside. Menus, the theme panel and the table's Columns and Filters panels use it; `usePopoverPanel`
|
|
374
|
+
lost its own height cap. New `showPanel` opens a panel scrolled to the top, and a hovered menu item no longer scrolls
|
|
375
|
+
its list. Placing an open panel again (on every scroll) no longer throws its list back to the top.
|
|
376
|
+
- **A panel's padding is `--nx-popover-pad`.** `.nx-popover`'s own padding loaded after the menu's and the table
|
|
377
|
+
panels', so a menu was 16px inside its edge, not the 4px it asked for, and the table panels 16px, not 8px; both now
|
|
378
|
+
get theirs. `.nx-table-panel__body` is gone (`.nx-popover__scroll`).
|
|
379
|
+
- **One thin, quiet scroll bar everywhere, the design lab's.** `Nexus.Base.css` gives every element
|
|
380
|
+
`scrollbar-width: thin` and a thumb of the text colour at 22% on no track. The theme panel, menus and context menus,
|
|
381
|
+
the table's Columns and Filters panels, popup bodies, the date-time panel, the chat, code blocks and tables in
|
|
382
|
+
Markdown and rich text, and the admin page and menu drew the browser's 15px bar with a painted grey track; on
|
|
383
|
+
Windows, Chrome and Edge now draw every bar 10px wide, as the lab does (Firefox's thin bar is 8px; Safari draws its
|
|
384
|
+
own thin bar). The components' own bars are gone: the 8px `::-webkit-scrollbar` pill of the text area, the Markdown
|
|
385
|
+
preview and the rich text (Chromium and WebKit ignore it once `scrollbar-width` is set), and the thin bar with a
|
|
386
|
+
stronger thumb of the tree, the tabular form, the dropdown list and the time picker. The data table's rows keep
|
|
387
|
+
theirs (`Nexus.Table.css`) until a later change, and the time picker's columns still hide the thumb until the
|
|
388
|
+
pointer or the keys are in them. `--nx-field-scrollbar`, `--nx-field-scrollbar-hover`, `--nx-dropdown-scrollbar` and
|
|
389
|
+
`--nx-time-scrollbar` are gone; the text area's resize grip reads `--nx-field-grip`. Room kept for a bar narrows with
|
|
390
|
+
it: every `scrollbar-gutter: stable` (the admin page and its bar, whose right edge moves from 27 to 22px off the
|
|
391
|
+
window, the chat thread, the dropdown list) and the padding a modal popup keeps for the page's bar
|
|
392
|
+
(`--nx-scrollbar-gap`, measured).
|
|
393
|
+
- **A form whose values load shows its real inputs, disabled, instead of placeholders.** A `Crud`'s Edit popup, an
|
|
394
|
+
`ApiForm` with `loadValue` and a `Form` with `loading` have their final size from the first frame, and the values
|
|
395
|
+
land in the same inputs, as a load (no "Unsaved changes", no "Discard your changes?"). While loading, `FormStore`
|
|
396
|
+
takes nothing typed, even from an input whose `disabled` was taken off by hand, and draws such an input again from
|
|
397
|
+
the store when loading ends; a submit already waited. The `.nx-form__skeleton*` classes and their animation are gone.
|
|
398
|
+
- **The form's status line keeps its one line when empty, and a form's bar fills a popup's foot**, so the status sits
|
|
399
|
+
beside the buttons: "Loading…" or "Unsaved changes" coming and going no longer moves the bar, and a `Crud` popup
|
|
400
|
+
lost the empty 12px row it had above its buttons.
|
|
401
|
+
- **`Avatar`** (`nexus-shared/client`): a person's round picture, or their initials on a soft tone without one or
|
|
402
|
+
when it fails to load. `size` `sm`/`md`/`lg` or `--nx-avatar-size`; `onChangePicture` makes it a button with a
|
|
403
|
+
camera over it on hover. The chat's and the dropdown's avatars are now this one: `ChatAvatar` and
|
|
404
|
+
`DropdownAvatar` still exist and draw it, `.nx-chat-avatar` is gone (`.nx-avatar`), `.nx-dropdown-avatar` stays
|
|
405
|
+
as the dropdown's size, and `--nx-chat-avatar` is `--nx-avatar-size`.
|
|
406
|
+
- **`Button` `dot`**: a small dot on the corner, for something new behind it such as unread notifications; `"primary"`
|
|
407
|
+
marks a state that is on, such as filters.
|
|
408
|
+
- **`Menu`: a control inside a heading is chosen like a row.** An entry `{ heading: <node> }` may hold a button with
|
|
409
|
+
`role="menuitem"`: the arrow keys walk it with the rows, and a click on it closes the menu. A menu opened from the
|
|
410
|
+
keyboard still starts on its first row.
|
|
411
|
+
- **The data table is one frame**: the bar, the rows and the footer sit in one border (`.nx-table` carries it now,
|
|
412
|
+
not `.nx-table__frame`), with a line under the bar and the footer as the bottom band (`--nx-footbar`, a new
|
|
413
|
+
token). An empty bar takes no room. Column headings are 12px, weight 650.
|
|
414
|
+
- **`DataTable` `add`**: the table's own Add, first in the bar. With `excel`, it is a split button whose arrow holds
|
|
415
|
+
Download template, Import from a spreadsheet and Export to Excel, which then leave the options menu. `Crud`
|
|
416
|
+
passes its Add this way.
|
|
417
|
+
- **The data table's bar is one line on a wide table, in a new order**: the Add section (`add`, then the page's
|
|
418
|
+
`actions`) and the lists (`toolbar`) on the left; Reload, the options menu (⋮) and the search, rows per page inside
|
|
419
|
+
it, on the right. It was the lists, the search, Reload, ⋮, `actions` and Add, wrapping where they fell: at 1126px
|
|
420
|
+
on the admin shell the bar broke into three ragged rows. A narrower table now moves whole parts, by its own width
|
|
421
|
+
(container queries on `.nx-table`): under 670px the search takes the top line (with nothing on the left, it leads
|
|
422
|
+
the buttons' line), under 375px Reload moves to the end of ⋮ (`DataTable` asks the hidden button, so the width is
|
|
423
|
+
written once, in the stylesheet), and under 325px, beside lists, the Add section takes the last line with its buttons
|
|
424
|
+
sharing it. On the admin shell those are windows of 695, 400 and 350px. A bar wider than those widths expect (a
|
|
425
|
+
long Add label, the page's own buttons, cozy density) is squeezed into the next step as soon as a line would break
|
|
426
|
+
(`data-squeeze` on `.nx-table__bar`, set a frame after a size change). A `toolbar` or `actions` that renders
|
|
427
|
+
nothing takes no room and decides no step, and a bar left with nothing else is hidden. New classes
|
|
428
|
+
`.nx-table__bar-add` (Add and `actions`) and `.nx-table__reload`; the search is now a child of `.nx-table__bar` after
|
|
429
|
+
`.nx-table__bar-end`, not inside it, and grows to 19rem from a 12rem basis.
|
|
430
|
+
- **Columns opens from the options menu (⋮)**, as its first item; the bar has no Columns button. Reload, Columns
|
|
431
|
+
and the options button are ghost buttons. `TableColumnsPanel` takes an `anchor` and an `open()` handle for this.
|
|
432
|
+
- **`TabButtons` `labels="auto"`**: every label while its container (a data table) is at least 900px wide, only the
|
|
433
|
+
chosen one's below. `Crud`'s lists use it.
|
|
434
|
+
- **`DataTable` `selectBy`**: while rows are selected, their count opens a menu that selects again by what the rows
|
|
435
|
+
are: All on this page, then each `{ label, icon, test }`, which leaves exactly the matching rows on the page
|
|
436
|
+
selected. `Crud`'s main list offers Pinned, Not pinned, Flagged and Not flagged.
|
|
437
|
+
- **`SplitButton` `fixed`**: the main button never changes; the menu holds other ways to do it, run when picked.
|
|
438
|
+
- **`DataTable` `filterInputs`: an advanced filter.** Input params, each named after the column it filters, become
|
|
439
|
+
**Filters** in the options menu (⋮): a small form under the button, applied as it changes. The values are
|
|
440
|
+
`TableQuery.filters` (`TableStore.setFilters` sets them, `{}` clears); a client table matches them in `queryRows`
|
|
441
|
+
(a fifth argument), and a server gets them flat by name through `CrudBackend.pageRequest`'s third argument, as
|
|
442
|
+
`filterRequest` writes them (a date range as `{name}From`/`{name}Before`). A `CrudBackend` or loader of your own
|
|
443
|
+
ignores them until it reads that argument or `query.filters`. Also new: `activeFilters`, `asFilterInput`,
|
|
444
|
+
`filterMatcher`, `filterNames`; `usePopoverPanel` and `PopoverPanelHandle` (`nexus-shared/client`), the panel
|
|
445
|
+
Filters and Columns share; `followAnchor`'s second argument, a panel whose size it follows too.
|
|
446
|
+
- **Renamed: `.nx-columns__head`, `__title`, `__count` and `__foot` are `.nx-table-panel__*`**, shared by both panels.
|
|
447
|
+
An override of the old names no longer applies.
|
|
448
|
+
|
|
7
449
|
## 3.0.0
|
|
8
450
|
|
|
9
451
|
**Breaking. This changes the value of exported constants, and `2.0.0` is on npm with the old ones.** Publishing it
|