feat: implement on-demand loading for frontend components (#5647)

* fix: missing less loader while building client

* fix: correct regex for .less file handling and resolve less-loader path

* feat: dynamic import big react components for core plugins

* chore: revert lerna.json

* chore: remove global deps duplications [skip ci]

* chore: optimization

* feat: dynamic import for markdown  vditor plugin

* chore: optimization

* chore: more optimization

* feat: code split for plugins with some ui components

* fix: incorrect submodule commit

* fix: test cases failure

* chore: refactor hook lazy import

* chore: improve lazy component loading

* chore:  lazy load vditor lib's js files [skip ci]

* chore: add bundle analyze option for client bundle

* chore: update loading sytle

* fix: add spinner when loading umi js files

* chore: clean

* chore: resolve develop branch confliction

* chore: refactor helper function name

* fix: error of lazy duplication [skip ci]

* fix: replace useImported with uselazyhook

* chore: rename

* chore: add comments for the helper function

* chore: update  comment

* fix: keep suspense into component level

* fix: improve code

---------

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
gchust 2024-11-26 20:58:55 +08:00 committed by GitHub
parent f23257f953
commit 607773075a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 419 additions and 115 deletions

View File

@ -80,5 +80,6 @@ export default defineConfig({
}
return config;
},
extraBabelPlugins: ['react-imported-component/babel'],
routes: [{ path: '/*', component: 'index' }],
});

View File

@ -0,0 +1,25 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import React from 'react';
import { Spin } from 'antd';
export default function Loading() {
return (
<Spin
style={{
width: '100vw',
height: '100vh',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
/>
);
}

View File

@ -19,4 +19,4 @@
"directory": "packages/core/app"
},
"gitHead": "d0b4efe4be55f8c79a98a331d99d9f8cf99021a1"
}
}

View File

