@letterncms/ui 0.0.0-stage → 0.1.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 (77) hide show
  1. package/LICENSE.md +21 -0
  2. package/package.json +23 -5
  3. package/src/app/App.d.ts +3 -0
  4. package/src/app/Brand.d.ts +5 -0
  5. package/src/app/CommandPalette.d.ts +4 -0
  6. package/src/app/CustomizeSidebar.d.ts +8 -0
  7. package/src/app/ErrorBoundary.d.ts +16 -0
  8. package/src/app/Root.d.ts +1 -0
  9. package/src/app/Sidebar.d.ts +15 -0
  10. package/src/app/modules.d.ts +21 -0
  11. package/src/app/sidebarStore.d.ts +30 -0
  12. package/src/fields/CommandQueryField.d.ts +3 -0
  13. package/src/fields/FieldsEditor.d.ts +28 -0
  14. package/src/fields/LinkField.d.ts +16 -0
  15. package/src/fields/RichTextField.d.ts +6 -0
  16. package/src/fields/basic.d.ts +17 -0
  17. package/src/fields/complex.d.ts +6 -0
  18. package/src/fields/index.d.ts +6 -0
  19. package/src/lib/icon-data.d.ts +1 -0
  20. package/src/main.d.ts +1 -0
  21. package/src/screens/NotFound.d.ts +1 -0
  22. package/src/screens/auth/AuthScreen.d.ts +4 -0
  23. package/src/ui/Avatar.d.ts +16 -0
  24. package/src/ui/Badge.d.ts +14 -0
  25. package/src/ui/BlueprintEditor.d.ts +48 -0
  26. package/src/ui/Button.d.ts +19 -0
  27. package/src/ui/ComponentBoundary.d.ts +17 -0
  28. package/src/ui/DataCalendar.d.ts +23 -0
  29. package/src/ui/DataGrid.d.ts +22 -0
  30. package/src/ui/DataTable.d.ts +68 -0
  31. package/src/ui/DataView.d.ts +44 -0
  32. package/src/ui/Dialog.d.ts +32 -0
  33. package/src/ui/EmptyState.d.ts +8 -0
  34. package/src/ui/Field.d.ts +12 -0
  35. package/src/ui/Fields.d.ts +18 -0
  36. package/src/ui/Icon.d.ts +8 -0
  37. package/src/ui/Input.d.ts +15 -0
  38. package/src/ui/InspectorPanel.d.ts +11 -0
  39. package/src/ui/Kbd.d.ts +5 -0
  40. package/src/ui/List.d.ts +14 -0
  41. package/src/ui/Marquee.d.ts +5 -0
  42. package/src/ui/Menu.d.ts +37 -0
  43. package/src/ui/Page.d.ts +31 -0
  44. package/src/ui/PagePagination.d.ts +7 -0
  45. package/src/ui/PersonPicker.d.ts +12 -0
  46. package/src/ui/Popover.d.ts +13 -0
  47. package/src/ui/QueryError.d.ts +6 -0
  48. package/src/ui/Segmented.d.ts +36 -0
  49. package/src/ui/Select.d.ts +25 -0
  50. package/src/ui/SelectionBar.d.ts +7 -0
  51. package/src/ui/SidebarRow.d.ts +31 -0
  52. package/src/ui/Slideover.d.ts +21 -0
  53. package/src/ui/SortableTree.d.ts +30 -0
  54. package/src/ui/Spinner.d.ts +7 -0
  55. package/src/ui/Surface.d.ts +8 -0
  56. package/src/ui/SurfaceHost.d.ts +2 -0
  57. package/src/ui/Switch.d.ts +16 -0
  58. package/src/ui/Tooltip.d.ts +10 -0
  59. package/src/ui/ViewControls.d.ts +36 -0
  60. package/src/ui/Widget.d.ts +121 -0
  61. package/src/ui/WidgetSlot.d.ts +7 -0
  62. package/src/ui/calendar.d.ts +10 -0
  63. package/src/ui/cn.d.ts +2 -0
  64. package/src/ui/dateTime.d.ts +6 -0
  65. package/src/ui/entryActions.d.ts +13 -0
  66. package/src/ui/entryDisplay.d.ts +16 -0
  67. package/src/ui/fieldIdentity.d.ts +3 -0
  68. package/src/ui/index.d.ts +72 -0
  69. package/src/ui/overviewSource.d.ts +16 -0
  70. package/src/ui/recordDates.d.ts +13 -0
  71. package/src/ui/recordNavigation.d.ts +10 -0
  72. package/src/ui/shortcuts.d.ts +4 -0
  73. package/src/ui/slugify.d.ts +2 -0
  74. package/src/ui/theme.d.ts +4 -0
  75. package/src/ui/toast.d.ts +14 -0
  76. package/src/ui/viewPrefs.d.ts +17 -0
  77. package/README.md +0 -3
