@karni/ui 0.0.0-stage → 0.3.1

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 (96) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +288 -2
  3. package/dist/index.cjs +9 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.js +4764 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/manifest.json +85 -0
  8. package/dist/mockServiceWorker.js +361 -0
  9. package/dist/src/auth/AuthGuard.d.ts +12 -0
  10. package/dist/src/auth/LoginForm.d.ts +29 -0
  11. package/dist/src/auth/engine.d.ts +99 -0
  12. package/dist/src/auth/provider.d.ts +43 -0
  13. package/dist/src/auth/storage.d.ts +26 -0
  14. package/dist/src/auth/types.d.ts +32 -0
  15. package/dist/src/auth/usePermission.d.ts +7 -0
  16. package/dist/src/components/brand/karni-mark.d.ts +21 -0
  17. package/dist/src/components/brand/mark-paths.d.ts +8 -0
  18. package/dist/src/components/chart/data-chart.d.ts +74 -0
  19. package/dist/src/components/empty-state/empty-state.d.ts +20 -0
  20. package/dist/src/components/file-upload/FileUpload.d.ts +44 -0
  21. package/dist/src/components/layout/layout.d.ts +55 -0
  22. package/dist/src/components/modal/modal.d.ts +45 -0
  23. package/dist/src/components/page/page.d.ts +29 -0
  24. package/dist/src/components/primitives/button.d.ts +44 -0
  25. package/dist/src/components/primitives/checkbox.d.ts +5 -0
  26. package/dist/src/components/primitives/dropdown-menu.d.ts +24 -0
  27. package/dist/src/components/primitives/icons.d.ts +11 -0
  28. package/dist/src/components/primitives/input.d.ts +4 -0
  29. package/dist/src/components/primitives/label.d.ts +5 -0
  30. package/dist/src/components/primitives/select.d.ts +63 -0
  31. package/dist/src/components/primitives/skeleton.d.ts +4 -0
  32. package/dist/src/components/primitives/switch.d.ts +5 -0
  33. package/dist/src/components/primitives/tabs.d.ts +10 -0
  34. package/dist/src/components/primitives/textarea.d.ts +4 -0
  35. package/dist/src/components/resource/ResourceDetail.d.ts +7 -0
  36. package/dist/src/components/resource/ResourceForm.d.ts +7 -0
  37. package/dist/src/components/resource/ResourceSelect.d.ts +7 -0
  38. package/dist/src/components/resource/ResourceTable.d.ts +11 -0
  39. package/dist/src/components/resource/audit-fields.d.ts +17 -0
  40. package/dist/src/components/resource/detail/ResourceDetail.d.ts +30 -0
  41. package/dist/src/components/resource/displayLabel.d.ts +12 -0
  42. package/dist/src/components/resource/form/ResourceForm.d.ts +50 -0
  43. package/dist/src/components/resource/form/field-inputs.d.ts +20 -0
  44. package/dist/src/components/resource/form/fixtures.d.ts +24 -0
  45. package/dist/src/components/resource/form/prepare.d.ts +54 -0
  46. package/dist/src/components/resource/history/ChangeHistory.d.ts +18 -0
  47. package/dist/src/components/resource/import/ResourceImport.d.ts +18 -0
  48. package/dist/src/components/resource/select/ResourceSelect.d.ts +36 -0
  49. package/dist/src/components/resource/select/use-debounced-value.d.ts +2 -0
  50. package/dist/src/components/resource/table/BulkActionsBar.d.ts +20 -0
  51. package/dist/src/components/resource/table/ExportButtons.d.ts +17 -0
  52. package/dist/src/components/resource/table/FilterBar.d.ts +29 -0
  53. package/dist/src/components/resource/table/PaginationControls.d.ts +19 -0
  54. package/dist/src/components/resource/table/ResourceTable.d.ts +72 -0
  55. package/dist/src/components/resource/table/demo-fixtures.d.ts +25 -0
  56. package/dist/src/components/resource/table/derive-columns.d.ts +33 -0
  57. package/dist/src/components/resource/table/table-utils.d.ts +24 -0
  58. package/dist/src/components/resource/table/use-debounced-value.d.ts +1 -0
  59. package/dist/src/components/stat/stat.d.ts +24 -0
  60. package/dist/src/components/toast/toast-store.d.ts +36 -0
  61. package/dist/src/components/toast/toaster.d.ts +33 -0
  62. package/dist/src/components/toast/use-toast.d.ts +24 -0
  63. package/dist/src/hooks/keys.d.ts +24 -0
  64. package/dist/src/hooks/multipart.d.ts +44 -0
  65. package/dist/src/hooks/params.d.ts +25 -0
  66. package/dist/src/hooks/types.d.ts +41 -0
  67. package/dist/src/hooks/useChangeHistory.d.ts +29 -0
  68. package/dist/src/hooks/useResourceImport.d.ts +107 -0
  69. package/dist/src/hooks/useResourceItem.d.ts +4 -0
  70. package/dist/src/hooks/useResourceList.d.ts +4 -0
  71. package/dist/src/hooks/useResourceMutations.d.ts +13 -0
  72. package/dist/src/index.d.ts +95 -0
  73. package/dist/src/lib/cn.d.ts +8 -0
  74. package/dist/src/lib/roles.d.ts +45 -0
  75. package/dist/src/lib/variants.d.ts +33 -0
  76. package/dist/src/schema/client.d.ts +90 -0
  77. package/dist/src/schema/parse.d.ts +101 -0
  78. package/dist/src/schema/provider.d.ts +13 -0
  79. package/dist/src/schema/types.d.ts +84 -0
  80. package/dist/src/schema/useSchema.d.ts +17 -0
  81. package/dist/src/schema/zod.d.ts +4 -0
  82. package/dist/styles.css +87 -0
  83. package/dist/tailwind-preset.cjs +2 -0
  84. package/dist/tailwind-preset.cjs.map +1 -0
  85. package/dist/tailwind-preset.d.ts +58 -0
  86. package/dist/tailwind-preset.js +64 -0
  87. package/dist/tailwind-preset.js.map +1 -0
  88. package/eslint-plugin/index.js +72 -0
  89. package/eslint-plugin/lib/docs.js +11 -0
  90. package/eslint-plugin/lib/glob.js +51 -0
  91. package/eslint-plugin/rules/max-page-lines.js +65 -0
  92. package/eslint-plugin/rules/no-deep-imports.js +61 -0
  93. package/eslint-plugin/rules/no-direct-http.js +128 -0
  94. package/eslint-plugin/rules/no-raw-elements.js +65 -0
  95. package/eslint-plugin/rules.json +26 -0
  96. package/package.json +153 -4
