UNPKG

@workday/canvas-kit-labs-react

Version:

Canvas Kit Labs is an incubator for new and experimental components. Since we have a rather rigorous process for getting components in at a production level, it can be valuable to make them available earlier while we continuously iterate on the API/functi

264 lines (263 loc) • 11.1 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import * as React from 'react'; import { colors, space } from '@workday/canvas-kit-react/tokens'; import { styled, generateUniqueId, filterOutProps, accessibleHideStyles, } from '@workday/canvas-kit-react/common'; import { TertiaryButton } from '@workday/canvas-kit-react/button'; import { searchIcon, xIcon } from '@workday/canvas-system-icons-web'; import { FormField } from '@workday/canvas-kit-react/form-field'; import { Combobox } from '@workday/canvas-kit-labs-react/combobox'; import { TextInput } from '@workday/canvas-kit-react/text-input'; import { searchThemes, SearchTheme } from './themes'; import chroma from 'chroma-js'; function getInputColors(theme, isFocused) { return { background: isFocused && theme.backgroundFocus ? theme.backgroundFocus : theme.background, backgroundHover: theme.backgroundHover, color: isFocused && theme.colorFocus ? theme.colorFocus : theme.color, placeholderColor: isFocused && theme.placeholderColorFocus ? theme.placeholderColorFocus : theme.placeholderColor, boxShadow: isFocused && theme.boxShadowFocus ? theme.boxShadowFocus : theme.boxShadow, }; } const formCollapsedBackground = colors.frenchVanilla100; const maxWidth = 480; const minWidth = 120; const StyledSearchForm = styled('form')({ position: 'relative', flexGrow: 1, display: 'flex', alignItems: 'center', marginLeft: space.m, minWidth: minWidth, }, ({ isCollapsed, showForm, rightAlign, grow }) => { const collapseStyles = isCollapsed ? { top: 0, right: 0, left: 0, bottom: 0, margin: 0, position: showForm ? 'absolute' : 'relative', backgroundColor: showForm ? formCollapsedBackground : 'rgba(0, 0, 0, 0)', transition: 'background-color 120ms', maxWidth: showForm ? 'none' : `calc(${space.xl} + ${space.xxs})`, minWidth: `calc(${space.xl} + ${space.xs})`, overflow: showForm ? 'visible' : 'hidden', zIndex: 1, } : {}; const rightAlignStyles = rightAlign ? { textAlign: 'right', maxWidth: grow ? '100%' : maxWidth, } : {}; return { ...rightAlignStyles, ...collapseStyles }; }); const SearchContainer = styled('div')({ position: `relative`, display: 'flex', alignItems: 'center', width: `100%`, textAlign: 'left', }, ({ height }) => ({ minHeight: height, })); const SearchCombobox = styled(Combobox)({ width: `100%`, }); const SearchIcon = styled(TertiaryButton, { shouldForwardProp: filterOutProps(['isHidden', 'isCollapsed']), })(({ isCollapsed, isHidden }) => { return { position: `absolute`, margin: isCollapsed ? `auto ${space.xxs}` : `auto ${space.xxxs}`, top: 0, bottom: 0, left: 0, padding: 0, zIndex: 3, display: isHidden ? 'none' : 'flex', }; }); const CloseButton = styled(TertiaryButton, { shouldForwardProp: filterOutProps(['isCollapsed', 'showForm']), })(({ isCollapsed, showForm }) => { const collapseStyles = isCollapsed && showForm ? { display: 'inline-block', } : { display: 'none', }; return { position: `absolute`, top: 0, bottom: 0, right: 0, margin: `auto ${space.xxs}`, zIndex: 3, ...collapseStyles, }; }); const SearchField = styled(FormField)(({ isCollapsed, showForm, grow, height }) => { return { display: (isCollapsed && showForm) || !isCollapsed ? 'inline-block' : 'none', width: '100%', height: height, maxWidth: isCollapsed || grow ? '100%' : maxWidth, marginBottom: space.zero, '> div': { display: 'block', }, }; }); const SearchInput = styled(TextInput)(({ isCollapsed, inputColors, grow, height }) => { const collapseStyles = isCollapsed ? { fontSize: '20px', paddingLeft: `calc(${space.xl} + ${space.s})`, paddingRight: `calc(${space.xl} + ${space.s})`, maxWidth: 'none', minWidth: 0, backgroundColor: `rgba(0, 0, 0, 0)`, height: height, } : { maxWidth: grow ? '100%' : maxWidth, minWidth: minWidth, paddingLeft: `calc(${space.xl} + ${space.xxs})`, paddingRight: space.xl, backgroundColor: inputColors.background, height: height, }; return { fontSize: '14px', boxShadow: inputColors.boxShadow, color: inputColors.color, border: 'none', WebkitAppearance: 'none', transition: 'background-color 120ms, color 120ms, box-shadow 200ms, border-color 200ms', zIndex: 2, width: '100%', '&::-webkit-search-cancel-button': { display: 'none', }, '&::placeholder': { color: inputColors.placeholderColor, }, '&:placeholder-shown': { textOverflow: 'ellipsis', }, '&:not([disabled])': { '&:focus, &:active': { outline: 'none', boxShadow: inputColors.boxShadow, }, '&:hover': { backgroundColor: inputColors.backgroundHover, }, }, ...collapseStyles, }; }); class SearchForm extends React.Component { constructor() { super(...arguments); this.inputRef = React.createRef(); this.openRef = React.createRef(); this.defaultLabelId = generateUniqueId(); this.state = { showForm: false, searchQuery: '', isFocused: false, }; this.getTheme = () => { let theme; if (typeof this.props.searchTheme === 'number') { theme = searchThemes[this.props.searchTheme]; } else if (this.props.searchTheme) { theme = this.props.searchTheme; } else { theme = searchThemes[SearchTheme.Light]; } return theme; }; this.getThemeColors = () => { const theme = this.props.isCollapsed && this.state.showForm ? searchThemes[SearchTheme.Transparent] : this.getTheme(); return getInputColors(theme, this.state.isFocused); }; this.getIconButtonType = () => { let background = this.getThemeColors().background || `#fff`; if (this.props.isCollapsed && this.state.showForm) { background = formCollapsedBackground; } const isDarkBackground = chroma(background).get('lab.l') < 70; return isDarkBackground ? 'inverse' : undefined; }; this.handleSubmit = (event) => { event.preventDefault(); if (this.props.allowEmptyStringSearch || this.state.searchQuery.trim()) { this.props.onSubmit(event); } else { this.focusInput(); } }; this.openCollapsedSearch = () => { if (this.props.isCollapsed && !this.state.showForm) { this.setState({ showForm: true }); } }; this.closeCollapsedSearch = () => { if (this.props.isCollapsed && this.state.showForm) { this.setState({ showForm: false }); } }; this.focusInput = () => { if (this.inputRef.current) { this.inputRef.current.focus(); } }; this.focusOpen = () => { if (this.openRef.current) { this.openRef.current.focus(); } }; this.handleFocus = () => { this.setState({ isFocused: true }); }; this.handleBlur = () => { this.setState({ isFocused: false }); }; this.handleSearchInputChange = (event) => { event.preventDefault(); this.setState({ searchQuery: event.target.value }); if (this.props.onInputChange) { this.props.onInputChange(event); } }; } componentDidUpdate(prevProps, prevState) { const showFormToggled = this.state.showForm !== prevState.showForm; if (showFormToggled) { if (this.state.showForm) { this.focusInput(); } else { this.focusOpen(); } } } render() { const { clearButtonAriaLabel = 'Reset Search Form', placeholder = 'Search', inputLabel = 'Search', submitAriaLabel = 'Search', openButtonAriaLabel = 'Open Search', closeButtonAriaLabel = 'Cancel', labelId = this.defaultLabelId, showClearButton = true, height = 40, grow, onSubmit, isCollapsed, onInputChange, autocompleteItems, initialValue, searchTheme, rightAlign, allowEmptyStringSearch = false, ...elemProps } = this.props; return (_jsx(StyledSearchForm, { role: "search", action: ".", rightAlign: rightAlign, grow: grow, "aria-labelledby": labelId, isCollapsed: isCollapsed, onSubmit: this.handleSubmit, showForm: this.state.showForm, ...elemProps, children: _jsxs(SearchContainer, { height: height, children: [_jsx(SearchIcon, { "aria-label": submitAriaLabel, icon: searchIcon, isCollapsed: isCollapsed, variant: this.getIconButtonType(), type: "submit", isHidden: !!isCollapsed && !this.state.showForm }), _jsx(SearchIcon, { "aria-label": openButtonAriaLabel, icon: searchIcon, isCollapsed: isCollapsed, variant: this.getIconButtonType(), onClick: this.openCollapsedSearch, ref: this.openRef, type: "button", isHidden: !isCollapsed || (!!isCollapsed && this.state.showForm) }), _jsxs(SearchField, { grow: grow, id: labelId, isCollapsed: isCollapsed, showForm: this.state.showForm, height: height, children: [_jsx(FormField.Label, { cs: accessibleHideStyles, children: inputLabel }), _jsx(SearchCombobox, { initialValue: initialValue, clearButtonVariant: this.getIconButtonType(), autocompleteItems: autocompleteItems, onChange: this.handleSearchInputChange, onFocus: this.handleFocus, onBlur: this.handleBlur, showClearButton: !isCollapsed && showClearButton, clearButtonAriaLabel: clearButtonAriaLabel, labelId: labelId, children: _jsx(FormField.Input, { as: SearchInput, ref: this.inputRef, cs: { maxWidth: grow ? '100%' : maxWidth }, value: this.state.searchQuery, placeholder: placeholder, isCollapsed: isCollapsed, inputColors: this.getThemeColors(), height: height, name: "search", autoComplete: "off" }) })] }), _jsx(CloseButton, { "aria-label": closeButtonAriaLabel, icon: xIcon, isCollapsed: isCollapsed, showForm: this.state.showForm, onClick: this.closeCollapsedSearch, type: "button" })] }) })); } } SearchForm.Theme = SearchTheme; export { SearchForm };