@vendure/dashboard 3.4.2-master-202509081248 → 3.4.2-master-202509110229
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.
- package/package.json +4 -4
- package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_assets/assets.tsx +1 -0
- package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +1 -1
- package/src/app/routes/_authenticated/_channels/channels_.$id.tsx +5 -5
- package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_countries/countries_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_customers/customers_.$id.tsx +5 -5
- package/src/app/routes/_authenticated/_facets/facets.tsx +5 -8
- package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +2 -2
- package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +1 -1
- package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +5 -1
- package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +1 -1
- package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +5 -5
- package/src/app/routes/_authenticated/_products/products_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_products/products_.$id_.variants.tsx +2 -2
- package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +5 -5
- package/src/app/routes/_authenticated/_roles/roles_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_system/healthchecks.tsx +1 -0
- package/src/app/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx +4 -4
- package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +5 -4
- package/src/app/routes/_authenticated/_zones/zones_.$id.tsx +4 -4
- package/src/lib/components/data-input/affixed-input.tsx +18 -0
- package/src/lib/components/data-input/boolean-input.tsx +7 -0
- package/src/lib/components/data-input/checkbox-input.tsx +7 -0
- package/src/lib/components/data-input/datetime-input.tsx +7 -0
- package/src/lib/components/data-input/money-input.tsx +8 -0
- package/src/lib/components/data-input/number-input.tsx +7 -0
- package/src/lib/components/data-input/password-input.tsx +7 -0
- package/src/lib/components/data-input/rich-text-input.tsx +7 -0
- package/src/lib/components/data-input/text-input.tsx +7 -0
- package/src/lib/components/data-input/textarea-input.tsx +7 -0
- package/src/lib/components/data-table/data-table-view-options.tsx +29 -26
- package/src/lib/components/data-table/data-table.tsx +20 -0
- package/src/lib/components/data-table/types.ts +39 -0
- package/src/lib/components/layout/channel-switcher.tsx +1 -3
- package/src/lib/components/layout/generated-breadcrumbs.tsx +103 -43
- package/src/lib/components/shared/asset/asset-gallery.tsx +58 -0
- package/src/lib/components/shared/asset/asset-picker-dialog.tsx +39 -0
- package/src/lib/components/shared/detail-page-button.tsx +8 -22
- package/src/lib/components/shared/facet-value-chip.tsx +7 -0
- package/src/lib/components/shared/facet-value-selector.tsx +55 -0
- package/src/lib/components/shared/form-field-wrapper.tsx +51 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +128 -16
- package/src/lib/components/shared/permission-guard.tsx +30 -0
- package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +1 -1
- package/src/lib/components/shared/translatable-form-field.tsx +52 -0
- package/src/lib/components/shared/vendure-image.tsx +114 -2
- package/src/lib/framework/extension-api/define-dashboard-extension.ts +25 -3
- package/src/lib/framework/extension-api/extension-api-types.ts +12 -3
- package/src/lib/framework/extension-api/types/alerts.ts +2 -3
- package/src/lib/framework/extension-api/types/data-table.ts +2 -2
- package/src/lib/framework/extension-api/types/detail-forms.ts +2 -2
- package/src/lib/framework/extension-api/types/form-components.ts +2 -2
- package/src/lib/framework/extension-api/types/layout.ts +24 -13
- package/src/lib/framework/extension-api/types/login.ts +6 -5
- package/src/lib/framework/extension-api/types/navigation.ts +3 -3
- package/src/lib/framework/extension-api/types/widgets.ts +7 -3
- package/src/lib/framework/form-engine/form-engine-types.ts +13 -7
- package/src/lib/framework/form-engine/use-generated-form.tsx +44 -0
- package/src/lib/framework/layout-engine/page-layout.tsx +94 -31
- package/src/lib/framework/page/detail-page.tsx +3 -5
- package/src/lib/framework/page/list-page.tsx +87 -5
- package/src/lib/framework/page/use-detail-page.ts +4 -5
- package/src/lib/graphql/api.ts +2 -2
- package/src/lib/graphql/graphql-env.d.ts +7 -16
- package/src/lib/hooks/use-auth.tsx +1 -3
- package/src/lib/hooks/use-channel.ts +4 -2
- package/src/lib/hooks/use-page-block.tsx +9 -0
- package/src/lib/hooks/use-permissions.ts +6 -2
- package/src/lib/index.ts +2 -0
- package/src/lib/providers/auth.tsx +34 -2
- package/src/lib/providers/channel-provider.tsx +22 -1
- package/src/lib/components/shared/table-cell/table-cell-types.ts +0 -33
|
@@ -5,9 +5,11 @@ import {
|
|
|
5
5
|
BreadcrumbList,
|
|
6
6
|
BreadcrumbSeparator,
|
|
7
7
|
} from '@/vdb/components/ui/breadcrumb.js';
|
|
8
|
-
import { Link, useRouterState } from '@tanstack/react-router';
|
|
8
|
+
import { Link, useRouter, useRouterState } from '@tanstack/react-router';
|
|
9
9
|
import * as React from 'react';
|
|
10
10
|
import { Fragment } from 'react';
|
|
11
|
+
import { getNavMenuConfig } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
|
|
12
|
+
import type { NavMenuItem, NavMenuSection } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
|
|
11
13
|
|
|
12
14
|
export interface BreadcrumbPair {
|
|
13
15
|
label: string | React.ReactElement;
|
|
@@ -20,54 +22,112 @@ export type PageBreadcrumb = BreadcrumbPair | BreadcrumbShorthand;
|
|
|
20
22
|
|
|
21
23
|
export function GeneratedBreadcrumbs() {
|
|
22
24
|
const matches = useRouterState({ select: s => s.matches });
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
25
|
+
const currentPath = useRouterState({ select: s => s.location.pathname });
|
|
26
|
+
const router = useRouter();
|
|
27
|
+
const navMenuConfig = getNavMenuConfig();
|
|
28
|
+
const basePath = router.basepath || '';
|
|
29
|
+
|
|
30
|
+
const normalizeBreadcrumb = (breadcrumb: any, pathname: string): BreadcrumbPair[] => {
|
|
31
|
+
if (typeof breadcrumb === 'string') {
|
|
32
|
+
return [{ label: breadcrumb, path: pathname }];
|
|
33
|
+
}
|
|
34
|
+
if (React.isValidElement(breadcrumb)) {
|
|
35
|
+
return [{ label: breadcrumb, path: pathname }];
|
|
36
|
+
}
|
|
37
|
+
if (typeof breadcrumb === 'function') {
|
|
38
|
+
return [{ label: breadcrumb(), path: pathname }];
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(breadcrumb)) {
|
|
41
|
+
return breadcrumb.map((crumb: PageBreadcrumb) => {
|
|
42
|
+
if (typeof crumb === 'string' || React.isValidElement(crumb)) {
|
|
43
|
+
return { label: crumb, path: pathname };
|
|
44
|
+
}
|
|
45
|
+
return { label: crumb.label, path: crumb.path };
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return [];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const rawCrumbs: BreadcrumbPair[] = React.useMemo(() => {
|
|
52
|
+
return matches
|
|
53
|
+
.filter(match => match.loaderData?.breadcrumb)
|
|
54
|
+
.flatMap(({ pathname, loaderData }) =>
|
|
55
|
+
normalizeBreadcrumb(loaderData.breadcrumb, pathname)
|
|
56
|
+
);
|
|
57
|
+
}, [matches]);
|
|
58
|
+
|
|
59
|
+
const isBaseRoute = (p: string) => p === basePath || p === `${basePath}/`;
|
|
60
|
+
const pageCrumbs: BreadcrumbPair[] = rawCrumbs.filter(c => !isBaseRoute(c.path));
|
|
61
|
+
|
|
62
|
+
const normalizePath = (path: string): string => {
|
|
63
|
+
const normalizedPath = basePath && path.startsWith(basePath) ? path.slice(basePath.length) : path;
|
|
64
|
+
return normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const pathMatches = (cleanPath: string, rawUrl?: string): boolean => {
|
|
68
|
+
if (!rawUrl) return false;
|
|
69
|
+
const strip = (p: string) => (p !== '/' && p.endsWith('/') ? p.slice(0, -1) : p);
|
|
70
|
+
const p = strip(cleanPath);
|
|
71
|
+
const u = strip(rawUrl);
|
|
72
|
+
return p === u || p.startsWith(`${u}/`);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const checkSectionItems = (
|
|
76
|
+
section: NavMenuSection | NavMenuItem,
|
|
77
|
+
cleanPath: string,
|
|
78
|
+
): BreadcrumbPair | undefined => {
|
|
79
|
+
if (!('items' in section) || !Array.isArray(section.items)) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (const item of section.items) {
|
|
84
|
+
if (!item?.url) continue;
|
|
85
|
+
if (pathMatches(cleanPath, item.url)) {
|
|
86
|
+
return { label: section.title, path: item.url };
|
|
57
87
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const checkDirectSection = (
|
|
93
|
+
section: NavMenuSection | NavMenuItem,
|
|
94
|
+
cleanPath: string,
|
|
95
|
+
): BreadcrumbPair | undefined => {
|
|
96
|
+
if ('url' in section && section.url && pathMatches(cleanPath, section.url)) {
|
|
97
|
+
return { label: section.title, path: section.url };
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const findSectionCrumb = (path: string): BreadcrumbPair | undefined => {
|
|
103
|
+
const cleanPath = normalizePath(path);
|
|
104
|
+
const sections: Array<NavMenuSection | NavMenuItem> = navMenuConfig?.sections ?? [];
|
|
105
|
+
if (sections.length === 0) return undefined;
|
|
106
|
+
|
|
107
|
+
for (const section of sections) {
|
|
108
|
+
const result = checkSectionItems(section, cleanPath) || checkDirectSection(section, cleanPath);
|
|
109
|
+
if (result) {
|
|
110
|
+
return result;
|
|
63
111
|
}
|
|
64
|
-
}
|
|
65
|
-
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const sectionCrumb = React.useMemo(
|
|
117
|
+
() => findSectionCrumb(currentPath),
|
|
118
|
+
[currentPath, basePath, navMenuConfig],
|
|
119
|
+
);
|
|
120
|
+
const breadcrumbs: BreadcrumbPair[] = React.useMemo(() => {
|
|
121
|
+
const arr = sectionCrumb ? [sectionCrumb, ...pageCrumbs] : pageCrumbs;
|
|
122
|
+
return arr.filter((c, i, self) =>
|
|
123
|
+
self.findIndex(x => x.path === c.path && x.label === c.label) === i,
|
|
124
|
+
);
|
|
125
|
+
}, [sectionCrumb, pageCrumbs]);
|
|
66
126
|
return (
|
|
67
127
|
<Breadcrumb>
|
|
68
128
|
<BreadcrumbList>
|
|
69
129
|
{breadcrumbs.map(({ label, path }, index, arr) => (
|
|
70
|
-
<Fragment key={index}>
|
|
130
|
+
<Fragment key={`${path}-${index}`}>
|
|
71
131
|
<BreadcrumbItem className="hidden md:block">
|
|
72
132
|
<BreadcrumbLink asChild>
|
|
73
133
|
<Link to={path}>{label}</Link>
|
|
@@ -71,6 +71,13 @@ const AssetType = {
|
|
|
71
71
|
|
|
72
72
|
export type Asset = AssetFragment;
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @description
|
|
76
|
+
* Props for the {@link AssetGallery} component.
|
|
77
|
+
*
|
|
78
|
+
* @docsCategory components
|
|
79
|
+
* @docsPage AssetGallery
|
|
80
|
+
*/
|
|
74
81
|
export interface AssetGalleryProps {
|
|
75
82
|
onSelect?: (assets: Asset[]) => void;
|
|
76
83
|
selectable?: boolean;
|
|
@@ -82,16 +89,67 @@ export interface AssetGalleryProps {
|
|
|
82
89
|
* If set to 'manual', multiple selection will occur only if the user holds down the control/cmd key.
|
|
83
90
|
*/
|
|
84
91
|
multiSelect?: 'auto' | 'manual';
|
|
92
|
+
/**
|
|
93
|
+
* @description
|
|
94
|
+
* The initial assets that should be selected.
|
|
95
|
+
*/
|
|
85
96
|
initialSelectedAssets?: Asset[];
|
|
97
|
+
/**
|
|
98
|
+
* @description
|
|
99
|
+
* The number of assets to display per page.
|
|
100
|
+
*/
|
|
86
101
|
pageSize?: number;
|
|
102
|
+
/**
|
|
103
|
+
* @description
|
|
104
|
+
* Whether the gallery should have a fixed height.
|
|
105
|
+
*/
|
|
87
106
|
fixedHeight?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* @description
|
|
109
|
+
* Whether the gallery should show a header.
|
|
110
|
+
*/
|
|
88
111
|
showHeader?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* @description
|
|
114
|
+
* The class name to apply to the gallery.
|
|
115
|
+
*/
|
|
89
116
|
className?: string;
|
|
117
|
+
/**
|
|
118
|
+
* @description
|
|
119
|
+
* The function to call when files are dropped.
|
|
120
|
+
*/
|
|
90
121
|
onFilesDropped?: (files: File[]) => void;
|
|
122
|
+
/**
|
|
123
|
+
* @description
|
|
124
|
+
* The bulk actions to display in the gallery.
|
|
125
|
+
*/
|
|
91
126
|
bulkActions?: AssetBulkAction[];
|
|
127
|
+
/**
|
|
128
|
+
* @description
|
|
129
|
+
* Whether the gallery should display bulk actions.
|
|
130
|
+
*/
|
|
92
131
|
displayBulkActions?: boolean;
|
|
93
132
|
}
|
|
94
133
|
|
|
134
|
+
/**
|
|
135
|
+
* @description
|
|
136
|
+
* A component for displaying a gallery of assets.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```tsx
|
|
140
|
+
* <AssetGallery
|
|
141
|
+
onSelect={handleAssetSelect}
|
|
142
|
+
multiSelect="manual"
|
|
143
|
+
initialSelectedAssets={initialSelectedAssets}
|
|
144
|
+
fixedHeight={false}
|
|
145
|
+
displayBulkActions={false}
|
|
146
|
+
/>
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* @docsCategory components
|
|
150
|
+
* @docsPage AssetGallery
|
|
151
|
+
* @docsWeight 0
|
|
152
|
+
*/
|
|
95
153
|
export function AssetGallery({
|
|
96
154
|
onSelect,
|
|
97
155
|
selectable = true,
|
|
@@ -9,15 +9,54 @@ import {
|
|
|
9
9
|
import { useState } from 'react';
|
|
10
10
|
import { Asset, AssetGallery } from './asset-gallery.js';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* @description
|
|
14
|
+
* Props for the {@link AssetPickerDialog} component.
|
|
15
|
+
*
|
|
16
|
+
* @docsCategory components
|
|
17
|
+
* @docsPage AssetPickerDialog
|
|
18
|
+
*/
|
|
12
19
|
interface AssetPickerDialogProps {
|
|
20
|
+
/**
|
|
21
|
+
* @description
|
|
22
|
+
* Whether the dialog is open.
|
|
23
|
+
*/
|
|
13
24
|
open: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* @description
|
|
27
|
+
* The function to call when the dialog is closed.
|
|
28
|
+
*/
|
|
14
29
|
onClose: () => void;
|
|
30
|
+
/**
|
|
31
|
+
* @description
|
|
32
|
+
* The function to call when assets are selected.
|
|
33
|
+
*/
|
|
15
34
|
onSelect: (assets: Asset[]) => void;
|
|
35
|
+
/**
|
|
36
|
+
* @description
|
|
37
|
+
* Whether multiple assets can be selected.
|
|
38
|
+
*/
|
|
16
39
|
multiSelect?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* @description
|
|
42
|
+
* The initial assets that should be selected.
|
|
43
|
+
*/
|
|
17
44
|
initialSelectedAssets?: Asset[];
|
|
45
|
+
/**
|
|
46
|
+
* @description
|
|
47
|
+
* The title of the dialog.
|
|
48
|
+
*/
|
|
18
49
|
title?: string;
|
|
19
50
|
}
|
|
20
51
|
|
|
52
|
+
/**
|
|
53
|
+
* @description
|
|
54
|
+
* A dialog which allows the creation and selection of assets.
|
|
55
|
+
*
|
|
56
|
+
* @docsCategory components
|
|
57
|
+
* @docsPage AssetPickerDialog
|
|
58
|
+
* @docsWeight 0
|
|
59
|
+
*/
|
|
21
60
|
export function AssetPickerDialog({
|
|
22
61
|
open,
|
|
23
62
|
onClose,
|
|
@@ -3,46 +3,32 @@ import { ChevronRight } from 'lucide-react';
|
|
|
3
3
|
import { Button } from '../ui/button.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
+
* @description
|
|
6
7
|
* DetailPageButton is a reusable navigation component designed to provide consistent UX
|
|
7
8
|
* across list views when linking to detail pages. It renders as a ghost button with
|
|
8
9
|
* a chevron indicator, making it easy for users to identify clickable links that
|
|
9
10
|
* navigate to detail views.
|
|
10
11
|
*
|
|
11
|
-
* @component
|
|
12
12
|
* @example
|
|
13
|
+
* ```tsx
|
|
13
14
|
* // Basic usage with ID (relative navigation)
|
|
14
15
|
* <DetailPageButton id="123" label="Product Name" />
|
|
16
|
+
*
|
|
15
17
|
*
|
|
16
18
|
* @example
|
|
19
|
+
* ```tsx
|
|
17
20
|
* // Custom href with search params
|
|
18
21
|
* <DetailPageButton
|
|
19
22
|
* href="/products/detail/456"
|
|
20
23
|
* label="Custom Product"
|
|
21
24
|
* search={{ tab: 'variants' }}
|
|
22
25
|
* />
|
|
26
|
+
* ```
|
|
23
27
|
*
|
|
24
|
-
* @example
|
|
25
|
-
* // Disabled state
|
|
26
|
-
* <DetailPageButton
|
|
27
|
-
* id="789"
|
|
28
|
-
* label="Unavailable Item"
|
|
29
|
-
* disabled={true}
|
|
30
|
-
* />
|
|
31
|
-
*
|
|
32
|
-
* @param {Object} props - Component props
|
|
33
|
-
* @param {string|React.ReactNode} props.label - The text or content to display in the button
|
|
34
|
-
* @param {string} [props.id] - The ID for relative navigation (creates href as `./${id}`)
|
|
35
|
-
* @param {string} [props.href] - Custom href for navigation (takes precedence over id)
|
|
36
|
-
* @param {boolean} [props.disabled=false] - Whether the button is disabled (prevents navigation)
|
|
37
|
-
* @param {Record<string, string>} [props.search] - Search parameters to include in the navigation
|
|
38
|
-
*
|
|
39
|
-
* @returns {React.ReactElement} A styled button component that navigates to detail pages
|
|
40
28
|
*
|
|
41
|
-
* @
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* - Preloading is disabled by default for performance optimization
|
|
45
|
-
* - Styled as a ghost button variant for subtle, consistent appearance
|
|
29
|
+
* @docsCategory components
|
|
30
|
+
* @docsPage DetailPageButton
|
|
31
|
+
* @since 3.4.0
|
|
46
32
|
*/
|
|
47
33
|
export function DetailPageButton({
|
|
48
34
|
id,
|
|
@@ -20,6 +20,13 @@ interface FacetValueChipProps {
|
|
|
20
20
|
onRemove?: (id: string) => void;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @description
|
|
25
|
+
* A component for displaying a facet value as a chip.
|
|
26
|
+
*
|
|
27
|
+
* @docsCategory components
|
|
28
|
+
* @since 3.4.0
|
|
29
|
+
*/
|
|
23
30
|
export function FacetValueChip({
|
|
24
31
|
facetValue,
|
|
25
32
|
removable = true,
|
|
@@ -29,10 +29,51 @@ export interface Facet {
|
|
|
29
29
|
code: string;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* @description
|
|
34
|
+
* A component for selecting facet values.
|
|
35
|
+
*
|
|
36
|
+
* @docsCategory components
|
|
37
|
+
* @docsPage FacetValueSelector
|
|
38
|
+
* @since 3.4.0
|
|
39
|
+
*/
|
|
32
40
|
interface FacetValueSelectorProps {
|
|
41
|
+
/**
|
|
42
|
+
* @description
|
|
43
|
+
* The function to call when a facet value is selected.
|
|
44
|
+
*
|
|
45
|
+
* The `value` will have the following structure:
|
|
46
|
+
*
|
|
47
|
+
* ```ts
|
|
48
|
+
* {
|
|
49
|
+
* id: string;
|
|
50
|
+
* name: string;
|
|
51
|
+
* code: string;
|
|
52
|
+
* facet: {
|
|
53
|
+
* id: string;
|
|
54
|
+
* name: string;
|
|
55
|
+
* code: string;
|
|
56
|
+
* };
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
33
60
|
onValueSelect: (value: FacetValue) => void;
|
|
61
|
+
/**
|
|
62
|
+
* @description
|
|
63
|
+
* Whether the selector is disabled.
|
|
64
|
+
*/
|
|
34
65
|
disabled?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* @description
|
|
68
|
+
* The placeholder text for the selector.
|
|
69
|
+
*/
|
|
35
70
|
placeholder?: string;
|
|
71
|
+
/**
|
|
72
|
+
* @description
|
|
73
|
+
* The number of facet values to display per page.
|
|
74
|
+
*
|
|
75
|
+
* @default 4
|
|
76
|
+
*/
|
|
36
77
|
pageSize?: number;
|
|
37
78
|
}
|
|
38
79
|
|
|
@@ -85,6 +126,20 @@ const getFacetValuesForFacetDocument = graphql(`
|
|
|
85
126
|
}
|
|
86
127
|
`);
|
|
87
128
|
|
|
129
|
+
/**
|
|
130
|
+
* @description
|
|
131
|
+
* A component for selecting facet values.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```tsx
|
|
135
|
+
* <FacetValueSelector onValueSelect={onValueSelectHandler} disabled={disabled} />
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* @docsCategory components
|
|
139
|
+
* @docsPage FacetValueSelector
|
|
140
|
+
* @docsWeight 0
|
|
141
|
+
* @since 3.4.0
|
|
142
|
+
*/
|
|
88
143
|
export function FacetValueSelector({
|
|
89
144
|
onValueSelect,
|
|
90
145
|
disabled,
|
|
@@ -3,11 +3,27 @@ import { LocationWrapper } from '@/vdb/framework/layout-engine/location-wrapper.
|
|
|
3
3
|
import { FieldPath, FieldValues } from 'react-hook-form';
|
|
4
4
|
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '../ui/form.js';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @description
|
|
8
|
+
* The props for the FormFieldWrapper component.
|
|
9
|
+
*
|
|
10
|
+
* @docsCategory form-components
|
|
11
|
+
* @docsPage FormFieldWrapper
|
|
12
|
+
* @since 3.4.0
|
|
13
|
+
*/
|
|
6
14
|
export type FormFieldWrapperProps<
|
|
7
15
|
TFieldValues extends FieldValues = FieldValues,
|
|
8
16
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
9
17
|
> = React.ComponentProps<typeof FormField<TFieldValues, TName>> & {
|
|
18
|
+
/**
|
|
19
|
+
* @description
|
|
20
|
+
* The label for the form field.
|
|
21
|
+
*/
|
|
10
22
|
label?: React.ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* @description
|
|
25
|
+
* The description for the form field.
|
|
26
|
+
*/
|
|
11
27
|
description?: React.ReactNode;
|
|
12
28
|
/**
|
|
13
29
|
* @description
|
|
@@ -15,11 +31,46 @@ export type FormFieldWrapperProps<
|
|
|
15
31
|
* If false, the form control will not be rendered.
|
|
16
32
|
* This is useful when you want to render the form control in a custom way, e.g. for <Select/> components,
|
|
17
33
|
* where the FormControl needs to nested in the root component.
|
|
34
|
+
*
|
|
18
35
|
* @default true
|
|
19
36
|
*/
|
|
20
37
|
renderFormControl?: boolean;
|
|
21
38
|
};
|
|
22
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @description
|
|
42
|
+
* This is a wrapper that can be used in all forms to wrap the actual form control, and provide a label, description and error message.
|
|
43
|
+
*
|
|
44
|
+
* Use this instead of the default Shadcn FormField (etc.) components, as it also supports
|
|
45
|
+
* overridden form components.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* <PageBlock column="main" blockId="main-form">
|
|
50
|
+
* <DetailFormGrid>
|
|
51
|
+
* <FormFieldWrapper
|
|
52
|
+
* control={form.control}
|
|
53
|
+
* name="description"
|
|
54
|
+
* label={<Trans>Description</Trans>}
|
|
55
|
+
* render={({ field }) => <Input {...field} />}
|
|
56
|
+
* />
|
|
57
|
+
* <FormFieldWrapper
|
|
58
|
+
* control={form.control}
|
|
59
|
+
* name="code"
|
|
60
|
+
* label={<Trans>Code</Trans>}
|
|
61
|
+
* render={({ field }) => <Input {...field} />}
|
|
62
|
+
* />
|
|
63
|
+
* </DetailFormGrid>
|
|
64
|
+
* </PageBlock>
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* If you are dealing with translatable fields, use the {@link TranslatableFormFieldWrapper} component instead.
|
|
68
|
+
*
|
|
69
|
+
* @docsCategory form-components
|
|
70
|
+
* @docsPage FormFieldWrapper
|
|
71
|
+
* @docsWeight 0
|
|
72
|
+
* @since 3.4.0
|
|
73
|
+
*/
|
|
23
74
|
export function FormFieldWrapper<
|
|
24
75
|
TFieldValues extends FieldValues = FieldValues,
|
|
25
76
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|