scripts/django_cradmin/components/CradminSelectableListItem.jsx
import React from "react";
import {HotKeys} from "react-hotkeys";
import DomUtilities from "../utilities/DomUtilities";
export default class CradminSelectableListItem extends React.Component {
static get defaultProps() {
return {
className: 'selectable-list__item',
selectedClassName: 'selectable-list__item--selected',
contentClassName: 'selectable-list__itemcontent',
renderIcon: false,
iconWrapperClassName: 'selectable-list__icon',
selectedIconClassName: 'icon-check--light',
titleTagName: 'strong',
titleClassName: 'selectable-list__itemtitle',
descriptionClassName: '',
isSelected: false,
selectCallback: null,
setDataListFocus: true,
renderMode: 'TitleAndDescription',
focusClosestSiblingOnSelect: true,
previousItemData: null,
nextItemData: null,
disableTabNavigation: false,
useHotKeys: false
}
}
constructor(props) {
super(props);
this._name = `django_cradmin.components.CradminSelectableListItem.${this.props.signalNameSpace}.${this.props.itemKey}`;
this.handleSelect = this.handleSelect.bind(this);
this.handleFocus = this.handleFocus.bind(this);
this.handleBlur = this.handleBlur.bind(this);
this._onFocusOnSelectableItemSignal = this._onFocusOnSelectableItemSignal.bind(this);
this.initializeSignalHandlers();
}
initializeSignalHandlers() {
new window.ievv_jsbase_core.SignalHandlerSingleton().addReceiver(
`${this.props.signalNameSpace}.FocusOnSelectableItem.${this.props.itemKey}`,
this._name,
this._onFocusOnSelectableItemSignal
);
}
componentWillUnmount() {
new window.ievv_jsbase_core.SignalHandlerSingleton()
.removeAllSignalsFromReceiver(this._name);
}
handleSelect(event) {
event.preventDefault();
if(this.props.isSelected) {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.DeSelectItem`,
this.props.data
);
} else {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.SelectItem`,
this.props.data
);
if(this.props.focusClosestSiblingOnSelect) {
let closestSiblingData = this.props.nextItemData;
if(closestSiblingData == null) {
closestSiblingData = this.props.previousItemData;
}
if(closestSiblingData == null) {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.CouldNotFocusOnClosestSelectableItem`);
} else {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.FocusOnSelectableItem`,
closestSiblingData
);
}
}
}
}
_onFocusOnSelectableItemSignal() {
DomUtilities.forceFocus(this._domElement);
}
handleFocus() {
if(this.props.setDataListFocus) {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.Focus`
);
}
}
handleBlur() {
if(this.props.setDataListFocus) {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.Blur`
);
}
}
get ariaTitle() {
if(this.props.data.ariaTitle) {
return this.props.data.ariaTitle;
} else {
return this.props.data.title;
}
}
get fullClassName() {
let className = this.props.className;
if(this.props.isSelected) {
className = `${className} ${this.props.selectedClassName}`
}
return className;
}
renderTitle() {
return React.createElement(this.props.titleTagName, {
className: this.props.titleClassName
}, this.props.data.title);
}
renderDescription() {
if(this.props.data.description && this.props.data.description != '') {
return <p className={this.props.descriptionClassName}>{this.props.data.description}</p>;
} else {
return '';
}
}
renderIcon() {
if(this.props.isSelected) {
return <i className={this.props.selectedIconClassName} />;
} else {
return '';
}
}
renderIconWrapper() {
if(this.props.renderIcon) {
return <div className={this.props.iconWrapperClassName}>
{this.renderIcon()}
</div>;
} else {
return '';
}
}
renderContentModeTitleAndDescription() {
return <div className={this.props.contentClassName}>
{this.renderTitle()}
{this.renderDescription()}
</div>;
}
renderContentModeTitleOnly() {
return <div className={this.props.contentClassName}>
{this.props.data.title}
</div>;
}
renderContentModeHtml() {
return <div className={this.props.contentClassName}
dangerouslySetInnerHTML={{__html: this.props.data.html}}></div>;
}
renderContent() {
if(this.props.renderMode == 'TitleAndDescription') {
return this.renderContentModeTitleAndDescription();
} else if(this.props.renderMode == 'TitleOnly') {
return this.renderContentModeTitleOnly();
} else if(this.props.renderMode == 'html') {
return this.renderContentModeHtml();
} else {
throw new Error(`Invalid renderMode: ${this.props.renderMode}`);
}
}
getTabIndex() {
if(this.props.disableTabNavigation) {
return "-1";
} else {
return "0";
}
}
_focusPreviousItem() {
if(this.props.previousItemData == null) {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.FocusBeforeFirstSelectableItem`);
} else {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.FocusOnSelectableItem`,
this.props.previousItemData
);
}
}
_focusNextItem() {
if(this.props.nextItemData == null) {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.FocusAfterLastSelectableItem`);
} else {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.FocusOnSelectableItem`,
this.props.nextItemData
);
}
}
_onEscapeKey() {
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
`${this.props.signalNameSpace}.SelectableItemEscapeKey`);
}
get hotKeysMap() {
return {
'focusPreviousItem': ['up'],
'focusNextItem': ['down'],
'escapeKey': ['escape']
};
}
get hotKeysHandlers() {
return {
'focusPreviousItem': (event) => {
event.preventDefault();
this._focusPreviousItem();
},
'focusNextItem': (event) => {
event.preventDefault();
this._focusNextItem();
},
'escapeKey': (event) => {
event.preventDefault();
this._onEscapeKey();
}
}
}
renderWrapper() {
return <a href="#" className={this.fullClassName}
ref={(domElement) => { this._domElement = domElement; }}
tabIndex={this.getTabIndex()}
onClick={this.handleSelect}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
aria-label={this.ariaTitle}
role="button">
{this.renderIconWrapper()}
{this.renderContent()}
</a>;
}
render() {
if(this.props.useHotKeys) {
return <HotKeys keyMap={this.hotKeysMap} handlers={this.hotKeysHandlers}>
{this.renderWrapper()}
</HotKeys>
} else {
return this.renderWrapper();
}
}
}