@ -1,7 +1,11 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { DropResult } from '@hello-pangea/dnd';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { IconPlus } from '@/ui/display/icon';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
|
||||
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
|
||||
import { mainColorNames, ThemeColor } from '@/ui/theme/constants/colors';
|
||||
|
||||
import { SettingsObjectFieldSelectFormOption } from '../types/SettingsObjectFieldSelectFormOption';
|
||||
@ -18,6 +22,7 @@ type SettingsObjectFieldSelectFormProps = {
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(3.5)};
|
||||
`;
|
||||
|
||||
const StyledLabel = styled.span`
|
||||
@ -30,12 +35,6 @@ const StyledLabel = styled.span`
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const StyledRows = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
border-bottom: 0;
|
||||
border-left: 0;
|
||||
@ -57,39 +56,66 @@ export const SettingsObjectFieldSelectForm = ({
|
||||
onChange,
|
||||
values,
|
||||
}: SettingsObjectFieldSelectFormProps) => {
|
||||
const handleDragEnd = (result: DropResult) => {
|
||||
if (!result.destination) return;
|
||||
|
||||
const nextOptions = [...values];
|
||||
const [movedOption] = nextOptions.splice(result.source.index, 1);
|
||||
|
||||
nextOptions.splice(result.destination.index, 0, movedOption);
|
||||
|
||||
onChange(nextOptions);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledContainer>
|
||||
<StyledLabel>Options</StyledLabel>
|
||||
<StyledRows>
|
||||
{values.map((option, index) => (
|
||||
<SettingsObjectFieldSelectFormOptionRow
|
||||
key={index}
|
||||
isDefault={option.isDefault}
|
||||
onChange={(nextOption) => {
|
||||
const hasDefaultOptionChanged =
|
||||
!option.isDefault && nextOption.isDefault;
|
||||
const nextOptions = hasDefaultOptionChanged
|
||||
? values.map((value) => ({ ...value, isDefault: false }))
|
||||
: [...values];
|
||||
<DraggableList
|
||||
onDragEnd={handleDragEnd}
|
||||
draggableItems={
|
||||
<>
|
||||
{values.map((option, index) => (
|
||||
<DraggableItem
|
||||
key={option.value}
|
||||
draggableId={option.value}
|
||||
index={index}
|
||||
isDragDisabled={values.length === 1}
|
||||
itemComponent={
|
||||
<SettingsObjectFieldSelectFormOptionRow
|
||||
key={option.value}
|
||||
isDefault={option.isDefault}
|
||||
onChange={(nextOption) => {
|
||||
const hasDefaultOptionChanged =
|
||||
!option.isDefault && nextOption.isDefault;
|
||||
const nextOptions = hasDefaultOptionChanged
|
||||
? values.map((value) => ({
|
||||
...value,
|
||||
isDefault: false,
|
||||
}))
|
||||
: [...values];
|
||||
|
||||
nextOptions.splice(index, 1, nextOption);
|
||||
nextOptions.splice(index, 1, nextOption);
|
||||
|
||||
onChange(nextOptions);
|
||||
}}
|
||||
onRemove={
|
||||
values.length > 1
|
||||
? () => {
|
||||
const nextOptions = [...values];
|
||||
nextOptions.splice(index, 1);
|
||||
onChange(nextOptions);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
option={option}
|
||||
/>
|
||||
))}
|
||||
</StyledRows>
|
||||
onChange(nextOptions);
|
||||
}}
|
||||
onRemove={
|
||||
values.length > 1
|
||||
? () => {
|
||||
const nextOptions = [...values];
|
||||
nextOptions.splice(index, 1);
|
||||
onChange(nextOptions);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
option={option}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</StyledContainer>
|
||||
<StyledButton
|
||||
title="Add option"
|
||||
@ -101,6 +127,7 @@ export const SettingsObjectFieldSelectForm = ({
|
||||
{
|
||||
color: getNextColor(values[values.length - 1].color),
|
||||
label: `Option ${values.length + 1}`,
|
||||
value: v4(),
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
@ -6,6 +7,7 @@ import { ColorSample } from '@/ui/display/color/components/ColorSample';
|
||||
import {
|
||||
IconCheck,
|
||||
IconDotsVertical,
|
||||
IconGripVertical,
|
||||
IconTrash,
|
||||
IconX,
|
||||
} from '@/ui/display/icon';
|
||||
@ -23,6 +25,7 @@ import { mainColorNames } from '@/ui/theme/constants/colors';
|
||||
import { SettingsObjectFieldSelectFormOption } from '../types/SettingsObjectFieldSelectFormOption';
|
||||
|
||||
type SettingsObjectFieldSelectFormOptionRowProps = {
|
||||
className?: string;
|
||||
isDefault?: boolean;
|
||||
onChange: (value: SettingsObjectFieldSelectFormOption) => void;
|
||||
onRemove?: () => void;
|
||||
@ -33,11 +36,12 @@ const StyledRow = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(6)};
|
||||
padding: ${({ theme }) => theme.spacing(1)} 0;
|
||||
padding: ${({ theme }) => theme.spacing(1.5)} 0;
|
||||
`;
|
||||
|
||||
const StyledColorSample = styled(ColorSample)`
|
||||
cursor: pointer;
|
||||
margin-left: 9px;
|
||||
margin-right: 14px;
|
||||
`;
|
||||
|
||||
@ -51,11 +55,14 @@ const StyledOptionInput = styled(TextInput)`
|
||||
`;
|
||||
|
||||
export const SettingsObjectFieldSelectFormOptionRow = ({
|
||||
className,
|
||||
isDefault,
|
||||
onChange,
|
||||
onRemove,
|
||||
option,
|
||||
}: SettingsObjectFieldSelectFormOptionRowProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const dropdownScopeIds = useMemo(() => {
|
||||
const baseScopeId = `select-field-option-row-${v4()}`;
|
||||
return { color: `${baseScopeId}-color`, actions: `${baseScopeId}-actions` };
|
||||
@ -69,7 +76,12 @@ export const SettingsObjectFieldSelectFormOptionRow = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledRow>
|
||||
<StyledRow className={className}>
|
||||
<IconGripVertical
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
color={theme.font.color.extraLight}
|
||||
/>
|
||||
<DropdownScope dropdownScopeId={dropdownScopeIds.color}>
|
||||
<Dropdown
|
||||
dropdownPlacement="bottom-start"
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
mockedPeopleMetadata,
|
||||
} from '~/testing/mock-data/metadata';
|
||||
|
||||
import { fieldMetadataFormDefaultValues } from '../../hooks/useFieldMetadataForm';
|
||||
import {
|
||||
SettingsObjectFieldTypeSelectSection,
|
||||
SettingsObjectFieldTypeSelectSectionFormValues,
|
||||
@ -41,11 +42,10 @@ const meta: Meta<typeof SettingsObjectFieldTypeSelectSection> = {
|
||||
args: {
|
||||
fieldMetadata: fieldMetadataWithoutId,
|
||||
objectMetadataId: mockedCompaniesMetadata.node.id,
|
||||
values: {
|
||||
type: FieldMetadataType.Text,
|
||||
} as SettingsObjectFieldTypeSelectSectionFormValues,
|
||||
values: fieldMetadataFormDefaultValues,
|
||||
},
|
||||
parameters: {
|
||||
container: { width: 512 },
|
||||
msw: graphqlMocks,
|
||||
},
|
||||
};
|
||||
@ -92,6 +92,7 @@ export const WithRelationForm: Story = {
|
||||
)?.node,
|
||||
relationFieldMetadata,
|
||||
values: {
|
||||
...fieldMetadataFormDefaultValues,
|
||||
type: FieldMetadataType.Relation,
|
||||
relation: {
|
||||
field: relationFieldMetadata,
|
||||
@ -101,3 +102,38 @@ export const WithRelationForm: Story = {
|
||||
} as unknown as SettingsObjectFieldTypeSelectSectionFormValues,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithSelectForm: Story = {
|
||||
args: {
|
||||
fieldMetadata: { label: 'Industry', icon: 'IconBuildingFactory2' },
|
||||
values: {
|
||||
...fieldMetadataFormDefaultValues,
|
||||
type: FieldMetadataType.Enum,
|
||||
select: [
|
||||
{
|
||||
color: 'pink',
|
||||
isDefault: true,
|
||||
label: '💊 Health',
|
||||
value: 'HEALTH',
|
||||
},
|
||||
{
|
||||
color: 'purple',
|
||||
label: '🏭 Industry',
|
||||
value: 'INDUSTRY',
|
||||
},
|
||||
{ color: 'sky', label: '🤖 SaaS', value: 'SAAS' },
|
||||
{
|
||||
color: 'turquoise',
|
||||
label: '🌿 Green tech',
|
||||
value: 'GREEN_TECH',
|
||||
},
|
||||
{
|
||||
color: 'yellow',
|
||||
label: '🚲 Mobility',
|
||||
value: 'MOBILITY',
|
||||
},
|
||||
{ color: 'green', label: '🌏 NGO', value: 'NGO' },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { DeepPartial } from 'react-hook-form';
|
||||
import { v4 } from 'uuid';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
|
||||
@ -20,7 +21,7 @@ type FormValues = {
|
||||
select: SettingsObjectFieldTypeSelectSectionFormValues['select'];
|
||||
};
|
||||
|
||||
const defaultValues: FormValues = {
|
||||
export const fieldMetadataFormDefaultValues: FormValues = {
|
||||
icon: 'IconUsers',
|
||||
label: '',
|
||||
type: FieldMetadataType.Text,
|
||||
@ -29,7 +30,7 @@ const defaultValues: FormValues = {
|
||||
objectMetadataId: '',
|
||||
field: { label: '' },
|
||||
},
|
||||
select: [{ color: 'green', label: 'Option 1' }],
|
||||
select: [{ color: 'green', label: 'Option 1', value: v4() }],
|
||||
};
|
||||
|
||||
const fieldSchema = z.object({
|
||||
@ -96,9 +97,12 @@ type PartialFormValues = Partial<Omit<FormValues, 'relation'>> &
|
||||
|
||||
export const useFieldMetadataForm = () => {
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const [initialFormValues, setInitialFormValues] =
|
||||
useState<FormValues>(defaultValues);
|
||||
const [formValues, setFormValues] = useState<FormValues>(defaultValues);
|
||||
const [initialFormValues, setInitialFormValues] = useState<FormValues>(
|
||||
fieldMetadataFormDefaultValues,
|
||||
);
|
||||
const [formValues, setFormValues] = useState<FormValues>(
|
||||
fieldMetadataFormDefaultValues,
|
||||
);
|
||||
const [hasFieldFormChanged, setHasFieldFormChanged] = useState(false);
|
||||
const [hasRelationFormChanged, setHasRelationFormChanged] = useState(false);
|
||||
const [hasSelectFormChanged, setHasSelectFormChanged] = useState(false);
|
||||
|
||||
@ -4,5 +4,5 @@ export type SettingsObjectFieldSelectFormOption = {
|
||||
color: ThemeColor;
|
||||
isDefault?: boolean;
|
||||
label: string;
|
||||
value?: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
@ -812,7 +812,6 @@ export {
|
||||
IconBrandTabler,
|
||||
IconBrandTailwind,
|
||||
IconBrandTaobao,
|
||||
IconBrandTeams,
|
||||
IconBrandTed,
|
||||
IconBrandTelegram,
|
||||
IconBrandTerraform,
|
||||
|
||||
Reference in New Issue
Block a user