mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
feat: add Gantt and Kanban blocks in pop ups/drawers (#4277)
* feat: add Gantt and Kanban blocks in pop ups/drawers * feat: add Gantt and Kanban blocks in pop ups/drawers * fix: bug * fix: bug * fix: bug * fix: bug
This commit is contained in:
parent
1283312eb9
commit
d787edfb47
@ -1052,7 +1052,7 @@ export const useBulkDestroyActionProps = () => {
|
|||||||
field.data.selectedRowKeys = [];
|
field.data.selectedRowKeys = [];
|
||||||
const currentPage = service.params[0]?.page;
|
const currentPage = service.params[0]?.page;
|
||||||
const totalPage = service.data?.meta?.totalPage;
|
const totalPage = service.data?.meta?.totalPage;
|
||||||
if (currentPage === totalPage) {
|
if (currentPage === totalPage && service.params[0]) {
|
||||||
service.params[0].page = currentPage - 1;
|
service.params[0].page = currentPage - 1;
|
||||||
}
|
}
|
||||||
if (callBack) {
|
if (callBack) {
|
||||||
|
@ -22,122 +22,266 @@ import {
|
|||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
DataBlockInitializer,
|
DataBlockInitializer,
|
||||||
SchemaComponentOptions,
|
SchemaComponentOptions,
|
||||||
|
Collection,
|
||||||
|
CollectionFieldOptions,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { createGanttBlockUISchema } from './createGanttBlockUISchema';
|
import { createGanttBlockUISchema } from './createGanttBlockUISchema';
|
||||||
|
|
||||||
export const GanttBlockInitializer = () => {
|
export const GanttBlockInitializer = ({
|
||||||
|
filterCollections,
|
||||||
|
onlyCurrentDataSource,
|
||||||
|
hideSearch,
|
||||||
|
createBlockSchema,
|
||||||
|
showAssociationFields,
|
||||||
|
}: {
|
||||||
|
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
|
||||||
|
onlyCurrentDataSource: boolean;
|
||||||
|
hideSearch?: boolean;
|
||||||
|
createBlockSchema?: (options: any) => any;
|
||||||
|
showAssociationFields?: boolean;
|
||||||
|
}) => {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
const { createGanttBlock } = useCreateGanttBlock();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataBlockInitializer
|
||||||
|
{...itemConfig}
|
||||||
|
componentType={'Calendar'}
|
||||||
|
icon={<FormOutlined />}
|
||||||
|
onCreateBlockSchema={async (options) => {
|
||||||
|
if (createBlockSchema) {
|
||||||
|
return createBlockSchema(options);
|
||||||
|
}
|
||||||
|
createGanttBlock(options);
|
||||||
|
}}
|
||||||
|
onlyCurrentDataSource={onlyCurrentDataSource}
|
||||||
|
hideSearch={hideSearch}
|
||||||
|
filter={filterCollections}
|
||||||
|
showAssociationFields={showAssociationFields}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateGanttBlock = () => {
|
||||||
const { insert } = useSchemaInitializer();
|
const { insert } = useSchemaInitializer();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { getCollectionFields } = useCollectionManager_deprecated();
|
const { getCollectionFields } = useCollectionManager_deprecated();
|
||||||
const options = useContext(SchemaOptionsContext);
|
const options = useContext(SchemaOptionsContext);
|
||||||
const { theme } = useGlobalTheme();
|
const { theme } = useGlobalTheme();
|
||||||
const itemConfig = useSchemaInitializerItem();
|
|
||||||
|
|
||||||
return (
|
const createGanttBlock = async ({ item }) => {
|
||||||
<DataBlockInitializer
|
const collectionFields = getCollectionFields(item.name, item.dataSource);
|
||||||
{...itemConfig}
|
const stringFields = collectionFields
|
||||||
componentType={'Gantt'}
|
?.filter((field) => field.type === 'string')
|
||||||
icon={<FormOutlined />}
|
?.map((field) => {
|
||||||
onCreateBlockSchema={async ({ item }) => {
|
return {
|
||||||
const collectionFields = getCollectionFields(item.name, item.dataSource);
|
label: field?.uiSchema?.title,
|
||||||
const stringFields = collectionFields
|
value: field.name,
|
||||||
?.filter((field) => field.type === 'string')
|
};
|
||||||
?.map((field) => {
|
});
|
||||||
return {
|
const dateFields = collectionFields
|
||||||
label: field?.uiSchema?.title,
|
?.filter((field) => field.type === 'date')
|
||||||
value: field.name,
|
?.map((field) => {
|
||||||
};
|
return {
|
||||||
});
|
label: field?.uiSchema?.title,
|
||||||
const dateFields = collectionFields
|
value: field.name,
|
||||||
?.filter((field) => field.type === 'date')
|
};
|
||||||
?.map((field) => {
|
});
|
||||||
return {
|
const numberFields = collectionFields
|
||||||
label: field?.uiSchema?.title,
|
?.filter((field) => field.type === 'float')
|
||||||
value: field.name,
|
?.map((field) => {
|
||||||
};
|
return {
|
||||||
});
|
label: field?.uiSchema?.title,
|
||||||
const numberFields = collectionFields
|
value: field.name,
|
||||||
?.filter((field) => field.type === 'float')
|
};
|
||||||
?.map((field) => {
|
});
|
||||||
return {
|
const values = await FormDialog(
|
||||||
label: field?.uiSchema?.title,
|
t('Create gantt block'),
|
||||||
value: field.name,
|
() => {
|
||||||
};
|
return (
|
||||||
});
|
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
|
||||||
const values = await FormDialog(
|
<FormLayout layout={'vertical'}>
|
||||||
t('Create gantt block'),
|
<SchemaComponent
|
||||||
() => {
|
schema={{
|
||||||
return (
|
properties: {
|
||||||
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
|
title: {
|
||||||
<FormLayout layout={'vertical'}>
|
title: t('Title field'),
|
||||||
<SchemaComponent
|
enum: stringFields,
|
||||||
schema={{
|
required: true,
|
||||||
properties: {
|
'x-component': 'Select',
|
||||||
title: {
|
'x-decorator': 'FormItem',
|
||||||
title: t('Title field'),
|
},
|
||||||
enum: stringFields,
|
start: {
|
||||||
required: true,
|
title: t('Start date field'),
|
||||||
'x-component': 'Select',
|
enum: dateFields,
|
||||||
'x-decorator': 'FormItem',
|
required: true,
|
||||||
},
|
default: 'createdAt',
|
||||||
start: {
|
'x-component': 'Select',
|
||||||
title: t('Start date field'),
|
'x-decorator': 'FormItem',
|
||||||
enum: dateFields,
|
},
|
||||||
required: true,
|
end: {
|
||||||
default: 'createdAt',
|
title: t('End date field'),
|
||||||
'x-component': 'Select',
|
enum: dateFields,
|
||||||
'x-decorator': 'FormItem',
|
required: true,
|
||||||
},
|
'x-component': 'Select',
|
||||||
end: {
|
'x-decorator': 'FormItem',
|
||||||
title: t('End date field'),
|
},
|
||||||
enum: dateFields,
|
progress: {
|
||||||
required: true,
|
title: t('Progress field'),
|
||||||
'x-component': 'Select',
|
enum: numberFields,
|
||||||
'x-decorator': 'FormItem',
|
'x-component': 'Select',
|
||||||
},
|
'x-decorator': 'FormItem',
|
||||||
progress: {
|
},
|
||||||
title: t('Progress field'),
|
range: {
|
||||||
enum: numberFields,
|
title: t('Time scale'),
|
||||||
'x-component': 'Select',
|
enum: [
|
||||||
'x-decorator': 'FormItem',
|
{ label: '{{t("Hour")}}', value: 'hour', color: 'orange' },
|
||||||
},
|
{ label: '{{t("Quarter of day")}}', value: 'quarterDay', color: 'default' },
|
||||||
range: {
|
{ label: '{{t("Half of day")}}', value: 'halfDay', color: 'blue' },
|
||||||
title: t('Time scale'),
|
{ label: '{{t("Day")}}', value: 'day', color: 'yellow' },
|
||||||
enum: [
|
{ label: '{{t("Week")}}', value: 'week', color: 'pule' },
|
||||||
{ label: '{{t("Hour")}}', value: 'hour', color: 'orange' },
|
{ label: '{{t("Month")}}', value: 'month', color: 'green' },
|
||||||
{ label: '{{t("Quarter of day")}}', value: 'quarterDay', color: 'default' },
|
{ label: '{{t("Year")}}', value: 'year', color: 'green' },
|
||||||
{ label: '{{t("Half of day")}}', value: 'halfDay', color: 'blue' },
|
{ label: '{{t("QuarterYear")}}', value: 'quarterYear', color: 'red' },
|
||||||
{ label: '{{t("Day")}}', value: 'day', color: 'yellow' },
|
],
|
||||||
{ label: '{{t("Week")}}', value: 'week', color: 'pule' },
|
default: 'day',
|
||||||
{ label: '{{t("Month")}}', value: 'month', color: 'green' },
|
'x-component': 'Select',
|
||||||
{ label: '{{t("Year")}}', value: 'year', color: 'green' },
|
'x-decorator': 'FormItem',
|
||||||
{ label: '{{t("QuarterYear")}}', value: 'quarterYear', color: 'red' },
|
},
|
||||||
],
|
},
|
||||||
default: 'day',
|
}}
|
||||||
'x-component': 'Select',
|
/>
|
||||||
'x-decorator': 'FormItem',
|
</FormLayout>
|
||||||
},
|
</SchemaComponentOptions>
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormLayout>
|
|
||||||
</SchemaComponentOptions>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
theme,
|
|
||||||
).open({
|
|
||||||
initialValues: {},
|
|
||||||
});
|
|
||||||
insert(
|
|
||||||
createGanttBlockUISchema({
|
|
||||||
collectionName: item.name,
|
|
||||||
dataSource: item.dataSource,
|
|
||||||
fieldNames: {
|
|
||||||
...values,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}}
|
},
|
||||||
/>
|
theme,
|
||||||
);
|
).open({
|
||||||
|
initialValues: {},
|
||||||
|
});
|
||||||
|
insert(
|
||||||
|
createGanttBlockUISchema({
|
||||||
|
collectionName: item.name,
|
||||||
|
dataSource: item.dataSource,
|
||||||
|
fieldNames: {
|
||||||
|
...values,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { createGanttBlock };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function useCreateAssociationGanttBlock() {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const options = useContext(SchemaOptionsContext);
|
||||||
|
const { theme } = useGlobalTheme();
|
||||||
|
const { getCollectionFields } = useCollectionManager_deprecated();
|
||||||
|
|
||||||
|
const createAssociationGanttBlock = async ({ item }) => {
|
||||||
|
const field = item.associationField;
|
||||||
|
|
||||||
|
const collectionFields = getCollectionFields(item.name, item.dataSource);
|
||||||
|
const stringFields = collectionFields
|
||||||
|
?.filter((field) => field.type === 'string')
|
||||||
|
?.map((field) => {
|
||||||
|
return {
|
||||||
|
label: field?.uiSchema?.title,
|
||||||
|
value: field.name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const dateFields = collectionFields
|
||||||
|
?.filter((field) => field.type === 'date')
|
||||||
|
?.map((field) => {
|
||||||
|
return {
|
||||||
|
label: field?.uiSchema?.title,
|
||||||
|
value: field.name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const numberFields = collectionFields
|
||||||
|
?.filter((field) => field.type === 'float')
|
||||||
|
?.map((field) => {
|
||||||
|
return {
|
||||||
|
label: field?.uiSchema?.title,
|
||||||
|
value: field.name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const values = await FormDialog(
|
||||||
|
t('Create gantt block'),
|
||||||
|
() => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
title: t('Title field'),
|
||||||
|
enum: stringFields,
|
||||||
|
required: true,
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
start: {
|
||||||
|
title: t('Start date field'),
|
||||||
|
enum: dateFields,
|
||||||
|
required: true,
|
||||||
|
default: 'createdAt',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
title: t('End date field'),
|
||||||
|
enum: dateFields,
|
||||||
|
required: true,
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
progress: {
|
||||||
|
title: t('Progress field'),
|
||||||
|
enum: numberFields,
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
range: {
|
||||||
|
title: t('Time scale'),
|
||||||
|
enum: [
|
||||||
|
{ label: '{{t("Hour")}}', value: 'hour', color: 'orange' },
|
||||||
|
{ label: '{{t("Quarter of day")}}', value: 'quarterDay', color: 'default' },
|
||||||
|
{ label: '{{t("Half of day")}}', value: 'halfDay', color: 'blue' },
|
||||||
|
{ label: '{{t("Day")}}', value: 'day', color: 'yellow' },
|
||||||
|
{ label: '{{t("Week")}}', value: 'week', color: 'pule' },
|
||||||
|
{ label: '{{t("Month")}}', value: 'month', color: 'green' },
|
||||||
|
{ label: '{{t("Year")}}', value: 'year', color: 'green' },
|
||||||
|
{ label: '{{t("QuarterYear")}}', value: 'quarterYear', color: 'red' },
|
||||||
|
],
|
||||||
|
default: 'day',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormLayout>
|
||||||
|
</SchemaComponentOptions>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
theme,
|
||||||
|
).open({
|
||||||
|
initialValues: {},
|
||||||
|
});
|
||||||
|
insert(
|
||||||
|
createGanttBlockUISchema({
|
||||||
|
association: `${field.collectionName}.${field.name}`,
|
||||||
|
dataSource: item.dataSource,
|
||||||
|
fieldNames: {
|
||||||
|
...values,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { createAssociationGanttBlock };
|
||||||
|
}
|
||||||
|
@ -12,7 +12,6 @@ import React, { createContext, useContext, useEffect, useState } from 'react';
|
|||||||
import {
|
import {
|
||||||
useACLRoleContext,
|
useACLRoleContext,
|
||||||
useCollection_deprecated,
|
useCollection_deprecated,
|
||||||
BlockProvider,
|
|
||||||
useBlockRequestContext,
|
useBlockRequestContext,
|
||||||
TableBlockProvider,
|
TableBlockProvider,
|
||||||
useTableBlockContext,
|
useTableBlockContext,
|
||||||
@ -95,11 +94,9 @@ export const GanttBlockProvider = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div aria-label="block-item-gantt" role="button">
|
<div aria-label="block-item-gantt" role="button">
|
||||||
<BlockProvider name="gantt" {...props} params={params}>
|
<TableBlockProvider {...props} params={params}>
|
||||||
<TableBlockProvider {...props} params={params}>
|
<InternalGanttBlockProvider {...props} />
|
||||||
<InternalGanttBlockProvider {...props} />
|
</TableBlockProvider>
|
||||||
</TableBlockProvider>
|
|
||||||
</BlockProvider>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -114,7 +111,7 @@ export const useGanttBlockProps = () => {
|
|||||||
const { getPrimaryKey, name, template, writableView } = useCollection_deprecated();
|
const { getPrimaryKey, name, template, writableView } = useCollection_deprecated();
|
||||||
const { parseAction } = useACLRoleContext();
|
const { parseAction } = useACLRoleContext();
|
||||||
const ctxBlock = useTableBlockContext();
|
const ctxBlock = useTableBlockContext();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
const primaryKey = getPrimaryKey();
|
const primaryKey = getPrimaryKey();
|
||||||
const checkPermission = (record) => {
|
const checkPermission = (record) => {
|
||||||
const actionPath = `${name}:update`;
|
const actionPath = `${name}:update`;
|
||||||
@ -136,6 +133,7 @@ export const useGanttBlockProps = () => {
|
|||||||
ctx.field.data = data;
|
ctx.field.data = data;
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setLoading(true);
|
||||||
if (!ctx?.service?.loading) {
|
if (!ctx?.service?.loading) {
|
||||||
const data = formatData(
|
const data = formatData(
|
||||||
ctx.service.data?.data,
|
ctx.service.data?.data,
|
||||||
@ -147,6 +145,7 @@ export const useGanttBlockProps = () => {
|
|||||||
primaryKey,
|
primaryKey,
|
||||||
);
|
);
|
||||||
setTasks(data);
|
setTasks(data);
|
||||||
|
setLoading(false);
|
||||||
ctx.field.data = data;
|
ctx.field.data = data;
|
||||||
if (tasks.length > 0) {
|
if (tasks.length > 0) {
|
||||||
ctxBlock.setExpandFlag(true);
|
ctxBlock.setExpandFlag(true);
|
||||||
@ -159,5 +158,6 @@ export const useGanttBlockProps = () => {
|
|||||||
onExpanderClick,
|
onExpanderClick,
|
||||||
expandAndCollapseAll,
|
expandAndCollapseAll,
|
||||||
tasks,
|
tasks,
|
||||||
|
loading,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,7 @@ import {
|
|||||||
withDynamicSchemaProps,
|
withDynamicSchemaProps,
|
||||||
useDesignable,
|
useDesignable,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { message } from 'antd';
|
import { message, Spin } from 'antd';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import React, { SyntheticEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { SyntheticEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -143,10 +143,11 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => {
|
|||||||
tasks,
|
tasks,
|
||||||
expandAndCollapseAll,
|
expandAndCollapseAll,
|
||||||
fieldNames,
|
fieldNames,
|
||||||
|
loading,
|
||||||
} = useProps(props); // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
} = useProps(props); // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { designable } = useDesignable();
|
const { designable } = useDesignable();
|
||||||
const headerHeight = currentTheme.includes('compact') ? 45 : designable ? 65 : 55;
|
const headerHeight = currentTheme?.includes('compact') ? 45 : designable ? 65 : 55;
|
||||||
const rowHeight = currentTheme.includes('compact') ? 45 : 65;
|
const rowHeight = currentTheme?.includes('compact') ? 45 : 65;
|
||||||
const ctx = useGanttBlockContext();
|
const ctx = useGanttBlockContext();
|
||||||
const appInfo = useCurrentAppInfo();
|
const appInfo = useCurrentAppInfo();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -158,6 +159,7 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => {
|
|||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
const taskListRef = useRef<HTMLDivElement>(null);
|
const taskListRef = useRef<HTMLDivElement>(null);
|
||||||
const verticalGanttContainerRef = useRef<HTMLDivElement>(null);
|
const verticalGanttContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const ganttRef = useRef<HTMLDivElement>(null);
|
||||||
const [dateSetup, setDateSetup] = useState<DateSetup>(() => {
|
const [dateSetup, setDateSetup] = useState<DateSetup>(() => {
|
||||||
const [startDate, endDate] = ganttDateRange(tasks, viewMode, preStepsCount);
|
const [startDate, endDate] = ganttDateRange(tasks, viewMode, preStepsCount);
|
||||||
return { viewMode, dates: seedDates(startDate, endDate, viewMode) };
|
return { viewMode, dates: seedDates(startDate, endDate, viewMode) };
|
||||||
@ -521,6 +523,7 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => {
|
|||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
onClick: handleBarClick,
|
onClick: handleBarClick,
|
||||||
onDelete,
|
onDelete,
|
||||||
|
loading,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -536,6 +539,7 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => {
|
|||||||
height: ${headerHeight}px;
|
height: ${headerHeight}px;
|
||||||
}
|
}
|
||||||
`)}
|
`)}
|
||||||
|
ref={ganttRef}
|
||||||
>
|
>
|
||||||
<GanttRecordViewer visible={visible} setVisible={setVisible} record={record} />
|
<GanttRecordViewer visible={visible} setVisible={setVisible} record={record} />
|
||||||
<RecursionField name={'anctionBar'} schema={fieldSchema.properties.toolBar} />
|
<RecursionField name={'anctionBar'} schema={fieldSchema.properties.toolBar} />
|
||||||
@ -576,13 +580,15 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => {
|
|||||||
onScroll={handleScrollY}
|
onScroll={handleScrollY}
|
||||||
rtl={rtl}
|
rtl={rtl}
|
||||||
/>
|
/>
|
||||||
<HorizontalScroll
|
<Spin spinning={loading} style={{ visibility: 'hidden' }}>
|
||||||
svgWidth={svgWidth}
|
<HorizontalScroll
|
||||||
taskListWidth={taskListWidth}
|
svgWidth={svgWidth}
|
||||||
scroll={scrollX}
|
taskListWidth={taskListWidth}
|
||||||
rtl={rtl}
|
scroll={scrollX}
|
||||||
onScroll={handleScrollX}
|
rtl={rtl}
|
||||||
/>
|
onScroll={handleScrollX}
|
||||||
|
/>
|
||||||
|
</Spin>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -37,6 +37,7 @@ export type TaskGanttContentProps = {
|
|||||||
setGanttEvent: (value: GanttEvent) => void;
|
setGanttEvent: (value: GanttEvent) => void;
|
||||||
setFailedTask: (value: BarTask | null) => void;
|
setFailedTask: (value: BarTask | null) => void;
|
||||||
setSelectedTask: (taskId: string) => void;
|
setSelectedTask: (taskId: string) => void;
|
||||||
|
loading?: boolean;
|
||||||
} & EventOption;
|
} & EventOption;
|
||||||
|
|
||||||
export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { forwardRef, useEffect, useRef } from 'react';
|
import React, { forwardRef, useEffect, useRef } from 'react';
|
||||||
|
import { Spin } from 'antd';
|
||||||
import { Calendar, CalendarProps } from '../calendar/calendar';
|
import { Calendar, CalendarProps } from '../calendar/calendar';
|
||||||
import { Grid, GridProps } from '../grid/grid';
|
import { Grid, GridProps } from '../grid/grid';
|
||||||
import { TaskGanttContent, TaskGanttContentProps } from './task-gantt-content';
|
import { TaskGanttContent, TaskGanttContentProps } from './task-gantt-content';
|
||||||
@ -28,7 +29,6 @@ export const TaskGantt: React.FC<TaskGanttProps> = forwardRef(
|
|||||||
const horizontalContainerRef = useRef<HTMLDivElement>(null);
|
const horizontalContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const newBarProps = { ...barProps, svg: ganttSVGRef };
|
const newBarProps = { ...barProps, svg: ganttSVGRef };
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (horizontalContainerRef.current) {
|
if (horizontalContainerRef.current) {
|
||||||
horizontalContainerRef.current.scrollTop = scrollY;
|
horizontalContainerRef.current.scrollTop = scrollY;
|
||||||
@ -40,7 +40,6 @@ export const TaskGantt: React.FC<TaskGanttProps> = forwardRef(
|
|||||||
ref.current.scrollLeft = scrollX;
|
ref.current.scrollLeft = scrollX;
|
||||||
}
|
}
|
||||||
}, [scrollX]);
|
}, [scrollX]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.ganttverticalcontainer} ref={ref} dir="ltr">
|
<div className={styles.ganttverticalcontainer} ref={ref} dir="ltr">
|
||||||
<svg
|
<svg
|
||||||
@ -52,23 +51,25 @@ export const TaskGantt: React.FC<TaskGanttProps> = forwardRef(
|
|||||||
>
|
>
|
||||||
<Calendar {...calendarProps} />
|
<Calendar {...calendarProps} />
|
||||||
</svg>
|
</svg>
|
||||||
<div
|
<Spin spinning={barProps?.loading}>
|
||||||
ref={horizontalContainerRef}
|
<div
|
||||||
className={styles.horizontalcontainer}
|
ref={horizontalContainerRef}
|
||||||
style={ganttHeight ? { maxHeight: ganttHeight, width: gridProps.svgWidth } : { width: gridProps.svgWidth }}
|
className={styles.horizontalcontainer}
|
||||||
>
|
style={ganttHeight ? { maxHeight: ganttHeight, width: gridProps.svgWidth } : { width: gridProps.svgWidth }}
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width={gridProps.svgWidth}
|
|
||||||
height={barProps.rowHeight * (barProps.tasks.length || 3)}
|
|
||||||
fontFamily={barProps.fontFamily}
|
|
||||||
ref={ganttSVGRef}
|
|
||||||
className="ganttBody"
|
|
||||||
>
|
>
|
||||||
<Grid {...gridProps} />
|
<svg
|
||||||
<TaskGanttContent {...newBarProps} />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</svg>
|
width={gridProps.svgWidth}
|
||||||
</div>
|
height={barProps.rowHeight * barProps.tasks.length || 166}
|
||||||
|
fontFamily={barProps.fontFamily}
|
||||||
|
ref={ganttSVGRef}
|
||||||
|
className="ganttBody"
|
||||||
|
>
|
||||||
|
<Grid {...gridProps} />
|
||||||
|
<TaskGanttContent {...newBarProps} />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Spin>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -11,15 +11,16 @@ import { ISchema } from '@formily/react';
|
|||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
export const createGanttBlockUISchema = (options: {
|
export const createGanttBlockUISchema = (options: {
|
||||||
collectionName: string;
|
|
||||||
fieldNames: object;
|
fieldNames: object;
|
||||||
dataSource: string;
|
dataSource: string;
|
||||||
|
association?: string;
|
||||||
|
collectionName?: string;
|
||||||
}): ISchema => {
|
}): ISchema => {
|
||||||
const { collectionName, fieldNames, dataSource } = options;
|
const { collectionName, fieldNames, dataSource, association } = options;
|
||||||
|
|
||||||
return {
|
const schema = {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-acl-action': `${collectionName}:list`,
|
'x-acl-action': `${association || collectionName}:list`,
|
||||||
'x-decorator': 'GanttBlockProvider',
|
'x-decorator': 'GanttBlockProvider',
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
collection: collectionName,
|
collection: collectionName,
|
||||||
@ -135,4 +136,9 @@ export const createGanttBlockUISchema = (options: {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (association) {
|
||||||
|
schema['x-decorator-props']['association'] = association;
|
||||||
|
}
|
||||||
|
return schema;
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@ import { GanttBlockProvider, useGanttBlockProps } from './GanttBlockProvider';
|
|||||||
import { Event } from './components/gantt/Event';
|
import { Event } from './components/gantt/Event';
|
||||||
import { Gantt } from './components/gantt/gantt';
|
import { Gantt } from './components/gantt/gantt';
|
||||||
import { ViewMode } from './types/public-types';
|
import { ViewMode } from './types/public-types';
|
||||||
|
import { useCreateAssociationGanttBlock, useCreateGanttBlock } from './GanttBlockInitializer';
|
||||||
|
|
||||||
Gantt.ActionBar = ActionBar;
|
Gantt.ActionBar = ActionBar;
|
||||||
Gantt.ViewMode = ViewMode;
|
Gantt.ViewMode = ViewMode;
|
||||||
@ -48,7 +49,32 @@ export class PluginGanttClient extends Plugin {
|
|||||||
title: "{{t('Gantt')}}",
|
title: "{{t('Gantt')}}",
|
||||||
Component: 'GanttBlockInitializer',
|
Component: 'GanttBlockInitializer',
|
||||||
});
|
});
|
||||||
|
this.app.schemaInitializerManager.addItem('popup:common:addBlock', 'dataBlocks.gantt', {
|
||||||
|
title: "{{t('Gantt')}}",
|
||||||
|
Component: 'GanttBlockInitializer',
|
||||||
|
useComponentProps() {
|
||||||
|
const { createAssociationGanttBlock } = useCreateAssociationGanttBlock();
|
||||||
|
const { createGanttBlock } = useCreateGanttBlock();
|
||||||
|
|
||||||
|
return {
|
||||||
|
onlyCurrentDataSource: true,
|
||||||
|
filterCollections({ associationField }) {
|
||||||
|
if (associationField) {
|
||||||
|
return ['hasMany', 'belongsToMany'].includes(associationField.type);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
createBlockSchema: ({ item, fromOthersInPopup }) => {
|
||||||
|
if (fromOthersInPopup) {
|
||||||
|
return createGanttBlock({ item });
|
||||||
|
}
|
||||||
|
createAssociationGanttBlock({ item });
|
||||||
|
},
|
||||||
|
showAssociationFields: true,
|
||||||
|
hideSearch: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
this.app.addScopes({
|
this.app.addScopes({
|
||||||
useGanttBlockProps,
|
useGanttBlockProps,
|
||||||
});
|
});
|
||||||
|
@ -24,6 +24,8 @@ import {
|
|||||||
useSchemaInitializer,
|
useSchemaInitializer,
|
||||||
useSchemaInitializerItem,
|
useSchemaInitializerItem,
|
||||||
useAPIClient,
|
useAPIClient,
|
||||||
|
Collection,
|
||||||
|
CollectionFieldOptions,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { createKanbanBlockUISchema } from './createKanbanBlockUISchema';
|
import { createKanbanBlockUISchema } from './createKanbanBlockUISchema';
|
||||||
import { CreateAndSelectSort } from './CreateAndSelectSort';
|
import { CreateAndSelectSort } from './CreateAndSelectSort';
|
||||||
@ -85,7 +87,7 @@ const CreateKanbanForm = ({ item, sortFields, collectionFields, fields, options,
|
|||||||
field.groupField = field.form.values?.groupField;
|
field.groupField = field.form.values?.groupField;
|
||||||
field.setComponentProps({
|
field.setComponentProps({
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
collectionName: item.name,
|
collectionName: item.collectionName || item.name,
|
||||||
collectionFields,
|
collectionFields,
|
||||||
sortFields: sortFields,
|
sortFields: sortFields,
|
||||||
});
|
});
|
||||||
@ -109,74 +111,170 @@ const CreateKanbanForm = ({ item, sortFields, collectionFields, fields, options,
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const KanbanBlockInitializer = () => {
|
export const KanbanBlockInitializer = ({
|
||||||
const { insert } = useSchemaInitializer();
|
filterCollections,
|
||||||
const { t } = useTranslation();
|
onlyCurrentDataSource,
|
||||||
const { getCollectionFields } = useCollectionManager_deprecated();
|
hideSearch,
|
||||||
const { theme } = useGlobalTheme();
|
createBlockSchema,
|
||||||
|
showAssociationFields,
|
||||||
|
}: {
|
||||||
|
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
|
||||||
|
onlyCurrentDataSource: boolean;
|
||||||
|
hideSearch?: boolean;
|
||||||
|
createBlockSchema?: (options: any) => any;
|
||||||
|
showAssociationFields?: boolean;
|
||||||
|
}) => {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
const itemConfig = useSchemaInitializerItem();
|
||||||
const options = useContext(SchemaOptionsContext);
|
const { createKanbanBlock } = useCreateKanbanBlock();
|
||||||
const api = useAPIClient();
|
|
||||||
return (
|
return (
|
||||||
<DataBlockInitializer
|
<DataBlockInitializer
|
||||||
{...itemConfig}
|
{...itemConfig}
|
||||||
componentType={'Kanban'}
|
componentType={'Calendar'}
|
||||||
icon={<FormOutlined />}
|
icon={<FormOutlined />}
|
||||||
onCreateBlockSchema={async ({ item }) => {
|
onCreateBlockSchema={async (options) => {
|
||||||
const { data } = await api.resource('collections.fields', item.name).list({ paginate: false });
|
if (createBlockSchema) {
|
||||||
const targetFields = getCollectionFields(item.name, item.dataSource);
|
return createBlockSchema(options);
|
||||||
const collectionFields = item.dataSource === 'main' ? data.data : targetFields;
|
}
|
||||||
const fields = collectionFields
|
createKanbanBlock(options);
|
||||||
?.filter((field) => ['select', 'radioGroup'].includes(field.interface))
|
|
||||||
?.map((field) => {
|
|
||||||
return {
|
|
||||||
label: field?.uiSchema?.title,
|
|
||||||
value: field.name,
|
|
||||||
uiSchema: {
|
|
||||||
...field.uiSchema,
|
|
||||||
name: field.name,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const sortFields = collectionFields
|
|
||||||
?.filter((field) => ['sort'].includes(field.interface))
|
|
||||||
?.map((field) => {
|
|
||||||
return {
|
|
||||||
label: field?.uiSchema?.title,
|
|
||||||
value: field.name,
|
|
||||||
scopeKey: field.scopeKey,
|
|
||||||
uiSchema: {
|
|
||||||
...field.uiSchema,
|
|
||||||
name: field.name,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const values = await FormDialog(
|
|
||||||
t('Create kanban block'),
|
|
||||||
<CreateKanbanForm
|
|
||||||
item={item}
|
|
||||||
sortFields={sortFields}
|
|
||||||
collectionFields={collectionFields}
|
|
||||||
fields={fields}
|
|
||||||
options={options}
|
|
||||||
api={api}
|
|
||||||
/>,
|
|
||||||
theme,
|
|
||||||
).open({
|
|
||||||
initialValues: {},
|
|
||||||
});
|
|
||||||
insert(
|
|
||||||
createKanbanBlockUISchema({
|
|
||||||
sortField: values.dragSortBy,
|
|
||||||
groupField: values.groupField.value,
|
|
||||||
collectionName: item.name,
|
|
||||||
dataSource: item.dataSource,
|
|
||||||
params: {
|
|
||||||
sort: [values.dragSortBy],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
|
onlyCurrentDataSource={onlyCurrentDataSource}
|
||||||
|
hideSearch={hideSearch}
|
||||||
|
filter={filterCollections}
|
||||||
|
showAssociationFields={showAssociationFields}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useCreateKanbanBlock = () => {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { getCollectionFields } = useCollectionManager_deprecated();
|
||||||
|
const options = useContext(SchemaOptionsContext);
|
||||||
|
const { theme } = useGlobalTheme();
|
||||||
|
const api = useAPIClient();
|
||||||
|
const createKanbanBlock = async ({ item }) => {
|
||||||
|
console.log(item);
|
||||||
|
const collectionFields = getCollectionFields(item.name, item.dataSource);
|
||||||
|
const fields = collectionFields
|
||||||
|
?.filter((field) => ['select', 'radioGroup'].includes(field.interface))
|
||||||
|
?.map((field) => {
|
||||||
|
return {
|
||||||
|
label: field?.uiSchema?.title,
|
||||||
|
value: field.name,
|
||||||
|
uiSchema: {
|
||||||
|
...field.uiSchema,
|
||||||
|
name: field.name,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const sortFields = collectionFields
|
||||||
|
?.filter((field) => ['sort'].includes(field.interface))
|
||||||
|
?.map((field) => {
|
||||||
|
return {
|
||||||
|
label: field?.uiSchema?.title,
|
||||||
|
value: field.name,
|
||||||
|
scopeKey: field.scopeKey,
|
||||||
|
uiSchema: {
|
||||||
|
...field.uiSchema,
|
||||||
|
name: field.name,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const values = await FormDialog(
|
||||||
|
t('Create kanban block'),
|
||||||
|
<CreateKanbanForm
|
||||||
|
item={item}
|
||||||
|
sortFields={sortFields}
|
||||||
|
collectionFields={collectionFields}
|
||||||
|
fields={fields}
|
||||||
|
options={options}
|
||||||
|
api={api}
|
||||||
|
/>,
|
||||||
|
theme,
|
||||||
|
).open({
|
||||||
|
initialValues: {},
|
||||||
|
});
|
||||||
|
insert(
|
||||||
|
createKanbanBlockUISchema({
|
||||||
|
sortField: values.dragSortBy,
|
||||||
|
groupField: values.groupField.value,
|
||||||
|
collectionName: item.name,
|
||||||
|
dataSource: item.dataSource,
|
||||||
|
params: {
|
||||||
|
sort: [values.dragSortBy],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { createKanbanBlock };
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useCreateAssociationKanbanBlock() {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const options = useContext(SchemaOptionsContext);
|
||||||
|
const { theme } = useGlobalTheme();
|
||||||
|
const { getCollectionFields } = useCollectionManager_deprecated();
|
||||||
|
const api = useAPIClient();
|
||||||
|
|
||||||
|
const createAssociationKanbanBlock = async ({ item }) => {
|
||||||
|
console.log(item);
|
||||||
|
const field = item.associationField;
|
||||||
|
const collectionFields = getCollectionFields(item.name, item.dataSource);
|
||||||
|
const fields = collectionFields
|
||||||
|
?.filter((field) => ['select', 'radioGroup'].includes(field.interface))
|
||||||
|
?.map((field) => {
|
||||||
|
return {
|
||||||
|
label: field?.uiSchema?.title,
|
||||||
|
value: field.name,
|
||||||
|
uiSchema: {
|
||||||
|
...field.uiSchema,
|
||||||
|
name: field.name,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const sortFields = collectionFields
|
||||||
|
?.filter((field) => ['sort'].includes(field.interface))
|
||||||
|
?.map((field) => {
|
||||||
|
return {
|
||||||
|
label: field?.uiSchema?.title,
|
||||||
|
value: field.name,
|
||||||
|
scopeKey: field.scopeKey,
|
||||||
|
uiSchema: {
|
||||||
|
...field.uiSchema,
|
||||||
|
name: field.name,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const values = await FormDialog(
|
||||||
|
t('Create kanban block'),
|
||||||
|
<CreateKanbanForm
|
||||||
|
item={item}
|
||||||
|
sortFields={sortFields}
|
||||||
|
collectionFields={collectionFields}
|
||||||
|
fields={fields}
|
||||||
|
options={options}
|
||||||
|
api={api}
|
||||||
|
/>,
|
||||||
|
theme,
|
||||||
|
).open({
|
||||||
|
initialValues: {},
|
||||||
|
});
|
||||||
|
insert(
|
||||||
|
createKanbanBlockUISchema({
|
||||||
|
sortField: values.dragSortBy,
|
||||||
|
groupField: values.groupField.value,
|
||||||
|
association: `${field.collectionName}.${field.name}`,
|
||||||
|
dataSource: item.dataSource,
|
||||||
|
params: {
|
||||||
|
sort: [values.dragSortBy],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { createAssociationKanbanBlock };
|
||||||
|
}
|
||||||
|
@ -120,7 +120,8 @@ const useAssociationNames = (collection) => {
|
|||||||
|
|
||||||
export const KanbanBlockProvider = (props) => {
|
export const KanbanBlockProvider = (props) => {
|
||||||
const params = { ...props.params };
|
const params = { ...props.params };
|
||||||
const appends = useAssociationNames(props.collection);
|
console.log(props);
|
||||||
|
const appends = useAssociationNames(props.association || props.collection);
|
||||||
if (!Object.keys(params).includes('appends')) {
|
if (!Object.keys(params).includes('appends')) {
|
||||||
params['appends'] = appends;
|
params['appends'] = appends;
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,22 @@ import { ISchema } from '@formily/react';
|
|||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
export const createKanbanBlockUISchema = (options: {
|
export const createKanbanBlockUISchema = (options: {
|
||||||
collectionName: string;
|
|
||||||
groupField: string;
|
groupField: string;
|
||||||
sortField: string;
|
sortField: string;
|
||||||
dataSource: string;
|
dataSource: string;
|
||||||
params?: Record<string, any>;
|
params?: Record<string, any>;
|
||||||
|
collectionName?: string;
|
||||||
|
association?: string;
|
||||||
}): ISchema => {
|
}): ISchema => {
|
||||||
const { collectionName, groupField, sortField, dataSource, params } = options;
|
const { collectionName, groupField, sortField, dataSource, params, association } = options;
|
||||||
|
|
||||||
return {
|
const schema = {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-acl-action': `${collectionName}:list`,
|
'x-acl-action': `${association || collectionName}:list`,
|
||||||
'x-decorator': 'KanbanBlockProvider',
|
'x-decorator': 'KanbanBlockProvider',
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
collection: collectionName,
|
collection: collectionName,
|
||||||
|
dataSource,
|
||||||
action: 'list',
|
action: 'list',
|
||||||
groupField,
|
groupField,
|
||||||
sortField,
|
sortField,
|
||||||
@ -32,7 +34,6 @@ export const createKanbanBlockUISchema = (options: {
|
|||||||
paginate: false,
|
paginate: false,
|
||||||
...params,
|
...params,
|
||||||
},
|
},
|
||||||
dataSource,
|
|
||||||
},
|
},
|
||||||
// 'x-designer': 'Kanban.Designer',
|
// 'x-designer': 'Kanban.Designer',
|
||||||
'x-toolbar': 'BlockSchemaToolbar',
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
@ -122,4 +123,8 @@ export const createKanbanBlockUISchema = (options: {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
if (association) {
|
||||||
|
schema['x-decorator-props']['association'] = association;
|
||||||
|
}
|
||||||
|
return schema;
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,11 @@ import { KanbanCardViewer } from './Kanban.CardViewer';
|
|||||||
import { KanbanDesigner } from './Kanban.Designer';
|
import { KanbanDesigner } from './Kanban.Designer';
|
||||||
import { kanbanSettings } from './Kanban.Settings';
|
import { kanbanSettings } from './Kanban.Settings';
|
||||||
import { kanbanActionInitializers, kanbanActionInitializers_deprecated } from './KanbanActionInitializers';
|
import { kanbanActionInitializers, kanbanActionInitializers_deprecated } from './KanbanActionInitializers';
|
||||||
import { KanbanBlockInitializer } from './KanbanBlockInitializer';
|
import {
|
||||||
|
KanbanBlockInitializer,
|
||||||
|
useCreateAssociationKanbanBlock,
|
||||||
|
useCreateKanbanBlock,
|
||||||
|
} from './KanbanBlockInitializer';
|
||||||
import { KanbanBlockProvider, useKanbanBlockProps } from './KanbanBlockProvider';
|
import { KanbanBlockProvider, useKanbanBlockProps } from './KanbanBlockProvider';
|
||||||
|
|
||||||
Kanban.Card = KanbanCard;
|
Kanban.Card = KanbanCard;
|
||||||
@ -53,6 +57,32 @@ class PluginKanbanClient extends Plugin {
|
|||||||
title: '{{t("Kanban")}}',
|
title: '{{t("Kanban")}}',
|
||||||
Component: 'KanbanBlockInitializer',
|
Component: 'KanbanBlockInitializer',
|
||||||
});
|
});
|
||||||
|
this.app.schemaInitializerManager.addItem('popup:common:addBlock', 'dataBlocks.kanban', {
|
||||||
|
title: '{{t("Kanban")}}',
|
||||||
|
Component: 'KanbanBlockInitializer',
|
||||||
|
useComponentProps() {
|
||||||
|
const { createAssociationKanbanBlock } = useCreateAssociationKanbanBlock();
|
||||||
|
const { createKanbanBlock } = useCreateKanbanBlock();
|
||||||
|
|
||||||
|
return {
|
||||||
|
onlyCurrentDataSource: true,
|
||||||
|
filterCollections({ associationField }) {
|
||||||
|
if (associationField) {
|
||||||
|
return ['hasMany', 'belongsToMany'].includes(associationField.type);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
createBlockSchema: ({ item, fromOthersInPopup }) => {
|
||||||
|
if (fromOthersInPopup) {
|
||||||
|
return createKanbanBlock({ item });
|
||||||
|
}
|
||||||
|
createAssociationKanbanBlock({ item });
|
||||||
|
},
|
||||||
|
showAssociationFields: true,
|
||||||
|
hideSearch: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user