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