Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Multi Text features to Multi Select #230

Merged
merged 3 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 52 additions & 14 deletions packages/react-drylus/src/forms/MultiSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sv from '@drawbotics/drylus-style-vars';
import { css, cx } from 'emotion';
import React, { ReactNode } from 'react';
import React, { ReactNode, useState } from 'react';
import Select, {
ActionMeta,
GroupBase,
Expand Down Expand Up @@ -107,8 +107,11 @@ export interface MultiSelectOption<T> extends Option<T> {
}

export interface MultiSelectProps<T, K = string> {
/** The options to show in the list of options, note that label and value may differ depending on valueKey and labelKey */
options: Array<MultiSelectOption<T>>;
/**
* The options to show in the list of options, note that label and value may differ depending on valueKey and labelKey
* If this is not provided, the component will behave like as a multiple input field, with dropdown and select functionality disabled
* */
options?: Array<MultiSelectOption<T>>;

/** Determines which values are currently active */
values:
Expand Down Expand Up @@ -173,6 +176,8 @@ export const MultiSelect = <T extends any, K extends string>({
const error = isFunction(props.error) ? props.error(props.name) : props.error;
const values = isFunction(props.values) ? props.values(props.name) : props.values;

const [temporaryInput, setTemporaryInput] = useState("");

const baseProps = {
isMulti: true,
options: props.options,
Expand All @@ -181,15 +186,20 @@ export const MultiSelect = <T extends any, K extends string>({
isSearchable: true,
isClearable: false,
defaultValue: null,
value: props.options.filter((o) => {
value: props.options ? props.options.filter((o) => {
return values.includes(o.value);
}),
}) : values.map((value) => ({
label: value,
value: value
} as unknown as MultiSelectOption<T>)),
onChange: (
options: MultiValue<MultiSelectOption<T>>,
action: ActionMeta<MultiSelectOption<T>>,
) => {
setTemporaryInput("");

if (props.onChangeOptions && action.action === 'create-option') {
props.onChangeOptions([...props.options, action.option]);
props.onChangeOptions([...(props.options ?? []), action.option]);
}

if (props.onChange) {
Expand All @@ -208,6 +218,7 @@ export const MultiSelect = <T extends any, K extends string>({
[styles.error]: error != null && error !== false,
}),
components: {
DropdownIndicator: props.options ? components.DropdownIndicator : null,
IndicatorsContainer: (
indicatorContainerProps: React.PropsWithChildren<
IndicatorsContainerProps<MultiSelectOption<T>, true, GroupBase<MultiSelectOption<T>>>
Expand Down Expand Up @@ -243,6 +254,8 @@ export const MultiSelect = <T extends any, K extends string>({
<RoundIcon inversed name="check" size={Size.SMALL} color={Color.GREEN} />
</div>
);
} else {
return null
}
})}
<components.IndicatorsContainer {...indicatorContainerProps} />
Expand All @@ -261,14 +274,39 @@ export const MultiSelect = <T extends any, K extends string>({
},
props.className,
)}>
{props.onChangeOptions ? (
<CreatableSelect<MultiSelectOption<T>, true>
{...baseProps}
formatCreateLabel={props.formatCreateLabel ?? ((inputValue) => `+ ${inputValue}`)}
/>
) : (
<Select<MultiSelectOption<T>, true> {...baseProps} />
)}
{run(() => {
if (!props.options) {
return <CreatableSelect<MultiSelectOption<T>, true>
{...baseProps}
inputValue={temporaryInput}
isClearable={false}
isMulti
menuIsOpen={false}
onInputChange={(inputValue: string) => {
setTemporaryInput(inputValue)
}}
onKeyDown={(event) => {
if (!temporaryInput) return;
switch (event.key) {
case 'Enter':
case 'Tab':
setTemporaryInput("");
props.onChange?.([...values, temporaryInput as unknown as T], props.name)
event.preventDefault();
}
}}
formatCreateLabel={props.formatCreateLabel ?? ((inputValue) => `+ ${inputValue}`)}
/>
} else if (props.onChangeOptions) {
return <CreatableSelect<MultiSelectOption<T>, true>
{...baseProps}
formatCreateLabel={props.formatCreateLabel ?? ((inputValue) => `+ ${inputValue}`)}
/>
} else {
return <Select<MultiSelectOption<T>, true> {...baseProps} />

}
})}
{run(() => {
if (error && typeof error === 'string') {
return <Hint category={Category.DANGER}>{error}</Hint>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,16 @@ This can be useful if you want the user to pick all the options (i.e. without de
)}
</Controller>
</div>
</Playground>
</Playground>

### Multi Select as an input for multiple text
<Playground mode="jsx">
<div style={{ maxWidth: 500, height: 300 }}>
<ControlledMultiField style={{ maxWidth: 280, height: 300 }} component={
<MultiSelect
placeholder="Type to add emails..."
/>
}
/>
</div>
</Playground>