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 )

Flutter基础组件02Container

Flutter基础组件02Container

使用 useParams 的 Route Container 组件中的 Hook 错误

flutter 布局 Stack Positioned的混合操作 两个组件Container重叠 构建背景圆角操作 类似css的relative

flutter设置container的宽度撑满父组件