Merge branch 'next' into 730

This commit is contained in:
Zeke Zhang 2024-10-30 18:24:29 +08:00
commit e25b2f7d3b
31 changed files with 2372 additions and 302 deletions

View File

@ -1,7 +1,7 @@
name: Manual build pro image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.pr_number }}-${{ github.event.inputs.nocobase_pr_number }}
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.pr_number }}-${{ inputs.nocobase_pr_number }}
cancel-in-progress: true
run-name: Build pro image ${{ github.ref }}-${{ inputs.pr_number }}-${{ inputs.nocobase_pr_number }}

View File

@ -1,10 +1,10 @@
name: Build pro plugin docker image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.pro_plugin }}-${{ github.events.inputs.pr_number }}-${{ github.events.inputs.nocobase_pr_number }}
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.pro_plugin }}-${{ inputs.pr_number }}-${{ inputs.nocobase_pr_number }}
cancel-in-progress: true
run-name: Build pro plugin image ${{ github.ref }}-${{ github.event.inputs.pro_plugin }}-${{ github.events.inputs.pr_number }}-${{ github.events.inputs.nocobase_pr_number }}
run-name: Build pro plugin image ${{ github.ref }}-${{ inputs.pro_plugin }}-${{ inputs.pr_number }}-${{ inputs.nocobase_pr_number }}
on:
workflow_dispatch:

View File

@ -32,8 +32,8 @@
"@nocobase/utils": "1.4.0-alpha",
"ahooks": "^3.7.2",
"antd": "5.12.8",
"antd-style": "3.4.5",
"axios": "^1.7.0",
"antd-style": "3.7.1",
"bignumber.js": "^9.1.2",
"classnames": "^2.3.1",
"cronstrue": "^2.11.0",

View File

@ -47,6 +47,9 @@ export class NumberFieldInterface extends CollectionFieldInterface {
{ value: '0.001', label: '1.000' },
{ value: '0.0001', label: '1.0000' },
{ value: '0.00001', label: '1.00000' },
{ value: '0.000001', label: '1.000000' },
{ value: '0.0000001', label: '1.0000000' },
{ value: '0.00000001', label: '1.00000000' },
],
},
};

View File

@ -7,11 +7,18 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { useForm } from '@formily/react';
import { useCollectionRecordData } from '../../../../../data-source/collection-record/CollectionRecordProvider';
import { useSatisfiedActionValues } from '../../../../../schema-settings/LinkageRules/useActionValues';
import { useFormBlockContext } from '../../../../../block-provider';
import { useSubFormValue } from '../../../../../schema-component/antd/association-field/hooks';
export function useDataFormItemProps() {
const data = useCollectionRecordData();
const { valueMap: style } = useSatisfiedActionValues({ category: 'style', formValues: data });
const record = useCollectionRecordData();
const { form } = useFormBlockContext();
const subForm = useSubFormValue();
const { valueMap: style } = useSatisfiedActionValues({
category: 'style',
formValues: subForm?.formValue || form?.values || record,
form,
});
return { wrapperStyle: style };
}

View File

