outsystems-ui
Version:
OutSystems UI Framework
256 lines (224 loc) • 9.13 kB
text/typescript
/* eslint-disable @typescript-eslint/no-unused-vars, no-extra-boolean-cast */
namespace Providers.OSUI.Dropdown.VirtualSelect {
/**
* Class that represents the custom configurations received by the Dropdown.
*
* @export
* @class AbstractVirtualSelectConfig
* @extends {AbstractDropdownConfig}
*/
export abstract class AbstractVirtualSelectConfig extends OSFramework.OSUI.Patterns.Dropdown
.AbstractDropdownConfig {
// Store grouped options
private _groupedOptionsList: GroupDropDownOption[];
// Store the Provider Options
private _providerOptions: VirtualSelectOpts;
// Store configs set using extensibility
protected providerExtendedOptions: VirtualSelectOpts;
public ElementId: string;
public NoOptionsText: string;
public NoResultsText: string;
public OptionsList: DropDownOption[];
public PopupDropboxBreakpoint: string;
public Prompt: string;
public SearchPrompt: string;
public ShowDropboxAsPopup = true;
public StartingSelection: DropDownOption[];
// Method used to check if an image or an icon should be added to the given option
private _checkForFigType(option: DropDownOption): Enum.FigureType {
let hasImage = Enum.FigureType.None;
// Check if image_url_or_class filed has info
if (!!option && !!option.image_url_or_class) {
// The given info doesn't have spaces on it, check if it's a valid URL
hasImage = OSFramework.OSUI.Helper.URL.IsImage(option.image_url_or_class)
? Enum.FigureType.Image
: Enum.FigureType.Icon;
}
return hasImage;
}
// Method to build the group list format required by the provider:
// [
// {
// label: 'Option Group 1',
// options: [{ label: 'Option 1-1', value: '1' }, ... ]
// },
// ...
// ]
private _getGroupedOptionsList(): [boolean, GroupDropDownOption[]] {
const options: GroupDropDownOption[] = [];
let previousKey: string = undefined;
const [hasDescription, groupedOptions] = this._groupOptions();
for (const key in groupedOptions) {
options.push({
label: key,
options: groupedOptions[key],
// When using grouped options, we need to calculate the index
// to match the option index provided by VirtuaSelect
index: groupedOptions[previousKey]
? options[options.length - 1].index + groupedOptions[previousKey].length + 1
: 0,
});
previousKey = key;
}
return [hasDescription, options];
}
// Method used to generate the HTML String to be attached at the option label
private _getOptionIconPrefix(option: DropDownOption): string {
return `<i class="${Enum.CssClass.OptionItemIcon} ${option.image_url_or_class}"></i>`;
}
// Method used to generate the HTML String to be attached at the option label
private _getOptionImagePrefix(option: DropDownOption): string {
// Since we'll add a src attribute, lets sanitize the given url to avoid XSS
const sanitizedUrl = OSFramework.OSUI.Helper.Sanitize(option.image_url_or_class);
return `<img class="${Enum.CssClass.OptionItemImage}" src="${sanitizedUrl}">`;
}
// Method used to generate the option info that will be added
private _getOptionInfo(data: VirtualSelectOptionInfo): string {
let prefix = '';
let index: number;
let groupIndex: number;
// Group titles do not have labels thus it can skip this block
if (!data.isGroupTitle) {
// If it has group options, we need to use the calculated group index in order to match
// with the one received from the provider
if (data.isGroupOption) {
groupIndex = this._groupedOptionsList.findIndex((group) => group.index === data.groupIndex);
index = data.index - (data.groupIndex + 1);
} else {
groupIndex = 0;
index = data.index;
}
// Check if an image should be added to the Option item
const hasFigureType = this._checkForFigType(this._groupedOptionsList[groupIndex].options[index]);
switch (hasFigureType) {
case Enum.FigureType.Image:
prefix = this._getOptionImagePrefix(this._groupedOptionsList[groupIndex].options[index]);
break;
case Enum.FigureType.Icon:
prefix = this._getOptionIconPrefix(this._groupedOptionsList[groupIndex].options[index]);
break;
}
}
return `${prefix}${data.label}`;
}
// Provide a list in Virtual Select format depending if it is has groups or not
private _getOptionsList() {
// If the _groupedOptionsList has just one group that does not have a defined label,
// we infere that this should be that this is a regular DropDownOption list,
// otherwise this is a GroupDropDownOption List
if (this._groupedOptionsList.length === 1 && !!!this._groupedOptionsList[0].label) {
return this._groupedOptionsList[0].options;
} else {
return this._groupedOptionsList;
}
}
// Auxiliary method to group the options into an object where:
// keys correspond to the names of the groups
// values correspond to a list of the options in the group
private _groupOptions(): [boolean, { [key: string]: DropDownOption[] }] {
let hasDescription = false;
const groupOptionsObject = this.OptionsList.reduce(function (
previousValue: { [key: string]: DropDownOption[] },
option: DropDownOption
) {
const group_name = option.group_name || '';
const description = option.description || '';
option.customData = {};
// We need to set the customData to obtain it when getSelectedOptions() invoked
if (description !== '') {
option.customData = { description: description };
hasDescription = true;
}
if (group_name !== '') {
option.customData = { ...option.customData, group_name: group_name };
}
previousValue[group_name] = previousValue[group_name] || [];
previousValue[group_name].push(option);
return previousValue;
}, {});
return [hasDescription, groupOptionsObject];
}
/**
* Method used to set all the common VirtualSelect properties across the different types of instances
*
* @returns [VirtualSelectOpts]
* @memberof Providers.OSUI.Dropdown.VirtualSelect.AbstractVirtualSelectConfig
*/
public getProviderConfig(): VirtualSelectOpts {
/* In order to avoid XSS let's sanitize the label of each all options
- This must be done here since If we do this at the renderer option we will remain with the
library value unSanitized, that said we must do it before adding the list of options to the library! */
for (const option of this.OptionsList) {
option.label = OSFramework.OSUI.Helper.Sanitize(option.label);
}
// We need to keep the _groupedOptionsList in order to use it in this._getOptionInfo method
const [hasDescription, groupedOptionsList] = this._getGroupedOptionsList();
this._groupedOptionsList = groupedOptionsList;
// Set the library options
this._providerOptions = {
ele: this.ElementId,
disabled: this.IsDisabled,
dropboxWrapper: OSFramework.OSUI.GlobalEnum.HTMLElement.Body,
hasOptionDescription: hasDescription,
hideClearButton: false,
labelRenderer: this._getOptionInfo.bind(this),
noOptionsText: this.NoOptionsText,
noSearchResultsText: this.NoResultsText,
options: this._getOptionsList() as [],
placeholder: this.Prompt,
search: true,
searchNormalize: true,
searchPlaceholderText: this.SearchPrompt,
selectAllOnlyVisible: true,
selectedValue: this.getSelectedValues() as [],
showDropboxAsPopup: this.ShowDropboxAsPopup,
popupDropboxBreakpoint: this.PopupDropboxBreakpoint,
silentInitialValueSet: true,
textDirection: OutSystems.OSUI.Utils.GetIsRTL()
? OSFramework.OSUI.GlobalEnum.Direction.RTL
: OSFramework.OSUI.GlobalEnum.Direction.LTR,
updatePositionThrottle: 0,
useGroupValue: true,
zIndex: 251, // Higher than Sidebar and Popup
};
return this._providerOptions;
}
/**
* Method to validate and save the external provider configs
*
* @param {VirtualSelectOpts} newConfigs
* @memberof Providers.OSUI.Dropdown.VirtualSelect.AbstractVirtualSelectConfig
*/
public setExtensibilityConfigs(newConfigs: VirtualSelectOpts): void {
if (newConfigs[Enum.ExtendedConfigs.hasOptionDescription] !== undefined)
console.warn(
`The option description may be affected when modifying the property ${Enum.ExtendedConfigs.hasOptionDescription}.`
);
this.providerExtendedOptions = newConfigs;
}
/**
* Override, Validate configs key values
*
* @param {string} key
* @param {unknown} value
* @memberof Providers.OSUI.Dropdown.VirtualSelect.AbstractVirtualSelectConfig
*/
public validateDefault(key: string, value: unknown): unknown {
let validatedValue = undefined;
switch (key) {
case Enum.Properties.OptionsList:
validatedValue = value as DropDownOption;
break;
case Enum.Properties.StartingSelection:
validatedValue = value as DropDownOption;
break;
default:
validatedValue = super.validateDefault(key, value);
break;
}
return validatedValue;
}
// Common methods all DropdownsConfigs must implement
protected abstract getSelectedValues(): string[];
}
}