Skip to content

Commit

Permalink
chore(web): Select Field (#662)
Browse files Browse the repository at this point in the history
  • Loading branch information
jashanbhullar authored Sep 6, 2023
1 parent feca387 commit 4ee1969
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 1 deletion.
10 changes: 9 additions & 1 deletion web/src/beta/components/fields/PropertyFields/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import TextInput from "@reearth/beta/components/fields/TextInput";
import { type Item } from "@reearth/services/api/propertyApi/utils";

import ColorField from "../ColorField";
import SelectField from "../SelectField";
import SliderField from "../SliderField";
import SpacingInput, { SpacingValues } from "../SpacingInput";
import ToggleField from "../ToggleField";
Expand Down Expand Up @@ -32,7 +33,14 @@ const PropertyFields: React.FC<Props> = ({ propertyId, item }) => {
onChange={handlePropertyValueUpdate(item.schemaGroup, propertyId, sf.id, sf.type)}
/>
) : sf.ui === "selection" || sf.choices ? (
<p key={sf.id}>Selection or choices field</p>
<SelectField
key={sf.id}
name={sf.name}
value={(value as string) ?? ""}
description={sf.description}
options={sf.choices}
onChange={handlePropertyValueUpdate(item.schemaGroup, propertyId, sf.id, sf.type)}
/>
) : sf.ui === "buttons" ? (
<p key={sf.id}>Button radio field</p>
) : (
Expand Down
72 changes: 72 additions & 0 deletions web/src/beta/components/fields/SelectField/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useArgs } from "@storybook/preview-api";
import { Meta, StoryObj } from "@storybook/react";
import { useCallback } from "react";

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

import SelectField, { Props } from ".";

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

export default meta;

type Story = StoryObj<typeof SelectField>;

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

const handleChange = useCallback((value: string) => updateArgs({ value: value }), [updateArgs]);

return (
<Wrapper>
<div>
<SelectField {...args} onChange={handleChange} />
</div>
<div>
<SelectField
{...args}
name="Disabled"
description="Props are controlled by the field above"
disabled={true}
onChange={handleChange}
/>
</div>
<div>
<SelectField
{...args}
name="Empty"
value={undefined}
description="Even if you try, you won't be able to select the value"
onChange={handleChange}
/>
</div>
</Wrapper>
);
};

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

Default.args = {
name: "Select Field",
description: "Select from the options",
disabled: false,
value: undefined,
options: [
{
label: "item 1 akas bakas moti kiran kapoor takhat buland biba kaur",
key: "item_1",
},
{ label: "item 2", key: "item_2" },
{ label: "item 3", key: "item_3" },
],
onChange: () => console.log("clicked"),
};
137 changes: 137 additions & 0 deletions web/src/beta/components/fields/SelectField/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { useState, useCallback, useMemo } from "react";

import Icon from "@reearth/beta/components/Icon";
import * as Popover from "@reearth/beta/components/Popover";
import Text from "@reearth/beta/components/Text";
import { useT } from "@reearth/services/i18n";
import { styled } from "@reearth/services/theme";

import Property from "..";

export type SelectValue = {
label: string;
key: string;
};

export type Props = {
options?: SelectValue[];
onChange: (key: string) => void;
value?: string;
disabled?: boolean;
// Property field
name?: string;
description?: string;
};

const SelectField: React.FC<Props> = ({
options,
onChange,
value,
disabled = false,
name,
description,
}) => {
const t = useT();

const [open, setOpen] = useState(false);

const handlePopOver = useCallback(() => !disabled && setOpen(!open), [open, disabled]);

const handleClick = useCallback(
(key: string) => {
setOpen(false);
if (key != value) onChange(key);
},
[setOpen, onChange, value],
);

const selected = useMemo(() => {
return options?.find(({ key }) => key === value);
}, [options, value]);

return (
<Property name={name} description={description}>
<Popover.Provider open={open} placement="bottom-start" onOpenChange={handlePopOver}>
<Popover.Trigger asChild>
<InputWrapper disabled={disabled} onClick={handlePopOver}>
<Select selected={selected ? true : false} open={open}>
{selected ? selected.label : t("Please choose an option")}
</Select>
<ArrowDownIcon icon="arrowDown" size={12} />
</InputWrapper>
</Popover.Trigger>
<PickerWrapper>
{options?.map(({ label: value, key }) => (
<Option
size="footnote"
selected={selected?.key == key}
key={key}
onClick={() => handleClick(key)}>
{value}
</Option>
))}
</PickerWrapper>
</Popover.Provider>
</Property>
);
};

const InputWrapper = styled.div<{ disabled: boolean }>`
display: flex;
gap: 10px;
position: relative;
opacity: ${({ disabled }) => (disabled ? 0.6 : 1)};
cursor: ${({ disabled }) => (disabled ? "not-allowed" : "pointer")};
`;

const Select = styled.div<{ open: boolean; selected: boolean }>`
display: flex;
padding: 4px 8px;
border-radius: 4px;
width: 100%;
font-size: 12px;
color: ${({ theme, selected }) => (selected ? theme.content.main : theme.content.weaker)};
background: ${({ theme }) => theme.bg[1]};
box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.25) inset;
border: ${({ theme, open }) =>
open ? `1px solid ${theme.select.strong}` : `1px solid ${theme.outline.weak}`};
&:focus {
border: 1px solid ${({ theme }) => theme.select.strong};
}
&:focus-visible {
border: 1px solid ${({ theme }) => theme.select.strong};
outline: none;
}
`;

const ArrowDownIcon = styled(Icon)`
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
`;

const PickerWrapper = styled(Popover.Content)`
min-width: 100px;
gap: 10px;
border: 1px solid ${({ theme }) => theme.outline.weak};
outline: none;
border-radius: 4px;
background: ${({ theme }) => theme.bg[1]};
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25);
display: flex;
flex-direction: column;
justify-content: space-between;
`;

const Option = styled(Text)<{ selected: boolean }>`
padding: 4px 12px;
cursor: ${({ selected }) => (selected ? "not-allowed" : "pointer")};
&:hover {
background: ${({ theme, selected }) => (selected ? theme.bg[2] : theme.select.main)};
}
`;

export default SelectField;
1 change: 1 addition & 0 deletions web/src/services/i18n/translations/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Cancel: Cancel
Apply: Apply
You have passed the maximum value.: You have passed the maximum value.
You have passed the minimum value.: You have passed the minimum value.
Please choose an option: ''
Not found: Not found
Padding settings: Padding settings
Remove: Remove
Expand Down
1 change: 1 addition & 0 deletions web/src/services/i18n/translations/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Cancel: キャンセル
Apply: Apply
You have passed the maximum value.: You have passed the maximum value.
You have passed the minimum value.: You have passed the minimum value.
Please choose an option: ''
Not found: ページが見つかりません
Padding settings: ''
Remove: 削除
Expand Down

0 comments on commit 4ee1969

Please sign in to comment.