refactor: migrate @emotion/css to antd-style (#1043)

Reviewed-on: daoyoucloud/tachybase#1043
This commit is contained in:
sealday 2024-05-23 21:01:46 +08:00
parent 63c351ea1a
commit f2a2cc228f
67 changed files with 2060 additions and 1983 deletions

View File

@ -1,8 +1,10 @@
import Icon, { RightOutlined } from '@ant-design/icons';
import { css } from '@emotion/css';
import { uid } from '@tachybase/schema';
import { Menu, MenuProps, theme } from 'antd';
import React, { FC, ReactNode, useMemo } from 'react'; import React, { FC, ReactNode, useMemo } from 'react';
import { uid } from '@tachybase/schema';
import Icon, { RightOutlined } from '@ant-design/icons';
import { Menu, MenuProps, theme } from 'antd';
import { createStyles } from 'antd-style';
import { useCompile } from '../../../schema-component'; import { useCompile } from '../../../schema-component';
import { useSchemaInitializerItem } from '../context'; import { useSchemaInitializerItem } from '../context';
import { useSchemaInitializerMenuItems } from '../hooks'; import { useSchemaInitializerMenuItems } from '../hooks';
@ -18,6 +20,30 @@ export interface SchemaInitializerSubMenuProps {
children?: SchemaInitializerOptions['items']; children?: SchemaInitializerOptions['items'];
} }
const useStyles = createStyles(({ css, token }) => {
return {
menu: css`
box-shadow: none !important;
border-inline-end: 0 !important;
.ant-menu-sub {
max-height: 50vh !important;
}
.ant-menu-item {
margin-block: 0;
}
.ant-menu-root {
margin: 0 -${token.margin}px;
.ant-menu-submenu-title,
.ant-menu-item-only-child {
margin-inline: 0;
margin-block: 0;
width: 100%;
}
}
`,
};
});
const SchemaInitializerSubMenuContext = React.createContext<{ isInMenu?: true }>({}); const SchemaInitializerSubMenuContext = React.createContext<{ isInMenu?: true }>({});
const SchemaInitializerMenuProvider = (props) => { const SchemaInitializerMenuProvider = (props) => {
return ( return (
@ -34,6 +60,7 @@ export const SchemaInitializerMenu: FC<MenuProps> = (props) => {
const { componentCls, hashId } = useSchemaInitializerStyles(); const { componentCls, hashId } = useSchemaInitializerStyles();
const { items, ...others } = props; const { items, ...others } = props;
const { token } = theme.useToken(); const { token } = theme.useToken();
const { styles } = useStyles();
const itemsWithPopupClass = useMemo( const itemsWithPopupClass = useMemo(
() => items.map((item) => ({ ...item, popupClassName: `${hashId} ${componentCls}-menu-sub` })), () => items.map((item) => ({ ...item, popupClassName: `${hashId} ${componentCls}-menu-sub` })),
[componentCls, hashId, items], [componentCls, hashId, items],
@ -44,25 +71,7 @@ export const SchemaInitializerMenu: FC<MenuProps> = (props) => {
<Menu <Menu
selectable={false} selectable={false}
expandIcon={<RightOutlined style={{ fontSize: token.fontSizeSM, color: token.colorTextDescription }} />} expandIcon={<RightOutlined style={{ fontSize: token.fontSizeSM, color: token.colorTextDescription }} />}
rootClassName={css` rootClassName={styles.menu}
box-shadow: none !important;
border-inline-end: 0 !important;
.ant-menu-sub {
max-height: 50vh !important;
}
.ant-menu-item {
margin-block: 0;
}
.ant-menu-root {
margin: 0 -${token.margin}px;
.ant-menu-submenu-title,
.ant-menu-item-only-child {
margin-inline: 0;
margin-block: 0;
width: 100%;
}
}
`}
items={itemsWithPopupClass} items={itemsWithPopupClass}
{...others} {...others}
/> />

View File

@ -1,8 +1,9 @@
import { ISchema, observer } from '@tachybase/schema';
import { ConfigProvider, Popover, theme } from 'antd';
import React, { ComponentType, useCallback, useMemo, useState } from 'react'; import React, { ComponentType, useCallback, useMemo, useState } from 'react';
import { ISchema, observer } from '@tachybase/schema';
import { ConfigProvider, Popover, theme } from 'antd';
import { createStyles } from 'antd-style';
import { css } from '@emotion/css';
import { useNiceDropdownMaxHeight } from '../../../common/useNiceDropdownHeight'; import { useNiceDropdownMaxHeight } from '../../../common/useNiceDropdownHeight';
import { useFlag } from '../../../flag-provider'; import { useFlag } from '../../../flag-provider';
import { useDesignable } from '../../../schema-component'; import { useDesignable } from '../../../schema-component';
@ -11,6 +12,18 @@ import { SchemaInitializerContext } from '../context';
import { SchemaInitializerOptions } from '../types'; import { SchemaInitializerOptions } from '../types';
const defaultWrap = (s: ISchema) => s; const defaultWrap = (s: ISchema) => s;
const useStyles = createStyles(({ css, token }) => {
return {
popover: css`
.ant-popover-inner {
padding: ${`${token.paddingXXS}px 0`};
.ant-menu-submenu-title {
margin-block: 0;
}
}
`,
};
});
export function withInitializer<T>(C: ComponentType<T>) { export function withInitializer<T>(C: ComponentType<T>) {
const WithInitializer = observer( const WithInitializer = observer(
@ -47,6 +60,7 @@ export function withInitializer<T>(C: ComponentType<T>) {
const { wrapSSR, hashId, componentCls } = useSchemaInitializerStyles(); const { wrapSSR, hashId, componentCls } = useSchemaInitializerStyles();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { token } = theme.useToken(); const { token } = theme.useToken();
const { styles } = useStyles();
const dropdownMaxHeight = useNiceDropdownMaxHeight([visible]); const dropdownMaxHeight = useNiceDropdownMaxHeight([visible]);
const cProps = useMemo( const cProps = useMemo(
@ -79,14 +93,7 @@ export function withInitializer<T>(C: ComponentType<T>) {
placement={'bottomLeft'} placement={'bottomLeft'}
{...popoverProps} {...popoverProps}
arrow={false} arrow={false}
overlayClassName={css` overlayClassName={styles.popover}
.ant-popover-inner {
padding: ${`${token.paddingXXS}px 0`};
.ant-menu-submenu-title {
margin-block: 0;
}
}
`}
open={visible} open={visible}
onOpenChange={setVisible} onOpenChange={setVisible}
content={wrapSSR( content={wrapSSR(

View File

@ -1,28 +1,31 @@
import { css } from '@emotion/css'; import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { Field, GeneralField } from '@tachybase/schema'; import { Field, GeneralField, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { Col, Row } from 'antd'; import { Col, Row } from 'antd';
import { createStyles } from 'antd-style';
import merge from 'deepmerge'; import merge from 'deepmerge';
import template from 'lodash/template'; import template from 'lodash/template';
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { import {
DataBlockProvider, DataBlockProvider,
TableFieldResource, TableFieldResource,
WithoutTableFieldResource, useCollectionManager,
useCollectionParentRecord, useCollectionParentRecord,
useCollectionParentRecordData,
useCollectionRecord, useCollectionRecord,
useDataBlockProps, useDataBlockProps,
useDataBlockRequest, useDataBlockRequest,
useDataBlockResource, useDataBlockResource,
useDesignable, useDesignable,
useRecord, useRecord,
WithoutTableFieldResource,
} from '../'; } from '../';
import { ACLCollectionProvider } from '../acl/ACLProvider'; import { ACLCollectionProvider } from '../acl/ACLProvider';
import { import {
CollectionProvider_deprecated, CollectionProvider_deprecated,
useCollectionManager_deprecated,
useCollection_deprecated, useCollection_deprecated,
useCollectionManager_deprecated,
} from '../collection-manager'; } from '../collection-manager';
import { DataBlockCollector } from '../filter-provider/FilterProvider'; import { DataBlockCollector } from '../filter-provider/FilterProvider';
import { RecordProvider, useRecordIndex } from '../record-provider'; import { RecordProvider, useRecordIndex } from '../record-provider';
@ -119,9 +122,31 @@ export const useBlockRequestContext = () => {
return useContext(BlockRequestContext_deprecated); return useContext(BlockRequestContext_deprecated);
}; };
const useStyles = createStyles(({ css }) => {
return {
row: css`
height: 100%;
`,
left: css`
width: 200px;
flex: 0 0 auto;
`,
right: css`
flex: 1 1 auto;
min-width: 0;
`,
rightContainer: css`
display: flex;
flex-direction: column;
height: 100%;
`,
};
});
export const RenderChildrenWithAssociationFilter: React.FC<any> = (props) => { export const RenderChildrenWithAssociationFilter: React.FC<any> = (props) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { findComponent } = useDesignable(); const { findComponent } = useDesignable();
const { styles } = useStyles();
const field = useField(); const field = useField();
const Component = findComponent(field.component?.[0]) || React.Fragment; const Component = findComponent(field.component?.[0]) || React.Fragment;
const associationFilterSchema = fieldSchema.reduceProperties((buf, s) => { const associationFilterSchema = fieldSchema.reduceProperties((buf, s) => {
@ -134,39 +159,16 @@ export const RenderChildrenWithAssociationFilter: React.FC<any> = (props) => {
if (associationFilterSchema) { if (associationFilterSchema) {
return ( return (
<Component {...field.componentProps}> <Component {...field.componentProps}>
<Row <Row className={styles.row} gutter={16} wrap={false}>
className={css` <Col className={styles.left} style={props.associationFilterStyle}>
height: 100%;
`}
gutter={16}
wrap={false}
>
<Col
className={css`
width: 200px;
flex: 0 0 auto;
`}
style={props.associationFilterStyle}
>
<RecursionField <RecursionField
schema={fieldSchema} schema={fieldSchema}
onlyRenderProperties onlyRenderProperties
filterProperties={(s) => s['x-component'] === 'AssociationFilter'} filterProperties={(s) => s['x-component'] === 'AssociationFilter'}
/> />
</Col> </Col>
<Col <Col className={styles.right}>
className={css` <div className={styles.rightContainer}>
flex: 1 1 auto;
min-width: 0;
`}
>
<div
className={css`
display: flex;
flex-direction: column;
height: 100%;
`}
>
<RecursionField <RecursionField
schema={fieldSchema} schema={fieldSchema}
onlyRenderProperties onlyRenderProperties

View File

@ -1,9 +1,11 @@
import { DeleteOutlined, ExclamationCircleFilled } from '@ant-design/icons';
import { css } from '@emotion/css';
import { useForm } from '@tachybase/schema';
import { Button, message } from 'antd';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useForm } from '@tachybase/schema';
import { DeleteOutlined, ExclamationCircleFilled } from '@ant-design/icons';
import { Button, message } from 'antd';
import { createStyles } from 'antd-style';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
useActionContext, useActionContext,
useCollectionManager_deprecated, useCollectionManager_deprecated,
@ -54,6 +56,20 @@ export const useDestroyAction = () => {
}; };
}; };
const useStyles = createStyles(({ css }) => {
return {
modal: css`
.ant-modal-body {
margin-left: 35px;
margin-bottom: 35px;
.ant-checkbox-wrapper {
height: 25px;
}
}
`,
};
});
export const useBulkDestroyAction = () => { export const useBulkDestroyAction = () => {
const { state, setState, refresh } = useResourceActionContext(); const { state, setState, refresh } = useResourceActionContext();
const { resource } = useResourceContext(); const { resource } = useResourceContext();
@ -80,6 +96,7 @@ export const useBulkDestroyAction = () => {
export const DeleteCollectionAction = (props) => { export const DeleteCollectionAction = (props) => {
const { scope, getContainer, item: record, children, isBulk, useAction, ...otherProps } = props; const { scope, getContainer, item: record, children, isBulk, useAction, ...otherProps } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const { styles } = useStyles();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const getDestroyCollectionAction = () => { const getDestroyCollectionAction = () => {
if (isBulk) { if (isBulk) {
@ -124,15 +141,7 @@ export const DeleteCollectionAction = (props) => {
'x-component-props': { 'x-component-props': {
width: 520, width: 520,
getContainer: '{{ getContainer }}', getContainer: '{{ getContainer }}',
className: css` className: styles.modal,
.ant-modal-body {
margin-left: 35px;
margin-bottom: 35px;
.ant-checkbox-wrapper {
height: 25px;
}
}
`,
}, },
properties: { properties: {
info: { info: {

View File

@ -1,16 +1,29 @@
import { useField, useForm } from '@tachybase/schema';
import { useAsyncData } from '../../../../async-data-provider';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { Input, SchemaComponent } from '../../../../schema-component'; import { Field, useField } from '@tachybase/schema';
import { css } from '@emotion/css';
import { Button } from 'antd';
import { EditOutlined, RightSquareOutlined } from '@ant-design/icons'; import { EditOutlined, RightSquareOutlined } from '@ant-design/icons';
import { Button } from 'antd';
import { createStyles } from 'antd-style';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Field } from '@tachybase/schema';
import { useAsyncData } from '../../../../async-data-provider';
import { Input } from '../../../../schema-component';
const useStyles = createStyles(({ css }) => {
return {
input: css`
position: relative;
.ant-input {
width: 100%;
}
`,
};
});
export const SQLInput = ({ disabled }) => { export const SQLInput = ({ disabled }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { run, loading, error } = useAsyncData(); const { run, loading, error } = useAsyncData();
const { styles } = useStyles();
const field = useField<Field>(); const field = useField<Field>();
const execute = () => { const execute = () => {
if (!field.value) { if (!field.value) {
@ -39,14 +52,7 @@ export const SQLInput = ({ disabled }) => {
}, [field, error]); }, [field, error]);
return ( return (
<div <div className={styles.input}>
className={css`
position: relative;
.ant-input {
width: 100%;
}
`}
>
<Input.TextArea value={field.value} disabled={disabled} onChange={(e) => (field.value = e.target.value)} /> <Input.TextArea value={field.value} disabled={disabled} onChange={(e) => (field.value = e.target.value)} />
<Button.Group> <Button.Group>
<Button onClick={toggleEditing} ghost size="small" type="primary" icon={<EditOutlined />}> <Button onClick={toggleEditing} ghost size="small" type="primary" icon={<EditOutlined />}>

View File

@ -1,4 +1,3 @@
import { css } from '@emotion/css';
import { PresetFields } from '../components/PresetFields'; import { PresetFields } from '../components/PresetFields';
export const defaultConfigurableProperties = { export const defaultConfigurableProperties = {
@ -56,13 +55,6 @@ export const defaultConfigurableProperties = {
type: 'void', type: 'void',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-visible': '{{ createOnly }}', 'x-visible': '{{ createOnly }}',
'x-decorator-props': {
className: css`
.ant-formily-item {
margin-bottom: 0;
}
`,
},
'x-component': PresetFields, 'x-component': PresetFields,
}, },
}; };

View File

@ -1,7 +1,7 @@
import type { ThemeConfig as _ThemeConfig } from 'antd'; import type { ThemeConfig as _ThemeConfig } from 'antd';
import { AliasToken } from 'antd/es/theme/internal'; import { AliasToken } from 'antd/es/theme/internal';
export interface CustomToken extends AliasToken { export interface TachybaseToken {
/** 顶部导航栏主色 */ /** 顶部导航栏主色 */
colorPrimaryHeader: string; colorPrimaryHeader: string;
/** 导航栏背景色 */ /** 导航栏背景色 */
@ -25,7 +25,14 @@ export interface CustomToken extends AliasToken {
colorBorderSettingsHover: string; colorBorderSettingsHover: string;
} }
export interface CustomToken extends AliasToken, TachybaseToken {}
export interface ThemeConfig extends _ThemeConfig { export interface ThemeConfig extends _ThemeConfig {
name?: string; name?: string;
token?: Partial<CustomToken>; token?: Partial<CustomToken>;
} }
declare module 'antd-style' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface CustomToken extends TachybaseToken {}
}

View File

@ -7,7 +7,6 @@ import 'dayjs/plugin/localeData';
import 'dayjs/plugin/quarterOfYear'; import 'dayjs/plugin/quarterOfYear';
import 'dayjs/plugin/utc'; import 'dayjs/plugin/utc';
import 'dayjs/plugin/weekday'; import 'dayjs/plugin/weekday';
// 重置浏览器样式 // 重置浏览器样式
import 'antd/dist/reset.css'; import 'antd/dist/reset.css';
import './global.less'; import './global.less';

View File

@ -1,10 +1,12 @@
import { DisconnectOutlined, LoadingOutlined } from '@ant-design/icons'; import React, { FC } from 'react';
import { css } from '@emotion/css';
import { observer } from '@tachybase/schema'; import { observer } from '@tachybase/schema';
import { getSubAppName } from '@tachybase/sdk'; import { getSubAppName } from '@tachybase/sdk';
import { DisconnectOutlined, LoadingOutlined } from '@ant-design/icons';
import { Button, Modal, Result, Spin } from 'antd'; import { Button, Modal, Result, Spin } from 'antd';
import React, { FC } from 'react'; import { createStyles } from 'antd-style';
import { Navigate, useNavigate } from 'react-router-dom'; import { Navigate, useNavigate } from 'react-router-dom';
import { ACLPlugin } from '../acl'; import { ACLPlugin } from '../acl';
import { useAPIClient } from '../api-client'; import { useAPIClient } from '../api-client';
import { Application } from '../application'; import { Application } from '../application';
@ -30,6 +32,17 @@ const AppSpin = () => {
); );
}; };
const useStyles = createStyles(({ css }) => {
return {
modal: css`
top: 50%;
position: absolute;
width: 100%;
transform: translate(0, -50%);
`,
};
});
const useErrorProps = (app: Application, error: any) => { const useErrorProps = (app: Application, error: any) => {
const api = useAPIClient(); const api = useAPIClient();
if (!error) { if (!error) {
@ -68,15 +81,11 @@ const useErrorProps = (app: Application, error: any) => {
const AppError: FC<{ error: Error; app: Application }> = observer( const AppError: FC<{ error: Error; app: Application }> = observer(
({ app, error }) => { ({ app, error }) => {
const props = useErrorProps(app, error); const props = useErrorProps(app, error);
const { styles } = useStyles();
return ( return (
<div> <div>
<Result <Result
className={css` className={styles.modal}
top: 50%;
position: absolute;
width: 100%;
transform: translate(0, -50%);
`}
status="error" status="error"
title={app.i18n.t('App error')} title={app.i18n.t('App error')}
subTitle={app.i18n.t(error?.message)} subTitle={app.i18n.t(error?.message)}
@ -201,15 +210,11 @@ const getProps = (app: Application) => {
const AppMaintaining: FC<{ app: Application; error: Error }> = observer( const AppMaintaining: FC<{ app: Application; error: Error }> = observer(
({ app }) => { ({ app }) => {
const { icon, status, title, subTitle } = getProps(app); const { icon, status, title, subTitle } = getProps(app);
const { styles } = useStyles();
return ( return (
<div> <div>
<Result <Result
className={css` className={styles.modal}
top: 50%;
position: absolute;
width: 100%;
transform: translate(0, -50%);
`}
icon={icon} icon={icon}
status={status} status={status}
title={app.i18n.t(title)} title={app.i18n.t(title)}

View File

@ -1,11 +1,13 @@
import { css } from '@emotion/css';
import { SchemaOptionsContext } from '@tachybase/schema';
import { get } from 'lodash';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { SchemaOptionsContext } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import { get } from 'lodash';
import { useACLRoleContext } from '../acl/ACLProvider'; import { useACLRoleContext } from '../acl/ACLProvider';
import { PinnedPluginListContext } from './context'; import { PinnedPluginListContext } from './context';
export const PinnedPluginListProvider: React.FC<{ items: any }> = (props) => { export const PinnedPluginListProvider = (props: { items: any; children: React.ReactNode }) => {
const { children, items } = props; const { children, items } = props;
const ctx = useContext(PinnedPluginListContext); const ctx = useContext(PinnedPluginListContext);
return ( return (
@ -15,30 +17,35 @@ export const PinnedPluginListProvider: React.FC<{ items: any }> = (props) => {
); );
}; };
const useStyles = createStyles(({ css }) => {
return {
container: css`
display: inline-block;
.ant-btn {
border: 0;
height: 46px;
width: 46px;
border-radius: 0;
background: none;
color: rgba(255, 255, 255, 0.65);
&:hover {
background: rgba(255, 255, 255, 0.1) !important;
}
}
`,
};
});
export const PinnedPluginList = () => { export const PinnedPluginList = () => {
const { allowAll, snippets } = useACLRoleContext(); const { allowAll, snippets } = useACLRoleContext();
const getSnippetsAllow = (aclKey) => { const getSnippetsAllow = (aclKey) => {
return allowAll || snippets?.includes(aclKey); return allowAll || snippets?.includes(aclKey);
}; };
const { styles } = useStyles();
const ctx = useContext(PinnedPluginListContext); const ctx = useContext(PinnedPluginListContext);
const { components } = useContext(SchemaOptionsContext); const { components } = useContext(SchemaOptionsContext);
return ( return (
<div <div className={styles.container}>
className={css`
.ant-btn {
border: 0;
height: 46px;
width: 46px;
border-radius: 0;
background: none;
color: rgba(255, 255, 255, 0.65);
&:hover {
background: rgba(255, 255, 255, 0.1) !important;
}
}
`}
style={{ display: 'inline-block' }}
>
{Object.keys(ctx.items) {Object.keys(ctx.items)
.sort((a, b) => ctx.items[a].order - ctx.items[b].order) .sort((a, b) => ctx.items[a].order - ctx.items[b].order)
.filter((key) => getSnippetsAllow(ctx.items[key].snippet)) .filter((key) => getSnippetsAllow(ctx.items[key].snippet))

View File

@ -1,18 +1,18 @@
import { App, Card, Divider, Popconfirm, Space, Switch, Typography, message } from 'antd';
import classnames from 'classnames';
import React, { FC, useState } from 'react'; import React, { FC, useState } from 'react';
import { ClockCircleOutlined, DeleteOutlined, ReadOutlined, ReloadOutlined, SettingOutlined } from '@ant-design/icons';
import { App, Card, Divider, message, Popconfirm, Space, Switch, Typography } from 'antd';
import classnames from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { DeleteOutlined, ReadOutlined, ReloadOutlined, SettingOutlined, ClockCircleOutlined } from '@ant-design/icons';
import { css } from '@emotion/css';
import { useAPIClient } from '../api-client'; import { useAPIClient } from '../api-client';
import { useApp } from '../application'; import { useApp } from '../application';
import { PluginDetail } from './PluginDetail'; import { PluginDetail } from './PluginDetail';
import { NPM_REGISTRY_ADDRESS } from './PluginForm/form/PluginNpmForm';
import { PluginUpgradeModal } from './PluginForm/modal/PluginUpgradeModal'; import { PluginUpgradeModal } from './PluginForm/modal/PluginUpgradeModal';
import { useStyles } from './style'; import { useStyles } from './style';
import type { IPluginData } from './types'; import type { IPluginData } from './types';
import { NPM_REGISTRY_ADDRESS } from './PluginForm/form/PluginNpmForm';
interface IPluginInfo extends IPluginCard { interface IPluginInfo extends IPluginCard {
onClick: () => void; onClick: () => void;
@ -111,28 +111,7 @@ function PluginInfo(props: IPluginInfo) {
// style={{ marginBottom: theme.marginLG }} // style={{ marginBottom: theme.marginLG }}
title={<div>{title}</div>} title={<div>{title}</div>}
hoverable hoverable
className={css` className={styles.card}
.ant-card-actions {
li .ant-space {
gap: 2px !important;
}
li a {
.anticon {
margin-right: 3px;
/* display: none; */
}
}
li:last-child {
width: 20% !important;
}
li:first-child {
width: 80% !important;
border-inline-end: 0;
text-align: left;
padding-left: 16px;
}
}
`}
actions={[ actions={[
<Space split={<Divider type="vertical" />} key={'1'}> <Space split={<Divider type="vertical" />} key={'1'}>
<a <a

View File

@ -1,39 +1,22 @@
export * from './PluginManagerLink'; import React, { useEffect, useMemo, useState } from 'react';
import type { TableProps } from 'antd'; import { fuzzysearch } from '@tachybase/utils/client';
import { PageHeader } from '@ant-design/pro-layout'; import { PageHeader } from '@ant-design/pro-layout';
import { useDebounce } from 'ahooks'; import { useDebounce } from 'ahooks';
import { import { Card, Form, Input, Radio, Result, Select, Space, Spin, Table, Tag, type TableProps } from 'antd';
Button,
Card,
Col,
Divider,
Input,
List,
Result,
Row,
Space,
Spin,
Tabs,
Table,
Tag,
Radio,
Form,
Select,
} from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { css } from '@emotion/css';
import { useACLRoleContext } from '../acl/ACLProvider'; import { useACLRoleContext } from '../acl/ACLProvider';
import { useRequest } from '../api-client'; import { useRequest } from '../api-client';
import { i18n } from '../i18n';
import { useToken } from '../style'; import { useToken } from '../style';
import { PluginCard, SwitchAction } from './PluginCard'; import { SwitchAction } from './PluginCard';
import { useStyles } from './style'; import { useStyles } from './style';
import { IPluginData } from './types'; import { IPluginData } from './types';
import { fuzzysearch } from '@tachybase/utils/client';
import { i18n } from '../i18n'; export * from './PluginManagerLink';
export interface TData { export interface TData {
data: IPluginData[]; data: IPluginData[];

View File

@ -1,11 +1,12 @@
import React from 'react';
import { ApiOutlined, SettingOutlined } from '@ant-design/icons'; import { ApiOutlined, SettingOutlined } from '@ant-design/icons';
import { css } from '@emotion/css'; import { Button, Dropdown, Tooltip } from 'antd';
import { Button, Card, Dropdown, Popover, Tooltip } from 'antd';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { useApp } from '../application'; import { useApp } from '../application';
import { ActionContextProvider, useCompile } from '../schema-component'; import { useCompile } from '../schema-component';
import { useToken } from '../style'; import { useToken } from '../style';
export const PluginManagerLink = () => { export const PluginManagerLink = () => {
@ -27,14 +28,10 @@ export const PluginManagerLink = () => {
}; };
export const SettingsCenterDropdown = () => { export const SettingsCenterDropdown = () => {
const [visible, setVisible] = useState(false);
const compile = useCompile(); const compile = useCompile();
const { t } = useTranslation();
const { token } = useToken(); const { token } = useToken();
const navigate = useNavigate();
const app = useApp(); const app = useApp();
const settings = app.pluginSettingsManager.getList(); const settings = app.pluginSettingsManager.getList();
const [open, setOpen] = useState(false);
return ( return (
<Dropdown <Dropdown
menu={{ menu={{
@ -56,86 +53,7 @@ export const SettingsCenterDropdown = () => {
<Button <Button
data-testid="plugin-settings-button" data-testid="plugin-settings-button"
icon={<SettingOutlined style={{ color: token.colorTextHeaderMenu }} />} icon={<SettingOutlined style={{ color: token.colorTextHeaderMenu }} />}
// title={t('All plugin settings')}
/> />
</Dropdown> </Dropdown>
); );
return (
settings.length > 0 && (
<ActionContextProvider value={{ visible, setVisible }}>
<Popover
open={open}
onOpenChange={(open) => {
setOpen(open);
}}
arrow={false}
content={
<div style={{ maxWidth: '21rem', overflow: 'auto', maxHeight: '50vh' }}>
<Card
bordered={false}
className={css`
box-shadow: none;
`}
style={{ boxShadow: 'none' }}
>
{settings
.filter((v) => v.isTopLevel !== false)
.map((setting) => (
<Card.Grid
style={{
width: settings.length === 1 ? '100%' : settings.length === 2 ? '50%' : '33.33%',
}}
className={css`
cursor: pointer;
padding: 0 !important;
box-shadow: none !important;
&:hover {
border-radius: ${token.borderRadius}px;
background: rgba(0, 0, 0, 0.045);
}
`}
key={setting.name}
>
<a
role="button"
aria-label={setting.name}
onClick={(e) => {
e.preventDefault();
setOpen(false);
navigate(setting.path);
}}
title={compile(setting.title)}
style={{ display: 'block', color: 'inherit', minWidth: '4.5rem', padding: token.marginSM }}
href={setting.path}
>
<div style={{ fontSize: '1.2rem', textAlign: 'center', marginBottom: '0.3rem' }}>
{setting.icon || <SettingOutlined />}
</div>
<div
style={{
textAlign: 'center',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
fontSize: token.fontSizeSM,
}}
>
{compile(setting.title)}
</div>
</a>
</Card.Grid>
))}
</Card>
</div>
}
>
<Button
data-testid="plugin-settings-button"
icon={<SettingOutlined style={{ color: token.colorTextHeaderMenu }} />}
// title={t('All plugin settings')}
/>
</Popover>
</ActionContextProvider>
)
);
}; };

View File

@ -1,12 +1,13 @@
import { PageHeader } from '@ant-design/pro-layout';
import { css } from '@emotion/css';
import { Layout, Menu, Result } from 'antd';
import _, { get } from 'lodash';
import React, { createContext, useCallback, useMemo } from 'react'; import React, { createContext, useCallback, useMemo } from 'react';
import { PageHeader } from '@ant-design/pro-layout';
import { Layout, Menu, Result } from 'antd';
import _ from 'lodash';
import { Navigate, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom'; import { Navigate, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom';
import { useStyles } from './style';
import { ADMIN_SETTINGS_PATH, PluginSettingsPageType, useApp } from '../application'; import { ADMIN_SETTINGS_PATH, PluginSettingsPageType, useApp } from '../application';
import { useCompile } from '../schema-component'; import { useCompile } from '../schema-component';
import { useStyles } from './style';
export const SettingsCenterContext = createContext<any>({}); export const SettingsCenterContext = createContext<any>({});
SettingsCenterContext.displayName = 'SettingsCenterContext'; SettingsCenterContext.displayName = 'SettingsCenterContext';

View File

@ -1,6 +1,6 @@
import { createStyles } from 'antd-style'; import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ token }) => { export const useStyles = createStyles(({ token, css }) => {
return { return {
cardActionDisabled: { cardActionDisabled: {
color: token.colorTextDisabled, color: token.colorTextDisabled,
@ -55,5 +55,27 @@ export const useStyles = createStyles(({ token }) => {
fontWeight: 'normal', fontWeight: 'normal',
fontSize: token.fontSize, fontSize: token.fontSize,
}, },
card: css`
.ant-card-actions {
li .ant-space {
gap: 2px !important;
}
li a {
.anticon {
margin-right: 3px;
/* display: none; */
}
}
li:last-child {
width: 20% !important;
}
li:first-child {
width: 80% !important;
border-inline-end: 0;
text-align: left;
padding-left: 16px;
}
}
`,
}; };
}); });

View File

@ -1,32 +1,154 @@
import { css } from '@emotion/css'; import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { useSessionStorageState } from 'ahooks'; import { useSessionStorageState } from 'ahooks';
import { App, ConfigProvider, Divider, Layout } from 'antd'; import { App, ConfigProvider, Divider, Layout } from 'antd';
import { createGlobalStyle } from 'antd-style'; import { createGlobalStyle, createStyles } from 'antd-style';
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { Link, Outlet, useLocation, useMatch, useNavigate, useParams } from 'react-router-dom'; import { Link, Outlet, useLocation, useMatch, useNavigate, useParams } from 'react-router-dom';
import { import {
ACLRolesCheckProvider, ACLRolesCheckProvider,
CurrentAppInfoProvider, CurrentAppInfoProvider,
CurrentUser, CurrentUser,
findByUid,
findMenuItem,
NavigateIfNotSignIn, NavigateIfNotSignIn,
PinnedPluginList, PinnedPluginList,
RemoteCollectionManagerProvider, RemoteCollectionManagerProvider,
RemoteSchemaTemplateManagerPlugin, RemoteSchemaTemplateManagerPlugin,
RemoteSchemaTemplateManagerProvider, RemoteSchemaTemplateManagerProvider,
SchemaComponent, SchemaComponent,
findByUid,
findMenuItem,
useACLRoleContext, useACLRoleContext,
useAdminSchemaUid, useAdminSchemaUid,
useDocumentTitle, useDocumentTitle,
useRequest, useRequest,
useSystemSettings, useSystemSettings,
useToken,
} from '../../../'; } from '../../../';
import { Plugin } from '../../../application/Plugin';
import { useAppSpin } from '../../../application/hooks/useAppSpin'; import { useAppSpin } from '../../../application/hooks/useAppSpin';
import { Plugin } from '../../../application/Plugin';
import { VariablesProvider } from '../../../variables'; import { VariablesProvider } from '../../../variables';
const useStyles = createStyles(({ css, token }) => {
return {
header: css`
.ant-menu.ant-menu-dark .ant-menu-item-selected,
.ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected,
.ant-menu-submenu-horizontal.ant-menu-submenu-selected {
background-color: ${token.colorBgHeaderMenuActive} !important;
color: ${token.colorTextHeaderMenuActive} !important;
}
.ant-menu-submenu-horizontal.ant-menu-submenu-selected > .ant-menu-submenu-title {
color: ${token.colorTextHeaderMenuActive} !important;
}
.ant-menu-dark.ant-menu-horizontal > .ant-menu-item:hover {
background-color: ${token.colorBgHeaderMenuHover} !important;
color: ${token.colorTextHeaderMenuHover} !important;
}
position: fixed;
left: 0;
right: 0;
height: var(--nb-header-height);
line-height: var(--nb-header-height);
padding: 0;
z-index: 100;
background-color: ${token.colorBgHeader} !important;
.ant-menu {
background-color: transparent;
}
.ant-menu-item,
.ant-menu-submenu-horizontal {
color: ${token.colorTextHeaderMenu} !important;
}
`,
headerA: css`
position: relative;
width: 100%;
height: 100%;
display: flex;
`,
headerB: css`
position: relative;
z-index: 1;
flex: 1 1 auto;
display: flex;
height: 100%;
`,
titleContainer: css`
display: inline-flex;
flex-shrink: 0;
color: #fff;
padding: 0;
align-items: center;
padding: 0 12px 0 12px;
`,
logo: css`
object-fit: contain;
height: 28px;
`,
title: css`
color: #fff;
height: 32px;
margin: 0 0 0 12px;
font-weight: 600;
font-size: 18px;
line-height: 32px;
`,
right: css`
position: relative;
flex-shrink: 0;
height: 100%;
z-index: 10;
`,
editor: css`
flex: 1 1 auto;
width: 0;
`,
sider: css`
height: 100%;
/* position: fixed; */
position: relative;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0);
z-index: 100;
.ant-layout-sider-children {
top: var(--nb-header-height);
position: fixed;
width: 200px;
height: calc(100vh - var(--nb-header-height));
}
`,
main: css`
display: flex;
flex-direction: column;
position: relative;
overflow-y: auto;
height: 100vh;
max-height: 100vh;
> div {
position: relative;
}
.ant-layout-footer {
position: absolute;
bottom: 0;
text-align: center;
width: 100%;
z-index: 0;
padding: 0px 50px;
}
`,
mainHeader: css`
flex-shrink: 0;
height: var(--nb-header-height);
line-height: var(--nb-header-height);
background: transparent;
pointer-events: none;
`,
};
});
const filterByACL = (schema, options) => { const filterByACL = (schema, options) => {
const { allowAll, allowMenuItemIds = [] } = options; const { allowAll, allowMenuItemIds = [] } = options;
if (allowAll) { if (allowAll) {
@ -279,111 +401,24 @@ export const InternalAdminLayout = (props: any) => {
const result = useSystemSettings(); const result = useSystemSettings();
// const { service } = useCollectionManager_deprecated(); // const { service } = useCollectionManager_deprecated();
const params = useParams<any>(); const params = useParams<any>();
const { token } = useToken(); const { styles } = useStyles();
return ( return (
<Layout> <Layout>
<GlobalStyleForAdminLayout /> <GlobalStyleForAdminLayout />
<Layout.Header <Layout.Header className={styles.header}>
className={css` <div className={styles.headerA}>
.ant-menu.ant-menu-dark .ant-menu-item-selected, <div className={styles.headerB}>
.ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected, <div className={styles.titleContainer}>
.ant-menu-submenu-horizontal.ant-menu-submenu-selected { <img className={styles.logo} src={result?.data?.data?.logo?.url} />
background-color: ${token.colorBgHeaderMenuActive} !important; <h1 className={styles.title}>{result?.data?.data?.title}</h1>
color: ${token.colorTextHeaderMenuActive} !important;
}
.ant-menu-submenu-horizontal.ant-menu-submenu-selected > .ant-menu-submenu-title {
color: ${token.colorTextHeaderMenuActive} !important;
}
.ant-menu-dark.ant-menu-horizontal > .ant-menu-item:hover {
background-color: ${token.colorBgHeaderMenuHover} !important;
color: ${token.colorTextHeaderMenuHover} !important;
}
position: fixed;
left: 0;
right: 0;
height: var(--nb-header-height);
line-height: var(--nb-header-height);
padding: 0;
z-index: 100;
background-color: ${token.colorBgHeader} !important;
.ant-menu {
background-color: transparent;
}
.ant-menu-item,
.ant-menu-submenu-horizontal {
color: ${token.colorTextHeaderMenu} !important;
}
`}
>
<div
className={css`
position: relative;
width: 100%;
height: 100%;
display: flex;
`}
>
<div
className={css`
position: relative;
z-index: 1;
flex: 1 1 auto;
display: flex;
height: 100%;
`}
>
<div
className={css`
display: inline-flex;
flex-shrink: 0;
color: #fff;
padding: 0;
align-items: center;
padding: 0 12px 0 12px;
`}
>
<img
className={css`
object-fit: contain;
height: 28px;
`}
src={result?.data?.data?.logo?.url}
/>
<h1
className={css`
color: #fff;
height: 32px;
margin: 0 0 0 12px;
font-weight: 600;
font-size: 18px;
line-height: 32px;
`}
>
{result?.data?.data?.title}
</h1>
</div> </div>
<div <div className={styles.editor}>
className={css`
flex: 1 1 auto;
width: 0;
`}
>
<SetThemeOfHeaderSubmenu> <SetThemeOfHeaderSubmenu>
<MenuEditor sideMenuRef={sideMenuRef} /> <MenuEditor sideMenuRef={sideMenuRef} />
</SetThemeOfHeaderSubmenu> </SetThemeOfHeaderSubmenu>
</div> </div>
</div> </div>
<div <div className={styles.right}>
className={css`
position: relative;
flex-shrink: 0;
height: 100%;
z-index: 10;
`}
>
<PinnedPluginList /> <PinnedPluginList />
<ConfigProvider <ConfigProvider
theme={{ theme={{
@ -398,59 +433,10 @@ export const InternalAdminLayout = (props: any) => {
</div> </div>
</div> </div>
</Layout.Header> </Layout.Header>
{params.name && ( {params.name && <Layout.Sider className={styles.sider} theme={'light'} ref={sideMenuRef}></Layout.Sider>}
<Layout.Sider <Layout.Content className={styles.main}>
className={css` <header className={styles.mainHeader}></header>
height: 100%;
/* position: fixed; */
position: relative;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0);
z-index: 100;
.ant-layout-sider-children {
top: var(--nb-header-height);
position: fixed;
width: 200px;
height: calc(100vh - var(--nb-header-height));
}
`}
theme={'light'}
ref={sideMenuRef}
></Layout.Sider>
)}
<Layout.Content
className={css`
display: flex;
flex-direction: column;
position: relative;
overflow-y: auto;
height: 100vh;
max-height: 100vh;
> div {
position: relative;
}
.ant-layout-footer {
position: absolute;
bottom: 0;
text-align: center;
width: 100%;
z-index: 0;
padding: 0px 50px;
}
`}
>
<header
className={css`
flex-shrink: 0;
height: var(--nb-header-height);
line-height: var(--nb-header-height);
background: transparent;
pointer-events: none;
`}
></header>
<Outlet /> <Outlet />
{/* {service.contentLoading ? render() : <Outlet />} */}
</Layout.Content> </Layout.Content>
</Layout> </Layout>
); );

View File

@ -1,12 +1,30 @@
import { css } from '@emotion/css';
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { Modal, ModalProps } from 'antd';
import classNames from 'classnames';
import React from 'react'; import React from 'react';
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { Modal, ModalProps } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import { OpenSize, useActionContext } from '.'; import { OpenSize, useActionContext } from '.';
import { useSetAriaLabelForModal } from './hooks/useSetAriaLabelForModal'; import { useSetAriaLabelForModal } from './hooks/useSetAriaLabelForModal';
import { ComposedActionDrawer } from './types'; import { ComposedActionDrawer } from './types';
const useStyles = createStyles(({ css }) => {
return {
container: css`
&.nb-action-popup {
.ant-modal-header {
display: none;
}
.ant-modal-content {
background: var(--nb-box-bg);
}
}
`,
};
});
const openSizeWidthMap = new Map<OpenSize, string>([ const openSizeWidthMap = new Map<OpenSize, string>([
['small', '40%'], ['small', '40%'],
['middle', '60%'], ['middle', '60%'],
@ -15,6 +33,7 @@ const openSizeWidthMap = new Map<OpenSize, string>([
export const ActionModal: ComposedActionDrawer<ModalProps> = observer( export const ActionModal: ComposedActionDrawer<ModalProps> = observer(
(props) => { (props) => {
const { footerNodeName = 'Action.Modal.Footer', width, ...others } = props; const { footerNodeName = 'Action.Modal.Footer', width, ...others } = props;
const { styles } = useStyles();
const { visible, setVisible, openSize = 'middle', modalProps } = useActionContext(); const { visible, setVisible, openSize = 'middle', modalProps } = useActionContext();
const actualWidth = width ?? openSizeWidthMap.get(openSize); const actualWidth = width ?? openSizeWidthMap.get(openSize);
const schema = useFieldSchema(); const schema = useFieldSchema();
@ -43,21 +62,7 @@ export const ActionModal: ComposedActionDrawer<ModalProps> = observer(
destroyOnClose destroyOnClose
open={visible} open={visible}
onCancel={() => setVisible(false, true)} onCancel={() => setVisible(false, true)}
className={classNames( className={classNames(others.className, modalProps?.className, styles.container)}
others.className,
modalProps?.className,
css`
&.nb-action-popup {
.ant-modal-header {
display: none;
}
.ant-modal-content {
background: var(--nb-box-bg);
}
}
`,
)}
footer={ footer={
footerSchema ? ( footerSchema ? (
<RecursionField <RecursionField

View File

@ -1,7 +1,9 @@
import { css } from '@emotion/css';
import { observer, RecursionField, SchemaExpressionScopeContext, useField, useFieldSchema } from '@tachybase/schema';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { observer, RecursionField, SchemaExpressionScopeContext, useField, useFieldSchema } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { useActionContext } from '.'; import { useActionContext } from '.';
import { ComposedActionDrawer } from './types'; import { ComposedActionDrawer } from './types';
@ -10,11 +12,24 @@ const useScope = (key: string) => {
return scope[key]; return scope[key];
}; };
const useStyles = createStyles(({ css }) => {
return {
container: css`
display: flex;
width: 100%;
.ant-btn {
margin-right: 8px;
}
`,
};
});
export const ActionPage: ComposedActionDrawer = observer( export const ActionPage: ComposedActionDrawer = observer(
(props: any) => { (props: any) => {
const { footerNodeName = 'Action.Page.Footer', ...others } = props; const { footerNodeName = 'Action.Page.Footer', ...others } = props;
const { containerRefKey, visible, setVisible } = useActionContext(); const { containerRefKey, visible, setVisible } = useActionContext();
const containerRef = useScope(containerRefKey); const containerRef = useScope(containerRefKey);
const { styles } = useStyles();
const schema = useFieldSchema(); const schema = useFieldSchema();
const field = useField(); const field = useField();
const footerSchema = schema.reduceProperties((buf, s) => { const footerSchema = schema.reduceProperties((buf, s) => {
@ -38,17 +53,7 @@ export const ActionPage: ComposedActionDrawer = observer(
}} }}
/> />
{footerSchema && ( {footerSchema && (
<div <div className={styles.container}>
className={css`
display: flex;
/* justify-content: flex-end; */
/* flex-direction: row-reverse; */
width: 100%;
.ant-btn {
margin-right: 8px;
}
`}
>
<RecursionField <RecursionField
basePath={field.address} basePath={field.address}
schema={schema} schema={schema}

View File

@ -1,13 +1,23 @@
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { Button, Drawer, Space } from 'antd';
import classNames from 'classnames';
import React from 'react'; import React from 'react';
import { OpenSize } from '.'; import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { Button, Drawer, Space } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { useStyles } from './Action.Drawer.style'; import { useStyles } from './Action.Drawer.style';
import { useActionContext } from './hooks'; import { useActionContext } from './hooks';
import { ComposedActionDrawer } from './types'; import { ComposedActionDrawer } from './types';
import { css } from '@emotion/css';
import { useTranslation } from 'react-i18next'; const useStyles2 = createStyles(({ css }) => {
return {
container: css`
width: 100%;
justify-content: flex-end;
`,
};
});
export const ActionSheet: ComposedActionDrawer = observer( export const ActionSheet: ComposedActionDrawer = observer(
(props) => { (props) => {
@ -17,6 +27,7 @@ export const ActionSheet: ComposedActionDrawer = observer(
const schema = useFieldSchema(); const schema = useFieldSchema();
const field = useField(); const field = useField();
const { componentCls, hashId } = useStyles(); const { componentCls, hashId } = useStyles();
const { styles } = useStyles2();
const footerSchema = schema.reduceProperties((buf, s) => { const footerSchema = schema.reduceProperties((buf, s) => {
if (s['x-component'] === footerNodeName) { if (s['x-component'] === footerNodeName) {
return s; return s;
@ -40,12 +51,7 @@ export const ActionSheet: ComposedActionDrawer = observer(
onClose={() => setVisible(false, true)} onClose={() => setVisible(false, true)}
rootClassName={classNames(componentCls, hashId, drawerProps?.className, others.className, 'reset')} rootClassName={classNames(componentCls, hashId, drawerProps?.className, others.className, 'reset')}
footer={ footer={
<Space <Space className={styles.container}>
className={css`
width: 100%;
justify-content: flex-end;
`}
>
<Button type="primary" onClick={() => setVisible(false, true)}> <Button type="primary" onClick={() => setVisible(false, true)}>
{t('Close')} {t('Close')}
</Button> </Button>

View File

@ -1,12 +1,14 @@
import { cx } from '@emotion/css';
import { RecursionField, observer, useFieldSchema } from '@tachybase/schema';
import { Space } from 'antd';
import React, { CSSProperties, useContext } from 'react'; import React, { CSSProperties, useContext } from 'react';
import { observer, RecursionField, useFieldSchema } from '@tachybase/schema';
import { Space } from 'antd';
import cx from 'classnames';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { DndContext } from '../../common';
import { useDesignable, useProps } from '../../hooks';
import { useSchemaInitializerRender } from '../../../application'; import { useSchemaInitializerRender } from '../../../application';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps'; import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { DndContext } from '../../common';
import { useDesignable, useProps } from '../../hooks';
interface ActionBarContextForceProps { interface ActionBarContextForceProps {
layout?: 'one-column' | 'tow-columns'; layout?: 'one-column' | 'tow-columns';
@ -35,7 +37,7 @@ export const useActionBarContext = () => {
return useContext(ActionBarContext); return useContext(ActionBarContext);
}; };
const Portal: React.FC = (props) => { const Portal = (props) => {
const filedSchema = useFieldSchema(); const filedSchema = useFieldSchema();
const { container, parentComponents = ['BlockItem', 'CardItem'] } = useActionBarContext(); const { container, parentComponents = ['BlockItem', 'CardItem'] } = useActionBarContext();
return ( return (

View File

@ -1,15 +1,16 @@
import { EditOutlined } from '@ant-design/icons';
import { css } from '@emotion/css';
import { observer, useField, useFieldSchema } from '@tachybase/schema';
import React, { useContext, useState } from 'react'; import React, { useContext, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { Field, observer, useField, useFieldSchema } from '@tachybase/schema';
import { InternalSubTable } from './InternalSubTable';
import { EditOutlined } from '@ant-design/icons';
import { Button, Drawer } from 'antd'; import { Button, Drawer } from 'antd';
import { SubFormProvider, useAssociationFieldContext } from './hooks'; import { useTranslation } from 'react-i18next';
import { ActionContext, ActionContextProvider } from '../action/context';
import { ReadPrettyInternalViewer } from './InternalViewer';
import { useCollectionManager } from '../../../data-source'; import { useCollectionManager } from '../../../data-source';
import { FlagProvider } from '../../../flag-provider'; import { FlagProvider } from '../../../flag-provider';
import { ActionContext, ActionContextProvider } from '../action/context';
import { SubFormProvider, useAssociationFieldContext } from './hooks';
import { InternalSubTable } from './InternalSubTable';
import { ReadPrettyInternalViewer } from './InternalViewer';
export const InternaDrawerSubTable = observer( export const InternaDrawerSubTable = observer(
(props) => { (props) => {
@ -26,7 +27,7 @@ export const InternaDrawerSubTable = observer(
enableLink: true, enableLink: true,
}; };
const field = useField(); const field = useField<Field>();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const cm = useCollectionManager(); const cm = useCollectionManager();
@ -39,11 +40,7 @@ export const InternaDrawerSubTable = observer(
setVisible(true); setVisible(true);
}} }}
> >
<div <div style={{ maxWidth: '95%' }}>
className={css`
max-width: 95%;
`}
>
<ReadPrettyInternalViewer {...titleProps} /> <ReadPrettyInternalViewer {...titleProps} />
</div> </div>
<EditOutlined style={{ display: 'inline-flex', marginLeft: '5px' }} /> <EditOutlined style={{ display: 'inline-flex', marginLeft: '5px' }} />

View File

@ -1,12 +1,39 @@
import { css, cx } from '@emotion/css';
import { FormLayout } from '@tachybase/components';
import { RecursionField, useField, useFieldSchema, observer } from '@tachybase/schema';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { FormLayout } from '@tachybase/components';
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import cx from 'classnames';
import { ACLCollectionProvider, useACLActionParamsContext } from '../../../acl';
import { CollectionProvider_deprecated } from '../../../collection-manager'; import { CollectionProvider_deprecated } from '../../../collection-manager';
import { useAssociationFieldContext, useInsertSchema } from './hooks'; import { useAssociationFieldContext, useInsertSchema } from './hooks';
import { ACLCollectionProvider, useACLActionParamsContext } from '../../../acl';
import schema from './schema'; import schema from './schema';
const useStyles = createStyles(({ css }) => {
return {
body: css`
& .ant-formily-item-layout-vertical {
margin-bottom: 10px;
}
.ant-card-body {
padding: 15px 20px 5px;
}
.ant-divider-horizontal {
margin: 10px 0;
}
`,
showTitle: css`
.ant-card-body {
padding: 0px 20px 20px 0px;
}
> .ant-card-bordered {
border: none;
}
`,
};
});
export const InternalNester = observer( export const InternalNester = observer(
() => { () => {
const field = useField(); const field = useField();
@ -15,6 +42,8 @@ export const InternalNester = observer(
const { options: collectionField } = useAssociationFieldContext(); const { options: collectionField } = useAssociationFieldContext();
const showTitle = fieldSchema['x-decorator-props']?.showTitle ?? true; const showTitle = fieldSchema['x-decorator-props']?.showTitle ?? true;
const { actionName } = useACLActionParamsContext(); const { actionName } = useACLActionParamsContext();
const { styles } = useStyles();
useEffect(() => { useEffect(() => {
insertNester(schema.Nester); insertNester(schema.Nester);
}, []); }, []);
@ -23,29 +52,9 @@ export const InternalNester = observer(
<ACLCollectionProvider actionPath={`${collectionField.target}:${actionName}`}> <ACLCollectionProvider actionPath={`${collectionField.target}:${actionName}`}>
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
<div <div
className={cx( className={cx(styles.body, {
css` [styles.showTitle]: showTitle === false,
& .ant-formily-item-layout-vertical { })}
margin-bottom: 10px;
}
.ant-card-body {
padding: 15px 20px 5px;
}
.ant-divider-horizontal {
margin: 10px 0;
}
`,
{
[css`
.ant-card-body {
padding: 0px 20px 20px 0px;
}
> .ant-card-bordered {
border: none;
}
`]: showTitle === false,
},
)}
> >
<RecursionField <RecursionField
onlyRenderProperties onlyRenderProperties

View File

@ -1,18 +1,47 @@
import { EditOutlined } from '@ant-design/icons';
import { css } from '@emotion/css';
import { observer, useFieldSchema } from '@tachybase/schema';
import React, { useContext, useRef, useState } from 'react'; import React, { useContext, useRef, useState } from 'react';
import { observer, useFieldSchema } from '@tachybase/schema';
import { EditOutlined } from '@ant-design/icons';
import { createStyles } from 'antd-style';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ActionContext, ActionContextProvider } from '../action/context'; import { ActionContext, ActionContextProvider } from '../action/context';
import { useGetAriaLabelOfPopover } from '../action/hooks/useGetAriaLabelOfPopover'; import { useGetAriaLabelOfPopover } from '../action/hooks/useGetAriaLabelOfPopover';
import { useSetAriaLabelForPopover } from '../action/hooks/useSetAriaLabelForPopover'; import { useSetAriaLabelForPopover } from '../action/hooks/useSetAriaLabelForPopover';
import { StablePopover } from '../popover'; import { StablePopover } from '../popover';
import { useAssociationFieldContext } from './hooks';
import { InternalNester } from './InternalNester'; import { InternalNester } from './InternalNester';
import { ReadPrettyInternalViewer } from './InternalViewer'; import { ReadPrettyInternalViewer } from './InternalViewer';
import { useAssociationFieldContext } from './hooks';
const useStyles = createStyles(({ css }) => {
return {
pretty: css`
max-width: 95%;
`,
mask: css`
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: transparent;
z-index: 9999;
`,
container: css`
min-width: 600px;
max-width: 800px;
max-height: 440px;
overflow: auto;
.ant-card {
border: 0px;
}
`,
};
});
export const InternaPopoverNester = observer( export const InternaPopoverNester = observer(
(props) => { (props) => {
const { styles } = useStyles();
const { options } = useAssociationFieldContext(); const { options } = useAssociationFieldContext();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
@ -24,18 +53,7 @@ export const InternaPopoverNester = observer(
shouldMountElement: true, shouldMountElement: true,
}; };
const content = ( const content = (
<div <div ref={ref} className={styles.container}>
ref={ref}
style={{ minWidth: '600px', maxWidth: '800px', maxHeight: '440px', overflow: 'auto' }}
className={css`
min-width: 600px;
max-height: 440px;
overflow: auto;
.ant-card {
border: 0px;
}
`}
>
<InternalNester {...nesterProps} /> <InternalNester {...nesterProps} />
</div> </div>
); );
@ -62,11 +80,7 @@ export const InternaPopoverNester = observer(
title={t(options?.uiSchema?.rawTitle)} title={t(options?.uiSchema?.rawTitle)}
> >
<span style={{ cursor: 'pointer', display: 'flex' }}> <span style={{ cursor: 'pointer', display: 'flex' }}>
<div <div className={styles.pretty}>
className={css`
max-width: 95%;
`}
>
<ReadPrettyInternalViewer {...props} /> <ReadPrettyInternalViewer {...props} />
</div> </div>
<EditOutlined style={{ display: 'inline-flex', margin: '5px' }} /> <EditOutlined style={{ display: 'inline-flex', margin: '5px' }} />
@ -77,15 +91,7 @@ export const InternaPopoverNester = observer(
role="button" role="button"
aria-label={getAriaLabel('mask')} aria-label={getAriaLabel('mask')}
onClick={() => setVisible(false)} onClick={() => setVisible(false)}
className={css` className={styles.mask}
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: transparent;
z-index: 9999;
`}
/> />
)} )}
</ActionContextProvider> </ActionContextProvider>

View File

@ -1,7 +1,9 @@
import { css } from '@emotion/css';
import { FormLayout } from '@tachybase/components';
import { RecursionField, SchemaOptionsContext, observer, useField, useFieldSchema } from '@tachybase/schema';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { FormLayout } from '@tachybase/components';
import { observer, RecursionField, SchemaOptionsContext, useField, useFieldSchema } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import { ACLCollectionProvider, useACLActionParamsContext } from '../../../acl'; import { ACLCollectionProvider, useACLActionParamsContext } from '../../../acl';
import { CollectionProvider_deprecated } from '../../../collection-manager'; import { CollectionProvider_deprecated } from '../../../collection-manager';
import { FormItem, useSchemaOptionsContext } from '../../../schema-component'; import { FormItem, useSchemaOptionsContext } from '../../../schema-component';
@ -9,8 +11,28 @@ import Select from '../select/Select';
import { useAssociationFieldContext, useInsertSchema } from './hooks'; import { useAssociationFieldContext, useInsertSchema } from './hooks';
import schema from './schema'; import schema from './schema';
const useStyles = createStyles(({ css }) => {
return {
container: css`
.ant-formily-item-bordered-none {
.ant-input-number-group-addon {
border: none !important;
background: none;
}
.ant-checkbox-wrapper {
margin-left: 8px;
}
.ant-table {
margin: 0px !important;
}
}
`,
};
});
export const InternalSubTable = observer( export const InternalSubTable = observer(
() => { () => {
const { styles } = useStyles();
const field: any = useField(); const field: any = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const insert = useInsertSchema('SubTable'); const insert = useInsertSchema('SubTable');
@ -36,24 +58,7 @@ export const InternalSubTable = observer(
return ( return (
<CollectionProvider_deprecated name={options.target}> <CollectionProvider_deprecated name={options.target}>
<ACLCollectionProvider actionPath={`${options.target}:${actionName || 'view'}`}> <ACLCollectionProvider actionPath={`${options.target}:${actionName || 'view'}`}>
<FormLayout <FormLayout className={styles.container} layout={'vertical'} bordered={false}>
className={css`
.ant-formily-item-bordered-none {
.ant-input-number-group-addon {
border: none !important;
background: none;
}
.ant-checkbox-wrapper {
margin-left: 8px;
}
.ant-table {
margin: 0px !important;
}
}
`}
layout={'vertical'}
bordered={false}
>
<SchemaOptionsContext.Provider <SchemaOptionsContext.Provider
value={{ value={{
scope: option.scope, scope: option.scope,

View File

@ -1,6 +1,6 @@
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { toArr } from '@tachybase/schema';
import React, { Fragment, useRef, useState } from 'react'; import React, { Fragment, useRef, useState } from 'react';
import { observer, RecursionField, toArr, useField, useFieldSchema } from '@tachybase/schema';
import { useDesignable } from '../../'; import { useDesignable } from '../../';
import { BlockAssociationContext, WithoutTableFieldResource } from '../../../block-provider'; import { BlockAssociationContext, WithoutTableFieldResource } from '../../../block-provider';
import { CollectionProvider_deprecated, useCollectionManager_deprecated } from '../../../collection-manager'; import { CollectionProvider_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
@ -27,7 +27,7 @@ const toValue = (value, placeholder) => {
export function isObject(value) { export function isObject(value) {
return typeof value === 'object' && value !== null; return typeof value === 'object' && value !== null;
} }
export const ReadPrettyInternalViewer: React.FC = observer( export const ReadPrettyInternalViewer = observer(
(props: any) => { (props: any) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const recordCtx = useRecord(); const recordCtx = useRecord();

View File

@ -1,31 +1,57 @@
import { CloseOutlined, PlusOutlined } from '@ant-design/icons';
import { css } from '@emotion/css';
import { ArrayField } from '@tachybase/schema';
import { spliceArrayState } from '@tachybase/schema';
import { RecursionField, observer, useFieldSchema } from '@tachybase/schema';
import { action } from '@tachybase/schema';
import { each } from '@tachybase/schema';
import { Button, Card, Divider, Tooltip } from 'antd';
import _ from 'lodash';
import React, { useCallback, useContext } from 'react'; import React, { useCallback, useContext } from 'react';
import {
action,
ArrayField,
each,
observer,
RecursionField,
spliceArrayState,
useFieldSchema,
} from '@tachybase/schema';
import { CloseOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Card, Divider, Tooltip } from 'antd';
import { createStyles } from 'antd-style';
import _ from 'lodash';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FormActiveFieldsProvider } from '../../../block-provider'; import { FormActiveFieldsProvider } from '../../../block-provider';
import { useCollection } from '../../../data-source';
import { import {
useCollectionRecord, useCollectionRecord,
useCollectionRecordData, useCollectionRecordData,
} from '../../../data-source/collection-record/CollectionRecordProvider'; } from '../../../data-source/collection-record/CollectionRecordProvider';
import { isNewRecord, markRecordAsNew } from '../../../data-source/collection-record/isNewRecord';
import { FlagProvider } from '../../../flag-provider'; import { FlagProvider } from '../../../flag-provider';
import { RecordIndexProvider, RecordProvider } from '../../../record-provider'; import { RecordIndexProvider, RecordProvider } from '../../../record-provider';
import { isPatternDisabled, isSystemField } from '../../../schema-settings'; import { isPatternDisabled, isSystemField } from '../../../schema-settings';
import { import {
DefaultValueProvider, DefaultValueProvider,
IsAllowToSetDefaultValueParams,
interfacesOfUnsupportedDefaultValue, interfacesOfUnsupportedDefaultValue,
IsAllowToSetDefaultValueParams,
} from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue'; } from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue';
import { AssociationFieldContext } from './context'; import { AssociationFieldContext } from './context';
import { SubFormProvider, useAssociationFieldContext } from './hooks'; import { SubFormProvider, useAssociationFieldContext } from './hooks';
import { isNewRecord, markRecordAsNew } from '../../../data-source/collection-record/isNewRecord';
import { useCollection } from '../../../data-source'; const useStyles = createStyles(({ css }) => {
return {
input: css`
position: relative;
.ant-input {
width: 100%;
}
`,
card: css`
> .ant-card-body > .ant-divider:last-child {
display: none;
}
`,
button: css`
border: 1px solid #f0f0f0 !important;
box-shadow: none;
`,
};
});
export const Nester = (props) => { export const Nester = (props) => {
const { options } = useContext(AssociationFieldContext); const { options } = useContext(AssociationFieldContext);
@ -133,17 +159,10 @@ const ToManyNester = observer(
!isSystemField(collectionField, getInterface) !isSystemField(collectionField, getInterface)
); );
}, []); }, []);
const { styles } = useStyles();
return field.value.length > 0 ? ( return field.value.length > 0 ? (
<Card <Card bordered={true} style={{ position: 'relative' }} className={styles.card}>
bordered={true}
style={{ position: 'relative' }}
className={css`
> .ant-card-body > .ant-divider:last-child {
display: none;
}
`}
>
{field.value.map((value, index) => { {field.value.map((value, index) => {
let allowed = allowDissociate; let allowed = allowDissociate;
if (!allowDissociate) { if (!allowDissociate) {
@ -221,10 +240,7 @@ const ToManyNester = observer(
<Tooltip key={'add'} title={t('Add new')}> <Tooltip key={'add'} title={t('Add new')}>
<Button <Button
type={'default'} type={'default'}
className={css` className={styles.button}
border: 1px solid #f0f0f0 !important;
box-shadow: none;
`}
block block
icon={<PlusOutlined />} icon={<PlusOutlined />}
onClick={() => { onClick={() => {

View File

@ -1,13 +1,19 @@
import { css } from '@emotion/css';
import { ArrayField } from '@tachybase/schema';
import { exchangeArrayState } from '@tachybase/schema';
import { observer, RecursionField, useFieldSchema } from '@tachybase/schema';
import { action } from '@tachybase/schema';
import { isArr } from '@tachybase/schema';
import { Button } from 'antd';
import { unionBy, uniqBy } from 'lodash';
import React, { useContext, useMemo, useState } from 'react'; import React, { useContext, useMemo, useState } from 'react';
import {
action,
ArrayField,
exchangeArrayState,
isArr,
observer,
RecursionField,
useFieldSchema,
} from '@tachybase/schema';
import { Button } from 'antd';
import { createStyles } from 'antd-style';
import { unionBy, uniqBy } from 'lodash';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
FormProvider, FormProvider,
RecordPickerContext, RecordPickerContext,
@ -20,6 +26,7 @@ import { FormActiveFieldsProvider } from '../../../block-provider/hooks/useFormA
import { TableSelectorParamsProvider } from '../../../block-provider/TableSelectorProvider'; import { TableSelectorParamsProvider } from '../../../block-provider/TableSelectorProvider';
import { CollectionProvider_deprecated } from '../../../collection-manager'; import { CollectionProvider_deprecated } from '../../../collection-manager';
import { CollectionRecordProvider, useCollectionRecord } from '../../../data-source'; import { CollectionRecordProvider, useCollectionRecord } from '../../../data-source';
import { markRecordAsNew } from '../../../data-source/collection-record/isNewRecord';
import { FlagProvider } from '../../../flag-provider'; import { FlagProvider } from '../../../flag-provider';
import { useCompile } from '../../hooks'; import { useCompile } from '../../hooks';
import { ActionContextProvider } from '../action'; import { ActionContextProvider } from '../action';
@ -27,11 +34,69 @@ import { Table } from '../table-v2/Table';
import { useAssociationFieldContext, useFieldNames } from './hooks'; import { useAssociationFieldContext, useFieldNames } from './hooks';
import { useTableSelectorProps } from './InternalPicker'; import { useTableSelectorProps } from './InternalPicker';
import { getLabelFormatValue, useLabelUiSchema } from './util'; import { getLabelFormatValue, useLabelUiSchema } from './util';
import { markRecordAsNew } from '../../../data-source/collection-record/isNewRecord';
const useStyles = createStyles(({ css }) => {
return {
addNew: css`
display: block;
border-radius: 0px;
border-right: 1px solid rgba(0, 0, 0, 0.06);
`,
select: css`
display: block;
border-radius: 0px;
`,
table: css`
.ant-formily-item.ant-formily-item-feedback-layout-loose {
margin-bottom: 0px !important;
}
.ant-formily-editable {
vertical-align: sub;
}
.ant-table-footer {
display: flex;
}
`,
container: css`
.ant-table-footer {
padding: 0 !important;
}
.ant-formily-item-error-help {
display: none;
}
.ant-description-textarea {
line-height: 34px;
}
.ant-table-cell .ant-formily-item-error-help {
display: block;
position: absolute;
font-size: 12px;
top: 100%;
background: #fff;
width: 100%;
margin-top: -15px;
padding: 3px;
z-index: 1;
border-radius: 3px;
box-shadow: 0 0 10px #eee;
animation: none;
transform: translateY(0);
opacity: 1;
}
`,
input: css`
position: relative;
.ant-input {
width: 100%;
}
`,
};
});
export const SubTable: any = observer( export const SubTable: any = observer(
(props: any) => { (props: any) => {
const { openSize } = props; const { openSize } = props;
const { styles } = useStyles();
const { field, options: collectionField } = useAssociationFieldContext<ArrayField>(); const { field, options: collectionField } = useAssociationFieldContext<ArrayField>();
const { t } = useTranslation(); const { t } = useTranslation();
const [visibleSelector, setVisibleSelector] = useState(false); const [visibleSelector, setVisibleSelector] = useState(false);
@ -108,50 +173,12 @@ export const SubTable: any = observer(
return filter; return filter;
}; };
return ( return (
<div <div className={styles.container}>
className={css`
.ant-table-footer {
padding: 0 !important;
}
.ant-formily-item-error-help {
display: none;
}
.ant-description-textarea {
line-height: 34px;
}
.ant-table-cell .ant-formily-item-error-help {
display: block;
position: absolute;
font-size: 12px;
top: 100%;
background: #fff;
width: 100%;
margin-top: -15px;
padding: 3px;
z-index: 1;
border-radius: 3px;
box-shadow: 0 0 10px #eee;
animation: none;
transform: translateY(0);
opacity: 1;
}
`}
>
<FlagProvider isInSubTable> <FlagProvider isInSubTable>
<CollectionRecordProvider record={null} parentRecord={recordV2}> <CollectionRecordProvider record={null} parentRecord={recordV2}>
<FormActiveFieldsProvider name="nester"> <FormActiveFieldsProvider name="nester">
<Table <Table
className={css` className={styles.table}
.ant-formily-item.ant-formily-item-feedback-layout-loose {
margin-bottom: 0px !important;
}
.ant-formily-editable {
vertical-align: sub;
}
.ant-table-footer {
display: flex;
}
`}
bordered bordered
size={'small'} size={'small'}
field={field} field={field}
@ -167,11 +194,7 @@ export const SubTable: any = observer(
<Button <Button
type={'text'} type={'text'}
block block
className={css` className={styles.addNew}
display: block;
border-radius: 0px;
border-right: 1px solid rgba(0, 0, 0, 0.06);
`}
onClick={() => { onClick={() => {
field.value = field.value || []; field.value = field.value || [];
field.value.push(markRecordAsNew({})); field.value.push(markRecordAsNew({}));
@ -184,10 +207,7 @@ export const SubTable: any = observer(
<Button <Button
type={'text'} type={'text'}
block block
className={css` className={styles.select}
display: block;
border-radius: 0px;
`}
onClick={() => { onClick={() => {
setVisibleSelector(true); setVisibleSelector(true);
}} }}

View File

@ -1,86 +1,87 @@
import { css } from '@emotion/css';
import { useFieldSchema } from '@tachybase/schema';
import cls from 'classnames';
import React from 'react'; import React from 'react';
import { useCollection_deprecated } from '../../../collection-manager'; import { useFieldSchema } from '@tachybase/schema';
import { DndContext, SortableItem } from '../../common';
import { useDesigner } from '../../hooks'; import { createStyles } from 'antd-style';
import { useToken } from '../__builtins__'; import cls from 'classnames';
import { AssociationFilterBlockDesigner } from './AssociationFilter.BlockDesigner';
import { AssociationFilterItem } from './AssociationFilter.Item';
import { AssociationFilterItemDesigner } from './AssociationFilter.Item.Designer';
import { AssociationFilterProvider } from './AssociationFilterProvider';
import { useSchemaInitializerRender } from '../../../application'; import { useSchemaInitializerRender } from '../../../application';
import { Plugin } from '../../../application/Plugin'; import { Plugin } from '../../../application/Plugin';
import { useCollection_deprecated } from '../../../collection-manager';
import { import {
associationFilterFilterBlockInitializer, associationFilterFilterBlockInitializer,
filterCollapseItemInitializer, filterCollapseItemInitializer,
filterCollapseItemInitializer_deprecated, filterCollapseItemInitializer_deprecated,
} from '../../../modules/blocks/filter-blocks/collapse/filterCollapseItemInitializer'; } from '../../../modules/blocks/filter-blocks/collapse/filterCollapseItemInitializer';
import { DndContext, SortableItem } from '../../common';
import { useDesigner } from '../../hooks';
import { AssociationFilterBlockDesigner } from './AssociationFilter.BlockDesigner';
import { associationFilterInitializer } from './AssociationFilter.Initializer'; import { associationFilterInitializer } from './AssociationFilter.Initializer';
import { AssociationFilterItem } from './AssociationFilter.Item';
import { AssociationFilterItemDesigner } from './AssociationFilter.Item.Designer';
import { AssociationFilterProvider } from './AssociationFilterProvider';
const useStyles = createStyles(({ css, token }) => {
return {
container: css`
height: 100%;
overflow-y: auto;
position: relative;
border-radius: ${token.borderRadiusLG}px;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-form-item:hover {
> .general-schema-designer {
background: var(--colorBgSettingsHover) !important;
border: 0 !important;
top: -5px !important;
bottom: -5px !important;
left: -5px !important;
right: -5px !important;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
border: 2px solid var(--colorBorderSettingsHover);
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
};
});
export const AssociationFilter = (props) => { export const AssociationFilter = (props) => {
const { token } = useToken();
const Designer = useDesigner(); const Designer = useDesigner();
const filedSchema = useFieldSchema(); const filedSchema = useFieldSchema();
const { styles } = useStyles();
const { render } = useSchemaInitializerRender(filedSchema['x-initializer'], filedSchema['x-initializer-props']); const { render } = useSchemaInitializerRender(filedSchema['x-initializer'], filedSchema['x-initializer-props']);
return ( return (
<DndContext> <DndContext>
<SortableItem <SortableItem className={cls('nb-block-item', props.className, styles.container)}>
className={cls(
'nb-block-item',
props.className,
css`
height: 100%;
overflow-y: auto;
position: relative;
border-radius: ${token.borderRadiusLG}px;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-form-item:hover {
> .general-schema-designer {
background: var(--colorBgSettingsHover) !important;
border: 0 !important;
top: -5px !important;
bottom: -5px !important;
left: -5px !important;
right: -5px !important;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
border: 2px solid var(--colorBorderSettingsHover);
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
)}
>
<Designer /> <Designer />
{props.children} {props.children}
{render()} {render()}

View File

@ -1,15 +1,67 @@
import { css } from '@emotion/css';
import cls from 'classnames';
import React from 'react'; import React from 'react';
import { useFieldSchema } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import cls from 'classnames';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { SortableItem } from '../../common'; import { SortableItem } from '../../common';
import { useDesigner, useProps } from '../../hooks'; import { useDesigner, useProps } from '../../hooks';
import { useGetAriaLabelOfBlockItem } from './hooks/useGetAriaLabelOfBlockItem'; import { useGetAriaLabelOfBlockItem } from './hooks/useGetAriaLabelOfBlockItem';
import { useFieldSchema } from '@tachybase/schema';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps'; const useStyles = createStyles(({ css }) => {
return {
container: css`
position: relative;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-form-item:hover {
> .general-schema-designer {
background: var(--colorBgSettingsHover) !important;
border: 0 !important;
top: -5px !important;
bottom: -5px !important;
left: -5px !important;
right: -5px !important;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
border: 2px solid var(--colorBorderSettingsHover);
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
};
});
export const BlockItem: React.FC<any> = withDynamicSchemaProps((props) => { export const BlockItem: React.FC<any> = withDynamicSchemaProps((props) => {
// 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema // 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema
const { className, children } = useProps(props); const { className, children } = useProps(props);
const { styles } = useStyles();
const Designer = useDesigner(); const Designer = useDesigner();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
@ -19,54 +71,7 @@ export const BlockItem: React.FC<any> = withDynamicSchemaProps((props) => {
<SortableItem <SortableItem
role="button" role="button"
aria-label={getAriaLabel()} aria-label={getAriaLabel()}
className={cls( className={cls('nb-block-item', className, styles.container)}
'nb-block-item',
className,
css`
position: relative;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-form-item:hover {
> .general-schema-designer {
background: var(--colorBgSettingsHover) !important;
border: 0 !important;
top: -5px !important;
bottom: -5px !important;
left: -5px !important;
right: -5px !important;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
border: 2px solid var(--colorBorderSettingsHover);
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
)}
> >
<Designer {...fieldSchema['x-toolbar-props']} /> <Designer {...fieldSchema['x-toolbar-props']} />
{children} {children}

View File

@ -1,9 +1,21 @@
import { css } from '@emotion/css'; import React from 'react';
import { usePrefixCls } from '@tachybase/components'; import { usePrefixCls } from '@tachybase/components';
import { connect, mapProps, mapReadPretty } from '@tachybase/schema'; import { connect, mapProps, mapReadPretty } from '@tachybase/schema';
import { ColorPicker as AntdColorPicker } from 'antd'; import { ColorPicker as AntdColorPicker } from 'antd';
import { createStyles } from 'antd-style';
import cls from 'classnames'; import cls from 'classnames';
import React from 'react';
const useStyles = createStyles(({ css }) => {
return {
color: css`
display: inline-flex;
.ant-color-picker-trigger-disabled {
cursor: default;
}
`,
};
});
export const ColorPicker = connect( export const ColorPicker = connect(
(props) => { (props) => {
@ -48,20 +60,12 @@ export const ColorPicker = connect(
}), }),
mapReadPretty((props) => { mapReadPretty((props) => {
const prefixCls = usePrefixCls('description-color-picker', props); const prefixCls = usePrefixCls('description-color-picker', props);
const { styles } = useStyles();
return ( return (
<div <div
role="button" role="button"
aria-label="color-picker-read-pretty" aria-label="color-picker-read-pretty"
className={cls( className={cls(prefixCls, styles.color, props.className)}
prefixCls,
css`
display: inline-flex;
.ant-color-picker-trigger-disabled {
cursor: default;
}
`,
props.className,
)}
> >
<AntdColorPicker disabled value={props.value} size="small" {...props} /> <AntdColorPicker disabled value={props.value} size="small" {...props} />
</div> </div>

View File

@ -1,36 +1,44 @@
import { css } from '@emotion/css'; import React, { useMemo } from 'react';
import { connect, mapReadPretty } from '@tachybase/schema'; import { connect, mapReadPretty } from '@tachybase/schema';
import { error } from '@tachybase/utils/client'; import { error } from '@tachybase/utils/client';
import { createStyles } from 'antd-style';
import cronstrue from 'cronstrue'; import cronstrue from 'cronstrue';
import React, { useMemo } from 'react';
import { CronProps, Cron as ReactCron } from 'react-js-cron'; import { CronProps, Cron as ReactCron } from 'react-js-cron';
import 'react-js-cron/dist/styles.css';
import { useAPIClient } from '../../../api-client'; import { useAPIClient } from '../../../api-client';
import 'react-js-cron/dist/styles.css';
const useStyles = createStyles(({ css }) => {
return {
input: css`
.react-js-cron {
padding: 0.5em 0.5em 0 0.5em;
border: 1px dashed #ccc;
.react-js-cron-field {
flex-shrink: 0;
margin-bottom: 0.5em;
> span {
flex-shrink: 0;
margin: 0 0.5em 0 0;
}
> .react-js-cron-select {
margin: 0 0.5em 0 0;
}
}
}
`,
};
});
const Input = (props: Omit<CronProps, 'setValue'> & { onChange: (value: string) => void }) => { const Input = (props: Omit<CronProps, 'setValue'> & { onChange: (value: string) => void }) => {
const { onChange, ...rest } = props; const { onChange, ...rest } = props;
const { styles } = useStyles();
return ( return (
<fieldset <fieldset className={styles.input}>
className={css`
.react-js-cron {
padding: 0.5em 0.5em 0 0.5em;
border: 1px dashed #ccc;
.react-js-cron-field {
flex-shrink: 0;
margin-bottom: 0.5em;
> span {
flex-shrink: 0;
margin: 0 0.5em 0 0;
}
> .react-js-cron-select {
margin: 0 0.5em 0 0;
}
}
}
`}
>
<ReactCron setValue={onChange} locale={window['cronLocale']} {...rest} /> <ReactCron setValue={onChange} locale={window['cronLocale']} {...rest} />
</fieldset> </fieldset>
); );

View File

@ -1,54 +1,57 @@
import { css } from '@emotion/css'; import React, { createRef, forwardRef } from 'react';
import { useFieldSchema } from '@tachybase/schema'; import { useFieldSchema } from '@tachybase/schema';
import { Button } from 'antd'; import { Button } from 'antd';
import React, { forwardRef, createRef } from 'react'; import { createStyles } from 'antd-style';
import { composeRef } from 'rc-util/lib/ref'; import { composeRef } from 'rc-util/lib/ref';
import { useCompile } from '../../hooks';
import { useTableBlockContext, useTableSelectorContext } from '../../../block-provider'; import { useTableBlockContext, useTableSelectorContext } from '../../../block-provider';
import { Icon } from '../../../icon'; import { Icon } from '../../../icon';
import { useCompile } from '../../hooks';
const actionDesignerCss = css` const useStyles = createStyles(({ css }) => {
position: relative; return {
&:hover { designer: css`
.general-schema-designer { position: relative;
display: block; &:hover {
} .general-schema-designer {
} display: block;
.general-schema-designer { }
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
} }
} .general-schema-designer {
} position: absolute;
`; z-index: 999;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
};
});
const InternalExpandAction = (props, ref) => { const InternalExpandAction = (props, ref) => {
const schema = useFieldSchema(); const schema = useFieldSchema();
const { styles } = useStyles();
const ctxSelector = useTableSelectorContext(); const ctxSelector = useTableSelectorContext();
const ctxBlock = useTableBlockContext(); const ctxBlock = useTableBlockContext();
const isTableSelector = schema.parent?.parent?.['x-decorator'] === 'TableSelectorProvider'; const isTableSelector = schema.parent?.parent?.['x-decorator'] === 'TableSelectorProvider';
@ -59,7 +62,7 @@ const InternalExpandAction = (props, ref) => {
const buttonRef = composeRef(ref, internalRef); const buttonRef = composeRef(ref, internalRef);
return ( return (
//@ts-ignore //@ts-ignore
<div className={actionDesignerCss} ref={buttonRef as React.Ref<HTMLButtonElement>}> <div className={styles.designer} ref={buttonRef as React.Ref<HTMLButtonElement>}>
{ctx?.params['tree'] && ( {ctx?.params['tree'] && (
<Button <Button
onClick={() => { onClick={() => {

View File

@ -1,22 +1,38 @@
import { css } from '@emotion/css';
import { createForm, Field, Form } from '@tachybase/schema';
import { observer, useField, useFieldSchema, useForm } from '@tachybase/schema';
import { flatten, unflatten } from '@tachybase/utils/client';
import { Button, Space } from 'antd';
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react'; import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
import { createForm, Field, Form, observer, useField, useFieldSchema, useForm } from '@tachybase/schema';
import { flatten, unflatten } from '@tachybase/utils/client';
import { Button, Space } from 'antd';
import { createStyles } from 'antd-style';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { FormProvider, SchemaComponent } from '../../core'; import { FormProvider, SchemaComponent } from '../../core';
import { useDesignable } from '../../hooks'; import { useDesignable } from '../../hooks';
import { useProps } from '../../hooks/useProps'; import { useProps } from '../../hooks/useProps';
import { Action } from '../action'; import { Action } from '../action';
import { StablePopover } from '../popover'; import { StablePopover } from '../popover';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
export const FilterActionContext = createContext<any>(null); export const FilterActionContext = createContext<any>(null);
FilterActionContext.displayName = 'FilterActionContext'; FilterActionContext.displayName = 'FilterActionContext';
const useStyles = createStyles(({ css }) => {
return {
container: css`
display: flex;
justify-content: flex-end;
width: 100%;
`,
button: css`
border-color: var(--colorSettings);
color: var(--colorSettings);
`,
};
});
export const FilterAction = withDynamicSchemaProps( export const FilterAction = withDynamicSchemaProps(
observer((props: any) => { observer((props: any) => {
const { styles } = useStyles();
const { t } = useTranslation(); const { t } = useTranslation();
const field = useField<Field>(); const field = useField<Field>();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
@ -55,13 +71,7 @@ export const FilterAction = withDynamicSchemaProps(
}, },
}} }}
/> />
<div <div className={styles.container}>
className={css`
display: flex;
justify-content: flex-end;
width: 100%;
`}
>
<Space> <Space>
<SaveConditions /> <SaveConditions />
<Button <Button
@ -104,16 +114,14 @@ const SaveConditions = () => {
const { fieldSchema, field, designable, dn } = useContext(FilterActionContext); const { fieldSchema, field, designable, dn } = useContext(FilterActionContext);
const form = useForm(); const form = useForm();
const { t } = useTranslation(); const { t } = useTranslation();
const { styles } = useStyles();
if (!designable) { if (!designable) {
return null; return null;
} }
return ( return (
<Button <Button
type={'dashed'} type={'dashed'}
className={css` className={styles.button}
border-color: var(--colorSettings);
color: var(--colorSettings);
`}
onClick={() => { onClick={() => {
const defaultValue = { ...form.values.filter }; const defaultValue = { ...form.values.filter };
fieldSchema.default = defaultValue; fieldSchema.default = defaultValue;

View File

@ -1,12 +1,13 @@
import { CloseCircleOutlined } from '@ant-design/icons';
import { css } from '@emotion/css';
import { observer } from '@tachybase/schema';
import { Cascader, Select, Space } from 'antd';
import React, { useCallback, useContext, useMemo } from 'react'; import React, { useCallback, useContext, useMemo } from 'react';
import { observer } from '@tachybase/schema';
import { CloseCircleOutlined } from '@ant-design/icons';
import { Cascader, Select, Space } from 'antd';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useCompile } from '../..'; import { useCompile } from '../..';
import { DynamicComponent } from './DynamicComponent';
import { RemoveConditionContext } from './context'; import { RemoveConditionContext } from './context';
import { DynamicComponent } from './DynamicComponent';
import { useValues } from './useValues'; import { useValues } from './useValues';
export const FilterItem = observer( export const FilterItem = observer(
@ -55,12 +56,8 @@ export const FilterItem = observer(
<div style={style} className="nc-filter-item"> <div style={style} className="nc-filter-item">
<Space> <Space>
<Cascader <Cascader
// @ts-ignore
role="button"
data-testid="select-filter-field" data-testid="select-filter-field"
className={css` style={{ width: '160px' }}
width: 160px;
`}
fieldNames={fieldNames} fieldNames={fieldNames}
changeOnSelect={false} changeOnSelect={false}
value={dataIndex} value={dataIndex}
@ -69,12 +66,8 @@ export const FilterItem = observer(
placeholder={t('Select field')} placeholder={t('Select field')}
/> />
<Select <Select
// @ts-ignore
role="button"
data-testid="select-filter-operator" data-testid="select-filter-operator"
className={css` style={{ minWidth: '110px' }}
min-width: 110px;
`}
popupMatchSelectWidth={false} popupMatchSelectWidth={false}
value={operator?.value} value={operator?.value}
options={compile(operators)} options={compile(operators)}

View File

@ -1,8 +1,9 @@
import { css } from '@emotion/css';
import { useFieldSchema, useForm } from '@tachybase/schema';
import { Button } from 'antd';
import React from 'react'; import React from 'react';
import { useFieldSchema, useForm } from '@tachybase/schema';
import { Button } from 'antd';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useDesignable } from '../../hooks'; import { useDesignable } from '../../hooks';
export const SaveDefaultValue = (props) => { export const SaveDefaultValue = (props) => {
@ -15,10 +16,10 @@ export const SaveDefaultValue = (props) => {
} }
return ( return (
<Button <Button
className={css` style={{
border-color: var(--colorSettings); borderColor: 'var(--colorSettings)',
color: var(--colorSettings); color: 'var(--colorSettings)',
`} }}
type={'dashed'} type={'dashed'}
onClick={() => { onClick={() => {
const filterSchema = fieldSchema?.parent?.parent?.parent?.properties?.filter; const filterSchema = fieldSchema?.parent?.parent?.parent?.properties?.filter;
@ -34,7 +35,6 @@ export const SaveDefaultValue = (props) => {
}); });
dn.refresh(); dn.refresh();
filterSchema.default = defaultValue; filterSchema.default = defaultValue;
console.log('filterSchema', defaultValue);
}} }}
> >
{t('Save conditions')} {t('Save conditions')}

View File

@ -1,25 +1,47 @@
import { css, cx } from '@emotion/css';
import { FormItem as Item } from '@tachybase/components';
import { Field } from '@tachybase/schema';
import { observer, useField, useFieldSchema } from '@tachybase/schema';
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import { FormItem as Item } from '@tachybase/components';
import { Field, observer, useField, useFieldSchema } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import cx from 'classnames';
import { ACLCollectionFieldProvider } from '../../../acl/ACLProvider'; import { ACLCollectionFieldProvider } from '../../../acl/ACLProvider';
import { useApp } from '../../../application'; import { useApp } from '../../../application';
import { useFormActiveFields } from '../../../block-provider/hooks/useFormActiveFields'; import { useFormActiveFields } from '../../../block-provider/hooks/useFormActiveFields';
import { Collection_deprecated } from '../../../collection-manager'; import { Collection_deprecated } from '../../../collection-manager';
import { CollectionFieldProvider, useContextConfigSetting } from '../../../data-source';
import { GeneralSchemaDesigner } from '../../../schema-settings'; import { GeneralSchemaDesigner } from '../../../schema-settings';
import { useVariables } from '../../../variables'; import { useVariables } from '../../../variables';
import useContextVariable from '../../../variables/hooks/useContextVariable'; import useContextVariable from '../../../variables/hooks/useContextVariable';
import { BlockItem } from '../block-item'; import { BlockItem } from '../block-item';
import { HTMLEncode } from '../input/shared'; import { HTMLEncode } from '../input/shared';
import { FilterFormDesigner } from './FormItem.FilterFormDesigner'; import { FilterFormDesigner } from './FormItem.FilterFormDesigner';
import { useEnsureOperatorsValid } from './SchemaSettingOptions';
import useLazyLoadDisplayAssociationFieldsOfForm from './hooks/useLazyLoadDisplayAssociationFieldsOfForm'; import useLazyLoadDisplayAssociationFieldsOfForm from './hooks/useLazyLoadDisplayAssociationFieldsOfForm';
import useParseDefaultValue from './hooks/useParseDefaultValue'; import useParseDefaultValue from './hooks/useParseDefaultValue';
import { CollectionFieldProvider, useContextConfigSetting } from '../../../data-source';
const useStyles = createStyles(({ css }) => {
return {
space: css`
& .ant-space {
flex-wrap: wrap;
}
`,
hide: css`
> .ant-formily-item-label {
display: none;
}
`,
row: css`
display: flex;
flex-direction: row;
align-items: baseline;
`,
};
});
export const FormItem: any = observer( export const FormItem: any = observer(
(props: any) => { (props: any) => {
const { styles } = useStyles();
const { layoutDirection: selfLayoutDirection } = props; const { layoutDirection: selfLayoutDirection } = props;
const field = useField<Field>(); const field = useField<Field>();
const schema = useFieldSchema(); const schema = useFieldSchema();
@ -57,24 +79,12 @@ export const FormItem: any = observer(
}, [field.description]); }, [field.description]);
const className = useMemo(() => { const className = useMemo(() => {
return cx( return cx(
css` styles.space,
& .ant-space {
flex-wrap: wrap;
}
`,
{ {
[css` [styles.hide]: showTitle === false,
> .ant-formily-item-label {
display: none;
}
`]: showTitle === false,
}, },
{ {
[css` [styles.row]: finishLayoutDirection === 'row',
display: flex;
flex-direction: row;
align-items: baseline;
`]: finishLayoutDirection === 'row',
}, },
); );
}, [showTitle]); }, [showTitle]);

View File

@ -1,14 +1,29 @@
import { css } from '@emotion/css';
import { FormLayout } from '@tachybase/components';
import { createForm, Field, Form as FormilyForm, onFieldInit, onFormInputChange } from '@tachybase/schema';
import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { reaction } from '@tachybase/schema';
import { uid } from '@tachybase/schema';
import { getValuesByPath } from '@tachybase/utils/client';
import { ConfigProvider, Spin } from 'antd';
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import { FormLayout } from '@tachybase/components';
import {
createForm,
Field,
FieldContext,
FormContext,
Form as FormilyForm,
observer,
onFieldInit,
onFormInputChange,
reaction,
RecursionField,
uid,
useField,
useFieldSchema,
} from '@tachybase/schema';
import { getValuesByPath } from '@tachybase/utils/client';
import { ConfigProvider, Spin } from 'antd';
import { createStyles } from 'antd-style';
import { useActionContext } from '..'; import { useActionContext } from '..';
import { useAttach, useComponent } from '../..'; import { useAttach, useComponent } from '../..';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { useTemplateBlockContext } from '../../../block-provider/TemplateBlockProvider';
import { ActionType } from '../../../schema-settings/LinkageRules/type'; import { ActionType } from '../../../schema-settings/LinkageRules/type';
import { useLocalVariables, useVariables } from '../../../variables'; import { useLocalVariables, useVariables } from '../../../variables';
import { VariableOption, VariablesContextType } from '../../../variables/types'; import { VariableOption, VariablesContextType } from '../../../variables/types';
@ -18,13 +33,21 @@ import { isVariable, REGEX_OF_VARIABLE } from '../../../variables/utils/isVariab
import { getInnermostKeyAndValue, getTargetField } from '../../common/utils/uitls'; import { getInnermostKeyAndValue, getTargetField } from '../../common/utils/uitls';
import { useProps } from '../../hooks/useProps'; import { useProps } from '../../hooks/useProps';
import { collectFieldStateOfLinkageRules, getTempFieldState } from './utils'; import { collectFieldStateOfLinkageRules, getTempFieldState } from './utils';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { useTemplateBlockContext } from '../../../block-provider/TemplateBlockProvider';
export interface FormProps { export interface FormProps {
[key: string]: any; [key: string]: any;
} }
const useStyles = createStyles(({ css }) => {
return {
container: css`
.ant-formily-item-feedback-layout-loose {
margin-bottom: 12px;
}
`,
};
});
const FormComponent: React.FC<FormProps> = (props) => { const FormComponent: React.FC<FormProps> = (props) => {
const { form, children, ...others } = props; const { form, children, ...others } = props;
const field = useField(); const field = useField();
@ -203,6 +226,7 @@ export const Form: React.FC<FormProps> & {
} = withDynamicSchemaProps( } = withDynamicSchemaProps(
observer((props) => { observer((props) => {
const field = useField<Field>(); const field = useField<Field>();
const { styles } = useStyles();
// 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema // 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema
const { form, disabled, ...others } = useProps(props); const { form, disabled, ...others } = useProps(props);
@ -210,14 +234,7 @@ export const Form: React.FC<FormProps> & {
const formDisabled = disabled || field.disabled; const formDisabled = disabled || field.disabled;
return ( return (
<ConfigProvider componentDisabled={formDisabled}> <ConfigProvider componentDisabled={formDisabled}>
<form <form onSubmit={(e) => e.preventDefault()} className={styles.container}>
onSubmit={(e) => e.preventDefault()}
className={css`
.ant-formily-item-feedback-layout-loose {
margin-bottom: 12px;
}
`}
>
<Spin spinning={field.loading || false}> <Spin spinning={field.loading || false}>
{form ? ( {form ? (
<WithForm form={form} {...others} disabled={formDisabled} /> <WithForm form={form} {...others} disabled={formDisabled} />

View File

@ -1,23 +1,39 @@
import { css } from '@emotion/css';
import { useCollection } from '@tachybase/client';
import { ObjectField } from '@tachybase/schema';
import { useField, useFieldSchema } from '@tachybase/schema';
import { Card } from 'antd';
import React from 'react'; import React from 'react';
import { useCollection } from '@tachybase/client';
import { ObjectField, useField, useFieldSchema } from '@tachybase/schema';
import { Card } from 'antd';
import { createStyles } from 'antd-style';
import { useNavigate } from 'react-router-dom';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { useCollectionParentRecordData } from '../../../data-source/collection-record/CollectionRecordProvider'; import { useCollectionParentRecordData } from '../../../data-source/collection-record/CollectionRecordProvider';
import { RecordProvider } from '../../../record-provider'; import { RecordProvider } from '../../../record-provider';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { useNavigate } from 'react-router-dom';
import { useGridCardDetailUrl } from './hooks'; import { useGridCardDetailUrl } from './hooks';
const itemCss = css` const useStyles = createStyles(({ css }) => {
display: flex; return {
width: 100%; itemCss: css`
height: 100%; display: flex;
flex-direction: column; width: 100%;
justify-content: space-between; height: 100%;
gap: 8px; flex-direction: column;
`; justify-content: space-between;
gap: 8px;
`,
card: css`
/* background-color: re/d; */
height: 100%;
> .ant-card-body {
padding: 24px 24px 0px;
height: 100%;
}
.nb-action-bar {
padding: 5px 0;
}
`,
};
});
export const GridCardItem = withDynamicSchemaProps((props) => { export const GridCardItem = withDynamicSchemaProps((props) => {
const collection = useCollection(); const collection = useCollection();
@ -25,6 +41,7 @@ export const GridCardItem = withDynamicSchemaProps((props) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const parentRecordData = useCollectionParentRecordData(); const parentRecordData = useCollectionParentRecordData();
const navigate = useNavigate(); const navigate = useNavigate();
const { styles } = useStyles();
const detailUrl = useGridCardDetailUrl({ collection, field, fieldSchema }); const detailUrl = useGridCardDetailUrl({ collection, field, fieldSchema });
// XXX: 实现的有些丑陋, 需要想想有没有更好的办法 // XXX: 实现的有些丑陋, 需要想想有没有更好的办法
const handleClick = () => { const handleClick = () => {
@ -37,23 +54,8 @@ export const GridCardItem = withDynamicSchemaProps((props) => {
} }
}; };
return ( return (
<Card <Card role="button" aria-label="grid-card-item" className={styles.card} onClick={handleClick}>
role="button" <div className={styles.itemCss}>
aria-label="grid-card-item"
className={css`
/* background-color: re/d; */
height: 100%;
> .ant-card-body {
padding: 24px 24px 0px;
height: 100%;
}
.nb-action-bar {
padding: 5px 0;
}
`}
onClick={handleClick}
>
<div className={itemCss}>
{/* @ts-ignore */} {/* @ts-ignore */}
<RecordProvider record={field.value} parent={parentRecordData}> <RecordProvider record={field.value} parent={parentRecordData}>
{props.children} {props.children}

View File

@ -1,9 +1,13 @@
import { css, cx } from '@emotion/css';
import { ArrayField } from '@tachybase/schema';
import { RecursionField, Schema, useField, useFieldSchema } from '@tachybase/schema';
import { List as AntdList, Col, PaginationProps } from 'antd';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { ArrayField, RecursionField, Schema, useField, useFieldSchema } from '@tachybase/schema';
import { List as AntdList, Col, PaginationProps } from 'antd';
import { createStyles } from 'antd-style';
import cx from 'classnames';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { SortableItem } from '../../common'; import { SortableItem } from '../../common';
import { InfiniteScroll } from '../../common/infinite-scroll/infinite-scroll';
import { SchemaComponentOptions } from '../../core'; import { SchemaComponentOptions } from '../../core';
import { useDesigner, useProps } from '../../hooks'; import { useDesigner, useProps } from '../../hooks';
import { GridCardBlockProvider, useGridCardBlockContext, useGridCardItemProps } from './GridCard.Decorator'; import { GridCardBlockProvider, useGridCardBlockContext, useGridCardItemProps } from './GridCard.Decorator';
@ -11,8 +15,6 @@ import { GridCardDesigner } from './GridCard.Designer';
import { GridCardItem } from './GridCard.Item'; import { GridCardItem } from './GridCard.Item';
import { useGridCardActionBarProps } from './hooks'; import { useGridCardActionBarProps } from './hooks';
import { defaultColumnCount, pageSizeOptions } from './options'; import { defaultColumnCount, pageSizeOptions } from './options';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { InfiniteScroll } from '../../common/infinite-scroll/infinite-scroll';
const rowGutter = { const rowGutter = {
md: 12, md: 12,
@ -20,46 +22,51 @@ const rowGutter = {
xs: 5, xs: 5,
}; };
const designerCss = css` const useStyles = createStyles(({ css }) => {
width: 100%; return {
&:hover { designer: css`
> .general-schema-designer { width: 100%;
display: block; &:hover {
} > .general-schema-designer {
} display: block;
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
} }
}
} > .general-schema-designer {
`; position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
};
});
const InternalGridCard = (props) => { const InternalGridCard = (props) => {
// 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema // 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema
const { columnCount: columnCountProp, pagination } = useProps(props); const { columnCount: columnCountProp, pagination } = useProps(props);
const { styles } = useStyles();
const { service, columnCount: _columnCount = defaultColumnCount, needInfiniteScroll } = useGridCardBlockContext(); const { service, columnCount: _columnCount = defaultColumnCount, needInfiniteScroll } = useGridCardBlockContext();
const columnCount = columnCountProp || _columnCount; const columnCount = columnCountProp || _columnCount;
const { run, params } = service; const { run, params } = service;
@ -132,7 +139,7 @@ const InternalGridCard = (props) => {
useGridCardActionBarProps, useGridCardActionBarProps,
}} }}
> >
<SortableItem className={cx('nb-card-list', designerCss)}> <SortableItem className={cx('nb-card-list', styles.designer)}>
<AntdList <AntdList
pagination={ pagination={
!meta || meta.count <= meta.pageSize || needInfiniteScroll !meta || meta.count <= meta.pageSize || needInfiniteScroll

View File

@ -1,7 +1,8 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { Icon, icons } from '../../../icon';
import { Tooltip } from 'antd'; import { Tooltip } from 'antd';
import { css } from '@emotion/css';
import { Icon, icons } from '../../../icon';
export const IconFilterList = (props) => { export const IconFilterList = (props) => {
const { currentKey, filterKey, onChange, changePop } = props; const { currentKey, filterKey, onChange, changePop } = props;
@ -24,10 +25,12 @@ export const IconFilterList = (props) => {
{[...iconKeysByFilter.keys()].map((key) => ( {[...iconKeysByFilter.keys()].map((key) => (
<span <span
key={key} key={key}
className={css` style={{
background-color: ${currentKey == key ? '#9bb0d393' : ''}; fontSize: 18,
`} marginRight: 10,
style={{ fontSize: 18, marginRight: 10, cursor: 'pointer' }} cursor: 'pointer',
backgroundColor: currentKey == key ? '#9bb0d393' : '',
}}
> >
<Tooltip <Tooltip
destroyTooltipOnHide destroyTooltipOnHide

View File

@ -1,9 +1,8 @@
import { Field } from '@tachybase/schema'; import React, { Ref, useEffect, useState } from 'react';
import { useField } from '@tachybase/schema'; import { Field, useField } from '@tachybase/schema';
import { Input } from 'antd'; import { Input } from 'antd';
import { TextAreaProps } from 'antd/es/input'; import { TextAreaProps } from 'antd/es/input';
import React, { useState, useEffect, Ref } from 'react';
import { cx, css } from '@emotion/css';
export type JSONTextAreaProps = TextAreaProps & { value?: string; space?: number }; export type JSONTextAreaProps = TextAreaProps & { value?: string; space?: number };
@ -25,13 +24,11 @@ export const Json = React.forwardRef<typeof Input.TextArea, JSONTextAreaProps>(
return ( return (
<Input.TextArea <Input.TextArea
{...props} {...props}
className={cx( style={{
css` fontSize: '80%',
font-size: 80%; fontFamily: "Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace",
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; ...props.style,
`, }}
props.className,
)}
ref={ref} ref={ref}
value={text} value={text}
onChange={(ev) => { onChange={(ev) => {

View File

@ -1,12 +1,13 @@
import { css, cx } from '@emotion/css'; import React from 'react';
import { usePrefixCls } from '@tachybase/components';
import { Typography } from 'antd'; import { Typography } from 'antd';
import { InputProps, TextAreaProps } from 'antd/es/input'; import { InputProps, TextAreaProps } from 'antd/es/input';
import cls from 'classnames'; import cls from 'classnames';
import React from 'react';
import { useCompile } from '../..'; import { useCompile } from '../..';
import { EllipsisWithTooltip } from './EllipsisWithTooltip'; import { EllipsisWithTooltip } from './EllipsisWithTooltip';
import { HTMLEncode } from './shared'; import { HTMLEncode } from './shared';
import { usePrefixCls } from '@tachybase/components';
export const ReadPretty = () => null; export const ReadPretty = () => null;
@ -121,26 +122,6 @@ const _URL = (props: InputProps) => {
); );
}; };
const _JSON = (props: TextAreaProps & { space: number }) => {
const prefixCls = usePrefixCls('json', props);
return (
<pre
className={cx(
prefixCls,
props.className,
css`
margin-bottom: 0;
line-height: 1.5;
font-size: 90%;
`,
)}
style={props.style}
>
{props.value != null ? JSON.stringify(props.value, null, props.space ?? 2) : ''}
</pre>
);
};
ReadPretty.Input = _Input; ReadPretty.Input = _Input;
ReadPretty.TextArea = _TextArea; ReadPretty.TextArea = _TextArea;
ReadPretty.URL = _URL; ReadPretty.URL = _URL;

View File

@ -1,17 +1,32 @@
import { css, cx } from '@emotion/css';
import { FormLayout } from '@tachybase/components';
import { createForm } from '@tachybase/schema';
import { FormContext, useField } from '@tachybase/schema';
import _ from 'lodash';
import React, { createContext, useContext, useEffect, useMemo } from 'react'; import React, { createContext, useContext, useEffect, useMemo } from 'react';
import { BlockProvider, useBlockRequestContext, useParsedFilter } from '../../../block-provider'; import { FormLayout } from '@tachybase/components';
import { createForm, FormContext, useField } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import _ from 'lodash';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps'; import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { BlockProvider, useBlockRequestContext, useParsedFilter } from '../../../block-provider';
const useStyles = createStyles(({ css }) => {
return {
container: css`
.ant-description-input {
line-height: 34px;
}
.ant-formily-item-feedback-layout-loose {
margin-bottom: 12px;
}
`,
};
});
export const ListBlockContext = createContext<any>({}); export const ListBlockContext = createContext<any>({});
ListBlockContext.displayName = 'ListBlockContext'; ListBlockContext.displayName = 'ListBlockContext';
const InternalListBlockProvider = (props) => { const InternalListBlockProvider = (props) => {
const { resource, service } = useBlockRequestContext(); const { resource, service } = useBlockRequestContext();
const { styles } = useStyles();
const field = useField(); const field = useField();
const form = useMemo(() => { const form = useMemo(() => {
@ -35,18 +50,7 @@ const InternalListBlockProvider = (props) => {
> >
<FormContext.Provider value={form}> <FormContext.Provider value={form}>
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
<div <div className={styles.container}>{props.children}</div>
className={cx(css`
.ant-description-input {
line-height: 34px;
}
.ant-formily-item-feedback-layout-loose {
margin-bottom: 12px;
}
`)}
>
{props.children}
</div>
</FormLayout> </FormLayout>
</FormContext.Provider> </FormContext.Provider>
</ListBlockContext.Provider> </ListBlockContext.Provider>

View File

@ -1,30 +1,32 @@
import { ObjectField } from '@tachybase/schema';
import { useField } from '@tachybase/schema';
import classnames from 'classnames';
import React from 'react'; import React from 'react';
import { css, cx } from '@emotion/css'; import { ObjectField, useField } from '@tachybase/schema';
import { useDesignable } from '../../hooks';
import { createStyles } from 'antd-style';
import classnames from 'classnames';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { useCollectionParentRecordData } from '../../../data-source/collection-record/CollectionRecordProvider'; import { useCollectionParentRecordData } from '../../../data-source/collection-record/CollectionRecordProvider';
import { RecordProvider } from '../../../record-provider'; import { RecordProvider } from '../../../record-provider';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps'; import { useDesignable } from '../../hooks';
const useStyles = createStyles(({ css }, props: { designable: boolean }) => {
return {
item: css`
.nb-action-bar {
gap: 20px !important;
margin-top: ${props.designable ? '20px' : '0px'};
}
`,
};
});
export const ListItem = withDynamicSchemaProps((props) => { export const ListItem = withDynamicSchemaProps((props) => {
const field = useField<ObjectField>(); const field = useField<ObjectField>();
const { designable } = useDesignable(); const { designable } = useDesignable();
const { styles } = useStyles({ designable });
const parentRecordData = useCollectionParentRecordData(); const parentRecordData = useCollectionParentRecordData();
return ( return (
<div <div className={classnames(props.className, ['itemCss', styles.item])}>
className={cx(classnames(props.className), [
'itemCss',
css`
.nb-action-bar {
gap: 20px !important;
margin-top: ${designable ? '20px' : '0px'};
}
`,
])}
>
<RecordProvider record={field.value} parent={parentRecordData}> <RecordProvider record={field.value} parent={parentRecordData}>
{props.children} {props.children}
</RecordProvider> </RecordProvider>

View File

@ -1,17 +1,18 @@
import { cx } from '@emotion/css';
import { ArrayField } from '@tachybase/schema';
import { RecursionField, Schema, useField, useFieldSchema } from '@tachybase/schema';
import { List as AntdList, PaginationProps } from 'antd';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { ArrayField, RecursionField, Schema, useField, useFieldSchema } from '@tachybase/schema';
import { List as AntdList, PaginationProps } from 'antd';
import cx from 'classnames';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { SortableItem } from '../../common'; import { SortableItem } from '../../common';
import { SchemaComponentOptions } from '../../core'; import { SchemaComponentOptions } from '../../core';
import { useDesigner } from '../../hooks'; import { useDesigner } from '../../hooks';
import { useListActionBarProps } from './hooks';
import { ListBlockProvider, useListBlockContext, useListItemProps } from './List.Decorator'; import { ListBlockProvider, useListBlockContext, useListItemProps } from './List.Decorator';
import { ListDesigner } from './List.Designer'; import { ListDesigner } from './List.Designer';
import { ListItem } from './List.Item'; import { ListItem } from './List.Item';
import useStyles from './List.style'; import useStyles from './List.style';
import { useListActionBarProps } from './hooks';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
const InternalList = (props) => { const InternalList = (props) => {
const { service } = useListBlockContext(); const { service } = useListBlockContext();

View File

@ -1,8 +1,11 @@
import { TreeSelect } from '@tachybase/components';
import { Field, onFieldChange } from '@tachybase/schema';
import { ISchema, Schema, useField, useFieldSchema } from '@tachybase/schema';
import React from 'react'; import React from 'react';
import { TreeSelect } from '@tachybase/components';
import { Field, ISchema, onFieldChange, Schema, useField, useFieldSchema } from '@tachybase/schema';
import { message } from 'antd';
import { saveAs } from 'file-saver';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { findByUid } from '.'; import { findByUid } from '.';
import { createDesignable } from '../..'; import { createDesignable } from '../..';
import { import {
@ -15,8 +18,6 @@ import {
useAPIClient, useAPIClient,
useDesignable, useDesignable,
} from '../../../'; } from '../../../';
import { message } from 'antd';
import { saveAs } from 'file-saver';
const toItems = (properties = {}) => { const toItems = (properties = {}) => {
const items = []; const items = [];

View File

@ -0,0 +1,160 @@
import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css }) => {
return {
subMenuDesignerCss: css`
position: relative;
display: inline-block;
margin-left: -24px;
margin-right: -34px;
padding: 0 34px 0 24px;
width: calc(100% + 58px);
height: 100%;
overflow: hidden;
text-overflow: ellipsis;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-action-link {
> .general-schema-designer {
top: -10px;
bottom: -10px;
left: -10px;
right: -10px;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
designerCss: css`
position: relative;
display: flex;
justify-content: center;
align-items: center;
margin-left: -20px;
margin-right: -20px;
padding: 0 20px;
width: calc(100% + 40px);
height: 100%;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-action-link {
> .general-schema-designer {
top: -10px;
bottom: -10px;
left: -10px;
right: -10px;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
headerMenuClass: css`
.ant-menu-item:hover {
> .ant-menu-title-content > div {
.general-schema-designer {
display: block;
}
}
}
`,
sideMenuClass: css`
height: 100%;
overflow-y: auto;
overflow-x: hidden;
.ant-menu-item {
> .ant-menu-title-content {
height: 100%;
margin-left: -24px;
margin-right: -16px;
padding: 0 16px 0 24px;
> div {
> .general-schema-designer {
right: 6px !important;
}
}
}
}
.ant-menu-submenu-title {
.ant-menu-title-content {
height: 100%;
margin-left: -24px;
margin-right: -34px;
padding: 0 34px 0 24px;
> div {
> .general-schema-designer {
right: 6px !important;
}
> span.anticon {
margin-right: 10px;
}
}
}
}
`,
menuItemClass: css`
:active {
background: inherit;
}
`,
};
});

View File

@ -1,182 +1,29 @@
import { css } from '@emotion/css'; import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { import {
FieldContext, FieldContext,
observer, observer,
RecursionField, RecursionField,
SchemaContext, SchemaContext,
SchemaExpressionScopeContext, SchemaExpressionScopeContext,
uid,
useField, useField,
useFieldSchema, useFieldSchema,
} from '@tachybase/schema'; } from '@tachybase/schema';
import { uid } from '@tachybase/schema';
import { error } from '@tachybase/utils/client'; import { error } from '@tachybase/utils/client';
import { Menu as AntdMenu, MenuProps } from 'antd'; import { Menu as AntdMenu, MenuProps } from 'antd';
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { createDesignable, DndContext, SortableItem, useDesignable, useDesigner } from '../..'; import { createDesignable, DndContext, SortableItem, useDesignable, useDesigner } from '../..';
import { Icon, useAPIClient, useSchemaInitializerRender } from '../../../'; import { Icon, useAPIClient, useSchemaInitializerRender } from '../../../';
import { useCollectMenuItems, useMenuItem } from '../../../hooks/useMenuItem'; import { useCollectMenuItems, useMenuItem } from '../../../hooks/useMenuItem';
import { useProps } from '../../hooks/useProps'; import { useProps } from '../../hooks/useProps';
import { useMenuTranslation } from './locale'; import { useMenuTranslation } from './locale';
import { MenuDesigner } from './Menu.Designer'; import { MenuDesigner } from './Menu.Designer';
import { useStyles } from './Menu.styles';
import { findKeysByUid, findMenuItem } from './util'; import { findKeysByUid, findMenuItem } from './util';
const subMenuDesignerCss = css`
position: relative;
display: inline-block;
margin-left: -24px;
margin-right: -34px;
padding: 0 34px 0 24px;
width: calc(100% + 58px);
height: 100%;
overflow: hidden;
text-overflow: ellipsis;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-action-link {
> .general-schema-designer {
top: -10px;
bottom: -10px;
left: -10px;
right: -10px;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`;
const designerCss = css`
position: relative;
display: flex;
justify-content: center;
align-items: center;
margin-left: -20px;
margin-right: -20px;
padding: 0 20px;
width: calc(100% + 40px);
height: 100%;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-action-link {
> .general-schema-designer {
top: -10px;
bottom: -10px;
left: -10px;
right: -10px;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`;
const headerMenuClass = css`
.ant-menu-item:hover {
> .ant-menu-title-content > div {
.general-schema-designer {
display: block;
}
}
}
`;
const sideMenuClass = css`
height: 100%;
overflow-y: auto;
overflow-x: hidden;
.ant-menu-item {
> .ant-menu-title-content {
height: 100%;
margin-left: -24px;
margin-right: -16px;
padding: 0 16px 0 24px;
> div {
> .general-schema-designer {
right: 6px !important;
}
}
}
}
.ant-menu-submenu-title {
.ant-menu-title-content {
height: 100%;
margin-left: -24px;
margin-right: -34px;
padding: 0 34px 0 24px;
> div {
> .general-schema-designer {
right: 6px !important;
}
> span.anticon {
margin-right: 10px;
}
}
}
}
`;
const menuItemClass = css`
:active {
background: inherit;
}
`;
type ComposedMenu = React.FC<any> & { type ComposedMenu = React.FC<any> & {
Item?: React.FC<any>; Item?: React.FC<any>;
URL?: React.FC<any>; URL?: React.FC<any>;
@ -199,6 +46,7 @@ const HeaderMenu = ({
children, children,
}) => { }) => {
const { Component, getMenuItems } = useMenuItem(); const { Component, getMenuItems } = useMenuItem();
const { styles } = useStyles();
const items = useMemo(() => { const items = useMemo(() => {
const designerBtn = { const designerBtn = {
key: 'x-designer-button', key: 'x-designer-button',
@ -225,7 +73,7 @@ const HeaderMenu = ({
<Component /> <Component />
<AntdMenu <AntdMenu
{...others} {...others}
className={headerMenuClass} className={styles.headerMenuClass}
onSelect={(info: any) => { onSelect={(info: any) => {
const s = schema.properties?.[info.key]; const s = schema.properties?.[info.key];
@ -286,6 +134,7 @@ const SideMenu = ({
designable, designable,
}) => { }) => {
const { Component, getMenuItems } = useMenuItem(); const { Component, getMenuItems } = useMenuItem();
const { styles } = useStyles();
// fix https://nocobase.height.app/T-3331/description // fix https://nocobase.height.app/T-3331/description
// 使用 ref 用来防止闭包问题 // 使用 ref 用来防止闭包问题
@ -340,7 +189,7 @@ const SideMenu = ({
onSelect={(info) => { onSelect={(info) => {
onSelect?.(info); onSelect?.(info);
}} }}
className={sideMenuClass} className={styles.sideMenuClass}
items={items as MenuProps['items']} items={items as MenuProps['items']}
/> />
</MenuModeContext.Provider>, </MenuModeContext.Provider>,
@ -488,12 +337,13 @@ Menu.Item = observer(
const { pushMenuItem } = useCollectMenuItems(); const { pushMenuItem } = useCollectMenuItems();
const { icon, children, ...others } = props; const { icon, children, ...others } = props;
const schema = useFieldSchema(); const schema = useFieldSchema();
const { styles } = useStyles();
const field = useField(); const field = useField();
const Designer = useContext(MenuItemDesignerContext); const Designer = useContext(MenuItemDesignerContext);
const item = useMemo(() => { const item = useMemo(() => {
return { return {
...others, ...others,
className: menuItemClass, className: styles.menuItemClass,
key: schema.name, key: schema.name,
eventKey: schema.name, eventKey: schema.name,
schema, schema,
@ -503,7 +353,7 @@ Menu.Item = observer(
<SortableItem <SortableItem
role="button" role="button"
aria-label={t(field.title)} aria-label={t(field.title)}
className={designerCss} className={styles.designerCss}
removeParentsIfNoChildren={false} removeParentsIfNoChildren={false}
> >
<Icon type={icon} /> <Icon type={icon} />
@ -545,6 +395,7 @@ Menu.URL = observer(
const schema = useFieldSchema(); const schema = useFieldSchema();
const field = useField(); const field = useField();
const Designer = useContext(MenuItemDesignerContext); const Designer = useContext(MenuItemDesignerContext);
const { styles } = useStyles();
if (!pushMenuItem) { if (!pushMenuItem) {
error('Menu.URL must be wrapped by GetMenuItemsContext.Provider'); error('Menu.URL must be wrapped by GetMenuItemsContext.Provider');
@ -554,7 +405,7 @@ Menu.URL = observer(
const item = useMemo(() => { const item = useMemo(() => {
return { return {
...others, ...others,
className: menuItemClass, className: styles.menuItemClass,
key: schema.name, key: schema.name,
eventKey: schema.name, eventKey: schema.name,
schema, schema,
@ -564,7 +415,11 @@ Menu.URL = observer(
label: ( label: (
<SchemaContext.Provider value={schema}> <SchemaContext.Provider value={schema}>
<FieldContext.Provider value={field}> <FieldContext.Provider value={field}>
<SortableItem className={designerCss} removeParentsIfNoChildren={false} aria-label={t(field.title)}> <SortableItem
className={styles.designerCss}
removeParentsIfNoChildren={false}
aria-label={t(field.title)}
>
<Icon type={icon} /> <Icon type={icon} />
<span <span
style={{ style={{
@ -601,17 +456,18 @@ Menu.SubMenu = observer(
const field = useField(); const field = useField();
const mode = useContext(MenuModeContext); const mode = useContext(MenuModeContext);
const Designer = useContext(MenuItemDesignerContext); const Designer = useContext(MenuItemDesignerContext);
const { styles } = useStyles();
const submenu = useMemo(() => { const submenu = useMemo(() => {
return { return {
...others, ...others,
className: menuItemClass, className: styles.menuItemClass,
key: schema.name, key: schema.name,
eventKey: schema.name, eventKey: schema.name,
label: ( label: (
<SchemaContext.Provider value={schema}> <SchemaContext.Provider value={schema}>
<FieldContext.Provider value={field}> <FieldContext.Provider value={field}>
<SortableItem <SortableItem
className={subMenuDesignerCss} className={styles.subMenuDesignerCss}
removeParentsIfNoChildren={false} removeParentsIfNoChildren={false}
aria-label={t(field.title)} aria-label={t(field.title)}
> >

View File

@ -1,7 +1,7 @@
import { css } from '@emotion/css';
import { useField, useFieldSchema } from '@tachybase/schema';
import React, { useContext, useEffect, useRef, useState } from 'react'; import React, { useContext, useEffect, useRef, useState } from 'react';
import { useRecord } from '../../../record-provider'; import { useField, useFieldSchema } from '@tachybase/schema';
import { createStyles } from 'antd-style';
const FixedBlockContext = React.createContext<{ const FixedBlockContext = React.createContext<{
setFixedBlock: (value: string | false) => void; setFixedBlock: (value: string | false) => void;
@ -37,7 +37,7 @@ export const useFixedBlock = () => {
return useContext(FixedBlockContext); return useContext(FixedBlockContext);
}; };
export const FixedBlockWrapper: React.FC = (props) => { export const FixedBlockWrapper = (props) => {
const fixedBlock = useFixedSchema(); const fixedBlock = useFixedSchema();
const { height, fixedBlockUID } = useFixedBlock(); const { height, fixedBlockUID } = useFixedBlock();
/** /**
@ -59,30 +59,36 @@ export const FixedBlockWrapper: React.FC = (props) => {
interface FixedBlockProps { interface FixedBlockProps {
height: number | string; height: number | string;
children: React.ReactNode;
} }
const fixedBlockCss = css` const useStyles = createStyles(({ css }) => {
overflow: hidden; return {
position: relative; fixedBlockCss: css`
.noco-card-item { overflow: hidden;
height: 100%; position: relative;
.ant-card { .noco-card-item {
display: flex; height: 100%;
flex-direction: column; .ant-card {
height: 100%; display: flex;
.ant-card-body { flex-direction: column;
height: 1px; height: 100%;
flex: 1; .ant-card-body {
display: flex; height: 1px;
flex-direction: column; flex: 1;
overflow: hidden; display: flex;
flex-direction: column;
overflow: hidden;
}
}
} }
} `,
} };
`; });
const FixedBlock: React.FC<FixedBlockProps> = (props) => { const FixedBlock = (props: FixedBlockProps) => {
const { height } = props; const { height } = props;
const { styles } = useStyles();
const [fixedBlockUID, _setFixedBlock] = useState<false | string>(false); const [fixedBlockUID, _setFixedBlock] = useState<false | string>(false);
const fixedBlockUIDRef = useRef(fixedBlockUID); const fixedBlockUIDRef = useRef(fixedBlockUID);
const setFixedBlock = (v) => { const setFixedBlock = (v) => {
@ -92,7 +98,7 @@ const FixedBlock: React.FC<FixedBlockProps> = (props) => {
return ( return (
<FixedBlockContext.Provider value={{ inFixedBlock: true, height, setFixedBlock, fixedBlockUID, fixedBlockUIDRef }}> <FixedBlockContext.Provider value={{ inFixedBlock: true, height, setFixedBlock, fixedBlockUID, fixedBlockUIDRef }}>
<div <div
className={fixedBlockUID ? fixedBlockCss : ''} className={fixedBlockUID ? styles.fixedBlockCss : ''}
style={{ style={{
height: fixedBlockUID ? `calc(100vh - ${height})` : undefined, height: fixedBlockUID ? `calc(100vh - ${height})` : undefined,
}} }}

View File

@ -1,16 +1,29 @@
import { css } from '@emotion/css';
import { FormItem } from '@tachybase/components';
import { Field, createForm } from '@tachybase/schema';
import { FormContext, RecursionField, observer, useField, useFieldSchema } from '@tachybase/schema';
import React, { useMemo, useRef } from 'react'; import React, { useMemo, useRef } from 'react';
import { FormItem } from '@tachybase/components';
import { createForm, Field, FormContext, observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import { useCollectionManager_deprecated } from '../../../collection-manager'; import { useCollectionManager_deprecated } from '../../../collection-manager';
import { StablePopover } from '../popover'; import { StablePopover } from '../popover';
const useStyles = createStyles(({ css }) => {
return {
overlay: css`
padding-top: 0;
.ant-popover-arrow {
display: none;
}
`,
};
});
export const Editable = observer( export const Editable = observer(
(props) => { (props) => {
const field: any = useField(); const field: any = useField();
const containerRef = useRef(null); const containerRef = useRef(null);
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { styles } = useStyles();
const value = field.value; const value = field.value;
const schema: any = { const schema: any = {
name: fieldSchema.name, name: fieldSchema.name,
@ -51,12 +64,7 @@ export const Editable = observer(
} }
trigger="click" trigger="click"
placement={'bottomLeft'} placement={'bottomLeft'}
overlayClassName={css` overlayClassName={styles.overlay}
padding-top: 0;
.ant-popover-arrow {
display: none;
}
`}
> >
<div style={{ minHeight: 30, padding: '0 8px' }}> <div style={{ minHeight: 30, padding: '0 8px' }}>
<FormContext.Provider value={form}> <FormContext.Provider value={form}>

View File

@ -1,59 +1,64 @@
import { css } from '@emotion/css';
import { observer } from '@tachybase/schema';
import React from 'react'; import React from 'react';
import { observer } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import { SortableItem, useDesigner } from '../..'; import { SortableItem, useDesigner } from '../..';
import { useFlag } from '../../../flag-provider/hooks/useFlag'; import { useFlag } from '../../../flag-provider/hooks/useFlag';
export const designerCss = ({ margin = '-18px -16px', padding = '18px 16px' } = {}) => css` export const useStyles = createStyles(({ css }, { margin = '-18px -16px', padding = '18px 16px' }) => {
position: relative; return {
margin: ${margin}; designer: css`
padding: ${padding}; position: relative;
margin: ${margin};
padding: ${padding};
&:hover { &:hover {
> .general-schema-designer { > .general-schema-designer {
display: block; display: block;
} }
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover) !important;
border: 0 !important;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
} }
} > .general-schema-designer {
} position: absolute;
`; z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover) !important;
border: 0 !important;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
};
});
export const TableColumnActionBar = observer( export const TableColumnActionBar = observer(
(props) => { (props) => {
const Designer = useDesigner(); const Designer = useDesigner();
const { isInSubTable } = useFlag() || {}; const { isInSubTable } = useFlag() || {};
const { styles } = useStyles({
margin: isInSubTable ? '-12px -8px' : '-18px -16px',
padding: isInSubTable ? '12px 8px' : '18px 16px',
});
return ( return (
<SortableItem <SortableItem className={styles.designer}>
className={designerCss({
margin: isInSubTable ? '-12px -8px' : '-18px -16px',
padding: isInSubTable ? '12px 8px' : '18px 16px',
})}
>
<Designer /> <Designer />
{props.children} {props.children}
</SortableItem> </SortableItem>

View File

@ -1,15 +1,16 @@
import { useField, useFieldSchema } from '@tachybase/schema';
import React, { useLayoutEffect } from 'react'; import React, { useLayoutEffect } from 'react';
import { useField, useFieldSchema } from '@tachybase/schema';
import { import {
CollectionFieldContext,
SortableItem, SortableItem,
useCollection_deprecated, useCollection_deprecated,
useCollectionManager_deprecated, useCollectionManager_deprecated,
useCompile, useCompile,
useDesigner, useDesigner,
CollectionFieldContext,
useFlag, useFlag,
} from '../../../'; } from '../../../';
import { designerCss } from './Table.Column.ActionBar'; import { useStyles } from './Table.Column.ActionBar';
import { isCollectionFieldComponent } from './utils'; import { isCollectionFieldComponent } from './utils';
export const useColumnSchema = () => { export const useColumnSchema = () => {
@ -33,10 +34,14 @@ export const useColumnSchema = () => {
export const TableColumnDecorator = (props) => { export const TableColumnDecorator = (props) => {
const Designer = useDesigner(); const Designer = useDesigner();
const { isInSubTable } = useFlag() || {};
const { styles } = useStyles({
margin: isInSubTable ? '-12px -8px' : '-18px -16px',
padding: isInSubTable ? '12px 8px' : '18px 16px',
});
const field = useField(); const field = useField();
const { fieldSchema, uiSchema, collectionField } = useColumnSchema(); const { fieldSchema, uiSchema, collectionField } = useColumnSchema();
const compile = useCompile(); const compile = useCompile();
const { isInSubTable } = useFlag() || {};
useLayoutEffect(() => { useLayoutEffect(() => {
if (field.title) { if (field.title) {
return; return;
@ -49,12 +54,7 @@ export const TableColumnDecorator = (props) => {
} }
}, [uiSchema?.title]); }, [uiSchema?.title]);
return ( return (
<SortableItem <SortableItem className={styles.designer}>
className={designerCss({
margin: isInSubTable ? '-12px -8px' : '-18px -16px',
padding: isInSubTable ? '12px 8px' : '18px 16px',
})}
>
<CollectionFieldContext.Provider value={collectionField}> <CollectionFieldContext.Provider value={collectionField}>
<Designer fieldSchema={fieldSchema} uiSchema={uiSchema} collectionField={collectionField} /> <Designer fieldSchema={fieldSchema} uiSchema={uiSchema} collectionField={collectionField} />
{/* <RecursionField name={columnSchema.name} schema={columnSchema}/> */} {/* <RecursionField name={columnSchema.name} schema={columnSchema}/> */}

View File

@ -0,0 +1,117 @@
import { TinyColor } from '@ctrl/tinycolor';
import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css, token }) => {
const borderColor = new TinyColor(token.colorSettings).setAlpha(0.6).toHex8String();
return {
container: css`
height: 100%;
overflow: hidden;
.ant-table-thead {
.ant-table-cell {
text-align: center;
}
}
.ant-table-wrapper {
height: 100%;
.ant-spin-nested-loading {
height: 100%;
.ant-spin-container {
height: 100%;
display: flex;
flex-direction: column;
}
}
}
.ant-table {
overflow-x: auto;
overflow-y: hidden;
}
`,
toolbar: css`
// 扩大 SchemaToolbar 的面积
.nb-action-link {
margin: -${token.paddingContentVerticalLG}px -${token.marginSM}px;
padding: ${token.paddingContentVerticalLG}px ${token.marginSM}px;
}
`,
topActive: css`
& > td {
border-top: 2px solid ${borderColor} !important;
}
`,
bottomActive: css`
& > td {
border-bottom: 2px solid ${borderColor} !important;
}
`,
highlightRow: css`
& > td {
background-color: ${token.controlItemBgActiveHover} !important;
}
&:hover > td {
background-color: ${token.controlItemBgActiveHover} !important;
}
`,
headerCellDesigner: css`
max-width: 300px;
white-space: nowrap;
&:hover .general-schema-designer {
display: block;
}
`,
bodyCell: css`
max-width: 300px;
white-space: nowrap;
.nb-read-pretty-input-number {
text-align: right;
}
.ant-color-picker-trigger {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
`,
rowSelect: css`
position: relative;
display: flex;
float: left;
align-items: center;
justify-content: space-evenly;
padding-right: 8px;
.nb-table-index {
opacity: 0;
}
&:not(.checked) {
.nb-table-index {
opacity: 1;
}
}
`,
rowSelectHover: css`
&:hover {
.nb-table-index {
opacity: 0;
}
.nb-origin-node {
display: block;
}
}
`,
cellChecked: css`
position: relative;
display: flex;
align-items: center;
justify-content: space-evenly;
`,
cellCheckedNode: css`
position: absolute;
right: 50%;
transform: translateX(50%);
&:not(.checked) {
display: none;
}
`,
};
});

View File

@ -1,19 +1,26 @@
import { DeleteOutlined, MenuOutlined, CopyOutlined } from '@ant-design/icons'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { TinyColor } from '@ctrl/tinycolor'; import {
import { SortableContext, SortableContextProps, useSortable } from '@dnd-kit/sortable'; action,
import { css } from '@emotion/css'; ArrayField,
import { ArrayField } from '@tachybase/schema'; observer,
import { spliceArrayState } from '@tachybase/schema'; RecursionField,
import { RecursionField, Schema, observer, useField, useFieldSchema } from '@tachybase/schema'; Schema,
import { action } from '@tachybase/schema'; spliceArrayState,
import { uid } from '@tachybase/schema'; uid,
useField,
useFieldSchema,
} from '@tachybase/schema';
import { isPortalInBody } from '@tachybase/utils/client'; import { isPortalInBody } from '@tachybase/utils/client';
import { CopyOutlined, DeleteOutlined, MenuOutlined } from '@ant-design/icons';
import { SortableContext, SortableContextProps, useSortable } from '@dnd-kit/sortable';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { Table as AntdTable, TableColumnProps } from 'antd'; import { Table as AntdTable, TableColumnProps } from 'antd';
import { default as classNames, default as cls } from 'classnames'; import { default as classNames, default as cls } from 'classnames';
import _, { each } from 'lodash'; import _, { each } from 'lodash';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useToken } from '../__builtins__';
import { DndContext, useDesignable, useTableSize } from '../..'; import { DndContext, useDesignable, useTableSize } from '../..';
import { import {
RecordIndexProvider, RecordIndexProvider,
@ -25,13 +32,13 @@ import {
useTableBlockContext, useTableBlockContext,
useTableSelectorContext, useTableSelectorContext,
} from '../../../'; } from '../../../';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { useACLFieldWhitelist } from '../../../acl/ACLProvider'; import { useACLFieldWhitelist } from '../../../acl/ACLProvider';
import { useToken } from '../__builtins__'; import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
import { isNewRecord, markRecordAsNew } from '../../../data-source/collection-record/isNewRecord';
import { SubFormProvider } from '../association-field/hooks'; import { SubFormProvider } from '../association-field/hooks';
import { ColumnFieldProvider } from './components/ColumnFieldProvider'; import { ColumnFieldProvider } from './components/ColumnFieldProvider';
import { useStyles } from './Table.styles';
import { extractIndex, isCollectionFieldComponent, isColumnComponent } from './utils'; import { extractIndex, isCollectionFieldComponent, isColumnComponent } from './utils';
import { isNewRecord, markRecordAsNew } from '../../../data-source/collection-record/isNewRecord';
const useArrayField = (props) => { const useArrayField = (props) => {
const field = useField<ArrayField>(); const field = useField<ArrayField>();
@ -40,6 +47,7 @@ const useArrayField = (props) => {
const useTableColumns = (props: { showDel?: boolean; isSubTable?: boolean }) => { const useTableColumns = (props: { showDel?: boolean; isSubTable?: boolean }) => {
const { token } = useToken(); const { token } = useToken();
const { styles } = useStyles();
const field = useArrayField(props); const field = useArrayField(props);
const schema = useFieldSchema(); const schema = useFieldSchema();
const { schemaInWhitelist } = useACLFieldWhitelist(); const { schemaInWhitelist } = useACLFieldWhitelist();
@ -77,16 +85,7 @@ const useTableColumns = (props: { showDel?: boolean; isSubTable?: boolean }) =>
<RecordIndexProvider index={record.__index || index}> <RecordIndexProvider index={record.__index || index}>
<RecordProvider isNew={isNewRecord(record)} record={record} parent={parentRecordData}> <RecordProvider isNew={isNewRecord(record)} record={record} parent={parentRecordData}>
<ColumnFieldProvider schema={s} basePath={field.address.concat(record.__index || index)}> <ColumnFieldProvider schema={s} basePath={field.address.concat(record.__index || index)}>
<span <span role="button" className={styles.toolbar}>
role="button"
className={css`
// 扩大 SchemaToolbar 的面积
.nb-action-link {
margin: -${token.paddingContentVerticalLG}px -${token.marginSM}px;
padding: ${token.paddingContentVerticalLG}px ${token.marginSM}px;
}
`}
>
<RecursionField <RecursionField
basePath={field.address.concat(record.__index || index)} basePath={field.address.concat(record.__index || index)}
schema={s} schema={s}
@ -173,32 +172,16 @@ const useTableColumns = (props: { showDel?: boolean; isSubTable?: boolean }) =>
}; };
const SortableRow = (props) => { const SortableRow = (props) => {
const { token } = useToken(); const { styles } = useStyles();
const id = props['data-row-key']?.toString(); const id = props['data-row-key']?.toString();
const { setNodeRef, isOver, active, over } = useSortable({ const { setNodeRef, isOver, active, over } = useSortable({
id, id,
}); });
const classObj = useMemo(() => {
const borderColor = new TinyColor(token.colorSettings).setAlpha(0.6).toHex8String();
return {
topActiveClass: css`
& > td {
border-top: 2px solid ${borderColor} !important;
}
`,
bottomActiveClass: css`
& > td {
border-bottom: 2px solid ${borderColor} !important;
}
`,
};
}, [token.colorSettings]);
const className = const className =
(active?.data.current?.sortable.index ?? -1) > (over?.data.current?.sortable?.index ?? -1) (active?.data.current?.sortable.index ?? -1) > (over?.data.current?.sortable?.index ?? -1)
? classObj.topActiveClass ? styles.topActive
: classObj.bottomActiveClass; : styles.bottomActive;
return ( return (
<tr <tr
@ -263,6 +246,7 @@ export const Table: any = withDynamicSchemaProps(
isSubTable?: boolean; isSubTable?: boolean;
}) => { }) => {
const { token } = useToken(); const { token } = useToken();
const { styles } = useStyles();
const { pagination: pagination1, useProps, ...others1 } = props; const { pagination: pagination1, useProps, ...others1 } = props;
// 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema // 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema
@ -309,14 +293,7 @@ export const Table: any = withDynamicSchemaProps(
}, },
}; };
}; };
highlightRow = css` highlightRow = styles.highlightRow;
& > td {
background-color: ${token.controlItemBgActiveHover} !important;
}
&:hover > td {
background-color: ${token.controlItemBgActiveHover} !important;
}
`;
} }
useEffect(() => { useEffect(() => {
@ -338,21 +315,7 @@ export const Table: any = withDynamicSchemaProps(
); );
}, },
cell: (props) => { cell: (props) => {
return ( return <th {...props} className={cls(props.className, styles.headerCellDesigner)} />;
<th
{...props}
className={cls(
props.className,
css`
max-width: 300px;
white-space: nowrap;
&:hover .general-schema-designer {
display: block;
}
`,
)}
/>
);
}, },
}, },
body: { body: {
@ -379,26 +342,7 @@ export const Table: any = withDynamicSchemaProps(
row: (props) => { row: (props) => {
return <SortableRow {...props}></SortableRow>; return <SortableRow {...props}></SortableRow>;
}, },
cell: (props) => ( cell: (props) => <td {...props} className={classNames(props.className, styles.bodyCell)} />,
<td
{...props}
className={classNames(
props.className,
css`
max-width: 300px;
white-space: nowrap;
.nb-read-pretty-input-number {
text-align: right;
}
.ant-color-picker-trigger {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
`,
)}
/>
),
}, },
}; };
}, [field, onRowDragEnd, dragSort]); }, [field, onRowDragEnd, dragSort]);
@ -470,67 +414,16 @@ export const Table: any = withDynamicSchemaProps(
<div <div
role="button" role="button"
aria-label={`table-index-${index}`} aria-label={`table-index-${index}`}
className={classNames( className={classNames(checked ? 'checked' : null, styles.rowSelect, {
checked ? 'checked' : null, [styles.rowSelectHover]: isRowSelect,
css` })}
position: relative;
display: flex;
float: left;
align-items: center;
justify-content: space-evenly;
padding-right: 8px;
.nb-table-index {
opacity: 0;
}
&:not(.checked) {
.nb-table-index {
opacity: 1;
}
}
`,
{
[css`
&:hover {
.nb-table-index {
opacity: 0;
}
.nb-origin-node {
display: block;
}
}
`]: isRowSelect,
},
)}
> >
<div <div className={classNames(checked ? 'checked' : null, styles.cellChecked)}>
className={classNames(
checked ? 'checked' : null,
css`
position: relative;
display: flex;
align-items: center;
justify-content: space-evenly;
`,
)}
>
{dragSort && <SortHandle id={getRowKey(record)} />} {dragSort && <SortHandle id={getRowKey(record)} />}
{showIndex && <TableIndex index={index} />} {showIndex && <TableIndex index={index} />}
</div> </div>
{isRowSelect && ( {isRowSelect && (
<div <div className={classNames('nb-origin-node', checked ? 'checked' : null, styles.cellCheckedNode)}>
className={classNames(
'nb-origin-node',
checked ? 'checked' : null,
css`
position: absolute;
right: 50%;
transform: translateX(50%);
&:not(.checked) {
display: none;
}
`,
)}
>
{originNode} {originNode}
</div> </div>
)} )}
@ -541,7 +434,7 @@ export const Table: any = withDynamicSchemaProps(
} }
: undefined, : undefined,
}; };
const SortableWrapper = useCallback<React.FC>( const SortableWrapper = useCallback(
({ children }) => { ({ children }) => {
return dragSort return dragSort
? React.createElement<Omit<SortableContextProps, 'children'>>( ? React.createElement<Omit<SortableContextProps, 'children'>>(
@ -570,32 +463,7 @@ export const Table: any = withDynamicSchemaProps(
}; };
}, [fixedBlock, tableHeight]); }, [fixedBlock, tableHeight]);
return ( return (
<div <div className={styles.container}>
className={css`
height: 100%;
overflow: hidden;
.ant-table-thead {
.ant-table-cell {
text-align: center;
}
}
.ant-table-wrapper {
height: 100%;
.ant-spin-nested-loading {
height: 100%;
.ant-spin-container {
height: 100%;
display: flex;
flex-direction: column;
}
}
}
.ant-table {
overflow-x: auto;
overflow-y: hidden;
}
`}
>
<SortableWrapper> <SortableWrapper>
<AntdTable <AntdTable
ref={tableSizeRefCallback} ref={tableSizeRefCallback}

View File

@ -1,19 +1,23 @@
import { MenuOutlined } from '@ant-design/icons'; import React, { useContext, useMemo, useState } from 'react';
import { TinyColor } from '@ctrl/tinycolor';
import { css } from '@emotion/css';
import { ArrayField, Field } from '@tachybase/schema';
import { import {
ArrayField,
Field,
observer,
RecursionField, RecursionField,
Schema, Schema,
SchemaExpressionScopeContext, SchemaExpressionScopeContext,
observer,
useField, useField,
useFieldSchema, useFieldSchema,
} from '@tachybase/schema'; } from '@tachybase/schema';
import { MenuOutlined } from '@ant-design/icons';
import { TinyColor } from '@ctrl/tinycolor';
import { Table, TableColumnProps } from 'antd'; import { Table, TableColumnProps } from 'antd';
import { createStyles } from 'antd-style';
import { default as classNames, default as cls } from 'classnames'; import { default as classNames, default as cls } from 'classnames';
import React, { useContext, useState } from 'react';
import ReactDragListView from 'react-drag-listview'; import ReactDragListView from 'react-drag-listview';
import { useToken } from '../__builtins__';
import { DndContext } from '../..'; import { DndContext } from '../..';
import { import {
RecordIndexProvider, RecordIndexProvider,
@ -23,7 +27,68 @@ import {
useRequest, useRequest,
useSchemaInitializerRender, useSchemaInitializerRender,
} from '../../../'; } from '../../../';
import { useToken } from '../__builtins__';
const useStyles = createStyles(({ css, token }) => {
return {
drag: css`
position: relative;
display: flex;
align-items: center;
justify-content: space-evenly;
`,
node: css`
position: absolute;
right: 50%;
transform: translateX(50%);
&:not(.checked) {
display: none;
}
`,
headerCell: css`
&:hover .general-schema-designer {
display: block;
}
`,
bodyCell: css`
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`,
cell: css`
position: relative;
display: flex;
align-items: center;
justify-content: space-evenly;
padding-right: 8px;
.nb-table-index {
opacity: 0;
}
&:not(.checked) {
.nb-table-index {
opacity: 1;
}
}
&:hover {
.nb-table-index {
opacity: 0;
}
.nb-origin-node {
display: block;
}
}
`,
table: css`
.ant-table {
overflow-x: auto;
overflow-y: hidden;
}
`,
line: css`
border-bottom: 2px solid ${new TinyColor(token.colorSettings).setAlpha(0.6).toHex8String()} !important;
`,
};
});
const isColumnComponent = (schema: Schema) => { const isColumnComponent = (schema: Schema) => {
return schema['x-component']?.endsWith('.Column') > -1; return schema['x-component']?.endsWith('.Column') > -1;
@ -36,6 +101,7 @@ const useTableColumns = () => {
const scope = useContext(SchemaExpressionScopeContext); const scope = useContext(SchemaExpressionScopeContext);
const parentRecordData = useCollectionParentRecordData(); const parentRecordData = useCollectionParentRecordData();
const recordData = useCollectionRecordData(); const recordData = useCollectionRecordData();
const { styles } = useStyles();
const columns = schema const columns = schema
.reduceProperties((buf, s) => { .reduceProperties((buf, s) => {
@ -73,59 +139,6 @@ const useTableColumns = () => {
}); });
}; };
export const components = {
header: {
wrapper: (props) => {
return (
<DndContext>
<thead {...props} />
</DndContext>
);
},
cell: (props) => {
return (
<th
{...props}
className={cls(
props.className,
css`
&:hover .general-schema-designer {
display: block;
}
`,
)}
/>
);
},
},
body: {
wrapper: (props) => {
return (
<DndContext>
<tbody {...props} />
</DndContext>
);
},
row: (props) => {
return <tr {...props} />;
},
cell: (props) => (
<td
{...props}
className={classNames(
props.className,
css`
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`,
)}
/>
),
},
};
const useDef = () => { const useDef = () => {
const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [selectedRowKeys, setSelectedRowKeys] = useState([]);
return [selectedRowKeys, setSelectedRowKeys]; return [selectedRowKeys, setSelectedRowKeys];
@ -174,6 +187,37 @@ export const TableArray: React.FC<any> = observer(
...others ...others
} = props; } = props;
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys(); const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
const { styles } = useStyles();
const components = useMemo(
() => ({
header: {
wrapper: (props) => {
return (
<DndContext>
<thead {...props} />
</DndContext>
);
},
cell: (props) => {
return <th {...props} className={cls(props.className, styles.headerCell)} />;
},
},
body: {
wrapper: (props) => {
return (
<DndContext>
<tbody {...props} />
</DndContext>
);
},
row: (props) => {
return <tr {...props} />;
},
cell: (props) => <td {...props} className={classNames(props.className, styles.bodyCell)} />,
},
}),
[styles],
);
useDataSource({ useDataSource({
onSuccess(data) { onSuccess(data) {
field.value = data?.data || []; field.value = data?.data || [];
@ -200,64 +244,14 @@ export const TableArray: React.FC<any> = observer(
index = index + (current - 1) * pageSize; index = index + (current - 1) * pageSize;
} }
return ( return (
<div <div className={classNames(checked ? 'checked' : null, styles.cell)}>
className={classNames( <div className={classNames(checked ? 'checked' : null, styles.drag)}>
checked ? 'checked' : null,
css`
position: relative;
display: flex;
align-items: center;
justify-content: space-evenly;
padding-right: 8px;
.nb-table-index {
opacity: 0;
}
&:not(.checked) {
.nb-table-index {
opacity: 1;
}
}
&:hover {
.nb-table-index {
opacity: 0;
}
.nb-origin-node {
display: block;
}
}
`,
)}
>
<div
className={classNames(
checked ? 'checked' : null,
css`
position: relative;
display: flex;
align-items: center;
justify-content: space-evenly;
`,
)}
>
{dragSort && <SortHandle role="button" aria-label={`sort-handle-${record?.name || index}`} />} {dragSort && <SortHandle role="button" aria-label={`sort-handle-${record?.name || index}`} />}
{showIndex && ( {showIndex && (
<TableIndex role="button" aria-label={`table-index-${record?.name || index}`} index={index} /> <TableIndex role="button" aria-label={`table-index-${record?.name || index}`} index={index} />
)} )}
</div> </div>
<div <div className={classNames('nb-origin-node', checked ? 'checked' : null, styles.node)}>
className={classNames(
'nb-origin-node',
checked ? 'checked' : null,
css`
position: absolute;
right: 50%;
transform: translateX(50%);
&:not(.checked) {
display: none;
}
`,
)}
>
{originNode} {originNode}
</div> </div>
</div> </div>
@ -273,14 +267,8 @@ export const TableArray: React.FC<any> = observer(
}; };
return ( return (
<div <div className={styles.table}>
className={css` {/* @ts-ignore */}
.ant-table {
overflow-x: auto;
overflow-y: hidden;
}
`}
>
<ReactDragListView <ReactDragListView
handleSelector={'.drag-handle'} handleSelector={'.drag-handle'}
onDragEnd={async (fromIndex, toIndex) => { onDragEnd={async (fromIndex, toIndex) => {
@ -289,9 +277,7 @@ export const TableArray: React.FC<any> = observer(
field.move(fromIndex, toIndex); field.move(fromIndex, toIndex);
await move(from, to); await move(from, to);
}} }}
lineClassName={css` lineClassName={styles.line}
border-bottom: 2px solid ${new TinyColor(token.colorSettings).setAlpha(0.6).toHex8String()} !important;
`}
> >
<Table <Table
rowKey={defaultRowKey} rowKey={defaultRowKey}

View File

@ -1,53 +1,56 @@
import { css } from '@emotion/css';
import { observer } from '@tachybase/schema';
import React from 'react'; import React from 'react';
import { observer } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import { SortableItem, useDesigner } from '../..'; import { SortableItem, useDesigner } from '../..';
export const designerCss = css` export const useStyles = createStyles(({ css }) => {
position: relative; return {
&:hover { designer: css`
> .general-schema-designer { position: relative;
display: block; &:hover {
} > .general-schema-designer {
} display: block;
> .general-schema-designer { }
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover) !important;
border: 0 !important;
top: -16px !important;
bottom: -16px !important;
left: -16px !important;
right: -16px !important;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
} }
} > .general-schema-designer {
} position: absolute;
`; z-index: 999;
display: none;
background: var(--colorBgSettingsHover) !important;
border: 0 !important;
top: -16px !important;
bottom: -16px !important;
left: -16px !important;
right: -16px !important;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
};
});
export const TableColumnActionBar = observer( export const TableColumnActionBar = observer(
(props) => { (props) => {
const { styles } = useStyles();
const Designer = useDesigner(); const Designer = useDesigner();
return ( return (
<SortableItem className={designerCss}> <SortableItem className={styles.designer}>
<Designer /> <Designer />
{props.children} {props.children}
</SortableItem> </SortableItem>

View File

@ -1,7 +1,8 @@
import { useField, useFieldSchema } from '@tachybase/schema';
import React, { useLayoutEffect } from 'react'; import React, { useLayoutEffect } from 'react';
import { useField, useFieldSchema } from '@tachybase/schema';
import { SortableItem, useCollection_deprecated, useCompile, useDesignable, useDesigner } from '../../../'; import { SortableItem, useCollection_deprecated, useCompile, useDesignable, useDesigner } from '../../../';
import { designerCss } from './Table.Column.ActionBar'; import { useStyles } from './Table.Column.ActionBar';
export const useColumnSchema = () => { export const useColumnSchema = () => {
const { getField } = useCollection_deprecated(); const { getField } = useCollection_deprecated();
@ -24,6 +25,7 @@ export const TableColumnDecorator = (props) => {
const Designer = useDesigner(); const Designer = useDesigner();
const field = useField(); const field = useField();
const { fieldSchema, uiSchema, collectionField } = useColumnSchema(); const { fieldSchema, uiSchema, collectionField } = useColumnSchema();
const { styles } = useStyles();
const { refresh } = useDesignable(); const { refresh } = useDesignable();
const compile = useCompile(); const compile = useCompile();
useLayoutEffect(() => { useLayoutEffect(() => {
@ -38,7 +40,7 @@ export const TableColumnDecorator = (props) => {
} }
}, [uiSchema?.title]); }, [uiSchema?.title]);
return ( return (
<SortableItem className={designerCss}> <SortableItem className={styles.designer}>
<Designer fieldSchema={fieldSchema} uiSchema={uiSchema} collectionField={collectionField} /> <Designer fieldSchema={fieldSchema} uiSchema={uiSchema} collectionField={collectionField} />
{/* <RecursionField name={columnSchema.name} schema={columnSchema}/> */} {/* <RecursionField name={columnSchema.name} schema={columnSchema}/> */}
{field.title || compile(uiSchema?.title)} {field.title || compile(uiSchema?.title)}

View File

@ -1,14 +1,68 @@
import { css } from '@emotion/css';
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { Tabs as AntdTabs, TabPaneProps, TabsProps } from 'antd';
import classNames from 'classnames';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { Tabs as AntdTabs, TabPaneProps, TabsProps } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import { useSchemaInitializerRender } from '../../../application';
import { Icon } from '../../../icon'; import { Icon } from '../../../icon';
import { DndContext, SortableItem } from '../../common'; import { DndContext, SortableItem } from '../../common';
import { useDesigner } from '../../hooks/useDesigner'; import { useDesigner } from '../../hooks/useDesigner';
import { useTabsContext } from './context'; import { useTabsContext } from './context';
import { TabsDesigner } from './Tabs.Designer'; import { TabsDesigner } from './Tabs.Designer';
import { useSchemaInitializerRender } from '../../../application';
const useStyles = createStyles(({ css }) => {
return {
designer: css`
position: relative;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-action-link {
> .general-schema-designer {
top: -10px;
bottom: -10px;
left: -10px;
right: -10px;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
};
});
export const Tabs: any = observer( export const Tabs: any = observer(
(props: TabsProps) => { (props: TabsProps) => {
@ -48,60 +102,13 @@ export const Tabs: any = observer(
{ displayName: 'Tabs' }, { displayName: 'Tabs' },
); );
const designerCss = css`
position: relative;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-action-link {
> .general-schema-designer {
top: -10px;
bottom: -10px;
left: -10px;
right: -10px;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`;
Tabs.TabPane = observer( Tabs.TabPane = observer(
(props: TabPaneProps & { icon?: any }) => { (props: TabPaneProps & { icon?: any }) => {
const { styles } = useStyles();
const Designer = useDesigner(); const Designer = useDesigner();
const field = useField(); const field = useField();
return ( return (
<SortableItem className={classNames('nb-action-link', designerCss, props.className)}> <SortableItem className={classNames('nb-action-link', styles.designer, props.className)}>
{props.icon && <Icon style={{ marginRight: 2 }} type={props.icon} />} {props.tab || field.title} {props.icon && <Icon style={{ marginRight: 2 }} type={props.icon} />} {props.tab || field.title}
<Designer /> <Designer />
</SortableItem> </SortableItem>

View File

@ -1,18 +1,21 @@
import { CloseCircleFilled } from '@ant-design/icons'; import React, { useCallback, useEffect, useMemo } from 'react';
import { css, cx } from '@emotion/css';
import { useForm } from '@tachybase/schema'; import { useForm } from '@tachybase/schema';
import { error } from '@tachybase/utils/client'; import { error } from '@tachybase/utils/client';
import { CloseCircleFilled } from '@ant-design/icons';
import { Input as AntInput, Cascader, DatePicker, InputNumber, Select, Space, Tag } from 'antd'; import { Input as AntInput, Cascader, DatePicker, InputNumber, Select, Space, Tag } from 'antd';
import { createStyles } from 'antd-style';
import useAntdInputStyle from 'antd/es/input/style'; import useAntdInputStyle from 'antd/es/input/style';
import type { DefaultOptionType } from 'antd/lib/cascader'; import type { DefaultOptionType } from 'antd/lib/cascader';
import cx from 'classnames';
import classNames from 'classnames'; import classNames from 'classnames';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import React, { useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useCompile } from '../../hooks'; import { useCompile } from '../../hooks';
import { XButton } from './XButton';
import { useStyles } from './style'; import { useStyles } from './style';
import { XButton } from './XButton';
const JT_VALUE_RE = /^\s*{{\s*([^{}]+)\s*}}\s*$/; const JT_VALUE_RE = /^\s*{{\s*([^{}]+)\s*}}\s*$/;
@ -129,6 +132,39 @@ function getTypedConstantOption(type: string, types: true | string[], fieldNames
}; };
} }
const useStyles2 = createStyles(({ css }, { props: { disabled: boolean } }) => {
return {
container: css`
position: relative;
line-height: 0;
&:hover {
.clear-button {
display: inline-block;
}
}
.ant-input {
overflow: auto;
white-space: nowrap;
${disabled ? '' : 'padding-right: 28px;'}
.ant-tag {
display: inline;
line-height: 19px;
margin: 0;
padding: 2px 7px;
border-radius: 10px;
white-space: nowrap;
}
}
`,
button: css`
margin-left: -1px;
`,
};
});
export function Input(props) { export function Input(props) {
const { const {
value = '', value = '',
@ -259,40 +295,12 @@ export function Input(props) {
}, [variable, options.length]); }, [variable, options.length]);
const disabled = props.disabled || form.disabled; const disabled = props.disabled || form.disabled;
const { styles: styles2 } = useStyles2({ disabled });
return wrapSSR( return wrapSSR(
<Space.Compact style={style} className={classNames(componentCls, hashId, className)}> <Space.Compact style={style} className={classNames(componentCls, hashId, className)}>
{variable ? ( {variable ? (
<div <div className={cx('variable', styles2.container)}>
className={cx(
'variable',
css`
position: relative;
line-height: 0;
&:hover {
.clear-button {
display: inline-block;
}
}
.ant-input {
overflow: auto;
white-space: nowrap;
${disabled ? '' : 'padding-right: 28px;'}
.ant-tag {
display: inline;
line-height: 19px;
margin: 0;
padding: 2px 7px;
border-radius: 10px;
white-space: nowrap;
}
}
`,
)}
>
<div <div
role="button" role="button"
aria-label="variable-tag" aria-label="variable-tag"
@ -341,14 +349,7 @@ export function Input(props) {
fieldNames={fieldNames} fieldNames={fieldNames}
disabled={disabled} disabled={disabled}
> >
{button ?? ( {button ?? <XButton className={styles2.button} type={variable ? 'primary' : 'default'} />}
<XButton
className={css(`
margin-left: -1px;
`)}
type={variable ? 'primary' : 'default'}
/>
)}
</Cascader> </Cascader>
</Space.Compact>, </Space.Compact>,
); );

View File

@ -1,21 +1,23 @@
import React from 'react'; import React from 'react';
import { createStyles } from 'antd-style';
import { Input } from '../input'; import { Input } from '../input';
import { RawTextArea } from './RawTextArea'; import { RawTextArea } from './RawTextArea';
import { css } from '@emotion/css';
const useStyles = createStyles(({ css }) => {
return {
button: css`
&:not(:hover) {
border-right-color: transparent;
border-top-color: transparent;
}
background-color: transparent;
`,
};
});
export function JSONInput(props) { export function JSONInput(props) {
return ( const { styles } = useStyles();
<RawTextArea return <RawTextArea buttonClass={styles.button} {...props} component={Input.JSON} />;
buttonClass={css`
&:not(:hover) {
border-right-color: transparent;
border-top-color: transparent;
}
background-color: transparent;
`}
{...props}
component={Input.JSON}
/>
);
} }

View File

@ -1,6 +1,7 @@
import React, { useRef, useState } from 'react'; import React, { useRef, useState } from 'react';
import { css } from '@emotion/css';
import { Button, Input } from 'antd'; import { Button, Input } from 'antd';
import { createStyles } from 'antd-style';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { VariableSelect } from './VariableSelect'; import { VariableSelect } from './VariableSelect';
@ -16,11 +17,38 @@ function setNativeInputValue(input, value) {
); );
} }
const useStyles = createStyles(({ css }) => {
return {
container: css`
position: relative;
.ant-input {
width: 100%;
}
`,
group: css`
position: absolute;
right: 0;
top: 0;
.ant-btn-sm {
font-size: 85%;
}
`,
select: css`
&:not(:hover) {
border-right-color: transparent;
border-top-color: transparent;
}
background-color: transparent;
`,
};
});
export function RawTextArea(props): JSX.Element { export function RawTextArea(props): JSX.Element {
const inputRef = useRef<any>(null); const inputRef = useRef<any>(null);
const { changeOnSelect, component: Component = Input.TextArea, ...others } = props; const { changeOnSelect, component: Component = Input.TextArea, ...others } = props;
const scope = typeof props.scope === 'function' ? props.scope() : props.scope; const scope = typeof props.scope === 'function' ? props.scope() : props.scope;
const [options, setOptions] = useState(scope ? cloneDeep(scope) : []); const [options, setOptions] = useState(scope ? cloneDeep(scope) : []);
const { styles } = useStyles();
function onInsert(selected) { function onInsert(selected) {
if (!inputRef.current) { if (!inputRef.current) {
@ -38,36 +66,11 @@ export function RawTextArea(props): JSX.Element {
textArea.focus(); textArea.focus();
} }
return ( return (
<div <div className={styles.container}>
className={css`
position: relative;
.ant-input {
width: 100%;
}
`}
>
<Component {...others} ref={inputRef} /> <Component {...others} ref={inputRef} />
<Button.Group <Button.Group className={styles.group}>
className={css`
position: absolute;
right: 0;
top: 0;
.ant-btn-sm {
font-size: 85%;
}
`}
>
<VariableSelect <VariableSelect
className={ className={props.buttonClass ?? styles.select}
props.buttonClass ??
css`
&:not(:hover) {
border-right-color: transparent;
border-top-color: transparent;
}
background-color: transparent;
`
}
fieldNames={props.fieldNames} fieldNames={props.fieldNames}
options={options} options={options}
setOptions={setOptions} setOptions={setOptions}

View File

@ -1,15 +1,16 @@
import { css, cx } from '@emotion/css';
import { useForm } from '@tachybase/schema';
import { Input, Space } from 'antd';
import { cloneDeep } from 'lodash';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import sanitizeHTML from 'sanitize-html'; import { useForm } from '@tachybase/schema';
import { error } from '@tachybase/utils/client'; import { error } from '@tachybase/utils/client';
import { Space } from 'antd';
import { createStyles } from 'antd-style';
import cx from 'classnames';
import { cloneDeep } from 'lodash';
import sanitizeHTML from 'sanitize-html';
import { EllipsisWithTooltip } from '../..'; import { EllipsisWithTooltip } from '../..';
import { VariableSelect } from './VariableSelect';
import { useStyles } from './style'; import { useStyles } from './style';
import { VariableSelect } from './VariableSelect';
type RangeIndexes = [number, number, number, number]; type RangeIndexes = [number, number, number, number];
@ -187,12 +188,60 @@ function getCurrentRange(element: HTMLElement): RangeIndexes {
return result; return result;
} }
const useStyles2 = createStyles(({ css }) => {
return {
container: css`
&.ant-input-group.ant-input-group-compact {
display: flex;
.ant-input {
flex-grow: 1;
min-width: 200px;
}
.ant-input-disabled {
.ant-tag {
color: #bfbfbf;
border-color: #d9d9d9;
}
}
}
> .x-button {
height: min-content;
}
`,
button: css`
overflow: auto;
white-space: ${multiline ? 'normal' : 'nowrap'};
.ant-tag {
display: inline;
line-height: 19px;
margin: 0 0.5em;
padding: 2px 7px;
border-radius: 10px;
}
`,
pretty: css`
overflow: auto;
.ant-tag {
display: inline;
line-height: 19px;
margin: 0 0.25em;
padding: 2px 7px;
border-radius: 10px;
}
`,
};
});
export function TextArea(props) { export function TextArea(props) {
const { wrapSSR, hashId, componentCls } = useStyles(); const { wrapSSR, hashId, componentCls } = useStyles();
const { value = '', scope, onChange, multiline = true, changeOnSelect } = props; const { value = '', scope, onChange, multiline = true, changeOnSelect } = props;
const inputRef = useRef<HTMLDivElement>(null); const inputRef = useRef<HTMLDivElement>(null);
const [options, setOptions] = useState([]); const [options, setOptions] = useState([]);
const form = useForm(); const form = useForm();
const { styles } = useStyles2();
const keyLabelMap = useMemo(() => createOptionsValueLabelMap(options), [options]); const keyLabelMap = useMemo(() => createOptionsValueLabelMap(options), [options]);
const [ime, setIME] = useState<boolean>(false); const [ime, setIME] = useState<boolean>(false);
const [changed, setChanged] = useState(false); const [changed, setChanged] = useState(false);
@ -360,31 +409,7 @@ export function TextArea(props) {
const disabled = props.disabled || form.disabled; const disabled = props.disabled || form.disabled;
return wrapSSR( return wrapSSR(
<Space.Compact <Space.Compact className={cx(componentCls, hashId, styles.container)}>
className={cx(
componentCls,
hashId,
css`
&.ant-input-group.ant-input-group-compact {
display: flex;
.ant-input {
flex-grow: 1;
min-width: 200px;
}
.ant-input-disabled {
.ant-tag {
color: #bfbfbf;
border-color: #d9d9d9;
}
}
}
> .x-button {
height: min-content;
}
`,
)}
>
<div <div
role="button" role="button"
aria-label="textbox" aria-label="textbox"
@ -394,23 +419,7 @@ export function TextArea(props) {
onPaste={onPaste} onPaste={onPaste}
onCompositionStart={onCompositionStart} onCompositionStart={onCompositionStart}
onCompositionEnd={onCompositionEnd} onCompositionEnd={onCompositionEnd}
className={cx( className={cx(hashId, 'ant-input', { 'ant-input-disabled': disabled }, styles.button)}
hashId,
'ant-input',
{ 'ant-input-disabled': disabled },
css`
overflow: auto;
white-space: ${multiline ? 'normal' : 'nowrap'};
.ant-tag {
display: inline;
line-height: 19px;
margin: 0 0.5em;
padding: 2px 7px;
border-radius: 10px;
}
`,
)}
ref={inputRef} ref={inputRef}
contentEditable={!disabled} contentEditable={!disabled}
dangerouslySetInnerHTML={{ __html: html }} dangerouslySetInnerHTML={{ __html: html }}
@ -458,12 +467,13 @@ async function preloadOptions(scope, value) {
return options; return options;
} }
TextArea.ReadPretty = function ReadPretty(props): JSX.Element { TextArea.ReadPretty = function ReadPretty(props) {
const { value } = props; const { value } = props;
const scope = typeof props.scope === 'function' ? props.scope() : props.scope; const scope = typeof props.scope === 'function' ? props.scope() : props.scope;
const [options, setOptions] = useState([]); const [options, setOptions] = useState([]);
const keyLabelMap = useMemo(() => createOptionsValueLabelMap(options), [options]); const keyLabelMap = useMemo(() => createOptionsValueLabelMap(options), [options]);
const { styles } = useStyles2();
useEffect(() => { useEffect(() => {
preloadOptions(scope, value) preloadOptions(scope, value)
@ -474,22 +484,7 @@ TextArea.ReadPretty = function ReadPretty(props): JSX.Element {
}, [scope, value]); }, [scope, value]);
const html = renderHTML(value ?? '', keyLabelMap); const html = renderHTML(value ?? '', keyLabelMap);
const content = ( const content = <span dangerouslySetInnerHTML={{ __html: html }} className={styles.pretty} />;
<span
dangerouslySetInnerHTML={{ __html: html }}
className={css`
overflow: auto;
.ant-tag {
display: inline;
line-height: 19px;
margin: 0 0.25em;
padding: 2px 7px;
border-radius: 10px;
}
`}
/>
);
return ( return (
<EllipsisWithTooltip ellipsis popoverContent={content}> <EllipsisWithTooltip ellipsis popoverContent={content}>

View File

@ -1,7 +1,9 @@
import { cx } from '@emotion/css';
import { Cascader } from 'antd';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Cascader } from 'antd';
import cx from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useToken } from '../__builtins__'; import { useToken } from '../__builtins__';
import useStyles from './VariableSelect.style'; import useStyles from './VariableSelect.style';
import { XButton } from './XButton'; import { XButton } from './XButton';

View File

@ -1,8 +1,10 @@
import React, { createContext, HTMLAttributes, useContext, useMemo } from 'react';
import { observer, Schema, useField, useFieldSchema } from '@tachybase/schema';
import { TinyColor } from '@ctrl/tinycolor'; import { TinyColor } from '@ctrl/tinycolor';
import { useDraggable, useDroppable } from '@dnd-kit/core'; import { useDraggable, useDroppable } from '@dnd-kit/core';
import { cx } from '@emotion/css'; import cx from 'classnames';
import { Schema, observer, useField, useFieldSchema } from '@tachybase/schema';
import React, { HTMLAttributes, createContext, useContext, useMemo } from 'react';
import { useToken } from '../../antd/__builtins__'; import { useToken } from '../../antd/__builtins__';
export const DraggableContext = createContext(null); export const DraggableContext = createContext(null);

View File

@ -1,9 +1,11 @@
import { DownOutlined } from '@ant-design/icons'; import React, { createRef, forwardRef, useEffect, useMemo, useState } from 'react';
import { css } from '@emotion/css';
import { observer, RecursionField, useField, useFieldSchema, useForm } from '@tachybase/schema'; import { observer, RecursionField, useField, useFieldSchema, useForm } from '@tachybase/schema';
import { DownOutlined } from '@ant-design/icons';
import { Button, Dropdown, MenuProps } from 'antd'; import { Button, Dropdown, MenuProps } from 'antd';
import React, { useEffect, useMemo, useState, forwardRef, createRef } from 'react'; import { createStyles } from 'antd-style';
import { composeRef } from 'rc-util/lib/ref'; import { composeRef } from 'rc-util/lib/ref';
import { useDesignable } from '../../'; import { useDesignable } from '../../';
import { useACLRolesCheck, useRecordPkValue } from '../../acl/ACLProvider'; import { useACLRolesCheck, useRecordPkValue } from '../../acl/ACLProvider';
import { import {
@ -17,45 +19,45 @@ import { linkageAction } from '../../schema-component/antd/action/utils';
import { parseVariables } from '../../schema-component/common/utils/uitls'; import { parseVariables } from '../../schema-component/common/utils/uitls';
import { useLocalVariables, useVariables } from '../../variables'; import { useLocalVariables, useVariables } from '../../variables';
export const actionDesignerCss = css` const useStyles = createStyles(({ css }) => {
position: relative; return {
&:hover { designer: css`
.general-schema-designer { position: relative;
display: block; &:hover {
} .general-schema-designer {
} display: block;
.general-schema-designer { }
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
} }
} .general-schema-designer {
} position: absolute;
`; z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--colorBgSettingsHover);
border: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: var(--colorSettings);
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
align-self: stretch;
}
}
}
`,
};
});
export function useAclCheck(actionPath) { export function useAclCheck(actionPath) {
const aclCheck = useAclCheckFn(); const aclCheck = useAclCheckFn();
@ -108,6 +110,7 @@ const InternalCreateRecordAction = (props: any, ref) => {
const values = useRecord(); const values = useRecord();
const ctx = useActionContext(); const ctx = useActionContext();
const variables = useVariables(); const variables = useVariables();
const { styles } = useStyles();
const localVariables = useLocalVariables({ currentForm: { values } as any }); const localVariables = useLocalVariables({ currentForm: { values } as any });
useEffect(() => { useEffect(() => {
field.stateOfLinkageRules = {}; field.stateOfLinkageRules = {};
@ -129,7 +132,7 @@ const InternalCreateRecordAction = (props: any, ref) => {
const buttonRef = composeRef(ref, internalRef); const buttonRef = composeRef(ref, internalRef);
return ( return (
//@ts-ignore //@ts-ignore
<div className={actionDesignerCss} ref={buttonRef as React.Ref<HTMLButtonElement>}> <div className={styles.actionDesignerCss} ref={buttonRef as React.Ref<HTMLButtonElement>}>
<ActionContextProvider value={{ ...ctx, visible, setVisible }}> <ActionContextProvider value={{ ...ctx, visible, setVisible }}>
<CreateAction <CreateAction
{...props} {...props}
@ -201,6 +204,7 @@ export const CreateAction = observer(
const localVariables = useLocalVariables({ currentForm: { values } as any }); const localVariables = useLocalVariables({ currentForm: { values } as any });
const compile = useCompile(); const compile = useCompile();
const { designable } = useDesignable(); const { designable } = useDesignable();
const { styles } = useStyles();
const icon = props.icon || null; const icon = props.icon || null;
const menuItems = useMemo<MenuProps['items']>(() => { const menuItems = useMemo<MenuProps['items']>(() => {
return inheritsCollections.map((option) => ({ return inheritsCollections.map((option) => ({
@ -233,7 +237,7 @@ export const CreateAction = observer(
}); });
}, [field, linkageRules, localVariables, variables]); }, [field, linkageRules, localVariables, variables]);
return ( return (
<div className={actionDesignerCss}> <div className={styles.actionDesignerCss}>
<FinallyButton <FinallyButton
{...{ {...{
inheritsCollections, inheritsCollections,

View File

@ -1,51 +1,20 @@
import { DragOutlined, MenuOutlined, PlusOutlined } from '@ant-design/icons'; import React, { FC, PropsWithChildren, useEffect, useMemo, useRef } from 'react';
import { css } from '@emotion/css';
import { useField, useFieldSchema } from '@tachybase/schema'; import { useField, useFieldSchema } from '@tachybase/schema';
import { DragOutlined, MenuOutlined, PlusOutlined } from '@ant-design/icons';
import { Space } from 'antd'; import { Space } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import React, { FC, PropsWithChildren, useEffect, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { SchemaToolbarProvider, useSchemaInitializerRender, useSchemaSettingsRender } from '../application'; import { SchemaToolbarProvider, useSchemaInitializerRender, useSchemaSettingsRender } from '../application';
import { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider'; import { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider';
import { useDataSource } from '../data-source/data-source/DataSourceProvider'; import { useDataSource } from '../data-source/data-source/DataSourceProvider';
import { DragHandler, useCompile, useDesignable, useGridContext, useGridRowContext } from '../schema-component'; import { DragHandler, useCompile, useDesignable, useGridContext, useGridRowContext } from '../schema-component';
import { gridRowColWrap } from '../schema-initializer/utils'; import { gridRowColWrap } from '../schema-initializer/utils';
import { SchemaSettingsDropdown } from './SchemaSettings';
import { useGetAriaLabelOfDesigner } from './hooks/useGetAriaLabelOfDesigner'; import { useGetAriaLabelOfDesigner } from './hooks/useGetAriaLabelOfDesigner';
import { SchemaSettingsDropdown } from './SchemaSettings';
import { useStyles } from './styles'; import { useStyles } from './styles';
const titleCss = css`
pointer-events: none;
position: absolute;
font-size: 12px;
/* background: var(--colorSettings);
color: #fff; */
padding: 0;
line-height: 16px;
height: 16px;
border-bottom-right-radius: 2px;
border-radius: 2px;
top: 2px;
left: 2px;
.title-tag {
padding: 0 3px;
border-radius: 2px;
background: var(--colorSettings);
color: #fff;
display: block;
}
`;
const overrideAntdCSS = css`
& .ant-space-item .anticon {
margin: 0;
}
&:hover {
display: block !important;
}
`;
export interface GeneralSchemaDesignerProps { export interface GeneralSchemaDesignerProps {
disableInitializer?: boolean; disableInitializer?: boolean;
title?: string; title?: string;
@ -89,6 +58,7 @@ export const GeneralSchemaDesigner: FC<PropsWithChildren<GeneralSchemaDesignerPr
); );
const rowCtx = useGridRowContext(); const rowCtx = useGridRowContext();
const ctx = useGridContext(); const ctx = useGridContext();
const { styles } = useStyles();
const dm = useDataSourceManager(); const dm = useDataSourceManager();
const dataSources = dm?.getDataSources(); const dataSources = dm?.getDataSources();
const dataSourceContext = useDataSource(); const dataSourceContext = useDataSource();
@ -116,9 +86,9 @@ export const GeneralSchemaDesigner: FC<PropsWithChildren<GeneralSchemaDesignerPr
} }
return ( return (
<SchemaToolbarProvider {...contextValue}> <SchemaToolbarProvider {...contextValue}>
<div className={classNames('general-schema-designer', overrideAntdCSS)}> <div className={classNames('general-schema-designer', styles.overrideAntdCSS)}>
{title && ( {title && (
<div className={classNames('general-schema-designer-title', titleCss)}> <div className={classNames('general-schema-designer-title', styles.titleCss)}>
<Space size={2}> <Space size={2}>
<span className={'title-tag'}> <span className={'title-tag'}>
{showDataSource && dataSource {showDataSource && dataSource

View File

@ -1,6 +1,6 @@
import { createStyles } from 'antd-style'; import { createStyles } from 'antd-style';
export const useStyles = createStyles(() => { export const useStyles = createStyles(({ css }) => {
return { return {
toolbar: { toolbar: {
position: 'absolute', position: 'absolute',
@ -52,5 +52,35 @@ export const useStyles = createStyles(() => {
alignSelf: 'stretch', alignSelf: 'stretch',
}, },
}, },
titleCss: css`
pointer-events: none;
position: absolute;
font-size: 12px;
/* background: var(--colorSettings);
color: #fff; */
padding: 0;
line-height: 16px;
height: 16px;
border-bottom-right-radius: 2px;
border-radius: 2px;
top: 2px;
left: 2px;
.title-tag {
padding: 0 3px;
border-radius: 2px;
background: var(--colorSettings);
color: #fff;
display: block;
}
`,
overrideAntdCSS: css`
& .ant-space-item .anticon {
margin: 0;
}
&:hover {
display: block !important;
}
`,
}; };
}); });