@nxgt/material 3.86.2 → 3.88.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 (113) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +2 -0
  3. package/dist/{alert-dialog-DZnt8PYE.js → alert-dialog-CEGs7LMN.js} +1 -1
  4. package/dist/{async-slice-D3OdV2BS.js → async-slice-CR0M95Jq.js} +1 -1
  5. package/dist/{button-MAL5an7L.js → button-h9hXBEQB.js} +1 -1
  6. package/dist/{calculator-C3a8YMwl.js → calculator-Dn_KL0b0.js} +10 -10
  7. package/dist/calculator-field.js +6 -6
  8. package/dist/{calculator-segment-BigMpjhn.js → calculator-segment-DMFptsDf.js} +1 -1
  9. package/dist/calculator.js +1 -1
  10. package/dist/charts.js +3 -3
  11. package/dist/{chip-DKAKoNxq.js → chip-C-RAjar5.js} +2 -2
  12. package/dist/{code-editor-B6dFQSUJ.js → code-editor-DApeIOZ3.js} +4 -4
  13. package/dist/code-editor.js +4 -4
  14. package/dist/{code-language-items-CXEMiWoF.js → code-language-items-CGtYfc1l.js} +2 -2
  15. package/dist/components/components.css +2 -0
  16. package/dist/components/ui/collections/marking-list/marking-list.css +16 -0
  17. package/dist/components/ui/primitives/toggle-group/toggle-group-connected.css +1 -1
  18. package/dist/components/ui/primitives/toggle-group/toggle-group-segmented.css +1 -1
  19. package/dist/components/ui/primitives/toggle-group/toggle-group-separate.css +1 -1
  20. package/dist/components/ui/primitives/toggle-group/toggle-group.css +12 -1
  21. package/dist/{components-BTukNsPn.js → components-BIATLurA.js} +16555 -15979
  22. package/dist/components.js +37 -37
  23. package/dist/{conversion-variant-BT5n4-jm.js → conversion-variant-Cf0sEmnS.js} +3 -3
  24. package/dist/{currency-field-t41sFYg_.js → currency-field-cwC7cqUE.js} +4 -4
  25. package/dist/{dialog-BjKiRXJg.js → dialog-Bul68q8u.js} +3 -3
  26. package/dist/dnd.js +1 -1
  27. package/dist/{dropdown-menu-IRFp4CH9.js → dropdown-menu-xUUE5X6T.js} +1 -1
  28. package/dist/editor.js +7 -7
  29. package/dist/{empty-state-C70prDDr.js → empty-state-CCRfjt0n.js} +1 -1
  30. package/dist/{financial-variant-BIpzs8IL.js → financial-variant-CYgRZzKT.js} +5 -5
  31. package/dist/{hooks-CjO11MS7.js → hooks-Dzc6xz5t.js} +2 -2
  32. package/dist/hooks.js +2 -2
  33. package/dist/{hover-card-cU_aJa18.js → hover-card-CZLFQwZv.js} +1 -1
  34. package/dist/i18n.js +3 -3
  35. package/dist/{icon-badge-D3KO6uEQ.js → icon-badge-0Xo9q7Ey.js} +1 -1
  36. package/dist/{icon-button-DrqNk1Ww.js → icon-button-DE1EcO2-.js} +1 -1
  37. package/dist/lib/components/ui/collections/filter/sections/filter-sections-classes.d.ts +4 -3
  38. package/dist/lib/components/ui/collections/index.d.ts +1 -0
  39. package/dist/lib/components/ui/collections/marking-list/index.d.ts +2 -0
  40. package/dist/lib/components/ui/collections/marking-list/marking-list.d.ts +11 -0
  41. package/dist/lib/components/ui/collections/marking-list/marking-list.utils.d.ts +18 -0
  42. package/dist/lib/components/ui/collections/marking-list/partials/marking-row-item.d.ts +16 -0
  43. package/dist/lib/components/ui/collections/marking-list/partials/marking-segments.d.ts +20 -0
  44. package/dist/lib/components/ui/collections/marking-list/partials/marking-set-remaining.d.ts +11 -0
  45. package/dist/lib/components/ui/collections/marking-list/partials/marking-tally.d.ts +14 -0
  46. package/dist/lib/components/ui/collections/marking-list/types.d.ts +74 -0
  47. package/dist/lib/components/ui/collections/marking-list/use-marking-expanded.d.ts +9 -0
  48. package/dist/lib/components/ui/collections/marking-list/use-marking-focus.d.ts +22 -0
  49. package/dist/lib/components/ui/collections/marking-list/use-marking-handlers.d.ts +11 -0
  50. package/dist/lib/components/ui/collections/marking-list/use-tally-speech.d.ts +16 -0
  51. package/dist/lib/components/ui/collections/page-footer/page-footer.d.ts +5 -1
  52. package/dist/lib/components/ui/collections/server-table/index.d.ts +1 -1
  53. package/dist/lib/components/ui/collections/server-table/server-table-paging.d.ts +8 -0
  54. package/dist/lib/components/ui/collections/server-table/types-paging.d.ts +6 -0
  55. package/dist/lib/components/ui/dates/half-day-range-field/half-day-end.d.ts +21 -0
  56. package/dist/lib/components/ui/dates/half-day-range-field/half-day-ends.d.ts +11 -0
  57. package/dist/lib/components/ui/dates/half-day-range-field/half-day-range-field.d.ts +39 -0
  58. package/dist/lib/components/ui/dates/half-day-range-field/half-day-range-form-field.d.ts +3 -0
  59. package/dist/lib/components/ui/dates/half-day-range-field/half-day-range.d.ts +25 -0
  60. package/dist/lib/components/ui/dates/half-day-range-field/index.d.ts +3 -0
  61. package/dist/lib/components/ui/dates/half-day-range-field/use-half-day-draft.d.ts +30 -0
  62. package/dist/lib/components/ui/dates/index.d.ts +1 -0
  63. package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
  64. package/dist/lib/i18n/resources/en/en.d.ts +17 -0
  65. package/dist/lib/i18n/resources/en/material.json.d.ts +17 -0
  66. package/dist/lib/i18n/resources/fr/fr.d.ts +17 -0
  67. package/dist/lib/i18n/resources/fr/material.json.d.ts +17 -0
  68. package/dist/lib/i18n/resources/resources.d.ts +34 -0
  69. package/dist/lib/i18n/translator.d.ts +1 -1
  70. package/dist/lib.js +1 -1
  71. package/dist/main.js +53 -53
  72. package/dist/map.js +7 -7
  73. package/dist/material.css +1 -1
  74. package/dist/pdf-reader.js +1 -1
  75. package/dist/{percent-field-D47IlaPF.js → percent-field-BFwxazf1.js} +1 -1
  76. package/dist/{picker-surface-Cg4V4-E0.js → picker-surface-DkfTv_ft.js} +4 -4
  77. package/dist/{popover-Dzk4-l9U.js → popover-BM6xApJd.js} +1 -1
  78. package/dist/{pos-types-JOXMY4Y2.js → pos-types-CpTpT4vE.js} +1 -1
  79. package/dist/{pos-variant-B2mS8bMB.js → pos-variant-Bs5BUB1L.js} +4 -4
  80. package/dist/print.js +1 -1
  81. package/dist/{programmer-variant-C_Tl0P4_.js → programmer-variant-B1eI1fd0.js} +2 -2
  82. package/dist/{public-ByAsqBOf.js → public-CX7ik5Yg.js} +10 -10
  83. package/dist/{rich-code-block-CJAfqk6b.js → rich-code-block-CdVtxD75.js} +5 -5
  84. package/dist/{scientific-variant-CskM4MLV.js → scientific-variant-rfvJKxB4.js} +2 -2
  85. package/dist/{sortable-provider-ICS_vkLA.js → sortable-provider-BopzsciJ.js} +2 -2
  86. package/dist/{spinner-CweqX5A9.js → spinner-DcW21pN1.js} +1 -1
  87. package/dist/{tabs-95Y2LmnV.js → tabs-CdoBFtWx.js} +1 -1
  88. package/dist/theme-picker.js +2 -2
  89. package/dist/{theme-toggle-WAFkncYx.js → theme-toggle-Ch3GkDDU.js} +1 -1
  90. package/dist/{toolbar-items-BsgzHSNf.js → toolbar-items-CZ1lJXZZ.js} +4 -4
  91. package/dist/{translate-material-DTIAPmlU.js → translate-material-BrYBBZ66.js} +1 -1
  92. package/dist/{translator-BRZNhsy5.js → translator-DoVsjUff.js} +34 -0
  93. package/dist/{use-chat-composer-CleSUh_y.js → use-chat-composer-BjKhuLfT.js} +15 -15
  94. package/dist/{use-heading-spy-D8WbHJuK.js → use-heading-spy-CGTTRV2N.js} +4 -4
  95. package/dist/{use-material-translation-CCy1Me0h.js → use-material-translation-BdrItuwY.js} +1 -1
  96. package/dist/{use-return-focus-BA8G1qVU.js → use-return-focus-Cqzvj9R0.js} +1 -1
  97. package/docs/components/README.md +2 -1
  98. package/docs/components/data-table-filter-and-lists.md +103 -0
  99. package/docs/components/date-and-time.md +111 -0
  100. package/docs/components/primitives.md +4 -1
  101. package/docs/guides/forms.md +1 -0
  102. package/docs/guides/i18n.md +7 -0
  103. package/docs/guides/theming.md +1 -1
  104. package/docs/previews/data-table-filter-lists-markinglist.webp +0 -0
  105. package/docs/previews/date-time-halfdayrangefield.webp +0 -0
  106. package/docs/roadmap.md +7 -2
  107. package/lib/components/components.css +2 -0
  108. package/lib/components/ui/collections/marking-list/marking-list.css +16 -0
  109. package/lib/components/ui/primitives/toggle-group/toggle-group-connected.css +1 -1
  110. package/lib/components/ui/primitives/toggle-group/toggle-group-segmented.css +1 -1
  111. package/lib/components/ui/primitives/toggle-group/toggle-group-separate.css +1 -1
  112. package/lib/components/ui/primitives/toggle-group/toggle-group.css +12 -1
  113. package/package.json +2 -2
