OptionsSchema API

Options schema provides an interface to define various types of profile options with descriptions, hints, validation, and visibility controls.

Schema is an array of option configuration objects [{...optionConfig}, ...], which will be used to create options UI for the user, and then construct the final options object that will be attached to operation and sent to the processor.

Example schema:

[
    {name: 'foo', type: 'boolean', default: false},
    {name: 'bar', type: 'string', default: 'baz'},
];

Inside processor:

module.exports = async (payload) {
    payload.options.foo; // false, or true if configured by user
    payload.options.bar; // 'baz', or other string configured by user
}

Base

Every option below extends this interface, so any of these properties can be defined on any option going forward, unless stated otherwise.

interface OptionBase {
    name: string;
    title?: string;
    hint?: string | OptionToStringFn;
    description?: string | OptionToStringFn;
    isDisabled?: (value: any, options: object, path: (string | number)[]) => boolean;
    isHidden?: (value: any, options: object, path: (string | number)[]) => boolean;
}

type OptionToStringFn = (value: any, options: object, path: (string | number)[]) => string | number | null | undefined;

name

Type: string required

Every options has to have a name. This is the property name under which it'll be available on the final serialized options object.


title

Type: string optional

Option title for humans.


hint

Type: string | OptionToStringFn optional

A short option hint that will be displayed right next to the input.

This should be a short, 10 characters and less string about option's units, or other relevant nugget of info about the given option, or its current value.

It can also be a function that determines hint dynamically based on a value of the current option, or any other option in options schema.

Example

This option is a string that allows math expression such as 1280 * 720 to be used to enter a number of pixels, and uses hint to display it's current value in megapixels:

{
    name: 'pixels',
    type: 'string',
    hint: (value) => {
        try {
            return `${(eval(value) / (1024 * 1024)).toFixed(1)}MPx`;
        } catch {
            return 'invalid';
        }
    }
}

Just remember that in this case the processor will receive the expression, and should do the eval() step to get the value as well.


description

Type: string | OptionToStringFn optional

Describe the details of a given option. This can be a string of any length (with reason), and it can include HTML tags for formatting.

You can also pass a function to generate description dynamically based on current option's value, or a value of any other option in schema.

Example

Describe option's behavior change depending on another option:

description: (value, options) => options.foo ? `Does this` : `Does that.`,

isDisabled

Type: (value: any, options: object, path: (string | number)[]) => boolean optional

A function to determine wether the option UI should be disabled. Params:

value

Type: any

Current option value.

options

Type: object

Options object with current values.

path

Type: (string | number)[]

Path to the current option inside the options object. For example, if given option is the 1st member of a list foo, it'll be ['foo', 0].

Example

Disable option bar if foo is unchecked.

[
    {name: 'foo', type: 'boolean', default: true},
    {
        name: 'bar',
        type: 'boolean',
        default: true,
        isDisabled: (_, options) => !options.foo,
    },
];

isHidden

Type: (value: any, options: object, path: (string | number)[]) => boolean optional

A function to optionally hide the option UI. This is very useful for creating option categories, and hiding options that are not relevant unless some other option is enabled. Params:

value

Type: any

Current option value.

options

Type: object

Options object with current values.

path

Type: (string | number)[]

Path to the current option inside the options object. For example, if given option is the bar property on a namespace foo, it'll be ['foo', 'bar'].

Example

Hide option bar if foo is unchecked.

[
    {name: 'foo', type: 'boolean', default: true},
    {
        name: 'bar',
        type: 'boolean',
        default: true,
        isHidden: (_, options) => !options.foo,
    },
];

Boolean

interface OptionBoolean extends OptionBase {
    type: 'boolean';
    default?: boolean;
}

A boolean option that creates a checkbox UI.


type

Type: 'boolean' required

Setting type to 'boolean' creates a boolean option.


default

Type: boolean
Default: false

A default value.

Number

interface OptionNumber extends OptionBase {
    type: 'number';
    default?: number;
    kind?: 'integer' | 'float';
    nullable?: boolean;
    min?: number;
    max?: number;
    step?: number;
    cols?: number;
    softMin?: boolean;
    softMax?: boolean;
    steps?: number[];
}

A number option that can create a:

Input typeCondition
short input fielddefault
slidermin, max, and step have to be configured.
slider + short input fieldmin, max, step, and either softMin or softMax has to be configured.

type

Type: 'number' required

Setting type to 'number' creates a number option.


default

Type: number optional
Default: undefined

A default number. Required if you don't wish for this option's value to be nullable.


kind

Type: 'integer' | 'float' optional
Default: 'integer'

What kind of numbers are you interested in.


nullable

Type: boolean optional
Default: false if default is defined, true otherwise

When enabled, empty input box results in null value.
When disabled, empty input box means validation error and previous valid value will be kept.

You have to specify default value if you're manually disabling this.


min/max

Type: number optional

Min and max number limits. You don't have to define both, but in case both are, min has to be smaller than max.


