@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:
|
|
266
|
+
onClick: handleClick,
|
|
243
267
|
disabled,
|
|
244
|
-
loading:
|
|
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={
|
|
1629
|
-
*
|
|
1630
|
-
* confirmation="Publish this post?"
|
|
1662
|
+
* action={recipe.canDelete()}
|
|
1663
|
+
* input={{ _action: "deleteRecipe", id: recipe.id }}
|
|
1631
1664
|
* >
|
|
1632
|
-
*
|
|
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-
|
|
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
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-
|
|
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-
|
|
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
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`, `
|
|
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
|
+
"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.
|
|
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.
|
|
83
|
-
"@cfast/auth": "0.
|
|
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/
|
|
85
|
+
"@cfast/db": "0.8.0",
|
|
86
|
+
"@cfast/permissions": "0.7.0",
|
|
87
87
|
"@cfast/storage": "0.2.1"
|
|
88
88
|
},
|
|
89
89
|
"scripts": {
|