@tailor-platform/app-shell 0.28.0 → 0.29.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.
@@ -14,6 +14,62 @@ import { useRouteError } from 'react-router';
14
14
  import { useSearchParams } from 'react-router';
15
15
  import { VariantProps } from 'class-variance-authority';
16
16
 
17
+ /**
18
+ * A single action row in the ActionPanel (icon + label, triggered by onClick).
19
+ * For navigation, call useNavigate() inside onClick (e.g. navigate("/path")).
20
+ */
21
+ declare interface ActionItem {
22
+ /** Unique key for React */
23
+ key: string;
24
+ /** Visible label */
25
+ label: string;
26
+ /** Icon: any component or SVG; rendered in a fixed-size slot (e.g. 16px) */
27
+ icon: ReactNode;
28
+ /** Called when the row is clicked. For navigation, use useNavigate() inside the callback. */
29
+ onClick?: () => void | Promise<void>;
30
+ /** Optional; disable the row */
31
+ disabled?: boolean;
32
+ /** When true, show loading indicator in the row and make it non-interactive */
33
+ loading?: boolean;
34
+ }
35
+
36
+ /**
37
+ * ActionPanel — Card with title and list of actions (icon + label).
38
+ *
39
+ * Each row has an icon and label and is triggered by onClick. For navigation, use
40
+ * useNavigate() inside the callback (e.g. onClick: () => navigate("/path")).
41
+ * Use `astw:w-full` so the panel fills the width of its parent container.
42
+ *
43
+ * When an action has `loading: true`, the row shows a spinner in the icon slot and is
44
+ * non-interactive (useful for backend-driven actions: parent sets loading from mutation/request state).
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * const navigate = useNavigate();
49
+ * <ActionPanel
50
+ * title="Actions"
51
+ * actions={[
52
+ * { key: "1", label: "Create invoice", icon: <ReceiptIcon />, onClick: () => openModal() },
53
+ * { key: "2", label: "View docs", icon: <DocIcon />, onClick: () => navigate("/docs") },
54
+ * { key: "3", label: "Saving…", icon: <SaveIcon />, onClick: () => {}, loading: true },
55
+ * ]}
56
+ * />
57
+ * ```
58
+ */
59
+ export declare function ActionPanel({ title, actions, className }: ActionPanelProps): JSX.Element;
60
+
61
+ /**
62
+ * Props for the ActionPanel component.
63
+ */
64
+ export declare interface ActionPanelProps {
65
+ /** Card title */
66
+ title: string;
67
+ /** List of actions to render */
68
+ actions: ActionItem[];
69
+ /** Additional CSS classes for the card container */
70
+ className?: string;
71
+ }
72
+
17
73
  export declare const AppShell: {
18
74
  (props: AppShellProps): JSX.Element | null;
19
75
  /* Excluded from this release type: WithPages */
@@ -237,6 +293,11 @@ export declare const badgeVariants: (props?: ({
237
293
  */
238
294
  declare type BadgeVariantType = "default" | "success" | "warning" | "error" | "neutral" | "outline-success" | "outline-warning" | "outline-error" | "outline-info" | "outline-neutral";
239
295
 
296
+ /**
297
+ * Supported area values for Layout.Column
298
+ */
299
+ declare type ColumnArea = "left" | "main" | "right";
300
+
240
301
  /**
241
302
  * Supported column count options
242
303
  */
@@ -248,6 +309,14 @@ declare type ColumnCount = 1 | 2 | 3;
248
309
  declare interface ColumnProps {
249
310
  /** Additional CSS classes */
250
311
  className?: string;
312
+ /**
313
+ * Column area role. When specified on all columns, determines width template
314
+ * based on role rather than position.
315
+ * - "left": fixed 320px
316
+ * - "main": flex-1
317
+ * - "right": fixed 280px
318
+ */
319
+ area?: ColumnArea;
251
320
  /** Child content */
252
321
  children?: ReactNode;
253
322
  }
@@ -522,31 +591,43 @@ export declare const defineI18nLabels: <const L extends Exclude<string, "en">, c
522
591
  };
523
592
 
524
593
  /**
525
- * Define a root-level resource that renders a React component.
594
+ * Define a module that appears as a top-level item in the sidebar navigation.
595
+ *
596
+ * A module can be configured in several ways:
597
+ * - **With component**: renders the component at the module path.
598
+ * - **Without component, with resources**: child resources are accessible, but
599
+ * navigating to the module path itself returns a 404.
600
+ * - **With guards only**: useful for redirect-only paths (e.g. legacy URL migration).
601
+ * The guard loader runs and can redirect; if all guards pass, the path returns 404.
602
+ * - **Path only (no component, no resources, no guards)**: the module path falls
603
+ * through to the catch-all 404 route.
526
604
  *
527
605
  * @example
528
606
  * ```
529
- * // Define a minimal resource
530
- * defineReactResource({
531
- * path: "custom-page",
532
- * component: () => {
533
- * return (
534
- * <div>
535
- * <p>This is a custom page.</p>
536
- * </div>
537
- * );
538
- * },
607
+ * defineModule({
608
+ * path: "dashboard",
609
+ * component: (props) => <div>{props.title}</div>,
610
+ * resources: [defineResource({ ... })],
539
611
  * });
540
612
  * ```
541
613
  */
542
614
  export declare function defineModule(props: DefineModuleProps): Module;
543
615
 
544
- declare type DefineModuleProps = CommonProps & CommonModuleProps & {
616
+ declare type DefineModuleProps = CommonProps & CommonModuleProps & ErrorBoundaryProps & {
545
617
  /**
546
618
  * React component to render.
547
619
  *
548
- * If not provided, a guard with `redirectTo()` must be used to redirect
549
- * users to the appropriate resource.
620
+ * If not provided and no guards are given, the module path will return a 404
621
+ * response while child resources remain accessible.
622
+ *
623
+ * If `resources` is non-empty and guards are provided, guards take precedence
624
+ * over the default 404: for example, `redirectTo()` will redirect instead of
625
+ * returning 404. If all guards return `pass()`, the module path still returns
626
+ * a 404 since there is no component to render.
627
+ *
628
+ * Guard-only modules (no component, no resources) are also supported — for
629
+ * example, to maintain legacy URL paths that redirect elsewhere. In this case,
630
+ * at least one guard must be provided.
550
631
  *
551
632
  * @example
552
633
  * ```tsx
@@ -554,22 +635,23 @@ declare type DefineModuleProps = CommonProps & CommonModuleProps & {
554
635
  * ```
555
636
  */
556
637
  component?: (props: ResourceComponentProps) => ReactNode;
557
- /**
558
- * Error boundary component for this module and its child resources.
559
- * When an error occurs in this module or its resources, this component will render.
560
- * Use the `useRouteError` hook to access error details within the component.
561
- */
562
- errorBoundary?: ErrorBoundaryComponent;
563
638
  };
564
639
 
565
640
  /**
566
641
  * Define a resource that renders a React component.
567
642
  *
643
+ * A resource can be configured in several ways:
644
+ * - **With component**: renders the component at the resource path.
645
+ * - **Without component, with subResources**: sub-resources are accessible,
646
+ * but navigating to the resource path itself returns a 404.
647
+ * - **With guards only**: useful for redirect-only paths.
648
+ * The guard loader runs and can redirect; if all guards pass, the path returns 404.
649
+ *
568
650
  * This resource can be used as a sub-resource of a module or as a root-level resource.
569
651
  *
570
652
  * @example
571
653
  * ```
572
- * // Define a minimal resource
654
+ * // Resource with component and sub-resources
573
655
  * defineResource({
574
656
  * path: "custom-page",
575
657
  * component: () => {
@@ -592,23 +674,39 @@ declare type DefineModuleProps = CommonProps & CommonModuleProps & {
592
674
  * }),
593
675
  * ]
594
676
  * });
677
+ *
678
+ * // Resource without component (sub-resources only, path returns 404)
679
+ * defineResource({
680
+ * path: "namespace",
681
+ * subResources: [
682
+ * defineResource({
683
+ * path: "page-a",
684
+ * component: () => <div>Page A</div>,
685
+ * }),
686
+ * ]
687
+ * });
595
688
  * ```
596
689
  *
597
690
  */
598
691
  export declare function defineResource(props: DefineResourceProps): Resource;
599
692
 
600
- declare type DefineResourceProps = CommonProps & ReactResourceProps & {
693
+ declare type DefineResourceProps = CommonProps & ErrorBoundaryProps & {
601
694
  /**
602
- * Sub-resources of the resource.
695
+ * React component to render.
696
+ *
697
+ * If not provided and no guards are given, the resource path will return a 404
698
+ * response while sub-resources remain accessible.
699
+ *
700
+ * If `subResources` is non-empty and guards are provided, guards take precedence
701
+ * over the default 404: for example, `redirectTo()` will redirect instead of
702
+ * returning 404. If all guards return `pass()`, the resource path still returns
703
+ * a 404 since there is no component to render.
603
704
  */
604
- subResources?: Array<Resource>;
705
+ component?: (props: ResourceComponentProps) => ReactNode;
605
706
  /**
606
- * Error boundary component for this resource and its sub-resources.
607
- * When an error occurs in this resource, this component will render.
608
- * Overrides module-level or global error boundaries.
609
- * Use the `useRouteError` hook to access error details within the component.
707
+ * Sub-resources of the resource.
610
708
  */
611
- errorBoundary?: ErrorBoundaryComponent;
709
+ subResources?: Array<Resource>;
612
710
  };
613
711
 
614
712
  /**
@@ -698,6 +796,16 @@ export declare interface EnhancedAuthClient extends AuthClient {
698
796
  */
699
797
  export declare type ErrorBoundaryComponent = ReactNode;
700
798
 
799
+ declare type ErrorBoundaryProps = {
800
+ /**
801
+ * Error boundary component for this module/resource and its children.
802
+ * When an error occurs, this component will render.
803
+ * Overrides any parent-level (module or global) error boundary.
804
+ * Use the `useRouteError` hook to access error details within the component.
805
+ */
806
+ errorBoundary?: ErrorBoundaryComponent;
807
+ };
808
+
701
809
  /**
702
810
  * Type-safe path builder for file-based routing.
703
811
  *
@@ -818,6 +926,51 @@ export declare type GuardResult =
818
926
  to: string;
819
927
  };
820
928
 
929
+ /**
930
+ * Layout.Header — Page-level header with title, actions, and an optional
931
+ * full-width slot.
932
+ *
933
+ * Place this as a direct child of `<Layout>`, above any `<Layout.Column>`
934
+ * children. The header spans the full width of the grid via `col-span-full`,
935
+ * regardless of column count.
936
+ *
937
+ * If multiple `Layout.Header` children are provided to `<Layout>`, only the
938
+ * first one is rendered.
939
+ *
940
+ * - **title** — Rendered as an `<h1>` on the left side.
941
+ * - **actions** — Rendered on the right side (e.g. save / cancel buttons).
942
+ * - **children** — Rendered full-width below the title/actions row.
943
+ * Useful for tabs, breadcrumbs, or other secondary navigation.
944
+ *
945
+ * ### Data attributes
946
+ *
947
+ * The rendered `<header>` has `data-layout-header`, which `<Layout>` uses
948
+ * internally to apply `col-span-full`.
949
+ *
950
+ * @example
951
+ * ```tsx
952
+ * <Layout>
953
+ * <Layout.Header
954
+ * title="Purchase Orders"
955
+ * actions={[<Button key="create">Create</Button>]}
956
+ * >
957
+ * <Tabs value={tab} onValueChange={setTab}>
958
+ * <TabsList>
959
+ * <TabsTrigger value="all">All</TabsTrigger>
960
+ * <TabsTrigger value="open">Open</TabsTrigger>
961
+ * </TabsList>
962
+ * </Tabs>
963
+ * </Layout.Header>
964
+ * <Layout.Column>…</Layout.Column>
965
+ * </Layout>
966
+ * ```
967
+ */
968
+ declare function Header({ title, actions, children }: LayoutHeaderProps): JSX.Element;
969
+
970
+ declare namespace Header {
971
+ var displayName: string;
972
+ }
973
+
821
974
  /**
822
975
  * Deny access and show 404 Not Found.
823
976
  *
@@ -858,23 +1011,41 @@ declare type LabelDefinition<Def extends Record<string, LabelValue>> = {
858
1011
  declare type LabelValue = string | ((props: any) => string);
859
1012
 
860
1013
  /**
861
- * Layout - Responsive column layout component
1014
+ * Layout – Responsive grid layout component
1015
+ *
1016
+ * Uses CSS Grid for responsive column layouts. On mobile, all columns
1017
+ * stack vertically (`grid-cols-1`). At `lg` (2-column) or `xl` (3+ column)
1018
+ * breakpoints, columns display side by side with widths determined by
1019
+ * position or the `area` prop on each `Layout.Column`.
1020
+ *
1021
+ * ### Child filtering
862
1022
  *
863
- * Automatically handles responsive behavior for 1, 2, or 3 column layouts.
864
- * Uses flexbox with viewport breakpoints for simple, CSS-only responsive behavior.
1023
+ * Only `Layout.Header` and `Layout.Column` are recognized as children;
1024
+ * any other elements are silently ignored and will not be rendered.
1025
+ * If multiple `Layout.Header` children are provided, only the first one
1026
+ * is rendered.
1027
+ *
1028
+ * ### Grid template
1029
+ *
1030
+ * Column widths are controlled via a `--layout-cols` CSS custom property
1031
+ * set on the root `<div>`, consumed by `grid-cols-[var(--layout-cols)]`.
1032
+ * The value is computed from position (see `Layout.Column`) or `area` props.
1033
+ * For a single column, no custom property is set and the grid remains
1034
+ * `grid-cols-1`.
865
1035
  *
866
1036
  * @example
867
1037
  * ```tsx
868
- * // Basic layout
869
- * <Layout columns={2}>
1038
+ * // With Layout.Header
1039
+ * <Layout>
1040
+ * <Layout.Header title="Page Title" actions={[<Button key="save">Save</Button>]} />
870
1041
  * <Layout.Column>Main content</Layout.Column>
871
1042
  * <Layout.Column>Side panel</Layout.Column>
872
1043
  * </Layout>
873
1044
  *
874
- * // With header and actions
875
- * <Layout columns={2} title="Page Title" actions={[<Button key="save">Save</Button>]}>
876
- * <Layout.Column>...</Layout.Column>
877
- * <Layout.Column>...</Layout.Column>
1045
+ * // With area prop
1046
+ * <Layout>
1047
+ * <Layout.Column area="left">Sidebar</Layout.Column>
1048
+ * <Layout.Column area="main">Content</Layout.Column>
878
1049
  * </Layout>
879
1050
  * ```
880
1051
  */
@@ -882,6 +1053,19 @@ export declare function Layout({ columns, className, gap, title, actions, childr
882
1053
 
883
1054
  export declare namespace Layout {
884
1055
  var Column: React_2.ForwardRefExoticComponent<ColumnProps & React_2.RefAttributes<HTMLDivElement>>;
1056
+ var Header: Header;
1057
+ }
1058
+
1059
+ /**
1060
+ * Props for Layout.Header component
1061
+ */
1062
+ declare interface LayoutHeaderProps {
1063
+ /** Page title (left side) */
1064
+ title?: string;
1065
+ /** Action buttons or nodes (right side) */
1066
+ actions?: ReactNode[];
1067
+ /** Renders full-width below the title/actions row (e.g. tabs) */
1068
+ children?: ReactNode;
885
1069
  }
886
1070
 
887
1071
  /**
@@ -891,20 +1075,30 @@ export declare interface LayoutProps {
891
1075
  /**
892
1076
  * Number of columns (1, 2, or 3).
893
1077
  *
894
- * **Required.** Must match the exact number of `Layout.Column` children.
895
- * The component will throw an error in development if there's a mismatch.
1078
+ * @deprecated Auto-detected from Layout.Column children count when omitted.
896
1079
  */
897
- columns: ColumnCount;
1080
+ columns?: ColumnCount;
898
1081
  /** Additional CSS classes */
899
1082
  className?: string;
900
- /** Gap between columns (default: 4 = 16px) */
1083
+ /**
1084
+ * Gap between columns (default: 4 = 16px)
1085
+ *
1086
+ * @deprecated Remove the `gap` prop and use `className` with the `astw:` prefix
1087
+ * (e.g. `className="astw:gap-6"`) to override the default gap.
1088
+ */
901
1089
  gap?: number;
902
- /** Header title - displayed at the top of the layout */
1090
+ /** Header title - displayed at the top of the layout
1091
+ *
1092
+ * @deprecated Use `<Layout.Header title="...">` instead.
1093
+ */
903
1094
  title?: string;
904
- /** Header actions - array of action components (e.g., buttons) displayed on the right side of the header.
905
- * Layout and spacing are handled automatically. */
1095
+ /** Header actions displayed on the right side of the header.
1096
+ * Layout and spacing are handled automatically.
1097
+ *
1098
+ * @deprecated Use `<Layout.Header actions={...}>` instead.
1099
+ */
906
1100
  actions?: ReactNode[];
907
- /** Child elements - must be exactly `columns` number of Layout.Column components */
1101
+ /** Child elements - Layout.Header and/or Layout.Column components */
908
1102
  children: ReactNode;
909
1103
  }
910
1104
 
@@ -976,13 +1170,6 @@ export declare type PageMeta = {
976
1170
  */
977
1171
  export declare const pass: () => GuardResult;
978
1172
 
979
- declare type ReactResourceProps = {
980
- /**
981
- * React component to render.
982
- */
983
- component: (props: ResourceComponentProps) => ReactNode;
984
- };
985
-
986
1173
  /**
987
1174
  * Redirect to another path.
988
1175
  *
@@ -1007,7 +1194,7 @@ export declare const redirectTo: (to: string) => GuardResult;
1007
1194
  */
1008
1195
  declare type Resource = CommonPageResource & {
1009
1196
  _type: "resource";
1010
- component: () => ReactNode;
1197
+ component?: () => ReactNode;
1011
1198
  subResources?: Array<Resource>;
1012
1199
  errorBoundary: ErrorBoundaryComponent;
1013
1200
  guards?: Guard[];