step

Type: number optional

Forces the number to be incremented and decremented by this amount.

Combine with min and max to create a slider UI.


cols

Type: number optional

When input field is displayed, this sets the suggested number of character columns it should span, similar to HTML textareas.


softMin/softMax

Type: boolean optional

Tells Drovp that the min/max limits are only suggestions (maybe you want to render a slider), but the user should be allowed to set the number below/above these limits.

Combine with min, max, and step to add a small input box next to the slider.


steps

Type: number[] optional

Instead of min, max, and step, you can pass a specific list of steps to be used as values of a slider.

Example:

steps: [0, 1, 3, 5, 7, 11],

String

interface OptionString extends OptionBase {
    type: 'string';
    default?: string;
    rows?: number;
    cols?: number;
    min?: number;
    max?: number;
    validator?: (value: string, options: object, path: (string | number)[]) => boolean;
    asyncValidator?: (value: string, options: object, path: (string | number)[]) => Promise<boolean>;
    asyncValidatorDebounce?: number;
    validationDependencies?: string[];
}

A string option that can create an input, or a textarea field, depending on how it's configured.


type

Type: 'string' required

Setting type to 'string' creates a string option.


default

Type: string optional
Default: '' (empty string)

A default string.


rows

Type: number optional
Default: 1

How many lines should the text input occupy. Bump it for inputs where you expect more text than usual, or you want to support new lines.

Anything above 1 creates a textarea.


cols

Type: number optional

Limits the horizontal size of the input. This is capped as to not overflow parent elements.


min/max

Type: number optional

Min and max string length limits.


validator

Type: (value: string, options: object, path: (string | number)[]) => boolean optional

A synchronous validator. New option value will not be assigned until it passes this validation.

value

Type: string

Input value.

options

Type: object

Current options object.

path

Type: (string | number)[]

Path to this option on an options object. Example: ['foo', 0, 'bar']

Return

Validator has to return a boolean that decides wether the input value should be assigned. You can also throw with a message describing what is wrong.


asyncValidator

Type: (value: string, options: object, path: (string | number)[]) => Promise<boolean> optional

An asynchronous validator. This is currently just for cosmetic purposes, and doesn't prevent the value from being assigned.

value

Type: string

Input value.

options

Type: object

Current options object.

path

Type: (string | number)[]

Path to this option on an options object. Example: ['foo', 0, 'bar']

Return

Validator has to resolve with true if value is valid, anything falsy if it's not, or throw an error with a message of what is wrong with the value.


asyncValidatorDebounce

Type: number optional
Default: 500

Debounce time for async validator. Value is in milliseconds.


validationDependencies

Type: string[] optional

Names or "dot paths" to options that validation depends on, if any. Declaring this tells Drovp that this option has to be re-validated when any of these dependencies changes.

Example

For options object that looks like this:

{
    foo: 'value',
    bar: {baz: 'value'},
    'dot.prop': 'value'
}

Dependencies array can be:

validationDependencies: ['foo', 'bar.baz', 'dot\\.prop'];

Path

interface OptionPath extends OptionBase {
    type: 'path';
    default?: string;
    kind?: 'file' | 'directory';
    filters?: FileFilter[];
    formatSelection?: (newValue: string, oldValue: string) => string;
}

A file or a directory path option which creates a filesystem dialogue input UI.


type

Type: 'path' required

Setting type to 'path' creates a path option.


default

Type: string optional
Default: '' (empty string)

A default path if any.


kind

Type: 'file' | 'directory' optional
Default: file

Wether you're interested in a file or a directory path.


filters

Type: FileFilter[] optional

An array with Electron FileFilter objects.

Example:

[
    {name: 'Images', extensions: ['jpg', 'png', 'gif']},
    {name: 'Movies', extensions: ['mkv', 'avi', 'mp4']},
    {name: 'Custom File Type', extensions: ['as']},
    {name: 'All Files', extensions: ['*']},
];

formatSelection

Type: (newValue: string, oldValue: string) => string optional

Allows formatting the output of the path selector. It's used for example by the @drovp/save-as-path helper module to extract the filename template of the old path, and append it to the new directory path.

Color

interface OptionColor extends OptionBase {
    type: 'color';
    default?: string;
    formatSelection?: (newValue: string, oldValue: string) => string;
}

An option that creates a string UI input. This is currently very basic. It doesn't provide any validation, only ability to use default browser color input UI to select an #rrggbb value.


type

Type: 'color' required

Setting type to 'color' creates a color option.

Select

interface OptionSelect extends OptionBase {
    type: 'select';
    default?: string | number | (string | number)[];
    options: (string | number)[] | {[x: string]: string};
    max?: number;
}

A string, number, or an array of strings or numbers to be selected out of configured options. According to the configuration and verboseness of the options, this creates checkboxes, radios, or a dropdown UI (depending on what is most appropriate and fits the best on the screen).


type

Type: 'select' required

Setting type to 'select' creates a select option.


formatSelection

