Merge branch 'main' into next

This commit is contained in:
Zeke Zhang 2025-05-07 15:07:20 +08:00
commit 14fea19cb1
3 changed files with 121 additions and 2 deletions

View File

@ -889,6 +889,7 @@
"After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.",
"No pages yet, please configure first": "No pages yet, please configure first",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode",
"Specifies a Permissions Policy for the <iframe>. The policy defines what features are available to the <iframe> (for example, access to the microphone, camera, battery, web-share, etc.) based on the origin of the request.": "Specifies a Permissions Policy for the <iframe>. The policy defines what features are available to the <iframe> (for example, access to the microphone, camera, battery, web-share, etc.) based on the origin of the request.",
"Deprecated": "Deprecated",
"Full permissions": "Full permissions",
"Refresh data blocks": "Refresh data blocks",

View File

@ -1104,6 +1104,16 @@
"Colon":"冒号",
"No pages yet, please configure first": "暂无页面,请先配置",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "点击右上角的“界面配置”图标,进入界面配置模式",
"Specifies a Permissions Policy for the <iframe>. The policy defines what features are available to the <iframe> (for example, access to the microphone, camera, battery, web-share, etc.) based on the origin of the request.": "用于为 <iframe> 指定其权限策略。该策略根据请求的来源规定 <iframe> 可以使用哪些特性例如访问麦克风、摄像头、电池、web 共享等)。",
"Controls whether the current document is allowed to autoplay media requested through the HTMLMediaElement interface. When this policy is disabled and there were no user gestures, the Promise returned by HTMLMediaElement.play() will reject with a NotAllowedError DOMException. The autoplay attribute on <audio> and <video> elements will be ignored.": "控制是否允许当前文档自动播放媒体。这种控制是通过接口 HTMLMediaElement 来实现。当这种规则被禁用而且没有用户操作的时候HTMLMediaElement.play() 返回的 Promise 会拒绝并抛出一个 DOMException 异常。<audio> 和 <video> 上的 autoplay 属性会被忽略。",
"Controls whether the current document is allowed to use video input devices. When this policy is disabled, the Promise returned by getUserMedia() will reject with a NotAllowedError DOMException.": "控制是否允许当前文档使用视频输入设备。当这种规则被禁用时MediaDevices.getUserMedia() 返回的 Promise 会拒绝并抛出 NotAllowedError DOMException 异常。",
"Controls whether the current document is allowed to set document.domain. When this policy is disabled, attempting to set document.domain will fail and cause a SecurityError DOMException to be thrown.": "控制是否允许当前文档设置 document.domain。当这种规则被禁用时尝试设置 document.domain 会失败并抛出 SecurityError DOMException 异常。",
"Controls whether the current document is allowed to use the Encrypted Media Extensions API (EME). When this policy is disabled, the Promise returned by Navigator.requestMediaKeySystemAccess() will reject with a SecurityError DOMException.": "控制是否允许当前文档使用 Encrypted Media Extension APIEME。当这种规则被禁用时Navigator.requestMediaKeySystemAccess() 返回的 Promise 会拒绝并抛出 DOMException 异常。",
"Controls whether the current document is allowed to use Element.requestFullscreen(). When this policy is disabled, the returned Promise rejects with a TypeError.": "控制是否允许当前文档使用 Element.requestFullScreen()。当这种规则被禁用时,返回的 Promise 会拒绝并抛出 TypeError。",
"Controls whether the current document is allowed to use the Geolocation Interface. When this policy is disabled, calls to getCurrentPosition() and watchPosition() will cause those functions callbacks to be invoked with a GeolocationPositionError code of PERMISSION_DENIED.": "控制是否允许当前文档使用 Geolocation 接口。当这种规则被禁用时,调用 getCurrentPosition() 和 watchPosition() 会返回包含 PERMISSION_DENIED 的 PositionError。",
"Controls whether the current document is allowed to use audio input devices. When this policy is disabled, the Promise returned by MediaDevices.getUserMedia() will reject with a NotAllowedError DOMException.": "控制是否允许当前文档使用音频输入设备。当这种规则被禁用时MediaDevices.getUserMedia() 返回的 Promise 会拒绝并抛出错误 NotAllowedError。",
"Controls whether the current document is allowed to use the Web MIDI API. When this policy is disabled, the Promise returned by Navigator.requestMIDIAccess() will reject with a SecurityError DOMException.": "控制是否允许当前文档使用 Web MIDI API。当这种规则被禁用时Navigator.requestMIDIAccess() 返回的 Promise 会拒绝并抛出错误 DOMException。",
"Controls whether the current document is allowed to use the Payment Request API. When this policy is enabled, the PaymentRequest() constructor will throw a SecurityError DOMException.": "控制是否允许当前文档使用 Payment Request API。当这种规则被启用时构造函数 PaymentRequest() 会抛出错误 SecurityError。",
"After successful submission, the selected data blocks will be automatically refreshed.": "提交成功后,会自动刷新这里选中的数据区块。",
"Block Linkage rules":"区块联动规则",
"Field Linkage rules":"字段联动规则"

View File

