mirror of
https://github.com/flarum/core.git
synced 2025-08-03 23:17:43 +02:00
chore: extract buildSettingComponent
method into a FormGroup
component (#3927)
* chore: extract `buildSettingComponent` method into a `FormGroup` component * chore: typings * feat: move to common
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import app from 'flarum/admin/app';
|
||||
import ItemList from 'flarum/common/utils/ItemList';
|
||||
import generateElementId from 'flarum/admin/utils/generateElementId';
|
||||
import generateElementId from 'flarum/common/utils/generateElementId';
|
||||
import FormModal, { IFormModalAttrs } from 'flarum/common/components/FormModal';
|
||||
|
||||
import Mithril from 'mithril';
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { extend } from 'flarum/common/extend';
|
||||
import AdminPage from 'flarum/admin/components/AdminPage';
|
||||
import SelectTagsSettingComponent from './components/SelectTagsSettingComponent';
|
||||
import FormGroup from 'flarum/common/components/FormGroup';
|
||||
import type { IFormGroupAttrs } from 'flarum/common/components/FormGroup';
|
||||
|
||||
export default function () {
|
||||
extend(AdminPage.prototype, 'customSettingComponents', function (items) {
|
||||
items.add('flarum-tags.select-tags', (attrs) => {
|
||||
return <SelectTagsSettingComponent {...attrs} settingValue={this.settings[attrs.setting]} />;
|
||||
extend(FormGroup.prototype, 'customFieldComponents', function (items) {
|
||||
items.add('flarum-tags.select-tags', (attrs: IFormGroupAttrs) => {
|
||||
return <SelectTagsSettingComponent {...attrs} settingValue={attrs.bidi} />;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ import './utils/saveSettings';
|
||||
import './utils/ExtensionData';
|
||||
import './utils/isExtensionEnabled';
|
||||
import './utils/getCategorizedExtensions';
|
||||
import './utils/generateElementId';
|
||||
|
||||
import './components/SettingDropdown';
|
||||
import './components/EditCustomFooterModal';
|
||||
@@ -22,7 +21,6 @@ import './components/ExtensionLinkButton';
|
||||
import './components/PermissionGrid';
|
||||
import './components/ExtensionPermissionGrid';
|
||||
import './components/MailPage';
|
||||
import './components/UploadImageButton';
|
||||
import './components/LoadingModal';
|
||||
import './components/DashboardPage';
|
||||
import './components/BasicsPage';
|
||||
|
@@ -3,17 +3,11 @@ import type Mithril from 'mithril';
|
||||
import app from '../app';
|
||||
import Page, { IPageAttrs } from '../../common/components/Page';
|
||||
import Button from '../../common/components/Button';
|
||||
import Switch from '../../common/components/Switch';
|
||||
import Select from '../../common/components/Select';
|
||||
import classList from '../../common/utils/classList';
|
||||
import Stream from '../../common/utils/Stream';
|
||||
import saveSettings from '../utils/saveSettings';
|
||||
import AdminHeader from './AdminHeader';
|
||||
import generateElementId from '../utils/generateElementId';
|
||||
import ColorPreviewInput from '../../common/components/ColorPreviewInput';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import type { IUploadImageButtonAttrs } from './UploadImageButton';
|
||||
import UploadImageButton from './UploadImageButton';
|
||||
import FormGroup, { FieldComponentOptions } from '../../common/components/FormGroup';
|
||||
import extractText from '../../common/utils/extractText';
|
||||
|
||||
export interface AdminHeaderOptions {
|
||||
@@ -28,115 +22,9 @@ export interface AdminHeaderOptions {
|
||||
className: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that matches any valid value for the `type` attribute on an HTML `<input>` element.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-type
|
||||
*
|
||||
* Note: this will be exported from a different location in the future.
|
||||
*
|
||||
* @see https://github.com/flarum/core/issues/3039
|
||||
*/
|
||||
export type HTMLInputTypes =
|
||||
| 'button'
|
||||
| 'checkbox'
|
||||
| 'color'
|
||||
| 'date'
|
||||
| 'datetime-local'
|
||||
| 'email'
|
||||
| 'file'
|
||||
| 'hidden'
|
||||
| 'image'
|
||||
| 'month'
|
||||
| 'number'
|
||||
| 'password'
|
||||
| 'radio'
|
||||
| 'range'
|
||||
| 'reset'
|
||||
| 'search'
|
||||
| 'submit'
|
||||
| 'tel'
|
||||
| 'text'
|
||||
| 'time'
|
||||
| 'url'
|
||||
| 'week';
|
||||
|
||||
export interface CommonSettingsItemOptions extends Mithril.Attributes {
|
||||
export type SettingsComponentOptions = FieldComponentOptions & {
|
||||
setting: string;
|
||||
label?: Mithril.Children;
|
||||
help?: Mithril.Children;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate an HTML input element.
|
||||
*/
|
||||
export interface HTMLInputSettingsComponentOptions extends CommonSettingsItemOptions {
|
||||
/**
|
||||
* Any valid HTML input `type` value.
|
||||
*/
|
||||
type: HTMLInputTypes;
|
||||
}
|
||||
|
||||
const BooleanSettingTypes = ['bool', 'checkbox', 'switch', 'boolean'] as const;
|
||||
const SelectSettingTypes = ['select', 'dropdown', 'selectdropdown'] as const;
|
||||
const TextareaSettingTypes = ['textarea'] as const;
|
||||
const ColorPreviewSettingType = 'color-preview' as const;
|
||||
const ImageUploadSettingType = 'image-upload' as const;
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate a Switch.
|
||||
*/
|
||||
export interface SwitchSettingComponentOptions extends CommonSettingsItemOptions {
|
||||
type: typeof BooleanSettingTypes[number];
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate a Select dropdown.
|
||||
*/
|
||||
export interface SelectSettingComponentOptions extends CommonSettingsItemOptions {
|
||||
type: typeof SelectSettingTypes[number];
|
||||
/**
|
||||
* Map of values to their labels
|
||||
*/
|
||||
options: { [value: string]: Mithril.Children };
|
||||
default: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate a Textarea.
|
||||
*/
|
||||
export interface TextareaSettingComponentOptions extends CommonSettingsItemOptions {
|
||||
type: typeof TextareaSettingTypes[number];
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate a ColorPreviewInput.
|
||||
*/
|
||||
export interface ColorPreviewSettingComponentOptions extends CommonSettingsItemOptions {
|
||||
type: typeof ColorPreviewSettingType;
|
||||
}
|
||||
|
||||
export interface ImageUploadSettingComponentOptions extends CommonSettingsItemOptions, IUploadImageButtonAttrs {
|
||||
type: typeof ImageUploadSettingType;
|
||||
}
|
||||
|
||||
export interface CustomSettingComponentOptions extends CommonSettingsItemOptions {
|
||||
type: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* All valid options for the setting component builder.
|
||||
*/
|
||||
export type SettingsComponentOptions =
|
||||
| HTMLInputSettingsComponentOptions
|
||||
| SwitchSettingComponentOptions
|
||||
| SelectSettingComponentOptions
|
||||
| TextareaSettingComponentOptions
|
||||
| ColorPreviewSettingComponentOptions
|
||||
| ImageUploadSettingComponentOptions
|
||||
| CustomSettingComponentOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Valid attrs that can be returned by the `headerInfo` function
|
||||
@@ -206,41 +94,6 @@ export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAt
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of extension-defined custom setting components to be available through
|
||||
* {@link AdminPage.buildSettingComponent}.
|
||||
*
|
||||
* The ItemList key represents the value for `type` to be provided when calling
|
||||
* {@link AdminPage.buildSettingComponent}. Other attributes passed are provided
|
||||
* as arguments to the function added to the ItemList.
|
||||
*
|
||||
* ItemList priority has no effect here.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* extend(AdminPage.prototype, 'customSettingComponents', function (items) {
|
||||
* // You can access the AdminPage instance with `this` to access its `settings` property.
|
||||
*
|
||||
* // Prefixing the key with your extension ID is recommended to avoid collisions.
|
||||
* items.add('my-ext.setting-component', (attrs) => {
|
||||
* return (
|
||||
* <div className={attrs.className}>
|
||||
* <label>{attrs.label}</label>
|
||||
* {attrs.help && <p className="helpText">{attrs.help}</p>}
|
||||
*
|
||||
* My setting component!
|
||||
* </div>
|
||||
* );
|
||||
* })
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
customSettingComponents(): ItemList<(attributes: CommonSettingsItemOptions) => Mithril.Children> {
|
||||
const items = new ItemList<(attributes: CommonSettingsItemOptions) => Mithril.Children>();
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* `buildSettingComponent` takes a settings object and turns it into a component.
|
||||
* Depending on the type of input, you can set the type to 'bool', 'select', or
|
||||
@@ -284,77 +137,9 @@ export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAt
|
||||
return entry.call(this);
|
||||
}
|
||||
|
||||
const customSettingComponents = this.customSettingComponents();
|
||||
const { setting, ...attrs } = entry;
|
||||
|
||||
const { setting, help, type, label, ...componentAttrs } = entry;
|
||||
|
||||
const value = this.setting(setting)();
|
||||
|
||||
const [inputId, helpTextId] = [generateElementId(), generateElementId()];
|
||||
|
||||
let settingElement: Mithril.Children;
|
||||
|
||||
// Typescript being Typescript
|
||||
// https://github.com/microsoft/TypeScript/issues/14520
|
||||
if ((BooleanSettingTypes as readonly string[]).includes(type)) {
|
||||
return (
|
||||
// TODO: Add aria-describedby for switch help text.
|
||||
//? Requires changes to Checkbox component to allow providing attrs directly for the element(s).
|
||||
<div className="Form-group">
|
||||
<Switch state={!!value && value !== '0'} onchange={this.settings[setting]} {...componentAttrs}>
|
||||
{label}
|
||||
</Switch>
|
||||
{help ? <div className="helpText">{help}</div> : null}
|
||||
</div>
|
||||
);
|
||||
} else if ((SelectSettingTypes as readonly string[]).includes(type)) {
|
||||
const { default: defaultValue, options, ...otherAttrs } = componentAttrs;
|
||||
|
||||
settingElement = (
|
||||
<Select
|
||||
id={inputId}
|
||||
aria-describedby={helpTextId}
|
||||
value={value || defaultValue}
|
||||
options={options}
|
||||
onchange={this.settings[setting]}
|
||||
{...otherAttrs}
|
||||
/>
|
||||
);
|
||||
} else if (type === ImageUploadSettingType) {
|
||||
const { value, ...otherAttrs } = componentAttrs;
|
||||
|
||||
settingElement = <UploadImageButton value={this.settings[setting]} {...otherAttrs} />;
|
||||
} else if (customSettingComponents.has(type)) {
|
||||
return customSettingComponents.get(type)({ setting, help, label, ...componentAttrs });
|
||||
} else {
|
||||
componentAttrs.className = classList('FormControl', componentAttrs.className);
|
||||
|
||||
if ((TextareaSettingTypes as readonly string[]).includes(type)) {
|
||||
settingElement = <textarea id={inputId} aria-describedby={helpTextId} bidi={this.setting(setting)} {...componentAttrs} />;
|
||||
} else {
|
||||
let Tag: VnodeElementTag = 'input';
|
||||
|
||||
if (type === ColorPreviewSettingType) {
|
||||
Tag = ColorPreviewInput;
|
||||
} else {
|
||||
componentAttrs.type = type;
|
||||
}
|
||||
|
||||
settingElement = <Tag id={inputId} aria-describedby={helpTextId} bidi={this.setting(setting)} {...componentAttrs} />;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="Form-group">
|
||||
{label && <label for={inputId}>{label}</label>}
|
||||
{help && (
|
||||
<div id={helpTextId} className="helpText">
|
||||
{help}
|
||||
</div>
|
||||
)}
|
||||
{settingElement}
|
||||
</div>
|
||||
);
|
||||
return <FormGroup bidi={this.setting(setting)} {...attrs} />;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -3,7 +3,7 @@ import Button from '../../common/components/Button';
|
||||
import EditCustomCssModal from './EditCustomCssModal';
|
||||
import EditCustomHeaderModal from './EditCustomHeaderModal';
|
||||
import EditCustomFooterModal from './EditCustomFooterModal';
|
||||
import UploadImageButton from './UploadImageButton';
|
||||
import UploadImageButton from '../../common/components/UploadImageButton';
|
||||
import AdminPage from './AdminPage';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import type Mithril from 'mithril';
|
||||
|
@@ -36,6 +36,7 @@ import './utils/withAttr';
|
||||
import './utils/focusTrap';
|
||||
import './utils/isDark';
|
||||
import './utils/KeyboardNavigatable';
|
||||
import './utils/generateElementId';
|
||||
|
||||
import './models/Notification';
|
||||
import './models/User';
|
||||
@@ -76,6 +77,8 @@ import './components/TextEditorButton';
|
||||
import './components/Tooltip';
|
||||
import './components/AutocompleteDropdown';
|
||||
import './components/GambitsAutocompleteDropdown';
|
||||
import './components/UploadImageButton';
|
||||
import './components/FormGroup';
|
||||
|
||||
import './helpers/fullTime';
|
||||
import './components/Avatar';
|
||||
|
259
framework/core/js/src/common/components/FormGroup.tsx
Normal file
259
framework/core/js/src/common/components/FormGroup.tsx
Normal file
@@ -0,0 +1,259 @@
|
||||
import Component from '../Component';
|
||||
import generateElementId from '../utils/generateElementId';
|
||||
import Switch from './Switch';
|
||||
import Select from './Select';
|
||||
import UploadImageButton from './UploadImageButton';
|
||||
import classList from '../utils/classList';
|
||||
import ColorPreviewInput from './ColorPreviewInput';
|
||||
import Stream from '../utils/Stream';
|
||||
import ItemList from '../utils/ItemList';
|
||||
import type { IUploadImageButtonAttrs } from './UploadImageButton';
|
||||
import type { ComponentAttrs } from '../Component';
|
||||
import type Mithril from 'mithril';
|
||||
|
||||
/**
|
||||
* A type that matches any valid value for the `type` attribute on an HTML `<input>` element.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-type
|
||||
*
|
||||
* Note: this will be exported from a different location in the future.
|
||||
*
|
||||
* @see https://github.com/flarum/core/issues/3039
|
||||
*/
|
||||
export type HTMLInputTypes =
|
||||
| 'button'
|
||||
| 'checkbox'
|
||||
| 'color'
|
||||
| 'date'
|
||||
| 'datetime-local'
|
||||
| 'email'
|
||||
| 'file'
|
||||
| 'hidden'
|
||||
| 'image'
|
||||
| 'month'
|
||||
| 'number'
|
||||
| 'password'
|
||||
| 'radio'
|
||||
| 'range'
|
||||
| 'reset'
|
||||
| 'search'
|
||||
| 'submit'
|
||||
| 'tel'
|
||||
| 'text'
|
||||
| 'time'
|
||||
| 'url'
|
||||
| 'week';
|
||||
|
||||
export interface CommonFieldOptions extends Mithril.Attributes {
|
||||
label?: Mithril.Children;
|
||||
help?: Mithril.Children;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate an HTML input element.
|
||||
*/
|
||||
export interface HTMLInputFieldComponentOptions extends CommonFieldOptions {
|
||||
/**
|
||||
* Any valid HTML input `type` value.
|
||||
*/
|
||||
type: HTMLInputTypes;
|
||||
}
|
||||
|
||||
const BooleanSettingTypes = ['bool', 'checkbox', 'switch', 'boolean'] as const;
|
||||
const SelectSettingTypes = ['select', 'dropdown', 'selectdropdown'] as const;
|
||||
const TextareaSettingTypes = ['textarea'] as const;
|
||||
const ColorPreviewSettingType = 'color-preview' as const;
|
||||
const ImageUploadSettingType = 'image-upload' as const;
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate a Switch.
|
||||
*/
|
||||
export interface SwitchFieldComponentOptions extends CommonFieldOptions {
|
||||
type: typeof BooleanSettingTypes[number];
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate a Select dropdown.
|
||||
*/
|
||||
export interface SelectFieldComponentOptions extends CommonFieldOptions {
|
||||
type: typeof SelectSettingTypes[number];
|
||||
/**
|
||||
* Map of values to their labels
|
||||
*/
|
||||
options: { [value: string]: Mithril.Children };
|
||||
default: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate a Textarea.
|
||||
*/
|
||||
export interface TextareaFieldComponentOptions extends CommonFieldOptions {
|
||||
type: typeof TextareaSettingTypes[number];
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid options for the setting component builder to generate a ColorPreviewInput.
|
||||
*/
|
||||
export interface ColorPreviewFieldComponentOptions extends CommonFieldOptions {
|
||||
type: typeof ColorPreviewSettingType;
|
||||
}
|
||||
|
||||
export interface ImageUploadFieldComponentOptions extends CommonFieldOptions, IUploadImageButtonAttrs {
|
||||
type: typeof ImageUploadSettingType;
|
||||
}
|
||||
|
||||
export interface CustomFieldComponentOptions extends CommonFieldOptions {
|
||||
type: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* All valid options for the setting component builder.
|
||||
*/
|
||||
export type FieldComponentOptions =
|
||||
| HTMLInputFieldComponentOptions
|
||||
| SwitchFieldComponentOptions
|
||||
| SelectFieldComponentOptions
|
||||
| TextareaFieldComponentOptions
|
||||
| ColorPreviewFieldComponentOptions
|
||||
| ImageUploadFieldComponentOptions
|
||||
| CustomFieldComponentOptions;
|
||||
|
||||
export type IFormGroupAttrs = ComponentAttrs &
|
||||
FieldComponentOptions & {
|
||||
bidi?: Stream<any>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds a field component based on the provided attributes.
|
||||
* Depending on the type of input, you can set the type to 'bool', 'select', or
|
||||
* any standard <input> type. Any values inside the 'extra' object will be added
|
||||
* to the component as an attribute.
|
||||
*
|
||||
* Alternatively, you can pass a callback that will be executed in ExtensionPage's
|
||||
* context to include custom JSX elements.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* <FormGroup key="acme.checkbox"
|
||||
* label={app.translator.trans('acme.admin.setting_label')}
|
||||
* type="bool"
|
||||
* help={app.translator.trans('acme.admin.setting_help')}
|
||||
* className="Setting-item" />
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* <FormGroup key="acme.select"
|
||||
* label={app.translator.trans('acme.admin.setting_label')}
|
||||
* type="select"
|
||||
* options={{
|
||||
* 'option1': 'Option 1 label',
|
||||
* 'option2': 'Option 2 label',
|
||||
* }}
|
||||
* default="option1" />
|
||||
*/
|
||||
export default class FormGroup<CustomAttrs extends IFormGroupAttrs = IFormGroupAttrs> extends Component<CustomAttrs> {
|
||||
view(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children {
|
||||
const customFieldComponents = this.customFieldComponents();
|
||||
|
||||
const { help, type, label, bidi, ...componentAttrs } = this.attrs;
|
||||
|
||||
// TypeScript being TypeScript
|
||||
const attrs = componentAttrs as unknown as Omit<IFormGroupAttrs, 'bidi' | 'label' | 'help' | 'type'>;
|
||||
|
||||
const value = bidi ? bidi() : null;
|
||||
|
||||
const [inputId, helpTextId] = [generateElementId(), generateElementId()];
|
||||
|
||||
let settingElement: Mithril.Children;
|
||||
|
||||
// Typescript being Typescript
|
||||
// https://github.com/microsoft/TypeScript/issues/14520
|
||||
if ((BooleanSettingTypes as readonly string[]).includes(type)) {
|
||||
return (
|
||||
// TODO: Add aria-describedby for switch help text.
|
||||
//? Requires changes to Checkbox component to allow providing attrs directly for the element(s).
|
||||
<div className="Form-group">
|
||||
<Switch state={!!value && value !== '0'} onchange={bidi} {...attrs}>
|
||||
{label}
|
||||
</Switch>
|
||||
{help ? <div className="helpText">{help}</div> : null}
|
||||
</div>
|
||||
);
|
||||
} else if ((SelectSettingTypes as readonly string[]).includes(type)) {
|
||||
const { default: defaultValue, options, ...otherAttrs } = attrs;
|
||||
|
||||
settingElement = (
|
||||
<Select id={inputId} aria-describedby={helpTextId} value={value || defaultValue} options={options} onchange={bidi} {...otherAttrs} />
|
||||
);
|
||||
} else if (type === ImageUploadSettingType) {
|
||||
const { value, ...otherAttrs } = attrs;
|
||||
|
||||
settingElement = <UploadImageButton value={bidi} {...otherAttrs} />;
|
||||
} else if (customFieldComponents.has(type)) {
|
||||
return customFieldComponents.get(type)(this.attrs);
|
||||
} else {
|
||||
attrs.className = classList('FormControl', attrs.className);
|
||||
|
||||
if ((TextareaSettingTypes as readonly string[]).includes(type)) {
|
||||
settingElement = <textarea id={inputId} aria-describedby={helpTextId} bidi={bidi} {...attrs} />;
|
||||
} else {
|
||||
let Tag: VnodeElementTag = 'input';
|
||||
|
||||
if (type === ColorPreviewSettingType) {
|
||||
Tag = ColorPreviewInput;
|
||||
} else {
|
||||
attrs.type = type;
|
||||
}
|
||||
|
||||
settingElement = <Tag id={inputId} aria-describedby={helpTextId} bidi={bidi} {...attrs} />;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="Form-group">
|
||||
{label && <label for={inputId}>{label}</label>}
|
||||
{help && (
|
||||
<div id={helpTextId} className="helpText">
|
||||
{help}
|
||||
</div>
|
||||
)}
|
||||
{settingElement}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of extension-defined custom setting components to be available.
|
||||
*
|
||||
* The ItemList key represents the value for the `type` attribute.
|
||||
* All attributes passed are provided as arguments to the function added to the ItemList.
|
||||
*
|
||||
* ItemList priority has no effect here.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* extend(AdminPage.prototype, 'customFieldComponents', function (items) {
|
||||
* // You can access the AdminPage instance with `this` to access its `settings` property.
|
||||
*
|
||||
* // Prefixing the key with your extension ID is recommended to avoid collisions.
|
||||
* items.add('my-ext.setting-component', (attrs) => {
|
||||
* return (
|
||||
* <div className={attrs.className}>
|
||||
* <label>{attrs.label}</label>
|
||||
* {attrs.help && <p className="helpText">{attrs.help}</p>}
|
||||
*
|
||||
* My setting component!
|
||||
* </div>
|
||||
* );
|
||||
* })
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
customFieldComponents(): ItemList<(attributes: CustomAttrs) => Mithril.Children> {
|
||||
const items = new ItemList<(attributes: CustomAttrs) => Mithril.Children>();
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user