Skip to content

Commit

Permalink
chore(web): list field (#687)
Browse files Browse the repository at this point in the history
  • Loading branch information
jashanbhullar committed Sep 26, 2023
1 parent 8c4b5b3 commit 07bc841
Show file tree
Hide file tree
Showing 13 changed files with 650 additions and 115 deletions.
2 changes: 1 addition & 1 deletion web/src/beta/components/DragAndDropList/index.tsx
Expand Up @@ -5,7 +5,7 @@ import { styled } from "@reearth/services/theme";

import Item from "./Item";

type Props<Item extends { id: string } = { id: string }> = {
export type Props<Item extends { id: string } = { id: string }> = {
uniqueKey: string;
items: Item[];
getId: (item: Item) => string;
Expand Down
13 changes: 8 additions & 5 deletions web/src/beta/components/Slider/index.tsx
Expand Up @@ -12,11 +12,14 @@ export type Props = {
max?: number;
} & ComponentProps<typeof SliderWithTooltip>;

const Slider: React.FC<Props> = ({ ...props }) => (
<SliderStyled disabled={props.disabled as boolean}>
<SliderWithTooltip {...props} />
</SliderStyled>
);
const Slider: React.FC<Props> = ({ ...props }) => {
const calculatedStep = props.step ? props.step : props.max ? props.max / 10 : 0.1;
return (
<SliderStyled disabled={props.disabled as boolean}>
<SliderWithTooltip step={calculatedStep} {...props} />
</SliderStyled>
);
};

const SliderStyled = styled.div<{ disabled: boolean }>`
width: 100%;
Expand Down
109 changes: 109 additions & 0 deletions web/src/beta/components/fields/ListField/index.stories.tsx
@@ -0,0 +1,109 @@
import { useArgs } from "@storybook/preview-api";
import { Meta, StoryObj } from "@storybook/react";
import { useCallback } from "react";

import { styled } from "@reearth/services/theme";

import ListField, { Props } from ".";

const meta: Meta<typeof ListField> = {
component: ListField,
};

export default meta;

type Story = StoryObj<typeof ListField>;

export const Default: Story = (args: Props) => {
const [_, updateArgs] = useArgs();

const addItem = useCallback(() => {
const randomId = (Math.random() + 1).toString(36).substring(7);
updateArgs({
items: [
...args.items,
{
id: randomId,
value: `Item ${randomId}`,
},
],
});
}, [updateArgs, args.items]);

const removeItem = useCallback(
(key: string) => {
updateArgs({ items: args.items.filter(({ id }) => id != key) });
},
[updateArgs, args.items],
);

const onItemDrop = useCallback(
(item: { id: string; value: string }, index: number) => {
const items = [...args.items];
items.splice(
items.findIndex(x => x.id === item.id),
1,
);
items.splice(index, 0, item);
updateArgs({ items });
},
[updateArgs, args.items],
);

const onSelect = useCallback((id: string) => updateArgs({ selected: id }), [updateArgs]);

return (
<Wrapper>
<div>
<ListField
{...args}
addItem={addItem}
removeItem={removeItem}
onItemDrop={onItemDrop}
onSelect={onSelect}
/>
</div>
</Wrapper>
);
};

const Wrapper = styled.div`
display: flex;
flex-direction: column;
gap: 10%;
margin-left: 2rem;
margin-top: 2rem;
height: 300px;
width: 300px;
`;

Default.args = {
name: "List Field",
description: "List field Sample description",
items: [
{
id: "w3tlwi",
value: "Item w3tlwi",
},
{
id: "77eg5",
value: "Item 77eg5",
},
{
id: "7p218",
value: "Item 7p218",
},
{
id: "xquyo",
value: "Item xquyo",
},
{
id: "2mewj",
value: "Item 2mewj",
},
{
id: "d2gmu",
value: "Item d2gmu",
},
],
};
124 changes: 124 additions & 0 deletions web/src/beta/components/fields/ListField/index.tsx
@@ -0,0 +1,124 @@
import { useCallback, useMemo } from "react";

import Button from "@reearth/beta/components/Button";
import DragAndDropList, {
Props as DragAndDropProps,
} from "@reearth/beta/components/DragAndDropList";
import Property from "@reearth/beta/components/fields";
import Text from "@reearth/beta/components/Text";
import { useT } from "@reearth/services/i18n";
import { styled } from "@reearth/services/theme";

type ListItem = {
id: string;
value: string;
};

export type Props = {
name?: string;
description?: string;
items: ListItem[];
removeItem: (id: string) => void;
addItem: () => void;
onSelect: (id: string) => void;
selected?: string;
} & Pick<DragAndDropProps, "onItemDrop">;

const ListField: React.FC<Props> = ({
name,
description,
items,
removeItem,
addItem,
onItemDrop,
onSelect,
selected,
}: Props) => {
const t = useT();

const deleteItem = useCallback(() => {
if (!selected) return;
removeItem(selected);
}, [selected, removeItem]);

const getId = useCallback(({ id }: ListItem) => {
return id;
}, []);

const disableRemoveButton = useMemo(() => {
if (!selected) return true;

return !items.find(({ id }) => id == selected);
}, [items, selected]);

return (
<Property name={name} description={description}>
<FieldWrapper>
<DragAndDropList<ListItem>
uniqueKey="ListField"
items={items}
onItemDrop={onItemDrop}
getId={getId}
renderItem={({ id, value }) => (
<Item onClick={() => onSelect(id)} selected={selected === id}>
<Text size="xFootnote">{value}</Text>
</Item>
)}
gap={0}
/>
</FieldWrapper>
<ButtonGroup>
<ButtonWrapper
onClick={deleteItem}
icon="trash"
buttonType="secondary"
text={t("Remove")}
size="medium"
disabled={disableRemoveButton}
/>
<ButtonWrapper
onClick={addItem}
icon="plus"
buttonType="secondary"
text={t("Add Item")}
size="medium"
/>
</ButtonGroup>
</Property>
);
};

const FieldWrapper = styled.div`
min-height: 84px;
max-height: 224px;
border-radius: 4px;
border: 1px solid rgba(77, 83, 88, 1);
overflow: auto;
`;

const Item = styled.div<{ selected: boolean }>`
display: flex;
align-items: center;
padding: 0 12px;
height: 28px;
cursor: pointer;
background: ${({ theme, selected }) => (selected ? theme.select.main : "inherit")};
&:hover {
background: ${({ theme, selected }) => (selected ? theme.select.main : theme.bg[2])};
}
`;

const ButtonGroup = styled.div`
display: flex;
gap: 4px;
`;

const ButtonWrapper = styled(Button)`
height: 28px;
width: 100%;
padding: 0px;
margin: 0px;
opacity: ${({ disabled }) => (disabled ? 0.6 : 1)};
`;

export default ListField;
38 changes: 27 additions & 11 deletions web/src/beta/components/fields/PropertyFields/hooks.ts
Expand Up @@ -3,25 +3,41 @@ import { useCallback } from "react";
import { ValueType, ValueTypes } from "@reearth/beta/utils/value";
import { usePropertyFetcher } from "@reearth/services/api";

export default () => {
const { useUpdatePropertyValue } = usePropertyFetcher();
export default (propertyId: string, schemaGroup: string) => {
const { useUpdatePropertyValue, useAddPropertyItem, useRemovePropertyItem, useMovePropertyItem } =
usePropertyFetcher();

const handlePropertyValueUpdate = useCallback(
(
schemaGroupId: string,
propertyId: string,
fieldId: string,
vt: ValueType,
itemId?: string,
) => {
(fieldId: string, vt: ValueType, itemId?: string) => {
return async (v?: ValueTypes[ValueType]) => {
await useUpdatePropertyValue(propertyId, schemaGroupId, itemId, fieldId, "en", v, vt);
await useUpdatePropertyValue(propertyId, schemaGroup, itemId, fieldId, "en", v, vt);
};
},
[useUpdatePropertyValue],
[propertyId, schemaGroup, useUpdatePropertyValue],
);

const handleAddPropertyItem = useCallback(() => {
return useAddPropertyItem(propertyId, schemaGroup);
}, [propertyId, schemaGroup, useAddPropertyItem]);

const handleRemovePropertyItem = useCallback(
(itemId: string) => {
return useRemovePropertyItem(propertyId, schemaGroup, itemId);
},
[propertyId, schemaGroup, useRemovePropertyItem],
);

const handleMovePropertyItem = useCallback(
({ id }: { id: string }, index: number) => {
return useMovePropertyItem(propertyId, schemaGroup, id, index);
},
[propertyId, schemaGroup, useMovePropertyItem],
);

return {
handlePropertyValueUpdate,
handleAddPropertyItem,
handleRemovePropertyItem,
handleMovePropertyItem,
};
};

0 comments on commit 07bc841

Please sign in to comment.