# BlockletStudio

`BlockletStudio` 组件提供了一个精简的、可嵌入的用户界面，用于发布和管理 blocklet 资源。它通过渲染一个全页 `iframe` 来运行，该 `iframe` 从指定的 blocklet 服务加载一个专用的发布界面。这使得您的应用程序能够提供一致且强大的资源管理体验，而无需从头开始构建 UI。

主机应用程序与 `BlockletStudio` iframe 之间的通信通过 `window.postMessage` API 进行安全处理，从而可以为上传、发布和连接等事件启用回调。

## 使用示例

要集成 `BlockletStudio`，您需要管理其可见性状态（例如，使用 `useState`）。由于 iframe 可能需要一些时间来加载，建议向用户显示一个加载指示器。`onOpened` 回调可用于表示 iframe 内容已准备就绪，此时可以隐藏加载指示器。

以下示例演示了如何实现一个打开 `BlockletStudio` 对话框并管理加载状态的按钮。

```tsx icon=logos:react title="Exporter.tsx"
import { Icon } from '@iconify-icon/react';
import ArrowUp from '@iconify-icons/tabler/arrow-big-up-line';
import { Box, IconButton, CircularProgress as Spinner, svgIconClasses } from '@mui/material';
import { useState } from 'react';
import { BlockletStudio } from '@arcblock/ux-react';

// 提供 studio 服务的 blocklet 的 DID。
const AI_STUDIO_COMPONENT_DID = 'z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9';

export default function Exporter() {
  const [showCreateResource, setShowCreateResource] = useState(false);
  const [opening, setOpening] = useState(false);

  const handleShowDialog = () => {
    setOpening(true);
    setShowCreateResource(true);
  };

  return (
    <>
      <IconButton
        sx={{
          position: 'relative',
          minWidth: 40,
          minHeight: 40,
          borderRadius: '100%',
          [`.${svgIconClasses.root}`]: {
            color: 'text.secondary',
          },
        }}
        onClick={handleShowDialog}>
        {opening ? <Spinner size={16} /> : <Box component={Icon} icon={ArrowUp} style={{ fontSize: 24 }} />}
      </IconButton>
      <BlockletStudio
        open={showCreateResource}
        setOpen={setShowCreateResource}
        onOpened={() => setOpening(false)}
        componentDid={AI_STUDIO_COMPONENT_DID}
        mode="dialog"
        title="Demo Project"
        description="This is a demo project for the 'aigne' blocklet."
        note='Please review all resources and components before publishing.'
        introduction="Welcome to the resource publisher."
        tenantScope="test-tenant-scope-id-2"
        resourcesParams={{ name: 'test-project', extra: true }}
        dependentComponentsMode="readonly"
        componentsTitle="Required Components"
        resourcesTitle="Add Project Files"
        onConnected={() => alert('Connected')}
        onUploaded={() => alert('Uploaded')}
        onReleased={() => alert('Released')}
        components={[
          { did: 'z8ia3xzq2tMq8CRHfaXj1BTYJyYnEcHbqP8cJ', included: true, required: true },
          { did: 'z2qZyjnsRffFtn2PDnDwDHTRbAu53RpKqDtFZ', included: true, required: false },
        ]}
        resources={{
          'z8iZpog7mcgcgBZzTiXJCWESvmnRrQmnd3XBB': [
            'template-448698592710885376',
            'template-448696391418511360',
          ],
        }}
      />
    </>
  );
}
```

## Props

`BlockletStudio` 组件接受以下 props 来控制其行为和外观。