@ -9,30 +9,36 @@
},
"typings": "./index.d.ts",
"dependencies": {
"@babel/core": "7.25.2",
"@babel/core": "^7.26.0",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-amd": "7.24.7",
"@babel/preset-env": "7.25.4",
"@babel/preset-env": "^7.26.0",
"@hapi/topo": "^6.0.0",
"@lerna/project": "4.0.0",
"@rspack/core": "1.0.14",
"@rspack/core": "1.1.1",
"@svgr/webpack": "^8.1.0",
"@types/gulp": "^4.0.13",
"@types/lerna__package": "5.1.0",
"@types/lerna__project": "5.1.0",
"@types/tar": "^6.1.5",
"@vercel/ncc": "0.36.1",
"babel-loader": "^9.2.1",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"chalk": "2.4.2",
"css-loader": "^6.8.1",
"esbuild-register": "^3.4.2",
"fast-glob": "^3.3.1",
"gulp": "4.0.2",
"gulp-typescript": "6.0.0-alpha.1",
"less": "^4.1.3",
"less-loader": "11.1.0",
"less": "^4.2.0",
"less-loader": "^12.2.0",
"postcss": "^8.4.29",
"postcss-loader": "^7.3.3",
"postcss-preset-env": "^9.1.2",
"react-imported-component": "^6.5.4",
"style-loader": "^3.3.3",
"@rsbuild/plugin-babel": "^1.0.3",
"@rsdoctor/rspack-plugin": "^0.4.8",
"tar": "^6.2.0",
"tsup": "8.2.4",
"typescript": "5.1.3",

View File

@ -24,10 +24,19 @@ import { buildClient } from './buildClient';
import { buildCjs } from './buildCjs';
import { buildPlugin } from './buildPlugin';
import { buildDeclaration } from './buildDeclaration';
import { PkgLog, getPkgLog, toUnixPath, getPackageJson, getUserConfig, UserConfig, writeToCache, readFromCache } from './utils';
import {
PkgLog,
getPkgLog,
toUnixPath,
getPackageJson,
getUserConfig,
UserConfig,
writeToCache,
readFromCache,
} from './utils';
import { getPackages } from './utils/getPackages';
import { Package } from '@lerna/package';
import { tarPlugin } from './tarPlugin'
import { tarPlugin } from './tarPlugin';
import { buildEsm } from './buildEsm';
import { addLicense } from './utils/addlicense';
@ -45,9 +54,9 @@ export async function build(pkgs: string[]) {
if (packages.length === 0) {
let msg = '';
if (pkgs.length) {
msg = `'${pkgs.join(', ')}' did not match any packages`
msg = `'${pkgs.join(', ')}' did not match any packages`;
} else {
msg = 'No package matched'
msg = 'No package matched';
}
console.warn(chalk.yellow(`[@nocobase/build]: ${msg}`));
return;
@ -63,7 +72,7 @@ export async function build(pkgs: string[]) {
if (clientCore) {
await buildPackage(clientCore, 'es', buildClient);
}
const esmPackages = packages.filter(pkg => ESM_PACKAGES.includes(pkg.name));
const esmPackages = packages.filter((pkg) => ESM_PACKAGES.includes(pkg.name));
await buildPackages(esmPackages, 'lib', buildCjs);
await buildPackages(esmPackages, 'es', buildEsm);
@ -78,6 +87,7 @@ export async function build(pkgs: string[]) {
if (appClient) {
await runScript(['umi', 'build'], ROOT_PATH, {
APP_ROOT: path.join(CORE_APP, 'client'),
ANALYZE: process.env.BUILD_ANALYZE === 'true' ? '1' : undefined,
});
}
writeToCache(BUILD_ERROR, {});
@ -89,7 +99,7 @@ export async function buildPackages(
doBuildPackage: (cwd: string, userConfig: UserConfig, sourcemap: boolean, log?: PkgLog) => Promise<any>,
) {
for await (const pkg of packages) {
writeToCache(BUILD_ERROR, { pkg: pkg.name })
writeToCache(BUILD_ERROR, { pkg: pkg.name });
await buildPackage(pkg, targetDir, doBuildPackage);
}
}

View File

@ -26,6 +26,7 @@ import {
getSourcePackages
} from './utils/buildPluginUtils';
import { getDepPkgPath, getDepsConfig } from './utils/getDepsConfig';
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin';
const validExts = ['.ts', '.tsx', '.js', '.jsx', '.mjs'];
const serverGlobalFiles: string[] = ['src/**', '!src/client/**', ...globExcludeFiles];
@ -403,6 +404,18 @@ export async function buildPluginClient(cwd: string, userConfig: UserConfig, sou
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack'],
},
{
test: /\.(?:js|mjs|cjs|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
targets: 'defaults',
// presets: [['@babel/preset-env']],
plugins: ['react-imported-component/babel'],
},
},
},
{
test: /\.jsx$/,
exclude: /[\\/]node_modules[\\/]/,
@ -453,7 +466,15 @@ export async function buildPluginClient(cwd: string, userConfig: UserConfig, sou
new rspack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
],
process.env.BUILD_ANALYZE === 'true' &&
new RsdoctorRspackPlugin({
// plugin options
// supports: {
// generateTileGraph: true,
// },
mode: 'brief',
}),
].filter(Boolean),
node: {
global: true,
},

View File

@ -101,6 +101,5 @@ export function defineGlobalDeps(requirejs: RequireJS) {
requirejs.define('lodash', () => lodash);
requirejs.define('ahooks', () => ahooks);
requirejs.define('@emotion/css', () => emotionCss);
requirejs.define('dayjs', () => dayjs);
requirejs.define('file-saver', () => FileSaver);
}

View File

@ -21,7 +21,6 @@ import qs from 'qs';
import { ChangeEvent, useCallback, useContext, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { NavigateFunction } from 'react-router-dom';
import { useReactToPrint } from 'react-to-print';
import {
AssociationFilter,
useCollection,
@ -1091,27 +1090,6 @@ export const useDisassociateActionProps = () => {
};
};
export const useDetailPrintActionProps = () => {
const { formBlockRef } = useFormBlockContext();
const printHandler = useReactToPrint({
content: () => formBlockRef.current,
pageStyle: `@media print {
* {
margin: 0;
}
:not(.ant-formily-item-control-content-component) > div.ant-formily-layout>div:first-child {
overflow: hidden; height: 0;
}
}`,
});
return {
async onClick() {
printHandler();
},
};
};
export const useBulkDestroyActionProps = () => {
const { field } = useBlockRequestContext();
const { resource, service } = useBlockRequestContext();

View File

@ -9,8 +9,9 @@
import { useExpressionScope } from '@formily/react';
import _ from 'lodash';
import React, { ComponentType, useMemo } from 'react';
import React, { ComponentType, Suspense, useMemo } from 'react';
import { useDesignable } from '../schema-component';
import { Spin } from 'antd';
const useDefaultDynamicComponentProps = () => undefined;
@ -84,7 +85,11 @@ export function withDynamicSchemaProps<T = any>(
return { ...props, ...schemaProps };
}, [schemaProps, props]);
return <Component {...memoProps}>{props.children}</Component>;
return (
<Suspense fallback={<Spin />}>
<Component {...memoProps}>{props.children}</Component>
</Suspense>
);
};
Component.displayName = displayName;

View File

@ -58,6 +58,7 @@ export * from './system-settings';
export * from './testUtils';
export * from './user';
export * from './variables';
export * from './lazy-helper';
export { withDynamicSchemaProps } from './hoc/withDynamicSchemaProps';

View File

@ -0,0 +1,101 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import React, { ComponentType, lazy as ReactLazy } from 'react';
import { Spin } from 'antd';
import { get } from 'lodash';
import { useImported } from 'react-imported-component';
/**
* Lazily loads a React component or multiple components.
*
* This function can be used to dynamically import a React component or multiple components
* and return them as lazy-loaded components. It uses `React.lazy` under the hood and displays
* a loading spinner (`Spin` from `antd`) while the component is being loaded.
*
* @template M - The type of the module being imported.
* @template K - The keys of the components in the module.
*
* @param {() => Promise<{ default: M }>} factory - A function that returns a promise resolving to the module with a default export.
* @returns {React.LazyExoticComponent<M>} A lazy-loaded component.
*
* @param {() => Promise<M>} factory - A function that returns a promise resolving to the module with named exports.
* @param {...K[]} componentNames - The names of the components to be lazy-loaded from the module.
* @returns {Record<K, React.LazyExoticComponent<M[K]>>} An object containing the lazy-loaded components.
*/
export function lazy<M extends ComponentType<any>>(
factory: () => Promise<{ default: M }>,
): React.LazyExoticComponent<M>;
export function lazy<M extends Record<string, any>, K extends keyof M & string>(
factory: () => Promise<M>,
...componentNames: K[]
): Record<K, React.LazyExoticComponent<M[K]>>;
export function lazy<M extends Record<string, any>, K extends keyof M & string>(
factory: () => Promise<M>,
...componentNames: K[]
) {
if (componentNames.length === 0) {
const LazyComponent = ReactLazy(() =>
factory().then((module) => ({
default: module.default,
})),
);
return (props) => (
<React.Suspense fallback={<Spin />}>
<LazyComponent {...props} />
</React.Suspense>
);
}
return componentNames.reduce(
(acc, name) => {
const LazyComponent = ReactLazy(() =>
factory().then((module) => ({
default: get(module, name),
})),
);
acc[name] = (props) => (
<React.Suspense fallback={<Spin />}>
<LazyComponent {...props} />
</React.Suspense>
);
return acc;
},
{} as Record<K, React.ComponentType<any>>,
);
}
/**
* A hook to lazily load a module and return a specific export from it.
*
* This hook uses `useImported` to dynamically import a module and return a specific export
* from the module. It throws a promise while the module is being loaded, which can be caught
* by the parent error boundary to show a loading state.
*
* @template T - The type of the export being picked from the module.
*
* @param {Parameters<typeof useImported>[0]} importor - The function to import the module.
* @param {string | ((module: any) => T)} picker - The name of the export to pick or a function to pick the export.
* @returns {T} The picked export from the imported module.
*
* @throws {Promise} Throws a promise while the module is being loaded.
*/
export function useLazy<T = () => any>(
importor: Parameters<typeof useImported>[0],
picker: string | ((module: any) => T),
): T {
const exportPicker = typeof picker === 'function' ? picker : (module) => module[picker];
const { imported, loading, loadable } = useImported(importor, exportPicker);
if (loading) {
throw loadable.resolution;
}
return imported as T;
}

View File

@ -10,9 +10,14 @@
import { TabsProps } from 'antd/es/tabs/index';
import React from 'react';
import { TFunction } from 'react-i18next';
import { GeneralPermissions } from './permissions/GeneralPermissions';
import { MenuItemsProvider } from './permissions/MenuItemsProvider';
import { MenuPermissions } from './permissions/MenuPermissions';
import { lazy } from '@nocobase/client';
// import { GeneralPermissions } from './permissions/GeneralPermissions';
// import { MenuItemsProvider } from './permissions/MenuItemsProvider';
// import { MenuPermissions } from './permissions/MenuPermissions';
const { GeneralPermissions } = lazy(() => import('./permissions/GeneralPermissions'), 'GeneralPermissions');
const { MenuItemsProvider } = lazy(() => import('./permissions/MenuItemsProvider'), 'MenuItemsProvider');
const { MenuPermissions } = lazy(() => import('./permissions/MenuPermissions'), 'MenuPermissions');
import { Role } from './RolesManagerProvider';
interface PermissionsTabsProps {

View File

@ -7,9 +7,10 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Plugin } from '@nocobase/client';
import { Plugin, lazy } from '@nocobase/client';
import { ACLSettingsUI } from './ACLSettingsUI';
import { RolesManagement } from './RolesManagement';
// import { RolesManagement } from './RolesManagement';
const { RolesManagement } = lazy(() => import('./RolesManagement'), 'RolesManagement');
import { RolesManager } from './roles-manager';
export class PluginACLClient extends Plugin {

View File

@ -13,7 +13,7 @@ import { uid } from '@formily/shared';
import { SchemaComponent, useAPIClient } from '@nocobase/client';
import { useMemoizedFn } from 'ahooks';
import { Checkbox, message } from 'antd';
import uniq from 'lodash/uniq';
import { uniq } from 'lodash';
import React, { useContext, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useACLTranslation } from '../locale';

View File

@ -10,7 +10,7 @@
import { uid } from '@formily/shared';
import { useActionContext, useCollectionRecord, useRecord, useRequest } from '@nocobase/client';
import { useEffect } from 'react';
import pick from 'lodash/pick';
import { pick } from 'lodash';
import { ISchema } from '@formily/react';
export const roleEditSchema = {

View File

@ -7,12 +7,15 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { useDetailsBlockContext, useFormBlockContext } from '@nocobase/client';
import { useReactToPrint } from 'react-to-print';
import { useDetailsBlockContext, useFormBlockContext, useLazy } from '@nocobase/client';
// import { useReactToPrint } from 'react-to-print';
export const useDetailPrintActionProps = () => {
const context = useFormBlockContext();
const { formBlockRef } = useDetailsBlockContext();
const useReactToPrint = useLazy<typeof import('react-to-print').useReactToPrint>(
() => import('react-to-print'),
'useReactToPrint',
);
const printHandler = useReactToPrint({
content: () => {
const content = context?.formBlockRef?.current || formBlockRef?.current;

View File

@ -7,8 +7,9 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Plugin } from '@nocobase/client';
import { Configuration } from './Configuration';
import { Plugin, lazy } from '@nocobase/client';
// import { Configuration } from './Configuration';
const { Configuration } = lazy(() => import('./Configuration'), 'Configuration');
export class PluginAPIKeysClient extends Plugin {
async load() {

View File

@ -8,12 +8,12 @@
*/
import { ISchema } from '@formily/react';
import { SchemaComponent, useAPIClient, useCurrentUserContext } from '@nocobase/client';
import { SchemaComponent, useAPIClient, useCurrentUserContext, useLazy } from '@nocobase/client';
import React, { useCallback } from 'react';
import { useAuthTranslation } from '../locale';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useForm } from '@formily/react';
import { useSignUpForms } from '../pages';
// import { useSignUpForms } from '../pages';
import { Authenticator } from '../authenticator';
export function useRedirect(next = '/admin') {
@ -102,6 +102,7 @@ export const SignInForm = (props: { authenticator: Authenticator }) => {
const { t } = useAuthTranslation();
const authenticator = props.authenticator;
const { authType, name, options } = authenticator;
const useSignUpForms = useLazy<typeof import('../pages').useSignUpForms>(() => import('../pages'), 'useSignUpForms');
const signUpPages = useSignUpForms();
const allowSignUp = signUpPages[authType] && options?.allowSignUp ? true : false;
const signUpLink = `/signup?name=${name}`;

View File

@ -7,17 +7,28 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Plugin } from '@nocobase/client';
import { Plugin, lazy, useLazy } from '@nocobase/client';
import { Registry } from '@nocobase/utils/client';
import { ComponentType } from 'react';
import { presetAuthType } from '../preset';
import { AuthProvider } from './AuthProvider';
import { Authenticator as AuthenticatorType } from './authenticator';
import { Options, SignInForm, SignUpForm } from './basic';
// import { AuthProvider } from './AuthProvider';
const { AuthProvider } = lazy(() => import('./AuthProvider'), 'AuthProvider');
import type { Authenticator as AuthenticatorType } from './authenticator';
// import { Options, SignInForm, SignUpForm } from './basic';
const { Options, SignInForm, SignUpForm } = lazy(() => import('./basic'), 'Options', 'SignInForm', 'SignUpForm');
import { NAMESPACE } from './locale';
import { AuthLayout, SignInPage, SignUpPage } from './pages';
import { Authenticator } from './settings/Authenticator';
export { AuthenticatorsContextProvider, AuthLayout } from './pages/AuthLayout';
// import { AuthLayout, SignInPage, SignUpPage } from './pages';
const { AuthLayout, SignInPage, SignUpPage } = lazy(() => import('./pages'), 'AuthLayout', 'SignInPage', 'SignUpPage');
// import { Authenticator } from './settings/Authenticator';
const { Authenticator } = lazy(() => import('./settings/Authenticator'), 'Authenticator');
// export { AuthenticatorsContextProvider, AuthLayout } from './pages/AuthLayout';
const { AuthenticatorsContextProvider, AuthLayout: ExportAuthLayout } = lazy(
() => import('./pages'),
'AuthenticatorsContextProvider',
'AuthLayout',
);
export { AuthenticatorsContextProvider, ExportAuthLayout as AuthLayout };
export type AuthOptions = {
components: Partial<{
@ -75,6 +86,12 @@ export class PluginAuthClient extends Plugin {
export { AuthenticatorsContext, useAuthenticator } from './authenticator';
export type { Authenticator } from './authenticator';
export { useSignIn } from './basic';
// export { useSignIn } from './basic';
const useSignIn = function (name: string) {
const useSignIn = useLazy<typeof import('./basic').useSignIn>(() => import('./basic'), 'useSignIn');
return useSignIn(name);
};
export { useSignIn };
export default PluginAuthClient;

View File

@ -8,7 +8,9 @@
*/
import { Plugin } from '@nocobase/client';
import { BackupAndRestoreList } from './Configuration';
// import { BackupAndRestoreList } from './Configuration';
import { lazy } from '@nocobase/client';
const { BackupAndRestoreList } = lazy(() => import('./Configuration'), 'BackupAndRestoreList');
import { DuplicatorProvider } from './DuplicatorProvider';
import { NAMESPACE } from './locale';

View File

@ -8,7 +8,6 @@
*/
import { Plugin } from '@nocobase/client';
import { QRCodeScanner } from './components/qrcode-scanner';
import { WorkbenchAction } from './WorkbenchAction';
import { WorkbenchBlock } from './WorkbenchBlock';
import { workbenchBlockInitializerItem } from './workbenchBlockInitializerItem';
@ -28,8 +27,10 @@ import {
WorkbenchCustomRequestActionSchemaInitializerItem,
workbenchActionSettingsCustomRequest,
} from './WorkbenchCustomRequestActionSchemaInitializerItem';
import { lazy } from '@nocobase/client';
export class PluginBlockWorkbenchClient extends Plugin {
async load() {
const { QRCodeScanner } = lazy(() => import('./components/qrcode-scanner'), 'QRCodeScanner');
this.app.addComponents({ WorkbenchBlock, QRCodeScanner, WorkbenchAction });
// 新增工作台区块的设置器

View File

@ -21,13 +21,11 @@ import {
withDynamicSchemaProps,
useACLRoleContext,
} from '@nocobase/client';
import { parseExpression } from 'cron-parser';
import type { Dayjs } from 'dayjs';
import dayjs from 'dayjs';
import get from 'lodash/get';
import { get } from 'lodash-es';
import React, { useMemo, useState, useEffect } from 'react';
import { Calendar as BigCalendar, View, dayjsLocalizer } from 'react-big-calendar';
import * as dates from 'react-big-calendar/lib/utils/dates';
import type { View } from 'react-big-calendar';
import { i18nt, useTranslation } from '../../locale';
import { CalendarRecordViewer, findEventSchema } from './CalendarRecordViewer';
import Header from './components/Header';
@ -37,6 +35,7 @@ import { useCalenderHeight } from './hook';
import useStyle from './style';
import type { ToolbarProps } from './types';
import { formatDate } from './utils';
import { useLazy } from '@nocobase/client';
interface Event {
id: string;
@ -47,7 +46,6 @@ interface Event {
}
const Weeks = ['month', 'week', 'day'] as View[];
const localizer = dayjsLocalizer(dayjs);
const getColorString = (
colorFieldValue: string,
@ -103,6 +101,10 @@ const useEvents = (
date: Date,
view: (typeof Weeks)[number],
) => {
const parseExpression = useLazy<typeof import('cron-parser').parseExpression>(
() => import('cron-parser'),
'parseExpression',
);
const { t } = useTranslation();
const { fields } = useCollection();
const labelUiSchema = fields.find((v) => v.name === fieldNames?.title)?.uiSchema;
@ -215,6 +217,7 @@ const useEvents = (
view,
t,
enumUiSchema?.uiSchema?.enum,
parseExpression,
]);
};
@ -237,6 +240,22 @@ export const Calendar: any = withDynamicSchemaProps(
const { openPopup } = usePopupUtils({
setVisible,
});
const reactBigCalendar = useLazy(
() => import('react-big-calendar'),
(module) => ({
BigCalendar: module.Calendar,
dayjsLocalizer: module.dayjsLocalizer,
}),
);
const eq = useLazy<typeof import('react-big-calendar/lib/utils/dates').eq>(
() => import('react-big-calendar/lib/utils/dates'),
'eq',
);
const localizer = useMemo(() => {
return reactBigCalendar.dayjsLocalizer(dayjs);
}, [reactBigCalendar]);
// 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema
const { dataSource, fieldNames, showLunar, defaultView } = useProps(props);
@ -308,6 +327,8 @@ export const Calendar: any = withDynamicSchemaProps(
}
};
const BigCalendar = reactBigCalendar?.BigCalendar;
return wrapSSR(
<div className={`${hashId} ${containerClassName}`} style={{ height: height || 700 }}>
<PopupContextProvider visible={visible} setVisible={setVisible}>
@ -348,7 +369,11 @@ export const Calendar: any = withDynamicSchemaProps(
if (!record) {
return;
}
record.__event = { ...event, start: formatDate(dayjs(event.start)), end: formatDate(dayjs(event.end)) };
record.__event = {
...event,
start: formatDate(dayjs(event.start)),
end: formatDate(dayjs(event.end)),
};
setRecord(record);
openPopup({
@ -361,7 +386,7 @@ export const Calendar: any = withDynamicSchemaProps(
agendaDateFormat: 'M-DD',
dayHeaderFormat: 'YYYY-M-DD',
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
if (dates.eq(start, end, 'month')) {
if (eq(start, end, 'month')) {
return local.format(start, 'YYYY-M', culture);
}
return `${local.format(start, 'YYYY-M', culture)} - ${local.format(end, 'YYYY-M', culture)}`;

View File

@ -11,12 +11,15 @@ 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/dist/react-big-calendar.esm';
import { CalendarToolbarContext } from './context';
import { useDesignable } from '@nocobase/client';
import { useDesignable, useLazy } from '@nocobase/client';
export const Nav = observer(
() => {
const Navigate = useLazy<typeof import('react-big-calendar/dist/react-big-calendar.esm').Navigate>(
() => import('react-big-calendar/dist/react-big-calendar.esm'),
'Navigate',
);
const { DesignableBar } = useDesignable();
const { onNavigate } = useContext(CalendarToolbarContext);
return (

View File

@ -10,9 +10,8 @@
import { observer } from '@formily/react';
import { Button } from 'antd';
import React, { useContext } from 'react';
import { Navigate } from 'react-big-calendar/dist/react-big-calendar.esm';
import { CalendarToolbarContext } from './context';
import { useDesignable } from '@nocobase/client';
import { useDesignable, useLazy } from '@nocobase/client';
import { useTranslation } from '../../locale';
export const Today = observer(
@ -20,6 +19,11 @@ export const Today = observer(
const { DesignableBar } = useDesignable();
const { onNavigate } = useContext(CalendarToolbarContext);
const { t } = useTranslation();
const Navigate = useLazy<typeof import('react-big-calendar/dist/react-big-calendar.esm').Navigate>(
() => import('react-big-calendar/dist/react-big-calendar.esm'),
'Navigate',
);
return (
<Button
onClick={() => {

View File

@ -27,8 +27,8 @@ import {
useResourceContext,
} from '@nocobase/client';
import { tval } from '@nocobase/utils/client';
import cloneDeep from 'lodash/cloneDeep';
import omit from 'lodash/omit';
// import cloneDeep from 'lodash/cloneDeep';
import { cloneDeep, omit } from 'lodash';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';

View File

@ -10,9 +10,9 @@
import { ArrayTable } from '@formily/antd-v5';
import { ISchema, useForm } from '@formily/react';
import { uid } from '@formily/shared';
import cloneDeep from 'lodash/cloneDeep';
import omit from 'lodash/omit';
import set from 'lodash/set';
// import cloneDeep from 'lodash/cloneDeep';
// import omit from 'lodash/omit';
import { cloneDeep, omit, set } from 'lodash';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';

View File

@ -11,15 +11,34 @@ import { Plugin } from '@nocobase/client';
import PluginACLClient from '@nocobase/plugin-acl/client';
import { uid } from '@nocobase/utils/client';
import React from 'react';
import { DatabaseConnectionProvider } from './DatabaseConnectionProvider';
import { lazy } from '@nocobase/client';
// import { DatabaseConnectionProvider } from './DatabaseConnectionProvider';
const { DatabaseConnectionProvider } = lazy(() => import('./DatabaseConnectionProvider'), 'DatabaseConnectionProvider');
import { ThirdDataSource } from './ThridDataSource';
import { BreadcumbTitle } from './component/BreadcumbTitle';
import { CollectionManagerPage } from './component/CollectionsManager';
import { DatabaseConnectionManagerPane } from './component/DatabaseConnectionManager';
import { MainDataSourceManager } from './component/MainDataSourceManager';
import { DataSourcePermissionManager } from './component/PermissionManager';
// import { BreadcumbTitle } from './component/BreadcumbTitle';
const { BreadcumbTitle } = lazy(() => import('./component/BreadcumbTitle'), 'BreadcumbTitle');
// import { CollectionManagerPage } from './component/CollectionsManager';
const { CollectionManagerPage } = lazy(() => import('./component/CollectionsManager'), 'CollectionManagerPage');
// import { DatabaseConnectionManagerPane } from './component/DatabaseConnectionManager';
const { DatabaseConnectionManagerPane } = lazy(
() => import('./component/DatabaseConnectionManager'),
'DatabaseConnectionManagerPane',
);
// import { MainDataSourceManager } from './component/MainDataSourceManager';
const { MainDataSourceManager } = lazy(() => import('./component/MainDataSourceManager'), 'MainDataSourceManager');
// import { DataSourcePermissionManager } from './component/PermissionManager';
const { DataSourcePermissionManager } = lazy(
() => import('./component/PermissionManager'),
'DataSourcePermissionManager',
);
import { NAMESPACE } from './locale';
import { CollectionMainProvider } from './component/MainDataSourceManager/CollectionMainProvider';
// import { CollectionMainProvider } from './component/MainDataSourceManager/CollectionMainProvider';
const { CollectionMainProvider } = lazy(
() => import('./component/MainDataSourceManager/CollectionMainProvider'),
'CollectionMainProvider',
);
export class PluginDataSourceManagerClient extends Plugin {
types = new Map();

View File

@ -7,9 +7,11 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Bar as G2PlotBar } from '@ant-design/plots';
import { G2PlotChart } from './g2plot';
import { ChartType } from '../chart';
import { lazy } from '@nocobase/client';
// import { Bar as G2PlotBar } from '@ant-design/plots';
const { Bar: G2PlotBar } = lazy(() => import('@ant-design/plots'), 'Bar');
export class Bar extends G2PlotChart {
constructor() {

View File

@ -9,8 +9,11 @@
import { G2PlotChart } from './g2plot';
import { ChartType, RenderProps } from '../chart';
import { DualAxes as G2DualAxes } from '@ant-design/plots';
import lodash from 'lodash';
import { lazy } from '@nocobase/client';
// import { DualAxes as G2DualAxes } from '@ant-design/plots';
const { DualAxes: G2DualAxes } = lazy(() => import('@ant-design/plots'), 'DualAxes');
export class DualAxes extends G2PlotChart {
constructor() {

View File

@ -7,11 +7,21 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Area, Column, Line, Scatter, Bar } from '@ant-design/plots';
import { Pie } from './pie';
import { DualAxes } from './dualAxes';
import { G2PlotChart } from './g2plot';
import { lazy } from '@nocobase/client';
// import { Area, Column, Line, Scatter, Bar } from '@ant-design/plots';
const { Area, Column, Line, Scatter, Bar } = lazy(
() => import('@ant-design/plots'),
'Area',
'Column',
'Line',
'Scatter',
'Bar',
);
export default [
new G2PlotChart({
name: 'line',

View File

@ -8,9 +8,12 @@
*/
import { G2PlotChart } from './g2plot';
import { Pie as G2Pie } from '@ant-design/plots';
// import { Pie as G2Pie } from '@ant-design/plots';
import { ChartType, RenderProps } from '../chart';
import { lazy } from '@nocobase/client';
const { Pie: G2Pie } = lazy(() => import('@ant-design/plots'), 'Pie');
export class Pie extends G2PlotChart {
constructor() {
super({ name: 'pie', title: 'Pie', Component: G2Pie });

View File

@ -12,5 +12,9 @@ import { PluginFieldMarkdownVditorClient } from '../';
export const useCDN = () => {
const plugin = usePlugin(PluginFieldMarkdownVditorClient);
if (!plugin.dependencyLoaded) {
plugin.initVditorDependency();
plugin.dependencyLoaded = true;
}
return plugin.getCDN();
};

View File

@ -9,16 +9,20 @@
import { Plugin } from '@nocobase/client';
import 'vditor/dist/index.css';
import { MarkdownVditor } from './components';
// import { MarkdownVditor } from './components';
import { lazy } from '@nocobase/client';
const { MarkdownVditor } = lazy(() => import('./components'), 'MarkdownVditor');
import { MarkdownVditorFieldInterface } from './interfaces/markdown-vditor';
export class PluginFieldMarkdownVditorClient extends Plugin {
dependencyLoaded = false;
async afterAdd() {}
async beforeLoad() {}
async load() {
this.app.addComponents({ MarkdownVditor });
this.initVditorDependency();
this.app.dataSourceManager.addFieldInterfaces([MarkdownVditorFieldInterface]);
}

View File

@ -8,8 +8,11 @@
*/
import { Plugin } from '@nocobase/client';
import { GraphCollectionPane } from './GraphCollectionShortcut';
// import { GraphCollectionPane } from './GraphCollectionShortcut';
import { NAMESPACE } from './locale';
import { lazy } from '@nocobase/client';
const { GraphCollectionPane } = lazy(() => import('./GraphCollectionShortcut'), 'GraphCollectionPane');
export class PluginGraphCollectionPlugin extends Plugin {
async load() {
this.app.pluginSettingsManager.add(`data-source-manager/main.graph`, {

View File

@ -8,7 +8,9 @@
*/
import { Plugin } from '@nocobase/client';
import { Localization } from './Localization';
// import { Localization } from './Localization';
import { lazy } from '@nocobase/client';
const { Localization } = lazy(() => import('./Localization'), 'Localization');
import { NAMESPACE } from './locale';
export class PluginLocalizationClient extends Plugin {

View File

@ -9,7 +9,9 @@
import { Plugin } from '@nocobase/client';
import { lang } from './locale';
import { LogsDownloader } from './LogsDownloader';
// import { LogsDownloader } from './LogsDownloader';
import { lazy } from '@nocobase/client';
const { LogsDownloader } = lazy(() => import('./LogsDownloader'), 'LogsDownloader');
export class PluginLoggerClient extends Plugin {
async afterAdd() {

View File

@ -13,7 +13,10 @@ import { MapBlockOptions } from './block';
import { mapActionInitializers, mapActionInitializers_deprecated } from './block/MapActionInitializers';
import { mapBlockSettings } from './block/MapBlock.Settings';
import { useMapBlockProps } from './block/MapBlockProvider';
import { Configuration, Map } from './components';
// import { Configuration, Map } from './components';
import { lazy } from '@nocobase/client';
const { Configuration, Map } = lazy(() => import('./components'), 'Configuration', 'Map');
import { fields } from './fields';
import { fieldSettingsComponentMap } from './fields/fieldSettingsComponentMap';
import { NAMESPACE, generateNTemplate } from './locale';

View File

@ -13,7 +13,14 @@ import { Navigate, Outlet } from 'react-router-dom';
import { MobileClientProvider } from './MobileClientProvider';
import MApplication from './router/Application';
import { mBlockInitializers, mBlockInitializers_deprecated } from './core/schema';
import { AppConfiguration, InterfaceConfiguration } from './configuration';
// import { AppConfiguration, InterfaceConfiguration } from './configuration';
import { lazy } from '@nocobase/client';
const { AppConfiguration, InterfaceConfiguration } = lazy(
() => import('./configuration'),
'AppConfiguration',
'InterfaceConfiguration',
);
import { NAMESPACE } from './locale';
export class PluginMobileClient extends Plugin {

View File

@ -9,7 +9,10 @@
import { Plugin } from '@nocobase/client';
import { MultiAppManagerProvider } from './MultiAppManagerProvider';
import { AppManager } from './AppManager';
// import { AppManager } from './AppManager';
import { lazy } from '@nocobase/client';
const { AppManager } = lazy(() => import('./AppManager'), 'AppManager');
import { NAMESPACE } from '../locale';
export class PluginMultiAppManagerClient extends Plugin {

View File

@ -9,10 +9,15 @@
import { Plugin } from '@nocobase/client';
import { lang as t } from './locale';
import { ChannelManager } from './manager/channel/components';
// import { ChannelManager } from './manager/channel/components';
import { lazy } from '@nocobase/client';
const { ChannelManager } = lazy(() => import('./manager/channel/components'), 'ChannelManager');
import { RegisterChannelOptions } from './manager/channel/types';
import { useNotificationTypes } from './manager/channel/hooks';
import { LogManager } from './manager/log/components/Manager';
// import { LogManager } from './manager/log/components/Manager';
const { LogManager } = lazy(() => import('./manager/log/components/Manager'), 'LogManager');
import NotificationManager from './notification-manager';
const NAMESPACE = 'notification-manager';

View File

@ -8,9 +8,14 @@
*/
import { ISchema, Plugin } from '@nocobase/client';
import { AdminPublicFormList } from './components/AdminPublicFormList';
import { AdminPublicFormPage } from './components/AdminPublicFormPage';
import { PublicFormPage } from './components/PublicFormPage';
// import { AdminPublicFormList } from './components/AdminPublicFormList';
// import { AdminPublicFormPage } from './components/AdminPublicFormPage';
// import { PublicFormPage } from './components/PublicFormPage';
import { lazy } from '@nocobase/client';
const { AdminPublicFormList } = lazy(() => import('./components/AdminPublicFormList'), 'AdminPublicFormList');
const { AdminPublicFormPage } = lazy(() => import('./components/AdminPublicFormPage'), 'AdminPublicFormPage');
const { PublicFormPage } = lazy(() => import('./components/PublicFormPage'), 'PublicFormPage');
import { formSchemaCallback } from './schemas/formSchemaCallback';
import { publicFormBlockSettings, publicMarkdownBlockSettings } from './settings';
import { NAMESPACE } from './locale';

View File

@ -11,11 +11,18 @@ import { Plugin, createStyles, defaultTheme, useCurrentUserSettingsMenu, useGlob
import { ConfigProvider } from 'antd';
import _ from 'lodash';
import React, { useEffect, useMemo } from 'react';
import InitializeTheme from './components/InitializeTheme';
import { ThemeEditorProvider } from './components/ThemeEditorProvider';
import ThemeList from './components/ThemeList';
import { ThemeListProvider } from './components/ThemeListProvider';
import CustomTheme from './components/theme-editor';
// import InitializeTheme from './components/InitializeTheme';
// import { ThemeEditorProvider } from './components/ThemeEditorProvider';
// import ThemeList from './components/ThemeList';
// import { ThemeListProvider } from './components/ThemeListProvider';
// import CustomTheme from './components/theme-editor';
import { lazy } from '@nocobase/client';
const InitializeTheme = lazy(() => import('./components/InitializeTheme'));
const { ThemeEditorProvider } = lazy(() => import('./components/ThemeEditorProvider'), 'ThemeEditorProvider');
const ThemeList = lazy(() => import('./components/ThemeList'));
const { ThemeListProvider } = lazy(() => import('./components/ThemeListProvider'), 'ThemeListProvider');
const CustomTheme = lazy(() => import('./components/theme-editor'));
import { useThemeSettings } from './hooks/useThemeSettings';
import { NAMESPACE } from './locale';

View File

@ -11,7 +11,9 @@ import { Plugin } from '@nocobase/client';
import { Registry, tval } from '@nocobase/utils/client';
import { ComponentType } from 'react';
import { NAMESPACE } from './locale';
import { UserDataSyncSource } from './UserDataSyncSource';
// import { UserDataSyncSource } from './UserDataSyncSource';
import { lazy } from '@nocobase/client';
const { UserDataSyncSource } = lazy(() => import('./UserDataSyncSource'), 'UserDataSyncSource');
export type SourceOptions = {
components: Partial<{

View File

@ -9,9 +9,12 @@
import { Plugin } from '@nocobase/client';
import { tval } from '@nocobase/utils/client';
import { UsersManagement } from './UsersManagement';
// import { UsersManagement } from './UsersManagement';
import ACLPlugin from '@nocobase/plugin-acl/client';
import { RoleUsersManager } from './RoleUsersManager';
// import { RoleUsersManager } from './RoleUsersManager';
import { lazy } from '@nocobase/client';
const { UsersManagement } = lazy(() => import('./UsersManagement'), 'UsersManagement');
const { RoleUsersManager } = lazy(() => import('./RoleUsersManager'), 'RoleUsersManager');
class PluginUsersClient extends Plugin {
async load() {

View File

@ -8,7 +8,9 @@
*/
import { Plugin } from '@nocobase/client';
import { VerificationProviders } from './VerificationProviders';
// import { VerificationProviders } from './VerificationProviders';
import { lazy } from '@nocobase/client';
const { VerificationProviders } = lazy(() => import('./VerificationProviders'), 'VerificationProviders');
import { NAMESPACE } from './locale';
export class PluginVerificationClient extends Plugin {

View File

@ -14,9 +14,14 @@ import { isValid } from '@formily/shared';
import { Plugin, useCompile, WorkflowConfig } from '@nocobase/client';
import { Registry } from '@nocobase/utils/client';
import { ExecutionPage } from './ExecutionPage';
import { WorkflowPage } from './WorkflowPage';
import { WorkflowPane } from './WorkflowPane';
// import { ExecutionPage } from './ExecutionPage';
// import { WorkflowPage } from './WorkflowPage';
// import { WorkflowPane } from './WorkflowPane';
import { lazy } from '@nocobase/client';
const { ExecutionPage } = lazy(() => import('./ExecutionPage'), 'ExecutionPage');
const { WorkflowPage } = lazy(() => import('./WorkflowPage'), 'WorkflowPage');
const { WorkflowPane } = lazy(() => import('./WorkflowPane'), 'WorkflowPane');
import { Trigger } from './triggers';
import CollectionTrigger from './triggers/collection';
import ScheduleTrigger from './triggers/schedule';

View File

@ -30891,4 +30891,4 @@ zustand@^4.4.1:
zwitch@^2.0.0, zwitch@^2.0.4:
version "2.0.4"
resolved "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"
integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==
integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==