@@ -0,0 +1,74 @@
1
+ import { ComponentProps, ReactNode } from 'react';
2
+ import { Tone } from '../../tones.js';
3
+ /** One choice of the segmented control every row shares: "Present", "Absent". */
4
+ export type MarkingChoice = {
5
+ /** Stable id, the value a row holds and the callbacks hand back. */
6
+ id: string;
7
+ /** The visible label. */
8
+ label: ReactNode;
9
+ /**
10
+ * The label as plain text, for the tally and the accessible names when
11
+ * `label` is not a string. Defaults to `label` when that is a string.
12
+ */
13
+ text?: string;
14
+ /** Colours the chosen segment and its tally figure (`data-tone`). */
15
+ tone?: Tone;
16
+ /** A leading icon; decorative, the label names the choice. */
17
+ icon?: ReactNode;
18
+ };
19
+ /** One person of the list. */
20
+ export type MarkingRow = {
21
+ /** Stable id, the key of `value` and of the callbacks. */
22
+ id: string;
23
+ /** The person's name. It names the row's choice group. */
24
+ name: string;
25
+ /** A line under the name: a class, a number, a status. */
26
+ description?: ReactNode;
27
+ /** Before the name: an `Avatar`. */
28
+ leading?: ReactNode;
29
+ /**
30
+ * Locks the row: its choice is read-only (the segments stay focusable, with
31
+ * `aria-disabled`, and each one is described by this reason) and the reason is
32
+ * shown under the name ("Excused by the office"). A value it holds still counts
33
+ * in the tally; a locked row with none counts as `locked`, not `unmarked`.
34
+ */
35
+ locked?: ReactNode;
36
+ };
37
+ /** The choice of each row, by row id; a missing id or `null` is unmarked. */
38
+ export type MarkingValue = Record<string, string | null | undefined>;
39
+ export type MarkingListProps = Omit<ComponentProps<'div'>, 'children' | 'onChange'> & {
40
+ /** The choice set, shared by every row, in display order. */
41
+ choices: readonly MarkingChoice[];
42
+ /** The people, in display order. */
43
+ rows: readonly MarkingRow[];
44
+ /** The choice of each row (controlled). */
45
+ value: MarkingValue;
46
+ /** A segment was pressed; `null` when the chosen segment is pressed again. */
47
+ onValueChange: (rowId: string, choiceId: string | null) => void;
48
+ /**
49
+ * Shows the "Set remaining to" buttons. Pressing one hands the choice and the
50
+ * ids of the unmarked, unlocked rows; apply it in one update.
51
+ */
52
+ onSetRemaining?: (choiceId: string, rowIds: string[]) => void;
53
+ /** Names the list ("Roll call, class 6A"). Required for assistive technology. */
54
+ label: string;
55
+ /** Shows the live tally above the rows. Default `true`. */
56
+ tally?: boolean;
57
+ /**
58
+ * The detail of a row, shown on demand: with it every row gets an expand
59
+ * button, reached with Right past the last segment. Called only while the row
60
+ * is expanded. Keep it stable (`useCallback`) so closed rows do not re-render.
61
+ */
62
+ renderDetail?: (row: MarkingRow) => ReactNode;
63
+ /** Ids of the rows whose detail is open at first (uncontrolled). */
64
+ defaultExpanded?: readonly string[];
65
+ /** The ids of the open rows (controlled); pair it with `onExpandedChange`. */
66
+ expanded?: readonly string[];
67
+ /** The open rows changed. */
68
+ onExpandedChange?: (ids: string[]) => void;
69
+ /**
70
+ * The choices "Set remaining to" offers, by id. Default: all of them. Offer
71
+ * "Present" alone when the others are not a sensible default.
72
+ */
73
+ setRemainingChoices?: readonly string[];
74
+ };
@@ -0,0 +1,9 @@
1
+ /** Which rows are open: controlled by `expanded`, else its own state from `initial`. */
2
+ export declare function useMarkingExpanded({ expanded, initial, onChange, }: {
3
+ expanded?: readonly string[];
4
+ initial?: readonly string[];
5
+ onChange?: (ids: string[]) => void;
6
+ }): {
7
+ ids: readonly string[];
8
+ toggle: (rowId: string) => void;
9
+ };
@@ -0,0 +1,22 @@
1
+ import { KeyboardEvent } from 'react';
2
+ /**
3
+ * Roving focus over every row's cells: the segments, then the expand button
4
+ * when the list has details. One tab stop for the whole list. Left and Right
5
+ * move across a row (Right past the last segment reaches the expand button),
6
+ * Up and Down to the same cell of the previous or next row, stopping at the
7
+ * first and the last. Home and End go to a row's first and last segment.
8
+ * Locked rows stay on the path: their segments are focusable. Space and Enter
9
+ * press the cell (a button), so moving never marks anyone.
10
+ *
11
+ * `cols` is the number of cells in a row, `segments` how many of them are
12
+ * choices. A stop whose column no longer exists (the choices shrank) falls
13
+ * back to the last one, so the list keeps exactly one tab stop.
14
+ */
15
+ export declare function useMarkingFocus(rows: readonly {
16
+ id: string;
17
+ }[], cols: number, segments: number, chosenCol: (rowId: string) => number): {
18
+ /** The tab stop's column in a row, `-1` for every other row. */
19
+ tabbableCol: (rowId: string) => number;
20
+ onFocusCell: (row: string, col: number) => void;
21
+ onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
22
+ };
@@ -0,0 +1,11 @@
1
+ import { MarkingBulk } from './use-tally-speech.js';
2
+ /**
3
+ * The list's callbacks as stable functions, so a parent's new closure does not
4
+ * re-render closed rows, and the record of the last "Set remaining to" the
5
+ * tally says once.
6
+ */
7
+ export declare function useMarkingHandlers(onValueChange: (rowId: string, choiceId: string | null) => void, onSetRemaining?: (choiceId: string, rowIds: string[]) => void): {
8
+ onPress: (rowId: string, choiceId: string | null) => void;
9
+ setRemaining: ((choiceId: string, rowIds: string[]) => void) | undefined;
10
+ bulk: import('react').RefObject<MarkingBulk | null>;
11
+ };
@@ -0,0 +1,16 @@
1
+ import { RefObject } from 'react';
2
+ import { MarkingTally } from './marking-list.utils.js';
3
+ import { MarkingChoice } from './types.js';
4
+ /** A "Set remaining to" the list just applied, recorded for the tally to say. */
5
+ export type MarkingBulk = {
6
+ choiceId: string;
7
+ count: number;
8
+ };
9
+ /**
10
+ * What the tally says aloud, apart from what it shows. A "Set remaining to"
11
+ * is said at once ("33 marked Present"); only that, so loading a saved
12
+ * register stays silent. Single taps are summed up once the marking pauses
13
+ * ("Unmarked: 3"), compared with the last count spoken, so a change in
14
+ * another row within the pause does not lose it.
15
+ */
16
+ export declare function useTallySpeech(choices: readonly MarkingChoice[], tally: MarkingTally, bulk: RefObject<MarkingBulk | null>): string | null;
@@ -20,6 +20,10 @@ export type PageFooterProps = {
20
20
  pageHref?: (params: URLSearchParams) => string;
21
21
  /** The cursor's search param names. Default `after` and `trail`. */
22
22
  pagingNames?: Partial<PagingNames>;
23
+ /** How many earlier pages' cursors the URL keeps. Past it the oldest are
24
+ * dropped into a counted cut mark (`trail=~3,c4,c5`): the range stays exact,
25
+ * and « Previous » from the cut goes to the first page. Default `20`. */
26
+ maxTrail?: number;
23
27
  /** `'icon'`: icon-only Previous / Next with a tooltip, named "Previous
24
28
  * page" / "Next page". Default `'labelled'`. */
25
29
  pageControls?: 'labelled' | 'icon';
@@ -42,4 +46,4 @@ export type PageFooterProps = {
42
46
  * `ServerTable` draws. Renders nothing for an empty result. Without a
43
47
  * `totalCount` (a cursor feed with no total) it is the Previous / Next alone.
44
48
  */
45
- export declare function PageFooter({ pageInfo, totalCount, params, pageSize, count, pageHref, pagingNames, pageControls, live, disabled, hideOnSinglePage, linkComponent, className, ...props }: PageFooterProps): import("react").JSX.Element | null;
49
+ export declare function PageFooter({ pageInfo, totalCount, params, pageSize, count, pageHref, pagingNames, maxTrail, pageControls, live, disabled, hideOnSinglePage, linkComponent, className, ...props }: PageFooterProps): import("react").JSX.Element | null;
@@ -1,3 +1,3 @@
1
1
  export { defineFilterModel, effectiveConditions, type FilterCodecOptions, type FilterCondition, type FilterFieldError, type FilterFieldErrors, type FilterLockedCondition, type FilterModel, type FilterQuery, type FilterSplitErrors, parseFilterQuery, serializeFilterQuery, splitFilterErrors, } from '@nxgt/material-core/filter';
2
- export { type CursorConnection, createSorting, FIRST_PAGE, firstPage, isFiltered, isNewQuery, nextPage, type PageCursor, type PagingNames, pagingNames, paramsFromQuery, previousPage, queryFromParams, queryKey, type RouterQuery, readCursor, type ServerPagination, type ServerPaginationOptions, type SortingOptions, serverPagination, type TableFilterBinding, type TableFilterOptions, type TableSort, type TableSorting, tableFilters, withoutFilters, } from '@nxgt/material-core/table-url';
2
+ export { CUT_MARK, type CursorConnection, createSorting, FIRST_PAGE, firstPage, isFiltered, isNewQuery, nextPage, type PageCursor, type PagingNames, type PagingOptions, pageIndex, pageOffset, pagingNames, paramsFromQuery, previousPage, queryFromParams, queryKey, type RouterQuery, readCursor, type ServerPagination, type ServerPaginationOptions, type SortingOptions, serverPagination, type TableFilterBinding, type TableFilterOptions, type TableSort, type TableSorting, tableFilters, withoutFilters, } from '@nxgt/material-core/table-url';
3
3
  export * from './server-table.js';
@@ -0,0 +1,8 @@
1
+ import { RowData } from '@tanstack/react-table';
2
+ import { ServerTableProps } from './types.js';
3
+ /** The footer's place (offset, Previous, Next, the trail capped by `maxTrail`)
4
+ * and the first page's href, the way back from an empty page. */
5
+ export declare function serverTablePaging<T extends RowData>(props: ServerTableProps<T>, params: URLSearchParams, heading: URLSearchParams | string): {
6
+ place: import('@nxgt/material-core/table-url').ServerPagination;
7
+ firstPageHref: string;
8
+ };
@@ -1,4 +1,6 @@
1
1
  import { PagingNames } from '@nxgt/material-core/table-url';
2
+ /** The cursors `ServerTable` and `PageFooter` keep in the URL by default. */
3
+ export declare const DEFAULT_MAX_TRAIL = 20;
2
4
  /** The footer and paging props of `ServerTable`, apart from the page's data. */
3
5
  export type ServerTablePagingProps = {
4
6
  /** `'paging'` (default): Previous / Next over the URL's cursor. `'load-more'`: `LoadMoreFooter` ("Showing N of M" from `rows.length` and `totalCount`, the load button while `pageInfo.hasNextPage`, a failed alert with a retry) in place of them, for a list that grows: the page keeps the loaded rows in `rows` and fetches the next itself (`onLoadMore`), so the cursor params are not used. */
@@ -24,4 +26,8 @@ export type ServerTablePagingProps = {
24
26
  pageHref?: (params: URLSearchParams) => string;
25
27
  /** The cursor's search param names. Default `after` and `trail`. */
26
28
  pagingNames?: Partial<PagingNames>;
29
+ /** How many earlier pages' cursors the URL keeps. Past it the oldest are
30
+ * dropped into a counted cut mark (`trail=~3,c4,c5`): the range stays exact,
31
+ * and « Previous » from the cut goes to the first page. Default `20`. */
32
+ maxTrail?: number;
27
33
  };
@@ -0,0 +1,21 @@
1
+ import { DateFieldProps } from '../date-field/date-field.js';
2
+ import { HalfDay } from './half-day-range.js';
3
+ export type HalfDayEndFieldsProps = {
4
+ side: 'start' | 'end';
5
+ date?: Date;
6
+ half: HalfDay;
7
+ onDateChange: (date?: Date) => void;
8
+ onHalfChange: (half: HalfDay) => void;
9
+ disabled?: boolean;
10
+ required?: boolean;
11
+ error?: boolean;
12
+ minDate?: Date;
13
+ maxDate?: Date;
14
+ disabledDays?: DateFieldProps['disabledDays'];
15
+ locale?: DateFieldProps['locale'];
16
+ name?: string;
17
+ /** The helper text's id, read with each control. */
18
+ describedBy?: string;
19
+ };
20
+ /** One end: its day (a `DateField`) and a morning / afternoon choice beside it. */
21
+ export declare function HalfDayEndFields({ side, date, half, onDateChange, onHalfChange, error, name, describedBy, ...rest }: HalfDayEndFieldsProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { ComponentProps } from 'react';
2
+ import { HalfDayEndFields } from './half-day-end.js';
3
+ import { useHalfDayDraft } from './use-half-day-draft.js';
4
+ type Draft = ReturnType<typeof useHalfDayDraft>;
5
+ type EndProps = ComponentProps<typeof HalfDayEndFields>;
6
+ type Props = Pick<Draft, 'draft' | 'setDate' | 'setHalf'> & Pick<EndProps, 'disabled' | 'required' | 'error' | 'minDate' | 'maxDate' | 'disabledDays' | 'locale' | 'describedBy'> & {
7
+ name?: string;
8
+ };
9
+ /** The two ends side by side; the end's calendar starts at the start's day. */
10
+ export declare function HalfDayEnds({ draft, setDate, setHalf, name, minDate, ...shared }: Props): import("react").JSX.Element;
11
+ export {};
@@ -0,0 +1,39 @@
1
+ import { ReactNode } from 'react';
2
+ import { DateFieldProps } from '../date-field/date-field.js';
3
+ import { HalfDayRange } from './half-day-range.js';
4
+ export type HalfDayRangeFieldProps = {
5
+ /** `{ start: { date, half }, end: { date, half } }`, or `undefined` while a day is missing. */
6
+ value?: HalfDayRange;
7
+ /** Uncontrolled: the range the field starts with, kept by the field itself. */
8
+ defaultValue?: HalfDayRange;
9
+ /**
10
+ * Called with the range as soon as both days are set, ordered or not (an
11
+ * end before the start shows a message under the field; the form wrapper's
12
+ * rule refuses it in the form), and with `undefined` while a day is missing.
13
+ */
14
+ onValueChange?: (value?: HalfDayRange) => void;
15
+ label?: ReactNode;
16
+ helperText?: ReactNode;
17
+ error?: boolean;
18
+ required?: boolean;
19
+ disabled?: boolean;
20
+ className?: string;
21
+ /** A test hook (`data-pw`); the date inputs have no native `name`. */
22
+ name?: string;
23
+ id?: string;
24
+ /** The first day that can be picked, on both ends. */
25
+ minDate?: Date;
26
+ /** The last day that can be picked, on both ends. */
27
+ maxDate?: Date;
28
+ /** Days that cannot be picked: the calendar's `disabled` matcher. */
29
+ disabledDays?: DateFieldProps['disabledDays'];
30
+ locale?: DateFieldProps['locale'];
31
+ };
32
+ /**
33
+ * A range whose two ends are each a day plus a half (morning or afternoon),
34
+ * ordered on the half: the start morning of day D is not after the end
35
+ * afternoon of day D. Each end is a typed or picked `DateField` with a
36
+ * morning / afternoon toggle; the end's calendar starts at the start's day.
37
+ * An end before the start is refused with a message under the field.
38
+ */
39
+ export declare function HalfDayRangeField({ value, defaultValue, onValueChange, label, helperText, error, required, disabled, className, name, id, minDate, maxDate, ...rest }: HalfDayRangeFieldProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import { UseControllerProps } from 'react-hook-form';
2
+ import { HalfDayRangeFieldProps } from './half-day-range-field.js';
3
+ export declare function HalfDayRangeFormField<T extends Record<string, any>>({ control, name, rules, ...props }: Pick<UseControllerProps<T>, 'control' | 'name' | 'rules'> & Omit<HalfDayRangeFieldProps, 'name' | 'value' | 'defaultValue' | 'onValueChange'>): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ /** The half of a day an end falls on. */
2
+ export type HalfDay = 'morning' | 'afternoon';
3
+ /** One end of a half-day range: a calendar day and its half. */
4
+ export type HalfDayEnd = {
5
+ /** Local midnight of the day. */
6
+ date: Date;
7
+ half: HalfDay;
8
+ };
9
+ /** Both ends of a range, each a day plus a half; the range is ordered when valid (`isHalfDayRangeOrdered` checks it). */
10
+ export type HalfDayRange = {
11
+ start: HalfDayEnd;
12
+ end: HalfDayEnd;
13
+ };
14
+ /** The two halves, in the order of the day (the toggle's items). */
15
+ export declare const HALF_DAYS: readonly HalfDay[];
16
+ /** Orders two ends on the half: negative when `a` comes first, 0 when they are the same half. */
17
+ export declare function compareHalfDays(a: HalfDayEnd, b: HalfDayEnd): number;
18
+ /**
19
+ * Whether the end is not before the start, on the half: the morning of day D
20
+ * is not after the afternoon of day D, and the afternoon of D is after the
21
+ * morning of D (the same half of the same day is one half-day, allowed).
22
+ */
23
+ export declare function isHalfDayRangeOrdered(range: HalfDayRange): boolean;
24
+ /** The number of half-days the range covers, both ends included. */
25
+ export declare function halfDaysIn(range: HalfDayRange): number;
@@ -0,0 +1,3 @@
1
+ export * from './half-day-range.js';
2
+ export * from './half-day-range-field.js';
3
+ export * from './half-day-range-form-field.js';
@@ -0,0 +1,30 @@
1
+ import { HalfDay, HalfDayRange } from './half-day-range.js';
2
+ export type Side = 'start' | 'end';
3
+ export type Draft = {
4
+ startDate?: Date;
5
+ startHalf: HalfDay;
6
+ endDate?: Date;
7
+ endHalf: HalfDay;
8
+ };
9
+ export declare function toDraft(value?: HalfDayRange): Draft;
10
+ /** The range both days make, ordered or not; `undefined` while a day is missing. */
11
+ export declare function toRange(draft: Draft): HalfDayRange | undefined;
12
+ export declare function isUnordered(draft: Draft): boolean;
13
+ /**
14
+ * The two ends being edited. A half-typed range (a day missing) is the
15
+ * draft's, not a value: `onValueChange(undefined)`. Both days set, the range
16
+ * goes out as it is, ordered or not (`unordered` says the end is refused, and
17
+ * the form wrapper's rule refuses it in the form). The draft follows `value`
18
+ * when it changes from outside (a reset to `undefined` empties it), and keeps
19
+ * itself without a `value` (uncontrolled, from `defaultValue`).
20
+ *
21
+ * Limit: a reset to `undefined` while the emitted value is already
22
+ * `undefined` is not news, so a half-typed start or a cleared end stays on
23
+ * screen. Give the field a `key` that changes on reset to empty it.
24
+ */
25
+ export declare function useHalfDayDraft(value: HalfDayRange | undefined, defaultValue: HalfDayRange | undefined, onValueChange: ((value?: HalfDayRange) => void) | undefined): {
26
+ draft: Draft;
27
+ unordered: boolean;
28
+ setDate: (side: Side, date?: Date) => void;
29
+ setHalf: (side: Side, half: HalfDay) => void;
30
+ };
@@ -2,6 +2,7 @@ export * from './calendar/index.js';
2
2
  export * from './date-field/index.js';
3
3
  export * from './date-range-field/index.js';
4
4
  export * from './duration-field/index.js';
5
+ export * from './half-day-range-field/index.js';
5
6
  export * from './mini-month/index.js';
6
7
  export * from './now-indicator/index.js';
7
8
  export * from './time-field/index.js';