# ComponentInstaller

`ComponentInstaller` 是一個實用工具元件，對於依賴其他 blocklet 的功能扮演著守門員的角色。它會驗證指定的元件相依性是否已安裝。如果尚未安裝，它會為管理員（預設為 `owner` 或 `admin` 角色）提供一個使用者友善的介面，讓他們可以直接安裝。如果使用者缺乏必要的權限，它會顯示一則訊息，建議他們聯絡管理員。

此元件對於建立穩健的應用程式至關重要，能夠優雅地處理缺失的選用相依性，並透過引導管理員完成安裝過程來改善使用者體驗。

## 運作方式

該元件的邏輯遵循一個清晰的順序流程，以確保在渲染其子元件之前滿足所有相依性。

<!-- DIAGRAM_IMAGE_START:flowchart:4:3:1765962229 -->
![ComponentInstaller](assets/diagram/component-installer-diagram-0.zh-TW.jpg)
<!-- DIAGRAM_IMAGE_END -->

1.  **相依性檢查**：它會讀取 `did` prop，並對照 blocklet 的元資料（`window.blocklet.optionalComponents`）進行檢查，以確定所需元件是否已安裝。
2.  **權限檢查**：如果有任何元件缺失，它會使用 `SessionPermission` 元件來驗證目前使用者的角色是否與 `roles` prop 中指定的角色相符。
3.  **條件式渲染**：
    *   如果所有相依性都已安裝，它會渲染其 `children`。
    *   如果相依性缺失且使用者擁有權限，它會顯示一個彈出式安裝面板。
    *   如果相依性缺失且使用者缺乏權限，它會顯示聯絡管理員的建議，或者在啟用 `noPermissionMute` 的情況下渲染一個 `fallback` 元件。

## 基本用法

將任何依賴選用 blocklet 的元件或功能用 `ComponentInstaller` 包裹起來。提供所需元件的 DID。

```jsx "MyFeature.jsx" icon=logos:react
import ComponentInstaller from '@arcblock/blocklet-ui-react/lib/ComponentInstaller';
import MyDependentComponent from './MyDependentComponent';

export default function MyFeature() {
  // 請替換為所需元件的實際 DID
  const requiredComponentDid = 'z8ia2427634f1e909a304e2b963715a18';

  return (
    <ComponentInstaller did={requiredComponentDid}>
      {/* 只有在滿足相依性條件時，此元件才會被渲染 */}
      <MyDependentComponent />
    </ComponentInstaller>
  );
}
```

## 屬性 (Props)

`ComponentInstaller` 接受以下屬性 (props) 來自訂其行為。

<x-field-group>
  <x-field data-name="did" data-type="string | string[]" data-required="true">
    <x-field-desc markdown>要檢查的元件相依性的 DID (或 DID 陣列)。這是您所需 blocklet 的主要識別碼。</x-field-desc>
  </x-field>
  <x-field data-name="children" data-type="any" data-required="true">
    <x-field-desc markdown>一旦確認所有相依性都已安裝，要渲染的內容。可以是標準的 React 節點或渲染函數。</x-field-desc>
  </x-field>
  <x-field data-name="roles" data-type="string[]" data-default='["owner", "admin"]'>
    <x-field-desc markdown>允許查看安裝 UI 並安裝缺失元件的使用者角色陣列。</x-field-desc>
  </x-field>
  <x-field data-name="fallback" data-type="React.ReactNode" data-required="false">
    <x-field-desc markdown>一個備用元件，用於在檢查相依性期間，或當 `noPermissionMute` 對於沒有安裝權限的使用者啟用時顯示。</x-field-desc>
  </x-field>
  <x-field data-name="noPermissionMute" data-type="boolean" data-default="false">
    <x-field-desc markdown>如果為 `true`，沒有權限的使用者將看到 `fallback` 元件（或什麼都不顯示），而不是「聯絡管理員」的訊息。</x-field-desc>
  </x-field>
  <x-field data-name="disabled" data-type="boolean" data-default="false">
    <x-field-desc markdown>如果為 `true`，該元件會繞過所有檢查並立即渲染其 `children`。</x-field-desc>
  </x-field>
  <x-field data-name="onInstalled" data-type="function" data-required="false">
    <x-field-desc markdown>在相依性檢查完成後觸發的回呼函數。它會接收已安裝元件的列表。</x-field-desc>
  </x-field>
  <x-field data-name="onError" data-type="function" data-required="false">
    <x-field-desc markdown>如果在相依性檢查期間發生錯誤時觸發的回呼函數。它會接收導致錯誤的元件列表。</x-field-desc>
  </x-field>
  <x-field data-name="onClose" data-type="function" data-required="false">
    <x-field-desc markdown>當安裝彈出視窗關閉時調用的回呼函數。</x-field-desc>
  </x-field>
  <x-field data-name="closeByOutSize" data-type="boolean" data-default="false">
    <x-field-desc markdown>如果為 `true`，當使用者點擊安裝彈出視窗外部時，該視窗將會關閉。</x-field-desc>
  </x-field>
  <x-field data-name="warnIcon" data-type="React.ReactNode" data-required="false">
    <x-field-desc markdown>一個自訂的 React 節點，用於替換安裝彈出視窗中的預設警告圖示。</x-field-desc>
  </x-field>
