nexus-shared 3.1.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +704 -446
  2. package/LICENSE +21 -21
  3. package/README.md +44 -5
  4. package/dist/Client.Index.d.ts +3 -0
  5. package/dist/Client.Index.js +3 -0
  6. package/dist/Components/Forms/Crud.js +322 -161
  7. package/dist/Components/Forms/CrudHistory.d.ts +66 -0
  8. package/dist/Components/Forms/CrudHistory.js +140 -0
  9. package/dist/Components/Forms/CrudParts.js +2 -1
  10. package/dist/Components/Forms/Form.js +4 -1
  11. package/dist/Components/Inputs/CodeBox.d.ts +69 -0
  12. package/dist/Components/Inputs/CodeBox.js +316 -0
  13. package/dist/Components/Inputs/DatePicker.js +4 -1
  14. package/dist/Components/Inputs/DateRangePicker.js +4 -1
  15. package/dist/Components/Inputs/DateTimePicker.js +4 -1
  16. package/dist/Components/Inputs/Dropdown.js +4 -1
  17. package/dist/Components/Inputs/InputRenderer.js +3 -0
  18. package/dist/Components/Inputs/NumberBox.js +4 -2
  19. package/dist/Components/Inputs/TextArea.js +4 -1
  20. package/dist/Components/Inputs/TextBox.js +4 -1
  21. package/dist/Components/Inputs/TimePicker.js +4 -1
  22. package/dist/Components/Inputs/TreeView.d.ts +55 -5
  23. package/dist/Components/Inputs/TreeView.js +372 -23
  24. package/dist/Components/Layouts/ThemePicker.d.ts +3 -2
  25. package/dist/Components/Layouts/ThemePicker.js +16 -4
  26. package/dist/Components/Viewers/DataTable.js +63 -21
  27. package/dist/Components/Viewers/DataTableParts.d.ts +26 -8
  28. package/dist/Components/Viewers/DataTableParts.js +39 -10
  29. package/dist/Helpers/ApiModules.d.ts +3 -34
  30. package/dist/Helpers/ApiModules.js +3 -3
  31. package/dist/Helpers/ApiRoutes.d.ts +68 -26
  32. package/dist/Helpers/ApiRoutes.js +31 -27
  33. package/dist/Helpers/ArrayHelpers.d.ts +0 -0
  34. package/dist/Helpers/ArrayHelpers.js +2 -0
  35. package/dist/Helpers/CodeHelpers.d.ts +97 -0
  36. package/dist/Helpers/CodeHelpers.js +332 -0
  37. package/dist/Helpers/CrudBackend.d.ts +94 -4
  38. package/dist/Helpers/CrudBackend.js +168 -9
  39. package/dist/Helpers/CrudColumns.d.ts +51 -0
  40. package/dist/Helpers/CrudColumns.js +69 -0
  41. package/dist/Helpers/CrudHelpers.d.ts +50 -5
  42. package/dist/Helpers/CrudHelpers.js +109 -12
  43. package/dist/Helpers/InputParamsHelpers.js +8 -0
  44. package/dist/Helpers/MessageBuilder.js +2 -0
  45. package/dist/Helpers/NexusHelpers.d.ts +0 -0
  46. package/dist/Helpers/NexusHelpers.js +1 -0
  47. package/dist/Helpers/RequestCache.d.ts +35 -0
  48. package/dist/Helpers/RequestCache.js +113 -0
  49. package/dist/Helpers/TableExport.js +12 -12
  50. package/dist/Helpers/TableHelpers.d.ts +1 -1
  51. package/dist/Helpers/TableHelpers.js +7 -2
  52. package/dist/Helpers/TableJoins.d.ts +70 -0
  53. package/dist/Helpers/TableJoins.js +250 -0
  54. package/dist/Helpers/TableStore.d.ts +5 -0
  55. package/dist/Helpers/TableStore.js +9 -0
  56. package/dist/Helpers/TreeStore.d.ts +225 -11
  57. package/dist/Helpers/TreeStore.js +683 -90
  58. package/dist/Helpers/UserPreferences.d.ts +8 -0
  59. package/dist/Helpers/UserPreferences.js +17 -0
  60. package/dist/Interfaces/CrudInterfaces.d.ts +47 -9
  61. package/dist/Interfaces/FormInterfaces.d.ts +4 -1
  62. package/dist/Interfaces/InputInterfaces.d.ts +24 -0
  63. package/dist/Interfaces/MessageInterfaces.d.ts +1 -1
  64. package/dist/Interfaces/PreferenceInterfaces.d.ts +8 -0
  65. package/dist/Interfaces/PreferenceInterfaces.js +2 -5
  66. package/dist/Interfaces/TableInterfaces.d.ts +86 -4
  67. package/dist/Services/ApiProxy.d.ts +36 -1
  68. package/dist/Services/ApiProxy.js +182 -52
  69. package/dist/Services/ApiProxyRules.d.ts +203 -0
  70. package/dist/Services/ApiProxyRules.js +426 -0
  71. package/dist/Services/JoinSources.d.ts +48 -0
  72. package/dist/Services/JoinSources.js +74 -0
  73. package/dist/Services/ServerApi.d.ts +13 -2
  74. package/dist/Services/ServerApi.js +8 -3
  75. package/dist/Shared.Index.d.ts +4 -0
  76. package/dist/Shared.Index.js +4 -0
  77. package/package.json +87 -85
  78. package/src/Styles/Nexus.Avatar.css +79 -79
  79. package/src/Styles/Nexus.Base.css +117 -117
  80. package/src/Styles/Nexus.Button.css +180 -180
  81. package/src/Styles/Nexus.Calendar.css +273 -273
  82. package/src/Styles/Nexus.Chat.css +1443 -1443
  83. package/src/Styles/Nexus.Choice.css +343 -343
  84. package/src/Styles/Nexus.Crud.css +168 -168
  85. package/src/Styles/Nexus.DateTime.css +81 -81
  86. package/src/Styles/Nexus.Dropdown.css +725 -725
  87. package/src/Styles/Nexus.Editor.css +295 -295
  88. package/src/Styles/Nexus.Excel.css +389 -389
  89. package/src/Styles/Nexus.Form.css +279 -279
  90. package/src/Styles/Nexus.Index.css +37 -37
  91. package/src/Styles/Nexus.Input.css +196 -4
  92. package/src/Styles/Nexus.Markdown.css +237 -237
  93. package/src/Styles/Nexus.Menu.css +237 -237
  94. package/src/Styles/Nexus.Popup.css +724 -724
  95. package/src/Styles/Nexus.Rows.css +637 -637
  96. package/src/Styles/Nexus.Slider.css +276 -276
  97. package/src/Styles/Nexus.Tab.Buttons.css +432 -432
  98. package/src/Styles/Nexus.Table.css +31 -30
  99. package/src/Styles/Nexus.Theme.Picker.css +59 -3
  100. package/src/Styles/Nexus.Themes.css +194 -194
  101. package/src/Styles/Nexus.Time.css +144 -144
  102. package/src/Styles/Nexus.Toast.css +263 -263
  103. package/src/Styles/Nexus.Tokens.css +89 -89
  104. package/src/Styles/Nexus.Tree.css +500 -431
