react-select#ValueType TypeScript Examples
The following examples show how to use
react-select#ValueType.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts From utopia with MIT License | 7 votes |
/**
* A type guard for React Select's onChange values, which can either be a value
* or an array of values.
*/
export function isOptionsType<T extends OptionTypeBase>(
value: ValueType<T>,
): value is OptionsType<T> {
return Array.isArray(value)
}
Example #2
Source File: className-subsection.tsx From utopia with MIT License | 6 votes |
function valueTypeAsArray<T>(valueType: ValueType<T>): ReadonlyArray<T> {
if (valueType == null) {
return []
} else if (isOptionsType(valueType)) {
return valueType
} else {
return [valueType]
}
}
Example #3
Source File: utils.ts From utopia with MIT License | 6 votes |
/**
* A type guard for React Select's onChange values, which can either be a value
* or an array of values.
*/
export function isOptionType<T extends OptionTypeBase>(value: ValueType<T>): value is T {
return value != null && !Array.isArray(value)
}
Example #4
Source File: popup-list.tsx From utopia with MIT License | 6 votes |
getValueOfValueType = (value: ValueType<SelectOption>): SelectOption['value'] => {
if (Array.isArray(value)) {
if (value.length > 0) {
return value[0].value
} else {
return undefined
}
} else {
return (value as unknown as SelectOption).value
}
}
Example #5
Source File: popup-list.tsx From utopia with MIT License | 6 votes |
getIndexOfValue = (
value: ValueType<SelectOption>,
options: OptionsType<SelectOption>,
): number => {
const firstValue = getValueOfValueType(value)
let allOptions: SelectOption[] = []
options.forEach((option) => {
allOptions.push(option)
if (option.options != null) {
allOptions.push(...option.options)
}
})
const index = allOptions.findIndex((option) => option.value === firstValue)
return Math.max(0, index)
}
Example #6
Source File: GuideSelect.tsx From fhir-validator-app with Apache License 2.0 | 6 votes |
export function GuideSelect({ igs }: GuideSelectProps): ReactElement {
const [formState, dispatch] = useContext(FormContext);
const value = formState.implementationGuide;
const empty: (SelectOption | null | undefined)[] = [];
const handleChange = (value: ValueType<SelectOption>): void =>
dispatch({ name: 'implementationGuide', value: empty.concat(value)[0] });
const options = igs?.map((ig) => new SelectOption(ig, ig));
return (
<div>
<label htmlFor="implementation-guide">
Pick an Implementation Guide to validate against:
</label>
<Select
isClearable
isLoading={!options}
options={options}
name="implementation-guide"
id="implementation-guide"
value={value}
onChange={handleChange}
/>
</div>
);
}
Example #7
Source File: ProfileSelect.tsx From fhir-validator-app with Apache License 2.0 | 6 votes |
export function ProfileSelect({ options }: ProfileSelectProps): ReactElement {
const [formState, dispatch] = useContext(FormContext);
const ig = formState.implementationGuide?.value;
const value = formState.profileSelect;
const handleChange = (value: ValueType<SelectOption>): void =>
dispatch({ name: 'profileSelect', value });
return (
<div>
<label htmlFor="profile-select">Select a profile:</label>
<Select
isClearable
isDisabled={!ig}
isLoading={!!ig && !options}
options={options}
name="profile-select"
id="profile-select"
value={value}
onChange={handleChange}
/>
</div>
);
}
Example #8
Source File: SelectWidget.tsx From ke with MIT License | 5 votes |
BaseSelectWidget = forwardRef<HTMLSelectElement, BaseSelectWidgetProps>(
(props: BaseSelectWidgetProps, ref): JSX.Element => {
const {
name,
helpText,
description,
displayValue,
containerStore,
mainDetailObject,
data,
style,
setInitialValue,
handleChange,
required,
isDisabled = false,
isClearable = false,
placeholder = 'Выберите значение',
containerProps,
labelContainerProps,
widgetStyles: extenrnalStyles,
} = props
const context = containerStore.getState()
const [value, label] = getSelectContent(name, mainDetailObject, displayValue, context)
const isRequired = getAccessorWithDefault(required, mainDetailObject, context, false)
const [resultOptions, setResultOptions] = useState<SelectObject[]>([])
setInitialValue({ [name]: value })
const widgetStyles = {
menuPortal: (base: object) => ({ ...base, zIndex: 9999 }),
...extenrnalStyles,
}
useEffect(() => {
const responseOptions = getAccessor(data, mainDetailObject, context)
return applyCallback(responseOptions, setResultOptions)
}, [data, mainDetailObject, context])
const formatOption = (option: { value: any; label?: string; text?: string }): { value: any; label: string } => ({
value: option.value,
label: option?.label || option?.text || '',
})
const options = React.useMemo(() => resultOptions.map((option) => formatOption(option)), [resultOptions])
const { getDataTestId } = useCreateTestId()
return (
<WidgetWrapper
name={name}
style={style}
helpText={helpText}
description={description}
required={isRequired}
containerProps={containerProps}
labelContainerProps={labelContainerProps}
{...getDataTestId(props)}
>
<Select
inputRef={ref}
options={options}
defaultValue={value ? { value, label } : undefined}
onChange={(changeValue: ValueType<object | object[], boolean>) => handleChange(changeValue)}
styles={modifyStyles(widgetStyles)}
isDisabled={isDisabled}
components={components}
isClearable={isClearable}
placeholder={placeholder}
name={name}
/>
</WidgetWrapper>
)
}
)
Example #9
Source File: MultiSelectWidget.tsx From ke with MIT License | 4 votes |
MultiSelectWidget = (props: MultiSelectWidgetProps): JSX.Element => {
const {
name,
mainDetailObject,
optionLabel,
optionValue,
style,
helpText,
provider,
setInitialValue,
submitChange,
containerStore,
cacheTime,
targetPayload,
optionLabelMenu,
optionLabelValue,
staleTime,
componentsClasses,
} = props
const context = containerStore.getState()
const { targetUrl, content, dataResourceUrl, isRequired, widgetDescription } = useWidgetInitialization({
...props,
context,
})
const effectiveCacheTime = getAccessor(cacheTime, mainDetailObject, context)
const [value, setValue] = React.useState<object[] | null>(content as object[] | null)
setInitialValue(value ? getPayload(value, name, targetPayload) : null)
const handleChange = (changeValue: ValueType<MultiSelectValue, true>): void => {
setValue(changeValue as MultiSelectValue[])
const widgetPayload = getPayload(changeValue, name, targetPayload)
pushAnalytics({
eventName: EventNameEnum.FOREIGN_KEY_SELECT_OPTION_CHANGE,
widgetType: WidgetTypeEnum.INPUT,
value: widgetPayload,
objectForAnalytics: mainDetailObject,
...props,
})
submitChange({ url: targetUrl, payload: widgetPayload })
}
React.useMemo(() => {
setValue(content as object[] | null)
}, [content])
const { getDataTestId } = useCreateTestId()
return (
<WidgetWrapper
name={name}
style={style}
helpText={helpText}
description={widgetDescription}
required={isRequired}
{...getDataTestId(props)}
>
<AsyncSelectWidget
provider={provider}
cacheTime={effectiveCacheTime}
dataResourceUrl={dataResourceUrl}
handleChange={handleChange}
closeMenuOnSelect={false}
value={value}
isMulti
getOptionLabel={(val: object | null) => optionLabel(val, mainDetailObject)}
getOptionValue={optionValue}
getOptionLabelMenu={
optionLabelMenu ? (val: object | null) => optionLabelMenu(val, mainDetailObject) : undefined
}
getOptionLabelValue={
optionLabelValue ? (val: object | null) => optionLabelValue(val, mainDetailObject) : undefined
}
staleTime={staleTime}
componentsClasses={componentsClasses}
/>
</WidgetWrapper>
)
}
Example #10
Source File: AsyncSelectWidget.tsx From ke with MIT License | 4 votes |
AsyncSelectWidget = ({
dataResourceUrl,
handleChange,
value,
getOptionLabel,
getOptionValue,
styles,
isClearable = false,
isMulti = false,
defaultOptions = false,
searchParamName = 'search',
placeholder = 'Введите значение',
getOptionLabelMenu,
getOptionLabelValue,
additionalValues = [],
isDisabled = false,
menuPlacement,
className,
staleTime,
componentsClasses,
name,
}: AsyncSelectWidgetProps): JSX.Element => {
const debounceValue = 500
const widgetStyles = {
...{
menuPortal: (base: object) => ({ ...base, zIndex: 9999 }),
},
...(styles !== undefined ? styles : {}),
}
const additionalValuesFromAccessor = getAccessor(additionalValues)
const formatOptionLabel = (
option: object | object[] | null,
{ context }: { context: 'menu' | 'value' }
): string | null => {
if (!option) {
return option
}
if (context === 'menu') {
return getOptionLabelMenu ? getOptionLabelMenu(option) : getOptionLabel(option)
}
return getOptionLabelValue ? getOptionLabelValue(option) : getOptionLabel(option)
}
const { resourceKey, params } = useMemo(() => {
const url = new URL(dataResourceUrl)
return {
resourceKey: url.origin.concat(url.pathname),
params: Object.fromEntries(url.searchParams.entries()),
}
}, [dataResourceUrl])
const cacheUniqs = useMemo(() => [dataResourceUrl], [dataResourceUrl])
return (
<StatefullAsyncSelect
resource={{
key: resourceKey,
requestConfig: {
params,
},
staleTime: getAccessor(staleTime),
}}
value={value}
onChange={(changeValue: ValueType<object | object[], boolean>) => handleChange(changeValue)}
defaultOptions={defaultOptions || additionalValuesFromAccessor}
isClearable={isClearable}
isMulti={isMulti as false | undefined}
menuPortalTarget={document.body}
styles={modifyStyles(widgetStyles)}
formatOptionLabel={formatOptionLabel}
getOptionValue={(option: object | object[] | null) => (option ? getOptionValue(option) : option)}
placeholder={placeholder}
isDisabled={isDisabled}
menuPlacement={menuPlacement}
className={className}
components={components}
debounceTimeout={debounceValue}
searchParamName={searchParamName}
cacheUniqs={cacheUniqs}
componentsClasses={componentsClasses}
name={name}
/>
)
}
Example #11
Source File: preview-devices.tsx From utopia with MIT License | 4 votes |
PreviewReactSelectDeviceSelector: React.FunctionComponent<
React.PropsWithChildren<PreviewReactSelectDeviceSelectorProps>
> = ({ value, onChange, caratOffset }) => {
const PreviewReactSelectSingleValue = (singleValueProps: any) => {
return components.SingleValue == null ? null : (
<components.SingleValue {...singleValueProps}>
<span
style={{
whiteSpace: 'nowrap',
maxWidth: '10em',
textOverflow: 'ellipsis',
overflow: 'hidden',
display: 'inline-block',
paddingRight: '.3em',
}}
>
{singleValueProps.children}
</span>
<svg
width='9'
height='5'
viewBox='0 0 9 5'
xmlns='http://www.w3.org/2000/svg'
style={{
position: 'relative',
bottom: caratOffset,
}}
>
<path
id='dropdown_control'
d='M1,1 C5,4.66 3,4.66 7,1'
strokeWidth='1'
fill='none'
stroke='rgb(155, 155, 155)'
/>
</svg>
</components.SingleValue>
)
}
const selectOnChange = React.useCallback(
(newValue: ValueType<DeviceReactSelectOption>) => {
if (newValue != null && !Array.isArray(newValue)) {
onChange(newValue as DeviceReactSelectOption)
}
},
[onChange],
)
return (
<Select
className={``}
classNamePrefix='preview-lightselect'
menuShouldScrollIntoView={true}
menuPlacement='auto'
value={value}
isClearable={false}
isSearchable={false}
isDisabled={false}
components={{ SingleValue: PreviewReactSelectSingleValue }}
onChange={selectOnChange}
options={deviceReactSelectOptionList}
styles={{
container: (base: any, state: any) => {
return {
...base,
backgroundColor: 'transparent',
boxShadow: 'none',
textAlign: 'left',
cursor: 'default',
}
},
menu: (base: any, state: any) => {
return {
...base,
width: '200px !important',
left: -12,
}
},
control: (base: any, state: any) => {
return {
display: 'inline-block',
}
},
valueContainer: (base: any, state: any) => {
return {
display: 'inline-block',
}
},
singleValue: (base: any, state: any) => {
return {
display: 'inline-block',
height: 18,
position: 'relative',
}
},
indicatorsContainer: () => {
return {
display: 'none',
}
},
}}
/>
)
}
Example #12
Source File: className-subsection.tsx From utopia with MIT License | 4 votes |
ClassNameControl = React.memo(() => {
const editorStoreRef = useRefEditorState((store) => store)
const theme = useColorTheme()
const targets = useEditorState(
(store) => store.editor.selectedViews,
'ClassNameSubsection targets',
)
const dispatch = useEditorState((store) => store.dispatch, 'ClassNameSubsection dispatch')
const [filter, setFilter] = React.useState('')
const isFocusedRef = React.useRef(false)
const shouldPreviewOnFocusRef = React.useRef(false)
const updateFocusedOption = usePubSubAtomWriteOnly(focusedOptionAtom)
const focusedValueRef = React.useRef<string | null>(null)
const focusTriggerCount = useEditorState(
(store) => store.editor.inspector.classnameFocusCounter,
'ClassNameSubsection classnameFocusCounter',
)
const inputRef = useInputFocusOnCountIncrease<CreatableSelect<TailWindOption>>(focusTriggerCount)
const clearFocusedOption = React.useCallback(() => {
shouldPreviewOnFocusRef.current = false
updateFocusedOption(null)
dispatch([EditorActions.clearTransientProps()], 'canvas')
}, [updateFocusedOption, dispatch])
const onBlur = React.useCallback(() => {
isFocusedRef.current = false
shouldPreviewOnFocusRef.current = false
clearFocusedOption()
}, [clearFocusedOption])
const onFocus = React.useCallback(() => {
isFocusedRef.current = true
}, [])
const options = useFilteredOptions(filter, 100)
React.useEffect(() => {
return function cleanup() {
dispatch([EditorActions.clearTransientProps()], 'canvas')
}
/** deps is explicitly empty */
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const { selectedClasses, elementPaths, isSettable } = useGetSelectedClasses()
const selectedOptions = selectedClasses.map(getTailwindOptionForClassName)
const elementPath = elementPaths[0]
const isMenuEnabled = isSettable && elementPaths.length === 1
const selectedOptionsLength = selectedOptions?.length ?? 0
const [isExpanded, setIsExpanded] = React.useState(selectedOptionsLength > 0)
const expandSection = React.useCallback(() => {
setIsExpanded(true)
queuedFocusTimeout = window.setTimeout(() => inputRef.current?.focus(), 0)
}, [inputRef])
const contractSection = React.useCallback(() => {
setIsExpanded(false)
if (queuedFocusTimeout != null) {
window.clearTimeout(queuedFocusTimeout)
}
}, [])
const toggleIsExpanded = React.useCallback(() => {
if (isExpanded) {
contractSection()
} else {
expandSection()
}
}, [isExpanded, expandSection, contractSection])
const triggerCountRef = React.useRef(focusTriggerCount)
React.useEffect(() => {
if (!isExpanded && focusTriggerCount > triggerCountRef.current) {
triggerCountRef.current = focusTriggerCount
expandSection()
}
}, [focusTriggerCount, isExpanded, expandSection])
const onChange = React.useCallback(
(newValueType: ValueType<TailWindOption>) => {
// As the value of the dropdown is changing, hide the selection
// controls so they can see the results of what they're doing.
EditorActions.hideAndShowSelectionControls(dispatch)
const newValue = valueTypeAsArray(newValueType)
if (elementPath != null) {
if (queuedDispatchTimeout != null) {
window.clearTimeout(queuedDispatchTimeout)
queuedDispatchTimeout = undefined
}
dispatch(
[
EditorActions.setProp_UNSAFE(
elementPath,
PP.create(['className']),
jsxAttributeValue(newValue.map((value) => value.value).join(' '), emptyComments),
),
EditorActions.clearTransientProps(),
],
'everyone',
)
}
},
[dispatch, elementPath],
)
const onInputChange = React.useCallback(
(newInput: string) => {
if (newInput === '') {
clearFocusedOption()
}
focusedValueRef.current = null
setFilter(newInput)
},
[clearFocusedOption, setFilter],
)
const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLElement>) => {
// As someone is typing, hide the selection
// controls so they can see the results of what they're doing.
EditorActions.hideAndShowSelectionControls(dispatch)
const shouldStopPreviewing =
filter === '' && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')
if (shouldStopPreviewing) {
clearFocusedOption()
} else {
shouldPreviewOnFocusRef.current = true
}
if (
event.key === 'ArrowUp' ||
event.key === 'ArrowDown' ||
event.key === 'PageUp' ||
event.key === 'PageDown' ||
event.key === 'Home' ||
event.key === 'End'
) {
// Any of these keys will jump the focus to the menu
focusedValueRef.current = null
}
if (
filter === '' &&
selectedOptions != null &&
(event.key === 'Backspace' || event.key === 'Delete')
) {
if (event.key === 'Delete' && focusedValueRef.current == null) {
// prevent the default react-select behaviour here, as it will delete the last value
// if nothing is focused, which feels wrong
event.preventDefault()
} else {
const updatedFilterText = focusedValueRef.current ?? last(selectedOptions)?.label
if (updatedFilterText != null) {
setFilter(updatedFilterText)
}
}
}
if (event.key === 'Escape') {
inputRef.current?.blur()
}
const namesByKey = applyShortcutConfigurationToDefaults(
editorStoreRef.current.userState.shortcutConfig,
)
handleShortcuts(namesByKey, event.nativeEvent, null, {
[UNDO_CHANGES_SHORTCUT]: () => {
return dispatch([EditorActions.undo()])
},
[REDO_CHANGES_SHORTCUT]: () => {
return dispatch([EditorActions.redo()])
},
})
},
[clearFocusedOption, dispatch, editorStoreRef, filter, inputRef, selectedOptions],
)
const multiValueLabel: styleFn = React.useCallback(
(base, { isFocused }) => {
const enabledColor = isFocused ? theme.inverted.textColor.value : theme.inverted.primary.value
const color = isMenuEnabled ? enabledColor : theme.fg8.value
const backgroundColor = isFocused ? theme.inverted.primary.value : theme.bg1.value
return {
...base,
label: 'multiValueLabel',
display: 'flex',
alignItems: 'center',
paddingTop: 2,
paddingBottom: 2,
paddingLeft: 6,
paddingRight: 0,
fontSize: 9,
borderRadius: 3,
color: color,
backgroundColor: backgroundColor,
}
},
[isMenuEnabled, theme],
)
const multiValueRemove: styleFn = React.useCallback(
(base, state) => ({
label: 'multiValueRemove',
width: isMenuEnabled ? 16 : 0,
height: UtopiaTheme.layout.inputHeight.small,
display: 'flex',
alignItems: 'center',
padding: 0,
overflow: 'hidden',
marginRight: 2,
backgroundImage: `url(${
(state.isFocused as boolean)
? UNSAFE_getIconURL('cross-in-translucent-circle', 'blue')
: UNSAFE_getIconURL('cross-small')
})`,
backgroundSize: 16,
backgroundPosition: 'center center',
':hover': {
backgroundImage: `url(${UNSAFE_getIconURL('cross-in-translucent-circle', 'blue')})`,
},
}),
[isMenuEnabled],
)
const multiValue: styleFn = React.useCallback(
(base, { isFocused, data }) => {
const backgroundColor = isFocused ? theme.inverted.primary.value : theme.bg1.value
if (isFocused) {
focusedValueRef.current = data.label
}
return {
label: 'multiValue',
fontWeight: 600,
color: theme.emphasizedForeground.value,
borderRadius: UtopiaTheme.inputBorderRadius,
display: 'flex',
marginRight: 4,
marginTop: 2,
marginBottom: 2,
minWidth: 0,
height: UtopiaTheme.layout.inputHeight.small,
boxShadow: `inset 0 0 0 1px ${
isFocused ? theme.inspectorFocusedColor.value : 'transparent'
}`,
overflow: 'hidden',
backgroundColor: backgroundColor,
}
},
[theme],
)
const option: styleFn = React.useCallback(
(base, { isFocused, isDisabled, value }) => {
if (
isFocusedRef.current &&
shouldPreviewOnFocusRef.current &&
isFocused &&
targets.length === 1
) {
const oldClassNameString =
selectedOptions == null ? '' : selectedOptions.map((v) => v.value).join(' ') + ' '
const newClassNameString = oldClassNameString + value
if (queuedDispatchTimeout != null) {
window.clearTimeout(queuedDispatchTimeout)
}
queuedDispatchTimeout = window.setTimeout(() => {
dispatch(
[
EditorActions.setPropTransient(
targets[0],
PP.create(['className']),
jsxAttributeValue(newClassNameString, emptyComments),
),
],
'canvas',
)
}, 10)
updateFocusedOption(value)
}
const color = isFocused ? theme.inverted.textColor.value : theme.textColor.value
const backgroundColor = isFocused ? theme.inverted.primary.value : theme.bg1.value
const borderRadius = isFocused ? 3 : 0
return {
minHeight: 27,
display: 'flex',
alignItems: 'center',
paddingLeft: 8,
paddingRight: 8,
backgroundColor: backgroundColor,
color: color,
cursor: isDisabled ? 'not-allowed' : 'default',
borderRadius: borderRadius,
}
// return base
},
[dispatch, targets, selectedOptions, updateFocusedOption, theme],
)
return (
<div
style={{
backgroundColor: theme.emphasizedBackground.value,
boxShadow: `0px 0px 1px 0px ${theme.neutralInvertedBackground.o(30).value}`,
margin: 4,
}}
>
<InspectorSubsectionHeader style={{ color: theme.primary.value }}>
<span style={{ flexGrow: 1, cursor: 'pointer' }} onClick={toggleIsExpanded}>
Class Names
</span>
<SquareButton highlight onClick={toggleIsExpanded}>
<ExpandableIndicator visible collapsed={!isExpanded} selected={false} />
</SquareButton>
</InspectorSubsectionHeader>
{when(
isExpanded,
<React.Fragment>
<UIGridRow padded variant='<-------------1fr------------->'>
<CreatableSelect
ref={inputRef}
autoFocus={false}
placeholder={isMenuEnabled ? 'Add class…' : ''}
isMulti
value={selectedOptions}
isDisabled={!isMenuEnabled}
onChange={onChange}
onInputChange={onInputChange}
components={{
IndicatorsContainer,
Input,
MultiValueRemove,
}}
className='className-inspector-control'
styles={{
container,
control,
valueContainer,
multiValue,
multiValueLabel,
multiValueRemove,
placeholder,
menu,
option,
}}
filterOption={AlwaysTrue}
options={options}
menuIsOpen={isMenuEnabled}
onBlur={onBlur}
onFocus={onFocus}
escapeClearsValue={true}
formatOptionLabel={formatOptionLabel}
onKeyDown={handleKeyDown}
maxMenuHeight={199}
inputValue={filter}
/>
</UIGridRow>
{when(isMenuEnabled, <FooterSection options={options} filter={filter} />)}
</React.Fragment>,
)}
</div>
)
})
Example #13
Source File: popup-list.tsx From utopia with MIT License | 4 votes |
getPortalPosition = (
referenceTop: number,
options: OptionsType<SelectOption>,
value: ValueType<SelectOption>,
scrollIndexOffset: number,
): {
menuTop: number
menuHeight: number
scrollTop: number
croppedTop: boolean
croppedBottom: boolean
} => {
const optionsLength = options.reduce((working, o) => {
if (o.options == null) {
return working + 1
} else {
return working + 1 + o.options.length
}
}, 0)
const windowHeight = window.innerHeight
const indexOfValue = getIndexOfValue(value, options)
const centredIndex = indexOfValue + scrollIndexOffset
const windowHeightAboveReference = referenceTop - WindowEdgePadding
const windowHeightBelowReference =
windowHeight - (windowHeightAboveReference + OptionHeight) - WindowEdgePadding
const optionPaddingElements = 1
const optionPaddingAboveSelected = OptionHeight * Math.min(optionPaddingElements, optionsLength)
const optionPaddingBelowSelected =
OptionHeight * Math.min(optionPaddingElements, optionsLength - centredIndex)
if (
windowHeightAboveReference > optionPaddingAboveSelected &&
windowHeightBelowReference > optionPaddingBelowSelected
) {
const numberCroppedTop = calculateOptionsToCutOff(
optionsLength,
windowHeightAboveReference,
optionsLength - centredIndex - 1,
)
const howManyElementsToShowAboveSelected = centredIndex - numberCroppedTop
const numberCroppedBottom = calculateOptionsToCutOff(
optionsLength,
windowHeightBelowReference,
centredIndex,
)
const howManyElementsToShowBelowSelected =
optionsLength - 1 - centredIndex - numberCroppedBottom
const croppedMenuHeight =
(howManyElementsToShowAboveSelected + howManyElementsToShowBelowSelected + 1) * OptionHeight
return {
menuTop:
referenceTop - 2 * menuVerticalPadding - howManyElementsToShowAboveSelected * OptionHeight,
menuHeight: croppedMenuHeight,
scrollTop: numberCroppedTop * OptionHeight,
croppedTop: numberCroppedTop > 0,
croppedBottom: numberCroppedBottom > 0,
}
} else {
if (windowHeightAboveReference > windowHeightBelowReference) {
const numberCroppedTop = calculateOptionsToCutOff(optionsLength, windowHeightAboveReference)
const numberCroppedBottom = calculateOptionsToCutOff(
optionsLength,
windowHeightBelowReference,
)
const menuHeight = Math.min(
optionsLength * OptionHeight,
windowHeightAboveReference - numberCroppedTop * OptionHeight,
)
return {
menuTop: referenceTop - 2 * menuVerticalPadding - menuHeight,
menuHeight: menuHeight,
scrollTop: 0,
croppedTop: numberCroppedTop > 0,
croppedBottom: numberCroppedBottom > 0,
}
} else {
const numberCroppedTop = calculateOptionsToCutOff(optionsLength, windowHeightAboveReference)
const numberCroppedBottom = calculateOptionsToCutOff(
optionsLength,
windowHeightBelowReference,
)
const menuHeight = Math.min(
optionsLength * OptionHeight,
windowHeightBelowReference - numberCroppedBottom * OptionHeight,
)
return {
menuTop: referenceTop - 2 * menuVerticalPadding + OptionHeight,
menuHeight: menuHeight,
scrollTop: 0,
croppedTop: numberCroppedTop > 0,
croppedBottom: numberCroppedBottom > 0,
}
}
}
}
Example #14
Source File: popup-list.tsx From utopia with MIT License | 4 votes |
PopupList = React.memo<PopupListProps>(
React.forwardRef(
(
{
id,
options,
value,
onSubmitValue,
style,
containerMode = 'default',
controlStyles = getControlStyles('simple'),
disabled = !controlStyles.interactive,
},
ref,
) => {
const selectOnSubmitValue = React.useCallback(
(newValue: ValueType<SelectOption>) => {
if (isOptionType(newValue)) {
onSubmitValue(newValue)
}
},
[onSubmitValue],
)
const container: styleFn = getContainer(containerMode, controlStyles, style)
return (
<Select
id={id}
components={{
Option,
MenuList,
MenuPortal,
DropdownIndicator,
SingleValue,
}}
openMenuOnFocus={true}
openMenuOnClick={true}
value={value}
onChange={selectOnSubmitValue}
options={options}
menuPortalTarget={document.getElementById(CommonUtils.PortalTargetID)}
filterOption={createFilter({ ignoreAccents: true })}
isDisabled={disabled}
styles={{
container,
indicatorsContainer: (base) => ({
...base,
width: 14,
flexBasis: 14,
background: 'transparent',
// the control is on top of the input not inside it, so need to make space for input borders
height: OptionHeight - 2,
marginRight: 1,
marginTop: 1,
padding: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
'&:hover': {
filter: 'brightness(.99)',
},
'&:active': {
filter: 'brightness(.98)',
},
}),
control: () => ({
minHeight: OptionHeight,
height: OptionHeight,
backgroundColor: 'transparent',
alignItems: 'center',
cursor: 'default',
display: 'flex',
label: 'control',
outline: '0 !important',
position: 'relative',
transition: 'all 100ms',
}),
singleValue: () => ({
label: 'singleValue',
color: controlStyles.mainColor,
display: 'flex',
alignItems: 'center',
overflowX: 'scroll',
whiteSpace: 'nowrap',
}),
menu: () => ({
label: 'menu',
boxSizing: 'border-box',
height: '100%',
width: '100%',
padding: `${menuVerticalPadding}px 2px`,
}),
menuList: (_, menuListProps) => {
return {
padding: 0,
boxSizing: 'border-box',
label: 'menuList',
height: menuListProps.children.length * OptionHeight,
}
},
option: (_, optionProps) => ({
paddingBottom: 0,
paddingRight: '24px',
paddingLeft: '4px',
fontWeight: 400,
userSelect: 'none',
borderRadius: 2,
fontSize: 11,
backgroundColor:
optionProps.isFocused === true
? colorTheme.contextMenuHighlightBackground.value
: 'transparent',
color:
optionProps.isFocused === true
? colorTheme.contextMenuHighlightForeground.value
: colorTheme.contextMenuForeground.value,
height: OptionHeight,
textTransform: 'capitalize',
}),
input: () => ({
margin: 2,
paddingBottom: 2,
paddingTop: 2,
visibility: 'visible',
color: controlStyles.mainColor,
boxSizing: 'border-box',
position: 'absolute',
top: 0,
left: 0,
}),
valueContainer: () => ({
label: 'valueContainer',
height: OptionHeight,
boxSizing: 'border-box',
overflow: 'hidden',
padding: `2px 2px 2px ${ValueContainerLeftPadding}px`,
position: 'relative',
borderRadius: UtopiaTheme.inputBorderRadius,
display: 'flex',
alignItems: 'center',
flexGrow: containerMode === 'noBorder' ? 0 : 1,
}),
indicatorSeparator: displayNone,
clearIndicator: displayNone,
loadingIndicator: displayNone,
}}
/>
)
},
),
)