@@ -0,0 +1,8 @@
1
+ import { type SVGProps } from 'react';
2
+ export interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'name'> {
3
+ /** Semantic name (see icons.json), a registered plugin icon, or "svg:<path data>". */
4
+ name: string;
5
+ size?: number;
6
+ }
7
+ /** All iconography goes through here, so the icon set can be swapped in one place. */
8
+ export declare const Icon: import("react").MemoExoticComponent<({ name, size, className, children: _ignored, ...props }: IconProps) => import("react").JSX.Element>;
@@ -0,0 +1,15 @@
1
+ import { type InputHTMLAttributes, type TextareaHTMLAttributes } from 'react';
2
+ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
3
+ icon?: string;
4
+ size?: 'xs' | 'sm' | 'md' | 'lg';
5
+ invalid?: boolean;
6
+ suffix?: React.ReactNode;
7
+ }
8
+ export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
9
+ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
10
+ invalid?: boolean;
11
+ autoGrow?: boolean;
12
+ /** Keep shared sizing/focus behavior while the caller supplies document typography. */
13
+ appearance?: 'default' | 'plain';
14
+ }
15
+ export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
@@ -0,0 +1,11 @@
1
+ import { type ReactNode } from 'react';
2
+ /** A workspace inspector docks on desktop and becomes a focus-managed sheet on smaller screens. */
3
+ export declare function useWideInspector(): boolean;
4
+ export declare function InspectorPanel({ open, onOpenChange, title, children, className, docked }: {
5
+ open: boolean;
6
+ onOpenChange: (open: boolean) => void;
7
+ title: string;
8
+ children: ReactNode;
9
+ className?: string;
10
+ docked?: boolean;
11
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,5 @@
1
+ /** Keyboard shortcut. "mod" renders ⌘ on macOS and Ctrl elsewhere. */
2
+ export declare function Kbd({ keys, className }: {
3
+ keys: string;
4
+ className?: string;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import type { ReactNode } from 'react';
2
+ /** Dense row list (36px rows, hairline dividers). */
3
+ export declare function List({ children, className }: {
4
+ children: ReactNode;
5
+ className?: string;
6
+ }): import("react").JSX.Element;
7
+ export declare function Row({ to, onClick, children, selected, className, actions }: {
8
+ to?: string;
9
+ onClick?: () => void;
10
+ children: ReactNode;
11
+ selected?: boolean;
12
+ className?: string;
13
+ actions?: ReactNode;
14
+ }): import("react").JSX.Element;
@@ -0,0 +1,5 @@
1
+ /** Reveal the end of an overflowing title when its `cms-sidebar-row` is hovered. */
2
+ export declare function Marquee({ children, className }: {
3
+ children: string;
4
+ className?: string;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,37 @@
1
+ import type { ReactElement, ReactNode } from 'react';
2
+ export interface MenuItemDef {
3
+ label: string;
4
+ /** A second, quieter line under the label. */
5
+ description?: string;
6
+ icon?: string;
7
+ shortcut?: string;
8
+ danger?: boolean;
9
+ disabled?: boolean;
10
+ onSelect?: () => void;
11
+ /** Render as a group label instead of an item. */
12
+ heading?: boolean;
13
+ separator?: boolean;
14
+ checked?: boolean;
15
+ }
16
+ export declare function Menu({ trigger, items, align, side, className }: {
17
+ trigger: ReactElement;
18
+ items: (MenuItemDef | false | null | undefined)[];
19
+ align?: 'start' | 'center' | 'end';
20
+ side?: 'top' | 'bottom' | 'left' | 'right';
21
+ className?: string;
22
+ }): import("react").JSX.Element;
23
+ /** The same menu on right-click (or long-press) of its child — for actions on a row that has no room for a button. */
24
+ export declare function ContextMenu({ children, items, className }: {
25
+ children: ReactElement;
26
+ items: (MenuItemDef | false | null | undefined)[];
27
+ className?: string;
28
+ }): import("react").JSX.Element;
29
+ /** A generic menu panel item for custom popovers. */
30
+ export declare function MenuButton({ icon, label, onClick, active, hint, className }: {
31
+ icon?: string;
32
+ label: ReactNode;
33
+ onClick?: () => void;
34
+ active?: boolean;
35
+ hint?: ReactNode;
36
+ className?: string;
37
+ }): import("react").JSX.Element;
@@ -0,0 +1,31 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface Crumb {
3
+ label: ReactNode;
4
+ to?: string;
5
+ icon?: string;
6
+ }
7
+ /** Sticky 48px bar at the top of every screen: breadcrumbs left, actions right. */
8
+ export declare function Topbar({ crumbs, actions, children, className }: {
9
+ crumbs?: Crumb[];
10
+ actions?: ReactNode;
11
+ children?: ReactNode;
12
+ className?: string;
13
+ }): import("react").JSX.Element;
14
+ /** Standard screen: topbar + scrolling body with a readable max width. A title, no subtitle: the screen explains itself. */
15
+ export declare function Page({ crumbs, actions, title, headerActions, children, width, className }: {
16
+ crumbs?: Crumb[];
17
+ actions?: ReactNode;
18
+ title?: ReactNode;
19
+ headerActions?: ReactNode;
20
+ children: ReactNode;
21
+ width?: 'narrow' | 'default' | 'wide' | 'full';
22
+ className?: string;
23
+ }): import("react").JSX.Element;
24
+ /** A titled group inside a page — rows and dividers, not a card. */
25
+ export declare function Section({ title, description, actions, children, className }: {
26
+ title?: ReactNode;
27
+ description?: ReactNode;
28
+ actions?: ReactNode;
29
+ children: ReactNode;
30
+ className?: string;
31
+ }): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ export declare function PagePagination({ page, pages, total, perPage, onPage }: {
2
+ page: number;
3
+ pages: number;
4
+ total?: number;
5
+ perPage?: number;
6
+ onPage: (page: number) => void;
7
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,12 @@
1
+ interface PersonPickerProps {
2
+ value: string[];
3
+ onChange: (ids: string[]) => void;
4
+ max?: number;
5
+ disabled?: boolean;
6
+ label?: string;
7
+ id?: string;
8
+ 'aria-describedby'?: string;
9
+ }
10
+ /** Public summaries only. The single-person control keeps the same geometry in every state. */
11
+ export declare function PersonPicker({ value, onChange, max, disabled, label, id, 'aria-describedby': describedBy }: PersonPickerProps): import("react").JSX.Element;
12
+ export {};
@@ -0,0 +1,13 @@
1
+ import { type ReactElement, type ReactNode } from 'react';
2
+ export interface PopoverProps {
3
+ trigger: ReactElement;
4
+ children: ReactNode;
5
+ side?: 'top' | 'bottom' | 'left' | 'right';
6
+ align?: 'start' | 'center' | 'end';
7
+ open?: boolean;
8
+ onOpenChange?: (open: boolean) => void;
9
+ className?: string;
10
+ modal?: boolean;
11
+ returnFocus?: () => HTMLElement | null | false;
12
+ }
13
+ export declare function Popover({ trigger, children, side, align, open, onOpenChange, className, modal, returnFocus }: PopoverProps): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ /** A failed request remains distinguishable from a valid empty result. */
2
+ export declare function QueryError({ message, onRetry, compact }: {
3
+ message?: string;
4
+ onRetry: () => void;
5
+ compact?: boolean;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,36 @@
1
+ export interface SegmentOption<T extends string> {
2
+ value: T;
3
+ label?: string;
4
+ icon?: string;
5
+ title?: string;
6
+ }
7
+ /**
8
+ * Compact segmented control (lenses, breakpoints, alignment…). With no value, the
9
+ * `placeholder` option (the default that applies) shows lighter than a chosen one.
10
+ */
11
+ export declare function Segmented<T extends string>({ value, onChange, options, size, block, className, placeholder, id, disabled, 'aria-label': ariaLabel }: {
12
+ value: T;
13
+ onChange: (v: T) => void;
14
+ options: SegmentOption<T>[];
15
+ size?: 'xs' | 'sm';
16
+ block?: boolean;
17
+ className?: string;
18
+ placeholder?: T;
19
+ id?: string;
20
+ disabled?: boolean;
21
+ 'aria-label'?: string;
22
+ }): import("react").JSX.Element;
23
+ export declare function Tabs<T extends string>({ value, onChange, tabs, className, id, bordered, size }: {
24
+ value: T;
25
+ onChange: (v: T) => void;
26
+ tabs: {
27
+ value: T;
28
+ label: string;
29
+ count?: number;
30
+ indicator?: boolean;
31
+ }[];
32
+ className?: string;
33
+ id?: string;
34
+ bordered?: boolean;
35
+ size?: 'xs' | 'sm';
36
+ }): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface SelectOption {
3
+ value: string;
4
+ label: string;
5
+ icon?: string;
6
+ description?: string;
7
+ }
8
+ export interface SelectProps {
9
+ value: string | null;
10
+ onChange: (value: string | null) => void;
11
+ options: SelectOption[];
12
+ placeholder?: string;
13
+ /** A compact state hint beside the selected value, such as an inherited setting. */
14
+ valueHint?: ReactNode;
15
+ size?: 'xs' | 'sm' | 'md';
16
+ disabled?: boolean;
17
+ invalid?: boolean;
18
+ className?: string;
19
+ clearable?: boolean;
20
+ clearLabel?: string;
21
+ id?: string;
22
+ 'aria-label'?: string;
23
+ 'aria-describedby'?: string;
24
+ }
25
+ export declare function Select({ value, onChange, options, placeholder, valueHint, size, disabled, invalid, className, clearable, clearLabel, id, 'aria-label': ariaLabel, 'aria-describedby': describedBy }: SelectProps): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { type ReactNode } from 'react';
2
+ /** Floats over the list while entries are selected, so nothing below moves. */
3
+ export declare function SelectionBar({ count, children, onClear }: {
4
+ count: number;
5
+ children: ReactNode;
6
+ onClear: () => void;
7
+ }): import("react").JSX.Element;
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type MenuItemDef } from './Menu';
3
+ export interface SidebarRowAction extends MenuItemDef {
4
+ /** Also a button on the row, shown on hover and focus (it needs an `icon`). */
5
+ quick?: boolean;
6
+ }
7
+ export interface SidebarRowProps {
8
+ label: string;
9
+ icon?: string;
10
+ to?: string;
11
+ onClick?: () => void;
12
+ active?: boolean;
13
+ /** The rail: the icon alone, its label in a tooltip. */
14
+ collapsed?: boolean;
15
+ title?: string;
16
+ /** A count, or a dot in the rail. */
17
+ badge?: ReactNode;
18
+ shortcut?: string | null;
19
+ status?: ReactNode;
20
+ /** Everything the row can do, in its context menu; `quick` ones are buttons on the row too. */
21
+ actions?: (SidebarRowAction | false | null | undefined)[];
22
+ /** A row being dragged into place must not drag its link out of the page. */
23
+ sortable?: boolean;
24
+ }
25
+ /**
26
+ * Every row in the sidebar: a link or button, its label sliding to show its end
27
+ * when it does not fit, quick actions on hover, and all of them on right-click
28
+ * (or long-press) after Open in New Tab. Navigation and actions are siblings so
29
+ * buttons never live inside a link.
30
+ */
31
+ export declare function SidebarRow({ label, icon, to, onClick, active, collapsed, title, badge, shortcut, status, actions, sortable }: SidebarRowProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { type ReactNode, type RefObject } from 'react';
2
+ interface SlideoverProps {
3
+ id: string;
4
+ open: boolean;
5
+ expanded: boolean;
6
+ onOpenChange: (open: boolean) => void;
7
+ onExpandedChange: (expanded: boolean) => void;
8
+ title: ReactNode;
9
+ icon?: string;
10
+ actions?: ReactNode;
11
+ children: ReactNode;
12
+ className?: string;
13
+ }
14
+ /** Keeps workspace overlays anchored to the content area, independently of sidebar width. */
15
+ export declare const WorkspaceContext: import("react").Context<{
16
+ host: HTMLDivElement | null;
17
+ content: RefObject<HTMLDivElement | null>;
18
+ } | null>;
19
+ /** A persistent workspace overlay. Expansion leaves global navigation available. */
20
+ export declare function Slideover({ id, open, expanded, onOpenChange, onExpandedChange, title, icon, actions, children, className }: SlideoverProps): import("react").JSX.Element;
21
+ export {};
@@ -0,0 +1,30 @@
1
+ import { type ReactNode } from 'react';
2
+ /** A tree node as the editor sees it: any data plus children. */
3
+ export interface TreeItem {
4
+ id: string;
5
+ children?: TreeItem[];
6
+ [key: string]: unknown;
7
+ }
8
+ export interface SortableTreeProps<T extends TreeItem> {
9
+ items: T[];
10
+ onChange: (items: T[]) => void;
11
+ /** Row content. `handle` is the drag grip; place it where it fits. */
12
+ renderItem: (item: T, ctx: {
13
+ depth: number;
14
+ handle: ReactNode;
15
+ indent: () => void;
16
+ outdent: () => void;
17
+ canIndent: boolean;
18
+ canOutdent: boolean;
19
+ }) => ReactNode;
20
+ maxDepth?: number;
21
+ /** Pixels per level; also the horizontal drag distance that changes the level. */
22
+ indent?: number;
23
+ className?: string;
24
+ }
25
+ /**
26
+ * Drag-and-drop tree editor (menus, page trees). Drag vertically to reorder,
27
+ * sideways to nest; children travel with their parent. Keyboard: focus the
28
+ * grip, space to lift, arrows to move, space to drop.
29
+ */
30
+ export declare function SortableTree<T extends TreeItem>({ items, onChange, renderItem, maxDepth, indent, className }: SortableTreeProps<T>): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ export declare function Spinner({ size, className }: {
2
+ size?: number;
3
+ className?: string;
4
+ }): import("react").JSX.Element;
5
+ export declare function Skeleton({ className }: {
6
+ className?: string;
7
+ }): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { type SurfaceAction, type SurfaceDocument } from '@letterncms/sdk';
2
+ /** A native json-render spec, backed by the current CMS registry and design system. */
3
+ export interface SurfaceProps {
4
+ document: SurfaceDocument;
5
+ disabled?: boolean;
6
+ onAction?: (action: SurfaceAction) => void;
7
+ }
8
+ export declare function Surface({ document, disabled, onAction }: SurfaceProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import type { SurfaceProps } from './Surface';
2
+ export declare function Surface(props: SurfaceProps): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function Switch({ checked, onChange, disabled, label, size, 'aria-label': ariaLabel }: {
3
+ checked: boolean;
4
+ onChange: (v: boolean) => void;
5
+ disabled?: boolean;
6
+ label?: ReactNode;
7
+ size?: 'sm' | 'md';
8
+ 'aria-label'?: string;
9
+ }): import("react").JSX.Element;
10
+ export declare function Checkbox({ checked, onChange, disabled, label, indeterminate }: {
11
+ checked: boolean;
12
+ onChange: (v: boolean) => void;
13
+ disabled?: boolean;
14
+ label?: ReactNode;
15
+ indeterminate?: boolean;
16
+ }): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import type { ReactElement, ReactNode } from 'react';
2
+ export declare function TooltipProvider({ children }: {
3
+ children: ReactNode;
4
+ }): import("react").JSX.Element;
5
+ export declare function Tooltip({ content, shortcut, side, children }: {
6
+ content: ReactNode;
7
+ shortcut?: string;
8
+ side?: 'top' | 'bottom' | 'left' | 'right';
9
+ children: ReactElement;
10
+ }): import("react").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ import type { CalendarDate } from './viewPrefs';
3
+ export type OverviewView = 'table' | 'grid' | 'calendar';
4
+ export interface ViewColumn {
5
+ id: string;
6
+ label: string;
7
+ sort?: string;
8
+ descFirst?: boolean;
9
+ field?: unknown;
10
+ }
11
+ /** Shared display preferences for every overview, including collection entries. */
12
+ export declare function DisplayOptions({ view, all, shown, sortable, sortKey, isTree, cardColumns, onColumns, onSort, onReset, calendarDate, onCalendarDate }: {
13
+ view: OverviewView;
14
+ all: ViewColumn[];
15
+ shown: Set<string>;
16
+ sortable: ViewColumn[];
17
+ sortKey: string | null;
18
+ isTree?: boolean;
19
+ cardColumns?: string[];
20
+ onColumns: (ids: string[]) => void;
21
+ onSort: (key: string | null) => void;
22
+ onReset?: () => void;
23
+ calendarDate?: CalendarDate;
24
+ onCalendarDate?: (date: CalendarDate) => void;
25
+ }): import("react").JSX.Element;
26
+ export declare function ViewSwitcher({ value, onChange }: {
27
+ value: OverviewView;
28
+ onChange: (view: OverviewView) => void;
29
+ }): import("react").JSX.Element;
30
+ /** Every overview presents the same menu, view choices and reset behavior. */
31
+ export declare function OverviewControls({ onView, children, ...display }: ComponentProps<typeof DisplayOptions> & {
32
+ onView: (view: OverviewView) => void;
33
+ children?: ReactNode;
34
+ }): import("react").JSX.Element;
35
+ /** Header clicks cycle natural direction, reverse, then the source's default order. */
36
+ export declare function nextTableSort(field: string, current: string | null, descFirst?: boolean, implicit?: string | null): string | null;
@@ -0,0 +1,121 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * Home-screen widgets. The home screen gives each widget its registration (label, icon)
4
+ * through context; the widget draws its body inside `<Widget>`, which supplies the
5
+ * header, loading skeleton, error and empty states — so every widget, core or plugin,
6
+ * looks and behaves the same:
7
+ *
8
+ * export default function LatestReviews() {
9
+ * const reviews = useAbility<Review[]>('reviews/latest', { limit: 5 })
10
+ * return (
11
+ * <Widget action={{ label: t('All Reviews'), to: '/reviews' }} query={reviews} empty={reviews.data?.length === 0 && { icon: 'star', title: t('No reviews yet') }}>
12
+ * {reviews.data?.map((r) => <WidgetRow key={r.id} to={`/reviews/${r.id}`} icon="star" title={r.title} meta={timeAgo(r.createdAt)} />)}
13
+ * </Widget>
14
+ * )
15
+ * }
16
+ *
17
+ * The body sits in a container (`@container`), so a widget can adapt to the column it is
18
+ * in with container variants (`@md:grid-cols-4`) — the main column is wide, the side one narrow.
19
+ */
20
+ export interface WidgetMeta {
21
+ id: string;
22
+ label: string;
23
+ icon: string;
24
+ }
25
+ export declare const WidgetContext: import("react").Context<WidgetMeta | null>;
26
+ /** The registration of the widget being rendered (label, icon), when it is on the home screen. */
27
+ export declare function useWidget(): WidgetMeta | null;
28
+ export interface WidgetEmpty {
29
+ icon?: string;
30
+ title: string;
31
+ description?: ReactNode;
32
+ action?: ReactNode;
33
+ }
34
+ export interface WidgetProps {
35
+ /** Defaults to the registered label and icon. */
36
+ title?: ReactNode;
37
+ icon?: string;
38
+ /** A link in the header ("View all"), or any node (a menu, a button). */
39
+ action?: {
40
+ label: string;
41
+ to?: string;
42
+ onClick?: () => void;
43
+ } | ReactNode;
44
+ /** A react-query result: loading shows the skeleton, an error a retry. */
45
+ query?: {
46
+ isLoading: boolean;
47
+ isError?: boolean;
48
+ refetch?: () => unknown;
49
+ };
50
+ loading?: boolean;
51
+ /** The loading placeholder; rows by default. */
52
+ skeleton?: ReactNode;
53
+ /** Shown instead of the body when truthy. */
54
+ empty?: WidgetEmpty | false | null;
55
+ children?: ReactNode;
56
+ className?: string;
57
+ }
58
+ export declare function Widget({ title, icon, action, query, loading, skeleton, empty, children, className }: WidgetProps): import("react").JSX.Element;
59
+ /** Placeholder rows while a widget loads. */
60
+ export declare function WidgetSkeleton({ rows }: {
61
+ rows?: number;
62
+ }): import("react").JSX.Element;
63
+ /** The quiet empty (or error) state inside a widget: what it is, and one action. */
64
+ export declare function WidgetMessage({ icon, title, description, action }: WidgetEmpty): import("react").JSX.Element;
65
+ /**
66
+ * A row in a widget list: something to open, with an icon (or any leading node),
67
+ * a title, an optional second line and a quiet value on the right.
68
+ */
69
+ export declare function WidgetRow({ to, onClick, href, icon, leading, title, description, meta, className }: {
70
+ to?: string;
71
+ onClick?: () => void;
72
+ href?: string;
73
+ icon?: string;
74
+ leading?: ReactNode;
75
+ title: ReactNode;
76
+ description?: ReactNode;
77
+ meta?: ReactNode;
78
+ className?: string;
79
+ }): import("react").JSX.Element;
80
+ /** 1,284 · 12.9K · 4.2M in the admin's language. */
81
+ export declare function compactNumber(value: number): string;
82
+ /**
83
+ * A headline number: label, value, change against the previous period and an optional trend.
84
+ * `delta` is a fraction (0.12 = +12%); `good` says which direction is good news.
85
+ */
86
+ export declare function Stat({ label, value, delta, good, trend, size, className }: {
87
+ label: ReactNode;
88
+ value: ReactNode;
89
+ delta?: number | null;
90
+ good?: 'up' | 'down';
91
+ trend?: number[];
92
+ size?: 'sm' | 'md' | 'lg';
93
+ className?: string;
94
+ }): import("react").JSX.Element;
95
+ /** Change as a signed percentage, green when it is good news. */
96
+ export declare function Delta({ value, good }: {
97
+ value: number;
98
+ good?: 'up' | 'down';
99
+ }): import("react").JSX.Element;
100
+ /** A small trend line with a soft wash — one series, no axes (details live on the screen it links to). */
101
+ export declare function Sparkline({ values, className, label }: {
102
+ values: number[];
103
+ className?: string;
104
+ label?: string;
105
+ }): import("react").JSX.Element | null;
106
+ /** A filled bar for a share or a score (0–1). The tone carries the state; the label stays in text color. */
107
+ export declare function Meter({ value, tone, className, label }: {
108
+ value: number;
109
+ tone?: 'ink' | 'success' | 'warning' | 'danger' | 'muted';
110
+ className?: string;
111
+ label?: string;
112
+ }): import("react").JSX.Element;
113
+ /** A row with a proportional bar behind it — top pages, sources, devices. */
114
+ export declare function BarRow({ label, value, share, icon, to, href }: {
115
+ label: ReactNode;
116
+ value: ReactNode;
117
+ share: number;
118
+ icon?: ReactNode;
119
+ to?: string;
120
+ href?: string;
121
+ }): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type WidgetMeta } from './Widget';
3
+ /** One rendering path for home, chat and any other host of registered widgets. */
4
+ export declare function WidgetSlot({ widget, fallback }: {
5
+ widget: WidgetMeta;
6
+ fallback?: ReactNode;
7
+ }): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null;
@@ -0,0 +1,10 @@
1
+ export declare const calendarDayKey: (date: Date) => string;
2
+ export declare function calendarRange(value?: string): {
3
+ month: string;
4
+ year: number;
5
+ monthIndex: number;
6
+ first: Date;
7
+ start: Date;
8
+ end: Date;
9
+ days: Date[];
10
+ };
package/src/ui/cn.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** Join class names, skipping falsy values. */
2
+ export declare function cn(...classes: (string | false | null | undefined)[]): string;
@@ -0,0 +1,6 @@
1
+ /** datetime-local controls represent wall time; scheduling stores an unambiguous UTC instant. */
2
+ export declare function formatDateTimeInZone(iso: string, timeZone: string): string;
3
+ export declare function parseDateTimeInZone(wallTime: string, timeZone: string): {
4
+ value: string | null;
5
+ error?: 'invalid' | 'nonexistent' | 'ambiguous';
6
+ };
@@ -0,0 +1,13 @@
1
+ import type { EntryCapabilities, EntrySummary } from '@letterncms/sdk';
2
+ /** The editor, row menus, bulk bar and shortcuts share one capability policy. null means a new entry. */
3
+ export declare function entryActionAvailability(can: EntryCapabilities | undefined, entry?: Pick<EntrySummary, 'status'> | null): {
4
+ create: boolean;
5
+ edit: boolean;
6
+ duplicate: boolean;
7
+ delete: boolean;
8
+ publish: boolean;
9
+ unpublish: boolean;
10
+ restore: boolean;
11
+ discard: boolean;
12
+ move: boolean;
13
+ };
@@ -0,0 +1,16 @@
1
+ import { type FieldDef } from '@letterncms/sdk';
2
+ export declare const fieldColumnId: (handle: string) => string;
3
+ export declare function entryFieldColumns(fields: FieldDef[]): {
4
+ id: string;
5
+ label: string;
6
+ width: string | number;
7
+ field: FieldDef;
8
+ sort: string | undefined;
9
+ descFirst: boolean;
10
+ align: "end" | undefined;
11
+ }[];
12
+ export declare function entrySystemColumns(routable: boolean, locales: string[]): {
13
+ id: string;
14
+ label: string;
15
+ }[];
16
+ export declare function standardEntryColumns(routable: boolean, listing?: string[], view?: string, fields?: FieldDef[]): string[];
@@ -0,0 +1,3 @@
1
+ import type { FieldDef } from '@letterncms/sdk';
2
+ /** Form identity follows the value path, so nested names never collide with record fields. */
3
+ export declare function fieldControlId(field: Pick<FieldDef, 'handle' | 'id'>, path?: string[]): string;