feat: add clear button to relation widget
This commit is contained in:
parent
3d02a6b180
commit
ff81002628
@ -1,9 +1,11 @@
|
|||||||
import { Combobox, Transition } from '@headlessui/react';
|
import { Combobox, Transition } from '@headlessui/react';
|
||||||
import { Check as CheckIcon } from '@styled-icons/material/Check';
|
import { Check as CheckIcon } from '@styled-icons/material/Check';
|
||||||
|
import { Close as CloseIcon } from '@styled-icons/material/Close';
|
||||||
import { KeyboardArrowDown as KeyboardArrowDownIcon } from '@styled-icons/material/KeyboardArrowDown';
|
import { KeyboardArrowDown as KeyboardArrowDownIcon } from '@styled-icons/material/KeyboardArrowDown';
|
||||||
import React, { forwardRef, Fragment, useCallback } from 'react';
|
import React, { Fragment, forwardRef, useCallback } from 'react';
|
||||||
|
|
||||||
import classNames from '@staticcms/core/lib/util/classNames.util';
|
import classNames from '@staticcms/core/lib/util/classNames.util';
|
||||||
|
import IconButton from '../button/IconButton';
|
||||||
|
|
||||||
import type { ReactNode, Ref } from 'react';
|
import type { ReactNode, Ref } from 'react';
|
||||||
|
|
||||||
@ -27,13 +29,23 @@ export interface AutocompleteProps<T> {
|
|||||||
value: T | T[] | null;
|
value: T | T[] | null;
|
||||||
options: T[] | Option<T>[];
|
options: T[] | Option<T>[];
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
required?: boolean;
|
||||||
displayValue: (item: T | T[] | null) => string;
|
displayValue: (item: T | T[] | null) => string;
|
||||||
onQuery: (query: string) => void;
|
onQuery: (query: string) => void;
|
||||||
onChange: AutocompleteChangeEventHandler<T>;
|
onChange: AutocompleteChangeEventHandler<T | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Autocomplete = function <T>(
|
const Autocomplete = function <T>(
|
||||||
{ label, value, options, disabled, displayValue, onQuery, onChange }: AutocompleteProps<T>,
|
{
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
options,
|
||||||
|
disabled,
|
||||||
|
required,
|
||||||
|
displayValue,
|
||||||
|
onQuery,
|
||||||
|
onChange,
|
||||||
|
}: AutocompleteProps<T>,
|
||||||
ref: Ref<HTMLInputElement>,
|
ref: Ref<HTMLInputElement>,
|
||||||
) {
|
) {
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
@ -56,6 +68,10 @@ const Autocomplete = function <T>(
|
|||||||
[onChange, value],
|
[onChange, value],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const clear = useCallback(() => {
|
||||||
|
onChange(undefined);
|
||||||
|
}, [onChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full">
|
<div className="relative w-full">
|
||||||
<Combobox value={value} onChange={handleChange} disabled={disabled}>
|
<Combobox value={value} onChange={handleChange} disabled={disabled}>
|
||||||
@ -108,7 +124,8 @@ const Autocomplete = function <T>(
|
|||||||
displayValue={displayValue}
|
displayValue={displayValue}
|
||||||
onChange={event => onQuery(event.target.value)}
|
onChange={event => onQuery(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
|
<div className="absolute inset-y-0 right-0 flex items-center pr-2 gap-1">
|
||||||
|
<Combobox.Button>
|
||||||
<KeyboardArrowDownIcon
|
<KeyboardArrowDownIcon
|
||||||
className={classNames(
|
className={classNames(
|
||||||
`
|
`
|
||||||
@ -125,6 +142,26 @@ const Autocomplete = function <T>(
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</Combobox.Button>
|
</Combobox.Button>
|
||||||
|
{!required && !Array.isArray(value) ? (
|
||||||
|
<IconButton variant="text" disabled={disabled} onClick={clear}>
|
||||||
|
<CloseIcon
|
||||||
|
className={classNames(
|
||||||
|
`
|
||||||
|
h-5
|
||||||
|
w-5
|
||||||
|
text-gray-400
|
||||||
|
`,
|
||||||
|
disabled &&
|
||||||
|
`
|
||||||
|
text-gray-300/75
|
||||||
|
dark:text-gray-600/75
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Transition
|
<Transition
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
|
@ -323,6 +323,10 @@ const RelationControl: FC<WidgetControlProps<string | string[], RelationField>>
|
|||||||
[onChange, uniqueOptionsByValue],
|
[onChange, uniqueOptionsByValue],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isRequired = useMemo(() => field.required ?? true, [field.required]);
|
||||||
|
|
||||||
|
console.log('field.required', field.required);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
inputRef={ref}
|
inputRef={ref}
|
||||||
@ -363,6 +367,7 @@ const RelationControl: FC<WidgetControlProps<string | string[], RelationField>>
|
|||||||
value={selectedValue}
|
value={selectedValue}
|
||||||
options={uniqueOptions}
|
options={uniqueOptions}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
required={isRequired}
|
||||||
displayValue={item => {
|
displayValue={item => {
|
||||||
if (!item || Array.isArray(item)) {
|
if (!item || Array.isArray(item)) {
|
||||||
return '';
|
return '';
|
||||||
|
@ -77,7 +77,9 @@ const SelectControl: FC<WidgetControlProps<string | number | (string | number)[]
|
|||||||
? !selectedValue?.length
|
? !selectedValue?.length
|
||||||
: isNullish(selectedValue);
|
: isNullish(selectedValue);
|
||||||
|
|
||||||
if (field.required && isEmpty && isMultiple) {
|
const isRequired = field.required ?? true;
|
||||||
|
|
||||||
|
if (isRequired && isEmpty && isMultiple) {
|
||||||
setInternalValue([]);
|
setInternalValue([]);
|
||||||
onChange([]);
|
onChange([]);
|
||||||
} else if (isEmpty) {
|
} else if (isEmpty) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user