@nocobase/client-v2 2.1.0-alpha.40 → 2.1.0-alpha.45
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/es/Application.d.ts +7 -0
- package/es/BaseApplication.d.ts +13 -0
- package/es/RouterManager.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterface.d.ts +51 -15
- package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +82 -3
- package/es/collection-manager/field-configure.d.ts +80 -0
- package/es/collection-manager/field-validation.d.ts +43 -0
- package/es/collection-manager/filter-operators/index.d.ts +46 -0
- package/es/collection-manager/filter-operators/operators.d.ts +30 -0
- package/es/collection-manager/interfaces/checkbox.d.ts +1 -41
- package/es/collection-manager/interfaces/checkboxGroup.d.ts +12 -44
- package/es/collection-manager/interfaces/collection.d.ts +12 -51
- package/es/collection-manager/interfaces/color.d.ts +1 -16
- package/es/collection-manager/interfaces/createdAt.d.ts +1 -44
- package/es/collection-manager/interfaces/createdBy.d.ts +0 -4
- package/es/collection-manager/interfaces/dateOnly.d.ts +7 -44
- package/es/collection-manager/interfaces/datetime.d.ts +1 -44
- package/es/collection-manager/interfaces/datetimeNoTz.d.ts +1 -44
- package/es/collection-manager/interfaces/email.d.ts +1 -29
- package/es/collection-manager/interfaces/id.d.ts +1 -16
- package/es/collection-manager/interfaces/index.d.ts +2 -3
- package/es/collection-manager/interfaces/input.d.ts +1 -102
- package/es/collection-manager/interfaces/integer.d.ts +1 -95
- package/es/collection-manager/interfaces/json.d.ts +16 -7
- package/es/collection-manager/interfaces/m2m.d.ts +11 -19
- package/es/collection-manager/interfaces/m2o.d.ts +11 -19
- package/es/collection-manager/interfaces/markdown.d.ts +1 -63
- package/es/collection-manager/interfaces/multipleSelect.d.ts +12 -44
- package/es/collection-manager/interfaces/nanoid.d.ts +1 -34
- package/es/collection-manager/interfaces/number.d.ts +1 -87
- package/es/collection-manager/interfaces/o2m.d.ts +12 -24
- package/es/collection-manager/interfaces/obo.d.ts +207 -0
- package/es/collection-manager/interfaces/oho.d.ts +207 -0
- package/es/collection-manager/interfaces/password.d.ts +1 -56
- package/es/collection-manager/interfaces/percent.d.ts +1 -84
- package/es/collection-manager/interfaces/phone.d.ts +1 -25
- package/es/collection-manager/interfaces/properties/index.d.ts +0 -28
- package/es/collection-manager/interfaces/radioGroup.d.ts +1 -29
- package/es/collection-manager/interfaces/richText.d.ts +1 -63
- package/es/collection-manager/interfaces/select.d.ts +12 -44
- package/es/collection-manager/interfaces/snowflake-id.d.ts +1 -34
- package/es/collection-manager/interfaces/tableoid.d.ts +1 -10
- package/es/collection-manager/interfaces/textarea.d.ts +1 -51
- package/es/collection-manager/interfaces/time.d.ts +1 -16
- package/es/collection-manager/interfaces/types.d.ts +3 -12
- package/es/collection-manager/interfaces/unixTimestamp.d.ts +1 -44
- package/es/collection-manager/interfaces/updatedAt.d.ts +1 -44
- package/es/collection-manager/interfaces/updatedBy.d.ts +0 -4
- package/es/collection-manager/interfaces/url.d.ts +1 -20
- package/es/collection-manager/interfaces/uuid.d.ts +1 -34
- package/es/collection-manager/template-fields.d.ts +53 -0
- package/es/components/KeepAlive.d.ts +22 -0
- package/es/components/RouterBridge.d.ts +9 -0
- package/es/components/form/DialogFormLayout.d.ts +5 -29
- package/es/components/form/VariableInput.d.ts +53 -2
- package/es/components/form/filter/CollectionFilter.d.ts +49 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +49 -0
- package/es/components/form/filter/DateFilterDynamicComponent.d.ts +57 -0
- package/es/components/form/filter/FilterValueInput.d.ts +29 -0
- package/es/components/form/filter/index.d.ts +11 -0
- package/es/components/form/filter/useFilterActionProps.d.ts +96 -0
- package/es/components/form/index.d.ts +1 -0
- package/es/data-source/ExtendCollectionsProvider.d.ts +50 -0
- package/es/data-source/index.d.ts +9 -0
- package/es/flow/FlowPage.d.ts +2 -1
- package/es/flow/admin-shell/AdminLayoutRouteCoordinator.d.ts +8 -40
- package/es/flow/admin-shell/BaseLayoutModel.d.ts +89 -0
- package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +74 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +12 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutModel.d.ts +7 -92
- package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +11 -0
- package/es/flow/admin-shell/admin-layout/index.d.ts +3 -0
- package/es/flow/admin-shell/admin-layout/useApplications.d.ts +3 -2
- package/es/flow/admin-shell/useAdminLayoutRoutePage.d.ts +2 -2
- package/es/flow/admin-shell/useLayoutRoutePage.d.ts +23 -0
- package/es/flow/components/FlowRoute.d.ts +10 -1
- package/es/flow/components/filter/index.d.ts +2 -0
- package/es/flow/components/filter/useFilterOptions.d.ts +66 -0
- package/es/flow/index.d.ts +4 -0
- package/es/flow/models/base/PageModel/PageModel.d.ts +3 -1
- package/es/flow/models/blocks/assign-form/assignFieldValuesFlow.d.ts +84 -0
- package/es/flow/models/blocks/assign-form/index.d.ts +1 -0
- package/es/flow/models/blocks/form/FormActionGroupModel.d.ts +1 -0
- package/es/flow/models/blocks/form/FormActionModel.d.ts +9 -2
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +10 -0
- package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.d.ts +1 -1
- package/es/flow-compat/passwordUtils.d.ts +1 -1
- package/es/index.d.ts +6 -0
- package/es/index.mjs +552 -459
- package/es/layout-manager/LayoutContentRoute.d.ts +14 -0
- package/es/layout-manager/LayoutManager.d.ts +22 -0
- package/es/layout-manager/LayoutRoute.d.ts +14 -0
- package/es/layout-manager/index.d.ts +13 -0
- package/es/layout-manager/types.d.ts +20 -0
- package/es/layout-manager/utils.d.ts +14 -0
- package/es/nocobase-buildin-plugin/index.d.ts +3 -10
- package/es/settings-center/index.d.ts +1 -1
- package/es/settings-center/plugin-manager/BulkEnableButton.d.ts +15 -0
- package/es/settings-center/plugin-manager/PluginCard.d.ts +15 -0
- package/es/settings-center/plugin-manager/PluginDetail.d.ts +16 -0
- package/es/settings-center/{PluginManagerPage.d.ts → plugin-manager/index.d.ts} +1 -7
- package/es/settings-center/plugin-manager/types.d.ts +34 -0
- package/lib/index.js +552 -459
- package/package.json +8 -7
- package/src/Application.tsx +51 -12
- package/src/BaseApplication.tsx +32 -0
- package/src/PluginSettingsManager.ts +1 -1
- package/src/RouterManager.tsx +17 -1
- package/src/__tests__/PluginSettingsManager.test.ts +41 -2
- package/src/__tests__/app.test.tsx +17 -1
- package/src/__tests__/globalDeps.test.ts +1 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +45 -2
- package/src/__tests__/plugin-manager.test.tsx +177 -0
- package/src/__tests__/settings-center.test.tsx +24 -2
- package/src/collection-field-interface/CollectionFieldInterface.ts +71 -77
- package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +201 -4
- package/src/collection-manager/field-configure.ts +548 -0
- package/src/collection-manager/field-validation.ts +195 -0
- package/src/collection-manager/filter-operators/index.ts +176 -0
- package/src/collection-manager/{interfaces/properties → filter-operators}/operators.ts +24 -13
- package/src/collection-manager/interfaces/checkbox.ts +2 -9
- package/src/collection-manager/interfaces/checkboxGroup.ts +2 -10
- package/src/collection-manager/interfaces/collection.ts +2 -15
- package/src/collection-manager/interfaces/color.ts +2 -2
- package/src/collection-manager/interfaces/createdAt.ts +2 -2
- package/src/collection-manager/interfaces/createdBy.ts +1 -12
- package/src/collection-manager/interfaces/dateOnly.ts +8 -2
- package/src/collection-manager/interfaces/datetime.ts +2 -2
- package/src/collection-manager/interfaces/datetimeNoTz.ts +2 -2
- package/src/collection-manager/interfaces/email.ts +2 -9
- package/src/collection-manager/interfaces/id.ts +1 -2
- package/src/collection-manager/interfaces/index.ts +2 -3
- package/src/collection-manager/interfaces/input.ts +2 -133
- package/src/collection-manager/interfaces/integer.ts +2 -71
- package/src/collection-manager/interfaces/json.tsx +17 -11
- package/src/collection-manager/interfaces/m2m.tsx +0 -21
- package/src/collection-manager/interfaces/m2o.tsx +0 -22
- package/src/collection-manager/interfaces/markdown.ts +2 -51
- package/src/collection-manager/interfaces/multipleSelect.ts +2 -14
- package/src/collection-manager/interfaces/nanoid.ts +2 -2
- package/src/collection-manager/interfaces/number.ts +2 -85
- package/src/collection-manager/interfaces/o2m.tsx +1 -22
- package/src/collection-manager/interfaces/obo.tsx +145 -0
- package/src/collection-manager/interfaces/oho.tsx +145 -0
- package/src/collection-manager/interfaces/password.ts +2 -44
- package/src/collection-manager/interfaces/percent.ts +2 -74
- package/src/collection-manager/interfaces/phone.ts +2 -2
- package/src/collection-manager/interfaces/properties/index.ts +0 -133
- package/src/collection-manager/interfaces/radioGroup.ts +2 -2
- package/src/collection-manager/interfaces/richText.ts +2 -51
- package/src/collection-manager/interfaces/select.ts +2 -14
- package/src/collection-manager/interfaces/snowflake-id.ts +2 -2
- package/src/collection-manager/interfaces/tableoid.ts +1 -2
- package/src/collection-manager/interfaces/textarea.ts +2 -51
- package/src/collection-manager/interfaces/time.ts +2 -2
- package/src/collection-manager/interfaces/types.ts +4 -12
- package/src/collection-manager/interfaces/unixTimestamp.tsx +2 -2
- package/src/collection-manager/interfaces/updatedAt.ts +2 -2
- package/src/collection-manager/interfaces/updatedBy.ts +1 -12
- package/src/collection-manager/interfaces/url.ts +2 -4
- package/src/collection-manager/interfaces/uuid.ts +2 -2
- package/src/collection-manager/template-fields.ts +109 -0
- package/src/components/KeepAlive.tsx +131 -0
- package/src/components/README.md +90 -6
- package/src/components/README.zh-CN.md +90 -7
- package/src/components/RouterBridge.tsx +28 -4
- package/src/components/__tests__/KeepAlive.test.tsx +63 -0
- package/src/components/__tests__/RouterBridge.test.tsx +27 -0
- package/src/components/form/DialogFormLayout.tsx +5 -29
- package/src/components/form/VariableInput.tsx +101 -28
- package/src/components/form/__tests__/VariableInput.test.ts +85 -0
- package/src/components/form/filter/CollectionFilter.tsx +111 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +184 -0
- package/src/components/form/filter/DateFilterDynamicComponent.tsx +283 -0
- package/src/components/form/filter/FilterValueInput.tsx +198 -0
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +247 -0
- package/src/components/form/filter/__tests__/DateFilterDynamicComponent.test.tsx +148 -0
- package/src/components/form/filter/__tests__/FilterValueInput.test.tsx +243 -0
- package/src/components/form/filter/__tests__/compileFilterGroup.test.ts +146 -0
- package/src/components/form/filter/index.ts +13 -0
- package/src/components/form/filter/useFilterActionProps.ts +203 -0
- package/src/components/form/index.tsx +1 -0
- package/src/data-source/ExtendCollectionsProvider.tsx +144 -0
- package/src/data-source/__tests__/ExtendCollectionsProvider.test.tsx +264 -0
- package/src/data-source/index.ts +10 -0
- package/src/flow/FlowPage.tsx +35 -7
- package/src/flow/__tests__/FlowPage.test.tsx +79 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +529 -2
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +191 -0
- package/src/flow/actions/__tests__/openView.subModelKey.test.tsx +33 -0
- package/src/flow/actions/aclCheck.tsx +4 -0
- package/src/flow/actions/aclCheckRefresh.tsx +4 -0
- package/src/flow/actions/dateTimeFormat.tsx +12 -8
- package/src/flow/actions/linkageRules.tsx +122 -0
- package/src/flow/actions/openView.tsx +28 -4
- package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +11 -329
- package/src/flow/admin-shell/BaseLayoutModel.tsx +455 -0
- package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +502 -0
- package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +547 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +35 -7
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +160 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +0 -12
- package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +28 -201
- package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +11 -2
- package/src/flow/admin-shell/admin-layout/AppListRender.tsx +139 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +1 -26
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +149 -27
- package/src/flow/admin-shell/admin-layout/index.ts +3 -0
- package/src/flow/admin-shell/admin-layout/useApplications.tsx +34 -1
- package/src/flow/admin-shell/useAdminLayoutRoutePage.ts +10 -26
- package/src/flow/admin-shell/useLayoutRoutePage.ts +61 -0
- package/src/flow/components/AdminLayout.tsx +4 -154
- package/src/flow/components/FlowRoute.tsx +105 -15
- package/src/flow/components/filter/index.ts +3 -0
- package/src/flow/components/filter/useFilterOptions.ts +102 -0
- package/src/flow/index.ts +4 -0
- package/src/flow/models/actions/UpdateRecordActionModel.tsx +14 -95
- package/src/flow/models/actions/UpdateRecordActionUtils.ts +4 -7
- package/src/flow/models/actions/__tests__/AssignFormRefill.test.ts +26 -1
- package/src/flow/models/base/ActionModel.tsx +8 -1
- package/src/flow/models/base/PageModel/PageModel.tsx +51 -18
- package/src/flow/models/base/PageModel/RootPageModel.tsx +6 -13
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +102 -1
- package/src/flow/models/base/RouteModel.tsx +1 -1
- package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +63 -2
- package/src/flow/models/blocks/assign-form/assignFieldValuesFlow.tsx +206 -0
- package/src/flow/models/blocks/assign-form/index.ts +1 -0
- package/src/flow/models/blocks/form/FormActionGroupModel.tsx +14 -0
- package/src/flow/models/blocks/form/FormActionModel.tsx +30 -3
- package/src/flow/models/blocks/form/FormItemModel.tsx +8 -1
- package/src/flow/models/blocks/form/__tests__/FormActionGroupModel.test.ts +46 -0
- package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +71 -0
- package/src/flow/models/blocks/form/submitHandler.ts +8 -1
- package/src/flow/models/blocks/form/submitValues.ts +4 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +118 -16
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +114 -0
- package/src/flow/models/fields/AssociationFieldModel/SubFormFieldModel.tsx +7 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +1 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -5
- package/src/flow/models/fields/ClickableFieldModel.tsx +9 -1
- package/src/flow/models/fields/CollectionSelectorFieldModel.tsx +8 -2
- package/src/flow/models/fields/DisplayEnumFieldModel.tsx +8 -2
- package/src/flow/models/fields/DisplayTimeFieldModel.tsx +1 -1
- package/src/flow/models/fields/TimeFieldModel.tsx +1 -1
- package/src/flow/models/fields/__tests__/TimeFieldModel.test.tsx +61 -0
- package/src/flow/models/fields/mobile-components/MobileDatePicker.tsx +19 -3
- package/src/flow/models/fields/mobile-components/__tests__/MobileDatePicker.test.tsx +94 -0
- package/src/flow/models/topbar/TopbarActionModel.tsx +1 -1
- package/src/flow/utils/__tests__/dateTimeFormat.test.ts +91 -0
- package/src/index.ts +6 -0
- package/src/layout-manager/LayoutContentRoute.tsx +90 -0
- package/src/layout-manager/LayoutManager.tsx +185 -0
- package/src/layout-manager/LayoutRoute.tsx +138 -0
- package/src/layout-manager/__tests__/LayoutManager.test.tsx +335 -0
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +473 -0
- package/src/layout-manager/index.ts +14 -0
- package/src/layout-manager/types.ts +22 -0
- package/src/layout-manager/utils.ts +37 -0
- package/src/nocobase-buildin-plugin/index.tsx +69 -67
- package/src/nocobase-buildin-plugin/plugins/LocalePlugin.ts +1 -0
- package/src/settings-center/index.ts +1 -1
- package/src/settings-center/plugin-manager/BulkEnableButton.tsx +111 -0
- package/src/settings-center/plugin-manager/PluginCard.tsx +270 -0
- package/src/settings-center/plugin-manager/PluginDetail.tsx +195 -0
- package/src/settings-center/plugin-manager/index.tsx +254 -0
- package/src/settings-center/plugin-manager/types.ts +35 -0
- package/src/settings-center/utils.tsx +8 -1
- package/src/theme/__tests__/globalStyles.test.ts +24 -0
- package/src/theme/globalStyles.ts +10 -0
- package/src/utils/globalDeps.ts +2 -0
- package/es/collection-manager/interfaces/linkTo.d.ts +0 -90
- package/es/collection-manager/interfaces/o2o.d.ts +0 -621
- package/es/collection-manager/interfaces/properties/operators.d.ts +0 -294
- package/es/collection-manager/interfaces/subTable.d.ts +0 -172
- package/src/collection-manager/interfaces/linkTo.ts +0 -120
- package/src/collection-manager/interfaces/o2o.tsx +0 -561
- package/src/collection-manager/interfaces/subTable.ts +0 -218
- package/src/settings-center/PluginManagerPage.tsx +0 -162
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { DatePicker, Divider, InputNumber, Select, Space, theme } from 'antd';
|
|
11
|
+
import dayjs, { type Dayjs } from 'dayjs';
|
|
12
|
+
import React, { useMemo, useState } from 'react';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Value shape used by NocoBase's `$date*` filter operators. Two flavours:
|
|
16
|
+
*
|
|
17
|
+
* - For "Exact day" mode: a **formatted string** at the granularity of the picker — `"2026-02-15"` (date), `"2026-02"` (month), `"2026"` (year), `"2026-Q1"` (quarter). For `$dateBetween` (`isRange`), a `[string, string]` tuple. Strings (not Dayjs) so the value serializes verbatim into the query string the same way v1 does (`filter=%7B%22$and%22:[%7B%22lockedTs%22:%7B%22$dateOn%22:%222026-02%22%7D%7D]%7D`).
|
|
18
|
+
* - For relative modes (Today / Past 3 days / This Week / …): a `{ type, number?, unit? }` descriptor that the server resolves to a concrete range at query time.
|
|
19
|
+
*/
|
|
20
|
+
export type DateFilterValue =
|
|
21
|
+
| string
|
|
22
|
+
| [string, string]
|
|
23
|
+
| { type: string; number?: number; unit?: 'day' | 'week' | 'month' | 'year' }
|
|
24
|
+
| null
|
|
25
|
+
| undefined;
|
|
26
|
+
|
|
27
|
+
export interface DateFilterDynamicComponentProps {
|
|
28
|
+
value?: DateFilterValue;
|
|
29
|
+
onChange?: (value: DateFilterValue) => void;
|
|
30
|
+
/** `true` for `$dateBetween`; renders a `DatePicker.RangePicker`. */
|
|
31
|
+
isRange?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Translator. Defaults to identity so callers can omit it when running outside a translation context (tests, storybook).
|
|
34
|
+
*/
|
|
35
|
+
t?: (key: string) => string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const identity = (s: string) => s;
|
|
39
|
+
|
|
40
|
+
const PICKER_OPTIONS = [
|
|
41
|
+
{ label: 'Date', value: 'date' as const },
|
|
42
|
+
{ label: 'Month', value: 'month' as const },
|
|
43
|
+
{ label: 'Quarter', value: 'quarter' as const },
|
|
44
|
+
{ label: 'Year', value: 'year' as const },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const RELATIVE_PRIMARY = [
|
|
48
|
+
{ value: 'exact', label: 'Exact day' },
|
|
49
|
+
{ value: 'past', label: 'Past' },
|
|
50
|
+
{ value: 'next', label: 'Next' },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const RELATIVE_SECONDARY = [
|
|
54
|
+
{ value: 'today', label: 'Today' },
|
|
55
|
+
{ value: 'yesterday', label: 'Yesterday' },
|
|
56
|
+
{ value: 'tomorrow', label: 'Tomorrow' },
|
|
57
|
+
{ value: 'thisWeek', label: 'This Week' },
|
|
58
|
+
{ value: 'lastWeek', label: 'Last Week' },
|
|
59
|
+
{ value: 'nextWeek', label: 'Next Week' },
|
|
60
|
+
{ value: 'thisMonth', label: 'This Month' },
|
|
61
|
+
{ value: 'lastMonth', label: 'Last Month' },
|
|
62
|
+
{ value: 'nextMonth', label: 'Next Month' },
|
|
63
|
+
{ value: 'thisQuarter', label: 'This Quarter' },
|
|
64
|
+
{ value: 'lastQuarter', label: 'Last Quarter' },
|
|
65
|
+
{ value: 'nextQuarter', label: 'Next Quarter' },
|
|
66
|
+
{ value: 'thisYear', label: 'This Year' },
|
|
67
|
+
{ value: 'lastYear', label: 'Last Year' },
|
|
68
|
+
{ value: 'nextYear', label: 'Next Year' },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const UNIT_OPTIONS = [
|
|
72
|
+
{ value: 'day' as const, label: 'Day' },
|
|
73
|
+
{ value: 'week' as const, label: 'Calendar week' },
|
|
74
|
+
{ value: 'month' as const, label: 'Calendar Month' },
|
|
75
|
+
{ value: 'year' as const, label: 'Calendar Year' },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const isRelativeDescriptor = (value: DateFilterValue): value is { type: string; number?: number; unit?: any } =>
|
|
79
|
+
!!value && typeof value === 'object' && !Array.isArray(value) && 'type' in (value as any);
|
|
80
|
+
|
|
81
|
+
type PickerMode = 'date' | 'month' | 'quarter' | 'year';
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The exact format strings NocoBase's `$date*` server operators expect per picker granularity. Mirrors v1's `getPickerFormat` output under `underFilter=true`, so the URL-encoded query stays identical.
|
|
85
|
+
*
|
|
86
|
+
* Exported for unit tests and for downstream callers that need to parse / format `$date*` values outside this component.
|
|
87
|
+
*/
|
|
88
|
+
export const FORMAT_BY_PICKER: Record<PickerMode, string> = {
|
|
89
|
+
date: 'YYYY-MM-DD',
|
|
90
|
+
month: 'YYYY-MM',
|
|
91
|
+
quarter: 'YYYY-[Q]Q',
|
|
92
|
+
year: 'YYYY',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Parse a granularity-formatted string back into a `Dayjs`. Returns `null` on missing or unparseable input so the DatePicker's controlled-value contract stays clean.
|
|
97
|
+
*/
|
|
98
|
+
export const parseDateFilterValue = (value: string | undefined, picker: PickerMode): Dayjs | null => {
|
|
99
|
+
if (!value) return null;
|
|
100
|
+
const parsed = dayjs(value, FORMAT_BY_PICKER[picker]);
|
|
101
|
+
return parsed.isValid() ? parsed : null;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Format a `Dayjs` (typically from `DatePicker.onChange`) into the server-facing string at the active picker's granularity. Returns `undefined` for empty input so callers can drop the value entirely.
|
|
106
|
+
*/
|
|
107
|
+
export const formatDateFilterValue = (value: Dayjs | null | undefined, picker: PickerMode): string | undefined => {
|
|
108
|
+
if (!value || !dayjs.isDayjs(value)) return undefined;
|
|
109
|
+
return value.format(FORMAT_BY_PICKER[picker]);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* v2 port of v1's `DateFilterDynamicComponent` — a multi-mode value input for the `$date*` operator family. Three sub-controls glued in a `Space.Compact` row:
|
|
114
|
+
*
|
|
115
|
+
* 1. Mode select — `Exact day` / `Past` / `Next` / `Today` / `This Week` / … Picking a relative mode emits a `{ type, number?, unit? }` descriptor that the server resolves at query time; picking `Exact day` emits a raw `Dayjs` instead.
|
|
116
|
+
* 2. Picker granularity — only when mode is `Exact day`: `Date` / `Month` / `Quarter` / `Year`. Controls the antd `DatePicker`'s `picker` mode so admins can filter to e.g. "any day in 2026-03".
|
|
117
|
+
* 3. Date input — antd `DatePicker` for single dates, or `DatePicker.RangePicker` when `isRange` is true (used by `$dateBetween`).
|
|
118
|
+
*
|
|
119
|
+
* v1 wired its own `<DatePicker.FilterWithPicker>` for the third slot; v2 inlines the picker-granularity selector here so we don't have to fork antd's DatePicker. Drops v1's `@emotion/css` (uses antd token spacing) and the `useCompile` schema-template chain (call sites pass a plain `t` translator).
|
|
120
|
+
*/
|
|
121
|
+
export const DateFilterDynamicComponent: React.FC<DateFilterDynamicComponentProps> = (props) => {
|
|
122
|
+
const { value, onChange, isRange, t = identity } = props;
|
|
123
|
+
const { token } = theme.useToken();
|
|
124
|
+
const [picker, setPicker] = useState<PickerMode>('date');
|
|
125
|
+
const [open, setOpen] = useState(false);
|
|
126
|
+
|
|
127
|
+
const mode = isRelativeDescriptor(value) ? value.type : 'exact';
|
|
128
|
+
|
|
129
|
+
const primaryOptions = useMemo(() => RELATIVE_PRIMARY.map((o) => ({ ...o, label: t(o.label) })), [t]);
|
|
130
|
+
const secondaryOptions = useMemo(() => RELATIVE_SECONDARY.map((o) => ({ ...o, label: t(o.label) })), [t]);
|
|
131
|
+
const pickerOptions = useMemo(() => PICKER_OPTIONS.map((o) => ({ ...o, label: t(o.label) })), [t]);
|
|
132
|
+
const unitOptions = useMemo(() => UNIT_OPTIONS.map((o) => ({ ...o, label: t(o.label) })), [t]);
|
|
133
|
+
|
|
134
|
+
const handleSelectMode = (next: string) => {
|
|
135
|
+
setOpen(false);
|
|
136
|
+
if (next === 'exact') {
|
|
137
|
+
onChange?.(undefined);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (next === 'past' || next === 'next') {
|
|
141
|
+
onChange?.({ type: next, number: 1, unit: 'day' });
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
onChange?.({ type: next });
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const renderModeDropdown = () => (
|
|
148
|
+
<div style={{ maxHeight: 300, overflowY: 'auto' }}>
|
|
149
|
+
{primaryOptions.map((opt) => (
|
|
150
|
+
<div
|
|
151
|
+
key={opt.value}
|
|
152
|
+
role="option"
|
|
153
|
+
aria-selected={mode === opt.value}
|
|
154
|
+
onClick={() => handleSelectMode(opt.value)}
|
|
155
|
+
style={{
|
|
156
|
+
padding: `${token.paddingXXS}px ${token.paddingSM}px`,
|
|
157
|
+
cursor: 'pointer',
|
|
158
|
+
whiteSpace: 'nowrap',
|
|
159
|
+
}}
|
|
160
|
+
>
|
|
161
|
+
{opt.label}
|
|
162
|
+
</div>
|
|
163
|
+
))}
|
|
164
|
+
<Divider style={{ margin: `${token.marginXXS}px 0` }} />
|
|
165
|
+
{secondaryOptions.map((opt) => (
|
|
166
|
+
<div
|
|
167
|
+
key={opt.value}
|
|
168
|
+
role="option"
|
|
169
|
+
aria-selected={mode === opt.value}
|
|
170
|
+
onClick={() => handleSelectMode(opt.value)}
|
|
171
|
+
style={{
|
|
172
|
+
padding: `${token.paddingXXS}px ${token.paddingSM}px`,
|
|
173
|
+
cursor: 'pointer',
|
|
174
|
+
whiteSpace: 'nowrap',
|
|
175
|
+
overflow: 'hidden',
|
|
176
|
+
textOverflow: 'ellipsis',
|
|
177
|
+
}}
|
|
178
|
+
title={opt.label}
|
|
179
|
+
>
|
|
180
|
+
{opt.label}
|
|
181
|
+
</div>
|
|
182
|
+
))}
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Mode select compresses when the chosen mode is "Exact day / Past / Next" (room is needed for the secondary picker / number+unit row beside it). For named ranges (Today, This Week, etc.) it stretches since no sub-control follows.
|
|
187
|
+
const compactModes = new Set(['exact', 'past', 'next']);
|
|
188
|
+
const isCompact = compactModes.has(mode);
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<Space.Compact style={{ width: '100%' }}>
|
|
192
|
+
<Select
|
|
193
|
+
open={open}
|
|
194
|
+
onDropdownVisibleChange={setOpen}
|
|
195
|
+
value={mode}
|
|
196
|
+
onChange={handleSelectMode}
|
|
197
|
+
style={{
|
|
198
|
+
flex: '0 0 auto',
|
|
199
|
+
minWidth: 100,
|
|
200
|
+
maxWidth: isCompact ? 100 : undefined,
|
|
201
|
+
width: isCompact ? 100 : 'auto',
|
|
202
|
+
}}
|
|
203
|
+
popupMatchSelectWidth={false}
|
|
204
|
+
dropdownRender={renderModeDropdown}
|
|
205
|
+
options={[...primaryOptions, ...secondaryOptions]}
|
|
206
|
+
/>
|
|
207
|
+
|
|
208
|
+
{(mode === 'past' || mode === 'next') && (
|
|
209
|
+
<>
|
|
210
|
+
<InputNumber
|
|
211
|
+
value={isRelativeDescriptor(value) ? value.number : 1}
|
|
212
|
+
min={1}
|
|
213
|
+
onChange={(num) => {
|
|
214
|
+
if (!isRelativeDescriptor(value)) return;
|
|
215
|
+
onChange?.({ ...value, number: typeof num === 'number' ? num : 1 });
|
|
216
|
+
}}
|
|
217
|
+
style={{ flex: '0 0 auto' }}
|
|
218
|
+
/>
|
|
219
|
+
<Select
|
|
220
|
+
value={isRelativeDescriptor(value) ? value.unit : 'day'}
|
|
221
|
+
onChange={(unit) => {
|
|
222
|
+
if (!isRelativeDescriptor(value)) return;
|
|
223
|
+
onChange?.({ ...value, unit });
|
|
224
|
+
}}
|
|
225
|
+
options={unitOptions}
|
|
226
|
+
style={{ flex: '0 0 auto', minWidth: 130 }}
|
|
227
|
+
popupMatchSelectWidth
|
|
228
|
+
/>
|
|
229
|
+
</>
|
|
230
|
+
)}
|
|
231
|
+
|
|
232
|
+
{mode === 'exact' && !isRange && (
|
|
233
|
+
<>
|
|
234
|
+
<Select
|
|
235
|
+
value={picker}
|
|
236
|
+
onChange={(next) => {
|
|
237
|
+
setPicker(next);
|
|
238
|
+
// Picker change means the previously selected date is now expressed at a different granularity; clear it so the user doesn't carry a stale value into the new picker.
|
|
239
|
+
onChange?.(undefined);
|
|
240
|
+
}}
|
|
241
|
+
options={pickerOptions}
|
|
242
|
+
style={{ flex: '0 0 auto', width: 100 }}
|
|
243
|
+
popupMatchSelectWidth={false}
|
|
244
|
+
/>
|
|
245
|
+
<DatePicker
|
|
246
|
+
// Stored value is a granularity-formatted string (e.g. `"2026-02"` for month picker). Hydrate it back to a Dayjs for antd's controlled-value contract, then re-emit a formatted string on change so the URL serialization matches v1 exactly. Without the format step, antd's Dayjs would JSON.stringify to a full ISO timestamp and the server's `$dateOn` would never match.
|
|
247
|
+
value={parseDateFilterValue(typeof value === 'string' ? value : undefined, picker)}
|
|
248
|
+
onChange={(next) => onChange?.(formatDateFilterValue(next, picker))}
|
|
249
|
+
picker={picker}
|
|
250
|
+
format={FORMAT_BY_PICKER[picker]}
|
|
251
|
+
style={{ flex: 1, minWidth: 0 }}
|
|
252
|
+
/>
|
|
253
|
+
</>
|
|
254
|
+
)}
|
|
255
|
+
|
|
256
|
+
{mode === 'exact' && isRange && (
|
|
257
|
+
<DatePicker.RangePicker
|
|
258
|
+
// `$dateBetween` always operates at day granularity in v1's URL output, so we pin the range format to `YYYY-MM-DD`.
|
|
259
|
+
value={
|
|
260
|
+
Array.isArray(value)
|
|
261
|
+
? ([parseDateFilterValue(value[0], 'date'), parseDateFilterValue(value[1], 'date')] as [
|
|
262
|
+
Dayjs | null,
|
|
263
|
+
Dayjs | null,
|
|
264
|
+
])
|
|
265
|
+
: undefined
|
|
266
|
+
}
|
|
267
|
+
onChange={(next) => {
|
|
268
|
+
if (!next || !next[0] || !next[1]) {
|
|
269
|
+
onChange?.(undefined);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const [start, end] = next as [Dayjs, Dayjs];
|
|
273
|
+
onChange?.([start.format(FORMAT_BY_PICKER.date), end.format(FORMAT_BY_PICKER.date)]);
|
|
274
|
+
}}
|
|
275
|
+
format={FORMAT_BY_PICKER.date}
|
|
276
|
+
style={{ flex: 1, minWidth: 0 }}
|
|
277
|
+
/>
|
|
278
|
+
)}
|
|
279
|
+
</Space.Compact>
|
|
280
|
+
);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
export default DateFilterDynamicComponent;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Checkbox, ColorPicker, DatePicker, Input, InputNumber, Radio, Select, TimePicker } from 'antd';
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import type { FilterOperator, FilterOption } from '../../../flow/components/filter/useFilterOptions';
|
|
13
|
+
import { PasswordInput } from '../PasswordInput';
|
|
14
|
+
import { DateFilterDynamicComponent } from './DateFilterDynamicComponent';
|
|
15
|
+
|
|
16
|
+
export interface FilterValueInputProps {
|
|
17
|
+
/** The currently selected leaf field option from the field picker. */
|
|
18
|
+
field?: FilterOption;
|
|
19
|
+
/** The currently selected operator (full object, not just `.value`). */
|
|
20
|
+
operator?: FilterOperator;
|
|
21
|
+
/** Current value. Shape depends on operator/field. */
|
|
22
|
+
value: any;
|
|
23
|
+
/** Notify the parent when the user edits the value. */
|
|
24
|
+
onChange: (value: any) => void;
|
|
25
|
+
/** Translator used by sub-renderers. */
|
|
26
|
+
t?: (key: string) => string;
|
|
27
|
+
/** Optional placeholder for the fallback `Input`. */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const identity = (s: string) => s;
|
|
32
|
+
|
|
33
|
+
type EffectiveSchema = {
|
|
34
|
+
'x-component'?: string;
|
|
35
|
+
'x-component-props'?: Record<string, any>;
|
|
36
|
+
enum?: Array<{ value: any; label: string }> | any[];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const baseStyle = { minWidth: 200 } as const;
|
|
40
|
+
|
|
41
|
+
/** Resolve operator-level schema → field uiSchema → fallback Input. */
|
|
42
|
+
const resolveSchema = (field?: FilterOption, operator?: FilterOperator): EffectiveSchema => {
|
|
43
|
+
if (operator?.schema?.['x-component']) {
|
|
44
|
+
return operator.schema as EffectiveSchema;
|
|
45
|
+
}
|
|
46
|
+
const fieldSchema = field?.schema as EffectiveSchema | undefined;
|
|
47
|
+
if (fieldSchema?.['x-component']) {
|
|
48
|
+
return fieldSchema;
|
|
49
|
+
}
|
|
50
|
+
return { 'x-component': 'Input' };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Interface-aware value renderer for filter rows. Returns `null` for `noValue` operators (`$empty`, `$notEmpty`). Otherwise dispatches the effective `x-component` (operator schema > field uiSchema > Input) to a small registry of antd controls.
|
|
55
|
+
*/
|
|
56
|
+
export const FilterValueInput: React.FC<FilterValueInputProps> = (props) => {
|
|
57
|
+
const { field, operator, value, onChange, t = identity, placeholder } = props;
|
|
58
|
+
|
|
59
|
+
if (operator?.noValue) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const schema = resolveSchema(field, operator);
|
|
64
|
+
const componentName = schema['x-component'];
|
|
65
|
+
const componentProps = schema['x-component-props'] || {};
|
|
66
|
+
const enumOptions = (schema as any).enum || (field?.schema as any)?.enum;
|
|
67
|
+
|
|
68
|
+
switch (componentName) {
|
|
69
|
+
case 'DateFilterDynamicComponent':
|
|
70
|
+
return <DateFilterDynamicComponent value={value} onChange={onChange} isRange={!!componentProps.isRange} t={t} />;
|
|
71
|
+
|
|
72
|
+
case 'DatePicker':
|
|
73
|
+
case 'UnixTimestamp':
|
|
74
|
+
return (
|
|
75
|
+
<DatePicker
|
|
76
|
+
value={value}
|
|
77
|
+
onChange={onChange}
|
|
78
|
+
{...componentProps}
|
|
79
|
+
style={{ ...baseStyle, ...(componentProps.style || {}) }}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
case 'TimePicker':
|
|
84
|
+
return (
|
|
85
|
+
<TimePicker
|
|
86
|
+
value={value}
|
|
87
|
+
onChange={onChange}
|
|
88
|
+
{...componentProps}
|
|
89
|
+
style={{ ...baseStyle, ...(componentProps.style || {}) }}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
case 'InputNumber':
|
|
94
|
+
case 'Percent':
|
|
95
|
+
return (
|
|
96
|
+
<InputNumber
|
|
97
|
+
value={value}
|
|
98
|
+
onChange={(next) => onChange(next ?? undefined)}
|
|
99
|
+
{...componentProps}
|
|
100
|
+
style={{ ...baseStyle, ...(componentProps.style || {}) }}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
case 'ColorPicker':
|
|
105
|
+
return <ColorPicker value={value} onChange={(_color, hex) => onChange(hex)} {...componentProps} />;
|
|
106
|
+
|
|
107
|
+
case 'Checkbox':
|
|
108
|
+
return <Checkbox checked={!!value} onChange={(e) => onChange(e.target.checked)} {...componentProps} />;
|
|
109
|
+
|
|
110
|
+
case 'Checkbox.Group': {
|
|
111
|
+
const options = Array.isArray(componentProps.options) ? componentProps.options : enumOptions;
|
|
112
|
+
return (
|
|
113
|
+
<Checkbox.Group
|
|
114
|
+
value={value}
|
|
115
|
+
onChange={onChange}
|
|
116
|
+
options={options as any}
|
|
117
|
+
{...componentProps}
|
|
118
|
+
style={{ ...baseStyle, ...(componentProps.style || {}) }}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
case 'Radio.Group': {
|
|
124
|
+
const options = Array.isArray(componentProps.options) ? componentProps.options : enumOptions;
|
|
125
|
+
return (
|
|
126
|
+
<Radio.Group
|
|
127
|
+
value={value}
|
|
128
|
+
onChange={(e) => onChange(e.target.value)}
|
|
129
|
+
options={options as any}
|
|
130
|
+
{...componentProps}
|
|
131
|
+
style={{ ...baseStyle, ...(componentProps.style || {}) }}
|
|
132
|
+
/>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
case 'Select': {
|
|
137
|
+
const { mode, options, multiple } = componentProps;
|
|
138
|
+
const resolvedMode = mode === 'tags' || mode === 'multiple' ? mode : multiple ? 'multiple' : undefined;
|
|
139
|
+
const inlineOptions = Array.isArray(options) ? options : enumOptions;
|
|
140
|
+
const resolvedOptions = Array.isArray(inlineOptions)
|
|
141
|
+
? inlineOptions.map((option: any) => ({
|
|
142
|
+
...option,
|
|
143
|
+
label: typeof option.label === 'string' ? t(option.label) : option.label,
|
|
144
|
+
}))
|
|
145
|
+
: undefined;
|
|
146
|
+
return (
|
|
147
|
+
<Select
|
|
148
|
+
value={value}
|
|
149
|
+
onChange={onChange}
|
|
150
|
+
mode={resolvedMode}
|
|
151
|
+
options={resolvedOptions}
|
|
152
|
+
allowClear
|
|
153
|
+
{...componentProps}
|
|
154
|
+
style={{ ...baseStyle, ...(componentProps.style || {}) }}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
case 'Password':
|
|
160
|
+
return (
|
|
161
|
+
<PasswordInput
|
|
162
|
+
value={value}
|
|
163
|
+
onChange={(e: any) => onChange(e?.target?.value ?? e)}
|
|
164
|
+
{...componentProps}
|
|
165
|
+
style={{ ...baseStyle, ...(componentProps.style || {}) }}
|
|
166
|
+
/>
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
case 'Input.TextArea':
|
|
170
|
+
case 'Input.JSON':
|
|
171
|
+
case 'Markdown':
|
|
172
|
+
case 'RichText':
|
|
173
|
+
return (
|
|
174
|
+
<Input.TextArea
|
|
175
|
+
value={value}
|
|
176
|
+
onChange={(e) => onChange(e.target.value)}
|
|
177
|
+
{...componentProps}
|
|
178
|
+
style={{ ...baseStyle, ...(componentProps.style || {}) }}
|
|
179
|
+
/>
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
case 'Input':
|
|
183
|
+
case 'Input.URL':
|
|
184
|
+
case 'NanoIDInput':
|
|
185
|
+
default:
|
|
186
|
+
return (
|
|
187
|
+
<Input
|
|
188
|
+
value={value}
|
|
189
|
+
onChange={(e) => onChange(e.target.value)}
|
|
190
|
+
placeholder={placeholder}
|
|
191
|
+
{...componentProps}
|
|
192
|
+
style={{ ...baseStyle, ...(componentProps.style || {}) }}
|
|
193
|
+
/>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export default FilterValueInput;
|