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
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { iconMinus, iconSquare, iconSquares, iconX, InlineIcon } from "nexus-icons";
|
|
4
4
|
import { useContext, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
|
5
5
|
import { flushSync } from "react-dom";
|
|
6
|
-
import { afterAnimation } from "../../Helpers/AnimationHelpers.js";
|
|
6
|
+
import { afterAnimation, motionAllowed } from "../../Helpers/AnimationHelpers.js";
|
|
7
7
|
import { cx } from "../../Helpers/ClassHelpers.js";
|
|
8
8
|
import { cssVariablePosition, DRAG_CANCEL_SELECTOR, moveWithin, startDrag, viewportBounds } from "../../Helpers/DragHelpers.js";
|
|
9
9
|
import { HostedPopupContext } from "./PopupContext.js";
|
|
@@ -12,9 +12,6 @@ const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]):not([t
|
|
|
12
12
|
// Where a minimized popup flies to: the newest bar of the dock, at the bottom right.
|
|
13
13
|
const DOCK_EDGE = 16;
|
|
14
14
|
const DOCK_BAR = { width: 300, height: 44 };
|
|
15
|
-
function motionAllowed() {
|
|
16
|
-
return !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
17
|
-
}
|
|
18
15
|
function popupPosition(dialog) {
|
|
19
16
|
return cssVariablePosition(dialog, "--nx-popup-x", "--nx-popup-y");
|
|
20
17
|
}
|
|
@@ -4,10 +4,28 @@ export interface ThemePickerProps {
|
|
|
4
4
|
themes?: readonly ThemeOption[];
|
|
5
5
|
/** Show the Light / Dark / System choice. Default `true`. */
|
|
6
6
|
showMode?: boolean;
|
|
7
|
+
/** Show Compact / Cozy. Default `true`. */
|
|
8
|
+
showDensity?: boolean;
|
|
9
|
+
/** Show Animations: On / Off / System. Default `true`. */
|
|
10
|
+
showAnimations?: boolean;
|
|
11
|
+
/** Show the scale grid, a development tool. Default: in a development build only. */
|
|
12
|
+
showGrid?: boolean;
|
|
7
13
|
className?: string;
|
|
8
14
|
}
|
|
9
15
|
/**
|
|
10
|
-
* Light / Dark / System
|
|
11
|
-
*
|
|
16
|
+
* Two sections: the theme (Light / Dark / System and a card per theme with a live preview of its colors) and the
|
|
17
|
+
* appearance (the density, the animations and, in a development build, the scale grid). Each choice applies at once
|
|
18
|
+
* and is saved in the browser.
|
|
12
19
|
*/
|
|
13
|
-
export declare function ThemePicker({ themes, showMode, className }: ThemePickerProps): import("react").JSX.Element;
|
|
20
|
+
export declare function ThemePicker({ themes, showMode, showDensity, showAnimations, showGrid, className }: ThemePickerProps): import("react").JSX.Element;
|
|
21
|
+
export interface PreferencesPickerProps {
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The person's quick preferences, each applied at once and saved on this device: the interface language and the
|
|
26
|
+
* calendar; the date picker's language, the clock and the date format (the date settings every date component reads);
|
|
27
|
+
* the number scale and the thousands separator (every number box, slider and table number without a locale of its
|
|
28
|
+
* own); and the page size tables open with and whether a row click opens a record (every table, once chosen, over what
|
|
29
|
+
* the table names itself). `ThemeSwitcher` shows it under the theme.
|
|
30
|
+
*/
|
|
31
|
+
export declare function PreferencesPicker({ className }: PreferencesPickerProps): import("react").JSX.Element;
|
|
@@ -1,22 +1,105 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { iconCheck, iconDeviceDesktop, iconMoon, iconSun, InlineIcon } from "nexus-icons";
|
|
3
|
+
import { iconArrowsMaximize, iconArrowsMinimize, iconCheck, iconCircleCheck, iconCircleX, iconDeviceDesktop, iconEyeOff, iconLayoutGrid, iconMoon, iconSun, iconTable, InlineIcon, } from "nexus-icons";
|
|
4
4
|
import { useId } from "react";
|
|
5
5
|
import { cx } from "../../Helpers/ClassHelpers.js";
|
|
6
|
+
import { formatDate } from "../../Helpers/DateHelpers.js";
|
|
7
|
+
import { setDatePreferences, useDatePreferences } from "../../Helpers/DatePreferences.js";
|
|
8
|
+
import { DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES } from "../../Helpers/TableHelpers.js";
|
|
9
|
+
import { isDevelopmentBuild } from "../../Helpers/ThemeHelpers.js";
|
|
10
|
+
import { setUserPreferences, useUserPreferences } from "../../Helpers/UserPreferences.js";
|
|
11
|
+
import { DATE_FORMATS, DEFAULT_TIME_FORMAT } from "../../Interfaces/DateInterfaces.js";
|
|
12
|
+
import { ENumberScales } from "../../Interfaces/PreferenceInterfaces.js";
|
|
6
13
|
import { NEXUS_THEMES } from "../../Interfaces/ThemeInterfaces.js";
|
|
7
14
|
import { useTheme } from "../../Services/ThemeService.js";
|
|
8
15
|
import { TabButtons } from "../Documents/TabButtons.js";
|
|
16
|
+
import { Switch } from "../Inputs/CheckBox.js";
|
|
17
|
+
import { Dropdown } from "../Inputs/Dropdown.js";
|
|
18
|
+
import { TextBox } from "../Inputs/TextBox.js";
|
|
9
19
|
const MODES = [
|
|
10
20
|
{ value: "light", label: "Light", icon: _jsx(InlineIcon, { icon: iconSun }) },
|
|
11
21
|
{ value: "dark", label: "Dark", icon: _jsx(InlineIcon, { icon: iconMoon }) },
|
|
12
22
|
{ value: "system", label: "System", icon: _jsx(InlineIcon, { icon: iconDeviceDesktop }), title: "Follow the operating system" },
|
|
13
23
|
];
|
|
24
|
+
const DENSITIES = [
|
|
25
|
+
{ value: "compact", label: "Compact", icon: _jsx(InlineIcon, { icon: iconArrowsMinimize }) },
|
|
26
|
+
{ value: "cozy", label: "Cozy", icon: _jsx(InlineIcon, { icon: iconArrowsMaximize }), title: "Taller controls and rows, for touch screens and large monitors" },
|
|
27
|
+
];
|
|
28
|
+
const ANIMATIONS = [
|
|
29
|
+
{ value: "on", label: "On", icon: _jsx(InlineIcon, { icon: iconCircleCheck }), title: "Animate, even when the operating system asks for less motion" },
|
|
30
|
+
{ value: "off", label: "Off", icon: _jsx(InlineIcon, { icon: iconCircleX }), title: "Nothing moves" },
|
|
31
|
+
{ value: "system", label: "System", icon: _jsx(InlineIcon, { icon: iconDeviceDesktop }), title: "Follow the operating system's animations setting" },
|
|
32
|
+
];
|
|
33
|
+
const GRIDS = [
|
|
34
|
+
{ value: "off", label: "Off", icon: _jsx(InlineIcon, { icon: iconEyeOff }) },
|
|
35
|
+
{ value: "px", label: "px", icon: _jsx(InlineIcon, { icon: iconLayoutGrid }), title: "A line every 4px, a solid one every 48px" },
|
|
36
|
+
{ value: "cm", label: "mm", icon: _jsx(InlineIcon, { icon: iconTable }), title: "A line every millimetre, a solid one every centimetre" },
|
|
37
|
+
];
|
|
38
|
+
/** A small heading and what it names: a row of tab buttons by default, or the control given. */
|
|
39
|
+
function Choice({ id, legend, options, value, onChange, children }) {
|
|
40
|
+
return (_jsxs("div", { className: "nx-theme-picker__group", children: [_jsx("p", { id: id, className: "nx-theme-picker__legend", children: legend }), children ?? (options && value !== undefined && onChange && _jsx(TabButtons, { "aria-labelledby": id, name: id, options: options, value: value, onChange: onChange, block: true }))] }));
|
|
41
|
+
}
|
|
42
|
+
/** A group of settings under its name: one section of the panel, a line above it. */
|
|
43
|
+
function Section({ title, wide, children }) {
|
|
44
|
+
const id = useId();
|
|
45
|
+
return (_jsxs("section", { className: cx("nx-theme-picker__block", wide && "nx-theme-picker__block--wide"), "aria-labelledby": id, children: [_jsx("h3", { id: id, className: "nx-theme-picker__section", children: title }), children] }));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Two sections: the theme (Light / Dark / System and a card per theme with a live preview of its colors) and the
|
|
49
|
+
* appearance (the density, the animations and, in a development build, the scale grid). Each choice applies at once
|
|
50
|
+
* and is saved in the browser.
|
|
51
|
+
*/
|
|
52
|
+
export function ThemePicker({ themes = NEXUS_THEMES, showMode = true, showDensity = true, showAnimations = true, showGrid = isDevelopmentBuild(), className }) {
|
|
53
|
+
const { theme, mode, density, animations, grid, setTheme, setMode, setDensity, setAnimations, setGrid } = useTheme();
|
|
54
|
+
const name = useId();
|
|
55
|
+
return (_jsxs("div", { className: cx("nx-theme-picker", className), children: [_jsxs(Section, { title: "Theme", wide: true, children: [showMode && _jsx(Choice, { id: `${name}-mode`, legend: "Mode", options: MODES, value: mode, onChange: setMode }), _jsxs("fieldset", { className: "nx-theme-picker__group", children: [_jsx("legend", { className: "nx-theme-picker__legend", children: "Colors" }), _jsx("div", { className: "nx-theme-picker__grid", children: themes.map(option => (_jsxs("label", { className: "nx-theme-option", title: option.description, children: [_jsx("input", { type: "radio", className: "nx-visually-hidden", name: `${name}-theme`, value: option.id, checked: theme === option.id, onChange: () => setTheme(option.id) }), _jsx("span", { className: "nx-theme-option__preview", "data-nx-theme": option.id, "aria-hidden": "true", children: _jsxs("span", { className: "nx-theme-option__window", children: [_jsx("span", { className: "nx-theme-option__line" }), _jsx("span", { className: "nx-theme-option__line nx-theme-option__line--muted" }), _jsxs("span", { className: "nx-theme-option__swatches", children: [_jsx("span", { className: "nx-theme-option__pill" }), _jsx("span", { className: "nx-theme-option__dot nx-theme-option__dot--success" }), _jsx("span", { className: "nx-theme-option__dot nx-theme-option__dot--warning" }), _jsx("span", { className: "nx-theme-option__dot nx-theme-option__dot--danger" })] })] }) }), _jsxs("span", { className: "nx-theme-option__name", children: [option.name, _jsx(InlineIcon, { icon: iconCheck, className: "nx-theme-option__check" })] })] }, option.id))) })] })] }), (showDensity || showAnimations || showGrid) && (_jsxs(Section, { title: "Appearance", children: [showDensity && _jsx(Choice, { id: `${name}-density`, legend: "Density", options: DENSITIES, value: density, onChange: setDensity }), showAnimations && _jsx(Choice, { id: `${name}-animations`, legend: "Animations", options: ANIMATIONS, value: animations, onChange: setAnimations }), showGrid && _jsx(Choice, { id: `${name}-grid`, legend: "Scale grid", options: GRIDS, value: grid, onChange: setGrid })] }))] }));
|
|
56
|
+
}
|
|
57
|
+
/* The quick preferences: language, dates, numbers and tables, beside the theme. */
|
|
58
|
+
const LANGUAGES = [
|
|
59
|
+
{ value: "en", label: "English" },
|
|
60
|
+
{ value: "ne", label: _jsx("span", { lang: "ne", children: "\u0928\u0947\u092A\u093E\u0932\u0940" }), title: "Nepali" },
|
|
61
|
+
];
|
|
62
|
+
const CALENDARS = [
|
|
63
|
+
{ value: "ad", label: "AD", title: "Gregorian (AD)" },
|
|
64
|
+
{ value: "bs", label: "BS", title: "Bikram Sambat (BS)" },
|
|
65
|
+
];
|
|
66
|
+
const SCRIPTS = [
|
|
67
|
+
{ value: "latin", label: "English", title: "Month names and digits in English" },
|
|
68
|
+
{ value: "devanagari", label: _jsx("span", { lang: "ne", children: "\u0928\u0947\u092A\u093E\u0932\u0940" }), title: "Month names and digits in Nepali" },
|
|
69
|
+
];
|
|
70
|
+
const CLOCKS = [
|
|
71
|
+
{ value: "12", label: "12-hour", title: "2:30 PM" },
|
|
72
|
+
{ value: "24", label: "24-hour", title: "14:30" },
|
|
73
|
+
];
|
|
74
|
+
const SCALES = [
|
|
75
|
+
{ value: String(ENumberScales.International), label: "International", title: "1,234,567: thousand, million, billion" },
|
|
76
|
+
{ value: String(ENumberScales.SouthAsian), label: "South Asian", title: "12,34,567: thousand, lakh, crore" },
|
|
77
|
+
];
|
|
78
|
+
const SEPARATORS = [
|
|
79
|
+
{ value: "comma", label: "1,200", title: "Comma" },
|
|
80
|
+
{ value: "period", label: "1.200", title: "Period, with a comma for decimals" },
|
|
81
|
+
{ value: "space", label: "1\u00a0200", title: "Space" },
|
|
82
|
+
{ value: "none", label: "1200", title: "None" },
|
|
83
|
+
];
|
|
84
|
+
const PAGE_SIZES = DEFAULT_PAGE_SIZES.map(size => ({ value: size, label: `${size} rows` }));
|
|
85
|
+
const DESIGNS = [
|
|
86
|
+
{ value: "standard", label: "Standard", title: "The label above the box" },
|
|
87
|
+
{ value: "outlined", label: "Outlined", title: "The label on the box's edge" },
|
|
88
|
+
{ value: "inbox", label: "Inbox", title: "The label inside the top of the box" },
|
|
89
|
+
];
|
|
90
|
+
// A fixed day, so the server and the browser write the same examples, and one past the 12th, so day and month differ.
|
|
91
|
+
const SAMPLE_DATE = "2026-09-27";
|
|
14
92
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
93
|
+
* The person's quick preferences, each applied at once and saved on this device: the interface language and the
|
|
94
|
+
* calendar; the date picker's language, the clock and the date format (the date settings every date component reads);
|
|
95
|
+
* the number scale and the thousands separator (every number box, slider and table number without a locale of its
|
|
96
|
+
* own); and the page size tables open with and whether a row click opens a record (every table, once chosen, over what
|
|
97
|
+
* the table names itself). `ThemeSwitcher` shows it under the theme.
|
|
17
98
|
*/
|
|
18
|
-
export function
|
|
19
|
-
const
|
|
99
|
+
export function PreferencesPicker({ className }) {
|
|
100
|
+
const dates = useDatePreferences();
|
|
101
|
+
const preferences = useUserPreferences();
|
|
20
102
|
const name = useId();
|
|
21
|
-
|
|
103
|
+
const formats = DATE_FORMATS.map(format => ({ value: format, label: formatDate(SAMPLE_DATE, { ...dates, format }), description: format }));
|
|
104
|
+
return (_jsxs("div", { className: cx("nx-theme-picker", className), children: [_jsxs(Section, { title: "Language and region", children: [_jsx(Choice, { id: `${name}-language`, legend: "Interface language", options: LANGUAGES, value: preferences.language, onChange: language => setUserPreferences({ language }) }), _jsx(Choice, { id: `${name}-calendar`, legend: "Calendar", options: CALENDARS, value: dates.calendar, onChange: calendar => setDatePreferences({ calendar }) })] }), _jsxs(Section, { title: "Dates and times", children: [_jsx(Choice, { id: `${name}-script`, legend: "Date picker language", options: SCRIPTS, value: dates.script, onChange: script => setDatePreferences({ script }) }), _jsx(Choice, { id: `${name}-clock`, legend: "Clock", options: CLOCKS, value: /H/.test(dates.timeFormat) ? "24" : "12", onChange: clock => setDatePreferences({ timeFormat: clock === "24" ? "HH:mm" : DEFAULT_TIME_FORMAT }) }), _jsx(Choice, { id: `${name}-format`, legend: "Display date format", children: _jsx(Dropdown, { "aria-labelledby": `${name}-format`, options: formats, value: dates.format, clearable: false, searchable: false, onValueChange: format => format && setDatePreferences({ format }) }) })] }), _jsxs(Section, { title: "Numbers", children: [_jsx(Choice, { id: `${name}-scale`, legend: "Number scale", options: SCALES, value: String(preferences.numberScale), onChange: scale => setUserPreferences({ numberScale: Number(scale) === ENumberScales.SouthAsian ? ENumberScales.SouthAsian : ENumberScales.International }) }), _jsx(Choice, { id: `${name}-thousands`, legend: "Thousands separator", options: SEPARATORS, value: preferences.thousands, onChange: thousands => setUserPreferences({ thousands }) })] }), _jsxs(Section, { title: "Tables", children: [_jsx(Choice, { id: `${name}-page-size`, legend: "Default page size", children: _jsx(Dropdown, { "aria-labelledby": `${name}-page-size`, options: PAGE_SIZES, value: preferences.pageSize ?? DEFAULT_PAGE_SIZE, clearable: false, searchable: false, onValueChange: pageSize => pageSize && setUserPreferences({ pageSize }) }) }), _jsx(Switch, { label: "A row click opens the record", labelPosition: "start", checked: preferences.rowClick ?? false, onValueChange: rowClick => setUserPreferences({ rowClick }) })] }), _jsxs(Section, { title: "Forms", children: [_jsx(Choice, { id: `${name}-design`, legend: "Input design", options: DESIGNS, value: preferences.inputDesign ?? "standard", onChange: inputDesign => setUserPreferences({ inputDesign }) }), _jsx(TextBox, { label: "Example", placeholder: "Type to try it" })] })] }));
|
|
22
105
|
}
|
|
@@ -4,8 +4,10 @@ export interface ThemeScriptProps extends ThemeScriptOptions {
|
|
|
4
4
|
nonce?: string;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Applies the saved theme
|
|
8
|
-
* and
|
|
7
|
+
* Applies the saved theme, mode, density, animations and (in a development build) scale grid before the first paint,
|
|
8
|
+
* and keeps `data-nx-motion` in step with the system's reduced-motion setting: the stylesheets read that attribute,
|
|
9
|
+
* not the media query, so an app without this script animates whatever the system says. Render it in `<head>` of the
|
|
10
|
+
* root layout, and put `suppressHydrationWarning` on `<html>`, because the script sets attributes there.
|
|
9
11
|
*
|
|
10
12
|
* @example
|
|
11
13
|
* <html lang="en" suppressHydrationWarning>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { themeScript } from "../../Helpers/ThemeHelpers.js";
|
|
3
3
|
/**
|
|
4
|
-
* Applies the saved theme
|
|
5
|
-
* and
|
|
4
|
+
* Applies the saved theme, mode, density, animations and (in a development build) scale grid before the first paint,
|
|
5
|
+
* and keeps `data-nx-motion` in step with the system's reduced-motion setting: the stylesheets read that attribute,
|
|
6
|
+
* not the media query, so an app without this script animates whatever the system says. Render it in `<head>` of the
|
|
7
|
+
* root layout, and put `suppressHydrationWarning` on `<html>`, because the script sets attributes there.
|
|
6
8
|
*
|
|
7
9
|
* @example
|
|
8
10
|
* <html lang="en" suppressHydrationWarning>
|
|
@@ -5,9 +5,12 @@ export interface ThemeSwitcherProps extends ThemePickerProps {
|
|
|
5
5
|
label?: string;
|
|
6
6
|
/** Default `ghost`. */
|
|
7
7
|
variant?: ButtonVariant;
|
|
8
|
+
/** The quick preferences under the theme: language, dates, numbers and tables (`PreferencesPicker`). Default true. */
|
|
9
|
+
preferences?: boolean;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
|
-
* A palette button that opens
|
|
11
|
-
*
|
|
12
|
+
* A palette button that opens a quick preference panel below it, with the panel's arrow on the button: the theme
|
|
13
|
+
* (`ThemePicker`) and, under it, the language, dates, numbers and tables (`PreferencesPicker`). The panel is a native
|
|
14
|
+
* popover: it closes with Esc or a click outside, stays inside the window and scrolls inside itself.
|
|
12
15
|
*/
|
|
13
|
-
export declare function ThemeSwitcher({ label, variant, className, ...pickerProps }: ThemeSwitcherProps): import("react").JSX.Element;
|
|
16
|
+
export declare function ThemeSwitcher({ label, variant, className, preferences, ...pickerProps }: ThemeSwitcherProps): import("react").JSX.Element;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { iconPalette, InlineIcon } from "nexus-icons";
|
|
4
4
|
import { useId, useLayoutEffect, useRef, useState } from "react";
|
|
5
|
-
import { followAnchor, isPopoverOpen, placeUnderAnchor } from "../../Helpers/PopoverHelpers.js";
|
|
5
|
+
import { followAnchor, isPopoverOpen, placeUnderAnchor, showPanel } from "../../Helpers/PopoverHelpers.js";
|
|
6
6
|
import { Button } from "../Documents/Button.js";
|
|
7
|
-
import { ThemePicker } from "./ThemePicker.js";
|
|
7
|
+
import { PreferencesPicker, ThemePicker } from "./ThemePicker.js";
|
|
8
8
|
/**
|
|
9
|
-
* A palette button that opens
|
|
10
|
-
*
|
|
9
|
+
* A palette button that opens a quick preference panel below it, with the panel's arrow on the button: the theme
|
|
10
|
+
* (`ThemePicker`) and, under it, the language, dates, numbers and tables (`PreferencesPicker`). The panel is a native
|
|
11
|
+
* popover: it closes with Esc or a click outside, stays inside the window and scrolls inside itself.
|
|
11
12
|
*/
|
|
12
|
-
export function ThemeSwitcher({ label = "Theme", variant = "ghost", className, ...pickerProps }) {
|
|
13
|
+
export function ThemeSwitcher({ label = "Theme", variant = "ghost", className, preferences = true, ...pickerProps }) {
|
|
13
14
|
const panelId = useId();
|
|
14
15
|
const buttonRef = useRef(null);
|
|
15
16
|
const panelRef = useRef(null);
|
|
@@ -42,8 +43,8 @@ export function ThemeSwitcher({ label = "Theme", variant = "ghost", className, .
|
|
|
42
43
|
else if (isPopoverOpen(panel))
|
|
43
44
|
panel?.hidePopover();
|
|
44
45
|
else if (panel && typeof panel.showPopover === "function") {
|
|
45
|
-
panel
|
|
46
|
+
showPanel(panel);
|
|
46
47
|
setOpen(true);
|
|
47
48
|
}
|
|
48
|
-
} }), _jsx("div", { ref: panelRef, id: panelId, popover: "auto", role: "dialog", "aria-label": label, className: "nx-popover nx-theme-switcher__panel", onToggle: onToggle, children: _jsx(ThemePicker, { ...pickerProps }) })] }));
|
|
49
|
+
} }), _jsx("div", { ref: panelRef, id: panelId, popover: "auto", role: "dialog", "aria-label": label, className: "nx-popover nx-theme-switcher__panel", onToggle: onToggle, children: _jsxs("div", { className: "nx-popover__scroll nx-theme-switcher__body", children: [_jsx(ThemePicker, { ...pickerProps }), preferences && _jsx(PreferencesPicker, {})] }) })] }));
|
|
49
50
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface AvatarProps {
|
|
2
|
+
/** Whose picture it is: the initials stand in without one, and the name picks their tone. */
|
|
3
|
+
name: string;
|
|
4
|
+
/** Address of the picture. Anything but a non-empty string is no picture. */
|
|
5
|
+
src?: unknown;
|
|
6
|
+
/** `sm` 22px, `md` 28px, `lg` 44px. Default `md`; `--nx-avatar-size` sets any other. */
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
8
|
+
className?: string;
|
|
9
|
+
/** Makes the picture a button that offers to change it: a camera shows over it on hover, and a click calls this. */
|
|
10
|
+
onChangePicture?: () => void;
|
|
11
|
+
/** The button's name. Default "Change picture". */
|
|
12
|
+
changeLabel?: string;
|
|
13
|
+
/** For the button inside a menu: `role="menuitem"` and `tabIndex={-1}` let the arrow keys reach it. */
|
|
14
|
+
role?: string;
|
|
15
|
+
tabIndex?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A person: a round picture, or their initials on a soft tone when there is none or it fails to load. The chat,
|
|
19
|
+
* the dropdown's avatar column and the admin bar all draw this one.
|
|
20
|
+
*/
|
|
21
|
+
export declare function Avatar({ name, src, size, className, onChangePicture, changeLabel, role, tabIndex }: AvatarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Icon } from "nexus-icons";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { cx } from "../../Helpers/ClassHelpers.js";
|
|
6
|
+
import { avatarTone, initials } from "../../Helpers/DropdownHelpers.js";
|
|
7
|
+
/**
|
|
8
|
+
* A person: a round picture, or their initials on a soft tone when there is none or it fails to load. The chat,
|
|
9
|
+
* the dropdown's avatar column and the admin bar all draw this one.
|
|
10
|
+
*/
|
|
11
|
+
export function Avatar({ name, src, size = "md", className, onChangePicture, changeLabel = "Change picture", role, tabIndex }) {
|
|
12
|
+
const url = typeof src === "string" ? src.trim() : "";
|
|
13
|
+
const [failed, setFailed] = useState(null);
|
|
14
|
+
const photo = url !== "" && failed !== url;
|
|
15
|
+
const face = photo ? _jsx("img", { src: url, alt: "", loading: "lazy", decoding: "async", onError: () => setFailed(url) }) : initials(name);
|
|
16
|
+
const shared = { className: cx("nx-avatar", size !== "md" && `nx-avatar--${size}`, className), "data-tone": photo ? undefined : avatarTone(name) };
|
|
17
|
+
if (!onChangePicture) {
|
|
18
|
+
return (_jsx("span", { ...shared, "aria-hidden": "true", children: face }));
|
|
19
|
+
}
|
|
20
|
+
return (_jsxs("button", { ...shared, type: "button", role: role, tabIndex: tabIndex, "aria-label": changeLabel, title: changeLabel, onClick: onChangePicture, children: [face, _jsx("span", { className: "nx-avatar__veil", "aria-hidden": "true", children: _jsx(Icon, { name: "camera", size: "40%" }) })] }));
|
|
21
|
+
}
|