javascript 使用Container组件的React Dropdown
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript 使用Container组件的React Dropdown相关的知识,希望对你有一定的参考价值。
import React from 'react';
import Dropdown, {CLOSE_ALL, TOGGLE} from '../Core/Dropdown';
import {com} from './AppCom';
class DropdownContainer extends React.Component {
dropdownRef = React.createRef();
constructor(props) {
super(props);
this.state = {
dropdownList: []
}
}
componentDidMount() {
if (!DropdownContainer.singleton) {
DropdownContainer.singleton = this;
}
if (this.dropdownRef.current) {
DropdownContainer.singleton.setState(prevState => ({
dropdownList: prevState.dropdownList.concat(this.dropdownRef.current)
}));
}
}
onChange = ({selected}) => {
const items = this.props.items.map(option => {
option.selected = false;
return option;
});
const index = items.indexOf(selected);
items[index].selected = true;
};
onTransition = (ACTION, dropdown) => {
switch (ACTION) {
case TOGGLE:
dropdown.toggle();
break;
case CLOSE_ALL:
DropdownContainer.singleton.state.dropdownList.forEach(dropdownItem => {
if (!dropdown || dropdownItem !== dropdown) {
dropdownItem.close();
}
});
break;
}
};
onSubmit = () => {
const {items} = this.props;
const [category] = items;
const selectedItem = items.find(item => item.selected);
const actionUri = items.indexOf(selectedItem) === 0
? `/${category.value}`
: `/${category.value}/${selectedItem.value}`;
this.handleOptionSelect(com.subCategories, selectedItem);
this.handleOptionSelect(com.sortOptions, selectedItem);
com.refreshPLPMenuForm.setAttribute('action', actionUri);
com.refreshPLPMenuForm.submit();
};
handleOptionSelect(selectNodeList, selectedItem) {
const option = Array.from(selectNodeList.children).find(o => o.value === selectedItem.value);
if (option) {
Array.from(selectNodeList.children).forEach(o => o.removeAttribute('selected'));
option.setAttribute('selected', true);
}
}
render() {
return this.props.items.length && (
<Dropdown
ref={this.dropdownRef}
label={this.props.label}
items={this.props.items}
selected={this.props.selected}
onChange={this.onChange}
onTransition={this.onTransition}
onSubmit={this.onSubmit}
/>
);
}
}
DropdownContainer.singleton = null;
export default DropdownContainer;
export const com = {
refreshPLPMenuForm: document.getElementById('plp-refresh-menu-form'),
sortLabel: document.querySelector('.sort-label'),
sortOptions: document.getElementById('sort'),
selectedSortOption: document.querySelector('#sort > option[selected]'),
subCategoriesLabel: document.querySelector('.subcategories-label'),
subCategories: document.getElementById('subcategories'),
selectedSubCategory: document.querySelector('#subcategories > option[selected]')
};
import React from 'react';
export const TOGGLE = 'TOGGLE';
export const CLOSE_ALL = 'CLOSE_ALL';
export const generateTabIndex = function* () {
let i = 0;
while(true) {
yield i++;
}
};
class Dropdown extends React.Component {
dropdownRef = React.createRef();
constructor(props) {
super(props);
this.state = {
isOpen: false,
selected: props.items.find(item => item.selected)
};
}
componentDidMount() {
document.addEventListener('click', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside);
}
close = () => this.setState(() => ({isOpen: false}));
toggle = () => this.setState(prevState => ({isOpen: !prevState.isOpen}));
handleClickOutside = e => {
const [selector] = this.dropdownRef.current.classList;
if (!e.target.closest(`.${selector}`)) {
this.props.onTransition(CLOSE_ALL)
}
};
handleTransition = () => {
this.props.onTransition(CLOSE_ALL, this);
this.props.onTransition(TOGGLE, this);
window.onkeydown = this.state.isOpen ? null : e => e.preventDefault();
};
handleSwitch = e => {
const {items, label, onSubmit, onChange, onTransition} = this.props;
const index = items.indexOf(this.state.selected);
let selected;
switch (e.which) {
// Enter or space bar
case 13:
case 32:
onSubmit();
break;
// Up arrow
case 38:
selected = items[index - 1 < 0 ? items.length - 1 : index - 1];
this.setState(() => ({selected}));
onChange({selected, label});
break;
// Down arrow
case 40:
selected = items[index + 1 > items.length - 1 ? 0 : index + 1];
this.setState(() => ({selected}));
onChange({selected, label});
break;
// Any other non matching keys
default:
onTransition(CLOSE_ALL);
window.onkeydown = null;
}
};
handleClick = (selected) => {
const {onChange, onSubmit} = this.props;
this.setState(() => ({selected}));
onChange({selected});
onSubmit();
};
render() {
const {
listClass = '',
listItemClass = '',
listItemHeaderClass = '',
iconClass = ''
} = this.props;
return (
<ul
ref={this.dropdownRef}
className={`dropdown-list ${listClass}`}
tabIndex={Dropdown.gen.next().value}
onKeyDown={this.handleSwitch}
>
<li className={`dropdown-list__item ${listItemClass} ${this.state.isOpen ? 'dropdown-list__item--opened' : ''}`}
onClick={this.handleTransition}>
<span className="dropdown-label">
{this.props.label}
</span>
<span className={`dropdown-list__item-header ${listItemHeaderClass}`}>
{this.props.selected.text}
</span>
<span className={`icon-chevron-down dropdown-arrow ${iconClass}`}
/>
<ul className={`dropdown-list ${this.state.isOpen ? 'visible' : 'hidden'}`}>
{this.props.items.map(item => (
<li
key={item.value}
className={`dropdown-list__item ${item.selected ? 'dropdown-list__item--selected' : ''}`}
onClick={() => this.handleClick(item)}
>
{item.text}
</li>
))}
</ul>
</li>
</ul>
);
}
}
Dropdown.gen = generateTabIndex();
export default Dropdown;
@import "../core/fonts";
@import "../core/colors";
@import "../core/mixins";
.dropdown-label {
font: 12px $helvetica-light;
text-transform: none;
padding-right: 15px;
white-space: nowrap;
@include breakpoint(xp) {
display: inline;
font: 14px $helvetica-light;
}
}
.dropdown-placeholder {
float: right;
margin-right: 20px;
}
.dropdown-list {
margin: 0;
width: 100%;
@include breakpoint(xp) {
width: 300px;
}
* {
list-style: none;
box-sizing: border-box;
}
&__item {
position: relative;
padding: 10px 0 10px 0;
font: 12px $avantgarde-gothic;
text-transform: uppercase;
cursor: pointer;
z-index: 1;
background-color: $white;
border-top: 1px solid $white-dark;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
text-align: right;
@include breakpoint(xp) {
padding: 20px 0 20px 0;
font: 14px $avantgarde-gothic;
}
&:first-child {
border-top: none;
}
& > .dropdown-list {
position: absolute;
margin: 10px 0;
@include breakpoint(xp) {
margin: 20px 0;
}
& > .dropdown-list__item {
padding: 20px;
border-right: none;
border-right: 1px solid $white-dark;
border-left: 1px solid $white-dark;
&:hover {
background-color: $white-dark;
}
}
}
&--selected {
background-color: $white-dark;
}
&--opened {
border-right: 1px solid $white-dark;
border-left: 1px solid $white-dark;
border-bottom: 1px solid $white-dark;
}
}
&__item-header {
white-space: nowrap;
}
.dropdown-arrow {
margin-left: 10px;
margin-right: 8px;
font-size: 15px;
font-weight: bold;
@include breakpoint(xp) {
margin-right: 20px;
}
}
}
import React from 'react';
import DropdownContainer from './DropdownContainer';
import {com} from './AppCom';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
subCategories: {
label: '',
items: [],
selected: {
value: '',
text: '',
selected: false
}
},
sortOptions: {
label: '',
items: [],
selected: {
value: '',
text: '',
selected: false
}
}
}
}
componentWillMount() {
let {
sortLabel,
sortOptions,
selectedSortOption,
subCategoriesLabel,
subCategories,
selectedSubCategory
} = com;
this.setState(() => ({
sortOptions: {...this.getMenuState(sortLabel, sortOptions, selectedSortOption)},
subCategories: {...this.getMenuState(subCategoriesLabel, subCategories, selectedSubCategory)}
}));
}
getMenuState(labelNode, itemsNodeList, selectedNode) {
labelNode = labelNode && labelNode.innerText || '';
itemsNodeList = Array
.from(itemsNodeList || [])
.map(({value, innerText, selected}) => ({
value,
text: innerText,
selected
}));
selectedNode = {
value: selectedNode && selectedNode.value || '',
text: selectedNode && selectedNode.innerText || '',
selected: selectedNode && selectedNode.selected || false,
};
return {
label: labelNode.trim(),
items: itemsNodeList || [],
selected: selectedNode
};
}
render() {
return (
<div className="product-menu__wrapper">
<DropdownContainer {...this.state.subCategories}/>
<DropdownContainer {...this.state.sortOptions}/>
</div>
);
}
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
const appContainer = document.getElementById('refresh-product-menu-app');
if (appContainer) {
(async () => {
const {default: App} = await import ('./App');
ReactDOM.render(<App/>, appContainer);
})();
}
以上是关于javascript 使用Container组件的React Dropdown的主要内容,如果未能解决你的问题,请参考以下文章
Java AWT 图形界面编程Container 容器 ① ( Container 容器类子类 | Component 组件类常用 API | Container 容器类常用 API )
使用 useParams 的 Route Container 组件中的 Hook 错误
flutter 布局 Stack Positioned的混合操作 两个组件Container重叠 构建背景圆角操作 类似css的relative