@ -7,6 +7,7 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { LinkOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import { ISchema, useField, useFieldSchema } from '@formily/react';
import { uid } from '@formily/shared';
import {
@ -22,6 +23,7 @@ import {
SchemaSettingsLinkageRules,
LinkageRuleCategory,
} from '@nocobase/client';
import { Select, Tooltip } from 'antd';
import React from 'react';
import { useTranslation } from 'react-i18next';
@ -41,6 +43,84 @@ const getVariableComponentWithScope = (Com) => {
};
};
const AllowOptionsHelp = ({ type }) => {
const { t, i18n } = useTranslation();
const typeToDescription = {
autoplay: t(
'Controls whether the current document is allowed to autoplay media requested through the HTMLMediaElement interface. When this policy is disabled and there were no user gestures, the Promise returned by HTMLMediaElement.play() will reject with a NotAllowedError DOMException. The autoplay attribute on <audio> and <video> elements will be ignored.',
),
camera: t(
'Controls whether the current document is allowed to use video input devices. When this policy is disabled, the Promise returned by getUserMedia() will reject with a NotAllowedError DOMException.',
),
'document-domain': t(
'Controls whether the current document is allowed to set document.domain. When this policy is disabled, attempting to set document.domain will fail and cause a SecurityError DOMException to be thrown.',
),
'encrypted-media': t(
'Controls whether the current document is allowed to use the Encrypted Media Extensions API (EME). When this policy is disabled, the Promise returned by Navigator.requestMediaKeySystemAccess() will reject with a SecurityError DOMException.',
),
fullscreen: t(
'Controls whether the current document is allowed to use Element.requestFullscreen(). When this policy is disabled, the returned Promise rejects with a TypeError.',
),
geolocation: t(
'Controls whether the current document is allowed to use the Geolocation Interface. When this policy is disabled, calls to getCurrentPosition() and watchPosition() will cause those functions callbacks to be invoked with a GeolocationPositionError code of PERMISSION_DENIED.',
),
microphone: t(
'Controls whether the current document is allowed to use audio input devices. When this policy is disabled, the Promise returned by MediaDevices.getUserMedia() will reject with a NotAllowedError DOMException.',
),
midi: t(
'Controls whether the current document is allowed to use the Web MIDI API. When this policy is disabled, the Promise returned by Navigator.requestMIDIAccess() will reject with a SecurityError DOMException.',
),
payment: t(
'Controls whether the current document is allowed to use the Payment Request API. When this policy is enabled, the PaymentRequest() constructor will throw a SecurityError DOMException.',
),
};
const description = typeToDescription[type];
return (
<span>
{type}{' '}
<Tooltip
zIndex={9999}
title={
<span>
{description}{' '}
<a
href={`https://developer.mozilla.org/${
i18n.language === 'zh-CN' ? 'zh-CN' : 'en-US'
}/docs/Web/HTTP/Reference/Headers/Permissions-Policy/${type}`}
target="_blank"
rel="noreferrer"
>
<LinkOutlined />
</a>
</span>
}
>
<QuestionCircleOutlined />
</Tooltip>
</span>
);
};
const AllowDescription = () => {
const { t, i18n } = useTranslation();
const helpURL =
i18n.language === 'zh-CN'
? 'https://developer.mozilla.org/zh-CN/docs/Web/HTML/Reference/Elements/iframe#allow'
: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe#allow';
return (
<span>
{t(
'Specifies a Permissions Policy for the <iframe>. The policy defines what features are available to the <iframe> (for example, access to the microphone, camera, battery, web-share, etc.) based on the origin of the request.',
)}{' '}
<a href={helpURL} target="_blank" rel="noreferrer">
<LinkOutlined />
</a>
</span>
);
};
const commonOptions: any = {
items: [
{
@ -52,7 +132,7 @@ const commonOptions: any = {
const { t, i18n } = useTranslation();
const { dn } = useDesignable();
const api = useAPIClient();
const { mode, url, params, htmlId, height = '60vh', engine } = fieldSchema['x-component-props'] || {};
const { mode, url, params, htmlId, height = '60vh', engine, allow } = fieldSchema['x-component-props'] || {};
const saveHtml = async (html: string) => {
const options = {
values: { html },
@ -68,13 +148,14 @@ const commonOptions: any = {
}
};
const { urlSchema, paramsSchema } = useURLAndHTMLSchema();
const submitHandler = async ({ mode, url, html, height, params, engine }) => {
const submitHandler = async ({ mode, url, html, height, params, engine, allow }) => {
const componentProps = fieldSchema['x-component-props'] || {};
componentProps['mode'] = mode;
componentProps['height'] = height;
componentProps['engine'] = engine || 'string';
componentProps['params'] = params;
componentProps['url'] = url;
componentProps['allow'] = allow;
if (mode === 'html') {
const data = await saveHtml(html);
componentProps['htmlId'] = data.id;
@ -116,6 +197,7 @@ const commonOptions: any = {
height,
engine,
params,
allow,
};
if (htmlId) {
// eslint-disable-next-line no-unsafe-optional-chaining
@ -143,6 +225,31 @@ const commonOptions: any = {
...urlSchema,
required: true,
},
allow: {
title: 'Allow',
type: 'string',
'x-decorator': 'FormItem',
'x-component': (props) => {
return (
<Select
{...props}
allowClear
options={[
{ value: 'autoplay', label: <AllowOptionsHelp type="autoplay" /> },
{ value: 'camera', label: <AllowOptionsHelp type="camera" /> },
{ value: 'document-domain', label: <AllowOptionsHelp type="document-domain" /> },
{ value: 'encrypted-media', label: <AllowOptionsHelp type="encrypted-media" /> },
{ value: 'fullscreen', label: <AllowOptionsHelp type="fullscreen" /> },
{ value: 'geolocation', label: <AllowOptionsHelp type="geolocation" /> },
{ value: 'microphone', label: <AllowOptionsHelp type="microphone" /> },
{ value: 'midi', label: <AllowOptionsHelp type="midi" /> },
{ value: 'payment', label: <AllowOptionsHelp type="payment" /> },
]}
/>
);
},
description: <AllowDescription />,
},
params: paramsSchema,
engine: {
title: '{{t("Template engine")}}',
@ -195,6 +302,7 @@ const commonOptions: any = {
} as ISchema,
onSubmit: submitHandler,
noRecord: true,
width: 600,
};
},
},