umbraco-manifest-picker

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Umbraco Manifest Picker

Umbraco Manifest选择器

What is it?

是什么?

The
<umb-input-manifest>
component provides a picker UI for selecting registered extensions from the Umbraco extension registry. It allows users to pick from any extension type (workspaces, dashboards, property editors, etc.) and returns the selected manifest. This is useful for configuration UIs where users need to reference other extensions.
<umb-input-manifest>
组件提供了一个选择器UI,用于从Umbraco扩展注册表中选择已注册的扩展。它允许用户从任何扩展类型(工作区、仪表板、属性编辑器等)中进行选择,并返回选中的manifest。这在需要用户引用其他扩展的配置UI中非常实用。

Documentation

文档

Reference Example

参考示例

The Umbraco source includes a working example:
Location:
/Umbraco-CMS/src/Umbraco.Web.UI.Client/examples/manifest-picker/
This example demonstrates a dashboard that lets users browse and select extensions by type.
Umbraco源码中包含一个可用示例:
位置:/Umbraco-CMS/src/Umbraco.Web.UI.Client/examples/manifest-picker/
该示例展示了一个允许用户按类型浏览和选择扩展的仪表板。

Related Foundation Skills

相关基础技能

  • Extension Registry: For understanding how extensions are registered
    • Reference skill:
      umbraco-extension-registry
  • Umbraco Element: For creating picker UIs
    • Reference skill:
      umbraco-umbraco-element
  • 扩展注册表:用于理解扩展的注册方式
    • 参考技能:
      umbraco-extension-registry
  • Umbraco元素:用于创建选择器UI
    • 参考技能:
      umbraco-umbraco-element

Workflow

工作流程

  1. Fetch docs - Use WebFetch on the URLs above
  2. Ask questions - What extension types to pick? How to use selection?
  3. Generate files - Create element with manifest picker
  4. Explain - Show what was created and how selection works

  1. 获取文档 - 使用WebFetch访问上述URL
  2. 提出问题 - 要选择哪些扩展类型?如何使用选择结果?
  3. 生成文件 - 创建包含manifest选择器的元素
  4. 说明 - 展示创建的内容以及选择功能的工作方式

Basic Usage

基本用法

typescript
import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbInputManifestElement } from '@umbraco-cms/backoffice/components';

@customElement('my-manifest-picker-example')
export class MyManifestPickerExampleElement extends UmbLitElement {
  @state()
  private _selectedManifest = '';

  #onChange(event: { target: UmbInputManifestElement }) {
    const selectedManifest = event.target.value;
    this._selectedManifest = selectedManifest?.value ?? '';
  }

  override render() {
    return html`
      <uui-box>
        <umb-property-layout label="Select a workspace">
          <div slot="editor">
            <umb-input-manifest
              .extensionType=${'workspace'}
              @change=${this.#onChange}
            ></umb-input-manifest>
          </div>
        </umb-property-layout>

        ${this._selectedManifest
          ? html`<p>Selected: <code>${this._selectedManifest}</code></p>`
          : ''}
      </uui-box>
    `;
  }
}

typescript
import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbInputManifestElement } from '@umbraco-cms/backoffice/components';

@customElement('my-manifest-picker-example')
export class MyManifestPickerExampleElement extends UmbLitElement {
  @state()
  private _selectedManifest = '';

  #onChange(event: { target: UmbInputManifestElement }) {
    const selectedManifest = event.target.value;
    this._selectedManifest = selectedManifest?.value ?? '';
  }

  override render() {
    return html`
      <uui-box>
        <umb-property-layout label="Select a workspace">
          <div slot="editor">
            <umb-input-manifest
              .extensionType=${'workspace'}
              @change=${this.#onChange}
            ></umb-input-manifest>
          </div>
        </umb-property-layout>

        ${this._selectedManifest
          ? html`<p>Selected: <code>${this._selectedManifest}</code></p>`
          : ''}
      </uui-box>
    `;
  }
}

Dynamic Extension Type Selection

动态扩展类型选择

typescript
import { html, customElement, state, when, nothing } from '@umbraco-cms/backoffice/external/lit';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbInputManifestElement } from '@umbraco-cms/backoffice/components';
import type { UUISelectEvent } from '@umbraco-cms/backoffice/external/uui';

interface Option {
  name: string;
  value: string;
  selected: boolean;
}

@customElement('my-extension-browser')
export class MyExtensionBrowserElement extends UmbLitElement {
  #options: Option[] = [];

