mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 03:56:16 +00:00
feat: support to add Settings block in mobile (#5025)
* feat: support to add Settings block in mobile * style(Popup): clear the margin-bottom of the last block * refactor: extract hideDivider * fix: fix form values
This commit is contained in:
parent
5f9f94d008
commit
4590c754ce
@ -24,8 +24,7 @@ export const useMobileActionDrawerStyle = createStyles(({ css, token }: any) =>
|
||||
|
||||
// to match the button named 'Add block'
|
||||
& + .nb-grid-container > .nb-grid > .nb-grid-warp > .ant-btn {
|
||||
// 18px is the token marginBlock value
|
||||
margin: 12px 12px calc(12px + 18px);
|
||||
margin: 12px;
|
||||
}
|
||||
`,
|
||||
|
||||
@ -48,6 +47,15 @@ export const useMobileActionDrawerStyle = createStyles(({ css, token }: any) =>
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
background-color: ${token.colorBgLayout};
|
||||
|
||||
// clear the margin-bottom of the last block
|
||||
& > .nb-grid-container > .nb-grid > .nb-grid-warp > .nb-grid-row:nth-last-child(2) .noco-card-item {
|
||||
margin-bottom: 0;
|
||||
|
||||
& > .ant-card {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
footer: css`
|
||||
@ -62,6 +70,10 @@ export const useMobileActionDrawerStyle = createStyles(({ css, token }: any) =>
|
||||
z-index: 1000;
|
||||
border-top: 1px solid ${token.colorSplit};
|
||||
background-color: ${token.colorBgLayout};
|
||||
|
||||
.ant-btn {
|
||||
margin-left: 8px;
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||
import { ISchema, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||
import { Action, SchemaComponent, useActionContext } from '@nocobase/client';
|
||||
import { ConfigProvider } from 'antd';
|
||||
import { Popup } from 'antd-mobile';
|
||||
@ -17,7 +17,7 @@ import { useMobileActionDrawerStyle } from './ActionDrawer.style';
|
||||
import { BasicZIndexProvider, MIN_Z_INDEX_INCREMENT, useBasicZIndex } from './BasicZIndexProvider';
|
||||
import { usePopupContainer } from './FilterAction';
|
||||
|
||||
export const ActionDrawerUsedInMobile = observer((props: { footerNodeName?: string }) => {
|
||||
export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?: string }) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField();
|
||||
const { visible, setVisible } = useActionContext();
|
||||
@ -25,6 +25,13 @@ export const ActionDrawerUsedInMobile = observer((props: { footerNodeName?: stri
|
||||
const { styles } = useMobileActionDrawerStyle();
|
||||
const { basicZIndex } = useBasicZIndex();
|
||||
|
||||
// this schema need to add padding in the content area of the popup
|
||||
const isSpecialSchema = isChangePasswordSchema(fieldSchema) || isEditProfileSchema(fieldSchema);
|
||||
|
||||
const footerNodeName = isSpecialSchema ? 'Action.Drawer.Footer' : props.footerNodeName;
|
||||
|
||||
const specialStyle = isSpecialSchema ? { backgroundColor: 'white' } : {};
|
||||
|
||||
const newZIndex = basicZIndex + MIN_Z_INDEX_INCREMENT;
|
||||
|
||||
const zIndexStyle = useMemo(() => {
|
||||
@ -34,7 +41,7 @@ export const ActionDrawerUsedInMobile = observer((props: { footerNodeName?: stri
|
||||
}, [newZIndex]);
|
||||
|
||||
const footerSchema = fieldSchema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === props.footerNodeName) {
|
||||
if (s['x-component'] === footerNodeName) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
@ -81,24 +88,32 @@ export const ActionDrawerUsedInMobile = observer((props: { footerNodeName?: stri
|
||||
<CloseOutline />
|
||||
</span>
|
||||
</div>
|
||||
<SchemaComponent
|
||||
schema={fieldSchema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] !== props.footerNodeName;
|
||||
}}
|
||||
/>
|
||||
{/* used to offset the margin-bottom of the last block */}
|
||||
{/* The number 1 is to prevent the scroll bar from appearing */}
|
||||
<div style={{ marginBottom: 1 - marginBlock }}></div>
|
||||
{isSpecialSchema ? (
|
||||
<div style={{ padding: 12, ...specialStyle }}>
|
||||
<SchemaComponent
|
||||
schema={fieldSchema}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] !== footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<SchemaComponent
|
||||
schema={fieldSchema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] !== footerNodeName;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{footerSchema ? (
|
||||
<div className={styles.footer}>
|
||||
<div className={styles.footer} style={isSpecialSchema ? specialStyle : null}>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={fieldSchema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === props.footerNodeName;
|
||||
return s['x-component'] === footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@ -125,3 +140,11 @@ export const useToAdaptActionDrawerToMobile = () => {
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
function isEditProfileSchema(schema: ISchema) {
|
||||
return schema.title === `{{t("Edit profile")}}`;
|
||||
}
|
||||
|
||||
function isChangePasswordSchema(schema: ISchema) {
|
||||
return schema.title === `{{t("Change password")}}`;
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ export const useToAdaptFilterActionToMobile = () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 之所以不直接使用 mobile-container 作为容器,是因为会影响到区块的拖拽功能。详见:https://nocobase.height.app/T-4959
|
||||
* 之所以不直接在 mobile-container 中设置 transform,是因为会影响到子页面区块的拖拽功能。详见:https://nocobase.height.app/T-4959
|
||||
* @param visible
|
||||
* @returns
|
||||
*/
|
||||
|
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { Schema } from '@nocobase/utils';
|
||||
|
||||
// 隐藏 Grid 组件的左右 divider,因为移动端不需要在一行中并列展示两个区块
|
||||
export function hideDivider(schema: Schema) {
|
||||
schema?.mapProperties((schema) => {
|
||||
if (schema['x-component'] === 'Grid') {
|
||||
schema['x-component-props'] = {
|
||||
...schema['x-component-props'],
|
||||
showDivider: false,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return schema;
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
||||
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||
import {
|
||||
css,
|
||||
DndContext,
|
||||
@ -27,6 +27,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { MobilePageHeader } from '../../pages/dynamic-page';
|
||||
import { MobilePageContentContainer } from '../../pages/dynamic-page/content/MobilePageContentContainer';
|
||||
import { useStyles } from '../../pages/dynamic-page/header/tabs';
|
||||
import { hideDivider } from '../hideDivider';
|
||||
import { useMobileTabsForMobileActionPageStyle } from './MobileTabsForMobileActionPage.style';
|
||||
|
||||
export const MobileTabsForMobileActionPage: any = observer(
|
||||
@ -162,17 +163,3 @@ MobileTabsForMobileActionPage.TabPane = observer(
|
||||
);
|
||||
|
||||
MobileTabsForMobileActionPage.Designer = TabsOfPC.Designer;
|
||||
|
||||
// 隐藏 Grid 组件的左右 divider,因为移动端不需要在一行中并列展示两个区块
|
||||
function hideDivider(tabPaneSchema: Schema) {
|
||||
tabPaneSchema?.mapProperties((schema) => {
|
||||
if (schema['x-component'] === 'Grid') {
|
||||
schema['x-component-props'] = {
|
||||
...schema['x-component-props'],
|
||||
showDivider: false,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return tabPaneSchema;
|
||||
}
|
||||
|
@ -45,6 +45,9 @@ import {
|
||||
|
||||
// 导出 JSBridge,会挂在到 window 上
|
||||
import './js-bridge';
|
||||
import { MobileSettings } from './mobile-blocks/settings-block/MobileSettings';
|
||||
import { MobileSettingsBlockInitializer } from './mobile-blocks/settings-block/MobileSettingsBlockInitializer';
|
||||
import { MobileSettingsBlockSchemaSettings } from './mobile-blocks/settings-block/schemaSettings';
|
||||
import { MobileCheckerProvider } from './providers';
|
||||
|
||||
export * from './desktop-mode';
|
||||
@ -135,6 +138,7 @@ export class PluginMobileClient extends Plugin {
|
||||
mobileTabBarLinkSettings,
|
||||
mobilePageTabsSettings,
|
||||
mobileNavigationBarLinkSettings,
|
||||
MobileSettingsBlockSchemaSettings,
|
||||
);
|
||||
}
|
||||
|
||||
@ -150,6 +154,8 @@ export class PluginMobileClient extends Plugin {
|
||||
MobilePageTabs,
|
||||
MobileNavigationActionBar,
|
||||
MobileNotFoundPage,
|
||||
MobileSettingsBlockInitializer,
|
||||
MobileSettings,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { cx, SettingsMenu, SortableItem, useDesigner, useToken } from '@nocobase/client';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
export const InternalSettings = () => {
|
||||
const Designer = useDesigner();
|
||||
const { token } = useToken();
|
||||
const style = useMemo(() => {
|
||||
return {
|
||||
marginBottom: token.marginBlock,
|
||||
};
|
||||
}, [token.marginBlock]);
|
||||
|
||||
return (
|
||||
<SortableItem className={cx('nb-mobile-setting')} style={style}>
|
||||
<Designer />
|
||||
<SettingsMenu />
|
||||
</SortableItem>
|
||||
);
|
||||
};
|
||||
export const MobileSettings = InternalSettings;
|
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '@nocobase/client';
|
||||
import React from 'react';
|
||||
|
||||
export const MobileSettingsBlockInitializer = () => {
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
const { insert } = useSchemaInitializer();
|
||||
return (
|
||||
<SchemaInitializerItem
|
||||
icon={<SettingOutlined />}
|
||||
onClick={async () => {
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-component': 'MobileSettings',
|
||||
'x-settings': 'blockSettings:mobileSettings',
|
||||
'x-component-props': {},
|
||||
});
|
||||
}}
|
||||
{...itemConfig}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { SchemaSettings } from '@nocobase/client';
|
||||
import { usePluginTranslation } from '../../locale';
|
||||
|
||||
export const MobileSettingsBlockSchemaSettings = new SchemaSettings({
|
||||
name: 'blockSettings:mobileSettings',
|
||||
items: [
|
||||
{
|
||||
name: 'delete',
|
||||
type: 'remove',
|
||||
useComponentProps() {
|
||||
const { t } = usePluginTranslation();
|
||||
|
||||
return {
|
||||
removeParentsIfNoChildren: true,
|
||||
confirm: {
|
||||
title: t('Delete settings block'),
|
||||
},
|
||||
breakRemoveOn: {
|
||||
'x-component': 'Grid',
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import { SchemaInitializer, gridRowColWrap } from '@nocobase/client';
|
||||
import { generatePluginTranslationTemplate } from '../../../locale';
|
||||
|
||||
export const mobileAddBlockInitializer = new SchemaInitializer({
|
||||
title: '{{t("Add block")}}',
|
||||
@ -60,6 +61,11 @@ export const mobileAddBlockInitializer = new SchemaInitializer({
|
||||
title: '{{t("Markdown")}}',
|
||||
Component: 'MarkdownBlockInitializer',
|
||||
},
|
||||
{
|
||||
name: 'settings',
|
||||
title: generatePluginTranslationTemplate('Settings'),
|
||||
Component: 'MobileSettingsBlockInitializer',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -20,5 +20,6 @@
|
||||
"Title field is required": "Title field is required",
|
||||
"Icon field is required": "Icon field is required",
|
||||
"Desktop data blocks": "Desktop data blocks",
|
||||
"Other desktop blocks": "Other desktop blocks"
|
||||
"Other desktop blocks": "Other desktop blocks",
|
||||
"Settings": "Settings"
|
||||
}
|
||||
|
@ -21,5 +21,6 @@
|
||||
"Title field is required": "标题必填",
|
||||
"Icon field is required": "图标必填",
|
||||
"Desktop data blocks": "桌面端数据区块",
|
||||
"Other desktop blocks": "其他桌面端区块"
|
||||
"Other desktop blocks": "其他桌面端区块",
|
||||
"Settings": "设置"
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { SelectWithTitle, useAPIClient, useCurrentUserContext, useSystemSettings } from '@nocobase/client';
|
||||
import { SelectWithTitle, useCurrentUserContext, useSystemSettings } from '@nocobase/client';
|
||||
import { error } from '@nocobase/utils/client';
|
||||
import { MenuProps } from 'antd';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
|
Loading…
Reference in New Issue
Block a user