</x-field-group>

## 進階用法

### 檢查多個元件

您可以將一個 DID 陣列傳遞給 `did` prop，以一次性檢查多個相依性。只有在所有指定的元件都已安裝的情況下，子元件才會被渲染。

```jsx "MyDashboard.jsx" icon=logos:react
import ComponentInstaller from '@arcblock/blocklet-ui-react/lib/ComponentInstaller';
import AnalyticsWidget from './AnalyticsWidget';
import CmsWidget from './CmsWidget';

export default function MyDashboard() {
  const requiredDids = [
    'z8ia2427634f1e909a304e2b963715a18', // 分析服務
    'z8ia3c1f2e4b8e6a1b2c3d4e5f6a7b8c9', // 內容管理系統服務
  ];

  return (
    <ComponentInstaller did={requiredDids}>
      <AnalyticsWidget />
      <CmsWidget />
    </ComponentInstaller>
  );
}
```

### 使用備用元件處理載入狀態

提供一個 `fallback` 元件，以在相依性檢查進行中時改善使用者體驗。這對於搭配 `noPermissionMute` 為未經授權的使用者顯示佔位符也很有用。

```jsx "FeatureWithLoading.jsx" icon=logos:react
import ComponentInstaller from '@arcblock/blocklet-ui-react/lib/ComponentInstaller';
import CircularProgress from '@mui/material/CircularProgress';
import Box from '@mui/material/Box';
import MyDependentComponent from './MyDependentComponent';

export default function FeatureWithLoading() {
  const LoadingSpinner = (
    <Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}>
      <CircularProgress />
    </Box>
  );

  return (
    <ComponentInstaller
      did="z8ia2427634f1e909a304e2b963715a18"
      fallback={LoadingSpinner}
      noPermissionMute={true}
    >
      <MyDependentComponent />
    </ComponentInstaller>
  );
}
```

### 使用渲染屬性 (Render Props) 自訂 UI

為了完全控制 UI，您可以傳遞一個函數作為 `children`。此函數會接收一個包含 `hasPermission`、`optComponents` 和 `installStatus` 的物件，讓您可以建立完全自訂的安裝介面。

```jsx "CustomInstallerButton.jsx" icon=logos:react
import ComponentInstaller from '@arcblock/blocklet-ui-react/lib/ComponentInstaller';
import Button from '@mui/material/Button';

export default function CustomInstallerButton() {
  const requiredDid = 'z8ia2427634f1e909a304e2b963715a18';

  return (
    <ComponentInstaller did={requiredDid}>
      {({ hasPermission, optComponents, installStatus }) => {
        const isMissing = optComponents.length > 0;
        const status = installStatus[requiredDid] || 'not_installed';

        if (!isMissing) {
          return <p>功能已準備就緒！</p>;
        }

        if (!hasPermission) {
          return <p>請要求管理員安裝所需元件。</p>;
        }

        return (
          <Button
            variant="contained"
            disabled={status !== 'not_installed'}
            onClick={() => window.open(optComponents[0].installUrl, '_blank')}
          >
            {status === 'not_installed' ? `安裝 ${optComponents[0].meta.title}` : `安裝中... (${status})`}
          </Button>
        );
      }}
    </ComponentInstaller>
  );
}
```

## 總結

`ComponentInstaller` 是一個強大的元件，用於管理 blocklet 應用程式中的選用相依性。它提供了一種結構化且使用者友善的方式，以確保所需元件可用，並在必要時引導管理員完成安裝過程。透過使用此元件，您可以建立更具彈性、功能更豐富且能適應使用者環境的應用程式。

有關管理元件的更多詳細資訊，您可能也會對 [BlockletStudio](./components-component-management-blocklet-studio.md) 元件感興趣，它為資源和元件管理提供了更全面的介面。