  @state()
  private _selectedExtensionType = 'dashboard';

  @state()
  private _selectedManifest = '';

  constructor() {
    super();

    // Build list of available extension types from registry
    this.observe(umbExtensionsRegistry.extensions, (extensions) => {
      const types = [...new Set(extensions.map((x) => x.type))];
      this.#options = types.sort().map((x) => ({
        name: x,
        value: x,
        selected: x === this._selectedExtensionType,
      }));
    });
  }

  #onTypeSelect(event: UUISelectEvent) {
    this._selectedManifest = '';
    this._selectedExtensionType = event.target.value as string;
  }

  #onManifestChange(event: { target: UmbInputManifestElement }) {
    const selectedManifest = event.target.value;
    this._selectedManifest = selectedManifest?.value ?? '';
  }

  override render() {
    return html`
      <uui-box>
        <umb-property-layout label="Extension Type">
          <uui-select
            slot="editor"
            label="Select type"
            placeholder="Select type..."
            .options=${this.#options}
            @change=${this.#onTypeSelect}
          ></uui-select>
        </umb-property-layout>

        ${when(
          this._selectedExtensionType,
          () => html`
            <umb-property-layout
              label="Select Extension"
              description="Pick a ${this._selectedExtensionType}"
            >
              <div slot="editor">
                <umb-input-manifest
                  .extensionType=${this._selectedExtensionType}
                  @change=${this.#onManifestChange}
                ></umb-input-manifest>
              </div>
            </umb-property-layout>
          `,
          () => nothing
        )}

        ${when(
          this._selectedManifest,
          () => html`
            <umb-property-layout label="Selected Manifest">
              <div slot="editor">
                <code>${this._selectedManifest}</code>
              </div>
            </umb-property-layout>
          `,
          () => nothing
        )}
      </uui-box>
    `;
  }
}

typescript
import { html, customElement, state, when, nothing } from '@umbraco-cms/backoffice/external/lit';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbInputManifestElement } from '@umbraco-cms/backoffice/components';
import type { UUISelectEvent } from '@umbraco-cms/backoffice/external/uui';

interface Option {
  name: string;
  value: string;
  selected: boolean;
}

@customElement('my-extension-browser')
export class MyExtensionBrowserElement extends UmbLitElement {
  #options: Option[] = [];

  @state()
  private _selectedExtensionType = 'dashboard';

  @state()
  private _selectedManifest = '';

  constructor() {
    super();

    // Build list of available extension types from registry
    this.observe(umbExtensionsRegistry.extensions, (extensions) => {
      const types = [...new Set(extensions.map((x) => x.type))];
      this.#options = types.sort().map((x) => ({
        name: x,
        value: x,
        selected: x === this._selectedExtensionType,
      }));
    });
  }

  #onTypeSelect(event: UUISelectEvent) {
    this._selectedManifest = '';
    this._selectedExtensionType = event.target.value as string;
  }

  #onManifestChange(event: { target: UmbInputManifestElement }) {
    const selectedManifest = event.target.value;
    this._selectedManifest = selectedManifest?.value ?? '';
  }

  override render() {
    return html`
      <uui-box>
        <umb-property-layout label="Extension Type">
          <uui-select
            slot="editor"
            label="Select type"
            placeholder="Select type..."
            .options=${this.#options}
            @change=${this.#onTypeSelect}
          ></uui-select>
        </umb-property-layout>

        ${when(
          this._selectedExtensionType,
          () => html`
            <umb-property-layout
              label="Select Extension"
              description="Pick a ${this._selectedExtensionType}"
            >
              <div slot="editor">
                <umb-input-manifest
                  .extensionType=${this._selectedExtensionType}
                  @change=${this.#onManifestChange}
                ></umb-input-manifest>
              </div>
            </umb-property-layout>
          `,
          () => nothing
        )}

        ${when(
          this._selectedManifest,
          () => html`
            <umb-property-layout label="Selected Manifest">
              <div slot="editor">
                <code>${this._selectedManifest}</code>
              </div>
            </umb-property-layout>
          `,
          () => nothing
        )}
      </uui-box>
    `;
  }
}

Getting All Extension Types

获取所有扩展类型

typescript
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';

// In your element constructor
this.observe(umbExtensionsRegistry.extensions, (extensions) => {
  // Get unique extension types
  const types = [...new Set(extensions.map((x) => x.type))];
  console.log('Available types:', types);

  // Filter by type
  const dashboards = extensions.filter((x) => x.type === 'dashboard');
  console.log('Dashboards:', dashboards);

  // Get extension by alias
  const specific = extensions.find((x) => x.alias === 'Umb.Dashboard.Welcome');
  console.log('Specific extension:', specific);
});