@@ -0,0 +1,7 @@
1
+ export interface PermissionHelpers {
2
+ /** True when the user has at least one of the given role names. */
3
+ hasRole(...names: string[]): boolean;
4
+ isAuthenticated: boolean;
5
+ }
6
+ /** `isAuthenticated` and `hasRole(...names)` for the signed-in user. */
7
+ export declare function usePermission(): PermissionHelpers;
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ export interface KarniMarkProps extends Omit<React.SVGAttributes<SVGSVGElement>, "children"> {
3
+ /** Rendered size in px (width and height). Default 28. */
4
+ size?: number;
5
+ /**
6
+ * Which drawing: the full mark, the small-size variant, or `auto` (the
7
+ * default) — the small variant at 32px and below, the full mark above.
8
+ */
9
+ variant?: "auto" | "full" | "simple";
10
+ /** Accessible name; pass an empty string to hide a purely decorative mark. */
11
+ title?: string;
12
+ }
13
+ /**
14
+ * The Karni mark, inline SVG — no asset to load or bundle.
15
+ *
16
+ * It is the default content of `<Layout>`'s `sidebarHeader` slot so a bare
17
+ * shell is never nameless; a project replaces it by passing its own
18
+ * `sidebarHeader` (the scaffold already does). Do not recolour or restyle it:
19
+ * the mark is Karni's, see assets/brand/README.md in the repository.
20
+ */
21
+ export declare const KarniMark: React.ForwardRefExoticComponent<KarniMarkProps & React.RefAttributes<SVGSVGElement>>;
@@ -0,0 +1,8 @@
1
+ export declare const MARK_VIEWBOX = "0 0 1000 1000";
2
+ /** The full mark: ground, rings, strokes. */
3
+ export declare const MARK_PATHS: ReadonlyArray<{
4
+ readonly fill: string;
5
+ readonly d: string;
6
+ }>;
7
+ /** The small-size variant's SVG body (use at 32px and below). */
8
+ export declare const MARK_SIMPLE_BODY = "<path fill=\"#000000\" d=\"M495.8 884.4C291.9 874.2 135.4 724 114.7 518.5C97.9 351.3 160.2 221 290.9 150.2C372.3 106.1 439.7 86.4 513.6 85.2C686 82.3 812.9 171.5 867 333.5C903.9 444 890.2 573.8 828.6 698.6C766.8 823.4 644.3 891.8 495.8 884.4Z\"/>\n<circle cx=\"340\" cy=\"300\" r=\"44\" fill=\"#FB5D08\"/>\n<circle cx=\"500\" cy=\"276\" r=\"44\" fill=\"#FB5D08\"/>\n<circle cx=\"660\" cy=\"300\" r=\"44\" fill=\"#FB5D08\"/>\n<path fill=\"none\" stroke=\"#FFFFFF\" stroke-width=\"58\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n d=\"M318 420v280l182-250 182 250V420M500 450 410 700M500 450l90 250\"/>";
@@ -0,0 +1,74 @@
1
+ import * as React from "react";
2
+ /**
3
+ * In-house SVG chart (contract: no chart dependency).
4
+ *
5
+ * Accessibility model (contract D10): the SVG is a labelled image
6
+ * (role="img" + aria-label + <title>/<desc>); the REAL accessible data
7
+ * surface is a visually-hidden <table> rendered next to it. The hover
8
+ * tooltip is a visual-only affordance rendered OUTSIDE the role="img"
9
+ * element and hidden from assistive tech.
10
+ *
11
+ * Colors come exclusively from the --karni-chart-1..5 tokens, via the
12
+ * Tailwind preset's chart color scale (stroke-chart-N / fill-chart-N /
13
+ * bg-chart-N classes — whole class names, per contract D7).
14
+ */
15
+ export type DataChartKind = "line" | "bar";
16
+ export interface DataChartPoint {
17
+ label: string;
18
+ value: number;
19
+ }
20
+ export interface DataChartSeries {
21
+ name: string;
22
+ data: readonly DataChartPoint[];
23
+ }
24
+ export interface DataChartTooltipContext {
25
+ /** Category (x-axis) label under the pointer. */
26
+ label: string;
27
+ /** One entry per series that has a value at this category. */
28
+ points: ReadonlyArray<{
29
+ seriesName: string;
30
+ value: number;
31
+ swatchClassName: string;
32
+ }>;
33
+ }
34
+ export interface DataChartProps {
35
+ kind: DataChartKind;
36
+ /** Single-series shorthand (ignored when `series` is given). */
37
+ data?: readonly DataChartPoint[];
38
+ /** Multi-series data; series cycle through --karni-chart-1..5. */
39
+ series?: readonly DataChartSeries[];
40
+ /** Chart title: SVG <title>, aria-label and the hidden table's caption. */
41
+ title: string;
42
+ /** Longer description for the SVG <desc>. */
43
+ description?: string;
44
+ /** Formats values in the tooltip, y-axis ticks and the hidden table. */
45
+ valueFormatter?: (value: number) => string;
46
+ /** Formats x-axis tick labels (default: the category label itself). */
47
+ xTickFormatter?: (label: string) => string;
48
+ /** Escape hatch: fully custom tooltip content. */
49
+ renderTooltip?: (context: DataChartTooltipContext) => React.ReactNode;
50
+ /** viewBox width (default 640) — rendered size is responsive. */
51
+ width?: number;
52
+ /** viewBox height (default 240). */
53
+ height?: number;
54
+ /** Approximate number of y-axis ticks (default 4). */
55
+ yTickCount?: number;
56
+ /** Show the series legend (default: only for 2+ series). */
57
+ showLegend?: boolean;
58
+ /** Show the hover tooltip (default true). */
59
+ showTooltip?: boolean;
60
+ /** Column header of the hidden table's category column (default "Label"). */
61
+ categoryHeader?: string;
62
+ /** Text shown when there is no plottable data. */
63
+ emptyLabel?: string;
64
+ className?: string;
65
+ }
66
+ /**
67
+ * Minimal line/bar chart driven by `{label, value}` points or named series.
68
+ *
69
+ * Escape hatches: `valueFormatter`/`xTickFormatter`, `renderTooltip`,
70
+ * `width`/`height`/`yTickCount`, legend and tooltip toggles, `className`
71
+ * (merged via cn()). Colors are only themeable through the
72
+ * --karni-chart-1..5 CSS variables.
73
+ */
74
+ export declare function DataChart({ kind, data, series, title, description, valueFormatter, xTickFormatter, renderTooltip, width, height, yTickCount, showLegend, showTooltip, categoryHeader, emptyLabel, className, }: DataChartProps): React.ReactElement;
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ export interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
3
+ /** Optional illustration/icon slot, rendered above the title. */
4
+ icon?: React.ReactNode;
5
+ /** Short headline ("No events yet"). */
6
+ title: React.ReactNode;
7
+ /** Optional supporting copy under the title. */
8
+ description?: React.ReactNode;
9
+ /** Slot for a call to action (e.g. a "Create" button). */
10
+ action?: React.ReactNode;
11
+ children?: React.ReactNode;
12
+ }
13
+ /**
14
+ * Centered placeholder for empty collections: icon, title, description and
15
+ * an action slot. Used by ResourceTable's `empty` slot and reusable anywhere.
16
+ *
17
+ * Escape hatches: every visual part is a slot; `className` merges via cn();
18
+ * extra children render under the action.
19
+ */
20
+ export declare const EmptyState: React.ForwardRefExoticComponent<EmptyStateProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,44 @@
1
+ import * as React from "react";
2
+ /** Why a file was refused by <FileUpload> (surfaced through `onReject`). */
3
+ export type FileUploadRejectReason = "accept" | "size";
4
+ export interface FileUploadProps {
5
+ /**
6
+ * Current value. A `string` is an existing uploaded file (the display URL a
7
+ * karni backend returns); a `File` is a pending upload. Arrays are for
8
+ * `multiple` mode and may mix both.
9
+ */
10
+ value?: File | string | ReadonlyArray<File | string> | null;
11
+ /**
12
+ * Emits the newly chosen `File` (or `File[]` when `multiple`), and `null`
13
+ * when the value is cleared. Existing URL strings are display-only: they
14
+ * can be removed (emitting `null` / the remaining files) but never re-emitted.
15
+ */
16
+ onChange?: (value: File | File[] | null) => void;
17
+ /** Same grammar as the native `accept` attribute (".png,image/*,application/pdf"). */
18
+ accept?: string;
19
+ multiple?: boolean;
20
+ disabled?: boolean;
21
+ /** Maximum size per file, in bytes. Larger files are rejected via `onReject`. */
22
+ maxSize?: number;
23
+ /** Called once per refused file with the reason ("accept" | "size"). */
24
+ onReject?: (file: File, reason: FileUploadRejectReason) => void;
25
+ id?: string;
26
+ /** Forwarded to the hidden native file input. */
27
+ name?: string;
28
+ className?: string;
29
+ /** Replaces the default dropzone content (escape hatch). */
30
+ children?: React.ReactNode;
31
+ /** Set false to hide the per-file remove affordance. Default true. */
32
+ clearable?: boolean;
33
+ "aria-label"?: string;
34
+ }
35
+ /**
36
+ * Drag-and-drop or click-to-browse file picker that shows the current file and the pending ones.
37
+ *
38
+ * Contract "FileUpload".
39
+ *
40
+ * Shows an existing uploaded value (URL string) as a link, pending `File`
41
+ * values with name and size, and emits `File` objects — the hooks layer turns
42
+ * them into multipart bodies per amendment D2.
43
+ */
44
+ export declare function FileUpload(props: FileUploadProps): React.ReactElement;
@@ -0,0 +1,55 @@
1
+ import * as React from "react";
2
+ export interface LayoutNavItem {
3
+ /** Visible label of the nav entry. */
4
+ label: React.ReactNode;
5
+ /** Target URL (rendered as a plain <a> by default). */
6
+ href: string;
7
+ /** Optional icon rendered before the label. */
8
+ icon?: React.ReactNode;
9
+ /** Marks the current page (aria-current="page" + active styling). */
10
+ active?: boolean;
11
+ /** React key override when `href` is not unique. */
12
+ key?: string;
13
+ }
14
+ export interface LayoutProps extends React.HTMLAttributes<HTMLDivElement> {
15
+ /** Items of the derived sidebar nav. */
16
+ navItems?: readonly LayoutNavItem[];
17
+ /**
18
+ * Escape hatch for router integration: wraps/replaces the default anchor of
19
+ * one item. `defaultNode` is the styled <a> the layout would render — return
20
+ * it, or your own node (e.g. a TanStack Router <Link> with
21
+ * `className={defaultClassName}`).
22
+ */
23
+ renderNavItem?: (item: LayoutNavItem, defaults: {
24
+ defaultNode: React.ReactNode;
25
+ defaultClassName: string;
26
+ }) => React.ReactNode;
27
+ /** Escape hatch: replaces the entire derived nav inside the sidebar. */
28
+ sidebar?: React.ReactNode;
29
+ /**
30
+ * Brand/logo slot pinned to the top of the sidebar. Defaults to the Karni
31
+ * mark (`<KarniMark />`); pass your own brand, or `null` for no header.
32
+ */
33
+ sidebarHeader?: React.ReactNode;
34
+ /** Slot pinned to the bottom of the sidebar (user menu, logout, …). */
35
+ sidebarFooter?: React.ReactNode;
36
+ /** Header slot, rendered in the top bar next to the mobile menu button. */
37
+ header?: React.ReactNode;
38
+ /** Accessible name of the sidebar navigation (default "Main"). */
39
+ navLabel?: string;
40
+ sidebarClassName?: string;
41
+ headerClassName?: string;
42
+ contentClassName?: string;
43
+ children?: React.ReactNode;
44
+ }
45
+ /**
46
+ * App shell: sidebar navigation + header slot + content area.
47
+ *
48
+ * Responsive: under the `md` breakpoint the sidebar collapses behind a menu
49
+ * button and opens as an overlay drawer (Escape or backdrop click closes it).
50
+ *
51
+ * Escape hatches: `renderNavItem` wraps each derived link (router
52
+ * integration), `sidebar` replaces the derived nav wholesale, and
53
+ * `sidebarHeader`/`sidebarFooter`/`header` are free slots.
54
+ */
55
+ export declare const Layout: React.ForwardRefExoticComponent<LayoutProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,45 @@
1
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
2
+ import * as React from "react";
3
+ /**
4
+ * Escape hatch for footer buttons: any element wrapped in <ModalClose asChild>
5
+ * closes the modal on click (re-exported Radix Dialog.Close).
6
+ */
7
+ export declare const ModalClose: React.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
8
+ export interface ModalProps {
9
+ /** Dialog title — always rendered (Radix requires it for accessibility). */
10
+ title: React.ReactNode;
11
+ /** Optional description under the title, linked via aria-describedby. */
12
+ description?: React.ReactNode;
13
+ /** Body content. */
14
+ children?: React.ReactNode;
15
+ /** Footer slot (actions row). Use <ModalClose asChild> for cancel buttons. */
16
+ footer?: React.ReactNode;
17
+ /**
18
+ * Uncontrolled usage: a node (usually a Button) that opens the modal,
19
+ * rendered through Radix `asChild`.
20
+ */
21
+ trigger?: React.ReactNode;
22
+ /** Controlled open state (pair with onOpenChange). */
23
+ open?: boolean;
24
+ /** Initial state for uncontrolled usage. */
25
+ defaultOpen?: boolean;
26
+ onOpenChange?: (open: boolean) => void;
27
+ /** Hides the top-right X button. */
28
+ hideCloseButton?: boolean;
29
+ /** Extra classes for the content panel. */
30
+ className?: string;
31
+ /**
32
+ * Escape hatch: props spread onto Radix Dialog.Content
33
+ * (onEscapeKeyDown, onInteractOutside, …).
34
+ */
35
+ contentProps?: Omit<React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, "children" | "className">;
36
+ }
37
+ /**
38
+ * Themed Radix Dialog. Works controlled (`open` + `onOpenChange`) or
39
+ * uncontrolled via the `trigger` slot; `footer` is the actions row.
40
+ *
41
+ * Escape hatches: `contentProps` reaches the Radix content part, `className`
42
+ * restyles the panel, `hideCloseButton` drops the X, and `ModalClose` lets any
43
+ * footer element close the dialog.
44
+ */
45
+ export declare function Modal({ title, description, children, footer, trigger, open, defaultOpen, onOpenChange, hideCloseButton, className, contentProps, }: ModalProps): React.ReactElement;
@@ -0,0 +1,29 @@
1
+ import * as React from "react";
2
+ export interface PageProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
3
+ /** Page heading, rendered as an <h1>. */
4
+ title: React.ReactNode;
5
+ /** Optional sub-heading under the title. */
6
+ description?: React.ReactNode;
7
+ /** Slot rendered to the right of the title (buttons, menus, …). */
8
+ actions?: React.ReactNode;
9
+ /**
10
+ * Escape hatch: replaces the entire derived header (title/description/
11
+ * actions are ignored when given).
12
+ */
13
+ header?: React.ReactNode;
14
+ /** Extra classes for the derived header row. */
15
+ headerClassName?: string;
16
+ /** Extra classes for the content area under the header. */
17
+ contentClassName?: string;
18
+ children?: React.ReactNode;
19
+ }
20
+ /**
21
+ * Standard page chrome: an <h1> title row with an actions slot, then the
22
+ * content. The target usage `<Page title="Events"><ResourceTable … /></Page>`
23
+ * is a complete screen.
24
+ *
25
+ * Escape hatches: `header` replaces the whole derived header; `className`,
26
+ * `headerClassName` and `contentClassName` merge through cn(); every other
27
+ * prop passes through to the root <section>.
28
+ */
29
+ export declare const Page: React.ForwardRefExoticComponent<PageProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,44 @@
1
+ import { VariantProps } from '../../lib/variants';
2
+ import * as React from "react";
3
+ /**
4
+ * Exported for composition (escape hatch): render any element as a button
5
+ * lookalike with `className={buttonVariants({ variant: "outline" })}`.
6
+ */
7
+ export declare const buttonVariants: import('../..').VariantFn<{
8
+ variant: {
9
+ default: string;
10
+ destructive: string;
11
+ outline: string;
12
+ secondary: string;
13
+ ghost: string;
14
+ link: string;
15
+ };
16
+ size: {
17
+ default: string;
18
+ sm: string;
19
+ lg: string;
20
+ icon: string;
21
+ };
22
+ }>;
23
+ export type ButtonVariantProps = VariantProps<typeof buttonVariants>;
24
+ export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & ButtonVariantProps;
25
+ /**
26
+ * Themed button. `type` defaults to "button" (pass type="submit" in forms).
27
+ * `className` is merged via cn(), so consumer classes win.
28
+ */
29
+ export declare const Button: React.ForwardRefExoticComponent<React.ButtonHTMLAttributes<HTMLButtonElement> & import('../..').VariantSelection<{
30
+ variant: {
31
+ default: string;
32
+ destructive: string;
33
+ outline: string;
34
+ secondary: string;
35
+ ghost: string;
36
+ link: string;
37
+ };
38
+ size: {
39
+ default: string;
40
+ sm: string;
41
+ lg: string;
42
+ icon: string;
43
+ };
44
+ }> & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,5 @@
1
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
2
+ import * as React from "react";
3
+ export type CheckboxProps = React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>;
4
+ /** Themed checkbox (Radix Checkbox) with a check mark indicator. */
5
+ export declare const Checkbox: React.ForwardRefExoticComponent<Omit<CheckboxPrimitive.CheckboxProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,24 @@
1
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2
+ import * as React from "react";
3
+ /** Dropdown menu root (Radix DropdownMenu). */
4
+ export declare const DropdownMenu: React.FC<DropdownMenuPrimitive.DropdownMenuProps>;
5
+ /** The element that opens the menu. */
6
+ export declare const DropdownMenuTrigger: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuTriggerProps & React.RefAttributes<HTMLButtonElement>>;
7
+ /** Groups related menu items. */
8
+ export declare const DropdownMenuGroup: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuGroupProps & React.RefAttributes<HTMLDivElement>>;
9
+ /** Renders the menu into `document.body`. */
10
+ export declare const DropdownMenuPortal: React.FC<DropdownMenuPrimitive.DropdownMenuPortalProps>;
11
+ /** The styled menu panel. */
12
+ export declare const DropdownMenuContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
+ /** One menu item; `onSelect` runs the action. */
14
+ export declare const DropdownMenuItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
15
+ inset?: boolean;
16
+ } & React.RefAttributes<HTMLDivElement>>;
17
+ /** A menu item with a check mark that toggles `checked`. */
18
+ export declare const DropdownMenuCheckboxItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
19
+ /** A non-interactive heading inside the menu. */
20
+ export declare const DropdownMenuLabel: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
21
+ inset?: boolean;
22
+ } & React.RefAttributes<HTMLDivElement>>;
23
+ /** A divider line between menu items. */
24
+ export declare const DropdownMenuSeparator: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Minimal inline SVG icons used by the primitives (no icon dependency).
4
+ * All strokes use `currentColor`, so they follow the text color tokens.
5
+ * Internal to @karni/ui — not part of the public barrel.
6
+ */
7
+ type IconProps = React.SVGAttributes<SVGSVGElement>;
8
+ export declare function CheckIcon(props: IconProps): React.ReactElement;
9
+ export declare function ChevronDownIcon(props: IconProps): React.ReactElement;
10
+ export declare function ChevronUpIcon(props: IconProps): React.ReactElement;
11
+ export {};
@@ -0,0 +1,4 @@
1
+ import * as React from "react";
2
+ export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
3
+ /** Themed text input; every native `<input>` prop passes through. */
4
+ export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,5 @@
1
+ import * as LabelPrimitive from "@radix-ui/react-label";
2
+ import * as React from "react";
3
+ export type LabelProps = React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>;
4
+ /** Themed form label (Radix Label) that dims when its field is disabled. */
5
+ export declare const Label: React.ForwardRefExoticComponent<Omit<LabelPrimitive.LabelProps & React.RefAttributes<HTMLLabelElement>, "ref"> & React.RefAttributes<HTMLLabelElement>>;
@@ -0,0 +1,63 @@
1
+ import * as SelectPrimitive from "@radix-ui/react-select";
2
+ import * as React from "react";
3
+ /**
4
+ * The value that stands for "nothing selected" inside Radix Select, which reserves the empty string.
5
+ *
6
+ * This package maps the empty state through this sentinel at the component boundary (contract D8). Consumers of
7
+ * the composed <Select> never see it: `value`/`onValueChange` use `null`.
8
+ */
9
+ export declare const KARNI_SELECT_EMPTY = "__karni_empty__";
10
+ /** Radix Select root, for composing a custom select from the styled parts. */
11
+ export declare const SelectRoot: React.FC<SelectPrimitive.SelectProps>;
12
+ /** Groups related options inside `SelectContent`. */
13
+ export declare const SelectGroup: React.ForwardRefExoticComponent<SelectPrimitive.SelectGroupProps & React.RefAttributes<HTMLDivElement>>;
14
+ /** Shows the selected option's label inside `SelectTrigger`. */
15
+ export declare const SelectValue: React.ForwardRefExoticComponent<SelectPrimitive.SelectValueProps & React.RefAttributes<HTMLSpanElement>>;
16
+ /** The styled button that opens the select. */
17
+ export declare const SelectTrigger: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
18
+ /** The styled dropdown panel that lists the options. */
19
+ export declare const SelectContent: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
20
+ /** A heading inside a `SelectGroup`. */
21
+ export declare const SelectLabel: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
22
+ /** One selectable option; `value` is what the select reports. */
23
+ export declare const SelectItem: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
24
+ /** A divider line between option groups. */
25
+ export declare const SelectSeparator: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
26
+ export interface SelectOption {
27
+ /** Must be non-empty and not the reserved sentinel. */
28
+ value: string;
29
+ label: React.ReactNode;
30
+ disabled?: boolean;
31
+ }
32
+ export interface SelectProps {
33
+ /** `null`/`""`/`undefined` all mean "no selection". */
34
+ value: string | null | undefined;
35
+ /** Receives `null` when the clear item is chosen, else the option value. */
36
+ onValueChange?: (value: string | null) => void;
37
+ options: readonly SelectOption[];
38
+ placeholder?: React.ReactNode;
39
+ /**
40
+ * Renders a clear item at the top of the list (contract D8): pass it for
41
+ * nullable fields (label "—" by default) and filter contexts (e.g. "All").
42
+ * Choosing it emits `null`.
43
+ */
44
+ clearable?: boolean;
45
+ clearLabel?: React.ReactNode;
46
+ disabled?: boolean;
47
+ /** Forwarded to the hidden native select for HTML form submission. */
48
+ name?: string;
49
+ id?: string;
50
+ /** Merged onto the trigger. */
51
+ className?: string;
52
+ contentClassName?: string;
53
+ "aria-label"?: string;
54
+ /** Extra <SelectItem>/<SelectGroup> nodes appended after `options` (escape hatch). */
55
+ children?: React.ReactNode;
56
+ }
57
+ /**
58
+ * Styled select for a list of `options`, with `null` for no selection. Maps the
59
+ * empty state through the KARNI_SELECT_EMPTY sentinel so `""` never reaches
60
+ * Radix (D8). For fully custom composition use SelectRoot/SelectTrigger/
61
+ * SelectContent/SelectItem directly.
62
+ */
63
+ export declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,4 @@
1
+ import * as React from "react";
2
+ export type SkeletonProps = React.HTMLAttributes<HTMLDivElement>;
3
+ /** Loading placeholder block; size it with width/height utility classes. */
4
+ export declare const Skeleton: React.ForwardRefExoticComponent<SkeletonProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,5 @@
1
+ import * as SwitchPrimitive from "@radix-ui/react-switch";
2
+ import * as React from "react";
3
+ export type SwitchProps = React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>;
4
+ /** Themed on/off toggle (Radix Switch). */
5
+ export declare const Switch: React.ForwardRefExoticComponent<Omit<SwitchPrimitive.SwitchProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,10 @@
1
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
2
+ import * as React from "react";
3
+ /** Tab set root (Radix Tabs); holds the selected value. */
4
+ export declare const Tabs: React.ForwardRefExoticComponent<TabsPrimitive.TabsProps & React.RefAttributes<HTMLDivElement>>;
5
+ /** The row of tab buttons. */
6
+ export declare const TabsList: React.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsListProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
7
+ /** One tab button; `value` names the panel it opens. */
8
+ export declare const TabsTrigger: React.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
9
+ /** The panel shown while its `value` is the selected tab. */
10
+ export declare const TabsContent: React.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,4 @@
1
+ import * as React from "react";
2
+ export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
3
+ /** Themed multi-line text input; every native `<textarea>` prop passes through. */
4
+ export declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Barrel-facing path for <ResourceDetail>. The implementation lives in
3
+ * ./detail/ (resource form builder); every contract-pinned export name is
4
+ * re-exported here so src/index.ts keeps resolving this module.
5
+ */
6
+ export { ResourceDetail } from './detail/ResourceDetail';
7
+ export type { ResourceDetailProps, ResourceDetailFieldOverride } from './detail/ResourceDetail';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Barrel-facing path for <ResourceForm>. The implementation lives in
3
+ * ./form/ (resource form builder); every contract-pinned export name is
4
+ * re-exported here so src/index.ts keeps resolving this module.
5
+ */
6
+ export { ResourceForm } from './form/ResourceForm';
7
+ export type { ResourceFormProps, FieldOverride, KarniFieldProps } from './form/ResourceForm';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Barrel-facing path for <ResourceSelect>. The implementation lives in
3
+ * ./select/ (resource form builder); every contract-pinned export name is
4
+ * re-exported here so src/index.ts keeps resolving this module.
5
+ */
6
+ export { ResourceSelect } from './select/ResourceSelect';
7
+ export type { ResourceSelectProps } from './select/ResourceSelect';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Public entry for ResourceTable (owner: resource table builder).
3
+ *
4
+ * The implementation lives in ./table/ (ResourceTable + filter bar +
5
+ * pagination controls + bulk select, contract "ResourceTable" + D5); this
6
+ * module keeps the barrel's import path and exported names stable.
7
+ */
8
+ export { ResourceTable } from './table/ResourceTable';
9
+ export type { ResourceTableProps } from './table/ResourceTable';
10
+ export type { ResourceColumn } from './table/derive-columns';
11
+ export type { BulkAction } from './table/BulkActionsBar';
@@ -0,0 +1,17 @@
1
+ import { FieldDef } from '../../schema/types';
2
+ /** The audit field names (`created_at`, `created_by`, …) that tables hide and detail views group last. */
3
+ export declare const AUDIT_FIELDS: readonly string[];
4
+ /**
5
+ * True for a schema field that is one of AUDIT_FIELDS. The backend always
6
+ * emits these readOnly; a writable field that merely shares a name (a natural
7
+ * `id` key, say) is domain data and is never treated as audit.
8
+ */
9
+ export declare function isAuditField(field: Pick<FieldDef, "name" | "readOnly">): boolean;
10
+ /**
11
+ * Splits fields into domain fields (schema order kept) and audit fields
12
+ * (AUDIT_FIELDS order).
13
+ */
14
+ export declare function partitionAuditFields<F extends Pick<FieldDef, "name" | "readOnly">>(fields: readonly F[]): {
15
+ domain: F[];
16
+ audit: F[];
17
+ };
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ export interface ResourceDetailFieldOverride {
3
+ label?: string;
4
+ hidden?: boolean;
5
+ render?: (value: unknown, item: Record<string, unknown>) => React.ReactNode;
6
+ }
7
+ export interface ResourceDetailProps {
8
+ resource: string;
9
+ id: string | number;
10
+ fields?: Record<string, ResourceDetailFieldOverride>;
11
+ /** Appended below the field list (e.g. a child <ResourceTable>). */
12
+ sections?: ReadonlyArray<React.ReactNode>;
13
+ expand?: readonly string[];
14
+ title?: React.ReactNode;
15
+ actions?: React.ReactNode;
16
+ className?: string;
17
+ }
18
+ /**
19
+ * Schema-driven read view of one row: every readable field, labelled and formatted.
20
+ *
21
+ * Contract "ResourceDetail": a definition list of
22
+ * every non-writeOnly field — domain fields first (schema order), then the
23
+ * audit fields (AUDIT_FIELDS: id, created_at, updated_at, created_by,
24
+ * updated_by) in a visually muted "Record" group. Relations render through
25
+ * displayLabel when the object is expanded (the field is in `expand`), else
26
+ * as the raw id in <code>. Escape hatches: per-field `fields` overrides
27
+ * (label/hidden/render — audit fields included), appended `sections`, and the
28
+ * title/actions header slots.
29
+ */
30
+ export declare function ResourceDetail(props: ResourceDetailProps): React.ReactElement;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Human label heuristic for resource objects (contract "ResourceTable").
3
+ *
4
+ * Used by ResourceTable's relation cells, ResourceSelect's options and
5
+ * ResourceDetail's relation values. Overridable everywhere it is used:
6
+ * ResourceTable takes `getRowLabel`, ResourceSelect takes `getOptionLabel`.
7
+ */
8
+ /**
9
+ * Returns the first non-empty string among the `name` → `title` → `label` →
10
+ * `email` → `str` keys, falling back to `String(id)` and finally "".
11
+ */
12
+ export declare function displayLabel(obj: Record<string, unknown>): string;