@cfast/ui 0.3.1 → 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
  );
@@ -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.
@@ -1620,20 +1630,43 @@ declare function PermissionGate({ action, children, fallback, }: PermissionGateP
1620
1630
  * - `whenForbidden="disable"` -- shown but disabled when not permitted (default)
1621
1631
  * - `whenForbidden="show"` -- shown and clickable regardless of permission
1622
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
+ *
1623
1643
  * @param props - See {@link ActionButtonProps}.
1624
1644
  *
1625
- * @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
1626
1660
  * ```tsx
1627
1661
  * <ActionButton
1628
- * action={publishPost}
1629
- * whenForbidden="disable"
1630
- * confirmation="Publish this post?"
1662
+ * action={recipe.canDelete()}
1663
+ * input={{ _action: "deleteRecipe", id: recipe.id }}
1631
1664
  * >
1632
- * Publish
1665
+ * Delete
1633
1666
  * </ActionButton>
1634
1667
  * ```
1635
1668
  */
1636
- 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;
1637
1670
 
1638
1671
  /**
1639
1672
  * Provides the {@link useConfirm} context and renders the confirmation dialog.
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
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-DcCN1BR-.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-TLLCSKE5.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-DcCN1BR-.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-DcCN1BR-.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
@@ -38,7 +38,7 @@ import {
38
38
  useConfirm,
39
39
  useToast,
40
40
  useUIPlugin
41
- } from "./chunk-TLLCSKE5.js";
41
+ } from "./chunk-NGBQQYZZ.js";
42
42
 
43
43
  // src/fields/url-field.tsx
44
44
  import { jsx, jsxs } from "react/jsx-runtime";
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.1",
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.6.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,11 +79,11 @@
79
79
  "tsup": "^8",
80
80
  "typescript": "^5.7",
81
81
  "vitest": "^4.1.0",
82
- "@cfast/actions": "0.1.3",
83
- "@cfast/auth": "0.4.2",
84
- "@cfast/db": "0.4.1",
82
+ "@cfast/actions": "0.3.0",
83
+ "@cfast/auth": "0.6.0",
85
84
  "@cfast/pagination": "0.2.0",
86
- "@cfast/permissions": "0.5.1",
85
+ "@cfast/db": "0.8.0",
86
+ "@cfast/permissions": "0.7.0",
87
87
  "@cfast/storage": "0.2.1"
88
88
  },
89
89
  "scripts": {