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 type | Condition |
---|---|
short input field | default |
slider | min , max , and step have to be configured. |
slider + short input field | min , 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:
- an array of keywords (ugly)
- a
keyword: 'Title'
object map - a dynamic function that accepts current options object, and returns one of the above
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}