<x-field-group>
  <x-field data-name="open" data-type="boolean" data-required="true">
    <x-field-desc markdown>控制 `BlockletStudio` iframe 的可见性。设置为 `true` 显示，设置为 `false` 隐藏。</x-field-desc>
  </x-field>
  <x-field data-name="setOpen" data-type="(open: boolean) => void" data-required="true">
    <x-field-desc markdown>一个回调函数，`BlockletStudio` 使用它来请求关闭。通常，此函数会将与 `open` prop 关联的状态变量设置为 `false`。</x-field-desc>
  </x-field>
  <x-field data-name="componentDid" data-type="string" data-required="true">
    <x-field-desc markdown>提供资源发布服务的 blocklet 的去中心化标识符 (DID)。这决定了加载哪个 studio 界面。</x-field-desc>
  </x-field>
  <x-field data-name="onOpened" data-type="() => void" data-required="false">
    <x-field-desc markdown>一个可选的回调函数，当 `BlockletStudio` iframe 加载完成并准备好进行用户交互时执行。</x-field-desc>
  </x-field>
  <x-field data-name="onUploaded" data-type="(data: unknown) => void" data-required="false">
    <x-field-desc markdown>一个可选的回调函数，在用户成功上传新资源后触发。`data` 参数包含有关上传项的元数据。</x-field-desc>
  </x-field>
  <x-field data-name="onReleased" data-type="(data: unknown) => void" data-required="false">
    <x-field-desc markdown>一个可选的回调函数，在用户发布组件的新版本后触发。`data` 参数包含有关新版本的信息。</x-field-desc>
  </x-field>
  <x-field data-name="onConnected" data-type="(data: unknown) => void" data-required="false">
    <x-field-desc markdown>一个可选的回调函数，在用户连接资源或组件后触发。</x-field-desc>
  </x-field>
  <x-field data-name="tenantScope" data-type="string" data-required="false">
    <x-field-desc markdown>一个可选字符串，用于将资源范围限定于特定租户。这可用于确保多租户环境中的数据隔离。</x-field-desc>
  </x-field>
  <x-field data-name="resourcesParams" data-type="Record<string, any>" data-required="false" data-default="{}">
    <x-field-desc markdown>一个包含查询参数的对象，这些参数将传递给 blocklet 的资源获取 API 端点。这允许对资源进行动态过滤。</x-field-desc>
  </x-field>
  <x-field data-name="mode" data-type="string" data-required="false" data-default='"dialog"'>
    <x-field-desc markdown>决定 studio 界面的显示模式。默认为 `'dialog'`。</x-field-desc>
  </x-field>
  <x-field data-name="title" data-type="string" data-required="false">
    <x-field-desc markdown>一个可选的标题，显示在 studio 界面的顶部。</x-field-desc>
  </x-field>
  <x-field data-name="logo" data-type="string" data-required="false">
    <x-field-desc markdown>一个可选的 logo URL，显示在 studio 界面中。</x-field-desc>
  </x-field>
  <x-field data-name="description" data-type="string" data-required="false">
    <x-field-desc markdown>一个可选的字符串，提供简短描述，显示在 studio UI 中。</x-field-desc>
  </x-field>
  <x-field data-name="introduction" data-type="string" data-required="false">
    <x-field-desc markdown>一个可选的字符串，用于更详细的介绍，显示在 studio UI 中。</x-field-desc>
  </x-field>
  <x-field data-name="note" data-type="string" data-required="false">
    <x-field-desc markdown>一个可选的字符串，用于向用户显示注释或重要消息。</x-field-desc>
  </x-field>
  <x-field data-name="componentsTitle" data-type="string" data-required="false">
    <x-field-desc markdown>一个可选的字符串，用于自定义组件部分的标题。</x-field-desc>
  </x-field>
  <x-field data-name="resourcesTitle" data-type="string" data-required="false">
    <x-field-desc markdown>一个可选的字符串，用于自定义资源部分的标题。</x-field-desc>
  </x-field>
  <x-field data-name="components" data-type="Record<string, unknown>[]" data-required="false" data-default="[]">
    <x-field-desc markdown>一个对象数组，代表在 studio 中默认应预先选中的组件。</x-field-desc>
  </x-field>
  <x-field data-name="resources" data-type="Record<string, unknown>" data-required="false" data-default="{}">
    <x-field-desc markdown>一个对象，代表在 studio 中默认应预先选中的资源。</x-field-desc>
  </x-field>
  <x-field data-name="dependentComponentsMode" data-type="'auto' | 'readonly'" data-required="false">
    <x-field-desc markdown>控制依赖组件的行为。在 `'readonly'` 模式下，用户无法取消选择作为所选资源依赖项而自动包含的组件。</x-field-desc>
  </x-field>
  <x-field data-name="style" data-type="React.CSSProperties" data-required="false" data-default="{}">
    <x-field-desc markdown>一个用于将自定义 CSS 样式应用于 `iframe` 元素的对象。</x-field-desc>
  </x-field>
  <x-field data-name="zIndex" data-type="number" data-required="false" data-default="9999">
    <x-field-desc markdown>`iframe` 元素的 z-index CSS 属性，控制其堆叠顺序。</x-field-desc>
  </x-field>
</x-field-group>

## 依赖组件

为 `BlockletStudio` 提供资源 API 的 Blocklet 可以直接在资源数据中指定组件依赖项。当用户选择一个列出了 `dependentComponents` 的资源时，`BlockletStudio` 将在 UI 中自动选择这些组件。

此功能通过确保所有必要的依赖项都自动包含进来，从而简化了用户体验。

要实现此功能，您的 blocklet 的资源 API 应返回一个包含所需组件 DID 的 `dependentComponents` 数组。

### API 响应示例

以下是来自资源 API 端点的 JSON 响应示例。"Application" 和 "Tool" 资源声明了它们的组件依赖项。

```json 带有依赖项的 API 响应
{
  "resources": [
    {
      "id": "application-448698592710885376",
      "name": "My App (as Application)",
      "dependentComponents": [
        "error-did",
        "z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9",
        "z2qZyjnsRffFtn2PDnDwDHTRbAu53RpKqDtFZ",
        "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk"
      ]
    },
    {
      "id": "tool-448698592710885376",
      "name": "My App (as Tool)",
      "dependentComponents": ["error-did", "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk"]
    },
    {
      "id": "template-448698592710885376",
      "name": "My App (as Template)"
    }
  ]
}
```

## 总结

`BlockletStudio` 组件提供了一种强大而便捷的方式，可将资源和组件管理直接集成到您的应用程序中。通过利用 `iframe` 和一组清晰的 props，它在简化开发的同时提供了一致的用户体验。有关依赖项管理的相关功能，您可能还想查看 [ComponentInstaller](./components-component-management-component-installer.md) 文档。