@firecms/core 3.0.0-beta.5 → 3.0.0-beta.7
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/dist/components/ClearFilterSortButton.d.ts +5 -0
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
- package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
- package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
- package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
- package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +11 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
- package/dist/components/EntityPreview.d.ts +3 -2
- package/dist/components/FireCMSAppBar.d.ts +4 -2
- package/dist/components/ReferenceWidget.d.ts +3 -1
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -6
- package/dist/components/VirtualTable/types.d.ts +3 -3
- package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
- package/dist/components/common/index.d.ts +1 -0
- package/dist/components/common/table_height.d.ts +5 -0
- package/dist/components/common/types.d.ts +4 -6
- package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/core/Drawer.d.ts +5 -12
- package/dist/core/DrawerNavigationItem.d.ts +9 -0
- package/dist/core/NavigationRoutes.d.ts +1 -1
- package/dist/core/Scaffold.d.ts +8 -12
- package/dist/core/index.d.ts +3 -4
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/hooks/data/save.d.ts +1 -2
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useValidateAuthenticator.d.ts +2 -6
- package/dist/index.es.js +10087 -9914
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +1 -16
- package/dist/preview/components/ReferencePreview.d.ts +2 -1
- package/dist/types/collections.d.ts +13 -2
- package/dist/types/datasource.d.ts +2 -5
- package/dist/types/entities.d.ts +5 -1
- package/dist/types/entity_actions.d.ts +14 -0
- package/dist/types/index.d.ts +0 -1
- package/dist/types/navigation.d.ts +2 -2
- package/dist/types/plugins.d.ts +3 -1
- package/dist/types/properties.d.ts +10 -2
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/icon_synonyms.d.ts +1 -97
- package/dist/util/navigation_utils.d.ts +2 -2
- package/dist/util/objects.d.ts +1 -1
- package/dist/util/resolutions.d.ts +13 -13
- package/package.json +139 -119
- package/src/components/ClearFilterSortButton.tsx +41 -0
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +19 -18
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +10 -6
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +38 -34
- package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -1
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +2 -2
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +2 -4
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +47 -25
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
- package/src/components/EntityPreview.tsx +9 -7
- package/src/components/EntityView.tsx +5 -5
- package/src/components/FireCMSAppBar.tsx +32 -15
- package/src/components/HomePage/DefaultHomePage.tsx +1 -1
- package/src/components/HomePage/NavigationCard.tsx +2 -2
- package/src/components/HomePage/SmallNavigationCard.tsx +5 -5
- package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +1 -0
- package/src/components/ReferenceWidget.tsx +21 -11
- package/src/components/SearchIconsView.tsx +5 -5
- package/src/components/SelectableTable/SelectableTable.tsx +5 -3
- package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +28 -6
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
- package/src/components/VirtualTable/VirtualTable.tsx +36 -29
- package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
- package/src/components/VirtualTable/VirtualTableProps.tsx +1 -6
- package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
- package/src/components/VirtualTable/types.tsx +2 -3
- package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +2 -2
- package/src/components/common/index.ts +1 -0
- package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
- package/src/components/common/types.tsx +4 -6
- package/src/components/common/useColumnsIds.tsx +10 -2
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +11 -0
- package/src/components/index.tsx +1 -0
- package/src/core/Drawer.tsx +16 -68
- package/src/core/DrawerNavigationItem.tsx +62 -0
- package/src/core/EntityEditView.tsx +7 -8
- package/src/core/EntitySidePanel.tsx +1 -1
- package/src/core/FireCMS.tsx +37 -42
- package/src/core/NavigationRoutes.tsx +3 -4
- package/src/core/Scaffold.tsx +80 -66
- package/src/core/field_configs.tsx +2 -3
- package/src/core/index.tsx +3 -4
- package/src/form/EntityForm.tsx +29 -21
- package/src/form/PropertyFieldBinding.tsx +0 -2
- package/src/form/components/StorageItemPreview.tsx +2 -2
- package/src/form/components/StorageUploadProgress.tsx +3 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
- package/src/form/field_bindings/BlockFieldBinding.tsx +2 -2
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +4 -4
- package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -2
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +15 -13
- package/src/form/field_bindings/SelectFieldBinding.tsx +3 -3
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +4 -4
- package/src/hooks/data/save.ts +3 -1
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
- package/src/hooks/useBuildModeController.tsx +11 -5
- package/src/hooks/useBuildNavigationController.tsx +77 -21
- package/src/hooks/useProjectLog.tsx +3 -3
- package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
- package/src/hooks/useValidateAuthenticator.tsx +17 -37
- package/src/internal/useBuildDataSource.ts +42 -47
- package/src/internal/useBuildSideEntityController.tsx +86 -22
- package/src/preview/PropertyPreview.tsx +4 -14
- package/src/preview/components/BooleanPreview.tsx +2 -2
- package/src/preview/components/EnumValuesChip.tsx +1 -1
- package/src/preview/components/ReferencePreview.tsx +21 -22
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
- package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
- package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
- package/src/types/collections.ts +19 -3
- package/src/types/datasource.ts +7 -4
- package/src/types/entities.ts +9 -1
- package/src/types/entity_actions.tsx +16 -3
- package/src/types/index.ts +0 -1
- package/src/types/navigation.ts +2 -3
- package/src/types/plugins.tsx +4 -3
- package/src/types/properties.ts +15 -4
- package/src/types/storage.ts +1 -1
- package/src/util/entities.ts +1 -1
- package/src/util/enums.ts +1 -1
- package/src/util/icon_list.ts +2 -2
- package/src/util/icon_synonyms.ts +3 -99
- package/src/util/navigation_utils.ts +6 -6
- package/src/util/objects.ts +8 -21
- package/src/util/resolutions.ts +32 -31
- package/dist/components/VirtualTable/common.d.ts +0 -2
- package/dist/core/SideEntityView.d.ts +0 -7
- package/dist/internal/useBuildCustomizationController.d.ts +0 -2
- package/dist/internal/useLocaleConfig.d.ts +0 -1
- package/dist/types/appcheck.d.ts +0 -26
- package/src/core/SideEntityView.tsx +0 -38
- package/src/internal/useBuildCustomizationController.tsx +0 -5
- package/src/internal/useLocaleConfig.tsx +0 -18
- package/src/types/appcheck.ts +0 -29
|
@@ -4,7 +4,7 @@ import { Entity, EntityCollection, EntityReference, FilterValues } from "../type
|
|
|
4
4
|
import { getReferenceFrom } from "../util";
|
|
5
5
|
import { PreviewSize, ReferencePreview } from "../preview";
|
|
6
6
|
import { useNavigationController, useReferenceDialog } from "../hooks";
|
|
7
|
-
import { Button,
|
|
7
|
+
import { Button, cls } from "@firecms/ui";
|
|
8
8
|
|
|
9
9
|
export type ReferenceWidgetProps<M extends Record<string, any>> = {
|
|
10
10
|
name?: string,
|
|
@@ -27,6 +27,8 @@ export type ReferenceWidgetProps<M extends Record<string, any>> = {
|
|
|
27
27
|
forceFilter?: FilterValues<string>;
|
|
28
28
|
size: PreviewSize;
|
|
29
29
|
className?: string;
|
|
30
|
+
includeId?: boolean;
|
|
31
|
+
includeEntityLink?: boolean;
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -43,7 +45,9 @@ export function ReferenceWidget<M extends Record<string, any>>({
|
|
|
43
45
|
previewProperties,
|
|
44
46
|
forceFilter,
|
|
45
47
|
size,
|
|
46
|
-
className
|
|
48
|
+
className,
|
|
49
|
+
includeId,
|
|
50
|
+
includeEntityLink
|
|
47
51
|
}: ReferenceWidgetProps<M>) {
|
|
48
52
|
|
|
49
53
|
const navigationController = useNavigationController();
|
|
@@ -52,16 +56,15 @@ export function ReferenceWidget<M extends Record<string, any>>({
|
|
|
52
56
|
return navigationController.getCollection(path);
|
|
53
57
|
}, [path, navigationController.getCollection]);
|
|
54
58
|
|
|
55
|
-
// if (!collection) {
|
|
56
|
-
// throw Error(`Couldn't find the corresponding collection for the path: ${path}`);
|
|
57
|
-
// }
|
|
58
|
-
|
|
59
59
|
const onSingleEntitySelected = useCallback((entity: Entity<M> | null) => {
|
|
60
60
|
if (disabled)
|
|
61
61
|
return;
|
|
62
62
|
if (onReferenceSelected) {
|
|
63
63
|
const reference = entity ? getReferenceFrom(entity) : null;
|
|
64
|
-
onReferenceSelected?.({
|
|
64
|
+
onReferenceSelected?.({
|
|
65
|
+
reference,
|
|
66
|
+
entity
|
|
67
|
+
});
|
|
65
68
|
}
|
|
66
69
|
}, [disabled, onReferenceSelected]);
|
|
67
70
|
|
|
@@ -70,7 +73,10 @@ export function ReferenceWidget<M extends Record<string, any>>({
|
|
|
70
73
|
return;
|
|
71
74
|
if (onMultipleReferenceSelected) {
|
|
72
75
|
const references = entities ? entities.map(e => getReferenceFrom(e)) : null;
|
|
73
|
-
onMultipleReferenceSelected({
|
|
76
|
+
onMultipleReferenceSelected({
|
|
77
|
+
references,
|
|
78
|
+
entities
|
|
79
|
+
});
|
|
74
80
|
}
|
|
75
81
|
}, [disabled, onReferenceSelected]);
|
|
76
82
|
|
|
@@ -110,7 +116,9 @@ export function ReferenceWidget<M extends Record<string, any>>({
|
|
|
110
116
|
reference={ref}
|
|
111
117
|
disabled={disabled}
|
|
112
118
|
previewProperties={previewProperties}
|
|
113
|
-
size={size}
|
|
119
|
+
size={size}
|
|
120
|
+
includeId={includeId}
|
|
121
|
+
includeEntityLink={includeEntityLink}/>
|
|
114
122
|
})}
|
|
115
123
|
</div>
|
|
116
124
|
} else if (value?.isEntityReference && value?.isEntityReference()) {
|
|
@@ -119,10 +127,12 @@ export function ReferenceWidget<M extends Record<string, any>>({
|
|
|
119
127
|
onClick={onEntryClick}
|
|
120
128
|
disabled={disabled}
|
|
121
129
|
previewProperties={previewProperties}
|
|
122
|
-
size={size}
|
|
130
|
+
size={size}
|
|
131
|
+
includeId={includeId}
|
|
132
|
+
includeEntityLink={includeEntityLink}/>
|
|
123
133
|
|
|
124
134
|
}
|
|
125
|
-
return <div className={
|
|
135
|
+
return <div className={cls("text-sm font-medium",
|
|
126
136
|
"min-w-80 flex flex-col gap-4",
|
|
127
137
|
"relative transition-colors duration-200 ease-in rounded font-medium",
|
|
128
138
|
disabled ? "bg-opacity-50" : "hover:bg-opacity-75",
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import { coolIconKeys, debounce, Icon, IconButton, iconKeys, SearchBar, Tooltip } from "@firecms/ui";
|
|
4
|
-
import {
|
|
4
|
+
import { iconSynonyms, iconsSearch } from "../util";
|
|
5
5
|
|
|
6
6
|
const UPDATE_SEARCH_INDEX_WAIT_MS = 220;
|
|
7
7
|
|
|
8
|
-
if (process.env.NODE_ENV !== "production") {
|
|
9
|
-
Object.keys(
|
|
8
|
+
if (iconSynonyms && process.env.NODE_ENV !== "production") {
|
|
9
|
+
Object.keys(iconSynonyms).forEach((icon: string) => {
|
|
10
10
|
if (!iconKeys.includes(icon)) {
|
|
11
|
-
console.warn(`The icon ${icon} no longer exists. Remove it from \`
|
|
11
|
+
console.warn(`The icon ${icon} no longer exists. Remove it from \`iconSynonyms\``);
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
}
|
|
@@ -48,7 +48,7 @@ export function SearchIconsView({
|
|
|
48
48
|
return (
|
|
49
49
|
<>
|
|
50
50
|
<SearchBar
|
|
51
|
-
autoFocus
|
|
51
|
+
autoFocus={false}
|
|
52
52
|
innerClassName={"w-full sticky top-0 z-10"}
|
|
53
53
|
onTextSearch={(value?: string) => setQuery(value ?? "")}
|
|
54
54
|
placeholder="Search for more icons…"
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "../../types";
|
|
11
11
|
import { CellRendererParams, VirtualTable, VirtualTableColumn } from "../VirtualTable";
|
|
12
12
|
import { enumToObjectEntries } from "../../util";
|
|
13
|
-
import { OnCellValueChange, OnColumnResizeParams } from "../common
|
|
13
|
+
import { OnCellValueChange, OnColumnResizeParams } from "../common";
|
|
14
14
|
import { FilterFormFieldProps } from "../VirtualTable/VirtualTableHeader";
|
|
15
15
|
import { ReferenceFilterField } from "./filters/ReferenceFilterField";
|
|
16
16
|
import { StringNumberFilterField } from "./filters/StringNumberFilterField";
|
|
@@ -18,6 +18,7 @@ import { BooleanFilterField } from "./filters/BooleanFilterField";
|
|
|
18
18
|
import { DateTimeFilterField } from "./filters/DateTimeFilterField";
|
|
19
19
|
import { useOutsideAlerter } from "@firecms/ui";
|
|
20
20
|
import { SelectableTableContext } from "./SelectableTableContext";
|
|
21
|
+
import { getRowHeight } from "../common/table_height";
|
|
21
22
|
|
|
22
23
|
export type SelectableTableProps<M extends Record<string, any>> = {
|
|
23
24
|
|
|
@@ -115,7 +116,7 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
|
|
|
115
116
|
onEntityClick,
|
|
116
117
|
onColumnResize,
|
|
117
118
|
hoverRow = true,
|
|
118
|
-
size,
|
|
119
|
+
size = "m",
|
|
119
120
|
inlineEditing = false,
|
|
120
121
|
tableController:
|
|
121
122
|
{
|
|
@@ -224,7 +225,7 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
|
|
|
224
225
|
error={dataLoadingError}
|
|
225
226
|
paginationEnabled={paginationEnabled}
|
|
226
227
|
onColumnResize={onColumnResize}
|
|
227
|
-
|
|
228
|
+
rowHeight={getRowHeight(size)}
|
|
228
229
|
loading={dataLoading}
|
|
229
230
|
filter={filterValues}
|
|
230
231
|
onFilterUpdate={setFilterValues ? onFilterUpdate : undefined}
|
|
@@ -280,6 +281,7 @@ function createFilterField({
|
|
|
280
281
|
isArray={isArray}
|
|
281
282
|
path={baseProperty.path}
|
|
282
283
|
title={resolvedProperty?.name}
|
|
284
|
+
includeId={baseProperty.includeId}
|
|
283
285
|
previewProperties={baseProperty?.previewProperties}
|
|
284
286
|
hidden={hidden}
|
|
285
287
|
setHidden={setHidden}/>;
|
|
@@ -32,7 +32,7 @@ export function BooleanFilterField({
|
|
|
32
32
|
const valueSet = !!value;
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
|
-
<div className="w-[
|
|
35
|
+
<div className="w-[300px]">
|
|
36
36
|
<BooleanSwitchWithLabel
|
|
37
37
|
value={valueSetToTrue}
|
|
38
38
|
allowIndeterminate={true}
|
|
@@ -44,6 +44,5 @@ export function BooleanFilterField({
|
|
|
44
44
|
: `${title} is false`}
|
|
45
45
|
/>
|
|
46
46
|
</div>
|
|
47
|
-
)
|
|
48
|
-
;
|
|
47
|
+
);
|
|
49
48
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { VirtualTableWhereFilterOp } from "../../VirtualTable";
|
|
3
|
-
import { DateTimeField, Select, SelectItem } from "@firecms/ui";
|
|
3
|
+
import { Checkbox, DateTimeField, Label, Select, SelectItem } from "@firecms/ui";
|
|
4
4
|
import { useCustomizationController } from "../../../hooks";
|
|
5
5
|
|
|
6
6
|
interface DateTimeFilterFieldProps {
|
|
@@ -43,10 +43,10 @@ export function DateTimeFilterField({
|
|
|
43
43
|
|
|
44
44
|
const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
|
|
45
45
|
const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
|
|
46
|
-
const [internalValue, setInternalValue] = useState<Date | undefined>(fieldValue);
|
|
46
|
+
const [internalValue, setInternalValue] = useState<Date | null | undefined>(fieldValue);
|
|
47
47
|
|
|
48
|
-
function updateFilter(op: VirtualTableWhereFilterOp, val: Date | undefined) {
|
|
49
|
-
let newValue: Date | undefined = val;
|
|
48
|
+
function updateFilter(op: VirtualTableWhereFilterOp, val: Date | undefined | null) {
|
|
49
|
+
let newValue: Date | null | undefined = val;
|
|
50
50
|
const prevOpIsArray = multipleSelectOperations.includes(operation);
|
|
51
51
|
const newOpIsArray = multipleSelectOperations.includes(op);
|
|
52
52
|
if (prevOpIsArray !== newOpIsArray) {
|
|
@@ -73,7 +73,7 @@ export function DateTimeFilterField({
|
|
|
73
73
|
|
|
74
74
|
return (
|
|
75
75
|
|
|
76
|
-
<div className="flex w-[440px]
|
|
76
|
+
<div className="flex w-[440px]">
|
|
77
77
|
<div className="w-[80px]">
|
|
78
78
|
<Select value={operation}
|
|
79
79
|
onValueChange={(value) => {
|
|
@@ -88,19 +88,34 @@ export function DateTimeFilterField({
|
|
|
88
88
|
</Select>
|
|
89
89
|
</div>
|
|
90
90
|
|
|
91
|
-
<div className="flex-grow ml-2">
|
|
91
|
+
<div className="flex-grow ml-2 flex flex-col gap-2">
|
|
92
92
|
|
|
93
93
|
<DateTimeField
|
|
94
94
|
mode={mode}
|
|
95
95
|
size={"medium"}
|
|
96
96
|
locale={locale}
|
|
97
|
-
value={internalValue}
|
|
97
|
+
value={internalValue ?? undefined}
|
|
98
98
|
onChange={(dateValue: Date | undefined) => {
|
|
99
99
|
updateFilter(operation, dateValue === null ? undefined : dateValue);
|
|
100
100
|
}}
|
|
101
101
|
clearable={true}
|
|
102
102
|
/>
|
|
103
103
|
|
|
104
|
+
<Label
|
|
105
|
+
className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
|
|
106
|
+
htmlFor="null-filter"
|
|
107
|
+
>
|
|
108
|
+
<Checkbox id="null-filter"
|
|
109
|
+
checked={internalValue === null}
|
|
110
|
+
size={"small"}
|
|
111
|
+
onCheckedChange={(checked) => {
|
|
112
|
+
if (internalValue !== null)
|
|
113
|
+
updateFilter(operation, null);
|
|
114
|
+
else updateFilter(operation, undefined);
|
|
115
|
+
}}/>
|
|
116
|
+
Filter for null values
|
|
117
|
+
</Label>
|
|
118
|
+
|
|
104
119
|
</div>
|
|
105
120
|
|
|
106
121
|
</div>
|
|
@@ -4,7 +4,7 @@ import { Entity, EntityCollection, EntityReference } from "../../../types";
|
|
|
4
4
|
import { ReferencePreview } from "../../../preview";
|
|
5
5
|
import { getReferenceFrom } from "../../../util";
|
|
6
6
|
import { useNavigationController, useReferenceDialog } from "../../../hooks";
|
|
7
|
-
import { Button, Select, SelectItem } from "@firecms/ui";
|
|
7
|
+
import { Button, Checkbox, Label, Select, SelectItem } from "@firecms/ui";
|
|
8
8
|
|
|
9
9
|
interface ReferenceFilterFieldProps {
|
|
10
10
|
name: string,
|
|
@@ -13,6 +13,7 @@ interface ReferenceFilterFieldProps {
|
|
|
13
13
|
isArray?: boolean;
|
|
14
14
|
path?: string;
|
|
15
15
|
title?: string;
|
|
16
|
+
includeId?: boolean;
|
|
16
17
|
previewProperties?: string[];
|
|
17
18
|
hidden: boolean;
|
|
18
19
|
setHidden: (hidden: boolean) => void;
|
|
@@ -40,6 +41,7 @@ export function ReferenceFilterField({
|
|
|
40
41
|
isArray,
|
|
41
42
|
path,
|
|
42
43
|
title,
|
|
44
|
+
includeId = true,
|
|
43
45
|
previewProperties,
|
|
44
46
|
setHidden
|
|
45
47
|
}: ReferenceFilterFieldProps) {
|
|
@@ -54,7 +56,7 @@ export function ReferenceFilterField({
|
|
|
54
56
|
|
|
55
57
|
const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
|
|
56
58
|
const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
|
|
57
|
-
const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined>(fieldValue);
|
|
59
|
+
const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined | null>(fieldValue);
|
|
58
60
|
|
|
59
61
|
const selectedEntityIds = internalValue
|
|
60
62
|
? (Array.isArray(internalValue) ? internalValue.map((ref) => {
|
|
@@ -65,7 +67,7 @@ export function ReferenceFilterField({
|
|
|
65
67
|
}).filter(Boolean) as string[] : [internalValue.id])
|
|
66
68
|
: [];
|
|
67
69
|
|
|
68
|
-
function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[]) {
|
|
70
|
+
function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[] | null) {
|
|
69
71
|
|
|
70
72
|
const prevOpIsArray = multipleSelectOperations.includes(operation);
|
|
71
73
|
const newOpIsArray = multipleSelectOperations.includes(op);
|
|
@@ -134,7 +136,8 @@ export function ReferenceFilterField({
|
|
|
134
136
|
onClick={doOpenDialog}
|
|
135
137
|
reference={reference}
|
|
136
138
|
hover={true}
|
|
137
|
-
|
|
139
|
+
includeId={includeId}
|
|
140
|
+
includeEntityLink={false}
|
|
138
141
|
/>
|
|
139
142
|
);
|
|
140
143
|
};
|
|
@@ -142,7 +145,7 @@ export function ReferenceFilterField({
|
|
|
142
145
|
return (
|
|
143
146
|
|
|
144
147
|
<div className="flex w-[440px] flex-row">
|
|
145
|
-
<div className="w-[
|
|
148
|
+
<div className="w-[140px]">
|
|
146
149
|
<Select value={operation}
|
|
147
150
|
onValueChange={(value) => {
|
|
148
151
|
updateFilter(value as VirtualTableWhereFilterOp, internalValue);
|
|
@@ -156,21 +159,40 @@ export function ReferenceFilterField({
|
|
|
156
159
|
</Select>
|
|
157
160
|
</div>
|
|
158
161
|
|
|
159
|
-
<div className="flex-grow ml-2 h-full">
|
|
162
|
+
<div className="flex-grow ml-2 h-full gap-2 flex flex-col">
|
|
160
163
|
|
|
161
164
|
{internalValue && Array.isArray(internalValue) && <div>
|
|
162
165
|
{internalValue.map((ref, index) => buildEntry(ref))}
|
|
163
166
|
</div>}
|
|
167
|
+
|
|
164
168
|
{internalValue && !Array.isArray(internalValue) && <div>
|
|
165
169
|
{buildEntry(internalValue)}
|
|
166
170
|
</div>}
|
|
171
|
+
|
|
167
172
|
{(!internalValue || (Array.isArray(internalValue) && internalValue.length === 0)) &&
|
|
168
173
|
<Button onClick={doOpenDialog}
|
|
169
174
|
variant={"outlined"}
|
|
175
|
+
size={"large"}
|
|
170
176
|
className="h-full w-full">
|
|
171
177
|
{multiple ? "Select references" : "Select reference"}
|
|
172
178
|
</Button>
|
|
173
179
|
}
|
|
180
|
+
|
|
181
|
+
{!isArray && <Label
|
|
182
|
+
className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
|
|
183
|
+
htmlFor="null-filter"
|
|
184
|
+
>
|
|
185
|
+
<Checkbox id="null-filter"
|
|
186
|
+
checked={internalValue === null}
|
|
187
|
+
size={"small"}
|
|
188
|
+
onCheckedChange={(checked) => {
|
|
189
|
+
if (internalValue !== null)
|
|
190
|
+
updateFilter(operation, null);
|
|
191
|
+
else updateFilter(operation, undefined);
|
|
192
|
+
}}/>
|
|
193
|
+
Filter for null values
|
|
194
|
+
</Label>}
|
|
195
|
+
|
|
174
196
|
</div>
|
|
175
197
|
|
|
176
198
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { EnumValuesChip } from "../../../preview";
|
|
3
3
|
import { VirtualTableWhereFilterOp } from "../../VirtualTable";
|
|
4
|
-
import { ClearIcon, IconButton, Select, SelectItem, TextField } from "@firecms/ui";
|
|
4
|
+
import { Checkbox, ClearIcon, IconButton, Label, Select, SelectItem, TextField } from "@firecms/ui";
|
|
5
5
|
import { EnumValueConfig } from "../../../types";
|
|
6
6
|
|
|
7
7
|
interface StringNumberFilterFieldProps {
|
|
@@ -50,15 +50,15 @@ export function StringNumberFilterField({
|
|
|
50
50
|
|
|
51
51
|
const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
|
|
52
52
|
const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
|
|
53
|
-
const [internalValue, setInternalValue] = useState<string | number | string[] | number[] | undefined>(fieldValue);
|
|
53
|
+
const [internalValue, setInternalValue] = useState<string | number | string[] | number[] | null | undefined>(fieldValue);
|
|
54
54
|
|
|
55
|
-
function updateFilter(op: VirtualTableWhereFilterOp, val: string | number | string[] | number[] | undefined) {
|
|
55
|
+
function updateFilter(op: VirtualTableWhereFilterOp, val: string | number | string[] | number[] | null | undefined) {
|
|
56
56
|
let newValue = val;
|
|
57
57
|
const prevOpIsArray = multipleSelectOperations.includes(operation);
|
|
58
58
|
const newOpIsArray = multipleSelectOperations.includes(op);
|
|
59
59
|
if (prevOpIsArray !== newOpIsArray) {
|
|
60
60
|
// @ts-ignore
|
|
61
|
-
newValue = newOpIsArray ? (typeof val === "string" || typeof val === "number" ? [val] : []) :
|
|
61
|
+
newValue = newOpIsArray ? (typeof val === "string" || typeof val === "number" ? [val] : []) : undefined;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (typeof newValue === "number" && isNaN(newValue))
|
|
@@ -84,7 +84,7 @@ export function StringNumberFilterField({
|
|
|
84
84
|
const multiple = multipleSelectOperations.includes(operation);
|
|
85
85
|
return (
|
|
86
86
|
|
|
87
|
-
<div className="flex w-[440px]
|
|
87
|
+
<div className="flex w-[440px]">
|
|
88
88
|
<div className={"w-[80px]"}>
|
|
89
89
|
<Select value={operation}
|
|
90
90
|
position={"item-aligned"}
|
|
@@ -100,11 +100,11 @@ export function StringNumberFilterField({
|
|
|
100
100
|
</Select>
|
|
101
101
|
</div>
|
|
102
102
|
|
|
103
|
-
<div className="flex-grow ml-2">
|
|
103
|
+
<div className="flex-grow ml-2 flex flex-col gap-2">
|
|
104
104
|
|
|
105
105
|
{!enumValues && <TextField
|
|
106
106
|
type={dataType === "number" ? "number" : undefined}
|
|
107
|
-
value={internalValue !== undefined ? String(internalValue) : ""}
|
|
107
|
+
value={internalValue !== undefined && internalValue != null ? String(internalValue) : ""}
|
|
108
108
|
onChange={(evt) => {
|
|
109
109
|
const val = dataType === "number"
|
|
110
110
|
? parseFloat(evt.target.value)
|
|
@@ -118,26 +118,31 @@ export function StringNumberFilterField({
|
|
|
118
118
|
/>}
|
|
119
119
|
|
|
120
120
|
{enumValues &&
|
|
121
|
-
|
|
122
121
|
<Select
|
|
123
122
|
position={"item-aligned"}
|
|
124
123
|
value={internalValue !== undefined
|
|
125
124
|
? (Array.isArray(internalValue) ? internalValue.map(e => String(e)) : String(internalValue))
|
|
126
125
|
: isArray ? [] : ""}
|
|
127
126
|
onValueChange={(value) => {
|
|
128
|
-
|
|
127
|
+
if (value !== "")
|
|
128
|
+
updateFilter(operation, dataType === "number" ? parseInt(value as string) : value as string)
|
|
129
129
|
}}
|
|
130
130
|
multiple={multiple}
|
|
131
131
|
endAdornment={internalValue && <IconButton
|
|
132
|
-
className="absolute right-
|
|
132
|
+
className="absolute right-2 top-3"
|
|
133
133
|
onClick={(e) => updateFilter(operation, undefined)}>
|
|
134
134
|
<ClearIcon/>
|
|
135
135
|
</IconButton>}
|
|
136
|
-
renderValue={(enumKey) =>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
renderValue={(enumKey) => {
|
|
137
|
+
if (enumKey === null)
|
|
138
|
+
return "Filter for null values";
|
|
139
|
+
|
|
140
|
+
return <EnumValuesChip
|
|
141
|
+
key={`select_value_${name}_${enumKey}`}
|
|
142
|
+
enumKey={enumKey}
|
|
143
|
+
enumValues={enumValues}
|
|
144
|
+
size={"small"}/>;
|
|
145
|
+
}}>
|
|
141
146
|
{enumValues.map((enumConfig) => (
|
|
142
147
|
<SelectItem key={`select_value_${name}_${enumConfig.id}`}
|
|
143
148
|
value={String(enumConfig.id)}>
|
|
@@ -150,6 +155,21 @@ export function StringNumberFilterField({
|
|
|
150
155
|
</Select>
|
|
151
156
|
}
|
|
152
157
|
|
|
158
|
+
{!isArray && <Label
|
|
159
|
+
className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
|
|
160
|
+
htmlFor="null-filter"
|
|
161
|
+
>
|
|
162
|
+
<Checkbox id="null-filter"
|
|
163
|
+
checked={internalValue === null}
|
|
164
|
+
size={"small"}
|
|
165
|
+
onCheckedChange={(checked) => {
|
|
166
|
+
if (internalValue !== null)
|
|
167
|
+
updateFilter(operation, null);
|
|
168
|
+
else updateFilter(operation, undefined);
|
|
169
|
+
}}/>
|
|
170
|
+
Filter for null values
|
|
171
|
+
</Label>}
|
|
172
|
+
|
|
153
173
|
</div>
|
|
154
174
|
|
|
155
175
|
</div>
|
|
@@ -15,12 +15,11 @@ import {
|
|
|
15
15
|
VirtualTableWhereFilterOp
|
|
16
16
|
} from "./VirtualTableProps";
|
|
17
17
|
|
|
18
|
-
import { getRowHeight } from "./common";
|
|
19
18
|
import { VirtualTableContextProps } from "./types";
|
|
20
19
|
import { VirtualTableHeaderRow } from "./VirtualTableHeaderRow";
|
|
21
20
|
import { VirtualTableRow } from "./VirtualTableRow";
|
|
22
21
|
import { VirtualTableCell } from "./VirtualTableCell";
|
|
23
|
-
import { AssignmentIcon,
|
|
22
|
+
import { AssignmentIcon, CenteredView, cls, Typography } from "@firecms/ui";
|
|
24
23
|
|
|
25
24
|
const VirtualListContext = createContext<VirtualTableContextProps<any>>({} as any);
|
|
26
25
|
VirtualListContext.displayName = "VirtualListContext";
|
|
@@ -92,7 +91,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
92
91
|
data,
|
|
93
92
|
onResetPagination,
|
|
94
93
|
onEndReached,
|
|
95
|
-
|
|
94
|
+
rowHeight = 54,
|
|
96
95
|
columns: columnsProp,
|
|
97
96
|
onRowClick,
|
|
98
97
|
onColumnResize,
|
|
@@ -187,7 +186,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
187
186
|
onSortByUpdate(undefined);
|
|
188
187
|
}, [onSortByUpdate]);
|
|
189
188
|
|
|
190
|
-
const maxScroll = Math.max((data?.length ?? 0) *
|
|
189
|
+
const maxScroll = Math.max((data?.length ?? 0) * rowHeight - bounds.height, 0);
|
|
191
190
|
const onEndReachedInternal = useCallback((scrollOffset: number) => {
|
|
192
191
|
if (onEndReached && (data?.length ?? 0) > 0 && scrollOffset > endReachCallbackThreshold.current + 600) {
|
|
193
192
|
endReachCallbackThreshold.current = scrollOffset;
|
|
@@ -225,24 +224,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
225
224
|
}
|
|
226
225
|
|
|
227
226
|
if (onFilterUpdate) onFilterUpdate(newFilterValue);
|
|
228
|
-
|
|
229
|
-
if (column.key !== sortByProperty) {
|
|
230
|
-
resetSort();
|
|
231
|
-
}
|
|
232
|
-
}, [checkFilterCombination, currentSort, onFilterUpdate, resetSort, sortByProperty]);
|
|
233
|
-
|
|
234
|
-
const buildErrorView = useCallback(() => (
|
|
235
|
-
<div
|
|
236
|
-
className="h-full flex flex-col items-center justify-center sticky left-0">
|
|
237
|
-
|
|
238
|
-
<Typography variant={"h6"}>
|
|
239
|
-
{"Error fetching data from the data source"}
|
|
240
|
-
</Typography>
|
|
241
|
-
|
|
242
|
-
{error?.message && <Markdown className={"px-4 break-all"} source={error.message}/>}
|
|
243
|
-
|
|
244
|
-
</div>
|
|
245
|
-
), [error?.message]);
|
|
227
|
+
}, [checkFilterCombination, currentSort, onFilterUpdate, sortByProperty]);
|
|
246
228
|
|
|
247
229
|
const buildEmptyView = useCallback(() => {
|
|
248
230
|
if (loading)
|
|
@@ -255,11 +237,22 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
255
237
|
}, [emptyComponent, loading]);
|
|
256
238
|
|
|
257
239
|
const empty = !loading && (data?.length ?? 0) === 0;
|
|
258
|
-
const customView = error
|
|
240
|
+
const customView = error
|
|
241
|
+
? <CenteredView maxWidth={"2xl"}
|
|
242
|
+
className="flex flex-col gap-2">
|
|
243
|
+
|
|
244
|
+
<Typography variant={"h6"}>
|
|
245
|
+
{"Error fetching data from the data source"}
|
|
246
|
+
</Typography>
|
|
247
|
+
|
|
248
|
+
{error?.message && <SafeLinkRenderer text={error.message}/>}
|
|
249
|
+
|
|
250
|
+
</CenteredView>
|
|
251
|
+
: (empty ? buildEmptyView() : undefined);
|
|
259
252
|
|
|
260
253
|
const virtualListController = {
|
|
261
254
|
data,
|
|
262
|
-
|
|
255
|
+
rowHeight: rowHeight,
|
|
263
256
|
cellRenderer,
|
|
264
257
|
columns,
|
|
265
258
|
currentSort,
|
|
@@ -282,19 +275,19 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
282
275
|
return (
|
|
283
276
|
<div
|
|
284
277
|
ref={measureRef}
|
|
285
|
-
className={
|
|
278
|
+
className={cls("h-full w-full", className)}>
|
|
286
279
|
<VirtualListContext.Provider
|
|
287
280
|
value={virtualListController}>
|
|
288
281
|
|
|
289
282
|
<MemoizedList
|
|
290
283
|
outerRef={tableRef}
|
|
291
|
-
key={
|
|
284
|
+
key={rowHeight}
|
|
292
285
|
width={bounds.width}
|
|
293
286
|
height={bounds.height}
|
|
294
287
|
itemCount={(data?.length ?? 0) + (endAdornment ? 1 : 0)}
|
|
295
288
|
onScroll={onScroll}
|
|
296
289
|
includeAddColumn={Boolean(AddColumnComponent)}
|
|
297
|
-
itemSize={
|
|
290
|
+
itemSize={rowHeight}/>
|
|
298
291
|
|
|
299
292
|
</VirtualListContext.Provider>
|
|
300
293
|
</div>
|
|
@@ -334,7 +327,7 @@ function MemoizedList({
|
|
|
334
327
|
onRowClick,
|
|
335
328
|
data,
|
|
336
329
|
columns,
|
|
337
|
-
|
|
330
|
+
rowHeight = 54,
|
|
338
331
|
cellRenderer,
|
|
339
332
|
hoverRow,
|
|
340
333
|
rowClassName,
|
|
@@ -367,7 +360,7 @@ function MemoizedList({
|
|
|
367
360
|
...style,
|
|
368
361
|
top: `calc(${style.top}px + 48px)`
|
|
369
362
|
}}
|
|
370
|
-
|
|
363
|
+
rowHeight={rowHeight}>
|
|
371
364
|
|
|
372
365
|
{columns.map((column: VirtualTableColumn, columnIndex: number) => {
|
|
373
366
|
const cellData = rowData && rowData[column.key];
|
|
@@ -403,3 +396,17 @@ function MemoizedList({
|
|
|
403
396
|
{Row}
|
|
404
397
|
</List>;
|
|
405
398
|
}
|
|
399
|
+
|
|
400
|
+
const SafeLinkRenderer: React.FC<{
|
|
401
|
+
text: string;
|
|
402
|
+
}> = ({ text }) => {
|
|
403
|
+
const urlRegex = /https?:\/\/[^\s]+/g;
|
|
404
|
+
const htmlContent = text.replace(urlRegex, (url) => {
|
|
405
|
+
// For each URL found, replace it with an HTML <a> tag
|
|
406
|
+
return `<a href="${url}" class="underline" target="_blank">Link</a><br/>`;
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
return (
|
|
410
|
+
<div className={"break-all"} dangerouslySetInnerHTML={{ __html: htmlContent }}/>
|
|
411
|
+
);
|
|
412
|
+
};
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ArrowUpwardIcon,
|
|
8
8
|
Badge,
|
|
9
9
|
Button,
|
|
10
|
-
|
|
10
|
+
cls,
|
|
11
11
|
defaultBorderMixin,
|
|
12
12
|
FilterListIcon,
|
|
13
13
|
IconButton,
|
|
@@ -85,7 +85,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
|
|
|
85
85
|
return (
|
|
86
86
|
<ErrorBoundary>
|
|
87
87
|
<div
|
|
88
|
-
className={
|
|
88
|
+
className={cls("flex py-0 px-3 h-full text-xs uppercase font-semibold relative select-none items-center bg-gray-50 dark:bg-gray-900",
|
|
89
89
|
"text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200 ",
|
|
90
90
|
"hover:bg-gray-100 dark:hover:bg-gray-800 hover:bg-opacity-50 dark:hover:bg-opacity-50",
|
|
91
91
|
column.frozen ? "sticky left-0 z-10" : "relative z-0"
|
|
@@ -179,7 +179,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
|
|
|
179
179
|
|
|
180
180
|
{column.resizable && <div
|
|
181
181
|
ref={resizeHandleRef}
|
|
182
|
-
className={
|
|
182
|
+
className={cls(
|
|
183
183
|
"absolute h-full w-[6px] top-0 right-0 cursor-col-resize",
|
|
184
184
|
hovered && "bg-gray-300 dark:bg-gray-700"
|
|
185
185
|
)}
|
|
@@ -240,7 +240,7 @@ function FilterForm<M>({
|
|
|
240
240
|
}}
|
|
241
241
|
className={"text-gray-900 dark:text-white"}>
|
|
242
242
|
<div
|
|
243
|
-
className={
|
|
243
|
+
className={cls(defaultBorderMixin, "py-4 px-6 text-xs font-semibold uppercase border-b")}>
|
|
244
244
|
{column.title ?? id}
|
|
245
245
|
</div>
|
|
246
246
|
{filterField && <div className="m-4">
|
|
@@ -4,7 +4,7 @@ import { VirtualTableColumn, VirtualTableWhereFilterOp } from "./VirtualTablePro
|
|
|
4
4
|
import { ErrorBoundary } from "../ErrorBoundary";
|
|
5
5
|
import { VirtualTableHeader } from "./VirtualTableHeader";
|
|
6
6
|
import { VirtualTableContextProps } from "./types";
|
|
7
|
-
import {
|
|
7
|
+
import { cls, defaultBorderMixin } from "@firecms/ui";
|
|
8
8
|
|
|
9
9
|
export const VirtualTableHeaderRow = ({
|
|
10
10
|
columns,
|
|
@@ -100,7 +100,7 @@ export const VirtualTableHeaderRow = ({
|
|
|
100
100
|
|
|
101
101
|
return (
|
|
102
102
|
<div
|
|
103
|
-
className={
|
|
103
|
+
className={cls(defaultBorderMixin, "z-20 sticky min-w-full flex w-fit flex-row top-0 left-0 h-12 border-b bg-gray-50 dark:bg-gray-900")}>
|
|
104
104
|
{columns.map((c, columnIndex) => {
|
|
105
105
|
const column = columns[columnIndex];
|
|
106
106
|
|