# BlockletStudio

The `BlockletStudio` component provides a streamlined, embeddable user interface for publishing and managing blocklet resources. It operates by rendering a full-page `iframe` that loads a dedicated publishing interface from a specified blocklet service. This allows your application to offer a consistent and powerful resource management experience without needing to build the UI from scratch.

Communication between the host application and the `BlockletStudio` iframe is handled securely using the `window.postMessage` API, enabling callbacks for events like uploads, releases, and connections.

## Usage Example

To integrate `BlockletStudio`, you need to manage its visibility state (e.g., with `useState`). Since the iframe may take a moment to load, it is recommended to display a loading indicator to the user. The `onOpened` callback can be used to signal that the iframe content is ready, at which point the loading indicator can be hidden.

The following example demonstrates how to implement a button that opens the `BlockletStudio` dialog and manages a loading state.

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

// The DID of the blocklet that provides the studio service.
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

The `BlockletStudio` component accepts the following props to control its behavior and appearance.

<x-field-group>
  <x-field data-name="open" data-type="boolean" data-required="true">
    <x-field-desc markdown>Controls the visibility of the `BlockletStudio` iframe. Set to `true` to display it, and `false` to hide it.</x-field-desc>
  </x-field>
  <x-field data-name="setOpen" data-type="(open: boolean) => void" data-required="true">
    <x-field-desc markdown>A callback function used by `BlockletStudio` to request being closed. Typically, this function will set the state variable associated with the `open` prop to `false`.</x-field-desc>
  </x-field>
  <x-field data-name="componentDid" data-type="string" data-required="true">
    <x-field-desc markdown>The Decentralized Identifier (DID) of the blocklet that provides the resource publishing service. This determines which studio interface is loaded.</x-field-desc>
  </x-field>
  <x-field data-name="onOpened" data-type="() => void" data-required="false">
    <x-field-desc markdown>An optional callback function that is executed when the `BlockletStudio` iframe has finished loading and is ready for user interaction.</x-field-desc>
  </x-field>
  <x-field data-name="onUploaded" data-type="(data: unknown) => void" data-required="false">
    <x-field-desc markdown>An optional callback function that is triggered after a user successfully uploads a new resource. The `data` parameter contains metadata about the uploaded item.</x-field-desc>
  </x-field>
  <x-field data-name="onReleased" data-type="(data: unknown) => void" data-required="false">
    <x-field-desc markdown>An optional callback function that is triggered after a user publishes a new release of a component. The `data` parameter contains information about the new release.</x-field-desc>
  </x-field>
  <x-field data-name="onConnected" data-type="(data: unknown) => void" data-required="false">
    <x-field-desc markdown>An optional callback function that is triggered after a user connects a resource or component.</x-field-desc>
  </x-field>
  <x-field data-name="tenantScope" data-type="string" data-required="false">
    <x-field-desc markdown>An optional string to scope the resources to a specific tenant. This can be used to ensure data isolation in a multi-tenant environment.</x-field-desc>
  </x-field>
  <x-field data-name="resourcesParams" data-type="Record<string, any>" data-required="false" data-default="{}">
    <x-field-desc markdown>An object containing query parameters to be passed to the blocklet's resource-fetching API endpoint. This allows for dynamic filtering of resources.</x-field-desc>
  </x-field>
  <x-field data-name="mode" data-type="string" data-required="false" data-default='"dialog"'>
    <x-field-desc markdown>Determines the display mode of the studio interface. The default is `'dialog'`.</x-field-desc>
  </x-field>
  <x-field data-name="title" data-type="string" data-required="false">
    <x-field-desc markdown>An optional title to be displayed at the top of the studio interface.</x-field-desc>
  </x-field>
  <x-field data-name="logo" data-type="string" data-required="false">
    <x-field-desc markdown>An optional URL for a logo to be displayed in the studio interface.</x-field-desc>
  </x-field>
  <x-field data-name="description" data-type="string" data-required="false">
    <x-field-desc markdown>An optional string providing a short description, displayed in the studio UI.</x-field-desc>
  </x-field>
  <x-field data-name="introduction" data-type="string" data-required="false">
    <x-field-desc markdown>An optional string for a more detailed introduction, displayed in the studio UI.</x-field-desc>
  </x-field>
  <x-field data-name="note" data-type="string" data-required="false">
    <x-field-desc markdown>An optional string to display a note or important message to the user.</x-field-desc>
  </x-field>
  <x-field data-name="componentsTitle" data-type="string" data-required="false">
    <x-field-desc markdown>An optional string to customize the title of the components section.</x-field-desc>
  </x-field>
  <x-field data-name="resourcesTitle" data-type="string" data-required="false">
    <x-field-desc markdown>An optional string to customize the title of the resources section.</x-field-desc>
  </x-field>
  <x-field data-name="components" data-type="Record<string, unknown>[]" data-required="false" data-default="[]">
    <x-field-desc markdown>An array of objects representing components that should be pre-selected by default in the 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>An object representing resources that should be pre-selected by default in the studio.</x-field-desc>
  </x-field>
  <x-field data-name="dependentComponentsMode" data-type="'auto' | 'readonly'" data-required="false">
    <x-field-desc markdown>Controls the behavior of dependent components. In `'readonly'` mode, users cannot deselect components that are automatically included as dependencies of a selected resource.</x-field-desc>
  </x-field>
  <x-field data-name="style" data-type="React.CSSProperties" data-required="false" data-default="{}">
    <x-field-desc markdown>An object for applying custom CSS styles to the `iframe` element.</x-field-desc>
  </x-field>
  <x-field data-name="zIndex" data-type="number" data-required="false" data-default="9999">
    <x-field-desc markdown>The z-index CSS property for the `iframe` element, controlling its stacking order.</x-field-desc>
  </x-field>
</x-field-group>

## Dependent Components

Blocklets that provide a resource API for the `BlockletStudio` can specify component dependencies directly within the resource data. When a user selects a resource that lists `dependentComponents`, the `BlockletStudio` will automatically select those components in the UI.

This feature simplifies the user experience by ensuring that all necessary dependencies are included automatically.

To implement this, your blocklet's resource API should return a `dependentComponents` array containing the DIDs of the required components.

### API Response Example

Here is an example of a JSON response from a resource API endpoint. The "Application" and "Tool" resources declare their component dependencies.

```json API Response with Dependencies
{
  "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)"
    }
  ]
}
```

## Summary

The `BlockletStudio` component offers a powerful and convenient way to integrate resource and component management directly into your application. By leveraging an `iframe` and a clear set of props, it provides a consistent user experience while simplifying development. For related functionalities regarding dependency management, you may also want to review the [ComponentInstaller](./components-component-management-component-installer.md) documentation.