refactor: remove error-fallback and __builtins__ from plugin-core
This commit is contained in:
parent
dbd5622f27
commit
8d580b72b2
@ -1,3 +0,0 @@
|
||||
export * from './useConfig';
|
||||
export * from './usePrefixCls';
|
||||
export * from './useToken';
|
@ -1,5 +0,0 @@
|
||||
import { ConfigProvider } from 'antd';
|
||||
import { useContext } from 'react';
|
||||
|
||||
const { ConfigContext } = ConfigProvider;
|
||||
export const useConfig = () => useContext(ConfigContext);
|
@ -1,17 +0,0 @@
|
||||
import { ConfigProvider } from 'antd';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const usePrefixCls = (
|
||||
tag?: string,
|
||||
props?: {
|
||||
prefixCls?: string;
|
||||
},
|
||||
) => {
|
||||
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext) || {};
|
||||
if ('ConfigContext' in ConfigProvider) {
|
||||
return getPrefixCls?.(tag, props?.prefixCls) || '';
|
||||
} else {
|
||||
const prefix = props?.prefixCls ?? 'ant-';
|
||||
return `${prefix}${tag ?? ''}`;
|
||||
}
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
import { theme } from 'antd';
|
||||
import { CustomToken } from '../style';
|
||||
|
||||
interface Result extends ReturnType<typeof theme.useToken> {
|
||||
token: CustomToken;
|
||||
}
|
||||
|
||||
const useToken = () => {
|
||||
const result = theme.useToken();
|
||||
return result as Result;
|
||||
};
|
||||
|
||||
export { useToken };
|
@ -1,4 +0,0 @@
|
||||
export * from './hooks';
|
||||
export * from './loading';
|
||||
export * from './portal';
|
||||
export * from './style';
|
@ -1,14 +0,0 @@
|
||||
import { message } from 'antd';
|
||||
|
||||
export const loading = async (title: React.ReactNode = 'Loading...', processor: () => Promise<any>) => {
|
||||
let hide: any = null;
|
||||
const loading = setTimeout(() => {
|
||||
hide = message.loading(title);
|
||||
}, 100);
|
||||
try {
|
||||
return await processor();
|
||||
} finally {
|
||||
hide?.();
|
||||
clearTimeout(loading);
|
||||
}
|
||||
};
|
@ -1,63 +0,0 @@
|
||||
import { Observer, ReactFC } from '@formily/react';
|
||||
import { observable } from '@formily/reactive';
|
||||
import React, { Fragment } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { render as reactRender, unmount as reactUnmount } from './render';
|
||||
export interface IPortalProps {
|
||||
id?: string | symbol;
|
||||
}
|
||||
|
||||
const PortalMap = observable(new Map<string | symbol, React.ReactNode>());
|
||||
|
||||
export const createPortalProvider = (id: string | symbol) => {
|
||||
const Portal: ReactFC<IPortalProps> = (props) => {
|
||||
if (props.id && !PortalMap.has(props.id)) {
|
||||
PortalMap.set(props.id, null);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{props.children}
|
||||
<Observer>
|
||||
{() => {
|
||||
if (!props.id) return <></>;
|
||||
const portal = PortalMap.get(props.id);
|
||||
if (portal) return createPortal(portal, document.body);
|
||||
return <></>;
|
||||
}}
|
||||
</Observer>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
Portal.defaultProps = {
|
||||
id,
|
||||
};
|
||||
return Portal;
|
||||
};
|
||||
|
||||
export function createPortalRoot<T extends React.ReactNode>(host: HTMLElement, id: string) {
|
||||
function render(renderer?: () => T) {
|
||||
if (PortalMap.has(id)) {
|
||||
PortalMap.set(id, renderer?.());
|
||||
} else if (host) {
|
||||
reactRender(<Fragment>{renderer?.()}</Fragment>, host);
|
||||
}
|
||||
}
|
||||
|
||||
function unmount() {
|
||||
if (PortalMap.has(id)) {
|
||||
PortalMap.set(id, null);
|
||||
}
|
||||
if (host) {
|
||||
const unmountResult = reactUnmount(host);
|
||||
if (unmountResult && host.parentNode) {
|
||||
host.parentNode?.removeChild(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
render,
|
||||
unmount,
|
||||
};
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
import { ReactElement } from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import type { Root } from 'react-dom/client';
|
||||
|
||||
// 移植自rc-util: https://github.com/react-component/util/blob/master/src/React/render.ts
|
||||
|
||||
type CreateRoot = (container: ContainerType) => Root;
|
||||
|
||||
// Let compiler not to search module usage
|
||||
const fullClone = {
|
||||
...ReactDOM,
|
||||
} as typeof ReactDOM & {
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?: {
|
||||
usingClientEntryPoint?: boolean;
|
||||
};
|
||||
createRoot?: CreateRoot;
|
||||
};
|
||||
|
||||
const { version, render: reactRender, unmountComponentAtNode } = fullClone;
|
||||
|
||||
let createRoot: CreateRoot;
|
||||
try {
|
||||
const mainVersion = Number((version || '').split('.')[0]);
|
||||
if (mainVersion >= 18 && fullClone.createRoot) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
createRoot = fullClone.createRoot;
|
||||
}
|
||||
} catch (e) {
|
||||
// Do nothing;
|
||||
}
|
||||
|
||||
function toggleWarning(skip: boolean) {
|
||||
const { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED } = fullClone;
|
||||
|
||||
if (
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED &&
|
||||
typeof __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED === 'object'
|
||||
) {
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.usingClientEntryPoint = skip;
|
||||
}
|
||||
}
|
||||
|
||||
const MARK = '__antd_mobile_root__';
|
||||
|
||||
// ========================== Render ==========================
|
||||
type ContainerType = (Element | DocumentFragment) & {
|
||||
[MARK]?: Root;
|
||||
};
|
||||
|
||||
function legacyRender(node: ReactElement, container: ContainerType) {
|
||||
reactRender(node, container);
|
||||
}
|
||||
|
||||
function concurrentRender(node: ReactElement, container: ContainerType) {
|
||||
toggleWarning(true);
|
||||
const root = container[MARK] || createRoot(container);
|
||||
toggleWarning(false);
|
||||
root.render(node);
|
||||
container[MARK] = root;
|
||||
}
|
||||
|
||||
export function render(node: ReactElement, container: ContainerType) {
|
||||
if (createRoot as unknown) {
|
||||
concurrentRender(node, container);
|
||||
return;
|
||||
}
|
||||
legacyRender(node, container);
|
||||
}
|
||||
|
||||
// ========================== Unmount =========================
|
||||
function legacyUnmount(container: ContainerType) {
|
||||
return unmountComponentAtNode(container);
|
||||
}
|
||||
|
||||
async function concurrentUnmount(container: ContainerType) {
|
||||
// Delay to unmount to avoid React 18 sync warning
|
||||
return Promise.resolve().then(() => {
|
||||
container[MARK]?.unmount();
|
||||
delete container[MARK];
|
||||
});
|
||||
}
|
||||
|
||||
export function unmount(container: ContainerType) {
|
||||
if (createRoot as unknown) {
|
||||
return concurrentUnmount(container);
|
||||
}
|
||||
|
||||
return legacyUnmount(container);
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
|
||||
import { useStyleRegister } from '@ant-design/cssinjs';
|
||||
import { merge } from '@formily/shared';
|
||||
import type { ComponentTokenMap, GlobalToken } from 'antd/es/theme/interface';
|
||||
import { AliasToken } from 'antd/es/theme/internal';
|
||||
import { useConfig, usePrefixCls, useToken } from './hooks';
|
||||
|
||||
export interface CustomToken extends AliasToken {
|
||||
/** 顶部导航栏主色 */
|
||||
colorPrimaryHeader: string;
|
||||
/** 导航栏背景色 */
|
||||
colorBgHeader: string;
|
||||
/** 导航栏菜单背景色悬浮态 */
|
||||
colorBgHeaderMenuHover: string;
|
||||
/** 导航栏菜单背景色激活态 */
|
||||
colorBgHeaderMenuActive: string;
|
||||
/** 导航栏菜单文本色 */
|
||||
colorTextHeaderMenu: string;
|
||||
/** 导航栏菜单文本色悬浮态 */
|
||||
colorTextHeaderMenuHover: string;
|
||||
/** 导航栏菜单文本色激活态 */
|
||||
colorTextHeaderMenuActive: string;
|
||||
|
||||
/** UI 配置色 */
|
||||
colorSettings: string;
|
||||
/** 鼠标悬浮时显示的背景色 */
|
||||
colorBgSettingsHover: string;
|
||||
/** 鼠标悬浮时显示的边框色 */
|
||||
colorBorderSettingsHover: string;
|
||||
}
|
||||
|
||||
export type OverrideComponent = keyof ComponentTokenMap | string;
|
||||
|
||||
export interface StyleInfo {
|
||||
hashId: string;
|
||||
prefixCls: string;
|
||||
rootPrefixCls: string;
|
||||
iconPrefixCls: string;
|
||||
}
|
||||
|
||||
export type TokenWithCommonCls<T> = T & {
|
||||
/** Wrap component class with `.` prefix */
|
||||
componentCls: string;
|
||||
/** Origin prefix which do not have `.` prefix */
|
||||
prefixCls: string;
|
||||
/** Wrap icon class with `.` prefix */
|
||||
iconCls: string;
|
||||
/** Wrap ant prefixCls class with `.` prefix */
|
||||
antCls: string;
|
||||
};
|
||||
|
||||
export type GenerateStyle<
|
||||
ComponentToken extends object = TokenWithCommonCls<GlobalToken>,
|
||||
ReturnType = CSSInterpolation,
|
||||
> = (token: ComponentToken, options?: any) => ReturnType;
|
||||
|
||||
export const genCommonStyle = (token: any, componentPrefixCls: string): CSSObject => {
|
||||
const { fontFamily, fontSize } = token;
|
||||
|
||||
const rootPrefixSelector = `[class^="${componentPrefixCls}"], [class*=" ${componentPrefixCls}"]`;
|
||||
|
||||
return {
|
||||
[rootPrefixSelector]: {
|
||||
fontFamily,
|
||||
fontSize,
|
||||
boxSizing: 'border-box',
|
||||
|
||||
'&::before, &::after': {
|
||||
boxSizing: 'border-box',
|
||||
},
|
||||
|
||||
[rootPrefixSelector]: {
|
||||
boxSizing: 'border-box',
|
||||
|
||||
'&::before, &::after': {
|
||||
boxSizing: 'border-box',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
export type UseComponentStyleResult = {
|
||||
wrapSSR: ReturnType<typeof useStyleRegister>;
|
||||
hashId: string;
|
||||
componentCls: string;
|
||||
rootPrefixCls: string;
|
||||
};
|
||||
|
||||
export const genStyleHook = <ComponentName extends OverrideComponent>(
|
||||
component: ComponentName,
|
||||
styleFn: (token: TokenWithCommonCls<CustomToken>, props: any, info: StyleInfo) => CSSInterpolation,
|
||||
) => {
|
||||
return (props?: any): UseComponentStyleResult => {
|
||||
const { theme, token, hashId } = useToken();
|
||||
const { getPrefixCls, iconPrefixCls } = useConfig();
|
||||
const prefixCls = usePrefixCls(component);
|
||||
const rootPrefixCls = getPrefixCls();
|
||||
|
||||
return {
|
||||
wrapSSR: useStyleRegister(
|
||||
{
|
||||
theme: theme as any,
|
||||
token,
|
||||
hashId,
|
||||
path: ['formily-antd', component, prefixCls, iconPrefixCls],
|
||||
},
|
||||
() => {
|
||||
const componentCls = `.${prefixCls}`;
|
||||
const mergedToken: TokenWithCommonCls<CustomToken> = merge(token, {
|
||||
componentCls,
|
||||
prefixCls,
|
||||
iconCls: `.${iconPrefixCls}`,
|
||||
antCls: `.${rootPrefixCls}`,
|
||||
});
|
||||
|
||||
const styleInterpolation = styleFn(mergedToken, props, {
|
||||
hashId,
|
||||
prefixCls,
|
||||
rootPrefixCls,
|
||||
iconPrefixCls,
|
||||
});
|
||||
return [genCommonStyle(token, prefixCls), styleInterpolation];
|
||||
},
|
||||
),
|
||||
hashId,
|
||||
componentCls: prefixCls,
|
||||
rootPrefixCls,
|
||||
};
|
||||
};
|
||||
};
|
@ -1,45 +0,0 @@
|
||||
import { Button, Result, Typography } from 'antd';
|
||||
import React, { FC } from 'react';
|
||||
import { FallbackProps, useErrorBoundary } from 'react-error-boundary';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
const { Paragraph, Text, Link } = Typography;
|
||||
|
||||
export const ErrorFallback: FC<FallbackProps> = ({ error }) => {
|
||||
const { resetBoundary } = useErrorBoundary();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const subTitle = (
|
||||
<Trans>
|
||||
{'This is likely a NocoBase internals bug. Please open an issue at '}
|
||||
<Link href="https://github.com/nocobase/nocobase/issues" target="_blank">
|
||||
here
|
||||
</Link>
|
||||
</Trans>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: 'white' }}>
|
||||
<Result
|
||||
style={{ maxWidth: '60vw', margin: 'auto' }}
|
||||
status="error"
|
||||
title={t('Render Failed')}
|
||||
subTitle={subTitle}
|
||||
extra={[
|
||||
<Button type="primary" key="feedback" href="https://github.com/nocobase/nocobase/issues" target="_blank">
|
||||
{t('Feedback')}
|
||||
</Button>,
|
||||
<Button key="try" onClick={resetBoundary}>
|
||||
{t('Try again')}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Paragraph copyable>
|
||||
<Text type="danger" style={{ whiteSpace: 'pre-line', textAlign: 'center' }}>
|
||||
{error.stack}
|
||||
</Text>
|
||||
</Paragraph>
|
||||
</Result>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from 'testUtils';
|
||||
import App1 from '../demos/demo1';
|
||||
|
||||
describe('ErrorFallback', () => {
|
||||
it('should render correctly', () => {
|
||||
render(<App1 />);
|
||||
|
||||
expect(screen.getByText(/render failed/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/this is likely a nocobase internals bug\. please open an issue at/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: /feedback/i })).toBeInTheDocument();
|
||||
expect(screen.getByText(/try again/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/error: error message/i)).toBeInTheDocument();
|
||||
|
||||
// 底部复制按钮
|
||||
expect(document.querySelector('.ant-typography-copy')).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { ErrorFallback } from '../ErrorFallback';
|
||||
|
||||
const App = () => {
|
||||
throw new Error('error message');
|
||||
};
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback} onError={console.error}>
|
||||
<App />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
group:
|
||||
title: Schema Components
|
||||
---
|
||||
|
||||
# ErrorFallback
|
||||
|
||||
## Example
|
||||
|
||||
<code src="./demos/demo1.tsx"></code>
|
@ -1 +0,0 @@
|
||||
export * from './ErrorFallback';
|
Loading…
Reference in New Issue
Block a user