Type: (newValue: string, oldValue: string) => string optional

Allows formatting the output of the built in color picker, which always returns html hex color with hash.


default

Type: number | string | (number | string)[] optional

Default can be any value included in the options config documented below, or an array of those.

Setting default to an array makes this a multi-select input (user will be able to select multiple values). Naturally, the resulting value on the options object passed to the processor will also be an array.

Default array can be empty.

Example
{
    name: 'foo',
    type: 'select',
    options: ['foo', 'bar', 'baz'],
    default: 'foo', // allows switching between options above
    default: [], // allows selecting zero or more options above
}

options

Type: (string | number)[] | {[x: string]: string} required

Defines select options. It can be an array of numbers, strings, or an object map of values and titles.

Example
options: [1, 5, 10],
options: ['foo', 'bar', 'baz'],
options: {foo: 'Foo', bar: 'Bar', baz: 'Baz'},

nullable

Type: boolean optional
Default: false if default is defined, true otherwise

For single value selects, enabling this will allow the value to be set to null.

You have to specify default value if you're manually disabling this.


max

Type: number optional
Default: Infinity

For multi-value selects, setting this will limit the max number of options that can be selected.

List

interface OptionList extends OptionBase {
    type: 'list';
    default?: (string | number)[];
    schema: Omit<OptionString | OptionPath | OptionNumber | OptionSelect, keyof OptionBase>;
}

A dynamic list of strings, numbers, or paths.


type

Type: 'list' required

Setting type to 'list' creates a list option.


default

Type: (number | string)[] optional
Default: []

List with default values.


schema

Type: Omit<OptionString | OptionPath | OptionNumber | OptionSelect, keyof OptionBase> required

Schema of an item that should populate the list.

This is a simplified version of either String, Path, Number, or a Select schema without any of the Base properties (name, title, hint, description, isDisabled, isHidden).

Example

List of strings

{
    name: 'foos',
    type: 'list',
    schema: {type: 'string'}
}

Namespace

interface OptionNamespace extends OptionBase {
    type: 'namespace';
    schema: OptionSchema[];
}

A namespace to nest further options under.


type

Type: 'namespace' required

Setting type to 'namespace' creates a namespace option.


schema

Type: OptionSchema[] required

An array of option schemas.

Example

Providing this schema:

{
    name: 'foo',
    type: 'namespace',
    schema: [
        {name: 'bar', type: 'boolean', default: false},
        {name: 'baz', type: 'number', default: 0},
    ],
}

Will create this option object:

{
    foo: {
        bar: false,
        baz: 0,
    }
}

Collection

interface OptionCollection extends OptionBase {
    type: 'collection';
    schema: OptionSchema[];
    default?: {[key: string]: any}[];
    itemTitle?: string;
}

A dynamic list of option groups.


type

Type: 'collection' required

Setting type to 'collection' creates a collection option.


schema

Type: OptionSchema[] required

An array of option schemas.

Example

Providing this schema:

{
    name: 'foo',
    type: 'collection',
    schema: [
        {name: 'bar', type: 'boolean', default: false},
        {name: 'baz', type: 'number', default: 0},
    ],
}

Will create this option object (note that foo is an array of objects here):

{
    foo: [
        {
            bar: false,
            baz: 0,
        },
    ],
}

default

Type: {[key: string]: any}[] optional
Default: []

List with default collections.


itemTitle

Type: string optional

A singular name for each item in the collection. This is just so that + Add button can display a more descriptive + Add ${itemTitle}.

Divider

interface OptionDivider extends Pick<OptionBase, 'title' | 'description' | 'isHidden'> {
    type: 'divider';
}

A decorative divider element. Can contain title, description, and define isHidden determination function.

This is purely decorative and won't add anything to the final options object.


type

Type: 'divider' required

Setting type to 'divider' creates a divider element.

Category

interface OptionCategory extends Pick<OptionBase, 'isHidden'> {
    type: 'divider';
    options: string[] | object | ((options: object) => string[] | object);
}

A decorative category selector to help separate big option schemas into tab-able categories. The only thing available from Base is isHidden determination function.

This will create a non-persistent "category" property with a given name on a working options object that won't be included on the final options object sent to the processor.

This is useful to define isHidden determination function on other options that hides a given option when its category is not selected. See @drovp/image-optimizer processor configuration for an advanced usage example.


type

Type: 'divider' required

Setting type to 'divider' creates a divider element.


options

Type: string[] | object | ((options: OptionsData) => string[] | object) required

Options can be:

Example

A category and options that hide depending on its value:

[
    {
        name: 'category',
        type: 'category',
        options: {foo: 'Foo', bar: 'Bar'},
    },
    {
        name: 'foo',
        type: 'boolean',
        isHidden: ({category}) => category !== 'foo',
    },
    {
        name: 'bar',
        type: 'boolean',
        isHidden: ({category}) => category !== 'bar',
    },
],

And a resulting options object:

{foo: false, bar: false}