@danxbot/ui 20.8.1 → 20.10.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.
@@ -138,12 +138,32 @@ export interface BoardColumnProps<T> {
138
138
  * never colour or glyph alone (WCAG 1.4.1).
139
139
  */
140
140
  icon?: ReactNode;
141
+ /**
142
+ * DX-4540: the lane's real length when `column.items` holds only the cards
143
+ * loaded so far (a windowed lane, see the file header). The count badge, the
144
+ * empty-state decision and the WIP limit all read it instead of the loaded
145
+ * count, so a lane of 500 shows 500 while rendering 40. Omitted, they read
146
+ * `column.count` exactly as before.
147
+ *
148
+ * `null` is "the length is still being read" (a lane opening, or starting a
149
+ * new sort): the count shows a dash, and the lane is NOT empty — its cards
150
+ * are on their way, so the empty state stays away and the children (a
151
+ * `PagedWindowList` holding a loading placeholder) stay mounted.
152
+ */
153
+ total?: number | null;
154
+ /**
155
+ * DX-4540: controls drawn at the header's end, after the count — a
156
+ * `SortMenu`, say. A slot of content, not a boolean: supplying it is what
157
+ * adds it. It sits in the header, outside the scrolling list, so it stays
158
+ * put while the cards scroll.
159
+ */
160
+ actions?: ReactNode;
141
161
  /** The column's cards — a `BoardCard` per item, mapped by the consumer
142
- * exactly as `DragDropContainer`'s own children already are. This is the
143
- * virtualization seam — see the file header. */
162
+ * exactly as `DragDropContainer`'s own children already are. A windowed
163
+ * lane puts a `PagedWindowList` here — see the file header. */
144
164
  children?: ReactNode;
145
165
  }