@@ -10,6 +10,7 @@ import { Calendar } from "./Calendar.js";
10
10
  import { Field, hasContent } from "./Field.js";
11
11
  import { setInputValue, useInputField } from "./InputField.js";
12
12
  import { useReadOnlyNotice } from "./ReadOnlyNotice.js";
13
+ import { useInputDesign } from "../../Helpers/UserPreferences.js";
13
14
  const GAP = 4;
14
15
  const MARGIN = 8;
15
16
  /**
@@ -20,7 +21,9 @@ const MARGIN = 8;
20
21
  * @example <DatePicker name="checkIn" label="Check-in" required min={todayIsoDate()} />
21
22
  */
22
23
  export function DatePicker(props) {
23
- const { label, hint, error, design = "standard", size = "md", icon, prefix, clearable = true, closeOnSelect = true, fieldName, readOnlyMessage = "Read-only: this date cannot be changed.", validateOn, value, defaultValue, onValueChange, weekStart, weekendDays, isDateDisabled, showOutsideDays, className, inputClassName, style, required, min, max, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onClick, ...inputProps } = props;
24
+ const { label, hint, error, design: ownDesign, size = "md", icon, prefix, clearable = true, closeOnSelect = true, fieldName, readOnlyMessage = "Read-only: this date cannot be changed.", validateOn, value, defaultValue, onValueChange, weekStart, weekendDays, isDateDisabled, showOutsideDays, className, inputClassName, style, required, min, max, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onClick, ...inputProps } = props;
25
+ // The person's input design (the quick preferences) when this page and its form name none, for a labelled input.
26
+ const design = useInputDesign(ownDesign, Boolean(label));
24
27
  const preferences = useDatePreferences();
25
28
  const settings = {
26
29
  calendar: props.calendar ?? preferences.calendar,
@@ -10,6 +10,7 @@ import { Calendar } from "./Calendar.js";
10
10
  import { Field, hasContent } from "./Field.js";
11
11
  import { setInputValue, useInputField } from "./InputField.js";
12
12
  import { useReadOnlyNotice } from "./ReadOnlyNotice.js";
13
+ import { useInputDesign } from "../../Helpers/UserPreferences.js";
13
14
  const GAP = 4;
14
15
  const MARGIN = 8;
15
16
  // Two months of the small calendar are about 480px wide; a narrower window gets one month.
@@ -26,7 +27,9 @@ const sameRange = (a, b) => (a === null || b === null ? a === b : a[0] === b[0]
26
27
  * @example <DateRangePicker startName="checkIn" endName="checkOut" label="Stay" required min={todayIsoDate()} minDistance={1} />
27
28
  */
28
29
  export function DateRangePicker(props) {
29
- const { label, hint, error, design = "standard", size = "md", icon, prefix, months = 2, clearable = true, closeOnSelect = true, fieldName, readOnlyMessage = "Read-only: these dates cannot be changed.", validateOn, value, defaultValue, onValueChange, startName, endName, weekStart, weekendDays, isDateDisabled, showOutsideDays, className, inputClassName, style, required, min, max, minDistance, maxDistance, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onClick, ...inputProps } = props;
30
+ const { label, hint, error, design: ownDesign, size = "md", icon, prefix, months = 2, clearable = true, closeOnSelect = true, fieldName, readOnlyMessage = "Read-only: these dates cannot be changed.", validateOn, value, defaultValue, onValueChange, startName, endName, weekStart, weekendDays, isDateDisabled, showOutsideDays, className, inputClassName, style, required, min, max, minDistance, maxDistance, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onClick, ...inputProps } = props;
31
+ // The person's input design (the quick preferences) when this page and its form name none, for a labelled input.
32
+ const design = useInputDesign(ownDesign, Boolean(label));
30
33
  const preferences = useDatePreferences();
31
34
  const settings = {
32
35
  calendar: props.calendar ?? preferences.calendar,
@@ -15,6 +15,7 @@ import { Field, hasContent } from "./Field.js";
15
15
  import { setInputValue, useInputField } from "./InputField.js";
16
16
  import { useReadOnlyNotice } from "./ReadOnlyNotice.js";
17
17
  import { TimePanel } from "./TimePanel.js";
18
+ import { useInputDesign } from "../../Helpers/UserPreferences.js";
18
19
  const PANEL_GAP = 4;
19
20
  const PANEL_MARGIN = 8;
20
21
  /**
@@ -27,7 +28,9 @@ const PANEL_MARGIN = 8;
27
28
  * @example <DateTimePicker name="pickupAt" label="Pickup" required min={new Date()} step={900} />
28
29
  */
29
30
  export function DateTimePicker(props) {
30
- const { label, hint, error, design = "standard", size = "md", icon, prefix, calendar: calendarProp, format: formatProp, timeFormat: timeFormatProp, script: scriptProp, timeZone: timeZoneProp, showTimeZone = Boolean(timeZoneProp), view = "columns", defaultTime, clearable = true, fieldName, readOnlyMessage = "Read-only: this date and time cannot be changed.", validateOn, value, defaultValue, onValueChange, isDateDisabled, isTimeDisabled, weekStart, weekendDays, showOutsideDays, className, inputClassName, style, required, min, max, minTime, maxTime, step, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onClick, ...inputProps } = props;
31
+ const { label, hint, error, design: ownDesign, size = "md", icon, prefix, calendar: calendarProp, format: formatProp, timeFormat: timeFormatProp, script: scriptProp, timeZone: timeZoneProp, showTimeZone = Boolean(timeZoneProp), view = "columns", defaultTime, clearable = true, fieldName, readOnlyMessage = "Read-only: this date and time cannot be changed.", validateOn, value, defaultValue, onValueChange, isDateDisabled, isTimeDisabled, weekStart, weekendDays, showOutsideDays, className, inputClassName, style, required, min, max, minTime, maxTime, step, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onClick, ...inputProps } = props;
32
+ // The person's input design (the quick preferences) when this page and its form name none, for a labelled input.
33
+ const design = useInputDesign(ownDesign, Boolean(label));
31
34
  const preferences = useDatePreferences();
32
35
  const browserZone = useBrowserTimeZone();
33
36
  // Unknown on the server and while the page hydrates: the box stays empty until then rather than show another zone's time.
@@ -11,6 +11,7 @@ import { DropdownChip, DropdownMedia, DropdownOption, DropdownTag, hasIcon } fro
11
11
  import { Field } from "./Field.js";
12
12
  import { useInputField } from "./InputField.js";
13
13
  import { useReadOnlyNotice } from "./ReadOnlyNotice.js";
14
+ import { useInputDesign } from "../../Helpers/UserPreferences.js";
14
15
  /** Rows a local list renders at first, and adds each time it scrolls near its end, so 10,000 options open at once. */
15
16
  const RENDER_STEP = 100;
16
17
  /** Closer to the end of the list than this, in pixels, renders or loads more. */
@@ -27,7 +28,9 @@ const NAMES_LISTED = 20;
27
28
  const SKELETON_WIDTHS = ["72%", "54%", "64%", "46%"];
28
29
  const IDLE_LIST = { search: "", loadKey: undefined, items: [], page: 0, hasMore: false, total: undefined, status: "idle", errorPage: 1, firstKeys: new Set() };
29
30
  export function Dropdown(props) {
30
- const { label, hint, error, design = "standard", size = "md", icon, options, loadOptions, loadKey, pageSize = 50, selectedOptions, resolveOptions, valueKey = "value", labelKey = "label", descriptionKey = "description", iconKey = "icon", columns, searchKeys, isOptionDisabled = isDisabledItem, isOptionInactive = isInactiveItem, searchable: searchableProp, clearable = true, loading = false, emptyMessage = "No options", renderValue, panelWidth, readOnlyMessage, fieldName, validateOn, className, inputClassName, style, required, validate, name, form, placeholder, disabled, readOnly, ref, onBlur, onFocus, onKeyDown, onClick, onInvalid, multiple: multipleProp, value: valueProp, defaultValue, onValueChange, min, max, selectAll = true, maxChips = 10, ...inputProps } = props;
31
+ const { label, hint, error, design: ownDesign, size = "md", icon, options, loadOptions, loadKey, pageSize = 50, selectedOptions, resolveOptions, valueKey = "value", labelKey = "label", descriptionKey = "description", iconKey = "icon", columns, searchKeys, isOptionDisabled = isDisabledItem, isOptionInactive = isInactiveItem, searchable: searchableProp, clearable = true, loading = false, emptyMessage = "No options", renderValue, panelWidth, readOnlyMessage, fieldName, validateOn, className, inputClassName, style, required, validate, name, form, placeholder, disabled, readOnly, ref, onBlur, onFocus, onKeyDown, onClick, onInvalid, multiple: multipleProp, value: valueProp, defaultValue, onValueChange, min, max, selectAll = true, maxChips = 10, ...inputProps } = props;
32
+ // The person's input design (the quick preferences) when this page and its form name none, for a labelled input.
33
+ const design = useInputDesign(ownDesign, Boolean(label));
31
34
  const multiple = multipleProp === true;
32
35
  const controlled = valueProp !== undefined;
33
36
  const editable = !disabled && !readOnly;
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { isBooleanInput } from "../../Helpers/InputParamsHelpers.js";
3
3
  import { CheckBox, RadioButton, Switch } from "./CheckBox.js";
4
4
  import { CheckBoxGroup } from "./CheckBoxGroup.js";
5
+ import { CodeBox } from "./CodeBox.js";
5
6
  import { DatePicker } from "./DatePicker.js";
6
7
  import { DateRangePicker } from "./DateRangePicker.js";
7
8
  import { DateTimePicker } from "./DateTimePicker.js";
@@ -58,6 +59,8 @@ export function renderInput(params, overrides = {}) {
58
59
  return _jsx(TextArea, { ...props });
59
60
  case "number":
60
61
  return _jsx(NumberBox, { ...props });
62
+ case "code":
63
+ return _jsx(CodeBox, { ...props });
61
64
  case "checkbox":
62
65
  return _jsx(CheckBox, { ...props });
63
66
  case "switch":
@@ -4,7 +4,7 @@ import { InlineIcon, iconMinus, iconPlus } from "nexus-icons";
4
4
  import { useEffect, useLayoutEffect, useRef, } from "react";
5
5
  import { cx } from "../../Helpers/ClassHelpers.js";
6
6
  import { clampNumber, cleanNumberText, formatNumber, getNumberSymbols, isPartialNumber, parseNumber, readPastedNumber, roundDecimal, stepNumber, } from "../../Helpers/NumberHelpers.js";
7
- import { useNumberLocale } from "../../Helpers/UserPreferences.js";
7
+ import { useInputDesign, useNumberLocale } from "../../Helpers/UserPreferences.js";
8
8
  import { validateNumber } from "../../Helpers/ValidationHelpers.js";
9
9
  import { Field, hasContent } from "./Field.js";
10
10
  import { setInputValue, useInputField } from "./InputField.js";
@@ -24,7 +24,9 @@ const isNumber = (value) => value !== null && !Number.isNaN(value);
24
24
  * @example <NumberBox label="Amount" prefix="Rs" min={0} decimals={2} fixedDecimals grouping required />
25
25
  */
26
26
  export function NumberBox(props) {
27
- const { label, hint, error, design = "standard", size = "md", icon, iconEnd, prefix, suffix, step = 1, stepper = false, clamp = false, readOnlyMessage = "Read-only: this number cannot be changed.", fieldName, validateOn, value, defaultValue, onValueChange, decimals, fixedDecimals = false, grouping = false, locale: ownLocale, className, inputClassName, style, required, min, max, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onPaste, onClick, ...inputProps } = props;
27
+ const { label, hint, error, design: ownDesign, size = "md", icon, iconEnd, prefix, suffix, step = 1, stepper = false, clamp = false, readOnlyMessage = "Read-only: this number cannot be changed.", fieldName, validateOn, value, defaultValue, onValueChange, decimals, fixedDecimals = false, grouping = false, locale: ownLocale, className, inputClassName, style, required, min, max, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onPaste, onClick, ...inputProps } = props;
28
+ // The person's input design (the quick preferences) when this page and its form name none, for a labelled input.
29
+ const design = useInputDesign(ownDesign, Boolean(label));
28
30
  // Without a locale of its own, the box writes and reads numbers the way the person chose (Number scale, Thousands).
29
31
  const preferred = useNumberLocale();
30
32
  const locale = ownLocale ?? preferred;
@@ -4,13 +4,16 @@ import { cx } from "../../Helpers/ClassHelpers.js";
4
4
  import { Field } from "./Field.js";
5
5
  import { useReadOnlyNotice } from "./ReadOnlyNotice.js";
6
6
  import { useTextField } from "./TextField.js";
7
+ import { useInputDesign } from "../../Helpers/UserPreferences.js";
7
8
  /**
8
9
  * A multi-line text input with the same label, hint, validation, and designs as `TextBox`.
9
10
  *
10
11
  * @example <TextArea label="Description" maxLength={500} showCount autoResize />
11
12
  */
12
13
  export function TextArea(props) {
13
- const { label, hint, error, design = "standard", size = "md", icon, rows = 3, autoResize = false, maxRows = 12, showCount = false, readOnlyMessage = "Read-only: this field cannot be changed.", fieldName, validateOn, onValueChange, className, textAreaClassName, style, required, minLength, maxLength, pattern, patternMessage, validate, placeholder, disabled, readOnly, ref, onChange, onBlur, onInvalid, onKeyDown, onClick, ...textAreaProps } = props;
14
+ const { label, hint, error, design: ownDesign, size = "md", icon, rows = 3, autoResize = false, maxRows = 12, showCount = false, readOnlyMessage = "Read-only: this field cannot be changed.", fieldName, validateOn, onValueChange, className, textAreaClassName, style, required, minLength, maxLength, pattern, patternMessage, validate, placeholder, disabled, readOnly, ref, onChange, onBlur, onInvalid, onKeyDown, onClick, ...textAreaProps } = props;
15
+ // The person's input design (the quick preferences) when this page and its form name none, for a labelled input.
16
+ const design = useInputDesign(ownDesign, Boolean(label));
14
17
  const field = useTextField(props);
15
18
  const rowsStyle = { "--nx-field-rows": rows, "--nx-field-max-rows": Math.max(rows, maxRows) };
16
19
  // A lock at the top end of the box, and a note under it when someone tries to change the text, say why nothing happens.
@@ -7,6 +7,7 @@ import { Field, hasContent } from "./Field.js";
7
7
  import { setInputValue } from "./InputField.js";
8
8
  import { useReadOnlyNotice } from "./ReadOnlyNotice.js";
9
9
  import { useTextField } from "./TextField.js";
10
+ import { useInputDesign } from "../../Helpers/UserPreferences.js";
10
11
  // Values that are not prose: no red spell-check underline, no capital first letter on phones.
11
12
  const PLAIN_TYPES = new Set(["email", "password", "tel", "url"]);
12
13
  /**
@@ -17,7 +18,9 @@ const PLAIN_TYPES = new Set(["email", "password", "tel", "url"]);
17
18
  * @example <TextBox label="Email" type="email" required pattern={TEXT_PATTERNS.email} icon={<InlineIcon icon={iconMail} />} />
18
19
  */
19
20
  export function TextBox(props) {
20
- const { type = "text", label, hint, error, design = "standard", size = "md", icon, iconEnd, prefix, suffix, actionsStart, clearable = false, showCount = false, readOnlyMessage = "Read-only: this field cannot be changed.", fieldName, validateOn, onValueChange, className, inputClassName, style, required, minLength, maxLength, pattern, patternMessage, validate, placeholder, disabled, readOnly, ref, onChange, onBlur, onInvalid, onKeyDown, onClick, ...inputProps } = props;
21
+ const { type = "text", label, hint, error, design: ownDesign, size = "md", icon, iconEnd, prefix, suffix, actionsStart, clearable = false, showCount = false, readOnlyMessage = "Read-only: this field cannot be changed.", fieldName, validateOn, onValueChange, className, inputClassName, style, required, minLength, maxLength, pattern, patternMessage, validate, placeholder, disabled, readOnly, ref, onChange, onBlur, onInvalid, onKeyDown, onClick, ...inputProps } = props;
22
+ // The person's input design (the quick preferences) when this page and its form name none, for a labelled input.
23
+ const design = useInputDesign(ownDesign, Boolean(label));
21
24
  const field = useTextField(props);
22
25
  const [revealed, setRevealed] = useState(false);
23
26
  const editable = !disabled && !readOnly;
@@ -12,6 +12,7 @@ import { Field, hasContent } from "./Field.js";
12
12
  import { setInputValue, useInputField } from "./InputField.js";
13
13
  import { useReadOnlyNotice } from "./ReadOnlyNotice.js";
14
14
  import { TimePanel } from "./TimePanel.js";
15
+ import { useInputDesign } from "../../Helpers/UserPreferences.js";
15
16
  /**
16
17
  * A time input: type a time on either clock ("2:30 pm", "1430"), or pick one from the panel that opens under the box.
17
18
  * Written in the user's time format, 12- or 24-hour, and always held as an ISO time with no date and no zone, which a
@@ -20,7 +21,9 @@ import { TimePanel } from "./TimePanel.js";
20
21
  * @example <TimePicker name="arrival" label="Arrival" min="12:00" max="22:00" step={1800} view="list" />
21
22
  */
22
23
  export function TimePicker(props) {
23
- const { label, hint, error, design = "standard", size = "md", icon, prefix, view = "columns", clearable = true, fieldName, readOnlyMessage = "Read-only: this time cannot be changed.", validateOn, value, defaultValue, onValueChange, isTimeDisabled, className, inputClassName, style, required, min, max, step, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onClick, ...inputProps } = props;
24
+ const { label, hint, error, design: ownDesign, size = "md", icon, prefix, view = "columns", clearable = true, fieldName, readOnlyMessage = "Read-only: this time cannot be changed.", validateOn, value, defaultValue, onValueChange, isTimeDisabled, className, inputClassName, style, required, min, max, step, validate, name, form, placeholder, disabled, readOnly, ref, onChange, onBlur, onFocus, onInvalid, onKeyDown, onClick, ...inputProps } = props;
25
+ // The person's input design (the quick preferences) when this page and its form name none, for a labelled input.
26
+ const design = useInputDesign(ownDesign, Boolean(label));
24
27
  const preferences = useDatePreferences();
25
28
  const script = props.script ?? (usesDevanagari(preferences.calendar, preferences.script) ? "devanagari" : "latin");
26
29
  const seconds = normalizeTimeStep(step) % 60 !== 0;
@@ -1,5 +1,5 @@
1
1
  import { type CSSProperties, type ReactNode, type Ref } from "react";
2
- import { type TreeItem, type TreeValue, type TreeValueMode } from "../../Helpers/TreeStore.ts";
2
+ import { type TreeChildrenPage, type TreeChildrenRequest, type TreeItem, type TreeValue, type TreeValueMode } from "../../Helpers/TreeStore.ts";
3
3
  import type { Size } from "../../Interfaces/TypeInterfaces.ts";
4
4
  export type TreeAction = "create" | "update" | "delete";
5
5
  export interface TreeCreateRequest<TItem extends TreeItem = TreeItem> {
@@ -47,13 +47,46 @@ export interface TreeViewHandle<TItem extends TreeItem = TreeItem> {
47
47
  remove: (value: TreeValue) => Promise<boolean>;
48
48
  /** Focuses the focused row, or the first. */
49
49
  focus: () => void;
50
+ /**
51
+ * With `loadChildren`: drops the pages kept for the whole tree, or for one item and everything under it, and loads
52
+ * again only what is on screen - the top level and the open items with rows in view. An open item out of view loads
53
+ * when it scrolls in, a closed one when it is next expanded. What is drawn stays until the fresh pages replace it.
54
+ */
55
+ refresh: (value?: TreeValue) => void;
50
56
  }
51
57
  export interface TreeViewProps<TItem extends TreeItem = TreeItem> {
52
58
  /**
53
59
  * The items, flat: each names its parent through `parentKey`, and siblings keep this order. Keep the array stable
54
- * (state, a constant, or `useMemo`): a different array, or one with different items, builds the tree again.
60
+ * (state, a constant, or `useMemo`): a different array, or one with different items, builds the tree again. With
61
+ * `loadChildren`, leave it out to load the top level too, or give the top level and let the children load.
62
+ */
63
+ items?: readonly TItem[];
64
+ /**
65
+ * Loads children from a server, a page at a time: when an item is expanded the first time, when Load more is
66
+ * pressed, and for the top level when no `items` are given. It answers `{ items, total?, hasMore? }`, at once or in
67
+ * a promise; the children go under the item asked for, whatever their own parent field says. Values must be unique
68
+ * across the whole tree: map items whose ids could repeat between levels (`"c" + countryId`, `"r" + regionId`).
69
+ * A thrown error or rejected promise shows "Could not load the items." with Retry.
70
+ */
71
+ loadChildren?: (request: TreeChildrenRequest<TItem>) => TreeChildrenPage<TItem> | PromiseLike<TreeChildrenPage<TItem>>;
72
+ /** With `loadChildren`: children asked for at a time (the request's `take`). Default 50. */
73
+ childPageSize?: number;
74
+ /**
75
+ * With `loadChildren`: the item field saying whether it has children, before they are loaded - a boolean or a count,
76
+ * such as `childCount`. An item without it shows a chevron until its first page answers, and loses it if the page
77
+ * is empty. With `checkable` and `cascade`, an item known to have none can be checked before it is opened.
78
+ */
79
+ hasChildrenKey?: string;
80
+ /**
81
+ * With `loadChildren`: keeps the pages loaded in a cache shared by every tree on the page with the same key, for
82
+ * `cacheLifetime`: leaving the page and coming back, or a second tree, makes no call, and a request on its way is
83
+ * shared. Name the source, e.g. `"administrative-regions"`. Without it, pages are kept only in this tree.
55
84
  */
56
- items: readonly TItem[];
85
+ cacheKey?: string;
86
+ /** How long a kept page is used, in milliseconds. Default 5 minutes. */
87
+ cacheLifetime?: number;
88
+ /** What a click on an item whose items are not all loaded says, with `checkable` and `cascade`. */
89
+ partialMessage?: string;
57
90
  /** The item field that holds its value. Default `id`. */
58
91
  valueKey?: string;
59
92
  /** The item field that holds its parent's value; empty for a top-level item. Default `parentId`. */
@@ -96,7 +129,10 @@ export interface TreeViewProps<TItem extends TreeItem = TreeItem> {
96
129
  form?: string;
97
130
  /** With `checkable` and `cascade`: a parent shows how many of its items are checked, as "3/5". Default true. */
98
131
  counts?: boolean;
99
- /** What is expanded at the start: `"all"`, a number of levels, or the values of items to open. */
132
+ /**
133
+ * What is expanded at the start: `"all"`, a number of levels, or the values of items to open. With `loadChildren`,
134
+ * only items whose children are given in `items`: nothing is asked for at the start but the top level.
135
+ */
100
136
  defaultExpanded?: "all" | number | readonly TreeValue[];
101
137
  /** Items can be added: a + on each row adds under it, and Add in the toolbar adds at the top level. */
102
138
  creatable?: boolean;
@@ -115,7 +151,11 @@ export interface TreeViewProps<TItem extends TreeItem = TreeItem> {
115
151
  onUpdate?: (request: TreeUpdateRequest<TItem>) => TItem | void | Promise<TItem | void>;
116
152
  /** Deletes an item and everything under it. A thrown error keeps the items and shows the message. */
117
153
  onDelete?: (request: TreeDeleteRequest<TItem>) => void | Promise<void>;
118
- /** After an item is added, renamed, or deleted: every item in tree order. Passing this array back as `items` does not rebuild the tree. */
154
+ /**
155
+ * After an item is added, renamed, or deleted: every item in tree order. Passing this array back as `items` does not
156
+ * rebuild the tree. With `loadChildren` it holds the loaded children too, whose own parent fields may not name the
157
+ * item they were loaded under: read it, but keep passing the `items` you gave.
158
+ */
119
159
  onItemsChange?: (items: TItem[]) => void;
120
160
  /** Asks before deleting. Default true; needs `<PopupHost />` on the page. */
121
161
  confirmDelete?: boolean;
@@ -154,7 +194,17 @@ export interface TreeViewProps<TItem extends TreeItem = TreeItem> {
154
194
  * Keys: arrows move and expand, Home and End jump, letters find a row, Space checks, Enter opens, `*` expands the
155
195
  * siblings, F2 renames, Delete deletes, Insert adds under the focused row, and Shift+F10 opens the actions menu.
156
196
  *
197
+ * With `loadChildren` the children come from a server, a page at a time, when an item is first expanded: a loading
198
+ * row while they come, a row that says the page failed with Retry, and Load more with how many are left. Those rows
199
+ * are rows like the others, reached with the arrows and pressed with Enter or Space. What is loaded stays (collapse
200
+ * and expand again asks nothing), `cacheKey` shares it with the next tree, and the handle's `refresh` loads again what
201
+ * is on screen. With `cascade`, an item whose items are not all loaded cannot be checked as a whole: the value can only
202
+ * hold items that were loaded, so its check would silently leave the rest out.
203
+ *
157
204
  * @example
158
205
  * <TreeView label="Permissions" items={permissions} checkable name="actionIds" defaultExpanded={1} isItemDisabled={item => !item.canAssign} />
206
+ *
207
+ * <TreeView label="Regions" items={countries} hasChildrenKey="childCount" cacheKey="regions"
208
+ * loadChildren={({ parent, skip, take, signal }) => loadRegions(parent, skip, take, signal)} />
159
209
  */
160
210
  export declare function TreeView<TItem extends TreeItem = TreeItem>(props: TreeViewProps<TItem>): import("react").JSX.Element;