diff --git a/packages/client/package.json b/packages/client/package.json
index b5c306004..a33aed155 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -30,6 +30,7 @@
"i18next": "^21.6.0",
"marked": "^4.0.12",
"react-beautiful-dnd": "^13.1.0",
+ "react-big-calendar": "^0.38.7",
"react-helmet": "^6.1.0",
"react-i18next": "^11.15.1",
"react-image-lightbox": "^5.1.4",
diff --git a/packages/client/src/api-client/hooks/useRequest.ts b/packages/client/src/api-client/hooks/useRequest.ts
index 445d53899..be7e23a42 100644
--- a/packages/client/src/api-client/hooks/useRequest.ts
+++ b/packages/client/src/api-client/hooks/useRequest.ts
@@ -25,7 +25,7 @@ export function useRequest
(
const result = useReq(service, {
...options,
onSuccess(...args) {
- options.onSuccess && options.onSuccess(...args);
+ options.onSuccess?.(...args);
if (options.uid) {
api.services[options.uid] = result;
}
@@ -49,7 +49,7 @@ export function useRequest
(
{
...options,
onSuccess(...args) {
- options.onSuccess && options.onSuccess(...args);
+ options.onSuccess?.(...args);
if (options.uid) {
api.services[options.uid] = result;
}
diff --git a/packages/client/src/collection-manager/hooks/useCollectionField.ts b/packages/client/src/collection-manager/hooks/useCollectionField.ts
index 3b91f3afe..cdb743818 100644
--- a/packages/client/src/collection-manager/hooks/useCollectionField.ts
+++ b/packages/client/src/collection-manager/hooks/useCollectionField.ts
@@ -12,7 +12,7 @@ export const useCollectionField = () => {
if (!ctx) {
return {} as any;
}
- const resourceName = `${ctx?.collectinName || collection?.name}.${ctx.name}`;
+ const resourceName = `${ctx?.collectionName || collection?.name}.${ctx.name}`;
const resource = api?.resource(resourceName, record[ctx.sourceKey]);
return {
...ctx,
diff --git a/packages/client/src/collection-manager/types.ts b/packages/client/src/collection-manager/types.ts
index c010d4a2b..1859999db 100644
--- a/packages/client/src/collection-manager/types.ts
+++ b/packages/client/src/collection-manager/types.ts
@@ -28,8 +28,7 @@ export interface ICollectionProviderProps {
export interface CollectionFieldOptions {
name?: any;
- collectinName?: string;
- targetKey?: string;
+ collectionName?: string;
sourceKey?: string; // association field
uiSchema?: ISchema;
}
diff --git a/packages/client/src/locale/en_US.json b/packages/client/src/locale/en_US.json
index 2edf91151..b60a07cae 100644
--- a/packages/client/src/locale/en_US.json
+++ b/packages/client/src/locale/en_US.json
@@ -276,5 +276,6 @@
"Purple": "Purple",
"Default": "Default",
"Add card": "Add card",
- "edit title": "Edit Title"
+ "edit title": "Edit Title",
+ "turn page": "Turn Page"
}
diff --git a/packages/client/src/locale/zh_CN.json b/packages/client/src/locale/zh_CN.json
index d460ed8bd..4895c9597 100644
--- a/packages/client/src/locale/zh_CN.json
+++ b/packages/client/src/locale/zh_CN.json
@@ -276,5 +276,6 @@
"Default": "默认",
"Add card": "添加卡片",
- "edit title": "修改标题"
+ "edit title": "修改标题",
+ "turn page": "翻页"
}
diff --git a/packages/client/src/schema-component/antd/calendar/ActionInitializer.tsx b/packages/client/src/schema-component/antd/calendar/ActionInitializer.tsx
new file mode 100644
index 000000000..7e898c566
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/ActionInitializer.tsx
@@ -0,0 +1,163 @@
+import { observer, useFieldSchema } from '@formily/react';
+import { Switch } from 'antd';
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import { useCollection } from '../../../collection-manager';
+import { SchemaInitializer } from '../../../schema-initializer';
+import { useDesignable } from '../../hooks';
+
+export const ActionInitializer = observer((props: any) => {
+ const { name: collectName } = useCollection();
+ const { t } = useTranslation();
+ return (
+
+ Configure actions
+
+ );
+});
+
+const useCurrentActionSchema = (action: string) => {
+ const fieldSchema = useFieldSchema();
+ const { remove } = useDesignable();
+ const schema: any = fieldSchema.reduceProperties((buf, s) => {
+ if (s['x-action'] === action) {
+ return s;
+ }
+ return buf;
+ });
+ return {
+ schema,
+ exists: !!schema,
+ remove() {
+ schema && remove(schema);
+ },
+ };
+};
+
+const InitializeAction = SchemaInitializer.itemWrap((props) => {
+ const { item, insert } = props;
+ const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
+ return (
+ {
+ if (exists) {
+ return remove();
+ }
+ insert({
+ type: 'void',
+ 'x-component': 'Action',
+ ...item.schema,
+ });
+ }}
+ >
+
+ {item.title}
+
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar/Calendar.tsx b/packages/client/src/schema-component/antd/calendar/Calendar.tsx
index bf6aaf988..f1bbe5780 100644
--- a/packages/client/src/schema-component/antd/calendar/Calendar.tsx
+++ b/packages/client/src/schema-component/antd/calendar/Calendar.tsx
@@ -1,5 +1,185 @@
-import React from 'react';
+import { LeftOutlined, RightOutlined } from '@ant-design/icons';
+import { VoidField } from '@formily/core';
+import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
+import moment from 'moment';
+import React, { useState } from 'react';
+import { Calendar as BigCalendar, momentLocalizer } from 'react-big-calendar';
+import * as dates from 'react-big-calendar/lib/utils/dates';
+import { useTranslation } from 'react-i18next';
+import { SchemaComponent } from '../..';
+import { AsyncDataProvider, useRequest } from '../../../';
+import { i18n } from '../../../i18n';
+import { ActionBar, ActionContext } from '../action';
+import { ActionInitializer } from './ActionInitializer';
+import { CalendarContext, ToolbarContext } from './context';
+import { Event } from './Event';
+import { Filter } from './Filter';
+import { FooterActionInitializer } from './FooterActionInitializer';
+import { Nav } from './Nav';
+import './style.less';
+import { Title } from './Title';
+import { Today } from './Today';
+import type { ToolbarProps } from './types';
+import { toEvents } from './utils';
+import { ViewSelect } from './ViewSelect';
-export const Calendar = () => {
- return
Calendar
;
+const localizer = momentLocalizer(moment);
+
+function Toolbar(props: ToolbarProps) {
+ const fieldSchema = useFieldSchema();
+ const toolBarSchema: Schema = fieldSchema.reduceProperties((buf, current) => {
+ if (current['x-component'] === 'Calendar.ActionBar') {
+ return current;
+ }
+ return buf;
+ }, null);
+
+ return (
+
+
+
+ );
+}
+
+const messages: any = {
+ allDay: '',
+ previous: (
+
+
+
+ ),
+ next: (
+
+
+
+ ),
+ today: i18n.t('Today'),
+ month: i18n.t('Month'),
+ week: i18n.t('Week'),
+ work_week: i18n.t('Work week'),
+ day: i18n.t('Day'),
+ agenda: i18n.t('Agenda'),
+ date: i18n.t('Date'),
+ time: i18n.t('Time'),
+ event: i18n.t('Event'),
+ noEventsInRange: i18n.t('None'),
+ showMore: (count) => i18n.t('{{count}} more items', { count }),
};
+
+const useRequestProps = (props) => {
+ const { request, dataSource } = props;
+ if (request) {
+ return request;
+ }
+ return (params: any = {}) => {
+ return Promise.resolve({
+ data: dataSource,
+ });
+ };
+};
+
+const useDefDataSource = (props, options) => {
+ return useRequest(useRequestProps(props), options);
+};
+
+export const Calendar: any = observer((props: any) => {
+ const {
+ useDataSource = useDefDataSource,
+ fieldNames = {
+ id: 'id',
+ title: 'title',
+ start: 'start',
+ end: 'end',
+ },
+ } = props;
+ const { t } = useTranslation();
+
+ const field = useField();
+ const fieldSchema = useFieldSchema();
+
+ const [visible, setVisible] = useState(false);
+ const [record, setRecord] = useState({});
+ const result = useDataSource(props, {
+ uid: fieldSchema['x-uid'],
+ refreshDeps: [props.dataSource],
+ onSuccess(data) {
+ return toEvents(data?.data, fieldNames);
+ },
+ });
+ const eventSchema: Schema = fieldSchema.reduceProperties((buf, current) => {
+ if (current['x-component'] === 'Calendar.Event') {
+ return current;
+ }
+ return buf;
+ }, null);
+
+ return (
+
+
+
+
+
+
+
{
+ console.log('onSelectSlot', slotInfo);
+ }}
+ onDoubleClickEvent={(event) => {
+ console.log('onDoubleClickEvent');
+ }}
+ onSelectEvent={(event) => {
+ const record = result?.data?.data?.find((item) => item.id === event.id);
+ if (!record) {
+ return;
+ }
+ setRecord(record);
+ setVisible(true);
+ console.log('onSelectEvent');
+ }}
+ formats={{
+ monthHeaderFormat: 'Y-M',
+ agendaDateFormat: 'M-DD',
+ dayHeaderFormat: 'Y-M-DD',
+ dayRangeHeaderFormat: ({ start, end }, culture, local) => {
+ if (dates.eq(start, end, 'month')) {
+ return local.format(start, 'Y-M', culture);
+ }
+ return `${local.format(start, 'Y-M', culture)} - ${local.format(end, 'Y-M', culture)}`;
+ },
+ }}
+ defaultDate={new Date()}
+ components={{
+ toolbar: Toolbar,
+ }}
+ localizer={localizer}
+ />
+
+
+
+ );
+});
+
+Calendar.ActionInitializer = ActionInitializer;
+
+Calendar.FooterActionInitializer = FooterActionInitializer;
+
+Calendar.ActionBar = ActionBar;
+
+Calendar.Event = Event;
+
+Calendar.Title = Title;
+
+Calendar.Today = Today;
+
+Calendar.Nav = Nav;
+
+Calendar.ViewSelect = ViewSelect;
+
+Calendar.Filter = Filter;
diff --git a/packages/client/src/schema-component/antd/calendar/Event.tsx b/packages/client/src/schema-component/antd/calendar/Event.tsx
new file mode 100644
index 000000000..73a9f26f6
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/Event.tsx
@@ -0,0 +1,6 @@
+import { observer } from '@formily/react';
+import React from 'react';
+
+export const Event = observer((props) => {
+ return <>{props.children}>;
+});
diff --git a/packages/client/src/schema-component/antd/calendar/Filter.tsx b/packages/client/src/schema-component/antd/calendar/Filter.tsx
new file mode 100644
index 000000000..437868425
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/Filter.tsx
@@ -0,0 +1,69 @@
+import { FormButtonGroup, Submit } from '@formily/antd';
+import { createForm } from '@formily/core';
+import { FormProvider, observer, useFieldSchema } from '@formily/react';
+import { Button, Popover } from 'antd';
+import flatten from 'flat';
+import React, { useMemo, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { SchemaComponent } from '../../core';
+import { useDesignable } from '../../hooks';
+import { useCompile } from '../../hooks/useCompile';
+import { IconPicker } from '../icon-picker';
+
+export const Filter = observer((props: any) => {
+ const { t } = useTranslation();
+ const compile = useCompile();
+ const { properties } = props;
+ const { DesignableBar } = useDesignable();
+ const filedSchema = useFieldSchema();
+ const form = useMemo(() => createForm(), []);
+ const [visible, setVisible] = useState(false);
+ const obj = flatten(form.values.filter || {});
+ const count = Object.values(obj).filter((i) => (Array.isArray(i) ? i.length : i)).length;
+ const icon = props.icon || 'FilterOutlined';
+ filedSchema.mapProperties((p) => {
+ properties[p.name] = p;
+ });
+ return (
+
+
+
+
+ {
+ const { filter } = form.values;
+ console.log('Table.Filter', form.values);
+ setVisible(false);
+ }}
+ >
+ {t('Submit')}
+
+
+
+
+ }
+ >
+ }>
+ {count > 0 ? t('{{count}} filter items', { count }) : compile(filedSchema.title)}
+
+
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar/FooterActionInitializer.tsx b/packages/client/src/schema-component/antd/calendar/FooterActionInitializer.tsx
new file mode 100644
index 000000000..5696763c2
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/FooterActionInitializer.tsx
@@ -0,0 +1,138 @@
+import { observer, useFieldSchema } from '@formily/react';
+import { Switch } from 'antd';
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import { useCollection } from '../../../collection-manager';
+import { SchemaInitializer } from '../../../schema-initializer';
+import { useDesignable } from '../../hooks';
+
+export const FooterActionInitializer = observer((props: any) => {
+ const { name: collectName } = useCollection();
+ const { t } = useTranslation();
+ return (
+
+ Configure actions
+
+ );
+});
+
+const useCurrentActionSchema = (action: string) => {
+ const fieldSchema = useFieldSchema();
+ const { remove } = useDesignable();
+ const schema: any = fieldSchema.reduceProperties((buf, s) => {
+ if (s['x-action'] === action) {
+ return s;
+ }
+ return buf;
+ });
+ return {
+ schema,
+ exists: !!schema,
+ remove() {
+ schema && remove(schema);
+ },
+ };
+};
+
+const InitializeAction = SchemaInitializer.itemWrap((props) => {
+ const { item, insert } = props;
+ const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
+ return (
+ {
+ if (exists) {
+ return remove();
+ }
+ insert({
+ type: 'void',
+ 'x-component': 'Action',
+ ...item.schema,
+ });
+ }}
+ >
+
+ {item.title}
+
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar/Nav.tsx b/packages/client/src/schema-component/antd/calendar/Nav.tsx
new file mode 100644
index 000000000..e041b6d87
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/Nav.tsx
@@ -0,0 +1,19 @@
+import { LeftOutlined, RightOutlined } from '@ant-design/icons';
+import { observer } from '@formily/react';
+import { Button } from 'antd';
+import React, { useContext } from 'react';
+import { navigate } from 'react-big-calendar/lib/utils/constants';
+import { useDesignable } from '../../hooks';
+import { ToolbarContext } from './context';
+
+export const Nav = observer((props) => {
+ const { DesignableBar } = useDesignable();
+ const { onNavigate } = useContext(ToolbarContext);
+ return (
+
+ } onClick={() => onNavigate(navigate.PREVIOUS)}>
+ } onClick={() => onNavigate(navigate.NEXT)}>
+
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar/Title.tsx b/packages/client/src/schema-component/antd/calendar/Title.tsx
new file mode 100644
index 000000000..1b63431a8
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/Title.tsx
@@ -0,0 +1,15 @@
+import { observer } from '@formily/react';
+import React, { useContext } from 'react';
+import { useDesignable } from '../../hooks';
+import { ToolbarContext } from './context';
+
+export const Title = observer(() => {
+ const { DesignableBar } = useDesignable();
+ const { label } = useContext(ToolbarContext);
+ return (
+
+ {label}
+
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar/Today.tsx b/packages/client/src/schema-component/antd/calendar/Today.tsx
new file mode 100644
index 000000000..07c6cde45
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/Today.tsx
@@ -0,0 +1,23 @@
+import { observer } from '@formily/react';
+import { Button } from 'antd';
+import React, { useContext } from 'react';
+import { navigate } from 'react-big-calendar/lib/utils/constants';
+import { useTranslation } from 'react-i18next';
+import { useDesignable } from '../../hooks';
+import { ToolbarContext } from './context';
+
+export const Today = observer((props) => {
+ const { DesignableBar } = useDesignable();
+ const { onNavigate } = useContext(ToolbarContext);
+ const { t } = useTranslation();
+ return (
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar/ViewSelect.tsx b/packages/client/src/schema-component/antd/calendar/ViewSelect.tsx
new file mode 100644
index 000000000..5f484a37d
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/ViewSelect.tsx
@@ -0,0 +1,25 @@
+import { observer } from '@formily/react';
+import { Select } from 'antd';
+import React, { useContext } from 'react';
+import { useDesignable } from '../../hooks';
+import { ToolbarContext } from './context';
+
+export const ViewSelect = observer((props) => {
+ const { DesignableBar } = useDesignable();
+ const {
+ views,
+ view,
+ onView,
+ localizer: { messages },
+ } = useContext(ToolbarContext);
+ return (
+
+
+
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar/context.ts b/packages/client/src/schema-component/antd/calendar/context.ts
new file mode 100644
index 000000000..c1ce53153
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/context.ts
@@ -0,0 +1,5 @@
+import { createContext } from 'react';
+import type { ToolbarProps } from './types';
+
+export const ToolbarContext = createContext(null);
+export const CalendarContext = createContext(null);
diff --git a/packages/client/src/schema-component/antd/calendar/demos/apiClient.ts b/packages/client/src/schema-component/antd/calendar/demos/apiClient.ts
new file mode 100644
index 000000000..01d1bbb51
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/demos/apiClient.ts
@@ -0,0 +1,34 @@
+import { uid } from '@formily/shared';
+import { APIClient } from '@nocobase/client';
+import MockAdapter from 'axios-mock-adapter';
+import _ from 'lodash';
+
+export const apiClient = new APIClient();
+
+const mock = new MockAdapter(apiClient.axios);
+
+const sleep = (value: number) => new Promise((resolve) => setTimeout(resolve, value));
+
+mock.onGet('/tasks:list').reply(async (config) => {
+ // const [{ pageSize }] = config.params;
+ const pageSize = config.params.pageSize || 10;
+ const page = config.params.page || 1;
+ console.log(pageSize, page, config.params);
+ await sleep(1000);
+ return [
+ 200,
+ {
+ data: _.range(pageSize).map((v) => {
+ const month = Math.floor(Math.random() * 10 + 1);
+ const startDate = Math.floor(Math.random() * 20 + 1);
+ const endDate = startDate + Math.floor(Math.random() * 2 + 1);
+ return {
+ id: v + (page - 1) * pageSize,
+ title: uid(),
+ start: new Date(2022, month, startDate),
+ end: new Date(2022, month, endDate),
+ };
+ }),
+ },
+ ];
+});
diff --git a/packages/client/src/schema-component/antd/calendar/demos/defaultValues.ts b/packages/client/src/schema-component/antd/calendar/demos/defaultValues.ts
new file mode 100644
index 000000000..ab3a2f736
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/demos/defaultValues.ts
@@ -0,0 +1,180 @@
+const now = new Date();
+
+export default [
+ {
+ id: 0,
+ title: 'All Day Event very long title',
+ allDay: true,
+ start: new Date(2015, 3, 0),
+ end: new Date(2015, 3, 1),
+ },
+ {
+ id: 1,
+ title: 'Long Event',
+ start: new Date(2015, 3, 7),
+ end: new Date(2015, 3, 10),
+ },
+
+ {
+ id: 2,
+ title: 'DTS STARTS',
+ start: new Date(2016, 2, 13, 0, 0, 0),
+ end: new Date(2016, 2, 20, 0, 0, 0),
+ },
+
+ {
+ id: 3,
+ title: 'DTS ENDS',
+ start: new Date(2016, 10, 6, 0, 0, 0),
+ end: new Date(2016, 10, 13, 0, 0, 0),
+ },
+
+ {
+ id: 4,
+ title: 'Some Event',
+ start: new Date(2015, 3, 9, 0, 0, 0),
+ end: new Date(2015, 3, 10, 0, 0, 0),
+ },
+ {
+ id: 5,
+ title: 'Conference',
+ start: new Date(2015, 3, 11),
+ end: new Date(2015, 3, 13),
+ desc: 'Big conference for important people',
+ },
+ {
+ id: 6,
+ title: 'Meeting',
+ start: new Date(2015, 3, 12, 10, 30, 0, 0),
+ end: new Date(2015, 3, 12, 12, 30, 0, 0),
+ desc: 'Pre-meeting meeting, to prepare for the meeting',
+ },
+ {
+ id: 7,
+ title: 'Lunch',
+ start: new Date(2015, 3, 12, 12, 0, 0, 0),
+ end: new Date(2015, 3, 12, 13, 0, 0, 0),
+ desc: 'Power lunch',
+ },
+ {
+ id: 8,
+ title: 'Meeting',
+ start: new Date(2015, 3, 12, 14, 0, 0, 0),
+ end: new Date(2015, 3, 12, 15, 0, 0, 0),
+ },
+ {
+ id: 9,
+ title: 'Happy Hour',
+ start: new Date(2015, 3, 12, 17, 0, 0, 0),
+ end: new Date(2015, 3, 12, 17, 30, 0, 0),
+ desc: 'Most important meal of the day',
+ },
+ {
+ id: 10,
+ title: 'Dinner',
+ start: new Date(2015, 3, 12, 20, 0, 0, 0),
+ end: new Date(2015, 3, 12, 21, 0, 0, 0),
+ },
+ {
+ id: 11,
+ title: 'Planning Meeting with Paige',
+ start: new Date(2015, 3, 13, 8, 0, 0),
+ end: new Date(2015, 3, 13, 10, 30, 0),
+ },
+ {
+ id: 11.1,
+ title: 'Inconvenient Conference Call',
+ start: new Date(2015, 3, 13, 9, 30, 0),
+ end: new Date(2015, 3, 13, 12, 0, 0),
+ },
+ {
+ id: 11.2,
+ title: "Project Kickoff - Lou's Shoes",
+ start: new Date(2015, 3, 13, 11, 30, 0),
+ end: new Date(2015, 3, 13, 14, 0, 0),
+ },
+ {
+ id: 11.3,
+ title: 'Quote Follow-up - Tea by Tina',
+ start: new Date(2015, 3, 13, 15, 30, 0),
+ end: new Date(2015, 3, 13, 16, 0, 0),
+ },
+ {
+ id: 12,
+ title: 'Late Night Event',
+ start: new Date(2015, 3, 17, 19, 30, 0),
+ end: new Date(2015, 3, 18, 2, 0, 0),
+ },
+ {
+ id: 12.5,
+ title: 'Late Same Night Event',
+ start: new Date(2015, 3, 17, 19, 30, 0),
+ end: new Date(2015, 3, 17, 23, 30, 0),
+ },
+ {
+ id: 13,
+ title: 'Multi-day Event',
+ start: new Date(2015, 3, 20, 19, 30, 0),
+ end: new Date(2015, 3, 22, 2, 0, 0),
+ },
+ {
+ id: 14,
+ title: 'Today',
+ start: new Date(new Date().setHours(new Date().getHours() - 3)),
+ end: new Date(new Date().setHours(new Date().getHours() + 3)),
+ },
+ {
+ id: 15,
+ title: 'Point in Time Event',
+ start: now,
+ end: now,
+ },
+ {
+ id: 16,
+ title: 'Video Record',
+ start: new Date(2015, 3, 14, 15, 30, 0),
+ end: new Date(2015, 3, 14, 19, 0, 0),
+ },
+ {
+ id: 17,
+ title: 'Dutch Song Producing',
+ start: new Date(2015, 3, 14, 16, 30, 0),
+ end: new Date(2015, 3, 14, 20, 0, 0),
+ },
+ {
+ id: 18,
+ title: 'Itaewon Halloween Meeting',
+ start: new Date(2015, 3, 14, 16, 30, 0),
+ end: new Date(2015, 3, 14, 17, 30, 0),
+ },
+ {
+ id: 19,
+ title: 'Online Coding Test',
+ start: new Date(2015, 3, 14, 17, 30, 0),
+ end: new Date(2015, 3, 14, 20, 30, 0),
+ },
+ {
+ id: 20,
+ title: 'An overlapped Event',
+ start: new Date(2015, 3, 14, 17, 0, 0),
+ end: new Date(2015, 3, 14, 18, 30, 0),
+ },
+ {
+ id: 21,
+ title: 'Phone Interview',
+ start: new Date(2015, 3, 14, 17, 0, 0),
+ end: new Date(2015, 3, 14, 18, 30, 0),
+ },
+ {
+ id: 22,
+ title: 'Cooking Class',
+ start: new Date(2015, 3, 14, 17, 30, 0),
+ end: new Date(2015, 3, 14, 19, 0, 0),
+ },
+ {
+ id: 23,
+ title: 'Go to the gym',
+ start: new Date(2015, 3, 14, 18, 30, 0),
+ end: new Date(2015, 3, 14, 20, 0, 0),
+ },
+];
diff --git a/packages/client/src/schema-component/antd/calendar/demos/demo1.tsx b/packages/client/src/schema-component/antd/calendar/demos/demo1.tsx
new file mode 100644
index 000000000..bbec2e4ca
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/demos/demo1.tsx
@@ -0,0 +1,282 @@
+/**
+ * title: Calendar
+ */
+
+import { useField, useFieldSchema, useForm } from '@formily/react';
+import { observable } from '@formily/reactive';
+import { uid } from '@formily/shared';
+import {
+ AntdSchemaComponentProvider,
+ CalendarContext,
+ CollectionProvider,
+ SchemaComponent,
+ SchemaComponentProvider,
+ SchemaInitializerProvider,
+ useActionContext,
+ useAsyncData,
+ useRequest,
+} from '@nocobase/client';
+import React, { useContext, useEffect } from 'react';
+import defaultValues from './defaultValues';
+
+const dataSource = observable(defaultValues);
+const schema = {
+ type: 'void',
+ name: 'calendar1',
+ 'x-component': 'Calendar',
+ 'x-component-props': {
+ dataSource: dataSource,
+ },
+ properties: {
+ toolBar: {
+ type: 'void',
+ 'x-component': 'Calendar.ActionBar',
+ 'x-action-initializer': 'Calendar.ActionInitializer',
+ properties: {
+ today: {
+ type: 'void',
+ title: '今天',
+ 'x-component': 'Calendar.Today',
+ 'x-action': 'calendar:today',
+ 'x-align': 'left',
+ },
+ nav: {
+ type: 'void',
+ title: '翻页',
+ 'x-component': 'Calendar.Nav',
+ 'x-action': 'calendar:nav',
+ 'x-align': 'left',
+ },
+ title: {
+ type: 'void',
+ title: '标题',
+ 'x-component': 'Calendar.Title',
+ 'x-action': 'calendar:title',
+ 'x-align': 'left',
+ },
+ viewSelect: {
+ type: 'void',
+ title: '视图切换',
+ 'x-component': 'Calendar.ViewSelect',
+ 'x-action': 'calendar:viewSelect',
+ 'x-align': 'right',
+ },
+ // filter: {
+ // type: 'void',
+ // title: '过滤',
+ // 'x-align': 'right',
+ // 'x-component': 'Calendar.Filter',
+ // 'x-action': 'calendar:filter',
+ // },
+ },
+ },
+ event: {
+ type: 'void',
+ name: 'event',
+ 'x-component': 'Calendar.Event',
+ properties: {
+ modal: {
+ 'x-component': 'Action.Drawer',
+ 'x-decorator': 'Form',
+ 'x-decorator-props': {
+ useValues: '{{ useValues }}',
+ },
+ type: 'void',
+ title: 'Drawer Title',
+ properties: {
+ grid: {
+ type: 'void',
+ 'x-component': 'Grid',
+ 'x-item-initializer': 'FormItemInitializer',
+ 'x-item-initializer-props': {
+ readPretty: true,
+ },
+ },
+ footer: {
+ 'x-component': 'Action.Drawer.Footer',
+ type: 'void',
+ properties: {
+ [uid()]: {
+ title: 'submit',
+ 'x-component': 'ActionBar',
+ 'x-action-initializer': 'Calendar.FooterActionInitializer',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+};
+
+const useSaveAction = () => {
+ const { setVisible } = useActionContext();
+ const { refresh: dataRefresh, data } = useAsyncData();
+ const form = useForm();
+ const fieldSchema = useFieldSchema();
+ const field = useField();
+ return {
+ async run() {
+ setVisible(false);
+ console.log(form.values, fieldSchema, field, data);
+ dataSource.push({ ...form.values, start: new Date(form.values.start), end: new Date(form.values.end) });
+ schema['x-component-props']['dataSource'] = dataSource;
+ dataRefresh();
+ form.reset();
+ },
+ };
+};
+
+const useCloseAction = () => {
+ const { setVisible } = useActionContext();
+ return {
+ async run() {
+ setVisible(false);
+ },
+ };
+};
+
+const useRemoveAction = () => {
+ const { refresh: dataRefresh, data } = useAsyncData();
+ const { record } = useContext(CalendarContext);
+ const { setVisible } = useActionContext();
+
+ return {
+ async run() {
+ setVisible(false);
+ for (let i = 0, iLen = dataSource.length; i < iLen; i++) {
+ const element = dataSource[i];
+ if (element.id === record.id) {
+ dataSource.splice(i, 1);
+ break;
+ }
+ }
+ schema['x-component-props']['dataSource'] = dataSource;
+ dataRefresh();
+ },
+ };
+};
+
+const useEditAction = () => {
+ const { refresh: dataRefresh, data } = useAsyncData();
+ const { record } = useContext(CalendarContext);
+ const { setVisible } = useActionContext();
+ return {
+ async run() {
+ console.log(record);
+ },
+ };
+};
+
+const useUpdateAction = () => {
+ const form = useForm();
+ const { setVisible } = useActionContext();
+ const { refresh: dataRefresh, data } = useAsyncData();
+ return {
+ async run() {
+ setVisible(false);
+ console.log(form.values);
+ const record = form.values;
+ for (let i = 0, iLen = dataSource.length; i < iLen; i++) {
+ const element = dataSource[i];
+ if (element.id === record.id) {
+ dataSource[i] = { ...record };
+ break;
+ }
+ }
+ schema['x-component-props']['dataSource'] = dataSource;
+ form.reset();
+ },
+ };
+};
+
+const useValues = (options) => {
+ const { visible } = useActionContext();
+ const { record } = useContext(CalendarContext);
+ const result = useRequest(
+ () =>
+ Promise.resolve({
+ data: {
+ ...record,
+ },
+ }),
+ { ...options, manual: true },
+ );
+ useEffect(() => {
+ if (visible) {
+ result.run();
+ }
+ }, [visible]);
+ return result;
+};
+
+const collection = {
+ name: 'tests',
+ fields: [
+ {
+ type: 'string',
+ name: 'id',
+ interface: 'input',
+ uiSchema: {
+ type: 'string',
+ 'x-component': 'Input',
+ },
+ },
+ {
+ type: 'string',
+ name: 'title',
+ interface: 'input',
+ uiSchema: {
+ type: 'string',
+ 'x-component': 'Input',
+ },
+ },
+ {
+ type: 'string',
+ name: 'start',
+ interface: 'datetime',
+ uiSchema: {
+ type: 'string',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY-MM-DD',
+ },
+ },
+ },
+ {
+ type: 'string',
+ name: 'end',
+ interface: 'datetime',
+ uiSchema: {
+ type: 'string',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY-MM-DD',
+ },
+ },
+ },
+ ],
+};
+export default () => {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/client/src/schema-component/antd/calendar/index.md b/packages/client/src/schema-component/antd/calendar/index.md
index c77d221b9..e56d63dad 100644
--- a/packages/client/src/schema-component/antd/calendar/index.md
+++ b/packages/client/src/schema-component/antd/calendar/index.md
@@ -5,4 +5,12 @@ group:
path: /schema-components
---
-# Calendar 待定
+# Calendar
+
+## Examples
+
+### Calendar
+
+#### Sync Data
+
+
diff --git a/packages/client/src/schema-component/antd/calendar/index.ts b/packages/client/src/schema-component/antd/calendar/index.ts
index a72338059..0d52e1336 100644
--- a/packages/client/src/schema-component/antd/calendar/index.ts
+++ b/packages/client/src/schema-component/antd/calendar/index.ts
@@ -1 +1,2 @@
export * from './Calendar';
+export * from './context';
diff --git a/packages/client/src/schema-component/antd/calendar/style.less b/packages/client/src/schema-component/antd/calendar/style.less
new file mode 100644
index 000000000..aefe3d99d
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/style.less
@@ -0,0 +1,767 @@
+@charset "UTF-8";
+.rbc-btn {
+ color: inherit;
+ font: inherit;
+ margin: 0;
+}
+
+button.rbc-btn {
+ overflow: visible;
+ text-transform: none;
+ -webkit-appearance: button;
+ cursor: pointer;
+}
+
+button[disabled].rbc-btn {
+ cursor: not-allowed;
+}
+
+button.rbc-input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+.rbc-calendar {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ height: 100%;
+}
+
+.rbc-calendar *,
+.rbc-calendar *:before,
+.rbc-calendar *:after {
+ box-sizing: inherit;
+}
+
+.rbc-abs-full,
+.rbc-row-bg {
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.rbc-ellipsis,
+.rbc-event-label,
+.rbc-row-segment .rbc-event-content,
+.rbc-show-more {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.rbc-rtl {
+ direction: rtl;
+}
+
+.rbc-off-range {
+ color: rgba(0, 0, 0, 0.25);
+}
+
+.rbc-off-range-bg {
+ // background: #e6e6e6;
+}
+
+.rbc-header {
+ // font-size: 16px;
+ overflow: hidden;
+ flex: 1 0 0%;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ padding: 4px 12px;
+ text-align: right;
+ vertical-align: middle;
+ // font-weight: 500;
+ min-height: 32px;
+ color: rgba(0, 0, 0, 0.85);
+ margin: 0 4px;
+ border-bottom: 2px solid #f0f0f0;
+}
+.rbc-header + .rbc-header {
+ // border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-header + .rbc-header {
+ border-left-width: 0;
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-header > a,
+.rbc-header > a:active,
+.rbc-header > a:visited {
+ color: inherit;
+ text-decoration: none;
+}
+
+.rbc-row-content {
+ position: relative;
+ user-select: none;
+ -webkit-user-select: none;
+ z-index: 4;
+}
+
+.rbc-row-content-scrollable {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+.rbc-row-content-scrollable .rbc-row-content-scroll-container {
+ height: 100%;
+ overflow-y: scroll;
+ /* Hide scrollbar for Chrome, Safari and Opera */
+ -ms-overflow-style: none;
+ /* IE and Edge */
+ scrollbar-width: none;
+ /* Firefox */
+}
+.rbc-row-content-scrollable .rbc-row-content-scroll-container::-webkit-scrollbar {
+ display: none;
+}
+
+.rbc-toolbar {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+ margin-bottom: 10px;
+ font-size: 16px;
+}
+.rbc-toolbar .rbc-toolbar-label {
+ flex-grow: 1;
+ padding: 0 10px;
+ text-align: center;
+}
+.rbc-toolbar button {
+ outline: none;
+ font-size: 14px;
+ line-height: 1.5715;
+ height: 32px;
+ color: #373a3c;
+ display: inline-block;
+ margin: 0;
+ position: relative;
+ z-index: 1;
+ text-align: center;
+ vertical-align: middle;
+ background: none;
+ background-image: none;
+ border: 1px solid #d9d9d9;
+ padding: 4px 15px;
+ border-radius: 2px;
+ line-height: normal;
+ white-space: nowrap;
+}
+.rbc-toolbar button:active,
+.rbc-toolbar button.rbc-active {
+ // background-image: none;
+ // box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ // background-color: #e6e6e6;
+ z-index: 2;
+ color: #1890ff;
+ border-color: #1890ff;
+}
+.rbc-toolbar button:active:hover,
+.rbc-toolbar button:active:focus,
+.rbc-toolbar button.rbc-active:hover,
+.rbc-toolbar button.rbc-active:focus {
+ // color: #373a3c;
+ // background-color: #d4d4d4;
+ // border-color: #8c8c8c;
+ z-index: 2;
+ color: #40a9ff;
+ border-color: #40a9ff;
+}
+// .rbc-toolbar button:focus {
+// color: #373a3c;
+// background-color: #e6e6e6;
+// border-color: #adadad; }
+.rbc-toolbar button:hover {
+ // color: #373a3c;
+ // background-color: #e6e6e6;
+ z-index: 2;
+ color: #40a9ff;
+ border-color: #40a9ff;
+}
+
+.rbc-btn-group {
+ display: inline-block;
+ white-space: nowrap;
+}
+.rbc-btn-group > button:first-child:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.rbc-btn-group > button:last-child:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.rbc-rtl .rbc-btn-group > button:first-child:not(:last-child) {
+ border-radius: 4px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.rbc-rtl .rbc-btn-group > button:last-child:not(:first-child) {
+ border-radius: 4px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.rbc-btn-group > button:not(:first-child):not(:last-child) {
+ border-radius: 0;
+}
+.rbc-btn-group button + button {
+ margin-left: -1px;
+}
+.rbc-rtl .rbc-btn-group button + button {
+ margin-left: 0;
+ margin-right: -1px;
+}
+.rbc-btn-group + .rbc-btn-group,
+.rbc-btn-group + button {
+ margin-left: 10px;
+}
+
+.rbc-event {
+ border: none;
+ box-sizing: border-box;
+ box-shadow: none;
+ margin: 0;
+ padding: 2px 5px;
+ background-color: rgba(240, 240, 240, 0.65);
+ border-radius: 2px;
+ // color: #1890ff;
+ cursor: pointer;
+ font-size: 12px;
+ width: 100%;
+ text-align: left;
+ &:hover {
+ background-color: #e6f7ff;
+ color: #1890ff;
+ }
+}
+.rbc-slot-selecting .rbc-event {
+ cursor: inherit;
+ pointer-events: none;
+}
+.rbc-event.rbc-selected {
+ background-color: #e6f7ff;
+ color: #1890ff;
+}
+.rbc-event:focus {
+ // outline: 5px auto #3b99fc;
+}
+
+.rbc-event-label {
+ font-size: 80%;
+}
+
+.rbc-event-overlaps {
+ box-shadow: -1px 1px 5px 0px rgba(51, 51, 51, 0.5);
+}
+
+.rbc-event-continues-prior {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.rbc-event-continues-after {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.rbc-event-continues-earlier {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.rbc-event-continues-later {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.rbc-row {
+ display: flex;
+ flex-direction: row;
+}
+
+.rbc-row-segment {
+ padding: 0 4px 1px 4px;
+}
+
+.rbc-selected-cell {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.rbc-show-more {
+ z-index: 4;
+ font-weight: bold;
+ font-size: 85%;
+ height: auto;
+ line-height: normal;
+ color: inherit;
+ padding: 2px 5px;
+}
+
+.rbc-month-view {
+ position: relative;
+ // border: 1px solid #f0f0f0;
+ display: flex;
+ flex-direction: column;
+ flex: 1 0 0;
+ width: 100%;
+ user-select: none;
+ -webkit-user-select: none;
+ height: 68vh;
+}
+
+.rbc-month-header {
+ display: flex;
+ flex-direction: row;
+}
+
+.rbc-month-row {
+ display: flex;
+ position: relative;
+ flex-direction: column;
+ flex: 1 0 0;
+ flex-basis: 0px;
+ overflow: hidden;
+ height: 100%;
+}
+.rbc-month-row + .rbc-month-row {
+ // border-top: 1px solid #f0f0f0;
+}
+
+.rbc-date-cell {
+ // cursor: pointer;
+ flex: 1 1 0;
+ min-width: 0;
+ padding-right: 12px;
+ padding-top: 4px;
+ text-align: right;
+}
+.rbc-date-cell.rbc-now a {
+ // font-weight: bold;
+ color: #1890ff;
+}
+.rbc-date-cell > a,
+.rbc-date-cell > a:active,
+.rbc-date-cell > a:visited {
+ color: inherit;
+ text-decoration: none;
+}
+.rbc-date-cell > a:hover {
+ color: #1890ff;
+}
+
+.rbc-row-bg {
+ display: flex;
+ flex-direction: row;
+ flex: 1 0 0;
+ overflow: hidden;
+}
+
+.rbc-day-bg {
+ flex: 1 0 0%;
+ // border-top: 2px solid #f0f0f0;
+ margin: 0 4px;
+ &:hover {
+ background: #f5f5f5;
+ }
+ // cursor: pointer;
+}
+
+.rbc-month-view {
+ .rbc-day-bg {
+ border-top: 2px solid #f0f0f0;
+ }
+ .rbc-today {
+ border-color: #1890ff !important;
+ background-color: #e6f7ff !important;
+ }
+ .rbc-header {
+ border-bottom: 0 !important;
+ }
+}
+
+.rbc-day-bg + .rbc-day-bg {
+ // margin-left: 8px;
+ // border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-day-bg + .rbc-day-bg {
+ // border-left-width: 0;
+ // border-right: 1px solid #f0f0f0;
+}
+
+.rbc-overlay {
+ position: absolute;
+ z-index: 5;
+ margin-top: 5px;
+ border-radius: 2px;
+ // border: 1px solid #e5e5e5;
+ background-color: #fff;
+ box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
+ padding: 12px 16px;
+}
+.rbc-overlay > * + * {
+ margin-top: 1px;
+}
+
+.rbc-overlay-header {
+ font-weight: 500;
+ min-height: 32px;
+ border-bottom: 1px solid #f0f0f0;
+ margin: -12px -16px 12px -16px;
+ padding: 5px 16px 4px;
+}
+
+.rbc-agenda-view {
+ display: flex;
+ flex-direction: column;
+ flex: 1 0 0;
+ overflow: auto;
+}
+.rbc-agenda-view table.rbc-agenda-table {
+ width: 100%;
+ border: 1px solid #f0f0f0;
+ border-spacing: 0;
+ border-collapse: collapse;
+}
+.rbc-agenda-view table.rbc-agenda-table tbody > tr > td {
+ padding: 5px 10px;
+ vertical-align: top;
+}
+.rbc-agenda-view table.rbc-agenda-table .rbc-agenda-time-cell {
+ padding-left: 15px;
+ padding-right: 15px;
+ text-transform: lowercase;
+}
+.rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td {
+ border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td {
+ border-left-width: 0;
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-agenda-view table.rbc-agenda-table tbody > tr + tr {
+ border-top: 1px solid #f0f0f0;
+}
+.rbc-agenda-view table.rbc-agenda-table thead > tr > th {
+ padding: 3px 5px;
+ text-align: left;
+ border-bottom: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-agenda-view table.rbc-agenda-table thead > tr > th {
+ text-align: right;
+}
+
+.rbc-agenda-time-cell {
+ text-transform: lowercase;
+}
+.rbc-agenda-time-cell .rbc-continues-after:after {
+ content: ' »';
+}
+.rbc-agenda-time-cell .rbc-continues-prior:before {
+ content: '« ';
+}
+
+.rbc-agenda-date-cell,
+.rbc-agenda-time-cell {
+ white-space: nowrap;
+}
+
+.rbc-agenda-event-cell {
+ width: 100%;
+}
+
+.rbc-time-column {
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+}
+.rbc-time-column .rbc-timeslot-group {
+ flex: 1;
+}
+
+.rbc-timeslot-group {
+ border-bottom: 1px solid #f0f0f0;
+ min-height: 40px;
+ line-height: 39px;
+ display: flex;
+ flex-flow: column nowrap;
+ // cursor: pointer;
+ &:hover {
+ background: #f0f0f0;
+ }
+}
+
+.rbc-time-gutter,
+.rbc-header-gutter {
+ flex: none;
+}
+
+.rbc-label {
+ padding: 0 5px;
+}
+
+.rbc-day-slot {
+ position: relative;
+}
+.rbc-day-slot .rbc-events-container {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ right: 0;
+ margin-right: 10px;
+ top: 0;
+}
+.rbc-day-slot .rbc-events-container.rbc-rtl {
+ left: 10px;
+ right: 0;
+}
+.rbc-day-slot .rbc-event {
+ border: 1px solid #265985;
+ display: flex;
+ max-height: 100%;
+ min-height: 20px;
+ flex-flow: column wrap;
+ align-items: flex-start;
+ overflow: hidden;
+ position: absolute;
+}
+.rbc-day-slot .rbc-event-label {
+ flex: none;
+ padding-right: 5px;
+ width: auto;
+}
+.rbc-day-slot .rbc-event-content {
+ width: 100%;
+ flex: 1 1 0;
+ word-wrap: break-word;
+ line-height: 1;
+ height: 100%;
+ min-height: 1em;
+}
+.rbc-day-slot .rbc-time-slot {
+ // border-top: 1px solid #f7f7f7;
+}
+.rbc-time-header-gutter {
+ line-height: 40px;
+}
+.rbc-time-header-cell {
+ min-height: 32px !important;
+}
+.rbc-calendar.view-week {
+ .rbc-time-header-cell {
+ margin-top: -32px;
+ margin-bottom: 4px;
+ }
+ .rbc-time-view {
+ padding-top: 32px;
+ border-top: 0;
+ }
+ .rbc-header {
+ padding: 4px 8px;
+ }
+ .rbc-time-header-content {
+ padding: 4px 0;
+ }
+ .rbc-time-header-gutter {
+ padding: 0;
+ padding-top: 2px;
+ > div {
+ border-top: 2px solid #f0f0f0;
+ padding: 0 5px;
+ }
+ }
+}
+.rbc-time-view-resources .rbc-time-gutter,
+.rbc-time-view-resources .rbc-time-header-gutter {
+ position: sticky;
+ left: 0;
+ background-color: white;
+ border-right: 1px solid #f0f0f0;
+ z-index: 10;
+ margin-right: -1px;
+}
+
+.rbc-time-view-resources .rbc-time-header {
+ overflow: hidden;
+}
+
+.rbc-time-view-resources .rbc-time-header-content {
+ min-width: auto;
+ flex: 1 0 0;
+ flex-basis: 0px;
+}
+
+.rbc-time-view-resources .rbc-time-header-cell-single-day {
+ display: none;
+}
+
+.rbc-time-view-resources .rbc-day-slot {
+ min-width: 140px;
+}
+
+.rbc-time-view-resources .rbc-header,
+.rbc-time-view-resources .rbc-day-bg {
+ width: 140px;
+ flex: 1 1 0;
+ flex-basis: 0 px;
+}
+
+.rbc-time-header-content + .rbc-time-header-content {
+ margin-left: -1px;
+}
+
+.rbc-time-slot {
+ flex: 1 0 0;
+}
+.rbc-time-slot.rbc-now {
+ font-weight: bold;
+}
+
+.rbc-day-header {
+ text-align: center;
+}
+
+.rbc-slot-selection {
+ z-index: 10;
+ position: absolute;
+ background-color: rgba(0, 0, 0, 0.5);
+ color: white;
+ font-size: 75%;
+ width: 100%;
+ padding: 3px;
+}
+
+.rbc-slot-selecting {
+ cursor: move;
+}
+
+.rbc-time-view {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ width: 100%;
+ border-top: 1px solid #f0f0f0;
+ min-height: 0;
+}
+.rbc-time-view .rbc-time-gutter {
+ white-space: nowrap;
+}
+.rbc-time-view .rbc-allday-cell {
+ box-sizing: content-box;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.rbc-time-view .rbc-allday-cell + .rbc-allday-cell {
+ border-left: 1px solid #f0f0f0;
+}
+.rbc-time-view .rbc-allday-events {
+ position: relative;
+ z-index: 4;
+}
+.rbc-time-view .rbc-row {
+ box-sizing: border-box;
+ min-height: 20px;
+}
+
+.rbc-time-header {
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: row;
+}
+.rbc-time-header.rbc-overflowing {
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-time-header.rbc-overflowing {
+ border-right-width: 0;
+ border-left: 1px solid #f0f0f0;
+}
+.rbc-time-header > .rbc-row:first-child {
+ border-bottom: 1px solid #f0f0f0;
+}
+.rbc-time-header > .rbc-row.rbc-row-resource {
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.rbc-time-header-cell-single-day {
+ display: none;
+}
+
+.rbc-time-header-content {
+ flex: 1;
+ display: flex;
+ min-width: 0;
+ flex-direction: column;
+ // border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-time-header-content {
+ border-left-width: 0;
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-time-header-content > .rbc-row.rbc-row-resource {
+ border-bottom: 1px solid #f0f0f0;
+ flex-shrink: 0;
+}
+
+.rbc-time-content {
+ display: flex;
+ flex: 1 0 0%;
+ align-items: flex-start;
+ width: 100%;
+ border-top: 1px solid #f0f0f0;
+ overflow-y: auto;
+ position: relative;
+}
+.rbc-time-content > .rbc-time-gutter {
+ flex: none;
+}
+.rbc-time-content > * + * > * {
+ // border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-time-content > * + * > * {
+ border-left-width: 0;
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-time-content > .rbc-day-slot {
+ width: 100%;
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+.rbc-current-time-indicator {
+ position: absolute;
+ z-index: 3;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background-color: #74ad31;
+ pointer-events: none;
+}
+
+@media only screen and (max-width: 800px) {
+ .view-type-calendar .action-buttons {
+ position: relative !important;
+ left: 0 !important;
+ .filter-action-button {
+ left: 0 !important;
+ }
+ }
+ .rbc-toolbar .rbc-toolbar-label {
+ position: absolute;
+ top: 28px;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+ .rbc-toolbar {
+ justify-content: space-between !important;
+ }
+ .rbc-header {
+ padding: 4px !important;
+ }
+}
diff --git a/packages/client/src/schema-component/antd/calendar/types.d.ts b/packages/client/src/schema-component/antd/calendar/types.d.ts
new file mode 100644
index 000000000..d3f3470d3
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/types.d.ts
@@ -0,0 +1,8 @@
+export interface ToolbarProps {
+ localizer?: any;
+ label?: any;
+ view?: any;
+ views?: any;
+ onNavigate?: (action: string) => void;
+ onView?: (view: string) => void;
+}
diff --git a/packages/client/src/schema-component/antd/calendar/utils.ts b/packages/client/src/schema-component/antd/calendar/utils.ts
new file mode 100644
index 000000000..255eb53d1
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar/utils.ts
@@ -0,0 +1,43 @@
+import { get } from 'lodash';
+import { i18n } from '../../../i18n';
+
+export const fieldsToFilterColumns = (fields: any[], options: any = {}) => {
+ // const { fieldNames = [] } = options;
+ // const properties = {};
+ // fields.forEach((field, index) => {
+ // if (fieldNames?.length && !fieldNames.includes(field.name)) {
+ // return;
+ // }
+ // const fieldOption = interfaces.get(field.interface);
+ // if (!fieldOption?.operations) {
+ // return;
+ // }
+ // properties[`column${index}`] = {
+ // type: 'void',
+ // title: field?.uiSchema?.title,
+ // 'x-component': 'Filter.Column',
+ // 'x-component-props': {
+ // operations: fieldOption.operations,
+ // },
+ // properties: {
+ // [field.name]: {
+ // ...field.uiSchema,
+ // 'x-decorator': 'FormilyFormItem',
+ // title: null,
+ // },
+ // },
+ // };
+ // });
+ // return properties;
+};
+
+export const toEvents = (data: any[], fieldNames: any) => {
+ return data?.map((item) => {
+ return {
+ id: get(item, fieldNames.id || 'id'),
+ title: get(item, fieldNames.title) || i18n.t('Untitle'),
+ start: new Date(get(item, fieldNames.start)),
+ end: new Date(get(item, fieldNames.end || fieldNames.start)),
+ };
+ });
+};
diff --git a/packages/client/src/schema-component/core/SchemaComponent.tsx b/packages/client/src/schema-component/core/SchemaComponent.tsx
index 576a66411..fce2913f6 100644
--- a/packages/client/src/schema-component/core/SchemaComponent.tsx
+++ b/packages/client/src/schema-component/core/SchemaComponent.tsx
@@ -1,6 +1,5 @@
-import { ISchemaFieldProps, RecursionField, Schema } from '@formily/react';
-import React, { useContext, useMemo } from 'react';
-import { SchemaComponentContext } from '../context';
+import { IRecursionFieldProps, ISchemaFieldProps, RecursionField, Schema } from '@formily/react';
+import React, { useMemo } from 'react';
import { SchemaComponentOptions } from './SchemaComponentOptions';
function toSchema(schema?: any) {
@@ -37,7 +36,7 @@ const MemoizedSchemaComponent = (props: ISchemaFieldProps) => {
return ;
};
-export const SchemaComponent = (props: ISchemaFieldProps & { memoized?: boolean }) => {
+export const SchemaComponent = (props: (ISchemaFieldProps | IRecursionFieldProps) & { memoized?: boolean }) => {
const { memoized, ...others } = props;
if (memoized) {
return ;
diff --git a/packages/client/src/schema-initializer/Initializers/FormItemInitializer/index.tsx b/packages/client/src/schema-initializer/Initializers/FormItemInitializer/index.tsx
index 4ee101907..eeb66d9cb 100644
--- a/packages/client/src/schema-initializer/Initializers/FormItemInitializer/index.tsx
+++ b/packages/client/src/schema-initializer/Initializers/FormItemInitializer/index.tsx
@@ -7,7 +7,7 @@ import { SchemaInitializer, SchemaInitializerItemOptions } from '../..';
import { useCollection } from '../../../collection-manager';
import { useDesignable } from '../../../schema-component';
-const useFormItems = () => {
+const useFormItemInitializerFields = () => {
const { name, fields } = useCollection();
return fields?.map((field) => {
return {
@@ -127,7 +127,7 @@ export const FormItemInitializer = observer((props: any) => {
{
type: 'itemGroup',
title: t('Display fields'),
- children: useFormItems(),
+ children: useFormItemInitializerFields(),
},
{
type: 'divider',
diff --git a/packages/client/src/schema-initializer/index.ts b/packages/client/src/schema-initializer/index.ts
index c67aed1dd..6aa1022c7 100644
--- a/packages/client/src/schema-initializer/index.ts
+++ b/packages/client/src/schema-initializer/index.ts
@@ -1,4 +1,3 @@
export * from './SchemaInitializer';
export * from './SchemaInitializerProvider';
export * from './types';
-
diff --git a/yarn.lock b/yarn.lock
index f6052b924..1b8ad5f00 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1115,7 +1115,7 @@
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.12.13":
+"@babel/runtime@^7.12.13", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7":
version "7.17.2"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
@@ -2963,6 +2963,18 @@
dependencies:
debug "^4.3.1"
+"@popperjs/core@^2.5.3":
+ version "2.11.2"
+ resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9"
+ integrity sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==
+
+"@restart/hooks@^0.3.25":
+ version "0.3.27"
+ resolved "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.27.tgz#91f356d66d4699a8cd8b3d008402708b6a9dc505"
+ integrity sha512-s984xV/EapUIfkjlf8wz9weP2O9TNKR96C68FfMEy2bE69+H4cNv3RD4Mf97lW7Htt7PjZrYTjSC8f3SB9VCXw==
+ dependencies:
+ dequal "^2.0.2"
+
"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
@@ -3406,7 +3418,7 @@
"@types/history" "*"
"@types/react" "*"
-"@types/react@*", "@types/react@^16.9.43", "@types/react@^17.0.0":
+"@types/react@*", "@types/react@>=16.9.11", "@types/react@^16.9.43", "@types/react@^17.0.0":
version "17.0.34"
resolved "https://registry.npmjs.org/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102"
integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg==
@@ -3475,6 +3487,11 @@
resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
+"@types/warning@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
+ integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=
+
"@types/yargs-parser@*":
version "20.2.1"
resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
@@ -5326,6 +5343,11 @@ clone@^1.0.2:
resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+clsx@^1.0.4:
+ version "1.1.1"
+ resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
+ integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
+
cmd-shim@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd"
@@ -6046,6 +6068,11 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
+date-arithmetic@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.npmjs.org/date-arithmetic/-/date-arithmetic-4.1.0.tgz#e5d6434e9deb71f79760a37b729e4a515e730ddf"
+ integrity sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==
+
date-fns@2.x:
version "2.27.0"
resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.27.0.tgz#e1ff3c3ddbbab8a2eaadbb6106be2929a5a2d92b"
@@ -6374,6 +6401,14 @@ dom-align@^1.7.0:
resolved "https://registry.npmjs.org/dom-align/-/dom-align-1.12.2.tgz#0f8164ebd0c9c21b0c790310493cd855892acd4b"
integrity sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg==
+dom-helpers@^5.1.0, dom-helpers@^5.2.0:
+ version "5.2.1"
+ resolved "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
+ integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ csstype "^3.0.2"
+
domain-browser@^1.1.1:
version "1.2.0"
resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
@@ -10250,6 +10285,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
+lodash-es@^4.17.11:
+ version "4.17.21"
+ resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
+ integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
+
lodash._baseclone@~4.5.0:
version "4.5.7"
resolved "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-4.5.7.tgz#ce42ade08384ef5d62fa77c30f61a46e686f8434"
@@ -13697,6 +13737,23 @@ react-beautiful-dnd@^13.1.0:
redux "^4.0.4"
use-memo-one "^1.1.1"
+react-big-calendar@^0.38.7:
+ version "0.38.9"
+ resolved "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-0.38.9.tgz#71a4fb578ddaaea2f65c664dbdeca0063be7b1f2"
+ integrity sha512-Z0yviu6ah5njVfRZxOwM2qnPRJCVuywk68jtjLr6GcCJj5bzhgc9+rkP/FSgbH6aBIWsvcTWw6kGTg5CClX6Ww==
+ dependencies:
+ "@babel/runtime" "^7.1.5"
+ clsx "^1.0.4"
+ date-arithmetic "^4.1.0"
+ dom-helpers "^5.1.0"
+ invariant "^2.2.4"
+ lodash "^4.17.11"
+ lodash-es "^4.17.11"
+ memoize-one "^5.1.1"
+ prop-types "^15.7.2"
+ react-overlays "^4.1.1"
+ uncontrollable "^7.0.0"
+
react-docgen-typescript-dumi-tmp@^1.22.1-0:
version "1.22.1-0"
resolved "https://registry.npmjs.org/react-docgen-typescript-dumi-tmp/-/react-docgen-typescript-dumi-tmp-1.22.1-0.tgz#6f452de05c5c114a6e1dd60b34930afaa7ae39a0"
@@ -13763,7 +13820,7 @@ react-is@^17.0.1, react-is@^17.0.2:
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-react-lifecycles-compat@^3.0.0:
+react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
@@ -13778,6 +13835,20 @@ react-modal@^3.11.1:
react-lifecycles-compat "^3.0.0"
warning "^4.0.3"
+react-overlays@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-4.1.1.tgz#0060107cbe1c5171a744ccda3fbf0556d064bc5f"
+ integrity sha512-WtJifh081e6M24KnvTQoNjQEpz7HoLxqt8TwZM7LOYIkYJ8i/Ly1Xi7RVte87ZVnmqQ4PFaFiNHZhSINPSpdBQ==
+ dependencies:
+ "@babel/runtime" "^7.12.1"
+ "@popperjs/core" "^2.5.3"
+ "@restart/hooks" "^0.3.25"
+ "@types/warning" "^3.0.0"
+ dom-helpers "^5.2.0"
+ prop-types "^15.7.2"
+ uncontrollable "^7.0.0"
+ warning "^4.0.3"
+
react-redux@^7.2.0:
version "7.2.6"
resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa"
@@ -16155,6 +16226,16 @@ unbox-primitive@^1.0.1:
has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2"
+uncontrollable@^7.0.0:
+ version "7.2.1"
+ resolved "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738"
+ integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==
+ dependencies:
+ "@babel/runtime" "^7.6.3"
+ "@types/react" ">=16.9.11"
+ invariant "^2.2.4"
+ react-lifecycles-compat "^3.0.4"
+
underscore@^1.13.1:
version "1.13.1"
resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1"