From 1e3cdf9572ea3ed812abd0256eddee44fd93abf4 Mon Sep 17 00:00:00 2001 From: SemmyWong <67748948+semmywong@users.noreply.github.com> Date: Mon, 21 Feb 2022 15:31:31 +0800 Subject: [PATCH] feat: finish calendar component develop (#199) * feat: add Calendar component into schema components * feat: add Calendar component into schema components * feat: modify calendar get data async * refactor: remove toolbar * fix: modify calendar * feat: modify fieldName * feat: add new task in calendar * fix: modify calendar component * feat: add Calendar component into schema components * feat: modify calendar get data async * feat: add Calendar component into schema components * refactor: remove toolbar * fix: modify calendar * feat: modify fieldName * feat: add new task in calendar * fix: modify calendar component * fix: modify "useFormItemInitializerFields" * fix: add data to calendar * fix: add data to calendar * fix: add data to calendar * feat: add remove and edit in calendar * fix: modify edit and remove in calendar component * fix: modify edit and remove in calendar component * fix: modify edit and remove in calendar component * refactor: refactor event * feat: add Calendar component into schema components * feat: modify calendar get data async * feat: add Calendar component into schema components * refactor: remove toolbar * fix: modify calendar * feat: modify fieldName * feat: add new task in calendar * fix: modify calendar component * feat: add Calendar component into schema components * feat: modify calendar get data async * feat: add Calendar component into schema components * refactor: remove toolbar * fix: modify calendar * feat: add new task in calendar * fix: modify calendar component * fix: modify "useFormItemInitializerFields" * fix: add data to calendar * fix: add data to calendar * fix: add data to calendar * feat: add remove and edit in calendar * fix: modify edit and remove in calendar component * fix: modify edit and remove in calendar component * fix: modify edit and remove in calendar component * refactor: refactor event * fix: modify conflict * refactor: change new initializer Co-authored-by: chenos --- packages/client/package.json | 1 + .../client/src/api-client/hooks/useRequest.ts | 4 +- .../hooks/useCollectionField.ts | 2 +- .../client/src/collection-manager/types.ts | 3 +- packages/client/src/locale/en_US.json | 3 +- packages/client/src/locale/zh_CN.json | 3 +- .../antd/calendar/ActionInitializer.tsx | 163 ++++ .../antd/calendar/Calendar.tsx | 186 ++++- .../schema-component/antd/calendar/Event.tsx | 6 + .../schema-component/antd/calendar/Filter.tsx | 69 ++ .../antd/calendar/FooterActionInitializer.tsx | 138 ++++ .../schema-component/antd/calendar/Nav.tsx | 19 + .../schema-component/antd/calendar/Title.tsx | 15 + .../schema-component/antd/calendar/Today.tsx | 23 + .../antd/calendar/ViewSelect.tsx | 25 + .../schema-component/antd/calendar/context.ts | 5 + .../antd/calendar/demos/apiClient.ts | 34 + .../antd/calendar/demos/defaultValues.ts | 180 ++++ .../antd/calendar/demos/demo1.tsx | 282 +++++++ .../schema-component/antd/calendar/index.md | 10 +- .../schema-component/antd/calendar/index.ts | 1 + .../schema-component/antd/calendar/style.less | 767 ++++++++++++++++++ .../schema-component/antd/calendar/types.d.ts | 8 + .../schema-component/antd/calendar/utils.ts | 43 + .../schema-component/core/SchemaComponent.tsx | 7 +- .../FormItemInitializer/index.tsx | 4 +- .../client/src/schema-initializer/index.ts | 1 - yarn.lock | 87 +- 28 files changed, 2068 insertions(+), 21 deletions(-) create mode 100644 packages/client/src/schema-component/antd/calendar/ActionInitializer.tsx create mode 100644 packages/client/src/schema-component/antd/calendar/Event.tsx create mode 100644 packages/client/src/schema-component/antd/calendar/Filter.tsx create mode 100644 packages/client/src/schema-component/antd/calendar/FooterActionInitializer.tsx create mode 100644 packages/client/src/schema-component/antd/calendar/Nav.tsx create mode 100644 packages/client/src/schema-component/antd/calendar/Title.tsx create mode 100644 packages/client/src/schema-component/antd/calendar/Today.tsx create mode 100644 packages/client/src/schema-component/antd/calendar/ViewSelect.tsx create mode 100644 packages/client/src/schema-component/antd/calendar/context.ts create mode 100644 packages/client/src/schema-component/antd/calendar/demos/apiClient.ts create mode 100644 packages/client/src/schema-component/antd/calendar/demos/defaultValues.ts create mode 100644 packages/client/src/schema-component/antd/calendar/demos/demo1.tsx create mode 100644 packages/client/src/schema-component/antd/calendar/style.less create mode 100644 packages/client/src/schema-component/antd/calendar/types.d.ts create mode 100644 packages/client/src/schema-component/antd/calendar/utils.ts 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')} + + + + + } + > + + + ); +}); 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 ( +
+ + + +
+ ); +}); 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"