Moving the arttags table to tags table part 2
This commit is contained in:
@ -171,7 +171,7 @@ export default function ArtworkDetails({
|
||||
v: (
|
||||
<div className="flex flex-wrap gap-2 text-xs">
|
||||
<Badge variant="secondary">{(artwork.categories?.length ?? 0)} categories</Badge>
|
||||
<Badge variant="secondary">{(artwork.tagsV2?.length ?? 0)} tags</Badge>
|
||||
<Badge variant="secondary">{(artwork.tags?.length ?? 0)} tags</Badge>
|
||||
<Badge variant="secondary">{(artwork.colors?.length ?? 0)} colors</Badge>
|
||||
<Badge variant="secondary">{(artwork.variants?.length ?? 0)} variants</Badge>
|
||||
</div>
|
||||
|
||||
@ -39,7 +39,7 @@ export default function EditArtworkForm({ artwork, categories, tags }:
|
||||
year: artwork.year || undefined,
|
||||
creationDate: artwork.creationDate ? new Date(artwork.creationDate) : undefined,
|
||||
categoryIds: artwork.categories?.map(cat => cat.id) ?? [],
|
||||
tagIds: artwork.tagsV2?.map(tag => tag.id) ?? [],
|
||||
tagIds: artwork.tags?.map(tag => tag.id) ?? [],
|
||||
newCategoryNames: [],
|
||||
newTagNames: []
|
||||
}
|
||||
@ -283,6 +283,12 @@ export default function EditArtworkForm({ artwork, categories, tags }:
|
||||
.filter((t) => selectedTagIds.includes(t.id))
|
||||
.map((t) => ({ label: t.name, value: t.id }));
|
||||
|
||||
const fallbackSelectedOptions =
|
||||
artwork.tags
|
||||
?.filter((t) => selectedTagIds.includes(t.id))
|
||||
.filter((t) => !selectedExistingOptions.some((o) => o.value === t.id))
|
||||
.map((t) => ({ label: t.name, value: t.id })) ?? [];
|
||||
|
||||
// Selected "new" tags (so they remain visible)
|
||||
const selectedNewOptions = newTagNames.map((name) => ({
|
||||
label: `Create: ${name}`,
|
||||
@ -302,7 +308,11 @@ export default function EditArtworkForm({ artwork, categories, tags }:
|
||||
placeholder="Select or type to create tags"
|
||||
hidePlaceholderWhenSelected
|
||||
selectFirstItem
|
||||
value={[...selectedExistingOptions, ...selectedNewOptions]}
|
||||
value={[
|
||||
...selectedExistingOptions,
|
||||
...fallbackSelectedOptions,
|
||||
...selectedNewOptions,
|
||||
]}
|
||||
creatable
|
||||
createOption={(raw) => ({
|
||||
value: `__new__:${raw}`,
|
||||
|
||||
@ -142,15 +142,6 @@ function removePickedOption(groupOption: GroupOption, picked: Option[]) {
|
||||
return cloneOption;
|
||||
}
|
||||
|
||||
function isOptionsExist(groupOption: GroupOption, targetOption: Option[]) {
|
||||
for (const [, value] of Object.entries(groupOption)) {
|
||||
if (value.some((option) => targetOption.find((p) => p.value === option.value))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function normalizeInput(s: string) {
|
||||
return s.trim().replace(/\s+/g, " ");
|
||||
}
|
||||
@ -238,7 +229,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
[selected],
|
||||
);
|
||||
|
||||
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
|
||||
const handleClickOutside = React.useCallback((event: MouseEvent | TouchEvent) => {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node) &&
|
||||
@ -248,7 +239,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
setOpen(false);
|
||||
inputRef.current.blur();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleUnselect = React.useCallback(
|
||||
(option: Option) => {
|
||||
@ -294,7 +285,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('touchend', handleClickOutside);
|
||||
};
|
||||
}, [open]);
|
||||
}, [open, handleClickOutside]);
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
@ -311,7 +302,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
if (JSON.stringify(newOption) !== JSON.stringify(options)) {
|
||||
setOptions(newOption);
|
||||
}
|
||||
}, [arrayDefaultOptions, arrayOptions, groupBy, onSearch, options]);
|
||||
}, [arrayOptions, groupBy, onSearch, options]);
|
||||
|
||||
useEffect(() => {
|
||||
/** sync search */
|
||||
@ -334,8 +325,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
};
|
||||
|
||||
void exec();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedSearchTerm, groupBy, open, triggerSearchOnFocus]);
|
||||
}, [debouncedSearchTerm, groupBy, onSearchSync, open, triggerSearchOnFocus]);
|
||||
|
||||
useEffect(() => {
|
||||
/** async search */
|
||||
@ -360,8 +350,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
};
|
||||
|
||||
void exec();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedSearchTerm, groupBy, open, triggerSearchOnFocus]);
|
||||
}, [debouncedSearchTerm, groupBy, onSearch, open, triggerSearchOnFocus]);
|
||||
|
||||
const CreatableItem = () => {
|
||||
if (!creatable) return undefined;
|
||||
@ -448,14 +437,10 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
return commandProps.filter;
|
||||
}
|
||||
|
||||
if (creatable) {
|
||||
return (value: string, search: string) => {
|
||||
return value.toLowerCase().includes(search.toLowerCase()) ? 1 : -1;
|
||||
};
|
||||
}
|
||||
// Using default filter in `cmdk`. We don't have to provide it.
|
||||
return undefined;
|
||||
}, [creatable, commandProps?.filter]);
|
||||
return (value: string, search: string) => {
|
||||
return value.toLowerCase().includes(search.toLowerCase()) ? 1 : -1;
|
||||
};
|
||||
}, [commandProps?.filter]);
|
||||
|
||||
const orderedGroupEntries = React.useMemo(() => {
|
||||
const entries = Object.entries(selectables);
|
||||
@ -495,7 +480,8 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
} // When onSearch is provided, we don't want to filter the options. You can still override it.
|
||||
filter={commandFilter()}
|
||||
>
|
||||
<div
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'min-h-10 rounded-md border border-input text-base ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 md:text-sm',
|
||||
{
|
||||
@ -508,6 +494,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
if (disabled) return;
|
||||
inputRef?.current?.focus();
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<div className="relative flex flex-wrap gap-1">
|
||||
{selected.map((option) => {
|
||||
@ -594,7 +581,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div className="relative">
|
||||
{open && (
|
||||
<CommandList
|
||||
@ -626,7 +613,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
||||
return (
|
||||
<CommandItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
value={`${option.label}::${option.value}`}
|
||||
disabled={disabledItem}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user