Implement Multi Select Values (#415)
This commit is contained in:
parent
c5771f7c9f
commit
e7126b1835
18 changed files with 257 additions and 52 deletions
|
@ -7,7 +7,7 @@ import {IBlock, MutableBlock} from './block'
|
|||
interface Card extends IBlock {
|
||||
readonly icon: string
|
||||
readonly isTemplate: boolean
|
||||
readonly properties: Readonly<Record<string, string>>
|
||||
readonly properties: Readonly<Record<string, string | string[]>>
|
||||
readonly contentOrder: readonly string[]
|
||||
|
||||
duplicate(): MutableCard
|
||||
|
@ -28,10 +28,10 @@ class MutableCard extends MutableBlock implements Card {
|
|||
this.fields.isTemplate = value
|
||||
}
|
||||
|
||||
get properties(): Record<string, string> {
|
||||
return this.fields.properties as Record<string, string>
|
||||
get properties(): Record<string, string | string[]> {
|
||||
return this.fields.properties as Record<string, string | string[]>
|
||||
}
|
||||
set properties(value: Record<string, string>) {
|
||||
set properties(value: Record<string, string | string[]>) {
|
||||
this.fields.properties = value
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
width: 100%;
|
||||
.MenuWrapper {
|
||||
position: relative;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
64
webapp/src/components/properties/multiSelect.tsx
Normal file
64
webapp/src/components/properties/multiSelect.tsx
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import {IPropertyOption, IPropertyTemplate} from '../../blocks/board'
|
||||
|
||||
import Label from '../../widgets/label'
|
||||
|
||||
import ValueSelector from '../../widgets/valueSelector'
|
||||
|
||||
type Props = {
|
||||
emptyValue: string;
|
||||
propertyTemplate: IPropertyTemplate;
|
||||
propertyValue: string | string[];
|
||||
onChange: (value: string | string[]) => void;
|
||||
onChangeColor: (option: IPropertyOption, color: string) => void;
|
||||
onDeleteOption: (option: IPropertyOption) => void;
|
||||
onCreate: (newValue: string, currentValues: IPropertyOption[]) => void;
|
||||
onDeleteValue: (valueToDelete: IPropertyOption, currentValues: IPropertyOption[]) => void;
|
||||
isEditable: boolean;
|
||||
}
|
||||
|
||||
const MultiSelectProperty = (props: Props): JSX.Element => {
|
||||
const {propertyTemplate, emptyValue, propertyValue, isEditable, onChange, onChangeColor, onDeleteOption, onCreate, onDeleteValue} = props
|
||||
|
||||
const values = Array.isArray(propertyValue) ?
|
||||
propertyValue.map((v) => propertyTemplate.options.find((o) => o!.id === v)).filter((v): v is IPropertyOption => Boolean(v)) :
|
||||
[]
|
||||
|
||||
if (!isEditable) {
|
||||
return (
|
||||
<div
|
||||
className='octo-property-value'
|
||||
tabIndex={0}
|
||||
>
|
||||
{values.map((v) => (
|
||||
<Label
|
||||
key={v.id}
|
||||
color={v ? v.color : 'empty'}
|
||||
>
|
||||
{v.value}
|
||||
</Label>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ValueSelector
|
||||
isMulti={true}
|
||||
emptyValue={emptyValue}
|
||||
options={propertyTemplate.options}
|
||||
value={values}
|
||||
onChange={onChange}
|
||||
onChangeColor={onChangeColor}
|
||||
onDeleteOption={onDeleteOption}
|
||||
onDeleteValue={(valueToRemove) => onDeleteValue(valueToRemove, values)}
|
||||
onCreate={(newValue) => onCreate(newValue, values)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default MultiSelectProperty
|
|
@ -12,11 +12,13 @@ import {Utils} from '../utils'
|
|||
import {BoardTree} from '../viewModel/boardTree'
|
||||
import Editable from '../widgets/editable'
|
||||
import ValueSelector from '../widgets/valueSelector'
|
||||
|
||||
import Label from '../widgets/label'
|
||||
|
||||
import EditableDayPicker from '../widgets/editableDayPicker'
|
||||
import Switch from '../widgets/switch'
|
||||
|
||||
import MultiSelectProperty from './properties/multiSelect'
|
||||
import URLProperty from './properties/link/link'
|
||||
|
||||
type Props = {
|
||||
|
@ -60,6 +62,33 @@ const PropertyValueElement = (props:Props): JSX.Element => {
|
|||
}
|
||||
}
|
||||
|
||||
if (propertyTemplate.type === 'multiSelect') {
|
||||
return (
|
||||
<MultiSelectProperty
|
||||
isEditable={!readOnly && Boolean(boardTree)}
|
||||
emptyValue={emptyDisplayValue}
|
||||
propertyTemplate={propertyTemplate}
|
||||
propertyValue={propertyValue}
|
||||
onChange={(newValue) => mutator.changePropertyValue(card, propertyTemplate.id, newValue)}
|
||||
onChangeColor={(option: IPropertyOption, colorId: string) => mutator.changePropertyOptionColor(boardTree!.board, propertyTemplate, option, colorId)}
|
||||
onDeleteOption={(option: IPropertyOption) => mutator.deletePropertyOption(boardTree!, propertyTemplate, option)}
|
||||
onCreate={
|
||||
async (newValue, currentValues) => {
|
||||
const option: IPropertyOption = {
|
||||
id: Utils.createGuid(),
|
||||
value: newValue,
|
||||
color: 'propColorDefault',
|
||||
}
|
||||
currentValues.push(option)
|
||||
await mutator.insertPropertyOption(boardTree!, propertyTemplate, option, 'add property option')
|
||||
mutator.changePropertyValue(card, propertyTemplate.id, currentValues.map((v) => v.id))
|
||||
}
|
||||
}
|
||||
onDeleteValue={(valueToDelete, currentValues) => mutator.changePropertyValue(card, propertyTemplate.id, currentValues.filter((currentValue) => currentValue.id !== valueToDelete.id).map((currentValue) => currentValue.id))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (propertyTemplate.type === 'select') {
|
||||
let propertyColorCssClassName = ''
|
||||
const cardPropertyValue = propertyTemplate.options.find((o) => o.id === propertyValue)
|
||||
|
@ -107,7 +136,7 @@ const PropertyValueElement = (props:Props): JSX.Element => {
|
|||
} else if (propertyTemplate.type === 'url') {
|
||||
return (
|
||||
<URLProperty
|
||||
value={value}
|
||||
value={value as string}
|
||||
onChange={setValue}
|
||||
onSave={() => mutator.changePropertyValue(card, propertyTemplate.id, value)}
|
||||
onCancel={() => setValue(propertyValue)}
|
||||
|
@ -123,7 +152,7 @@ const PropertyValueElement = (props:Props): JSX.Element => {
|
|||
return (
|
||||
<EditableDayPicker
|
||||
className='octo-propertyvalue'
|
||||
value={value}
|
||||
value={value as string}
|
||||
onChange={(newValue) => mutator.changePropertyValue(card, propertyTemplate.id, newValue)}
|
||||
/>
|
||||
)
|
||||
|
@ -152,7 +181,7 @@ const PropertyValueElement = (props:Props): JSX.Element => {
|
|||
<Editable
|
||||
className='octo-propertyvalue'
|
||||
placeholderText='Empty'
|
||||
value={value}
|
||||
value={value as string}
|
||||
onChange={setValue}
|
||||
onSave={() => mutator.changePropertyValue(card, propertyTemplate.id, value)}
|
||||
onCancel={() => setValue(propertyValue)}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
padding: 8px;
|
||||
min-height: 32px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
line-height: 30px;
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ const Table = (props: Props) => {
|
|||
return
|
||||
}
|
||||
|
||||
displayValue = OctoUtils.propertyDisplayValue(card, card.properties[columnID], template!, props.intl) || ''
|
||||
displayValue = (OctoUtils.propertyDisplayValue(card, card.properties[columnID], template!, props.intl) || '') as string
|
||||
if (template.type === 'select') {
|
||||
displayValue = displayValue.toUpperCase()
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ class CsvExporter {
|
|||
row.push(`"${this.encodeText(card.title)}"`)
|
||||
visibleProperties.forEach((template) => {
|
||||
const propertyValue = card.properties[template.id]
|
||||
const displayValue = OctoUtils.propertyDisplayValue(card, propertyValue, template, intl) || ''
|
||||
const displayValue = (OctoUtils.propertyDisplayValue(card, propertyValue, template, intl) || '') as string
|
||||
if (template.type === 'number') {
|
||||
const numericValue = propertyValue ? Number(propertyValue).toString() : ''
|
||||
row.push(numericValue)
|
||||
|
|
|
@ -372,7 +372,7 @@ class Mutator {
|
|||
await this.updateBlock(newBoard, board, 'change option color')
|
||||
}
|
||||
|
||||
async changePropertyValue(card: Card, propertyId: string, value?: string, description = 'change property') {
|
||||
async changePropertyValue(card: Card, propertyId: string, value?: string | string[], description = 'change property') {
|
||||
const newCard = new MutableCard(card)
|
||||
if (value) {
|
||||
newCard.properties[propertyId] = value
|
||||
|
@ -383,6 +383,10 @@ class Mutator {
|
|||
}
|
||||
|
||||
async changePropertyType(boardTree: BoardTree, propertyTemplate: IPropertyTemplate, type: PropertyType) {
|
||||
if (propertyTemplate.type === type) {
|
||||
return
|
||||
}
|
||||
|
||||
const {board} = boardTree
|
||||
|
||||
const newBoard = new MutableBoard(board)
|
||||
|
@ -392,27 +396,40 @@ class Mutator {
|
|||
|
||||
const oldBlocks: IBlock[] = [board]
|
||||
const newBlocks: IBlock[] = [newBoard]
|
||||
if (propertyTemplate.type === 'select') {
|
||||
// Map select to their values
|
||||
|
||||
if (propertyTemplate.type === 'select' || propertyTemplate.type === 'multiSelect') { // If the old type was either select or multiselect
|
||||
const isNewTypeSelectOrMulti = type === 'select' || type === 'multiSelect'
|
||||
|
||||
for (const card of boardTree.allCards) {
|
||||
const oldValue = card.properties[propertyTemplate.id]
|
||||
const oldValue = Array.isArray(card.properties[propertyTemplate.id]) ?
|
||||
(card.properties[propertyTemplate.id].length > 0 && card.properties[propertyTemplate.id][0]) :
|
||||
card.properties[propertyTemplate.id]
|
||||
|
||||
if (oldValue) {
|
||||
const newValue = propertyTemplate.options.find((o) => o.id === oldValue)?.value
|
||||
const newValue = isNewTypeSelectOrMulti ?
|
||||
propertyTemplate.options.find((o) => o.id === oldValue)?.id :
|
||||
propertyTemplate.options.find((o) => o.id === oldValue)?.value
|
||||
const newCard = new MutableCard(card)
|
||||
|
||||
if (newValue) {
|
||||
newCard.properties[propertyTemplate.id] = newValue
|
||||
newCard.properties[propertyTemplate.id] = type === 'multiSelect' ? [newValue] : newValue
|
||||
} else {
|
||||
// This was an invalid select option, so delete it
|
||||
delete newCard.properties[propertyTemplate.id]
|
||||
}
|
||||
|
||||
newBlocks.push(newCard)
|
||||
oldBlocks.push(card)
|
||||
}
|
||||
|
||||
if (isNewTypeSelectOrMulti) {
|
||||
newTemplate.options = propertyTemplate.options
|
||||
}
|
||||
}
|
||||
} else if (type === 'select') {
|
||||
} else if (type === 'select' || type === 'multiSelect') { // if the new type is either select or multiselect
|
||||
// Map values to new template option IDs
|
||||
for (const card of boardTree.allCards) {
|
||||
const oldValue = card.properties[propertyTemplate.id]
|
||||
const oldValue = card.properties[propertyTemplate.id] as string
|
||||
if (oldValue) {
|
||||
let option = newTemplate.options.find((o) => o.value === oldValue)
|
||||
if (!option) {
|
||||
|
@ -425,7 +442,7 @@ class Mutator {
|
|||
}
|
||||
|
||||
const newCard = new MutableCard(card)
|
||||
newCard.properties[propertyTemplate.id] = option.id
|
||||
newCard.properties[propertyTemplate.id] = type === 'multiSelect' ? [option.id] : option.id
|
||||
|
||||
newBlocks.push(newCard)
|
||||
oldBlocks.push(card)
|
||||
|
|
|
@ -16,8 +16,8 @@ import {FilterCondition} from './blocks/filterClause'
|
|||
import {Utils} from './utils'
|
||||
|
||||
class OctoUtils {
|
||||
static propertyDisplayValue(block: IBlock, propertyValue: string | undefined, propertyTemplate: IPropertyTemplate, intl: IntlShape): string | undefined {
|
||||
let displayValue: string | undefined
|
||||
static propertyDisplayValue(block: IBlock, propertyValue: string | string[] | undefined, propertyTemplate: IPropertyTemplate, intl: IntlShape): string | string[] | undefined {
|
||||
let displayValue: string | string[] | undefined
|
||||
switch (propertyTemplate.type) {
|
||||
case 'select': {
|
||||
// The property value is the id of the template
|
||||
|
@ -40,7 +40,7 @@ class OctoUtils {
|
|||
}
|
||||
case 'date': {
|
||||
if (propertyValue) {
|
||||
displayValue = Utils.displayDate(new Date(parseInt(propertyValue, 10)), intl)
|
||||
displayValue = Utils.displayDate(new Date(parseInt(propertyValue as string, 10)), intl)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
|
@ -216,7 +216,9 @@ class MutableBoardTree implements BoardTree {
|
|||
if (option?.value.toLowerCase().includes(searchText)) {
|
||||
return true
|
||||
}
|
||||
} else if (propertyValue.toLowerCase().includes(searchText)) {
|
||||
|
||||
// TODO: Add search capability for multi-select values BIG BOYY
|
||||
} else if ((propertyValue as string).toLowerCase().includes(searchText)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -425,7 +427,7 @@ class MutableBoardTree implements BoardTree {
|
|||
return this.titleOrCreatedOrder(a, b)
|
||||
}
|
||||
|
||||
result = aValue.localeCompare(bValue)
|
||||
result = (aValue as string).localeCompare(bValue as string)
|
||||
}
|
||||
|
||||
if (result === 0) {
|
||||
|
|
|
@ -157,6 +157,21 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
|
|||
class="noicon"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="MenuOption TextOption menu-option"
|
||||
>
|
||||
<div
|
||||
class="noicon"
|
||||
/>
|
||||
<div
|
||||
class="menu-name"
|
||||
>
|
||||
Multi Select
|
||||
</div>
|
||||
<div
|
||||
class="noicon"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="MenuOption TextOption menu-option"
|
||||
>
|
||||
|
|
|
@ -20,4 +20,8 @@
|
|||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
&.margin-left {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ type Props = {
|
|||
title?: string
|
||||
icon?: React.ReactNode
|
||||
className?: string
|
||||
onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void
|
||||
}
|
||||
|
||||
function IconButton(props: Props): JSX.Element {
|
||||
|
@ -19,6 +20,7 @@ function IconButton(props: Props): JSX.Element {
|
|||
return (
|
||||
<div
|
||||
onClick={props.onClick}
|
||||
onMouseDown={props.onMouseDown}
|
||||
className={className}
|
||||
title={props.title}
|
||||
>
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
input {
|
||||
line-height: 20px;
|
||||
|
|
|
@ -8,13 +8,14 @@ type Props = {
|
|||
color?: string
|
||||
title?: string
|
||||
children: React.ReactNode
|
||||
classNames?: string
|
||||
}
|
||||
|
||||
// Switch is an on-off style switch / checkbox
|
||||
function Label(props: Props): JSX.Element {
|
||||
return (
|
||||
<span
|
||||
className={`Label ${props.color || 'empty'}`}
|
||||
className={`Label ${props.color || 'empty'} ${props.classNames ? props.classNames : ''}`}
|
||||
title={props.title}
|
||||
>
|
||||
{props.children}
|
||||
|
|
|
@ -119,6 +119,11 @@ const PropertyMenu = React.memo((props: Props) => {
|
|||
name={typeDisplayName(intl, 'select')}
|
||||
onClick={() => props.onTypeChanged('select')}
|
||||
/>
|
||||
<Menu.Text
|
||||
id='multiSelect'
|
||||
name={typeDisplayName(intl, 'multiSelect')}
|
||||
onClick={() => props.onTypeChanged('multiSelect')}
|
||||
/>
|
||||
<Menu.Text
|
||||
id='date'
|
||||
name={typeDisplayName(intl, 'date')}
|
||||
|
|
|
@ -13,18 +13,37 @@
|
|||
}
|
||||
|
||||
.Label {
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
border-radius: var(--default-rad);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.Label-no-padding {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.Label-single-select {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.Label-text {
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.value-menu-option {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.label-container {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
}
|
||||
.MenuWrapper {
|
||||
display: flex;
|
||||
|
@ -38,3 +57,19 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label-container > .Label {
|
||||
max-width: 600px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.octo-property-value > .Label {
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useState} from 'react'
|
||||
import React from 'react'
|
||||
import {useIntl} from 'react-intl'
|
||||
import {ActionMeta, ValueType, FormatOptionLabelMeta} from 'react-select'
|
||||
import CreatableSelect from 'react-select/creatable'
|
||||
|
@ -14,32 +14,52 @@ import MenuWrapper from './menuWrapper'
|
|||
import IconButton from './buttons/iconButton'
|
||||
import OptionsIcon from './icons/options'
|
||||
import DeleteIcon from './icons/delete'
|
||||
import CloseIcon from './icons/close'
|
||||
import Label from './label'
|
||||
|
||||
import './valueSelector.scss'
|
||||
|
||||
type Props = {
|
||||
options: IPropertyOption[]
|
||||
value?: IPropertyOption
|
||||
value?: IPropertyOption | IPropertyOption[]
|
||||
emptyValue: string
|
||||
onCreate: (value: string) => void
|
||||
onChange: (value: string) => void
|
||||
onChange: (value: string | string[]) => void
|
||||
onChangeColor: (option: IPropertyOption, color: string) => void
|
||||
onDeleteOption: (option: IPropertyOption) => void
|
||||
isMulti?: boolean
|
||||
onDeleteValue?: (value: IPropertyOption) => void
|
||||
}
|
||||
|
||||
type LabelProps = {
|
||||
option: IPropertyOption
|
||||
meta: FormatOptionLabelMeta<IPropertyOption, false>
|
||||
meta: FormatOptionLabelMeta<IPropertyOption, true | false>
|
||||
onChangeColor: (option: IPropertyOption, color: string) => void
|
||||
onDeleteOption: (option: IPropertyOption) => void
|
||||
onDeleteValue?: (value: IPropertyOption) => void
|
||||
}
|
||||
|
||||
const ValueSelectorLabel = React.memo((props: LabelProps): JSX.Element => {
|
||||
const {option, meta} = props
|
||||
const {option, onDeleteValue, meta} = props
|
||||
const intl = useIntl()
|
||||
if (meta.context === 'value') {
|
||||
return <Label color={option.color}>{option.value}</Label>
|
||||
return (
|
||||
<Label
|
||||
color={option.color}
|
||||
classNames={`${onDeleteValue ? 'Label-no-padding' : 'Label-single-select'}`}
|
||||
>
|
||||
<span className='Label-text'>{option.value}</span>
|
||||
{onDeleteValue &&
|
||||
<IconButton
|
||||
onClick={() => onDeleteValue(option)}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
icon={<CloseIcon/>}
|
||||
title='Close'
|
||||
className='margin-left'
|
||||
/>
|
||||
}
|
||||
</Label>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className='value-menu-option'>
|
||||
|
@ -71,22 +91,9 @@ const ValueSelectorLabel = React.memo((props: LabelProps): JSX.Element => {
|
|||
})
|
||||
|
||||
function ValueSelector(props: Props): JSX.Element {
|
||||
const [activated, setActivated] = useState(false)
|
||||
|
||||
if (!activated) {
|
||||
return (
|
||||
<div
|
||||
className='ValueSelector'
|
||||
onClick={() => setActivated(true)}
|
||||
>
|
||||
<Label color={props.value ? props.value.color : 'empty'}>
|
||||
{props.value ? props.value.value : props.emptyValue}
|
||||
</Label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<CreatableSelect
|
||||
isMulti={props.isMulti}
|
||||
isClearable={true}
|
||||
styles={{
|
||||
indicatorsContainer: (provided: CSSObject): CSSObject => ({
|
||||
|
@ -102,12 +109,12 @@ function ValueSelector(props: Props): JSX.Element {
|
|||
...provided,
|
||||
background: state.isFocused ? 'rgba(var(--main-fg), 0.1)' : 'rgb(var(--main-bg))',
|
||||
color: state.isFocused ? 'rgb(var(--main-fg))' : 'rgb(var(--main-fg))',
|
||||
padding: '2px 8px',
|
||||
padding: '8px',
|
||||
}),
|
||||
control: (): CSSObject => ({
|
||||
border: 0,
|
||||
width: '100%',
|
||||
margin: '4px 0 0 0',
|
||||
margin: '0',
|
||||
}),
|
||||
valueContainer: (provided: CSSObject): CSSObject => ({
|
||||
...provided,
|
||||
|
@ -131,22 +138,42 @@ function ValueSelector(props: Props): JSX.Element {
|
|||
...provided,
|
||||
overflowY: 'unset',
|
||||
}),
|
||||
multiValue: (provided: CSSObject): CSSObject => ({
|
||||
...provided,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
backgroundColor: 'transparent',
|
||||
}),
|
||||
multiValueLabel: (provided: CSSObject): CSSObject => ({
|
||||
...provided,
|
||||
display: 'flex',
|
||||
paddingLeft: 0,
|
||||
padding: 0,
|
||||
}),
|
||||
multiValueRemove: (): CSSObject => ({
|
||||
display: 'none',
|
||||
}),
|
||||
}}
|
||||
formatOptionLabel={(option: IPropertyOption, meta: FormatOptionLabelMeta<IPropertyOption, false>) => (
|
||||
formatOptionLabel={(option: IPropertyOption, meta: FormatOptionLabelMeta<IPropertyOption, true | false>) => (
|
||||
<ValueSelectorLabel
|
||||
option={option}
|
||||
meta={meta}
|
||||
onChangeColor={props.onChangeColor}
|
||||
onDeleteOption={props.onDeleteOption}
|
||||
onDeleteValue={props.onDeleteValue}
|
||||
/>
|
||||
)}
|
||||
className='ValueSelector'
|
||||
options={props.options}
|
||||
getOptionLabel={(o: IPropertyOption) => o.value}
|
||||
getOptionValue={(o: IPropertyOption) => o.id}
|
||||
onChange={(value: ValueType<IPropertyOption, false>, action: ActionMeta<IPropertyOption>): void => {
|
||||
onChange={(value: ValueType<IPropertyOption, true | false>, action: ActionMeta<IPropertyOption>): void => {
|
||||
if (action.action === 'select-option') {
|
||||
props.onChange((value as IPropertyOption).id)
|
||||
if (Array.isArray(value)) {
|
||||
props.onChange((value as IPropertyOption[]).map((option) => option.id))
|
||||
} else {
|
||||
props.onChange((value as IPropertyOption).id)
|
||||
}
|
||||
} else if (action.action === 'clear') {
|
||||
props.onChange('')
|
||||
}
|
||||
|
@ -156,7 +183,8 @@ function ValueSelector(props: Props): JSX.Element {
|
|||
value={props.value}
|
||||
closeMenuOnSelect={true}
|
||||
placeholder={props.emptyValue}
|
||||
defaultMenuIsOpen={true}
|
||||
hideSelectedOptions={false}
|
||||
defaultMenuIsOpen={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue