@cfast/ui 0.3.0 → 0.4.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.
@@ -219,15 +219,19 @@ function PermissionGate({
219
219
  }
220
220
 
221
221
  // src/components/action-button.tsx
222
+ import { useFetcher } from "react-router";
222
223
  import { jsx as jsx4 } from "react/jsx-runtime";
223
224
  function ActionButton({
224
225
  action,
225
226
  children,
226
227
  whenForbidden = "disable",
227
228
  confirmation: _confirmation,
229
+ href,
230
+ input,
228
231
  ...buttonProps
229
232
  }) {
230
233
  const Button = useComponent("button");
234
+ const fetcher = useFetcher();
231
235
  if (action.invisible) {
232
236
  return null;
233
237
  }
@@ -235,13 +239,33 @@ function ActionButton({
235
239
  return null;
236
240
  }
237
241
  const disabled = !action.permitted && whenForbidden === "disable";
242
+ const pending = action.pending || fetcher.state !== "idle";
243
+ if (href) {
244
+ if (disabled) {
245
+ return /* @__PURE__ */ jsx4(Button, { ...buttonProps, disabled: true, loading: false, children });
246
+ }
247
+ return /* @__PURE__ */ jsx4("a", { href, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsx4(Button, { ...buttonProps, disabled: false, loading: false, children }) });
248
+ }
249
+ const handleClick = () => {
250
+ if (input) {
251
+ const formData = new FormData();
252
+ for (const [key, value] of Object.entries(input)) {
253
+ if (value !== null && value !== void 0) {
254
+ formData.set(key, String(value));
255
+ }
256
+ }
257
+ fetcher.submit(formData, { method: "POST" });
258
+ } else {
259
+ action.submit();
260
+ }
261
+ };
238
262
  return /* @__PURE__ */ jsx4(
239
263
  Button,
240
264
  {
241
265
  ...buttonProps,
242
- onClick: () => action.submit(),
266
+ onClick: handleClick,
243
267
  disabled,
244
- loading: action.pending,
268
+ loading: pending,
245
269
  children
246
270
  }
247
271
  );
@@ -1363,6 +1387,7 @@ export {
1363
1387
  UIPluginProvider,
1364
1388
  useUIPlugin,
1365
1389
  useComponent,
1390
+ ConfirmContext,
1366
1391
  useConfirm,
1367
1392
  ToastContext,
1368
1393
  useToast,
@@ -1086,6 +1086,16 @@ type ActionButtonProps = {
1086
1086
  whenForbidden?: WhenForbidden;
1087
1087
  /** Confirmation message or options shown before executing the action. */
1088
1088
  confirmation?: string | ConfirmOptions;
1089
+ /**
1090
+ * When provided, renders a permission-gated link instead of a form button.
1091
+ * The link navigates to `href` when the action is permitted.
1092
+ */
1093
+ href?: string;
1094
+ /**
1095
+ * When provided, submits a form with these values as hidden fields via
1096
+ * `action.submit()`. Eliminates manual `<Form>` + `<input type="hidden">`.
1097
+ */
1098
+ input?: Record<string, string | number | boolean | null | undefined>;
1089
1099
  } & Omit<ButtonSlotProps, ActionButtonControlledProps>;
1090
1100
  /**
1091
1101
  * Props for the PermissionGate component.
@@ -1479,6 +1489,10 @@ declare function useComponent<K extends keyof UIPluginComponents>(slot: K): UIPl
1479
1489
  * the user confirms, or `false` if they cancel or dismiss.
1480
1490
  */
1481
1491
  type ConfirmFn = (options: ConfirmOptions) => Promise<boolean>;
1492
+ type ConfirmContextValue = {
1493
+ confirm: ConfirmFn;
1494
+ };
1495
+ declare const ConfirmContext: react.Context<ConfirmContextValue | null>;
1482
1496
  /**
1483
1497
  * Returns an imperative {@link ConfirmFn} that opens a confirmation dialog
1484
1498
  * and resolves to `true` (confirmed) or `false` (cancelled/dismissed).
@@ -1616,20 +1630,43 @@ declare function PermissionGate({ action, children, fallback, }: PermissionGateP
1616
1630
  * - `whenForbidden="disable"` -- shown but disabled when not permitted (default)
1617
1631
  * - `whenForbidden="show"` -- shown and clickable regardless of permission
1618
1632
  *
1633
+ * When `href` is provided, renders a permission-gated `<a>` link instead of
1634
+ * a form-submitting button. The link is only rendered when the action is
1635
+ * permitted (respects `whenForbidden`).
1636
+ *
1637
+ * When `input` is provided, submits a form with those values as hidden
1638
+ * fields via a fetcher, eliminating manual `<Form>` + `<input type="hidden">`.
1639
+ * This works with both `useActions()` results (where `action.submit()` is
1640
+ * already wired) and `useCfastLoader()` results (where `action.submit()`
1641
+ * is a no-op and the fetcher handles submission).
1642
+ *
1619
1643
  * @param props - See {@link ActionButtonProps}.
1620
1644
  *
1621
- * @example
1645
+ * @example Basic form submission
1646
+ * ```tsx
1647
+ * <ActionButton action={publishPost} confirmation="Publish this post?">
1648
+ * Publish
1649
+ * </ActionButton>
1650
+ * ```
1651
+ *
1652
+ * @example Navigation link (permission-gated)
1653
+ * ```tsx
1654
+ * <ActionButton action={documents.canAdd()} href="/documents/new">
1655
+ * New Document
1656
+ * </ActionButton>
1657
+ * ```
1658
+ *
1659
+ * @example Form data via input prop
1622
1660
  * ```tsx
1623
1661
  * <ActionButton
1624
- * action={publishPost}
1625
- * whenForbidden="disable"
1626
- * confirmation="Publish this post?"
1662
+ * action={recipe.canDelete()}
1663
+ * input={{ _action: "deleteRecipe", id: recipe.id }}
1627
1664
  * >
1628
- * Publish
1665
+ * Delete
1629
1666
  * </ActionButton>
1630
1667
  * ```
1631
1668
  */
1632
- declare function ActionButton({ action, children, whenForbidden, confirmation: _confirmation, ...buttonProps }: ActionButtonProps): react_jsx_runtime.JSX.Element | null;
1669
+ declare function ActionButton({ action, children, whenForbidden, confirmation: _confirmation, href, input, ...buttonProps }: ActionButtonProps): react_jsx_runtime.JSX.Element | null;
1633
1670
 
1634
1671
  /**
1635
1672
  * Provides the {@link useConfirm} context and renders the confirmation dialog.
@@ -2005,4 +2042,4 @@ declare function ListView<T = unknown>({ title, data, table: _table, columns, ac
2005
2042
  */
2006
2043
  declare function DetailView<T = unknown>({ title, table, record, fields: fieldsProp, exclude, breadcrumb, }: DetailViewProps<T>): react_jsx_runtime.JSX.Element;
2007
2044
 
2008
- export { type FormStatusProps as $, type AppShellProps as A, type BooleanFieldProps as B, type ChipSlotProps as C, type DateFieldProps as D, type EmailFieldProps as E, type FileFieldProps as F, DetailView as G, type DetailViewProps as H, type ImageFieldProps as I, type JsonFieldProps as J, DropZone as K, type DropZoneProps as L, type DropZoneSlotProps as M, type NumberFieldProps as N, FileList$1 as O, type FileListFile as P, type FileListProps as Q, type RelationFieldProps as R, FilterBar as S, type TextFieldProps as T, type UrlFieldProps as U, type FilterBarProps as V, type FilterDef as W, type FilterOption as X, type FilterType as Y, FormStatus as Z, type FormStatusData as _, type UploadFieldProps as a, ImagePreview as a0, type ImagePreviewProps as a1, ImpersonationBanner as a2, type ImpersonationBannerProps as a3, ListView as a4, type ListViewProps as a5, type PageContainerSlotProps as a6, PermissionGate as a7, type PermissionGateProps as a8, RoleBadge as a9, type RoleBadgeProps as aa, type SidebarSlotProps as ab, type TableCellSlotProps as ac, type TableRowSlotProps as ad, type TableSectionSlotProps as ae, type TableSlotProps as af, type ToastApi as ag, ToastContext as ah, type ToastOptions as ai, type ToastSlotProps as aj, type ToastType as ak, type TooltipSlotProps as al, type UIPlugin as am, type UIPluginComponents as an, UIPluginProvider as ao, type UploadFieldFile as ap, type UserMenuLink as aq, type WhenForbidden as ar, createUIPlugin as as, getInitials as at, useActionToast as au, useColumnInference as av, useComponent as aw, useConfirm as ax, useToast as ay, useUIPlugin as az, type FieldComponent as b, type EmptyStateProps as c, type NavigationProgressProps as d, type BreadcrumbItem as e, type TabItem as f, type NavigationItem as g, type UserMenuProps as h, ActionButton as i, type ActionButtonProps as j, type AlertSlotProps as k, type AppShellSlotProps as l, AvatarWithInitials as m, type AvatarWithInitialsProps as n, type BaseFieldProps as o, type BreadcrumbSlotProps as p, type BulkAction as q, BulkActionBar as r, type ButtonSlotProps as s, type ColumnDef as t, type ColumnShorthand as u, type ConfirmDialogSlotProps as v, type ConfirmOptions as w, ConfirmProvider as x, DataTable as y, type DataTableProps as z };
2045
+ export { type FormStatusData as $, type AppShellProps as A, type BooleanFieldProps as B, type ChipSlotProps as C, type DateFieldProps as D, type EmailFieldProps as E, type FileFieldProps as F, type DataTableProps as G, DetailView as H, type ImageFieldProps as I, type JsonFieldProps as J, type DetailViewProps as K, DropZone as L, type DropZoneProps as M, type NumberFieldProps as N, type DropZoneSlotProps as O, FileList$1 as P, type FileListFile as Q, type RelationFieldProps as R, type FileListProps as S, type TextFieldProps as T, type UrlFieldProps as U, FilterBar as V, type FilterBarProps as W, type FilterDef as X, type FilterOption as Y, type FilterType as Z, FormStatus as _, type UploadFieldProps as a, type FormStatusProps as a0, ImagePreview as a1, type ImagePreviewProps as a2, ImpersonationBanner as a3, type ImpersonationBannerProps as a4, ListView as a5, type ListViewProps as a6, type PageContainerSlotProps as a7, PermissionGate as a8, type PermissionGateProps as a9, useUIPlugin as aA, RoleBadge as aa, type RoleBadgeProps as ab, type SidebarSlotProps as ac, type TableCellSlotProps as ad, type TableRowSlotProps as ae, type TableSectionSlotProps as af, type TableSlotProps as ag, type ToastApi as ah, ToastContext as ai, type ToastOptions as aj, type ToastSlotProps as ak, type ToastType as al, type TooltipSlotProps as am, type UIPlugin as an, type UIPluginComponents as ao, UIPluginProvider as ap, type UploadFieldFile as aq, type UserMenuLink as ar, type WhenForbidden as as, createUIPlugin as at, getInitials as au, useActionToast as av, useColumnInference as aw, useComponent as ax, useConfirm as ay, useToast as az, type FieldComponent as b, type EmptyStateProps as c, type NavigationProgressProps as d, type BreadcrumbItem as e, type TabItem as f, type NavigationItem as g, type UserMenuProps as h, ActionButton as i, type ActionButtonProps as j, type AlertSlotProps as k, type AppShellSlotProps as l, AvatarWithInitials as m, type AvatarWithInitialsProps as n, type BaseFieldProps as o, type BreadcrumbSlotProps as p, type BulkAction as q, BulkActionBar as r, type ButtonSlotProps as s, type ColumnDef as t, type ColumnShorthand as u, ConfirmContext as v, type ConfirmDialogSlotProps as w, type ConfirmOptions as x, ConfirmProvider as y, DataTable as z };
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { i as ActionButton, m as AvatarWithInitials, r as BulkActionBar, x as ConfirmProvider, y as DataTable, G as DetailView, K as DropZone, O as FileList, S as FilterBar, Z as FormStatus, a0 as ImagePreview, a2 as ImpersonationBanner, a4 as ListView, a7 as PermissionGate, a9 as RoleBadge, ao as UIPluginProvider, as as createUIPlugin, at as getInitials, au as useActionToast, av as useColumnInference, aw as useComponent, ax as useConfirm, ay as useToast, az as useUIPlugin } from './client-C0K-7jLA.js';
1
+ export { i as ActionButton, m as AvatarWithInitials, r as BulkActionBar, y as ConfirmProvider, z as DataTable, H as DetailView, L as DropZone, P as FileList, V as FilterBar, _ as FormStatus, a1 as ImagePreview, a3 as ImpersonationBanner, a5 as ListView, a8 as PermissionGate, aa as RoleBadge, ap as UIPluginProvider, at as createUIPlugin, au as getInitials, av as useActionToast, aw as useColumnInference, ax as useComponent, ay as useConfirm, az as useToast, aA as useUIPlugin } from './client-Cb_d1sLi.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
4
4
  import '@cfast/actions/client';
package/dist/client.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  useConfirm,
24
24
  useToast,
25
25
  useUIPlugin
26
- } from "./chunk-PWBG6CGF.js";
26
+ } from "./chunk-NGBQQYZZ.js";
27
27
  export {
28
28
  ActionButton,
29
29
  AvatarWithInitials,
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { D as DateFieldProps, B as BooleanFieldProps, N as NumberFieldProps, T as TextFieldProps, E as EmailFieldProps, U as UrlFieldProps, I as ImageFieldProps, F as FileFieldProps, R as RelationFieldProps, J as JsonFieldProps, a as UploadFieldProps, b as FieldComponent, c as EmptyStateProps, d as NavigationProgressProps, e as BreadcrumbItem, f as TabItem, A as AppShellProps, g as NavigationItem, h as UserMenuProps } from './client-C0K-7jLA.js';
2
- export { i as ActionButton, j as ActionButtonProps, k as AlertSlotProps, l as AppShellSlotProps, m as AvatarWithInitials, n as AvatarWithInitialsProps, o as BaseFieldProps, p as BreadcrumbSlotProps, q as BulkAction, r as BulkActionBar, s as ButtonSlotProps, C as ChipSlotProps, t as ColumnDef, u as ColumnShorthand, v as ConfirmDialogSlotProps, w as ConfirmOptions, x as ConfirmProvider, y as DataTable, z as DataTableProps, G as DetailView, H as DetailViewProps, K as DropZone, L as DropZoneProps, M as DropZoneSlotProps, O as FileList, P as FileListFile, Q as FileListProps, S as FilterBar, V as FilterBarProps, W as FilterDef, X as FilterOption, Y as FilterType, Z as FormStatus, _ as FormStatusData, $ as FormStatusProps, a0 as ImagePreview, a1 as ImagePreviewProps, a2 as ImpersonationBanner, a3 as ImpersonationBannerProps, a4 as ListView, a5 as ListViewProps, a6 as PageContainerSlotProps, a7 as PermissionGate, a8 as PermissionGateProps, a9 as RoleBadge, aa as RoleBadgeProps, ab as SidebarSlotProps, ac as TableCellSlotProps, ad as TableRowSlotProps, ae as TableSectionSlotProps, af as TableSlotProps, ag as ToastApi, ah as ToastContext, ai as ToastOptions, aj as ToastSlotProps, ak as ToastType, al as TooltipSlotProps, am as UIPlugin, an as UIPluginComponents, ao as UIPluginProvider, ap as UploadFieldFile, aq as UserMenuLink, ar as WhenForbidden, as as createUIPlugin, at as getInitials, au as useActionToast, av as useColumnInference, aw as useComponent, ax as useConfirm, ay as useToast, az as useUIPlugin } from './client-C0K-7jLA.js';
1
+ import { D as DateFieldProps, B as BooleanFieldProps, N as NumberFieldProps, T as TextFieldProps, E as EmailFieldProps, U as UrlFieldProps, I as ImageFieldProps, F as FileFieldProps, R as RelationFieldProps, J as JsonFieldProps, a as UploadFieldProps, b as FieldComponent, c as EmptyStateProps, d as NavigationProgressProps, e as BreadcrumbItem, f as TabItem, A as AppShellProps, g as NavigationItem, h as UserMenuProps } from './client-Cb_d1sLi.js';
2
+ export { i as ActionButton, j as ActionButtonProps, k as AlertSlotProps, l as AppShellSlotProps, m as AvatarWithInitials, n as AvatarWithInitialsProps, o as BaseFieldProps, p as BreadcrumbSlotProps, q as BulkAction, r as BulkActionBar, s as ButtonSlotProps, C as ChipSlotProps, t as ColumnDef, u as ColumnShorthand, v as ConfirmContext, w as ConfirmDialogSlotProps, x as ConfirmOptions, y as ConfirmProvider, z as DataTable, G as DataTableProps, H as DetailView, K as DetailViewProps, L as DropZone, M as DropZoneProps, O as DropZoneSlotProps, P as FileList, Q as FileListFile, S as FileListProps, V as FilterBar, W as FilterBarProps, X as FilterDef, Y as FilterOption, Z as FilterType, _ as FormStatus, $ as FormStatusData, a0 as FormStatusProps, a1 as ImagePreview, a2 as ImagePreviewProps, a3 as ImpersonationBanner, a4 as ImpersonationBannerProps, a5 as ListView, a6 as ListViewProps, a7 as PageContainerSlotProps, a8 as PermissionGate, a9 as PermissionGateProps, aa as RoleBadge, ab as RoleBadgeProps, ac as SidebarSlotProps, ad as TableCellSlotProps, ae as TableRowSlotProps, af as TableSectionSlotProps, ag as TableSlotProps, ah as ToastApi, ai as ToastContext, aj as ToastOptions, ak as ToastSlotProps, al as ToastType, am as TooltipSlotProps, an as UIPlugin, ao as UIPluginComponents, ap as UIPluginProvider, aq as UploadFieldFile, ar as UserMenuLink, as as WhenForbidden, at as createUIPlugin, au as getInitials, av as useActionToast, aw as useColumnInference, ax as useComponent, ay as useConfirm, az as useToast, aA as useUIPlugin } from './client-Cb_d1sLi.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode } from 'react';
5
5
  import { ClientDescriptor, ActionHookResult } from '@cfast/actions/client';
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  AvatarWithInitials,
4
4
  BooleanField,
5
5
  BulkActionBar,
6
+ ConfirmContext,
6
7
  ConfirmProvider,
7
8
  DataTable,
8
9
  DateField,
@@ -37,7 +38,7 @@ import {
37
38
  useConfirm,
38
39
  useToast,
39
40
  useUIPlugin
40
- } from "./chunk-PWBG6CGF.js";
41
+ } from "./chunk-NGBQQYZZ.js";
41
42
 
42
43
  // src/fields/url-field.tsx
43
44
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -676,6 +677,7 @@ export {
676
677
  AvatarWithInitials,
677
678
  BooleanField,
678
679
  BulkActionBar,
680
+ ConfirmContext,
679
681
  ConfirmProvider,
680
682
  DataTable,
681
683
  DateField,
package/llms.txt CHANGED
@@ -51,7 +51,7 @@ useColumnInference(table: Record<string, unknown> | undefined, columns?: string[
51
51
  - `NavigationProgress` -- thin progress bar during React Router navigation.
52
52
 
53
53
  **Actions & feedback:**
54
- - `ActionButton` -- permission-aware button wrapping a `@cfast/actions` action. Props: `action`, `input`, `whenForbidden: "hide" | "disable" | "show"`, `confirmation`.
54
+ - `ActionButton` -- permission-aware button wrapping a `@cfast/actions` action. Props: `action`, `whenForbidden: "hide" | "disable" | "show"`, `confirmation`, `href` (render as link), `input` (submit form data via fetcher).
55
55
  - `PermissionGate` -- conditional render based on action permissions. Props: `action`, `input`, `fallback`.
56
56
  - `ConfirmDialog` -- standalone confirmation dialog.
57
57
  - `ToastProvider` -- mount once in root layout for toast notifications.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfast/ui",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Permission-aware React components with UI library plugins",
5
5
  "keywords": [
6
6
  "cfast",
@@ -41,7 +41,7 @@
41
41
  "@cfast/auth": ">=0.3.0 <0.5.0",
42
42
  "@cfast/db": ">=0.3.0 <0.5.0",
43
43
  "@cfast/pagination": ">=0.2.0 <0.4.0",
44
- "@cfast/permissions": ">=0.3.0 <0.5.0",
44
+ "@cfast/permissions": ">=0.3.0 <0.7.0",
45
45
  "@cfast/storage": ">=0.1.0 <0.3.0",
46
46
  "react": ">=19",
47
47
  "react-dom": ">=19",
@@ -79,12 +79,12 @@
79
79
  "tsup": "^8",
80
80
  "typescript": "^5.7",
81
81
  "vitest": "^4.1.0",
82
- "@cfast/actions": "0.1.2",
83
- "@cfast/auth": "0.4.0",
82
+ "@cfast/actions": "0.3.0",
83
+ "@cfast/auth": "0.6.0",
84
84
  "@cfast/pagination": "0.2.0",
85
- "@cfast/permissions": "0.4.0",
86
- "@cfast/storage": "0.2.0",
87
- "@cfast/db": "0.4.0"
85
+ "@cfast/db": "0.8.0",
86
+ "@cfast/permissions": "0.7.0",
87
+ "@cfast/storage": "0.2.1"
88
88
  },
89
89
  "scripts": {
90
90
  "build": "tsup src/index.ts src/client.ts --format esm --dts",