typescript
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';

// In your element constructor
this.observe(umbExtensionsRegistry.extensions, (extensions) => {
  // Get unique extension types
  const types = [...new Set(extensions.map((x) => x.type))];
  console.log('Available types:', types);

  // Filter by type
  const dashboards = extensions.filter((x) => x.type === 'dashboard');
  console.log('Dashboards:', dashboards);

  // Get extension by alias
  const specific = extensions.find((x) => x.alias === 'Umb.Dashboard.Welcome');
  console.log('Specific extension:', specific);
});

Common Extension Types

常见扩展类型

TypeDescription
dashboard
Section dashboards
workspace
Entity workspaces
workspaceView
Views within workspaces
workspaceAction
Workspace action buttons
propertyEditorUi
Property editor UIs
propertyEditorSchema
Property editor schemas
entityAction
Context menu actions
tree
Navigation trees
treeItem
Tree item renderers
section
Backoffice sections
sectionView
Section views
headerApp
Header bar apps
modal
Modal dialogs
condition
Extension conditions

TypeDescription
dashboard
后台区域仪表板
workspace
实体工作区
workspaceView
工作区内视图
workspaceAction
工作区操作按钮
propertyEditorUi
属性编辑器UI
propertyEditorSchema
属性编辑器架构
entityAction
上下文菜单操作
tree
导航树
treeItem
树项渲染器
section
后台区域
sectionView
区域视图
headerApp
顶部栏应用
modal
模态对话框
condition
扩展条件

UmbInputManifest Properties

UmbInputManifest属性

PropertyTypeDescription
extensionType
string
The type of extension to pick from
value
ManifestBase | undefined
The selected manifest

PropertyTypeDescription
extensionType
string
要选择的扩展类型
value
ManifestBase | undefined
选中的manifest

Events

事件

EventDetailDescription
change
{ target: UmbInputManifestElement }
Fired when selection changes

EventDetailDescription
change
{ target: UmbInputManifestElement }
当选择发生变化时触发

Accessing Selected Manifest Data

访问选中的Manifest数据

typescript
#onManifestChange(event: { target: UmbInputManifestElement }) {
  const manifest = event.target.value;

  if (manifest) {
    console.log('Alias:', manifest.alias);
    console.log('Name:', manifest.name);
    console.log('Type:', manifest.type);
    console.log('Full manifest:', manifest);
  }
}

typescript
#onManifestChange(event: { target: UmbInputManifestElement }) {
  const manifest = event.target.value;

  if (manifest) {
    console.log('Alias:', manifest.alias);
    console.log('Name:', manifest.name);
    console.log('Type:', manifest.type);
    console.log('Full manifest:', manifest);
  }
}

Use Cases

使用场景

  1. Configuration screens - Let users select which dashboard/workspace to show
  2. Extension management - Browse and inspect registered extensions
  3. Dynamic routing - Configure navigation targets
  4. Condition configuration - Select conditions to apply
  5. Package development - Test extension registration

  1. 配置界面 - 让用户选择要显示的仪表板/工作区
  2. 扩展管理 - 浏览和查看已注册的扩展
  3. 动态路由 - 配置导航目标
  4. 条件配置 - 选择要应用的条件
  5. 包开发 - 测试扩展注册

Best Practices

最佳实践

  1. Clear selection - Reset
    _selectedManifest
    when type changes
  2. Handle undefined - Check if
    value
    exists before accessing properties
  3. Use property layout - Wrap in
    <umb-property-layout>
    for consistent styling
  4. Show feedback - Display selected manifest alias/name to confirm selection
  5. Sort types - Sort extension type options alphabetically for usability
That's it! Always fetch fresh docs, keep examples minimal, generate complete working code.
  1. 清晰选择 - 当类型变化时重置
    _selectedManifest
  2. 处理未定义情况 - 在访问属性前检查
    value
    是否存在
  3. 使用属性布局 - 包裹在
    <umb-property-layout>
    中以保持样式一致
  4. 显示反馈 - 显示选中的manifest别名/名称以确认选择
  5. 排序类型 - 按字母顺序排序扩展类型选项以提升可用性
就是这样!请始终获取最新文档,保持示例简洁,生成完整的可运行代码。