import { consoleInfo, getNode, objectToAttributes, trigger, uniqueId, isObject, isEmptyObject, spacerClasses } from '@gravityforms/utils';
import { helpTextTemplate } from '../HelpText/index';
import { labelTemplate } from '../Label/index';
/**
* @function selectTemplate
* @description A select component to use wherever a simple select is needed.
*
* @since 2.2.0
*
* @param {object} options The options for the select component.
* @param {object} options.customAttributes Custom attributes for the select.
* @param {Array} options.customClasses An array of additional classes for the select.
* @param {boolean} options.disabled If select is disabled.
* @param {string} options.helpTextAttributes Custom attribute for the help text.
* @param {string} options.helpTextPosition The position of the help text. Above or below.
* @param {string} options.id Optional id. Auto generated if not passed.
* @param {string} options.initialValue Initial value for the select.
* @param {string} options.labelAttributes Any custom attributes for the label.
* @param {string} options.name The name attribute for the select.
* @param {Array} options.options An array of options, each option as an object in the following structure:
* {
* customOptionAttrs: {}, // Key-value pairs of custom attributes.
* customOptionClasses: [], // Array of strings of custom classes.
* label: '', // Label as a string.
* value: '', // Value as a string.
* }
* @param {string} options.size The select size. Small (size-s), Regular (size-r), or extra large (size-xl).
* @param {string|number|Array} options.spacing The spacing for the component, string or array.
* @param {string} options.theme The theme of the select.
* @param options.customAttributes.optionGroup
* @param options.customAttributes.groups
* @param {object} options.wrapperAttributes Any custom attributes for the wrapper element.
* @param {Array} options.wrapperClasses Any custom classes for the wrapper element.
* @param {object} options.wrapperTagName Tag to use for the textarea wrapper. Defaults to 'div'.
* @param {string} options.ariaLabel The aria-label text for the select element.
*
* @return {string} The html for the select component.
* @example
* import { selectTemplate } from '@gravityforms/components/html/admin/elements/Select';
*
* function Example() {
* const selectTemplateHTML = selectTemplate( options );
* document.body.insertAdjacentHTML( 'beforeend', selectTemplateHTML );
* }
*
*/
export const selectTemplate = ( {
customAttributes = {},
customClasses = [],
disabled = false,
helpTextAttributes = {},
helpTextPosition = 'below',
id = uniqueId( 'gform-admin-select' ),
initialValue = '',
labelAttributes = {},
name = '',
options = [],
size = 'size-r',
spacing = '',
theme = 'cosmos',
wrapperAttributes = {},
wrapperClasses = [],
wrapperTagName = 'div',
ariaLabel = '',
} ) => {
const inputId = id || uniqueId( 'gform-select' );
const helpTextId = `${ inputId }-help-text`;
const wrapperAttrs = objectToAttributes( {
...wrapperAttributes,
class: [
'gform-input-wrapper',
`gform-input-wrapper--theme-${ theme ? theme : 'cosmos' }`,
'gform-input-wrapper--select',
`gform-input-wrapper--${ size ? size : 'size-r' }`,
disabled && 'gform-input-wrapper--disabled',
...Object.keys( spacerClasses( spacing ) ),
...wrapperClasses,
],
} );
const selectObj = {
...customAttributes,
class: [
'gform-select',
...customClasses,
],
id: inputId,
name,
value: initialValue,
};
if ( disabled ) {
selectObj.disabled = true;
}
if ( helpTextAttributes.content ) {
selectObj[ 'aria-describedby' ] = helpTextId;
}
const selectAttrs = objectToAttributes( selectObj );
if ( ariaLabel ) {
selectObj[ 'aria-label' ] = ariaLabel;
}
const labelObj = {
...labelAttributes,
htmlFor: inputId,
};
const helpTextObj = {
...helpTextAttributes,
id: helpTextId,
};
const getSubOptions = ( choices = [] ) => {
if ( isObject( choices ) && ! isEmptyObject( choices ) ) {
return Object.entries( choices ).map( ( [ key ] ) => choices[ key ] );
}
return choices;
};
const getOption = ( {
customOptionAttributes = {},
customOptionClasses = [],
label = '',
value = '',
} ) => {
const optionObj = {
...customOptionAttributes,
class: [
'gform-select__option',
...customOptionClasses,
],
id: inputId,
name,
value,
};
return `
<option ${ objectToAttributes( optionObj ) }>
${ label }
</option>
`;
};
const getOptions = options.map( ( data ) => {
const subOptions = getSubOptions( data.choices );
return subOptions.length ? `
<optgroup label="${ data.label }">
${ subOptions.map( ( subData ) => getOption( subData ) ) }
</optgroup>
` : getOption( data );
} );
const helpText = helpTextTemplate( helpTextObj );
return `
<${ wrapperTagName } ${ wrapperAttrs }>
${ labelTemplate( labelObj ) }
${ helpTextPosition === 'above' ? helpText : '' }
<div class="gform-select__wrapper">
<select ${ selectAttrs }>
${ getOptions }
</select>
</div>
${ helpTextPosition === 'below' ? helpText : '' }
</${ wrapperTagName }>
`;
};
/**
* @class Select
* @description A select component to use wherever a simple select is needed.
*
* @since 2.2.0
*
* @param {object} options Component options.
* @param {object} options.customAttributes Custom attributes for the component.
* @param {string|Array|object} options.customClasses Custom classes for the component.
* @param {boolean} options.disabled If select is disabled.
* @param {string} options.helpTextAttributes Custom attribute for the help text.
* @param {string} options.helpTextPosition The position of the help text. Above or below.
* @param {string} options.id Optional id. Auto generated if not passed.
* @param {string} options.initialValue Initial value for the select.
* @param {string} options.labelAttributes Any custom attributes for the label.
* @param {string} options.name The name attribute for the select.
* @param {Array} options.options An array of options, each option as an object in the following structure:
* {
* customOptionAttrs: {}, // Key-value pairs of custom attributes.
* customOptionClasses: [], // Array of strings of custom classes.
* label: '', // Label as a string.
* value: '', // Value as a string.
* }
* @param {string} options.size The select size. Regular (size-r) or extra large (size-xl).
* @param {boolean} options.rendered Is this select already rendered in the dom, eg by php?
* @param {boolean} options.renderOnInit Render the select on init of the class?
* @param {string|number|Array|object} options.spacing The spacing for the component, as a string, number, array, or object.
* @param {string} options.theme The theme of the select.
* @param {string} options.target The target to render to. Any valid css selector string.
* @param {string} options.targetPosition The insert position for the target.
* @param {object} options.wrapperAttributes Custom attributes for the wrapper element.
* @param {string|Array|object} options.wrapperClasses Custom classes for the wrapper element.
* @param {object} options.wrapperTagName Tag to use for the textarea wrapper. Defaults to `div`.
* @param {string} options.ariaLabel The aria-label text for the select element.
*
* @return {Class} The select instance.
* @example
* import Select from '@gravityforms/components/html/admin/elements/Select';
*
* function Example() {
* const selectInstance = new Select( {
* name: 'select-name'
* options: { [
* {
* label: 'Option 1',
* value: 'option-1',
* },
* {
* label: 'Option 2',
* value: 'option-2',
* },
* {
* label: 'Option 3',
* value: 'option-3',
* },
* ] },
* target: '#select-target'
* } );
* }
*
*/
export default class Select {
/**
* @description The class constructor.
*
* @param {object} options The options object. Check defaults for descriptions.
*/
constructor( options = {} ) {
this.options = {};
Object.assign(
this.options,
{
customAttributes: {},
customClasses: [],
disabled: false,
helpTextAttributes: {},
helpTextPosition: 'below',
id: '',
initialValue: '',
labelAttributes: {},
name: '',
options: [],
rendered: false,
renderOnInit: true,
size: 'size-r',
spacing: '',
theme: 'cosmos',
target: '',
targetPosition: 'afterbegin',
wrapperAttributes: {},
wrapperClasses: [],
wrapperTagName: 'div',
ariaLabel: '',
},
options
);
trigger( { event: 'gform/select/pre_init', native: false, data: { instance: this } } );
this.elements = {};
if ( this.options.renderOnInit ) {
this.init();
}
}
/**
* @function render
* @description Renders the component into the dom.
*
* @since 1.0.0
*
* @return {void}
*/
render() {
const { rendered, target, targetPosition } = this.options;
if ( ! rendered ) {
const renderTarget = getNode( target, document, true );
renderTarget.insertAdjacentHTML(
targetPosition,
selectTemplate( this.options )
);
}
this.elements.select = getNode( `#${ this.options.id }`, document, true );
}
/**
* @function init
* @description Initialize the component.
*
* @since 1.0.0
*
* @return {void}
*/
init() {
this.render();
trigger( { event: 'gform/select/post_render', native: false, data: { instance: this } } );
consoleInfo( `Gravity Forms Admin: Initialized select component.` );
}
}