tag input is updated

This commit is contained in:
Nate Kelley 2025-03-20 11:56:40 -06:00
parent 11f2f3e550
commit ce738df0c1
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
9 changed files with 34 additions and 60 deletions

View File

@ -1,8 +1,10 @@
import React, { useMemo } from 'react';
import { useMemoizedFn } from '@/hooks';
import { AppModal } from '@/components/ui/modal';
import { TagInput } from '@/components/ui/inputs/InputTagInput';
import { InputTagInput } from '@/components/ui/inputs/InputTagInput';
import { useInviteUser } from '@/api/buster_rest/users';
import { validate } from 'email-validator';
import { useBusterNotifications } from '@/context/BusterNotifications';
export const InvitePeopleModal: React.FC<{
open: boolean;
@ -10,16 +12,13 @@ export const InvitePeopleModal: React.FC<{
}> = React.memo(({ open, onClose }) => {
const [emails, setEmails] = React.useState<string[]>([]);
const { mutateAsync: inviteUsers, isPending: inviting } = useInviteUser();
const { openErrorMessage } = useBusterNotifications();
const handleInvite = useMemoizedFn(async () => {
await inviteUsers({ emails });
onClose();
});
const onCloseEmail = useMemoizedFn((email: string) => {
setEmails(emails.filter((e) => e !== email));
});
const memoizedHeader = useMemo(() => {
return {
title: 'Invite others to join your workspace',
@ -41,10 +40,14 @@ export const InvitePeopleModal: React.FC<{
return (
<AppModal open={open} onClose={onClose} header={memoizedHeader} footer={memoizedFooter}>
<div className="flex flex-col">
<TagInput
value={emails}
<InputTagInput
tags={emails}
onTagAdd={(v) => {
setEmails([...emails, v]);
if (validate(v)) {
setEmails([...emails, v]);
} else {
openErrorMessage(`Invalid email - ${v}`);
}
}}
onTagRemove={(index) => {
setEmails(emails.filter((_, i) => i !== index));

View File

@ -1,13 +1,13 @@
'use client';
import type { Meta, StoryObj } from '@storybook/react';
import { TagInput } from './InputTagInput';
import { InputTagInput } from './InputTagInput';
import { useState } from 'react';
import React from 'react';
const meta: Meta<typeof TagInput> = {
const meta: Meta<typeof InputTagInput> = {
title: 'UI/Inputs/InputTagInput',
component: TagInput,
component: InputTagInput,
tags: ['autodocs'],
argTypes: {
variant: {
@ -31,10 +31,10 @@ const meta: Meta<typeof TagInput> = {
};
export default meta;
type Story = StoryObj<typeof TagInput>;
type Story = StoryObj<typeof InputTagInput>;
// Interactive component for Storybook
const InteractiveTagInput = (args: React.ComponentProps<typeof TagInput>) => {
const InteractiveTagInput = (args: React.ComponentProps<typeof InputTagInput>) => {
const [tags, setTags] = useState<string[]>(args.tags || []);
const handleTagAdd = (tag: string) => {
@ -47,7 +47,9 @@ const InteractiveTagInput = (args: React.ComponentProps<typeof TagInput>) => {
setTags(newTags);
};
return <TagInput {...args} tags={tags} onTagAdd={handleTagAdd} onTagRemove={handleTagRemove} />;
return (
<InputTagInput {...args} tags={tags} onTagAdd={handleTagAdd} onTagRemove={handleTagRemove} />
);
};
export const Default: Story = {

View File

@ -7,18 +7,17 @@ import { useMemoizedFn } from '@/hooks';
import { inputVariants } from './Input';
import { InputTag } from './InputTag';
export interface TagInputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>,
VariantProps<typeof inputVariants> {
tags?: string[];
export interface TagInputProps extends VariantProps<typeof inputVariants> {
tags: string[];
onTagAdd?: (tag: string) => void;
onTagRemove?: (index: number) => void;
placeholder?: string;
disabled?: boolean;
maxTags?: number;
className?: string;
}
const TagInput = React.forwardRef<HTMLInputElement, TagInputProps>(
const InputTagInput = React.forwardRef<HTMLInputElement, TagInputProps>(
(
{
className,
@ -116,6 +115,7 @@ const TagInput = React.forwardRef<HTMLInputElement, TagInputProps>(
))}
<input
ref={ref}
{...props}
type="text"
value={inputValue}
onChange={handleInputChange}
@ -123,13 +123,12 @@ const TagInput = React.forwardRef<HTMLInputElement, TagInputProps>(
className="placeholder:text-gray-light min-w-[120px] flex-1 bg-transparent outline-none disabled:cursor-not-allowed disabled:opacity-50"
placeholder={tags.length === 0 ? placeholder : undefined}
disabled={isDisabledInput}
{...props}
/>
</div>
</div>
);
}
);
TagInput.displayName = 'TagInput';
InputTagInput.displayName = 'TagInput';
export { TagInput };
export { InputTagInput };

View File

@ -135,32 +135,6 @@ export const BusterInfiniteList: React.FC<BusterInfiniteListProps> = React.memo(
return () => scrollableParent.removeEventListener('scroll', handleScroll);
}, [onScrollEnd, scrollEndThreshold]);
useWhyDidYouUpdate('BusterInfiniteList', {
columns,
rows,
selectedRowKeys,
onSelectChange,
emptyState,
contextMenu,
hideLastRowBorder,
showSelectAll,
onScrollEnd,
loadingNewContent,
rowClassName,
scrollEndThreshold,
useRowClickSelectChange,
showHeader,
itemData,
globalCheckStatus,
lastChildIndex,
showEmptyState,
containerRef,
scrollRef,
onGlobalSelectChange,
onSelectSectionChange,
onSelectChangePreflight
});
return (
<div ref={containerRef} className="infinite-list-container relative">
{showHeader && !showEmptyState && (

View File

@ -10,8 +10,6 @@ interface EmptyStateListProps {
export const EmptyStateList = React.memo(
({ show = true, text, variant = 'default' }: EmptyStateListProps) => {
console.log('hit!', show, text, variant);
if (!show) return null;
if (variant === 'card') {

View File

@ -13,7 +13,7 @@ const SelectGroup = SelectPrimitive.Group;
const SelectValue = SelectPrimitive.Value;
export const selectVariants = cva(
'flex w-full gap-x-1.5 transition-colors transition-border duration-200 items-center justify-between rounded border px-3 py-1 text-sm focus:outline-none cursor-pointer disabled:cursor-not-allowed disabled:opacity-60 [&>span]:line-clamp-1',
'flex w-full gap-x-1.5 transition-colors transition-border text-foreground duration-200 items-center justify-between rounded border px-3 py-1 text-sm focus:outline-none cursor-pointer disabled:cursor-not-allowed disabled:opacity-60 [&>span]:line-clamp-1',
{
variants: {
variant: {

View File

@ -157,9 +157,9 @@ const PermissionGroupAssignedCell: React.FC<{
<div className="flex" onClick={(e) => e.stopPropagation()}>
<Select
items={PERMISSION_USERS_OPTIONS}
value={assigned ? 'true' : 'false'}
value={assigned ? 'included' : 'not_included'}
onChange={(value) => {
onSelect({ id, assigned: value === 'true' });
onSelect({ id, assigned: value === 'included' });
}}
/>
</div>

View File

@ -58,10 +58,8 @@ const PermissionUsersAssignButton: React.FC<{
return {
selectable: true,
items: options.map((v) => ({
icon: v.icon,
label: v.label,
value: v.value ? 'included' : 'not_included',
onClick: () => onAssignClick(v.value === 'true')
...v,
onClick: () => onAssignClick(v.value === 'included')
}))
};
}, [selectedRowKeys]);

View File

@ -1,12 +1,12 @@
import { SelectItem } from '@/components/ui/select';
export const PERMISSION_USERS_OPTIONS: SelectItem<'true' | 'false'>[] = [
export const PERMISSION_USERS_OPTIONS: SelectItem<'included' | 'not_included'>[] = [
{
label: 'Assigned',
value: 'true'
value: 'included'
},
{
label: 'Not assigned',
value: 'false'
value: 'not_included'
}
];