lucid-ui
Version:
A UI component library from Xandr.
395 lines • 22.7 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SearchableSingleSelectDumb = void 0;
/* eslint-disable react/prop-types */
var lodash_1 = __importDefault(require("lodash"));
var react_1 = __importDefault(require("react"));
var prop_types_1 = __importDefault(require("prop-types"));
var style_helpers_1 = require("../../util/style-helpers");
var state_management_1 = require("../../util/state-management");
var text_manipulation_1 = require("../../util/text-manipulation");
var component_types_1 = require("../../util/component-types");
var SearchField_1 = require("../SearchField/SearchField");
var DropMenu_1 = require("../DropMenu/DropMenu");
var LoadingIcon_1 = __importDefault(require("../Icon/LoadingIcon/LoadingIcon"));
var Selection_1 = __importDefault(require("../Selection/Selection"));
var Validation_1 = require("../Validation/Validation");
var reducers = __importStar(require("./SearchableSingleSelect.reducers"));
var any = prop_types_1.default.any, bool = prop_types_1.default.bool, func = prop_types_1.default.func, number = prop_types_1.default.number, oneOfType = prop_types_1.default.oneOfType, shape = prop_types_1.default.shape, string = prop_types_1.default.string, node = prop_types_1.default.node;
var cx = style_helpers_1.lucidClassNames.bind('&-SearchableSingleSelect');
/** Option Group */
var OptionGroup = function (_props) { return null; };
OptionGroup.displayName = 'SearchableSingleSelect.OptionGroup';
OptionGroup.peek = {
description: "\n\t\tA special kind of `Option` that is always rendered at the top of\n\t\tthe menu and has an `optionIndex` of `null`. Useful for\n\t\tunselect.\n\t",
};
OptionGroup.propName = 'OptionGroup';
OptionGroup.propTypes = DropMenu_1.DropMenuDumb.OptionGroup.propTypes;
OptionGroup.defaultProps = DropMenu_1.DropMenuDumb.OptionGroup.defaultProps;
/** Search Field */
var SearchFieldComponent = function (_props) { return null; };
SearchFieldComponent.displayName = 'SearchableSingleSelect.SearchField';
SearchFieldComponent.peek = {
description: "\n\t\tPasses props through to the `Search Field`.\n\t",
};
SearchFieldComponent.propName = 'SearchField';
SearchFieldComponent.propTypes = SearchField_1.SearchFieldDumb.propTypes;
SearchFieldComponent.defaultProps = SearchField_1.SearchFieldDumb.defaultProps;
/** Selected */
var Selected = function (_props) { return null; };
Selected.displayName = 'SearchableSingleSelect.Option.Selected';
Selected.peek = {
description: "\n\t\tCustomizes the rendering of the Option when it is selected\n\t\tand is displayed instead of the Placeholder.\n\t",
};
Selected.propName = 'Selected';
Selected.propTypes = {};
var Option = function (_props) { return null; };
Option.displayName = 'SearchableSingleSelect.Option';
Option.peek = {
description: "\n\t\tA selectable option in the list.\n\t",
};
Option.Selected = Selected;
Option.propName = 'Option';
Option.propTypes = __assign({
/**
Customizes the rendering of the Option when it is selected and is
displayed instead of the Placeholder.
*/
Selected: any, value: string, filterText: string }, DropMenu_1.DropMenuDumb.Option.propTypes);
Option.defaultProps = DropMenu_1.DropMenuDumb.Option.defaultProps;
/** TODO: Remove nonPassThroughs when the component is converted to a functional component */
var nonPassThroughs = [
'children',
'className',
'isDisabled',
'isLoading',
'maxMenuHeight',
'onSearch',
'onSelect',
'optionFilter',
'searchText',
'selectedIndex',
'DropMenu',
'Option',
'Error',
'FixedOption',
'NullOption',
'OptionGroup',
'SearchField',
'initialState',
'callbackId',
];
var defaultProps = {
isDisabled: false,
isLoading: false,
optionFilter: text_manipulation_1.propsSearch,
searchText: '',
selectedIndex: null,
DropMenu: DropMenu_1.DropMenuDumb.defaultProps,
Error: null,
onSearch: lodash_1.default.noop,
onSelect: lodash_1.default.noop,
SearchField: SearchField_1.SearchFieldDumb.defaultProps,
};
var SearchableSingleSelect = /** @class */ (function (_super) {
__extends(SearchableSingleSelect, _super);
function SearchableSingleSelect() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.handleSearch = function (searchText, _a) {
var event = _a.event;
var _b = _this, props = _b.props, _c = _b.props, onSearch = _c.onSearch, optionFilter = _c.optionFilter, onExpand = _c.DropMenu.onExpand;
var options = lodash_1.default.map((0, component_types_1.findTypes)(props, SearchableSingleSelect.Option), 'props');
var firstVisibleIndex = lodash_1.default.findIndex(options, function (option) {
return optionFilter(searchText, option);
});
var firstVisibleProps = options[firstVisibleIndex];
// Just an extra call to make sure the search results show up when a user
// is typing
var dropMenuProps = _this.props.DropMenu;
onExpand && onExpand({ event: event, props: dropMenuProps });
return onSearch(searchText, firstVisibleIndex, {
event: event,
props: firstVisibleProps,
});
};
_this.renderUnderlinedChildren = function (childText, searchText) {
var _a = (0, text_manipulation_1.partitionText)(childText, new RegExp(lodash_1.default.escapeRegExp(searchText), 'i'), searchText.length), pre = _a[0], match = _a[1], post = _a[2];
return [
pre && (react_1.default.createElement("span", { key: 'pre', className: cx('&-Option-underline-pre') }, pre)),
match && (react_1.default.createElement("span", { key: 'match', className: cx('&-Option-underline-match') }, match)),
post && (react_1.default.createElement("span", { key: 'post', className: cx('&-Option-underline-post') }, post)),
];
};
_this.renderOptionContent = function (optionProps, searchText) {
return lodash_1.default.isString(optionProps.children) &&
lodash_1.default.isString(searchText) &&
searchText.length > 0
? _this.renderUnderlinedChildren(optionProps.children, searchText)
: lodash_1.default.isFunction(optionProps.children)
? react_1.default.createElement(optionProps.children, { searchText: searchText })
: optionProps.children;
};
_this.renderOption = function (_a) {
var optionProps = _a.optionProps, optionIndex = _a.optionIndex;
var _b = _this.props, searchText = _b.searchText, isLoading = _b.isLoading, optionFilter = _b.optionFilter;
return (react_1.default.createElement(DropMenu_1.DropMenuDumb.Option, __assign({ key: 'SearchableSingleSelectOption' + optionIndex }, lodash_1.default.omit(optionProps, ['children', 'Selected', 'filterText']), { isHidden: !optionFilter(searchText, optionProps), isDisabled: optionProps.isDisabled || isLoading }), _this.renderOptionContent(optionProps, searchText)));
};
_this.renderOptions = function () {
var _a = _this.props, searchText = _a.searchText, isLoading = _a.isLoading;
var _b = _this.state, optionGroups = _b.optionGroups, optionGroupDataLookup = _b.optionGroupDataLookup, ungroupedOptionData = _b.ungroupedOptionData, flattenedOptionsData = _b.flattenedOptionsData;
var visibleOptions = lodash_1.default.reject(flattenedOptionsData, 'optionProps.isHidden');
var isAllOptionsHidden = lodash_1.default.isEmpty(visibleOptions);
// for each option group passed in, render a DropMenu.OptionGroup, any label will be included in it's children, render each option inside the group
var dropMenuOptions = lodash_1.default.map(optionGroups, function (optionGroupProps, optionGroupIndex) { return (react_1.default.createElement(DropMenu_1.DropMenuDumb.OptionGroup, __assign({ key: 'SearchableSingleSelectOptionGroup' + optionGroupIndex }, lodash_1.default.omit(optionGroupProps, 'children', 'Selected')),
optionGroupProps.children,
lodash_1.default.map(optionGroupDataLookup[optionGroupIndex], _this.renderOption))); }).concat(
// then render all the ungrouped options at the end
lodash_1.default.map(ungroupedOptionData, function (options) { return _this.renderOption(options); }));
if (!isAllOptionsHidden || lodash_1.default.isEmpty(searchText)) {
return dropMenuOptions;
}
if (!isLoading) {
return (react_1.default.createElement(DropMenu_1.DropMenuDumb.Option, { isDisabled: true },
react_1.default.createElement("span", { className: cx('&-noresults') },
"No results match \"",
searchText,
"\"")));
}
return null;
};
_this.removeSelection = function (_a) {
var event = _a.event, props = _a.props;
var dropMenuProps = _this.props.DropMenu;
_this.props.DropMenu.onCollapse &&
_this.props.DropMenu.onCollapse({ event: event, props: dropMenuProps });
_this.props.onSearch('', null, { event: event, props: props });
_this.props.onSelect(null, { event: event, props: props });
};
_this.render = function () {
var _a = _this, props = _a.props, _b = _a.props, className = _b.className, isLoading = _b.isLoading, isDisabled = _b.isDisabled, maxMenuHeight = _b.maxMenuHeight, dropMenuProps = _b.DropMenu, optionContainerStyle = _b.DropMenu.optionContainerStyle, searchText = _b.searchText, selectedIndex = _b.selectedIndex, onSelect = _b.onSelect, passThroughs = __rest(_b, ["className", "isLoading", "isDisabled", "maxMenuHeight", "DropMenu", "DropMenu", "searchText", "selectedIndex", "onSelect"]);
var searchFieldProps = lodash_1.default.get((0, component_types_1.getFirst)(props, SearchableSingleSelect.SearchField), 'props', {});
var errorChildProps = lodash_1.default.first(lodash_1.default.map((0, component_types_1.findTypes)(props, Validation_1.Validation.Error), 'props'));
//user made a selection
if (!lodash_1.default.isNil(selectedIndex)) {
var selectedOptionProps = _this.state.flattenedOptionsData[selectedIndex].optionProps;
return (react_1.default.createElement("div", __assign({}, lodash_1.default.omit(passThroughs, nonPassThroughs), { className: cx('&', className) }),
react_1.default.createElement(Selection_1.default, { Label: lodash_1.default.isNil(selectedOptionProps.Selected)
? _this.renderOptionContent(selectedOptionProps, '')
: selectedOptionProps.Selected, className: cx('&', {
'&-select-error': errorChildProps &&
errorChildProps.children &&
errorChildProps.children !== true,
}, className), onRemove: _this.removeSelection, kind: 'default' }),
errorChildProps &&
errorChildProps.children &&
errorChildProps.children !== true ? (react_1.default.createElement("div", __assign({}, lodash_1.default.omit(errorChildProps, ['initialState', 'callbackId']), { className: cx('&-error-select-content') }), errorChildProps.children)) : null));
}
return (react_1.default.createElement("div", __assign({}, lodash_1.default.omit(passThroughs, nonPassThroughs), { className: cx('&', className) }),
react_1.default.createElement(DropMenu_1.DropMenuDumb, __assign({}, dropMenuProps, { selectedIndices: null, className: cx('&-DropMenu', {
'&-DropMenu-is-small': true,
}, dropMenuProps.className), optionContainerStyle: lodash_1.default.assign({}, optionContainerStyle, !lodash_1.default.isNil(maxMenuHeight) ? { maxHeight: maxMenuHeight } : null), isDisabled: isDisabled, onSelect: onSelect, ContextMenu: {
alignmentOffset: -13,
directonOffset: -1,
minWidthOffset: -28,
} }),
react_1.default.createElement(DropMenu_1.DropMenuDumb.Control, null,
react_1.default.createElement(SearchField_1.SearchFieldDumb, __assign({}, searchFieldProps, { autoComplete: searchFieldProps.autoComplete || 'off', isDisabled: isDisabled, className: cx('&-search', {
'&-search-is-error': errorChildProps && errorChildProps.children,
}, searchFieldProps.className), value: searchText, onChange: _this.handleSearch }))),
isLoading ? (react_1.default.createElement(DropMenu_1.DropMenuDumb.Option, { key: 'SearchableSingleSelectLoading', className: cx('&-loading'), isDisabled: true },
react_1.default.createElement(LoadingIcon_1.default, null))) : null,
_this.renderOptions()),
errorChildProps &&
errorChildProps.children &&
errorChildProps.children !== true ? (react_1.default.createElement("div", __assign({}, lodash_1.default.omit(errorChildProps, ['initialState', 'callbackId']), { className: cx('&-error-content') }), errorChildProps.children)) : null));
};
return _this;
}
SearchableSingleSelect.prototype.UNSAFE_componentWillMount = function () {
// preprocess the options data before rendering
var _a = DropMenu_1.DropMenuDumb.preprocessOptionData(this.props, SearchableSingleSelect), optionGroups = _a.optionGroups, flattenedOptionsData = _a.flattenedOptionsData, ungroupedOptionData = _a.ungroupedOptionData, optionGroupDataLookup = _a.optionGroupDataLookup;
this.setState({
optionGroups: optionGroups,
flattenedOptionsData: flattenedOptionsData,
ungroupedOptionData: ungroupedOptionData,
optionGroupDataLookup: optionGroupDataLookup,
});
};
SearchableSingleSelect.prototype.UNSAFE_componentWillReceiveProps = function (nextProps) {
// only preprocess options data when it changes (via new props) - better performance than doing this each render
var _a = DropMenu_1.DropMenuDumb.preprocessOptionData(nextProps, SearchableSingleSelect), optionGroups = _a.optionGroups, flattenedOptionsData = _a.flattenedOptionsData, ungroupedOptionData = _a.ungroupedOptionData, optionGroupDataLookup = _a.optionGroupDataLookup;
this.setState({
optionGroups: optionGroups,
flattenedOptionsData: flattenedOptionsData,
ungroupedOptionData: ungroupedOptionData,
optionGroupDataLookup: optionGroupDataLookup,
});
};
SearchableSingleSelect.displayName = 'SearchableSingleSelect';
SearchableSingleSelect.peek = {
description: "A control used to select a single option from a dropdown list using a `SearchField`.",
categories: ['controls', 'selectors'],
madeFrom: ['Checkbox', 'SearchField', 'DropMenu', 'LoadingIcon'],
};
SearchableSingleSelect.defaultProps = defaultProps;
SearchableSingleSelect.reducers = reducers;
SearchableSingleSelect.Option = Option;
SearchableSingleSelect.OptionGroup = OptionGroup;
SearchableSingleSelect.SearchField = SearchFieldComponent;
SearchableSingleSelect.NullOption = DropMenu_1.DropMenuDumb.NullOption;
SearchableSingleSelect.FixedOption = DropMenu_1.DropMenuDumb.FixedOption;
SearchableSingleSelect.DropMenu = DropMenu_1.DropMenuDumb;
SearchableSingleSelect.propTypes = {
/**
Should be instances of {\`SearchableSingleSelect.Option\`}. Other direct
child elements will not render.
*/
children: node,
/**
Appended to the component-specific class names set on the root element.
*/
className: string,
/**
Disables the control from being clicked or focused.
*/
isDisabled: bool,
/**
Displays a LoadingIcon to allow for asynchronous loading of options.
*/
isLoading: bool,
/**
The max height of the fly-out menu.
*/
maxMenuHeight: oneOfType([number, string]),
onSearch: func /**
Called when the user enters a value to search for; the set of visible
Options will be filtered using the value. Signature: \`(searchText,
firstVisibleIndex, {props, event}) => {}\` \`searchText\` is the value
from the \`SearchField\` and \`firstVisibleIndex\` is the index of the
first option that will be visible after filtering.
*/,
/**
Called when an option is selected. Signature: \`(optionIndex, {props,
event}) => {}\` \`optionIndex\` is the new \`selectedIndex\` or \`null\`.
*/
onSelect: func,
/**
The function that will be run against each Option's props to determine
whether it should be visible or not. The default behavior of the function
is to match, ignoring case, against any text node descendant of the
\`Option\`. Signature: \`(searchText, optionProps) => {}\` If \`true\`
is returned, the option will be visible. If \`false\`, the option will
not be visible.
*/
optionFilter: func,
/**
The current search text to filter the list of options by.
*/
searchText: string,
/**
Currently selected \`SearchableSingleSelect.Option\` index
or \`null\` if nothing is selected.
*/
selectedIndex: number,
/**
Object of DropMenu props which are passed through to the underlying
DropMenu component.
*/
DropMenu: shape(DropMenu_1.DropMenuDumb.propTypes),
Option: any /**
*Child Element* - These are menu options. Each \`Option\` may be passed a
prop called \`isDisabled\` to disable selection of that \`Option\`. Any
other props pass to Option will be available from the \`onSelect\`
handler. It also support the \`Selection\` prop that can be used to
forward along props to the underlying \`Selection\` component.
*/,
/**
In most cases this will be a string, but it also accepts any valid React
element. If this is a falsey value, then no error message will be
displayed. If this is the literal \`true\`, it will add the
\`-is-error\` class to the wrapper div, but not render the
\`-error-content\` \`div\`.
*/
Error: any,
FixedOption: any /**
*Child Element* - A special kind of \`Option\` that is always rendered at the top of
the menu.
*/,
NullOption: any /**
*Child Element* - A special kind of \`Option\` that is always rendered at
the top of the menu and has an \`optionIndex\` of \`null\`. Useful for
unselect.
*/,
OptionGroup: any /**
*Child Element* - Used to group \`Option\`s within the menu. Any
non-\`Option\`s passed in will be rendered as a label for the group.
*/,
/**
*Child Element* - Passes props through to the \`SearchField\` component.
*/
SearchField: any,
};
return SearchableSingleSelect;
}(react_1.default.Component));
exports.SearchableSingleSelectDumb = SearchableSingleSelect;
exports.default = (0, state_management_1.buildModernHybridComponent)(SearchableSingleSelect, { reducers: reducers });
//# sourceMappingURL=SearchableSingleSelect.js.map