mirror of
https://github.com/nocobase/nocobase
synced 2024-11-16 02:15:11 +00:00
Merge branch 'next' into 730
This commit is contained in:
commit
e25b2f7d3b
2
.github/workflows/manual-build-pro-image.yml
vendored
2
.github/workflows/manual-build-pro-image.yml
vendored
@ -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 }}
|
||||
|
@ -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:
|
||||
|
@ -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",
|
||||
|
@ -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' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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(() => {
|
||||
|
@ -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
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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({
|
||||
|
@ -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
@ -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();
|
||||
});
|
||||
});
|
@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -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;
|
@ -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) {
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
@ -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>
|
||||
|
@ -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".',
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -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}
|
||||
|
@ -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".',
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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": "标记为已读"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user