@ -936,7 +936,7 @@ test.describe('actions schema settings', () => {
).toBeVisible();
});
test('open mode', async ({ page, mockPage }) => {
test.skip('open mode', async ({ page, mockPage }) => {
const nocoPage = await mockPage(testingOfOpenModeForAddChild).waitForInit();
await nocoPage.goto();

View File

@ -8,6 +8,7 @@
*/
export * from './Action';
export * from './Action.Designer';
export * from './ActionBar';
export * from './context';
export * from './hooks';
@ -15,5 +16,5 @@ export * from './hooks/useGetAriaLabelOfAction';
export * from './hooks/useGetAriaLabelOfDrawer';
export * from './hooks/useGetAriaLabelOfModal';
export * from './hooks/useGetAriaLabelOfPopover';
export * from './Action.Designer';
export * from './types';
export * from './zIndexContext';

View File

@ -45,7 +45,7 @@ describe('CollectionSelect', () => {
expect(container).toMatchInlineSnapshot(`
<div>
<div
class="css-dev-only-do-not-override-wwtqkl ant-app"
class="css-dev-only-do-not-override-11aiz3o ant-app"
style="height: 100%;"
>
<div
@ -54,7 +54,7 @@ describe('CollectionSelect', () => {
role="button"
>
<div
class="css-1yh5po ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-wwtqkl"
class="css-1yh5po ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-11aiz3o"
>
<div
class="ant-formily-item-label"
@ -84,7 +84,7 @@ describe('CollectionSelect', () => {
class="ant-formily-item-control-content-component"
>
<div
class="ant-select css-dev-only-do-not-override-wwtqkl ant-select-focused ant-select-single ant-select-show-arrow ant-select-show-search"
class="ant-select css-dev-only-do-not-override-11aiz3o ant-select-focused ant-select-single ant-select-show-arrow ant-select-show-search"
data-testid="select-collection"
role="button"
>
@ -182,7 +182,7 @@ describe('CollectionSelect', () => {
expect(container).toMatchInlineSnapshot(`
<div>
<div
class="css-dev-only-do-not-override-wwtqkl ant-app"
class="css-dev-only-do-not-override-11aiz3o ant-app"
style="height: 100%;"
>
<div
@ -191,7 +191,7 @@ describe('CollectionSelect', () => {
role="button"
>
<div
class="css-1yh5po ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-wwtqkl"
class="css-1yh5po ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-11aiz3o"
>
<div
class="ant-formily-item-label"
@ -222,7 +222,7 @@ describe('CollectionSelect', () => {
>
<div>
<span
class="ant-tag css-dev-only-do-not-override-wwtqkl"
class="ant-tag css-dev-only-do-not-override-11aiz3o"
>
Users
</span>

View File

@ -26,7 +26,7 @@ describe('ColorPicker', () => {
expect(container).toMatchInlineSnapshot(`
<div>
<div
class="css-dev-only-do-not-override-wwtqkl ant-app"
class="css-dev-only-do-not-override-11aiz3o ant-app"
style="height: 100%;"
>
<div
@ -35,7 +35,7 @@ describe('ColorPicker', () => {
style="display: inline-block;"
>
<div
class="ant-color-picker-trigger css-dev-only-do-not-override-wwtqkl"
class="ant-color-picker-trigger css-dev-only-do-not-override-11aiz3o"
>
<div
class="ant-color-picker-color-block"
@ -90,7 +90,7 @@ describe('ColorPicker', () => {
expect(container).toMatchInlineSnapshot(`
<div>
<div
class="css-dev-only-do-not-override-wwtqkl ant-app"
class="css-dev-only-do-not-override-11aiz3o ant-app"
style="height: 100%;"
>
<div
@ -99,7 +99,7 @@ describe('ColorPicker', () => {
role="button"
>
<div
class="ant-color-picker-trigger ant-color-picker-sm css-dev-only-do-not-override-wwtqkl ant-color-picker-trigger-disabled"
class="ant-color-picker-trigger ant-color-picker-sm css-dev-only-do-not-override-11aiz3o ant-color-picker-trigger-disabled"
>
<div
class="ant-color-picker-color-block"

View File

@ -21,12 +21,12 @@ describe('Pagination', () => {
expect(container).toMatchInlineSnapshot(`
<div>
<div
class="css-dev-only-do-not-override-wwtqkl ant-app"
class="css-dev-only-do-not-override-11aiz3o ant-app"
style="height: 100%;"
>
<div>
<ul
class="ant-pagination css-dev-only-do-not-override-wwtqkl"
class="ant-pagination css-dev-only-do-not-override-11aiz3o"
>
<li
aria-disabled="true"
@ -131,7 +131,7 @@ describe('Pagination', () => {
expect(container).toMatchInlineSnapshot(`
<div>
<div
class="css-dev-only-do-not-override-wwtqkl ant-app"
class="css-dev-only-do-not-override-11aiz3o ant-app"
style="height: 100%;"
/>
</div>

View File

@ -229,8 +229,8 @@ const SortableRow = (props: {
const { ref, inView } = useInView({
threshold: 0,
triggerOnce: true,
initialInView: !!process.env.__E2E__ || isInSubTable || props.rowIndex < INITIAL_ROWS_NUMBER,
skip: !!process.env.__E2E__ || isInSubTable || props.rowIndex < INITIAL_ROWS_NUMBER,
initialInView: !!process.env.__E2E__ || isInSubTable || (props.rowIndex || 0) < INITIAL_ROWS_NUMBER,
skip: !!process.env.__E2E__ || isInSubTable,
});
const classObj = useMemo(() => {

View File

@ -20,11 +20,11 @@ describe('UnixTimestamp', () => {
expect(container).toMatchInlineSnapshot(`
<div>
<div
class="css-dev-only-do-not-override-wwtqkl ant-app"
class="css-dev-only-do-not-override-11aiz3o ant-app"
style="height: 100%;"
>
<div
class="ant-picker css-dev-only-do-not-override-wwtqkl"
class="ant-picker css-dev-only-do-not-override-11aiz3o"
>
<div
class="ant-picker-input"
@ -77,7 +77,7 @@ describe('UnixTimestamp', () => {
expect(container).toMatchInlineSnapshot(`
<div>
<div
class="css-dev-only-do-not-override-wwtqkl ant-app"
class="css-dev-only-do-not-override-11aiz3o ant-app"
style="height: 100%;"
>
<div

View File

@ -7,7 +7,9 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { useState, useEffect } from 'react';
import { useState, useEffect, useCallback } from 'react';
import { uid } from '@formily/shared';
import { Form, onFormValuesChange } from '@formily/core';
import { useVariables, useLocalVariables } from '../../variables';
import { useFieldSchema } from '@formily/react';
import { LinkageRuleCategory, LinkageRuleDataKeyMap } from './type';
@ -18,11 +20,13 @@ export function useSatisfiedActionValues({
category = 'default',
rules,
schema,
form,
}: {
category: `${LinkageRuleCategory}`;
formValues: Record<string, any>;
rules?: any;
schema?: any;
form?: Form;
}) {
const [valueMap, setValueMap] = useState({});
const fieldSchema = useFieldSchema();
@ -30,8 +34,7 @@ export function useSatisfiedActionValues({
const localVariables = useLocalVariables({ currentForm: { values: formValues } as any });
const localSchema = schema ?? fieldSchema;
const linkageRules = rules ?? localSchema[LinkageRuleDataKeyMap[category]];
useEffect(() => {
const compute = useCallback(() => {
if (linkageRules && formValues) {
getSatisfiedValueMap({ rules: linkageRules, variables, localVariables })
.then((valueMap) => {
@ -43,6 +46,22 @@ export function useSatisfiedActionValues({
throw new Error(err.message);
});
}
}, [variables, localVariables, formValues, linkageRules]);
}, [variables, localVariables, linkageRules, formValues]);
useEffect(() => {
compute();
}, [compute]);
useEffect(() => {
if (form) {
const id = uid();
form.addEffects(id, () => {
onFormValuesChange(() => {
compute();
});
});
return () => {
form.removeEffects(id);
};
}
}, [form, compute]);
return { valueMap };
}

View File

@ -17,7 +17,7 @@
"@formily/react": "2.x",
"@formily/shared": "2.x",
"antd": "5.x",
"antd-style": "3.4.5",
"antd-style": "3.7.1",
"cron-parser": "4.4.0",
"dayjs": "^1.11.8",
"lodash": "^4.17.21",

View File

@ -31,7 +31,6 @@ class PluginCollectionTreeServer extends Plugin {
if (!condition(collection.options)) {
return;
}
const tk = collection.filterTargetKey as string;
const name = `${dataSource.name}_${collection.name}_path`;
const parentForeignKey = collection.treeParentField?.foreignKey || 'parentId';
@ -51,8 +50,9 @@ class PluginCollectionTreeServer extends Plugin {
//afterCreate
this.db.on(`${collection.name}.afterCreate`, async (model: Model, options) => {
const { transaction } = options;
const tk = collection.filterTargetKey as string;
let path = `/${model.get(tk)}`;
path = await this.getTreePath(model, path, collection, tk, name, transaction);
path = await this.getTreePath(model, path, collection, name, transaction);
const rootPk = path.split('/')[1];
await this.app.db.getRepository(name).create({
values: {
@ -66,16 +66,18 @@ class PluginCollectionTreeServer extends Plugin {
//afterUpdate
this.db.on(`${collection.name}.afterUpdate`, async (model: Model, options) => {
const tk = collection.filterTargetKey;
// only update parentId and filterTargetKey
if (!(model._changed.has(tk) || model._changed.has(parentForeignKey))) {
return;
}
const { transaction } = options;
await this.updateTreePath(model, collection, tk, name, transaction);
await this.updateTreePath(model, collection, name, transaction);
});
// after remove
this.db.on(`${collection.name}.afterBulkUpdate`, async (options) => {
const tk = collection.filterTargetKey as string;
if (!(options.where && options.where[tk])) {
return;
}
@ -86,12 +88,13 @@ class PluginCollectionTreeServer extends Plugin {
transaction: options.transaction,
});
for (const model of instances) {
await this.updateTreePath(model, collection, tk, name, options.transaction);
await this.updateTreePath(model, collection, name, options.transaction);
}
});
//afterDestroy
this.db.on(`${collection.name}.afterDestroy`, async (model: Model, options: DestroyOptions) => {
const tk = collection.filterTargetKey as string;
await this.app.db.getRepository(name).destroy({
filter: {
nodePk: model.get(tk),
@ -139,10 +142,10 @@ class PluginCollectionTreeServer extends Plugin {
model: Model,
path: string,
collection: Collection,
tk: string,
pathCollectionName: string,
transaction?: Transaction,
) {
const tk = collection.filterTargetKey as string;
const parentForeignKey = collection.treeParentField?.foreignKey || 'parentId';
if (model.get(parentForeignKey) && model.get(parentForeignKey) !== null) {
const parent = await this.app.db.getRepository(collection.name).findOne({
@ -164,7 +167,7 @@ class PluginCollectionTreeServer extends Plugin {
});
const parentPath = lodash.get(parentPathData, 'path', null);
if (parentPath == null) {
path = await this.getTreePath(parent, path, collection, tk, pathCollectionName, transaction);
path = await this.getTreePath(parent, path, collection, pathCollectionName, transaction);
} else {
path = `${parentPath}/${model.get(tk)}`;
}
@ -177,12 +180,12 @@ class PluginCollectionTreeServer extends Plugin {
private async updateTreePath(
model: Model,
collection: Collection,
tk: string,
pathCollectionName: string,
transaction: Transaction,
) {
const tk = collection.filterTargetKey as string;
let path = `/${model.get(tk)}`;
path = await this.getTreePath(model, path, collection, tk, pathCollectionName, transaction);
path = await this.getTreePath(model, path, collection, pathCollectionName, transaction);
const collectionTreePath = this.db.getCollection(pathCollectionName);
const nodePkColumnName = collectionTreePath.getField('nodePk').columnName();
const pathData = await this.app.db.getRepository(pathCollectionName).findOne({

View File

@ -10,7 +10,7 @@
"homepage": "https://docs.nocobase.com/handbook/block-gantt",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/block-gantt",
"devDependencies": {
"antd-style": "3.4.5"
"antd-style": "3.7.1"
},
"peerDependencies": {
"@nocobase/client": "1.x",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
/**
* 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 { expect, test } from '@nocobase/test/e2e';
import { shouldDisplayImageNormally } from './templates';
test.describe('zIndex', () => {
test('should display image normally', async ({ page, mockMobilePage, mockRecord }) => {
const nocoPage = await mockMobilePage(shouldDisplayImageNormally).waitForInit();
const record = await mockRecord('image');
await nocoPage.goto();
const title = record.attachment[0].title;
// 通过鼠标 hover 到 Add block 按钮来检查是否符合预期
const check = async (level: number) => {
try {
switch (level) {
case 0:
await page.getByLabel('schema-initializer-Grid-').hover({ timeout: 300 });
break;
case 1:
await page.getByLabel('schema-initializer-Grid-popup').hover({ timeout: 300 });
break;
case 2:
await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover({ timeout: 300 });
break;
case 3:
await page.getByLabel('schema-initializer-Grid-popup').nth(2).hover({ timeout: 300 });
break;
}
} catch (e) {
return;
}
await page.waitForTimeout(300);
await expect(page.getByText('Desktop data blocks')).not.toBeVisible();
};
// 1. 在主页面,点击图片预览,图片不能被主页面盖住
await page.getByRole('link', { name: title }).click();
await page.waitForTimeout(300);
await check(0);
await page.getByLabel('Close lightbox').click();
// 2. 进入第一层子页面,然后点击图片预览, 图片不能被子页面盖住
await page.getByLabel('action-Action.Link-View-view-').click();
await page.getByRole('link', { name: title }).nth(1).click();
await page.waitForTimeout(300);
await check(1);
await page.getByLabel('Close lightbox').click();
// 3. 进入第二层子页面,然后点击图片预览, 图片不能被子页面盖住
await page.getByLabel('action-Action-Edit-update-').click();
await page.getByRole('link', { name: title }).nth(2).click();
await page.waitForTimeout(300);
await check(2);
await page.getByLabel('Close lightbox').click();
// 4. 进入第三层子页面,然后点击图片预览, 图片不能被子页面盖住
await page.getByLabel('action-Action-Edit-update-').nth(2).click();
await page.getByRole('link', { name: title }).nth(3).click();
await page.waitForTimeout(300);
await check(3);
await page.getByLabel('Close lightbox').click();
});
});

View File

@ -8,14 +8,14 @@
*/
import { ISchema, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import { Action, SchemaComponent, useActionContext } from '@nocobase/client';
import { Action, SchemaComponent, useActionContext, useZIndexContext, zIndexContext } from '@nocobase/client';
import { ConfigProvider } from 'antd';
import { Popup } from 'antd-mobile';
import { CloseOutline } from 'antd-mobile-icons';
import React, { useCallback, useEffect, useMemo } from 'react';
import { useMobileActionDrawerStyle } from './ActionDrawer.style';
import { BasicZIndexProvider, MIN_Z_INDEX_INCREMENT, useBasicZIndex } from './BasicZIndexProvider';
import { usePopupContainer } from './FilterAction';
import { MIN_Z_INDEX_INCREMENT } from './zIndex';
export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?: string }) => {
const fieldSchema = useFieldSchema();
@ -23,7 +23,7 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
const { visible, setVisible } = useActionContext();
const { popupContainerRef, visiblePopup } = usePopupContainer(visible);
const { componentCls, hashId } = useMobileActionDrawerStyle();
const { basicZIndex } = useBasicZIndex();
const parentZIndex = useZIndexContext();
// this schema need to add padding in the content area of the popup
const isSpecialSchema = isChangePasswordSchema(fieldSchema) || isEditProfileSchema(fieldSchema);
@ -32,7 +32,7 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
const specialStyle = isSpecialSchema ? { backgroundColor: 'white' } : {};
const newZIndex = basicZIndex + MIN_Z_INDEX_INCREMENT;
const newZIndex = parentZIndex + MIN_Z_INDEX_INCREMENT;
const zIndexStyle = useMemo(() => {
return {
@ -66,7 +66,7 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
}, [newZIndex]);
return (
<BasicZIndexProvider basicZIndex={newZIndex}>
<zIndexContext.Provider value={newZIndex}>
<ConfigProvider theme={theme}>
<Popup
className={`${componentCls} ${hashId}`}
@ -121,7 +121,7 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
) : null}
</Popup>
</ConfigProvider>
</BasicZIndexProvider>
</zIndexContext.Provider>
);
});

View File

@ -1,33 +0,0 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import React, { useMemo } from 'react';
const BasicZIndexContext = React.createContext<{
basicZIndex: number;
}>({
basicZIndex: 0,
});
/**
* used to accumulate z-index in nested popups
* @param props
* @returns
*/
export const BasicZIndexProvider: React.FC<{ basicZIndex: number }> = (props) => {
const value = useMemo(() => ({ basicZIndex: props.basicZIndex }), [props.basicZIndex]);
return <BasicZIndexContext.Provider value={value}>{props.children}</BasicZIndexContext.Provider>;
};
export const useBasicZIndex = () => {
return React.useContext(BasicZIndexContext);
};
// minimum z-index increment
export const MIN_Z_INDEX_INCREMENT = 10;

View File

@ -7,14 +7,14 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Filter, withDynamicSchemaProps } from '@nocobase/client';
import { Filter, useZIndexContext, withDynamicSchemaProps } from '@nocobase/client';
import { ConfigProvider } from 'antd';
import { Popup } from 'antd-mobile';
import { CloseOutline } from 'antd-mobile-icons';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useMobileActionDrawerStyle } from './ActionDrawer.style';
import { MIN_Z_INDEX_INCREMENT, useBasicZIndex } from './BasicZIndexProvider';
import { MIN_Z_INDEX_INCREMENT } from './zIndex';
const OriginFilterAction = Filter.Action;
@ -24,11 +24,11 @@ export const FilterAction = withDynamicSchemaProps((props) => {
{...props}
Container={(props) => {
const { visiblePopup, popupContainerRef } = usePopupContainer(props.open);
const { basicZIndex } = useBasicZIndex();
const parentZIndex = useZIndexContext();
const { componentCls, hashId } = useMobileActionDrawerStyle();
const { t } = useTranslation();
const newZIndex = basicZIndex + MIN_Z_INDEX_INCREMENT;
const newZIndex = parentZIndex + MIN_Z_INDEX_INCREMENT;
// eslint-disable-next-line react-hooks/rules-of-hooks
const closePopup = useCallback(() => {
@ -117,9 +117,9 @@ export const usePopupContainer = (visible: boolean) => {
const [mobileContainer] = useState<HTMLElement>(() => document.querySelector('.mobile-container'));
const [visiblePopup, setVisiblePopup] = useState(false);
const popupContainerRef = React.useRef<HTMLDivElement>(null);
const { basicZIndex } = useBasicZIndex();
const parentZIndex = useZIndexContext();
const newZIndex = basicZIndex + MIN_Z_INDEX_INCREMENT;
const newZIndex = parentZIndex + MIN_Z_INDEX_INCREMENT;
useEffect(() => {
if (!visible) {

View File

@ -8,22 +8,23 @@
*/
import { useField } from '@formily/react';
import { useZIndexContext, zIndexContext } from '@nocobase/client';
import { ConfigProvider } from 'antd';
import { Popup } from 'antd-mobile';
import { CloseOutline } from 'antd-mobile-icons';
import React, { useCallback, useMemo } from 'react';
import { BasicZIndexProvider, MIN_Z_INDEX_INCREMENT, useBasicZIndex } from './BasicZIndexProvider';
import { usePopupContainer } from './FilterAction';
import { useInternalPopoverNesterUsedInMobileStyle } from './InternalPopoverNester.style';
import { MIN_Z_INDEX_INCREMENT } from './zIndex';
const Container = (props) => {
const { onOpenChange } = props;
const { visiblePopup, popupContainerRef } = usePopupContainer(props.open);
const { componentCls, hashId } = useInternalPopoverNesterUsedInMobileStyle();
const field = useField();
const { basicZIndex } = useBasicZIndex();
const parentZIndex = useZIndexContext();
const newZIndex = basicZIndex + MIN_Z_INDEX_INCREMENT;
const newZIndex = parentZIndex + MIN_Z_INDEX_INCREMENT;
const title = field.title || '';
const zIndexStyle = useMemo(() => {
@ -49,7 +50,7 @@ const Container = (props) => {
}, [newZIndex]);
return (
<BasicZIndexProvider basicZIndex={newZIndex}>
<zIndexContext.Provider value={newZIndex}>
<ConfigProvider theme={theme}>
<div onClick={openPopup}>{props.children}</div>
<Popup
@ -78,7 +79,7 @@ const Container = (props) => {
<div style={{ height: 50 }}></div>
</Popup>
</ConfigProvider>
</BasicZIndexProvider>
</zIndexContext.Provider>
);
};

View File

@ -14,10 +14,12 @@ import {
TabsContextProvider,
useActionContext,
useTabsContext,
useZIndexContext,
zIndexContext,
} from '@nocobase/client';
import React, { useMemo } from 'react';
import { createPortal } from 'react-dom';
import { BasicZIndexProvider, MIN_Z_INDEX_INCREMENT, useBasicZIndex } from '../BasicZIndexProvider';
import { MIN_Z_INDEX_INCREMENT } from '../zIndex';
import { useMobileActionPageStyle } from './MobileActionPage.style';
import { MobileTabsForMobileActionPage } from './MobileTabsForMobileActionPage';
@ -34,11 +36,11 @@ export const MobileActionPage = ({ level, footerNodeName }) => {
const { componentCls, hashId } = useMobileActionPageStyle();
const tabContext = useTabsContext();
const containerDOM = useMemo(() => document.querySelector('.nb-mobile-subpages-slot'), []);
const { basicZIndex } = useBasicZIndex();
const parentZIndex = useZIndexContext();
// in nested popups, basicZIndex is an accumulated value to ensure that
// the z-index of the current level is always higher than the previous level
const newZIndex = basicZIndex + MIN_Z_INDEX_INCREMENT + (level || 1);
const newZIndex = parentZIndex + MIN_Z_INDEX_INCREMENT + (level || 1);
const footerSchema = fieldSchema.reduceProperties((buf, s) => {
if (s['x-component'] === footerNodeName) {
@ -58,7 +60,7 @@ export const MobileActionPage = ({ level, footerNodeName }) => {
}
const actionPageNode = (
<BasicZIndexProvider basicZIndex={newZIndex}>
<zIndexContext.Provider value={newZIndex}>
<div className={`${componentCls} ${hashId}`} style={zIndexStyle}>
<TabsContextProvider {...tabContext} tabBarExtraContent={<BackButtonUsedInSubPage />} tabBarGutter={48}>
<SchemaComponent components={components} schema={fieldSchema} onlyRenderProperties />
@ -76,7 +78,7 @@ export const MobileActionPage = ({ level, footerNodeName }) => {
</div>
)}
</div>
</BasicZIndexProvider>
</zIndexContext.Provider>
);
if (containerDOM) {

View File

@ -0,0 +1,11 @@
/**
* 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.
*/
// minimum z-index increment
export const MIN_Z_INDEX_INCREMENT = 10;

View File

@ -18,12 +18,12 @@ import {
OpenModeProvider,
useAssociationFieldModeContext,
usePlugin,
zIndexContext,
} from '@nocobase/client';
import React from 'react';
import { isDesktop } from 'react-device-detect';
import { ActionDrawerUsedInMobile, useToAdaptActionDrawerToMobile } from '../adaptor-of-desktop/ActionDrawer';
import { BasicZIndexProvider } from '../adaptor-of-desktop/BasicZIndexProvider';
import { useToAdaptFilterActionToMobile } from '../adaptor-of-desktop/FilterAction';
import { InternalPopoverNesterUsedInMobile } from '../adaptor-of-desktop/InternalPopoverNester';
import { useToAddMobilePopupBlockInitializers } from '../adaptor-of-desktop/mobile-action-page/blockInitializers';
@ -105,9 +105,9 @@ export const Mobile = () => {
<ResetSchemaOptionsProvider>
<AssociationFieldModeProvider modeToComponent={modeToComponent}>
{/* the z-index of all popups and subpages will be based on this value */}
<BasicZIndexProvider basicZIndex={800}>
<zIndexContext.Provider value={100}>
<MobileRouter />
</BasicZIndexProvider>
</zIndexContext.Provider>
</AssociationFieldModeProvider>
</ResetSchemaOptionsProvider>
</MobileAppProvider>

View File

@ -59,7 +59,7 @@ export const ContentConfigForm = ({ variableOptions }) => {
useTypedConstant: ['string'],
},
description: tval(
"Support two types of links in nocobase: internal links and external links. If using an internal link, the link starts with '/', for example, '/admin/page'. If using an external link, the link starts with 'http', for example, 'https://example.com'.",
'Support two types of links: internal links and external links. If using an internal link, the link starts with"/", for example, "/admin". If using an external link, the link starts with "http", for example, "https://example.com".',
),
},
},

View File

@ -16,7 +16,7 @@
* For more information, please rwefer to: https://www.nocobase.com/agreement.
*/
import React, { useEffect, useCallback, useContext } from 'react';
import React, { useEffect, useCallback } from 'react';
import { Badge, Button, ConfigProvider, Drawer, Tooltip } from 'antd';
import { CloseOutlined } from '@ant-design/icons';
import { createStyles } from 'antd-style';
@ -76,9 +76,15 @@ const InnerInbox = (props) => {
}}
>
<Tooltip title={t('Message')}>
<Button className={styles.button} title={'Apps'} icon={<Icon type={'MailOutlined'} />} onClick={onIconClick} />
<Badge count={unreadMsgsCountObs.value} size="small" offset={[-12, 14]}>
<Button
className={styles.button}
title={'Apps'}
icon={<Icon type={'MailOutlined'} />}
onClick={onIconClick}
/>
</Badge>
</Tooltip>
{unreadMsgsCountObs.value && <Badge count={unreadMsgsCountObs.value} size="small" offset={[-18, -16]}></Badge>}
<Drawer
title={DrawerTitle}
open={inboxVisible.value}

View File

@ -108,7 +108,7 @@ export const MessageConfigForm = ({ variableOptions }) => {
useTypedConstant: ['string'],
},
description: tval(
"Support two types of links in nocobase: internal links and external links. If using an internal link, the link starts with '/', for example, '/admin/page'. If using an external link, the link starts with 'http', for example, 'https://example.com'.",
'Support two types of links: internal links and external links. If using an internal link, the link starts with"/", for example, "/admin". If using an external link, the link starts with "http", for example, "https://example.com".',
),
},
},

View File

@ -17,6 +17,6 @@
"Message content": "Message content",
"Inapp Message": "Inapp Message",
"Detail URL": "Detail URL",
"Support two types of links in nocobase: internal links and external links. If using an internal link, the link starts with '/', for example, '/admin/page'. If using an external link, the link starts with 'http', for example, 'https://example.com'.": "Support two types of links in nocobase: internal links and external links. If using an internal link, the link starts with '/', for example, '/admin/page'. If using an external link, the link starts with 'http', for example, 'https://example.com'.",
"Support two types of links: internal links and external links. If using an internal link, the link starts with\"/\", for example, \"/admin\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".": "Support two types of links: internal links and external links. If using an internal link, the link starts with \"/\", for example, \"/admin\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".",
"Mark as read": "Mark as read"
}

View File

@ -16,6 +16,6 @@
"Message content": "消息内容",
"Inapp Message": "站内信",
"Detail URL": "详情链接",
"Support two types of links in nocobase: internal links and external links. If using an internal link, the link starts with '/', for example, '/admin/page'. If using an external link, the link starts with 'http', for example, 'https://example.com'.": "nocobase支持两种链接类型内部链接和外部链接。如果使用内部链接链接以'/'开头,例如,'/admin/page'。如果使用外部链接,链接以'http'开头,例如,'https://example.com'。",
"Support two types of links: internal links and external links. If using an internal link, the link starts with\"/\", for example, \"/admin\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".": "支持两种链接类型:内部链接和外部链接。如果使用内部链接,链接以“/”开头,例如“/admin”。如果使用外部链接链接以“http”开头例如“https://example.com”。",
"Mark as read": "标记为已读"
}

1305
yarn.lock

File diff suppressed because it is too large Load Diff