feat(chart max length): enable edit length from data type select box (#616)

* feat(chart max length): enable edit length from data type select box

* fix
This commit is contained in:
Guy Ben-Aharon
2025-03-11 18:49:10 +02:00
committed by GitHub
parent 62beb68fa1
commit bd67ccfbcf
4 changed files with 157 additions and 41 deletions

View File

@@ -24,12 +24,19 @@ export interface SelectBoxOption {
value: string;
label: string;
description?: string;
regex?: string;
extractRegex?: RegExp;
}
export interface SelectBoxProps {
options: SelectBoxOption[];
value?: string[] | string;
onChange?: (values: string[] | string) => void;
valueSuffix?: string;
optionSuffix?: (option: SelectBoxOption) => string;
onChange?: (
values: string[] | string,
regexMatches?: string[] | string
) => void;
placeholder?: string;
inputPlaceholder?: string;
emptyPlaceholder?: string;
@@ -55,10 +62,12 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
className,
options,
value,
valueSuffix,
onChange,
multiple,
oneLine,
selectAll,
optionSuffix,
deselectAll,
clearText,
showClear,
@@ -86,7 +95,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
);
const handleSelect = React.useCallback(
(selectedValue: string) => {
(selectedValue: string, regexMatches?: string[]) => {
if (multiple) {
const newValue =
value?.includes(selectedValue) && Array.isArray(value)
@@ -94,7 +103,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
: [...(value ?? []), selectedValue];
onChange?.(newValue);
} else {
onChange?.(selectedValue);
onChange?.(selectedValue, regexMatches);
setIsOpen(false);
}
},
@@ -199,6 +208,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
(opt) => opt.value === value
)?.label
}
{valueSuffix ? valueSuffix : ''}
</div>
)
) : (
@@ -239,11 +249,22 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
align="center"
>
<Command
filter={(value, search) =>
value.toLowerCase().includes(search.toLowerCase())
? 1
: 0
filter={(value, search, keywords) => {
if (
keywords?.length &&
keywords.some((keyword) =>
new RegExp(keyword).test(search)
)
) {
return 1;
}
return value
.toLowerCase()
.includes(search.toLowerCase())
? 1
: 0;
}}
>
<div className="relative">
<CommandInput
@@ -302,14 +323,36 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
const isSelected =
Array.isArray(value) &&
value.includes(option.value);
const isRegexMatch =
option.regex &&
new RegExp(option.regex)?.test(
searchTerm
);
const matches = option.extractRegex
? searchTerm.match(
option.extractRegex
)
: undefined;
return (
<CommandItem
className="flex items-center"
key={option.value}
keywords={
option.regex
? [option.regex]
: undefined
}
// value={option.value}
onSelect={() =>
handleSelect(
option.value
option.value,
matches?.map(
(match) =>
match.toString()
)
)
}
>
@@ -327,7 +370,15 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
)}
<div className="flex items-center truncate">
<span>
{option.label}
{isRegexMatch
? searchTerm
: option.label}
{!isRegexMatch &&
optionSuffix
? optionSuffix(
option
)
: ''}
</span>
{option.description && (
<span className="ml-1 text-xs text-muted-foreground">
@@ -337,9 +388,10 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
</span>
)}
</div>
{!multiple &&
{((!multiple &&
option.value ===
value && (
value) ||
isRegexMatch) && (
<CheckIcon
className={cn(
'ml-auto',

View File

@@ -227,7 +227,7 @@ export const TableNodeField: React.FC<TableNodeFieldProps> = React.memo(
!readonly ? 'group-hover:hidden' : ''
)}
>
{field.type.name}
{field.type.name.split(' ')[0]}
{field.nullable ? '?' : ''}
</div>
{readonly ? null : (

View File

@@ -55,7 +55,13 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
}, [localField]);
return (
<Popover onOpenChange={(isOpen) => !isOpen && setLocalField(field)}>
<Popover
onOpenChange={(isOpen) => {
if (isOpen) {
setLocalField(field);
}
}}
>
<PopoverTrigger asChild>
<Button
variant="ghost"

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useCallback } from 'react';
import { GripVertical, KeyRound } from 'lucide-react';
import { Input } from '@/components/input/input';
import type { DBField } from '@/lib/domain/db-field';
@@ -16,6 +16,10 @@ import { useTranslation } from 'react-i18next';
import { TableFieldToggle } from './table-field-toggle';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import type {
SelectBoxOption,
SelectBoxProps,
} from '@/components/select-box/select-box';
import { SelectBox } from '@/components/select-box/select-box';
import { TableFieldPopover } from './table-field-modal/table-field-modal';
@@ -36,10 +40,51 @@ export const TableField: React.FC<TableFieldProps> = ({
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id: field.id });
const dataFieldOptions = dataTypeMap[databaseType].map((type) => ({
const dataFieldOptions: SelectBoxOption[] = dataTypeMap[databaseType].map(
(type) => ({
label: type.name,
value: type.id,
}));
regex: type.hasCharMaxLength
? `^${type.name}\\(\\d+\\)$`
: undefined,
extractRegex: type.hasCharMaxLength ? /\((\d+)\)/ : undefined,
})
);
const onChangeDataType = useCallback<
NonNullable<SelectBoxProps['onChange']>
>(
(value, regexMatches) => {
const dataType = dataTypeMap[databaseType].find(
(v) => v.id === value
) ?? {
id: value as string,
name: value as string,
};
let characterMaximumLength: string | undefined = undefined;
if (regexMatches?.length && dataType?.hasCharMaxLength) {
characterMaximumLength = regexMatches[1];
} else if (
field.characterMaximumLength &&
dataType?.hasCharMaxLength
) {
characterMaximumLength = field.characterMaximumLength;
}
updateField({
characterMaximumLength,
type: dataTypeDataToDataType(
dataType ?? {
id: value as string,
name: value as string,
}
),
});
},
[updateField, databaseType, field.characterMaximumLength]
);
const style = {
transform: CSS.Translate.toString(transform),
@@ -90,26 +135,39 @@ export const TableField: React.FC<TableFieldProps> = ({
'side_panel.tables_section.table.field_type'
)}
value={field.type.id}
onChange={(value) =>
updateField({
characterMaximumLength: undefined,
type: dataTypeDataToDataType(
dataTypeMap[databaseType].find(
(v) => v.id === value
) ?? {
id: value as string,
name: value as string,
valueSuffix={
field.characterMaximumLength
? `(${field.characterMaximumLength})`
: ''
}
),
})
optionSuffix={(option) => {
const type = dataTypeMap[databaseType].find(
(v) => v.id === option.value
);
if (!type) {
return '';
}
if (type.hasCharMaxLength) {
return `(${!field.characterMaximumLength ? 'n' : field.characterMaximumLength})`;
}
return '';
}}
onChange={onChangeDataType}
emptyPlaceholder={t(
'side_panel.tables_section.table.no_types_found'
)}
/>
</span>
</TooltipTrigger>
<TooltipContent>{field.type.name}</TooltipContent>
<TooltipContent>
{field.type.name}
{field.characterMaximumLength
? `(${field.characterMaximumLength})`
: ''}
</TooltipContent>
</Tooltip>
</div>
<div className="flex w-4/12 justify-end gap-1 overflow-hidden">