146
- export declare function BoardColumn<T>({ column, emptyState, className, listClassName, accent, icon, children, }: BoardColumnProps<T>): import("react").JSX.Element;
166
+ export declare function BoardColumn<T>({ column, emptyState, className, listClassName, accent, icon, total, actions, children, }: BoardColumnProps<T>): import("react").JSX.Element;
147
167
  export interface BoardCardProps {
148
168
  id: BoardItemId;
149
169
  /** Position within its OWN column right now — same contract as
@@ -0,0 +1,31 @@
1
+ import { type ReactNode } from "react";
2
+ import type { PagedWindow } from "../lib/paged-window";
3
+ export interface PagedWindowStrings {
4
+ /** Shown in a page whose request failed. */
5
+ error: string;
6
+ /** The retry button's label. */
7
+ retry: string;
8
+ }
9
+ export interface PagedWindowItemPosition {
10
+ /** Position among the items currently held, across every held page. */
11
+ local: number;
12
+ /** Position in the whole list: `page * pageSize + index on the page`. */
13
+ global: number;
14
+ }
15
+ export interface PagedWindowListProps<T> {
16
+ /** The window to render — what `usePagedWindow` returned. */
17
+ window: PagedWindow<T>;
18
+ /** Stable across renders and pages. Never an array index. */
19
+ getKey: (item: T) => string | number;
20
+ renderItem: (item: T, position: PagedWindowItemPosition) => ReactNode;
21
+ /**
22
+ * One item's height INCLUDING its spacing, in px, used for placeholders until
23
+ * real pages have been measured. Replaced by the measured mean as pages
24
+ * arrive, so it only has to be close.
25
+ */
26
+ estimatedItemHeight: number;
27
+ /** Words in the failed-page row. */
28
+ strings?: Partial<PagedWindowStrings>;
29
+ className?: string;
30
+ }
31
+ export declare function PagedWindowList<T>({ window: win, getKey, renderItem, estimatedItemHeight, strings, className, }: PagedWindowListProps<T>): import("react").JSX.Element;
@@ -21,6 +21,7 @@ export declare const board: import("tailwind-variants").TVReturnType<{
21
21
  header: string[];
22
22
  title: string;
23
23
  glyph: string;
24
+ trailing: string;
24
25
  label: string;
25
26
  list: string;
26
27
  empty: string;
@@ -50,6 +51,7 @@ export declare const board: import("tailwind-variants").TVReturnType<{
50
51
  header: string[];
51
52
  title: string;
52
53
  glyph: string;
54
+ trailing: string;
53
55
  label: string;
54
56
  list: string;
55
57
  empty: string;
@@ -79,6 +81,7 @@ export declare const board: import("tailwind-variants").TVReturnType<{
79
81
  header: string[];
80
82
  title: string;
81
83
  glyph: string;
84
+ trailing: string;
82
85
  label: string;
83
86
  list: string;
84
87
  empty: string;
@@ -1,7 +1,7 @@
1
- import { type AccessRequestState } from "./approvalPanelParts";
1
+ import { type ExpiredNote, type AccessRequestState } from "./approvalPanelParts";
2
2
  import type { Permission, Role } from "./types";
3
3
  export type AccessRequestKind = "session" | "worker";
4
- export type { AccessRequestState };
4
+ export type { AccessRequestState, ExpiredNote };
5
5
  export interface AccessRequest {
6
6
  kind: AccessRequestKind;
7
7
  /** The session title or the worker id: what the requester calls itself. */
@@ -36,6 +36,11 @@ export interface AccessRequestPanelProps {
36
36
  defaultRoleId?: string;
37
37
  onApprove: (roleId: string) => Promise<void>;
38
38
  onDeny: () => Promise<void>;
39
+ /**
40
+ * Why an expired request expired and what its requester does next. Used only in the `expired` state; without it the readout says only
41
+ * that nothing was granted.
42
+ */
43
+ expiredNote?: ExpiredNote;
39
44
  className?: string;
40
45
  }
41
- export declare function AccessRequestPanel({ request, state, roles, permissions, ownPermissionKeys, defaultRoleId, onApprove, onDeny, className, }: AccessRequestPanelProps): import("react").JSX.Element;
46
+ export declare function AccessRequestPanel({ request, state, roles, permissions, ownPermissionKeys, defaultRoleId, onApprove, onDeny, expiredNote, className, }: AccessRequestPanelProps): import("react").JSX.Element;
@@ -1,4 +1,4 @@
1
- import { type AccessRequestState } from "./approvalPanelParts";
1
+ import { type AccessRequestState, type ExpiredNote } from "./approvalPanelParts";
2
2
  import type { Permission } from "./types";
3
3
  export interface PermissionRequest {
4
4
  /** The name of the key that is asking: what the agent's key is called. */
@@ -26,6 +26,11 @@ export interface PermissionRequestPanelProps {
26
26
  /** Receives exactly the permission keys left checked. */
27
27
  onApprove: (permissionKeys: string[]) => Promise<void>;
28
28
  onDeny: () => Promise<void>;
29
+ /**
30
+ * Why an expired request expired and what its requester does next. Used only in the `expired` state; without it the readout says only
31
+ * that nothing was granted.
32
+ */
33
+ expiredNote?: ExpiredNote;
29
34
  className?: string;
30
35
  }
31
- export declare function PermissionRequestPanel({ request, state, permissions, ownPermissionKeys, onApprove, onDeny, className, }: PermissionRequestPanelProps): import("react").JSX.Element;
36
+ export declare function PermissionRequestPanel({ request, state, permissions, ownPermissionKeys, onApprove, onDeny, expiredNote, className, }: PermissionRequestPanelProps): import("react").JSX.Element;
@@ -1,5 +1,12 @@
1
1
  import { type ReactNode } from "react";
2
2
  export type AccessRequestState = "pending" | "approved" | "denied" | "expired" | "claimed";
3
+ /** Why a request expired and what its requester does next, in the consumer's words; the Expired readout leads with them. */
4
+ export interface ExpiredNote {
5
+ /** Why the request is over, e.g. "The agent's session ended.". */
6
+ reason: string;
7
+ /** What the requester must do to get access, e.g. "The agent must reconnect and ask again.". */
8
+ nextStep: string;
9
+ }
3
10
  export declare function ApprovalHeader({ title, state }: {
4
11
  title: string;
5
12
  state: AccessRequestState;
@@ -40,4 +47,9 @@ export declare function ApprovalButtons({ busy, run, onApprove, onDeny, approveD
40
47
  /** True when there is nothing valid to approve. Deny is never disabled by this. */
41
48
  approveDisabled: boolean;
42
49
  }): import("react").JSX.Element;
50
+ /** The Expired readout: with a note it names the cause and the requester's next step, without one it only says nothing was granted. */
51
+ export declare function ExpiredNotice({ note, fallback }: {
52
+ note: ExpiredNote | undefined;
53
+ fallback: string;
54
+ }): import("react").JSX.Element;
43
55
  export {};
@@ -111,10 +111,12 @@ export { tokenize, listLanguages, resolveLanguage, defineGrammar, registerGramma
111
111
  export { parseMarkdown, MarkdownNodes, sanitizeHref, type MdNode, type MdInline, type MdCodeRenderer, } from "./lib/syntax/markdown";
112
112
  export { DragDrop, DragDropContainer, DragDropItem, DragDropHandle, DragDropSurface, type DragDropProps, type DragDropContainerProps, type DragDropItemProps, type DragDropHandleProps, type DragDropSurfaceProps, } from "./components/DragDrop";
113
113
  export { KanbanBoard, type KanbanBoardProps, type KanbanLane, type KanbanMove, type KanbanRefusal, } from "./components/kanban/KanbanBoard";
114
- export { DEFAULT_DRAG_STRINGS, DRAG_INSTRUCTIONS_ID, DragInstructions, DragLiveRegion, DragSession, DragSessionProvider, applyMove, attachAutoScroll, attachKeyboardSensor, attachPointerSensor, attachSessionWindowListeners, isNoOp, resolveTarget, sameTarget, scrollVelocity, useCreateDragSession, useDragAnnouncements, useDragSession, useDragState, useDraggable, useDropContainer, type ContainerId, type DragAxis, type DragCancelEvent, type DragEndEvent, type DragId, type DragMode, type DragPhase, type DragSessionCallbacks, type DragStartEvent, type DragState, type AutoScrollOptions, type DragAnnouncementContext, type DragAnnouncementOptions, type DragAnnouncements, type DragInstructionsProps, type DragLiveRegionProps, type DragStrings, type DropTarget, type KeyboardSensorOptions, type PointerActivation, } from "./lib/dnd";
114
+ export { DEFAULT_DRAG_STRINGS, DRAG_INSTRUCTIONS_ID, DragInstructions, DragLiveRegion, DragSession, DragSessionProvider, applyMove, attachAutoScroll, attachKeyboardSensor, attachPointerSensor, attachSessionWindowListeners, isNoOp, resolveTarget, sameTarget, scrollVelocity, useCreateDragSession, useDragAnnouncements, useDragSession, useDragState, useDragLive, useDraggable, useDropContainer, type ContainerId, type DragAxis, type DragCancelEvent, type DragEndEvent, type DragId, type DragMode, type DragPhase, type DragSessionCallbacks, type DragStartEvent, type DragState, type AutoScrollOptions, type DragAnnouncementContext, type DragAnnouncementOptions, type DragAnnouncements, type DragInstructionsProps, type DragLiveRegionProps, type DragStrings, type DropTarget, type KeyboardSensorOptions, type PointerActivation, } from "./lib/dnd";
115
115
  export { Board, BoardColumn, BoardCard, BoardCardSurface, type BoardProps, type BoardColumnProps, type BoardCardProps, type BoardCardSurfaceProps, } from "./components/Board";
116
116
  export { buildBoardModel, resolveMove, applyBoardMoves, useBoard, type BoardModel, type UseBoardResult, } from "./lib/board";
117
117
  export type { BoardAccessors, BoardColumnDef, BoardColumnId, BoardColumnModel, BoardItemId, BoardMove, BoardMoveRequest, BoardMoveSource, BoardMoveVerdict, UseBoardOptions, } from "./lib/board";
118
+ export { PagedWindowList, type PagedWindowItemPosition, type PagedWindowListProps, type PagedWindowStrings, } from "./components/PagedWindowList";
119
+ export { PagedWindowStore, usePagedWindow, windowPageNumbers, type LoadPage, type LoadPageContext, type PageResponse, type PageSnapshot, type PageStatus, type PagedWindow, type PagedWindowOptions, type PagedWindowSnapshot, type UsePagedWindowOptions, } from "./lib/paged-window";
118
120
  export type { MessageRole, MessagePart, TextPart, CodePart, ReasoningPart, ToolPart, ToolStatus, FilePart, ComponentPart, ChatFile, ChatMessage, RenderContext, PartRenderer, PartRenderers, ChatComponentRenderer, ChatComponents, } from "./components/chat/types";
119
121
  export { Chat, ChatPopover, type ChatProps, type ChatPopoverProps, type ChatSurface, } from "./components/chat/Chat";
120
122
  export { Message, MessagePartView, ToolCall, type MessageProps, type MessageAction, type MessagePartViewProps, type ToolCallProps, } from "./components/chat/Message";
@@ -152,7 +154,7 @@ export { cellLabel, eventLabel, formatDateRange, longDate, monthTitle, timeOfDay
152
154
  export { initialFocusDay, isInMonth, nextDayForKey, useGridNavigation, type GridNavigation, type GridNavigationOptions, } from "./components/calendar/use-grid-navigation";
153
155
  export { AmbiguousTimeError, addDays, addDuration, addMonths, atMidnight, atTime, comparePlainDate, dayFraction, dayLengthMs, dayOfWeek, daysBetween, daysInMonth, endOfMonth, hoursInDay, instantAtFraction, instantToPlainDate, instantToPlainDateTime, isSameDate, isoDateString, isoDateTimeString, monthGridDays, nextDay, offsetAt, parsePlainDate, plainDateTimeToInstant, relativeAge, startOfDay, startOfMonth, startOfWeek, weekDays, type CalendarDuration, type Disambiguation, type Instant, type PlainDate, type PlainDateTime, type TimeZone, } from "./lib/time";
154
156
  export { allDayBandSegment, dayKey, daySlots, isAllDay, packBand, packLanes, snapFractionToInstant, timeSegments, timedBandSegment, type AllDayEvent, type BandInput, type BandOptions, type BandPlacement, type BandResult, type BandSegment, type CalendarEvent, type CalendarEventId, type CalendarView, type DaySlot, type DragKind, type EventDraft, type EventPalette, type LaneInput, type LaneOptions, type LanePlacement, type PaletteEntry, type TimeSegment, type TimedEvent, type VisibleRange, } from "./lib/calendar";
155
- export { RBACProvider, useRBAC, Can, useHasPermission, AccountMenu, AccountMenuSection, TeamSwitcher, DevToolsPopover, RolePermissionMatrix, RoleEditorDialog, ProfileForm, ProfilePage, RolesPermissionsPage, MembersPanel, ChangePasswordForm, SessionsList, ApiKeysPanel, AgentKeysList, AccessRequestPanel, PermissionRequestPanel, RoleKeyPicker, isRoleSelectable, leastPrivilegeRoleId, InvitationsList, InviteMembersDialog, Brand, TeamSettingsForm, DangerZone, type RBACProviderProps, type RBACHookResult, type CanProps, type AccountMenuProps, type TeamSwitcherProps, type DevToolsPopoverProps, type RolePermissionMatrixProps, type RoleEditorDialogProps, type RoleEditorDialogSubmitInput, type ProfileFormProps, type ProfileFormAccount, type ProfileFormSaveInput, type ProfilePageProps, type RolesPermissionsPageProps, type MembersPanelProps, type MembersPanelMember, type ChangePasswordFormProps, type SessionsListProps, type Session, type ApiKeysPanelProps, type AgentKeysListProps, type ApiKey, type KeyGrant, type KeyRoleEditor, type AccessRequestPanelProps, type AccessRequest, type AccessRequestKind, type AccessRequestState, type PermissionRequestPanelProps, type PermissionRequest, type RoleKeyPickerProps, type InvitationsListProps, type InvitationsListItem, type InvitationStatus, type InviteMembersDialogProps, type Permission, type Role, type User, type Team, type TeamMembership, type BrandProps, type TeamSettingsFormProps, type TeamSettingsFormTeam, type TeamSettingsFormSaveInput, type DangerZoneProps, } from "./components/rbac";
157
+ export { RBACProvider, useRBAC, Can, useHasPermission, AccountMenu, AccountMenuSection, TeamSwitcher, DevToolsPopover, RolePermissionMatrix, RoleEditorDialog, ProfileForm, ProfilePage, RolesPermissionsPage, MembersPanel, ChangePasswordForm, SessionsList, ApiKeysPanel, AgentKeysList, AccessRequestPanel, PermissionRequestPanel, RoleKeyPicker, isRoleSelectable, leastPrivilegeRoleId, InvitationsList, InviteMembersDialog, Brand, TeamSettingsForm, DangerZone, type RBACProviderProps, type RBACHookResult, type CanProps, type AccountMenuProps, type TeamSwitcherProps, type DevToolsPopoverProps, type RolePermissionMatrixProps, type RoleEditorDialogProps, type RoleEditorDialogSubmitInput, type ProfileFormProps, type ProfileFormAccount, type ProfileFormSaveInput, type ProfilePageProps, type RolesPermissionsPageProps, type MembersPanelProps, type MembersPanelMember, type ChangePasswordFormProps, type SessionsListProps, type Session, type ApiKeysPanelProps, type AgentKeysListProps, type ApiKey, type KeyGrant, type KeyRoleEditor, type AccessRequestPanelProps, type AccessRequest, type AccessRequestKind, type AccessRequestState, type ExpiredNote, type PermissionRequestPanelProps, type PermissionRequest, type RoleKeyPickerProps, type InvitationsListProps, type InvitationsListItem, type InvitationStatus, type InviteMembersDialogProps, type Permission, type Role, type User, type Team, type TeamMembership, type BrandProps, type TeamSettingsFormProps, type TeamSettingsFormTeam, type TeamSettingsFormSaveInput, type DangerZoneProps, } from "./components/rbac";
156
158
  export { AuthLayout, AcceptInvitationCard, SignupCard, TermsAcceptance, LoginForm, ForgotPasswordForm, ResetPasswordForm, type AuthLayoutProps, type AcceptInvitationCardProps, type SignupCardProps, type TermsAcceptanceProps, type LoginFormProps, type ForgotPasswordFormProps, type ResetPasswordFormProps, } from "./components/auth";
157
159
  export { UseCaseCard, RoadmapItemCard, RoadmapSwimlaneBoard, UseCaseLedgerPage, RoadmapPage, PhaseManager, TrackManager, deriveRoadmapItemStatus, isFullyBuilt, isUnbuilt, trackIds, trackProgress, toneForIndex, CATEGORY_TONES, type Domain, type Phase, type Track, type RoadmapItem, type RoadmapItemStatus, type UseCase, type UseCaseStatus, type UseCaseDecision, type UseCaseCardProps, type RoadmapItemCardProps, type RoadmapSwimlaneBoardProps, type UseCaseLedgerPageProps, type RoadmapPageProps, type PhaseManagerProps, type TrackManagerProps, } from "./components/roadmap";
158
160
  export { ProvenanceDisclosure, ProvenanceDetail, ProvenanceBadge, ConfidenceReadout, ConfidenceMeter, ConfidenceBar, provenanceStateLabel, confidenceBand, confidenceScore, scoredConfidence, ratedConfidence, type ProvenanceRecord, type ProvenanceState, type ProvenanceSource, type ProvenanceCheck, type ProvenanceCall, type ProvenanceSignoff, type ConfidenceBand, type ConfidenceScore, type ProvenanceConfidence, type ConfidenceReadoutProps, type ConfidenceBarProps, type ConfidenceBarSize, type ProvenanceDisclosureProps, type ProvenanceDetailProps, type ProvenanceDetailTitleAs, type ProvenanceTriggerBadge, type ProvenanceBadgeProps, type ProvenanceBadgeAppearance, type ConfidenceMeterProps, } from "./components/provenance";
@@ -12,5 +12,5 @@ export { DragSession, type StartArgs } from "./session";
12
12
  export { attachPointerSensor, attachSessionWindowListeners, type PointerActivation, type PointerSensorOptions, } from "./sensors/pointer";
13
13
  export { attachKeyboardSensor, type KeyboardSensorOptions, } from "./sensors/keyboard";
14
14
  export { attachAutoScroll, scrollVelocity, type AutoScrollOptions, } from "./autoscroll";
15
- export { DragSessionProvider, useCreateDragSession, useDragSession, useDragState, useDraggable, useDropContainer, type UseDraggableOptions, type UseDraggableResult, type UseDropContainerOptions, type UseDropContainerResult, } from "./use-drag";
15
+ export { DragSessionProvider, useCreateDragSession, useDragSession, useDragState, useDragLive, useDraggable, useDropContainer, type UseDraggableOptions, type UseDraggableResult, type UseDropContainerOptions, type UseDropContainerResult, } from "./use-drag";
16
16
  export { DEFAULT_DRAG_STRINGS, DRAG_INSTRUCTIONS_ID, DragInstructions, DragLiveRegion, useDragAnnouncements, type DragAnnouncementContext, type DragAnnouncementOptions, type DragAnnouncements, type DragInstructionsProps, type DragLiveRegionProps, type DragStrings, } from "./announce";
@@ -14,6 +14,16 @@ export declare function useDragSession(component: string): DragSession;
14
14
  export declare function useCreateDragSession(callbacks: DragSessionCallbacks): DragSession;
15
15
  /** Subscribe to the session. */
16
16
  export declare function useDragState(session: DragSession): DragState;
17
+ /**
18
+ * True while a card is being carried in the enclosing `<DragDrop>`, and false
19
+ * outside one — it never throws, because the callers that ask (a windowed list
20
+ * deciding whether it may drop a page) are ordinary components that are
21
+ * sometimes inside a board and sometimes not.
22
+ *
23
+ * Reads only `phase === "dragging"`, so it re-renders its caller when a drag
24
+ * starts or ends and not on every pointer move, which `useDragState` would.
25
+ */
26
+ export declare function useDragLive(): boolean;
17
27
  export interface UseDraggableOptions {
18
28
  id: DragId;
19
29
  container: ContainerId;
@@ -0,0 +1,2 @@
1
+ export { PagedWindowStore, windowPageNumbers, type LoadPage, type LoadPageContext, type PageResponse, type PageSnapshot, type PageStatus, type PagedWindowOptions, type PagedWindowSnapshot, } from "./store";
2
+ export { usePagedWindow, type PagedWindow, type UsePagedWindowOptions } from "./use-paged-window";
@@ -0,0 +1,111 @@
1
+ export interface PageResponse<T> {
2
+ /** This page's items, in order. Fewer than `pageSize` only for the last page. */
3
+ items: readonly T[];
4
+ /** The WHOLE list's length right now. Every response refreshes it. */
5
+ total: number;
6
+ }
7
+ export interface LoadPageContext {
8
+ /** Aborted when the page is dropped, re-requested or the window is reset. */
9
+ signal: AbortSignal;
10
+ }
11
+ export type LoadPage<T> = (page: number, context: LoadPageContext) => Promise<PageResponse<T>>;
12
+ export type PageStatus = "loading" | "ready" | "error";
13
+ export interface PageSnapshot<T> {
14
+ page: number;
15
+ status: PageStatus;
16
+ /** Empty unless `ready`. A page being re-read keeps its old items and stays `ready`. */
17
+ items: readonly T[];
18
+ /** True while a `ready` page is being re-read in place. */
19
+ refreshing: boolean;
20
+ /** What the load rejected with. Only on `error`. */
21
+ error: unknown;
22
+ }
23
+ export interface PagedWindowSnapshot<T> {
24
+ /** The list's length as the latest response reported it; null before the first. */
25
+ total: number | null;
26
+ pageSize: number;
27
+ /** 0 until `total` is known, then `ceil(total / pageSize)`. */
28
+ pageCount: number;
29
+ /** The page the viewer is looking at. */
30
+ anchorPage: number;
31
+ /** Counts resets, so a view can scroll back to the top when the query changed. */
32
+ epoch: number;
33
+ /** The held pages, ascending. At most `windowPages` of them. */
34
+ pages: readonly PageSnapshot<T>[];
35
+ /** Every item of every `ready` held page, in list order. At most `windowPages * pageSize`. */
36
+ items: readonly T[];
37
+ }
38
+ export interface PagedWindowOptions<T> {
39
+ pageSize: number;
40
+ /** How many pages are held at once. @default 3 */
41
+ windowPages?: number;
42
+ loadPage: LoadPage<T>;
43
+ }
44
+ /**
45
+ * The pages to hold for an anchor: `windowPages` of them centred on it (the
46
+ * extra page of an even window goes after), clipped to `[0, pageCount)`. Pure,
47
+ * so the rule is testable without a store.
48
+ */
49
+ export declare function windowPageNumbers(anchorPage: number, pageCount: number, windowPages: number): number[];
50
+ export declare class PagedWindowStore<T> {
51
+ private readonly pageSize;
52
+ private readonly windowPages;
53
+ private loadPage;
54
+ private held;
55
+ private total;
56
+ private anchor;
57
+ /** The anchor the held pages were last synced to; differs from `anchor` only while frozen. */
58
+ private appliedAnchor;
59
+ private enabled;
60
+ private frozen;
61
+ private epoch;
62
+ private nextToken;
63
+ private snapshot;
64
+ private listeners;
65
+ constructor({ pageSize, windowPages, loadPage }: PagedWindowOptions<T>);
66
+ /**
67
+ * Replaced on every render by the React binding, so a consumer's loader can
68
+ * close over fresh state without the store being rebuilt (rebuilding drops
69
+ * every held page). A change in WHAT is loaded is a `reset`, never this.
70
+ */
71
+ setLoader(loadPage: LoadPage<T>): void;
72
+ getSnapshot: () => PagedWindowSnapshot<T>;
73
+ subscribe: (listener: () => void) => (() => void);
74
+ /** The viewer moved to another page. Ignored while frozen, applied on thaw. */
75
+ setAnchorPage(page: number): void;
76
+ /**
77
+ * While true the window neither loads nor drops pages, whatever the anchor
78
+ * does. A drag needs it: the carried card is mounted in its page, and
79
+ * dropping that page mid-drag would unmount the card and cancel the drag.
80
+ */
81
+ setFrozen(frozen: boolean): void;
82
+ /** Off, nothing is held or requested. Back on, the window loads again. */
83
+ setEnabled(enabled: boolean): void;
84
+ /** The query changed: forget everything, return to the first page. */
85
+ reset(): void;
86
+ /** Re-read one held page in place. A page that is not held loads fresh when it is next wanted. */
87
+ invalidatePage(page: number): void;
88
+ /** Re-read every held page in place. */
89
+ invalidateAll(): void;
90
+ /** Load the first window. The binding calls it on mount, and again after a `stop`. */
91
+ start(): void;
92
+ /** Abort every request and release every held page. The binding calls it on unmount. */
93
+ stop(): void;
94
+ private pageCount;
95
+ /**
96
+ * Pages to hold now. Page 0 alone before the first response (the count is
97
+ * unknown) and for an empty list: an empty page is still held, because it is
98
+ * what `invalidateAll` re-reads when the list gets its first item — an empty
99
+ * lane that holds nothing could never find out it was no longer empty.
100
+ */
101
+ private wanted;
102
+ private sync;
103
+ /** Start (or restart) a page's request; whatever was in flight is abandoned. */
104
+ private request;
105
+ private settle;
106
+ /** The list shrank: a held page past its end has nothing to show. */
107
+ private dropBeyondCount;
108
+ private dropAll;
109
+ private build;
110
+ private publish;
111
+ }
@@ -0,0 +1,40 @@
1
+ import { type PagedWindowOptions, type PagedWindowSnapshot } from "./store";
2
+ export interface UsePagedWindowOptions<T> extends PagedWindowOptions<T> {
3
+ /**
4
+ * Names WHAT is being listed: the filter, the sort, the time range. When it
5
+ * changes the window forgets every page, returns to the first and the list
6
+ * scrolls back to the top. A change in `loadPage`'s identity alone does not:
7
+ * a loader closing over fresh state must not throw away what is held.
8
+ */
9
+ queryKey: string;
10
+ /**
11
+ * Off, nothing is held or requested (a hidden lane). Back on, the window
12
+ * loads again.
13
+ * @default true
14
+ */
15
+ enabled?: boolean;
16
+ }
17
+ export interface PagedWindow<T> extends PagedWindowSnapshot<T> {
18
+ /** The viewer moved to this page. `PagedWindowList` reports it; a custom view may too. */
19
+ setAnchorPage: (page: number) => void;
20
+ /** Re-read one held page in place — a card on it changed. */
21
+ invalidatePage: (page: number) => void;
22
+ /** Re-read every held page in place — a card was created, moved or removed. */
23
+ invalidateAll: () => void;
24
+ /**
25
+ * While true the window neither loads nor drops pages. `PagedWindowList`
26
+ * sets it for the length of a drag: the carried card is mounted in its page,
27
+ * and dropping that page mid-drag would unmount the card and cancel the drag.
28
+ */
29
+ setFrozen: (frozen: boolean) => void;
30
+ }
31
+ /**
32
+ * Holds `windowPages` (default 3) pages of a long server-side list and tells a
33
+ * view which ones to show. `loadPage` answers one page AND the list's current
34
+ * length, so the scrollbar can span `total` before most of it is loaded.
35
+ *
36
+ * `pageSize` and `windowPages` are fixed for the life of the hook: changing one
37
+ * means every held page is cut differently, which is a different list — give it
38
+ * a new `queryKey` and a new mount instead. It throws rather than guessing.
39
+ */
40
+ export declare function usePagedWindow<T>({ pageSize, windowPages, loadPage, queryKey, enabled, }: UsePagedWindowOptions<T>): PagedWindow<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danxbot/ui",
3
- "version": "20.8.1",
3
+ "version": "20.10.0",
4
4
  "type": "module",
5
5
  "description": "Danxbot — a domain-agnostic React design system with motion as a first-class primitive.",
6
6
  "license": "MIT",
@@ -74,7 +74,7 @@
74
74
  "gates": "npm run shoot && npm run a11y && node scripts/shoot-overlays.mjs && npm run probe",
75
75
  "a11y": "node scripts/a11y-scan.mjs",
76
76
  "shoot": "node scripts/shoot.mjs",
77
- "probe": "node scripts/probe-scale.mjs && node scripts/probe-switch.mjs && node scripts/probe-slider.mjs && node scripts/probe-panel.mjs && node scripts/probe-popover-width.mjs && node scripts/probe-api.mjs && node scripts/probe-api-live.mjs && node scripts/probe-tabs-overflow.mjs && node scripts/probe-stat-row.mjs && node scripts/probe-packaging.mjs && node scripts/probe-containment.mjs && node scripts/probe-popover-containment.mjs && node scripts/probe-popover-panel-width.mjs && node scripts/probe-theme-menu-overflow.mjs && node scripts/probe-shell.mjs && node scripts/probe-nav-trigger-tap-target.mjs && node scripts/probe-attachment-touch.mjs && node scripts/probe-dense-button.mjs && node scripts/probe-card-header.mjs && node scripts/probe-inline-edit.mjs && node scripts/probe-read-more.mjs && node scripts/probe-section-nav.mjs && node scripts/probe-attention.mjs",
77
+ "probe": "node scripts/probe-scale.mjs && node scripts/probe-switch.mjs && node scripts/probe-slider.mjs && node scripts/probe-panel.mjs && node scripts/probe-popover-width.mjs && node scripts/probe-api.mjs && node scripts/probe-api-live.mjs && node scripts/probe-tabs-overflow.mjs && node scripts/probe-stat-row.mjs && node scripts/probe-packaging.mjs && node scripts/probe-containment.mjs && node scripts/probe-popover-containment.mjs && node scripts/probe-popover-panel-width.mjs && node scripts/probe-theme-menu-overflow.mjs && node scripts/probe-shell.mjs && node scripts/probe-nav-trigger-tap-target.mjs && node scripts/probe-attachment-touch.mjs && node scripts/probe-dense-button.mjs && node scripts/probe-card-header.mjs && node scripts/probe-inline-edit.mjs && node scripts/probe-read-more.mjs && node scripts/probe-section-nav.mjs && node scripts/probe-attention.mjs && node scripts/probe-paged-window.mjs",
78
78
  "sync": "node scripts/sync.mjs",
79
79
  "visual": "node scripts/visual-docker.mjs",
80
80
  "visual:update": "node scripts/visual-docker.mjs --update-snapshots",