@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:
|
|
266
|
+
onClick: handleClick,
|
|
243
267
|
disabled,
|
|
244
|
-
loading:
|
|
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={
|
|
1625
|
-
*
|
|
1626
|
-
* confirmation="Publish this post?"
|
|
1662
|
+
* action={recipe.canDelete()}
|
|
1663
|
+
* input={{ _action: "deleteRecipe", id: recipe.id }}
|
|
1627
1664
|
* >
|
|
1628
|
-
*
|
|
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
|
|
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,
|
|
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
|
|
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-
|
|
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`, `
|
|
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,12 +79,12 @@
|
|
|
79
79
|
"tsup": "^8",
|
|
80
80
|
"typescript": "^5.7",
|
|
81
81
|
"vitest": "^4.1.0",
|
|
82
|
-
"@cfast/actions": "0.
|
|
83
|
-
"@cfast/auth": "0.
|
|
82
|
+
"@cfast/actions": "0.3.0",
|
|
83
|
+
"@cfast/auth": "0.6.0",
|
|
84
84
|
"@cfast/pagination": "0.2.0",
|
|
85
|
-
"@cfast/
|
|
86
|
-
"@cfast/
|
|
87
|
-
"@cfast/
|
|
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",
|