# 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';

// 提供工作室服務的 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',
          ],
        }}
      />
    </>
  );
}
```

## 屬性

`BlockletStudio` 元件接受以下屬性來控制其行為和外觀。

<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` 屬性相關的狀態變數設定為 `false`。</x-field-desc>
  </x-field>
  <x-field data-name="componentDid" data-type="string" data-required="true">
    <x-field-desc markdown>提供資源發佈服務的 blocklet 的去中心化識別碼 (DID)。這決定了載入哪個工作室介面。</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>決定工作室介面的顯示模式。預設為 `'dialog'`。</x-field-desc>
  </x-field>
  <x-field data-name="title" data-type="string" data-required="false">
    <x-field-desc markdown>一個可選的標題，顯示在工作室介面的頂部。</x-field-desc>
  </x-field>
  <x-field data-name="logo" data-type="string" data-required="false">
    <x-field-desc markdown>一個可選的 logo URL，顯示在工作室介面中。</x-field-desc>
  </x-field>
  <x-field data-name="description" data-type="string" data-required="false">
    <x-field-desc markdown>一個可選的字串，提供簡短描述，顯示在工作室 UI 中。</x-field-desc>
  </x-field>
  <x-field data-name="introduction" data-type="string" data-required="false">
    <x-field-desc markdown>一個可選的字串，用於更詳細的介紹，顯示在工作室 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>一個物件陣列，代表在工作室中應預設預選的元件。</x-field-desc>
  </x-field>
  <x-field data-name="resources" data-type="Record<string, unknown>" data-required="false" data-default="{}">
    <x-field-desc markdown>一個物件，代表在工作室中應預設預選的資源。</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 回應範例。「應用程式」和「工具」資源宣告了它們的元件相依性。

```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` 和一組清晰的屬性，它在簡化開發的同時提供了一致的使用者體驗。有關相依性管理的相關功能，您可能還想查閱 [ComponentInstaller](./components-component-management-component-installer.md) 文件。