refactor(mobile): optimize style (#6270)

* feat(mobile): improve mobile UI styling and theme

- Adjust GridCard and mobile page styling
- Update mobile theme tokens with compact algorithm
- Refine spacing, border radius, and padding for mobile components

* feat(mobile): enhance workbench block layout and styling for mobile

- Refactor WorkbenchBlock and WorkbenchAction components for improved mobile responsiveness
- Integrate antd-mobile Grid and List components
- Update styling for grid and list layouts
- Optimize responsive space and item rendering
- Remove manual width calculations and resize observers

* feat(mobile): adjust menu item styling for mobile layout

- Add margin and width adjustments for mobile menu items
- Ensure consistent spacing and sizing of menu elements
- Improve visual layout of menu items in mobile view

* feat(mobile): adjust table container margins in mobile layout

- Remove hardcoded negative margins for table containers
- Improve responsive styling for mobile table layouts

* feat(mobile): refine grid card and nester component styling

- Update GridCard component with token-based margin and border radius
- Adjust InternalNester CSS with dynamic padding and margin
- Improve responsive styling for grid and card components

* feat(mobile): clean up and optimize mobile component styles

- Remove unnecessary chart block and action bar styling
- Simplify CSS rules for mobile components
- Clean up commented-out and redundant style definitions

* test(mobile): improve z-index test with precise click positioning

- Add click positioning to edit action buttons in z-index test
- Ensure consistent and precise interaction in multi-level page navigation

* chore: make e2e more stable
This commit is contained in:
Zeke Zhang 2025-02-24 21:12:44 +08:00 committed by GitHub
parent 3febd8b5ee
commit 235fd81e6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 151 additions and 221 deletions

View File

@ -34,6 +34,7 @@ test.describe('where to open a popup and what can be added to it', () => {
// add blocks // add blocks
await page.getByLabel('schema-initializer-Grid-popup:addNew:addBlock-general').hover(); await page.getByLabel('schema-initializer-Grid-popup:addNew:addBlock-general').hover();
await page.getByText('Markdown').click(); await page.getByText('Markdown').click();
await page.waitForTimeout(500);
await page.getByLabel('schema-initializer-Grid-popup:addNew:addBlock-general').hover(); await page.getByLabel('schema-initializer-Grid-popup:addNew:addBlock-general').hover();
await page.getByText('Form').hover(); await page.getByText('Form').hover();
await page.getByRole('menuitem', { name: 'Current collection' }).click(); await page.getByRole('menuitem', { name: 'Current collection' }).click();

View File

@ -18,24 +18,12 @@ import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'
import { useAssociationFieldContext, useInsertSchema } from './hooks'; import { useAssociationFieldContext, useInsertSchema } from './hooks';
import schema from './schema'; import schema from './schema';
const InternalNesterCss = css`
& .ant-formily-item-layout-vertical {
margin-bottom: 10px;
}
.ant-card-body {
padding: 15px 20px 5px;
}
.ant-divider-horizontal {
margin: 10px 0;
}
`;
const InternalNesterCardCss = css` const InternalNesterCardCss = css`
.ant-card-bordered { .ant-card-bordered {
border: none; border: none;
} }
.ant-card-body { .ant-card-body {
padding: 0px 20px 20px 0px; padding: 0px 20px 0px 0px;
} }
`; `;
@ -55,6 +43,20 @@ export const InternalNester = observer(
labelWrap = true, labelWrap = true,
} = fieldSchema?.['x-component-props'] || {}; } = fieldSchema?.['x-component-props'] || {};
const InternalNesterCss = css`
margin-top: 0.4em;
& .ant-formily-item-layout-vertical {
margin-bottom: 10px;
}
.ant-card-body {
padding: ${token.padding}px ${token.paddingLG}px;
}
.ant-divider-horizontal {
margin: 10px 0;
}
`;
useEffect(() => { useEffect(() => {
insertNester(schema.Nester); insertNester(schema.Nester);
}, []); }, []);

View File

@ -11,8 +11,19 @@ import { genStyleHook } from '../__builtins__';
const useStyles = genStyleHook('nb-grid-card', (token) => { const useStyles = genStyleHook('nb-grid-card', (token) => {
const { componentCls } = token; const { componentCls } = token;
return { return {
[componentCls]: { [componentCls]: {
'.nb-action-bar': {
borderRadius: token.borderRadiusBlock,
marginBottom: `${token.marginBlock / 2}px !important`,
},
'.ant-list-pagination': {
borderRadius: token.borderRadiusBlock,
marginTop: `${token.marginBlock / 2}px !important`,
},
'& > .nb-block-item': { '& > .nb-block-item': {
marginBottom: token.marginLG, marginBottom: token.marginLG,
'& > .nb-action-bar:has(:first-child:not(:empty))': { '& > .nb-action-bar:has(:first-child:not(:empty))': {

View File

@ -28,15 +28,10 @@ const itemCss = css`
const gridCardCss = css` const gridCardCss = css`
height: 100%; height: 100%;
> .ant-card-body { > .ant-card-body {
padding: 24px 24px 0px;
height: 100%; height: 100%;
button {
margin-bottom: 0px !important;
margin-top: 5px;
}
} }
.nb-action-bar { .nb-action-bar {
padding: 5px 0; padding-top: 5px;
} }
`; `;

View File

@ -17,6 +17,7 @@ import { getCardItemSchema } from '../../../block-provider';
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps'; import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent'; import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent';
import { useToken } from '../../../style/useToken';
import { SortableItem } from '../../common'; import { SortableItem } from '../../common';
import { SchemaComponentOptions } from '../../core'; import { SchemaComponentOptions } from '../../core';
import { useDesigner, useProps } from '../../hooks'; import { useDesigner, useProps } from '../../hooks';
@ -141,6 +142,7 @@ const InternalGridCard = withSkeletonComponent(
}, },
[fieldSchema.properties], [fieldSchema.properties],
); );
const { token } = useToken();
const onPaginationChange: PaginationProps['onChange'] = useCallback( const onPaginationChange: PaginationProps['onChange'] = useCallback(
(page, pageSize) => { (page, pageSize) => {
@ -207,7 +209,7 @@ const InternalGridCard = withSkeletonComponent(
...columnCount, ...columnCount,
sm: columnCount.xs, sm: columnCount.xs,
xl: columnCount.lg, xl: columnCount.lg,
gutter: [rowGutter, rowGutter], gutter: [token.marginBlock / 2, token.marginBlock / 2],
}} }}
renderItem={(item, index) => { renderItem={(item, index) => {
return ( return (

View File

@ -18,13 +18,20 @@ import { WorkbenchLayout } from './workbenchBlockSettings';
const useStyles = createStyles(({ token, css }) => ({ const useStyles = createStyles(({ token, css }) => ({
// 支持 css object 的写法 // 支持 css object 的写法
action: css` action: css`
display: flex;
background-color: transparent; background-color: transparent;
border: 0; border: 0;
height: auto; height: auto;
box-shadow: none; box-shadow: none;
padding-top: 8px;
`,
avatar: css`
width: 4em;
`, `,
title: css` title: css`
margin-top: ${token.marginSM}px; margin-top: ${token.marginSM}px;
width: 100%;
white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
`, `,
@ -37,8 +44,8 @@ function Button() {
const { layout } = useContext(WorkbenchBlockContext); const { layout } = useContext(WorkbenchBlockContext);
const { styles, cx } = useStyles(); const { styles, cx } = useStyles();
return layout === WorkbenchLayout.Grid ? ( return layout === WorkbenchLayout.Grid ? (
<div title={fieldSchema.title} style={{ width: '100%', overflow: 'hidden' }} className="nb-action-panel-container"> <div title={fieldSchema.title} className={cx(styles.avatar)}>
<Avatar style={{ backgroundColor }} size={54} icon={<Icon type={icon} />} /> <Avatar style={{ backgroundColor }} size={48} icon={<Icon type={icon} />} />
<div className={cx(styles.title)}>{fieldSchema.title}</div> <div className={cx(styles.title)}>{fieldSchema.title}</div>
</div> </div>
) : ( ) : (
@ -55,6 +62,7 @@ export const WorkbenchAction = withDynamicSchemaProps((props) => {
<Component <Component
className={cx(className, styles.action, 'nb-action-panel')} className={cx(className, styles.action, 'nb-action-panel')}
{...others} {...others}
type="text"
icon={null} icon={null}
title={<Button />} title={<Button />}
confirmTitle={fieldSchema.title} confirmTitle={fieldSchema.title}

View File

@ -7,23 +7,22 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
import { css } from '@emotion/css';
import { observer, useFieldSchema } from '@formily/react'; import { observer, useFieldSchema } from '@formily/react';
import { import {
CollectionContext, CollectionContext,
createStyles,
DataSourceContext, DataSourceContext,
DndContext, DndContext,
Icon, Icon,
NocoBaseRecursionField, NocoBaseRecursionField,
useBlockHeight,
useDesignable, useDesignable,
useOpenModeContext,
useSchemaInitializerRender, useSchemaInitializerRender,
withDynamicSchemaProps, withDynamicSchemaProps,
useOpenModeContext,
useBlockHeightProps,
} from '@nocobase/client'; } from '@nocobase/client';
import { Space, List, Avatar, theme } from 'antd'; import { Avatar, Space } from 'antd';
import React, { createContext, useEffect, useState, useRef, useMemo, useLayoutEffect } from 'react'; import { Grid, List } from 'antd-mobile';
import React, { createContext } from 'react';
import { WorkbenchLayout } from './workbenchBlockSettings'; import { WorkbenchLayout } from './workbenchBlockSettings';
const ConfigureActionsButton = observer( const ConfigureActionsButton = observer(
@ -45,105 +44,27 @@ const ResponsiveSpace = () => {
const isMobileMedia = isMobile(); const isMobileMedia = isMobile();
const { isMobile: underMobileCtx } = useOpenModeContext() || {}; const { isMobile: underMobileCtx } = useOpenModeContext() || {};
const { itemsPerRow = 4 } = fieldSchema.parent['x-decorator-props'] || {}; const { itemsPerRow = 4 } = fieldSchema.parent['x-decorator-props'] || {};
const isUnderMobile = isMobileMedia || underMobileCtx;
const containerRef = useRef(null); // 引用容器 if (underMobileCtx || isMobileMedia) {
const [containerWidth, setContainerWidth] = useState(0); // 容器宽度 return (
// 使用 ResizeObserver 动态获取容器宽度 <Grid columns={itemsPerRow} gap={gap}>
useEffect(() => { {fieldSchema.mapProperties((s, key) => {
const handleResize = () => { return (
if (containerRef.current) { <Grid.Item style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }} key={key}>
setContainerWidth(containerRef.current.offsetWidth); // 更新宽度 <NocoBaseRecursionField name={key} schema={s} />
</Grid.Item>
);
})}
</Grid>
);
} }
};
// 初始化 ResizeObserver
const resizeObserver = new ResizeObserver(handleResize);
// 监听容器宽度变化
if (containerRef.current) {
resizeObserver.observe(containerRef.current);
handleResize(); // 初始化时获取一次宽度
}
return () => {
if (containerRef.current) {
resizeObserver.unobserve(containerRef.current);
}
};
}, []);
useLayoutEffect(() => {
if (!containerRef.current) return;
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
setContainerWidth(entry.contentRect.width); // 更新宽度
}
});
observer.observe(containerRef.current);
return () => {
observer.unobserve(containerRef.current);
};
}, []);
// 计算每个元素的宽度
const itemWidth = useMemo(() => {
if (isUnderMobile) {
const totalGapWidth = gap * itemsPerRow;
const availableWidth = containerWidth - totalGapWidth;
return availableWidth / itemsPerRow;
}
return 70;
}, [itemsPerRow, gap, containerWidth]);
// 计算 Avatar 的宽度
const avatarSize = useMemo(() => {
return isUnderMobile ? (Math.floor(itemWidth * 0.8) > 70 ? 60 : Math.floor(itemWidth * 0.8)) : 54; // Avatar 大小为 item 宽度的 60%
}, [itemWidth, itemsPerRow, containerWidth]);
return ( return (
<div ref={containerRef} style={{ width: '100%' }}> <Space wrap size={gap} align="start">
<Space {fieldSchema.mapProperties((s, key) => {
wrap return <NocoBaseRecursionField name={key} schema={s} />;
style={{ width: '100%', display: 'flex' }} })}
size={gap}
align="start"
className={css`
.ant-space-item {
width: ${isUnderMobile ? itemWidth + 'px' : '100%'}
display: flex;
.ant-nb-action {
padding: ${isUnderMobile ? '4px 0px' : null};
}
.nb-action-panel-container {
width: ${itemWidth}px !important;
}
.ant-avatar-circle {
width: ${avatarSize}px !important;
height: ${avatarSize}px !important;
line-height: ${avatarSize}px !important;
}
}
`}
>
{fieldSchema.mapProperties((s, key) => (
<div
key={key}
style={
isUnderMobile && {
flexBasis: `${itemWidth}px`,
flexShrink: 0,
flexGrow: 0,
display: 'flex',
}
}
>
<NocoBaseRecursionField name={key} schema={s} key={key} />
</div>
))}
</Space> </Space>
</div>
); );
}; };
@ -157,36 +78,17 @@ const InternalIcons = () => {
{layout === WorkbenchLayout.Grid ? ( {layout === WorkbenchLayout.Grid ? (
<ResponsiveSpace /> <ResponsiveSpace />
) : ( ) : (
<List itemLayout="horizontal"> <List>
{fieldSchema.mapProperties((s, key) => { {fieldSchema.mapProperties((s, key) => {
const icon = s['x-component-props']?.['icon']; const icon = s['x-component-props']?.['icon'];
const backgroundColor = s['x-component-props']?.['iconColor']; const backgroundColor = s['x-component-props']?.['iconColor'];
return ( return (
<List.Item <List.Item
key={key} key={key}
className={css` prefix={<Avatar style={{ backgroundColor }} icon={<Icon type={icon} />} />}
.ant-list-item-meta-avatar { onClick={() => {}}
margin-inline-end: 0px !important;
}
.ant-list-item-meta-title {
overflow: hidden;
text-overflow: ellipsis;
font-size: 14px;
margin: 0 0 0 0;
}
.ant-list-item-meta-title button {
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
text-align: left;
}
`}
> >
<List.Item.Meta <NocoBaseRecursionField name={key} schema={s} />
avatar={<Avatar style={{ backgroundColor }} icon={<Icon type={icon} />} />}
title={<NocoBaseRecursionField name={key} schema={s} key={key} />}
></List.Item.Meta>
</List.Item> </List.Item>
); );
})} })}
@ -199,45 +101,56 @@ const InternalIcons = () => {
export const WorkbenchBlockContext = createContext({ layout: 'grid' }); export const WorkbenchBlockContext = createContext({ layout: 'grid' });
const useStyles = createStyles(({ token, css }) => ({
containerClass: css`
&.list {
margin: -${token.paddingLG}px;
border-radius: ${(token as any).borderRadiusBlock}px;
overflow: hidden;
.adm-list {
--padding-left: ${token.paddingLG}px;
--padding-right: ${token.paddingLG}px;
.adm-list-item-content-main {
display: flex;
button {
background-color: transparent;
border: none;
height: auto;
box-shadow: none;
padding: 16px 32px;
margin: -12px -32px;
width: calc(100% + 64px);
text-align: start;
color: ${token.colorText};
}
}
}
button[aria-label*='schema-initializer-WorkbenchBlock.ActionBar-workbench:configureActions'] {
margin-bottom: ${token.paddingLG}px;
margin-left: ${token.paddingLG}px;
}
}
`,
}));
export const WorkbenchBlock: any = withDynamicSchemaProps( export const WorkbenchBlock: any = withDynamicSchemaProps(
(props) => { (props) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { layout = 'grid' } = fieldSchema['x-component-props'] || {}; const { layout = 'grid' } = fieldSchema['x-component-props'] || {};
const { title } = fieldSchema['x-decorator-props'] || {}; const { styles } = useStyles();
const targetHeight = useBlockHeight();
const { token } = theme.useToken();
const { designable } = useDesignable();
const titleHeight = title ? token.fontSizeLG * token.lineHeightLG + token.padding * 2 - 1 : 0;
const internalHeight = 2 * token.paddingLG + token.controlHeight + token.marginLG + titleHeight;
const warperHeight =
targetHeight - (designable ? internalHeight : 2 * token.paddingLG + token.marginLG + titleHeight);
const targetWarperHeight = warperHeight > 0 ? warperHeight + 'px' : '100%';
return ( return (
<div <div className={`nb-action-penal-container ${layout} ${styles.containerClass}`}>
className="nb-action-penal-container"
style={{ height: targetHeight ? targetHeight - 2 * token.paddingLG - gap - titleHeight : '100%' }}
>
<div
className={css`
.nb-action-panel-warp {
height: ${targetHeight ? targetWarperHeight : '100%'};
overflow-y: auto;
margin-left: -24px;
margin-right: -24px;
padding-left: 24px;
padding-right: 24px;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
`}
>
<WorkbenchBlockContext.Provider value={{ layout }}> <WorkbenchBlockContext.Provider value={{ layout }}>
<DataSourceContext.Provider value={undefined}> <DataSourceContext.Provider value={undefined}>
<CollectionContext.Provider value={undefined}>{props.children}</CollectionContext.Provider> <CollectionContext.Provider value={undefined}>{props.children}</CollectionContext.Provider>
</DataSourceContext.Provider> </DataSourceContext.Provider>
</WorkbenchBlockContext.Provider> </WorkbenchBlockContext.Provider>
</div> </div>
</div>
); );
}, },
{ displayName: 'WorkbenchBlock' }, { displayName: 'WorkbenchBlock' },

View File

@ -56,14 +56,27 @@ test.describe('zIndex', () => {
await page.getByLabel('Close lightbox').click(); await page.getByLabel('Close lightbox').click();
// 3. 进入第二层子页面,然后点击图片预览, 图片不能被子页面盖住 // 3. 进入第二层子页面,然后点击图片预览, 图片不能被子页面盖住
await page.getByLabel('action-Action-Edit-update-').click(); await page.getByLabel('action-Action-Edit-update-').click({
position: {
x: 5,
y: 5,
},
});
await page.getByRole('link', { name: title }).nth(2).click(); await page.getByRole('link', { name: title }).nth(2).click();
await page.waitForTimeout(300); await page.waitForTimeout(300);
await check(2); await check(2);
await page.getByLabel('Close lightbox').click(); await page.getByLabel('Close lightbox').click();
// 4. 进入第三层子页面,然后点击图片预览, 图片不能被子页面盖住 // 4. 进入第三层子页面,然后点击图片预览, 图片不能被子页面盖住
await page.getByLabel('action-Action-Edit-update-').nth(2).click(); await page
.getByLabel('action-Action-Edit-update-')
.nth(2)
.click({
position: {
x: 5,
y: 5,
},
});
await page.getByRole('link', { name: title }).nth(3).click(); await page.getByRole('link', { name: title }).nth(3).click();
await page.waitForTimeout(300); await page.waitForTimeout(300);
await check(3); await check(3);

View File

@ -16,8 +16,10 @@ export const InternalSettings = () => {
const style = useMemo(() => { const style = useMemo(() => {
return { return {
marginBottom: token.marginBlock, marginBottom: token.marginBlock,
borderRadius: token.borderRadiusBlock,
overflow: 'hidden',
}; };
}, [token.marginBlock]); }, [token.borderRadiusBlock, token.marginBlock]);
return ( return (
<SortableItem className={cx('nb-mobile-setting')} style={style}> <SortableItem className={cx('nb-mobile-setting')} style={style}>

View File

@ -23,6 +23,7 @@ import {
import React from 'react'; import React from 'react';
import { isDesktop } from 'react-device-detect'; import { isDesktop } from 'react-device-detect';
import { theme } from 'antd';
import { ActionDrawerUsedInMobile, useToAdaptActionDrawerToMobile } from '../adaptor-of-desktop/ActionDrawer'; import { ActionDrawerUsedInMobile, useToAdaptActionDrawerToMobile } from '../adaptor-of-desktop/ActionDrawer';
import { useToAdaptFilterActionToMobile } from '../adaptor-of-desktop/FilterAction'; import { useToAdaptFilterActionToMobile } from '../adaptor-of-desktop/FilterAction';
import { InternalPopoverNesterUsedInMobile } from '../adaptor-of-desktop/InternalPopoverNester'; import { InternalPopoverNesterUsedInMobile } from '../adaptor-of-desktop/InternalPopoverNester';
@ -92,11 +93,13 @@ export const Mobile = () => {
<GlobalThemeProvider <GlobalThemeProvider
theme={{ theme={{
token: { token: {
marginBlock: 18, paddingPageHorizontal: 8,
borderRadiusBlock: 0, paddingPageVertical: 8,
boxShadowTertiary: 'none', marginBlock: 12,
borderRadiusBlock: 8,
fontSize: 14, fontSize: 14,
}, },
algorithm: theme.compactAlgorithm,
}} }}
> >
<AntdAppProvider className={`mobile-container ${componentCls} ${hashId}`}> <AntdAppProvider className={`mobile-container ${componentCls} ${hashId}`}>

View File

@ -8,43 +8,30 @@
*/ */
import { genStyleHook } from '@nocobase/client'; import { genStyleHook } from '@nocobase/client';
import { PageBackgroundColor } from '../constants';
export const useStyles = genStyleHook('nb-mobile', (token) => { export const useStyles = genStyleHook('nb-mobile', (token) => {
const { componentCls } = token; const { componentCls } = token;
return { return {
[componentCls]: { [componentCls]: {
// 调整移动端 Grid card 区块顶部按钮和卡片之间的间距
'--nb-spacing': '12px',
WebkitOverflowScrolling: 'touch', WebkitOverflowScrolling: 'touch',
display: 'initial', display: 'initial',
'& ::-webkit-scrollbar': { '& ::-webkit-scrollbar': {
display: 'none', display: 'none',
}, },
body: {
backgroundColor: PageBackgroundColor,
},
'.nb-details .ant-formily-item-feedback-layout-loose': { '.nb-details .ant-formily-item-feedback-layout-loose': {
marginBottom: '5px', marginBottom: '5px',
}, },
'.nb-details .ant-formily-item-layout-vertical .ant-formily-item-label': { '.nb-details .ant-formily-item-layout-vertical .ant-formily-item-label': {
marginBottom: '-8px', marginBottom: '-8px',
}, },
'.ant-card .ant-card-body': {
paddingBottom: '10px',
paddingTop: '10px',
},
'.ant-pagination-simple': { '.ant-pagination-simple': {
marginTop: '0px !important', marginTop: '0px !important',
}, },
'.nb-action-penal-container': {
marginTop: '-10px',
marginBottom: '-10px',
},
'.nb-action-penal-container button[aria-label*="schema-initializer-WorkbenchBlock.ActionBar-workbench:configureActions"]':
{
marginBottom: '10px',
},
'.ant-list-item': { '.ant-list-item': {
paddingTop: '8px', paddingTop: '8px',
paddingBottom: '8px', paddingBottom: '8px',
@ -58,9 +45,6 @@ export const useStyles = genStyleHook('nb-mobile', (token) => {
paddingBottom: '0px', paddingBottom: '0px',
paddingTop: '0px', paddingTop: '0px',
}, },
'.nb-chart-block .noco-card-item': {
marginBottom: '-13px',
},
'.ant-table-thead button[aria-label*="schema-initializer-TableV2-table:configureColumns"] > span:last-child': { '.ant-table-thead button[aria-label*="schema-initializer-TableV2-table:configureColumns"] > span:last-child': {
display: 'none !important', display: 'none !important',
}, },
@ -87,22 +71,6 @@ export const useStyles = genStyleHook('nb-mobile', (token) => {
'.ant-pagination .ant-pagination-item': { '.ant-pagination .ant-pagination-item': {
display: 'none', display: 'none',
}, },
'.ant-card-body .nb-action-bar .ant-btn': {
justifyContent: 'space-between',
display: 'flex',
alignItems: 'center',
gap: '8px',
'& span': {
display: 'contents',
},
},
'.ant-card-body .nb-action-bar .ant-btn-icon': {
marginInlineEnd: '0px !important',
},
'.ant-card-body .nb-table-container': {
marginRight: '-20px',
marginLeft: '-10px',
},
'.nb-action-bar button[aria-label*="schema-initializer-ActionBar-table:configureActions"] > span:last-child': { '.nb-action-bar button[aria-label*="schema-initializer-ActionBar-table:configureActions"] > span:last-child': {
display: 'none !important', display: 'none !important',
}, },
@ -121,6 +89,11 @@ export const useStyles = genStyleHook('nb-mobile', (token) => {
'[data-menu-id$="-theme"]': { '[data-menu-id$="-theme"]': {
display: 'none', display: 'none',
}, },
'& > .ant-menu > .ant-menu-item': {
marginInline: 8,
width: `calc(100% - 16px)`,
},
}, },
}, },
}; };

View File

@ -7,12 +7,16 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
import { useToken } from '@nocobase/client';
import _ from 'lodash'; import _ from 'lodash';
import React, { FC, useEffect } from 'react'; import React, { FC, useEffect } from 'react';
import { PageBackgroundColor } from '../../../constants';
export const MobilePageContentContainer: FC<{ hideTabBar?: boolean }> = ({ children, hideTabBar }) => { export const MobilePageContentContainer: FC<{ hideTabBar?: boolean }> = ({ children, hideTabBar }) => {
const [mobileTabBarHeight, setMobileTabBarHeight] = React.useState(0); const [mobileTabBarHeight, setMobileTabBarHeight] = React.useState(0);
const [mobilePageHeader, setMobilePageHeader] = React.useState(0); const [mobilePageHeader, setMobilePageHeader] = React.useState(0);
const { token } = useToken();
useEffect(() => { useEffect(() => {
const navigationBar = _.last(document.querySelectorAll<HTMLDivElement>('.mobile-page-header')); const navigationBar = _.last(document.querySelectorAll<HTMLDivElement>('.mobile-page-header'));
setMobilePageHeader(navigationBar?.offsetHeight); setMobilePageHeader(navigationBar?.offsetHeight);
@ -34,6 +38,9 @@ export const MobilePageContentContainer: FC<{ hideTabBar?: boolean }> = ({ child
boxSizing: 'border-box', boxSizing: 'border-box',
maxWidth: '100%', maxWidth: '100%',
overflowX: 'hidden', overflowX: 'hidden',
backgroundColor: PageBackgroundColor,
paddingInline: token.paddingPageHorizontal,
paddingBlock: token.paddingPageVertical,
}} }}
> >
{children} {children}