|
@@ -1,7 +1,10 @@ |
|
|
import * as React from 'react'; |
|
|
import * as React from 'react'; |
|
|
|
|
|
import { TagsInput } from 'react-tag-input-component'; |
|
|
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; |
|
|
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; |
|
|
import clsx from 'clsx'; |
|
|
import clsx from 'clsx'; |
|
|
import {useEnhanced} from '@/packages/react-utils'; |
|
|
import {useEnhanced} from '@/packages/react-utils'; |
|
|
|
|
|
import styles from './style.module.css'; |
|
|
|
|
|
import {delegateTriggerChangeEvent} from '@/utils/event'; |
|
|
|
|
|
|
|
|
type TagInputDerivedElement = HTMLInputElement; |
|
|
type TagInputDerivedElement = HTMLInputElement; |
|
|
|
|
|
|
|
@@ -43,6 +46,7 @@ export interface TagInputProps extends Omit<React.HTMLProps<TagInputDerivedEleme |
|
|
*/ |
|
|
*/ |
|
|
hiddenLabel?: boolean, |
|
|
hiddenLabel?: boolean, |
|
|
enhanced?: boolean, |
|
|
enhanced?: boolean, |
|
|
|
|
|
separator?: string, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -64,6 +68,9 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>( |
|
|
hiddenLabel = false, |
|
|
hiddenLabel = false, |
|
|
className, |
|
|
className, |
|
|
enhanced: enhancedProp = false, |
|
|
enhanced: enhancedProp = false, |
|
|
|
|
|
separator = ',', |
|
|
|
|
|
onChange, |
|
|
|
|
|
defaultValue, |
|
|
...etcProps |
|
|
...etcProps |
|
|
}: TagInputProps, |
|
|
}: TagInputProps, |
|
|
forwardedRef, |
|
|
forwardedRef, |
|
@@ -72,9 +79,20 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>( |
|
|
const defaultRef = React.useRef<TagInputDerivedElement>(null); |
|
|
const defaultRef = React.useRef<TagInputDerivedElement>(null); |
|
|
const ref = forwardedRef ?? defaultRef; |
|
|
const ref = forwardedRef ?? defaultRef; |
|
|
const labelId = React.useId(); |
|
|
const labelId = React.useId(); |
|
|
const dummyInputRef = React.useRef<HTMLInputElement>(null); |
|
|
|
|
|
|
|
|
const tags = React.useMemo(() => { |
|
|
|
|
|
if (defaultValue === undefined) { |
|
|
|
|
|
return []; |
|
|
|
|
|
} |
|
|
|
|
|
if (typeof defaultValue === 'string') { |
|
|
|
|
|
return defaultValue.split(separator); |
|
|
|
|
|
} |
|
|
|
|
|
if (typeof defaultValue === 'number') { |
|
|
|
|
|
return [defaultValue.toString()]; |
|
|
|
|
|
} |
|
|
|
|
|
return defaultValue as string[]; |
|
|
|
|
|
}, [defaultValue, separator]); |
|
|
|
|
|
|
|
|
const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => { |
|
|
|
|
|
|
|
|
const handleTagsInputChange = (tags: string[]) => { |
|
|
if (!(typeof ref === 'object' && ref)) { |
|
|
if (!(typeof ref === 'object' && ref)) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
@@ -82,80 +100,86 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>( |
|
|
if (!input) { |
|
|
if (!input) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
input.focus(); |
|
|
|
|
|
|
|
|
input.value = tags.join(separator); |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
delegateTriggerChangeEvent(input); |
|
|
|
|
|
}); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => { |
|
|
|
|
|
if (!(typeof dummyInputRef === 'object' && dummyInputRef)) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
const { current: input } = dummyInputRef; |
|
|
|
|
|
if (!input) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
input.value = e.currentTarget.value; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
const inputClassName = clsx( |
|
|
|
|
|
'bg-negative rounded-inherit w-full block', |
|
|
|
|
|
'focus:outline-0', |
|
|
|
|
|
'disabled:opacity-50 disabled:cursor-not-allowed', |
|
|
|
|
|
{ |
|
|
|
|
|
'text-xxs': size === 'small', |
|
|
|
|
|
'text-xs': size === 'medium', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'pl-4': variant === 'default', |
|
|
|
|
|
'pl-1.5': variant === 'alternate', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'pt-4': variant === 'alternate', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'pr-4': variant === 'default' && !indicator, |
|
|
|
|
|
'pr-1.5': variant === 'alternate' && !indicator, |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'pr-10': indicator && size === 'small', |
|
|
|
|
|
'pr-12': indicator && size === 'medium', |
|
|
|
|
|
'pr-16': indicator && size === 'large', |
|
|
|
|
|
}, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
<div |
|
|
<div |
|
|
className={clsx( |
|
|
className={clsx( |
|
|
'relative rounded ring-secondary/50', |
|
|
|
|
|
|
|
|
styles['tag-input'], |
|
|
|
|
|
size === 'small' && styles['tag-input-small'], |
|
|
|
|
|
size === 'medium' && styles['tag-input-medium'], |
|
|
|
|
|
size === 'large' && styles['tag-input-large'], |
|
|
|
|
|
'relative rounded ring-secondary/50 group', |
|
|
'focus-within:ring-4', |
|
|
'focus-within:ring-4', |
|
|
{ |
|
|
{ |
|
|
'block': block, |
|
|
'block': block, |
|
|
'inline-block align-middle': !block, |
|
|
'inline-block align-middle': !block, |
|
|
}, |
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'min-h-10': size === 'small', |
|
|
|
|
|
'min-h-12': size === 'medium', |
|
|
|
|
|
'min-h-16': size === 'large', |
|
|
|
|
|
}, |
|
|
className, |
|
|
className, |
|
|
)} |
|
|
)} |
|
|
> |
|
|
> |
|
|
<input |
|
|
<input |
|
|
{...etcProps} |
|
|
{...etcProps} |
|
|
ref={ref} |
|
|
ref={ref} |
|
|
|
|
|
readOnly={enhanced} |
|
|
aria-labelledby={labelId} |
|
|
aria-labelledby={labelId} |
|
|
type={type} |
|
|
type={type} |
|
|
data-testid="input" |
|
|
data-testid="input" |
|
|
|
|
|
onChange={onChange} |
|
|
className={clsx( |
|
|
className={clsx( |
|
|
'peer', |
|
|
|
|
|
|
|
|
inputClassName, |
|
|
|
|
|
!enhanced && { |
|
|
|
|
|
'h-10': size === 'small', |
|
|
|
|
|
'h-12': size === 'medium', |
|
|
|
|
|
'h-16': size === 'large', |
|
|
|
|
|
}, |
|
|
|
|
|
!enhanced && 'peer', |
|
|
enhanced && 'sr-only', |
|
|
enhanced && 'sr-only', |
|
|
)} |
|
|
)} |
|
|
onChange={handleChange} |
|
|
|
|
|
/> |
|
|
/> |
|
|
{enhanced && ( |
|
|
{enhanced && ( |
|
|
<input |
|
|
|
|
|
ref={dummyInputRef} |
|
|
|
|
|
readOnly |
|
|
|
|
|
tabIndex={-1} |
|
|
|
|
|
onFocus={handleFocus} |
|
|
|
|
|
className={clsx( |
|
|
|
|
|
'bg-negative rounded-inherit w-full block', |
|
|
|
|
|
'focus:outline-0', |
|
|
|
|
|
'disabled:opacity-50 disabled:cursor-not-allowed', |
|
|
|
|
|
{ |
|
|
|
|
|
'text-xxs': size === 'small', |
|
|
|
|
|
'text-xs': size === 'medium', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'pl-4': variant === 'default', |
|
|
|
|
|
'pl-1.5': variant === 'alternate', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'pt-4': variant === 'alternate', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'pr-4': variant === 'default' && !indicator, |
|
|
|
|
|
'pr-1.5': variant === 'alternate' && !indicator, |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'pr-10': indicator && size === 'small', |
|
|
|
|
|
'pr-12': indicator && size === 'medium', |
|
|
|
|
|
'pr-16': indicator && size === 'large', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
'h-10': size === 'small', |
|
|
|
|
|
'h-12': size === 'medium', |
|
|
|
|
|
'h-16': size === 'large', |
|
|
|
|
|
}, |
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
<TagsInput |
|
|
|
|
|
value={tags} |
|
|
|
|
|
classNames={{ |
|
|
|
|
|
input: 'peer bg-transparent', |
|
|
|
|
|
tag: 'text-xs p-1', |
|
|
|
|
|
}} |
|
|
|
|
|
onChange={handleTagsInputChange} |
|
|
/> |
|
|
/> |
|
|
)} |
|
|
)} |
|
|
{ |
|
|
{ |
|
@@ -164,7 +188,7 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>( |
|
|
data-testid="label" |
|
|
data-testid="label" |
|
|
id={labelId} |
|
|
id={labelId} |
|
|
className={clsx( |
|
|
className={clsx( |
|
|
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative', |
|
|
|
|
|
|
|
|
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 group-focus-within:text-secondary text-primary leading-none bg-negative', |
|
|
{ |
|
|
{ |
|
|
'sr-only': hiddenLabel, |
|
|
'sr-only': hiddenLabel, |
|
|
}, |
|
|
}, |
|
@@ -233,7 +257,7 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>( |
|
|
border && ( |
|
|
border && ( |
|
|
<span |
|
|
<span |
|
|
data-testid="border" |
|
|
data-testid="border" |
|
|
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" |
|
|
|
|
|
|
|
|
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none group-focus-within:border-secondary" |
|
|
/> |
|
|
/> |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|