Simple Select
1type OptionItem = {2 id: string | number;3 value: string | number;4};56export interface ISimpleSelectProps extends Omit<React.HTMLProps<HTMLDivElement>, 'value' | 'onSelect'> {7 hasError?: boolean;8 value?: string | number;9 selectInputStyles?: SerializedStyles;10 optionsListStyles?: SerializedStyles;11 optionStyles?: SerializedStyles;12 options?: OptionItem[];13 onSelect: (event?: React.PointerEvent<HTMLLIElement>) => void;14}
Themed Simple Select1import {SimpleSelect} from 'neutrino-ui';23const optionsList = [4 {id: -1, value: 'All items'},5 {id: 1, value: 'Item 1'},6 {id: 2, value: 'Item 2'},7 {id: 3, value: 'Item 3'},8 {id: 4, value: 'Item 4'},9 {id: 5, value: 'Item 5'},10 {id: 6, value: 'Item 6'},11 {id: 7, value: 'Item 7'},12 {id: 8, value: 'Item 8'},13 {id: 9, value: 'Item 9'},14 {id: 10, value: 'Item 10'},15 {id: 11, value: 'Item 11'},16 {id: 12, value: 'Item 12'},17];1819<SimpleSelect20 options={optionsList}21 css={{width: 300}}22 value={selected}23 onSelect={handleItemClick}24 selectInputStyles={css({borderRadius: 8})}25 optionsListStyles={css({borderRadius: 8, height: 300, overflow: 'auto'})}26 optionStyles={css({color: 'green'})}27/>
MultiSelect1import {ThemeProvider} from 'emotion-theming';2import {createTheme, SimpleSelect} from 'neutrino-ui';34const theme = createTheme({5 colors: {6 pageElementsColors: {7 formElements: '#325b72',8 selectedItem: '#293676',9 border: '#0FC0FC',10 },11 textColors: {12 text: '#fff',13 },14 },15 typography: {16 span: {17 color: '#fff',18 fontSize: '14px',19 },20 },21});2223const optionsList = [24 {id: -1, value: 'All items'},25 {id: 1, value: 'Item 1'},26 {id: 2, value: 'Item 2'},27 {id: 3, value: 'Item 3'},28 {id: 4, value: 'Item 4'},29 {id: 5, value: 'Item 5'},30 {id: 6, value: 'Item 6'},31 {id: 7, value: 'Item 7'},32 {id: 8, value: 'Item 8'},33 {id: 9, value: 'Item 9'},34 {id: 10, value: 'Item 10'},35 {id: 11, value: 'Item 11'},36 {id: 12, value: 'Item 12'},37];3839<ThemeProvider theme={theme}>40 <SimpleSelect41 options={optionsList}42 css={{width: 300}}43 value={selected}44 onSelect={handleItemClick}45 selectInputStyles={css({borderRadius: 8})}46 optionsListStyles={css({borderRadius: 8, height: 300, overflow: 'auto'})}47 />48</ThemeProvider>
Prefix: All items
1import React from 'react';2import {css} from '@emotion/core';3import styled from '@emotion/styled';4import {5 ArrowIcon,6 Combobox,7 useCombobox,8 useTheme,9 Span,10 Dropdown,11} from 'neutrino-ui';1213type OptionItem = {14 id: string | number;15 value: string | number;16};1718type Props = {19 options?: OptionItem[];20 value?: number[];21 onSelect: (values: number[]) => void;22};2324const TextBox = styled.div'25 display: flex;26 flex-flow: row nowrap;27 justify-content: flex-start;28 align-items: center;29 width: 100%;30 height: 48px;31 padding: 12px 16px;32';333435function SelectBox({children, styles}: any) {36 const {handleToggle, isOpen} = useToggle();37 const {colors} = useTheme();38 const baseCss = css({39 border: ' 1px $ {isOpen ? colors.pageElementsColors.activeBorder : colors.pageElementsColors.border} solid ',40 '&:hover': {cursor: 'pointer', borderColor: colors.pageElementsColors.activeBorder},41 color: colors.textColors.text,42 backgroundColor: colors.pageElementsColors.formElements,43 });4445 return (46 <TextBox onClick={handleToggle} css={[baseCss, styles]}>47 {children}48 <ToggleArrowIcon />49 </TextBox>50 );51}5253function Select({options, value, onSelect}: Props) {54 const [selectedChecks, setCheck] = React.useState<number[]>(value);55 const [selectRect, setSelectRect] = React.useState(null);56 const theme = useTheme();57 const mSelectRef = React.useRef<HTMLDivElement>(null);58 const optionsRef = React.useRef<HTMLDivElement>(null);59 const {isOpen, handleClose} = useToggle();6061 const listBaseCss = css({62 margin: 0,63 padding: 0,64 listStyle: 'none',65 border: ' 1px $ {theme.colors.pageElementsColors.border} solid ',66 boxSizing: 'border-box',67 backgroundColor: theme.colors.pageElementsColors.formElements,68 width: '100%',69 height: 200,70 overflow: 'auto',71 });7273 const optionBaseCss = css({74 padding: '8px 16px',75 borderBottom: '1px #ccc solid',76 margin: 0,77 color: theme.colors.textColors.text,78 fontSize: 14,79 cursor: 'pointer',80 });8182 const handleSelectItem = React.useCallback(83 (event: React.MouseEvent<HTMLLIElement>) => {84 const {value} = event.currentTarget;85 if (selectedChecks.includes(value)) {86 const updatedChecks = selectedChecks.filter(c => c !== value);87 setCheck(updatedChecks);88 } else {89 setCheck(prev => [...prev, value]);90 }91 },92 [selectedChecks],93 );9495 const applySelect = React.useCallback(() => {96 onSelect(selectedChecks);97 handleClose();98 }, [handleClose, onSelect, selectedChecks]);99100 const isSelected = React.useCallback((checkId: number = -1) => selectedChecks.includes(checkId), [101 selectedChecks,102 ]);103104 React.useEffect(() => {105 const handleClickOutside = (e: PointerEvent) => {106 if (e.target instanceof HTMLElement && isOpen) {107 const optionsList = optionsRef?.current;108 const selectInput = mSelectRef?.current;109 if (optionsList?.contains(e.target) || selectInput?.contains(e.target)) {110 return;111 }112 handleClose();113 }114 };115116 const handleScroll = (e: PointerEvent) =>117 window.requestAnimationFrame(() => {118 if (e.target instanceof HTMLElement && isOpen) {119 const optionsList = optionsRef?.current;120 if (optionsList?.contains(e.target)) {121 return;122 }123 setSelectRect(mSelectRef?.current.getBoundingClientRect());124 }125 });126127 if (isOpen) {128 setSelectRect(mSelectRef?.current.getBoundingClientRect());129 document.addEventListener('click', handleClickOutside);130 window.addEventListener('scroll', handleScroll, true);131 }132 return () => {133 document.removeEventListener('click', handleClickOutside);134 window.removeEventListener('scroll', handleScroll, true);135 };136 }, [handleClose, isOpen]);137138 return (139 <div css={{position: 'relative', width: 300}} ref={mSelectRef}>140 <SelectBox>141 <Span>Prefix: </Span>142 <Span>{selectedChecks.length > 0 ? ' ($ {selectedChecks.length}) ' : 'All items'}</Span>143 </SelectBox>144 <Dropdown isOpen={isOpen} ref={optionsRef} parentBound={isOpen ? selectRect : undefined}>145 <div css={listBaseCss}>146 <ul>147 {options?.map(option => {148 return (149 <li150 key={option.id}151 value={option.id}152 css={[153 optionBaseCss,154 css({155 backgroundColor: isSelected(Number(option.id))156 ? theme.colors.pageElementsColors.selectedItem157 : 'transparent',158 }),159 ]}160 onClick={handleSelectItem}161 >162 {option.value}163 </li>164 );165 })}166 </ul>167 <button onClick={applySelect}>Apply</button>168 </div>169 </Dropdown>170 </div>171 );172}173174export function MultiSelect(props: Props) {175 return (176 <ToggleProvider>177 <Select {...props} />178 </ToggleProvider>179 );180}181182// Somewhere in app...183<MultiSelect options={optionsList} value={items} onSelect={handleMultiSelect} />