UNPKG

polaris-glints

Version:

Glints forked polaris react

443 lines (415 loc) 16.2 kB
import React, { Component, createRef } from 'react'; import { SearchMinor, CancelSmallMinor, ChevronUpMinor, ChevronDownMinor } from '@shopify/polaris-icons'; import { classNames } from '../../utilities/css.js'; import { focusFirstFocusableNode } from '../../utilities/focus.js'; import { WithinFilterContext } from '../../utilities/within-filter-context.js'; import { Key } from '../../types.js'; import styles from './Filters.scss.js'; import { ResourceListContext } from '../../utilities/resource-list/context.js'; import { Collapsible } from '../Collapsible/Collapsible.js'; import { ConnectedFilterControl } from './components/ConnectedFilterControl/ConnectedFilterControl.js'; import { TagsWrapper } from './components/TagsWrapper/TagsWrapper.js'; import { Tag } from '../Tag/Tag.js'; import { Sheet } from '../Sheet/Sheet.js'; import { useI18n } from '../../utilities/i18n/hooks.js'; import { useMediaQuery } from '../../utilities/media-query/hooks.js'; import { ScrollLock } from '../ScrollLock/ScrollLock.js'; import { Badge } from '../Badge/Badge.js'; import { Text } from '../Text/Text.js'; import { Icon } from '../Icon/Icon.js'; import { Focus } from '../Focus/Focus.js'; import { Button } from '../Button/Button.js'; import { TextField } from '../TextField/TextField.js'; import { Scrollable } from '../Scrollable/Scrollable.js'; import { KeypressListener } from '../KeypressListener/KeypressListener.js'; import { Stack } from '../Stack/Stack.js'; var Suffix; (function (Suffix) { Suffix["Filter"] = "Filter"; Suffix["Shortcut"] = "Shortcut"; })(Suffix || (Suffix = {})); class FiltersInner extends Component { constructor(...args) { super(...args); this.context = void 0; this.state = { open: false, readyForFocus: false }; this.moreFiltersButtonContainer = /*#__PURE__*/createRef(); this.moreFiltersDoneButtonContainer = /*#__PURE__*/createRef(); this.focusNode = /*#__PURE__*/createRef(); this.closeFilters = () => { this.setState({ open: false }, () => { if (this.moreFiltersButtonContainer.current) { focusFirstFocusableNode(this.moreFiltersButtonContainer.current, false); } }); }; this.toggleFilters = () => { if (this.state.open === true) { this.closeFilters(); } else { this.openFilters(); } }; this.setReadyForFocus = newState => () => { this.setState({ readyForFocus: newState }); }; this.handleClearAll = () => { this.props.onClearAll(); this.moreFiltersDoneButtonContainer.current && focusFirstFocusableNode(this.moreFiltersDoneButtonContainer.current, false); }; } render() { const { filters, queryValue, onQueryBlur, onQueryChange, onQueryFocus, focused, onClearAll, appliedFilters, onQueryClear, queryPlaceholder, children, disabled = false, helpText, hideTags, hideQueryField, disableQueryField = false, i18n, mediaQuery: { isNavigationCollapsed } } = this.props; const { resourceName } = this.context; const { open, readyForFocus } = this.state; const backdropMarkup = open ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ScrollLock, null), /*#__PURE__*/React.createElement("div", { className: styles.Backdrop, onClick: this.closeFilters })) : null; const filtersContentMarkup = filters.map((filter, index) => { const filterIsOpen = this.state[`${filter.key}${Suffix.Filter}`] === true; const icon = filterIsOpen ? ChevronUpMinor : ChevronDownMinor; const className = classNames(styles.FilterTriggerContainer, filterIsOpen && styles.open, index === 0 && styles.first, filters.length !== 1 && index === filters.length - 1 && styles.last); const appliedFilterContent = this.getAppliedFilterContent(filter.key); const appliedFilterBadgeMarkup = appliedFilterContent ? /*#__PURE__*/React.createElement("div", { className: styles.AppliedFilterBadgeContainer }, /*#__PURE__*/React.createElement(Badge, { status: "new" }, appliedFilterContent)) : null; const collapsibleID = `${filter.key}Collapsible`; const buttonClassName = classNames(styles.FilterTrigger); return /*#__PURE__*/React.createElement("div", { key: filter.key, className: className }, /*#__PURE__*/React.createElement("button", { onClick: () => this.toggleFilter(filter.key), className: buttonClassName, id: `${filter.key}ToggleButton`, type: "button", "aria-controls": collapsibleID, "aria-expanded": filterIsOpen }, /*#__PURE__*/React.createElement("div", { className: styles.FilterTriggerLabelContainer }, /*#__PURE__*/React.createElement("h3", { className: styles.FilterTriggerTitle }, /*#__PURE__*/React.createElement(Text, { variant: "bodyMd", as: "span", color: this.props.disabled || filter.disabled ? 'subdued' : undefined }, filter.label)), /*#__PURE__*/React.createElement("span", { className: styles.FilterTriggerIcon }, /*#__PURE__*/React.createElement(Icon, { source: icon, color: "base" }))), appliedFilterBadgeMarkup), /*#__PURE__*/React.createElement(Collapsible, { id: collapsibleID, open: filterIsOpen, onAnimationEnd: this.setReadyForFocus(true) }, /*#__PURE__*/React.createElement("div", { className: styles.FilterNodeContainer }, /*#__PURE__*/React.createElement(Focus, { disabled: !filterIsOpen || !readyForFocus || !open, root: this.focusNode }, this.generateFilterMarkup(filter))))); }); const appliedFiltersCount = appliedFilters ? appliedFilters.length : 0; const moreFiltersLabel = hideTags && appliedFiltersCount > 0 ? i18n.translate('Polaris.Filters.moreFiltersWithCount', { count: appliedFiltersCount }) : i18n.translate('Polaris.Filters.moreFilters'); const rightActionMarkup = filters.length ? /*#__PURE__*/React.createElement("div", { ref: this.moreFiltersButtonContainer }, /*#__PURE__*/React.createElement(Button, { onClick: this.toggleFilters, disabled: disabled }, moreFiltersLabel)) : null; const filterResourceName = resourceName || { singular: i18n.translate('Polaris.ResourceList.defaultItemSingular'), plural: i18n.translate('Polaris.ResourceList.defaultItemPlural') }; const transformedFilters = this.transformFilters(filters); const filtersControlMarkup = /*#__PURE__*/React.createElement(ConnectedFilterControl, { rightPopoverableActions: transformedFilters, rightAction: rightActionMarkup, auxiliary: children, disabled: disabled, forceShowMorefiltersButton: filters.length > transformedFilters.length, queryFieldHidden: hideQueryField }, hideQueryField ? null : /*#__PURE__*/React.createElement(TextField, { placeholder: queryPlaceholder || i18n.translate('Polaris.Filters.filter', { resourceName: filterResourceName.plural }), onChange: onQueryChange, onBlur: onQueryBlur, onFocus: onQueryFocus, value: queryValue, focused: focused, label: queryPlaceholder || i18n.translate('Polaris.Filters.filter', { resourceName: filterResourceName.plural }), labelHidden: true, prefix: /*#__PURE__*/React.createElement("span", { className: styles.SearchIcon }, /*#__PURE__*/React.createElement(Icon, { source: SearchMinor })), clearButton: true, onClearButtonClick: onQueryClear, disabled: disabled || disableQueryField, autoComplete: "off" })); const filtersContainerHeaderClassname = classNames(styles.FiltersContainerHeader); const filtersDesktopHeaderMarkup = /*#__PURE__*/React.createElement("div", { className: filtersContainerHeaderClassname }, /*#__PURE__*/React.createElement(Text, { variant: "headingLg", as: "h3" }, moreFiltersLabel), /*#__PURE__*/React.createElement(Button, { icon: CancelSmallMinor, plain: true, accessibilityLabel: i18n.translate('Polaris.Filters.cancel'), onClick: this.closeFilters })); const filtersMobileHeaderMarkup = /*#__PURE__*/React.createElement("div", { className: filtersContainerHeaderClassname }, /*#__PURE__*/React.createElement(Button, { icon: CancelSmallMinor, plain: true, accessibilityLabel: i18n.translate('Polaris.Filters.cancel'), onClick: this.closeFilters }), /*#__PURE__*/React.createElement(Text, { variant: "headingLg", as: "h3" }, moreFiltersLabel), /*#__PURE__*/React.createElement(Button, { onClick: this.closeFilters, primary: true }, i18n.translate('Polaris.Filters.done'))); const filtersDesktopFooterClassname = classNames(styles.FiltersContainerFooter); const filtersDesktopFooterMarkup = /*#__PURE__*/React.createElement("div", { className: filtersDesktopFooterClassname }, /*#__PURE__*/React.createElement(Button, { onClick: this.handleClearAll, disabled: !this.hasAppliedFilters() }, i18n.translate('Polaris.Filters.clearAllFilters')), /*#__PURE__*/React.createElement("div", { ref: this.moreFiltersDoneButtonContainer }, /*#__PURE__*/React.createElement(Button, { onClick: this.closeFilters, primary: true }, i18n.translate('Polaris.Filters.done')))); const filtersMobileFooterMarkup = /*#__PURE__*/React.createElement("div", { className: styles.FiltersMobileContainerFooter }, this.hasAppliedFilters() ? /*#__PURE__*/React.createElement(Button, { onClick: onClearAll, fullWidth: true }, i18n.translate('Polaris.Filters.clearAllFilters')) : /*#__PURE__*/React.createElement("div", { className: styles.EmptyFooterState }, /*#__PURE__*/React.createElement(Text, { variant: "bodyMd", color: "subdued", as: "span" }, /*#__PURE__*/React.createElement("p", null, i18n.translate('Polaris.Filters.noFiltersApplied'))))); const shouldHideTagsContainer = !appliedFilters || appliedFilters.length < 1; const tagsMarkup = !hideTags ? /*#__PURE__*/React.createElement(TagsWrapper, { hidden: shouldHideTagsContainer }, /*#__PURE__*/React.createElement("div", { className: styles.TagsContainer, "aria-live": "polite" }, (appliedFilters || []).map(filter => { return /*#__PURE__*/React.createElement(Tag, { key: filter.key, onRemove: () => { filter.onRemove(filter.key); }, disabled: disabled }, filter.label); }))) : null; const filtersMobileContainerContentClassName = classNames(styles.FiltersMobileContainerContent); const filtersDesktopContainerContentClassName = classNames(styles.FiltersDesktopContainerContent); const filtersContainerMarkup = isNavigationCollapsed ? /*#__PURE__*/React.createElement(Sheet, { accessibilityLabel: moreFiltersLabel, open: open, onClose: this.closeFilters, onEntered: this.setReadyForFocus(true), onExit: this.setReadyForFocus(false) }, filtersMobileHeaderMarkup, /*#__PURE__*/React.createElement(Scrollable, { className: filtersMobileContainerContentClassName, shadow: true }, filtersContentMarkup, filtersMobileFooterMarkup)) : /*#__PURE__*/React.createElement(Sheet, { accessibilityLabel: moreFiltersLabel, open: open, onClose: this.closeFilters, onEntered: this.setReadyForFocus(true), onExit: this.setReadyForFocus(false) }, /*#__PURE__*/React.createElement("div", { className: styles.FiltersContainer }, filtersDesktopHeaderMarkup, /*#__PURE__*/React.createElement(Scrollable, { className: filtersDesktopContainerContentClassName, shadow: true }, filtersContentMarkup), filtersDesktopFooterMarkup)); const helpTextMarkup = helpText ? /*#__PURE__*/React.createElement("div", { id: "FiltersHelpText", className: styles.HelpText }, /*#__PURE__*/React.createElement(Text, { variant: "bodyMd", color: "subdued", as: "span" }, helpText)) : null; return /*#__PURE__*/React.createElement(WithinFilterContext.Provider, { value: true }, /*#__PURE__*/React.createElement("div", { className: styles.Filters }, filtersControlMarkup, filtersContainerMarkup, tagsMarkup, helpTextMarkup, backdropMarkup, /*#__PURE__*/React.createElement(KeypressListener, { keyCode: Key.Escape, handler: this.closeFilters }))); } hasAppliedFilters() { const { appliedFilters, queryValue } = this.props; const filtersApplied = Boolean(appliedFilters && appliedFilters.length > 0); const queryApplied = Boolean(queryValue && queryValue !== ''); return filtersApplied || queryApplied; } getAppliedFilterContent(key) { const { appliedFilters } = this.props; if (!appliedFilters) { return undefined; } const filter = appliedFilters.find(filter => filter.key === key); return filter == null ? undefined : filter.label; } getAppliedFilterRemoveHandler(key) { const { appliedFilters } = this.props; if (!appliedFilters) { return undefined; } const filter = appliedFilters.find(filter => filter.key === key); return filter == null ? undefined : filter.onRemove; } openFilters() { this.setState({ open: true }); } toggleFilter(key) { if (this.state[`${key}${Suffix.Filter}`] === true) { this.setState({ readyForFocus: false, [`${key}${Suffix.Filter}`]: false }); } else { this.setState({ readyForFocus: false, [`${key}${Suffix.Filter}`]: true }); } } openFilterShortcut(key) { this.setState({ [`${key}${Suffix.Shortcut}`]: true }); } closeFilterShortcut(key) { this.setState({ [`${key}${Suffix.Shortcut}`]: false }); } toggleFilterShortcut(key) { if (this.state[`${key}${Suffix.Shortcut}`] === true) { this.closeFilterShortcut(key); } else { this.openFilterShortcut(key); } } transformFilters(filters) { const transformedActions = []; getShortcutFilters(filters).forEach(filter => { const { key, label, disabled } = filter; transformedActions.push({ popoverContent: this.generateFilterMarkup(filter), popoverOpen: Boolean(this.state[`${key}${Suffix.Shortcut}`]), key, content: label, disabled, onAction: () => this.toggleFilterShortcut(key) }); }); return transformedActions; } generateFilterMarkup(filter) { const i18n = this.props.i18n; const removeCallback = this.getAppliedFilterRemoveHandler(filter.key); const removeHandler = removeCallback == null ? undefined : () => { removeCallback(filter.key); }; const clearButtonMarkup = !filter.hideClearButton && /*#__PURE__*/React.createElement(Button, { plain: true, disabled: removeHandler == null, onClick: removeHandler, accessibilityLabel: i18n.translate('Polaris.Filters.clearLabel', { filterName: filter.label }) }, i18n.translate('Polaris.Filters.clear')); return /*#__PURE__*/React.createElement("div", { ref: this.focusNode }, /*#__PURE__*/React.createElement(Stack, { vertical: true, spacing: "tight" }, filter.filter, clearButtonMarkup)); } } FiltersInner.contextType = ResourceListContext; function getShortcutFilters(filters) { return filters.filter(filter => filter.shortcut === true); } function Filters(props) { const i18n = useI18n(); const mediaQuery = useMediaQuery(); return /*#__PURE__*/React.createElement(FiltersInner, Object.assign({}, props, { i18n: i18n, mediaQuery: mediaQuery })); } export { Filters };