diff --git a/web/src/beta/components/Toggle/index.stories.tsx b/web/src/beta/components/Toggle/index.stories.tsx new file mode 100644 index 0000000000..6215c19e52 --- /dev/null +++ b/web/src/beta/components/Toggle/index.stories.tsx @@ -0,0 +1,53 @@ +import { useArgs } from "@storybook/preview-api"; +import { Meta, StoryObj } from "@storybook/react"; +import { useCallback } from "react"; + +import { styled } from "@reearth/services/theme"; + +import Toggle, { Props } from "."; + +const meta: Meta = { + component: Toggle, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = (args: Props) => { + const [_, updateArgs] = useArgs(); + + const handleChange = useCallback( + (checked: boolean) => updateArgs({ checked: !checked }), + [updateArgs], + ); + + return ( + +
+ +
+
+ +
+
+ +
+
+ ); +}; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + gap: 10%; + margin-left: 2rem; + margin-top: 2rem; + height: 300px; +`; + +Default.args = { + checked: true, + disabled: false, + onChange: () => console.log("clicked"), +}; diff --git a/web/src/beta/components/Toggle/index.tsx b/web/src/beta/components/Toggle/index.tsx new file mode 100644 index 0000000000..c3997c63c9 --- /dev/null +++ b/web/src/beta/components/Toggle/index.tsx @@ -0,0 +1,57 @@ +import { useCallback } from "react"; + +import { styled } from "@reearth/services/theme"; + +export type Props = { + checked: boolean; + onChange: (checked: boolean) => void; + disabled?: boolean; +}; + +const Toggle: React.FC = ({ checked, onChange, disabled = false }) => { + const handleClick = useCallback( + () => !disabled && onChange(checked), + [checked, onChange, disabled], + ); + + return ( + + + + + + ); +}; + +export default Toggle; + +const Wrapper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +const Switch = styled.label<{ + checked: boolean; + disabled: boolean; +}>` + cursor: ${({ disabled }) => (!disabled ? "pointer" : "not-allowed")}; + width: 24px; + height: 14px; + background: ${({ checked, theme }) => (checked ? theme.select.main : theme.secondary.main)}; + border: 1px solid ${({ checked, theme }) => (checked ? theme.select.main : theme.secondary.main)}; + border-radius: 12px; + opacity: ${({ disabled }) => (!disabled ? 1 : 0.5)}; + transition: 0.4s; +`; + +const TopSlider = styled.div<{ + checked: boolean; +}>` + width: 14px; + height: 14px; + background: ${({ theme }) => theme.content.withBackground}; + transition: 0.4s; + border-radius: 50%; + transform: ${({ checked }) => checked && "translateX(10px)"}; +`; diff --git a/web/src/beta/components/fields/PropertyFields/index.tsx b/web/src/beta/components/fields/PropertyFields/index.tsx index d927a6f49a..7be829a1c8 100644 --- a/web/src/beta/components/fields/PropertyFields/index.tsx +++ b/web/src/beta/components/fields/PropertyFields/index.tsx @@ -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 ToggleField from "../ToggleField"; import useHooks from "./hooks"; @@ -18,6 +19,7 @@ const PropertyFields: React.FC = ({ propertyId, item }) => { {item?.schemaFields.map(sf => { const isList = item && "items" in item; const value = !isList ? item.fields.find(f => f.id === sf.id)?.value : sf.defaultValue; + return sf.type === "string" ? ( sf.ui === "color" ? ( = ({ propertyId, item }) => { onChange={handlePropertyValueUpdate(item.schemaGroup, propertyId, sf.id, sf.type)} /> ) + ) : sf.type == "bool" ? ( + ) : (

{sf.name} field

); diff --git a/web/src/beta/components/fields/Toggle/index.tsx b/web/src/beta/components/fields/Toggle/index.tsx deleted file mode 100644 index 587ad32693..0000000000 --- a/web/src/beta/components/fields/Toggle/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { useCallback } from "react"; - -import Text from "@reearth/beta/components/Text"; -import { styled, useTheme } from "@reearth/services/theme"; - -export type ToggleSize = "sm" | "md"; - -interface ToggleButtonProps { - checked?: boolean; - disabled?: boolean; - parentSelected?: boolean; - label?: string; - size?: ToggleSize; - onChange?: (checked: boolean) => void; -} - -const ToggleButton: React.FC = ({ - checked, - disabled, - parentSelected, - label, - size = "md", - onChange, -}) => { - const theme = useTheme(); - - const handleClick = useCallback(() => onChange?.(!checked), [checked, onChange]); - - return ( - - {label && ( - - )} - - - - - ); -}; - -export default ToggleButton; - -const Wrapper = styled.div` - display: flex; - justify-content: space-between; - align-items: center; -`; - -const Switch = styled.label<{ - size?: ToggleSize; - checked?: boolean; - disabled?: boolean; - selected?: boolean; -}>` - cursor: pointer; - width: ${({ size }) => (size === "sm" ? "28px" : "40px")}; - height: ${({ size }) => (size === "sm" ? "14px" : "20px")}; - background: ${({ theme }) => theme.select.main}; - border: 1px solid ${({ theme }) => theme.select.main}; - border-radius: 11px; - opacity: ${({ checked, selected }) => (checked || selected ? 1 : 0.5)}; - transition: 0.4s; -`; - -const TopSlider = styled.div<{ - size?: ToggleSize; - checked?: boolean; - disabled?: boolean; - selected?: boolean; -}>` - width: ${({ size }) => (size === "sm" ? "14px" : "20px")}; - height: ${({ size }) => (size === "sm" ? "14px" : "20px")}; - background: ${({ selected, theme }) => - selected ? theme.content.withBackground : theme.content.main}; - transition: 0.4s; - border-radius: 50%; - transform: ${({ checked }) => checked && "translateX(100%)"}; -`; - -const Label = styled(Text)` - margin-right: 10px; - vertical-align: middle; - display: inline; -`; diff --git a/web/src/beta/components/fields/ToggleField/index.stories.tsx b/web/src/beta/components/fields/ToggleField/index.stories.tsx new file mode 100644 index 0000000000..9e704f49aa --- /dev/null +++ b/web/src/beta/components/fields/ToggleField/index.stories.tsx @@ -0,0 +1,67 @@ +import { useArgs } from "@storybook/preview-api"; +import { Meta, StoryObj } from "@storybook/react"; +import { useCallback } from "react"; + +import { styled } from "@reearth/services/theme"; + +import ToggleField, { Props } from "."; + +const meta: Meta = { + component: ToggleField, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = (args: Props) => { + const [_, updateArgs] = useArgs(); + + const handleChange = useCallback( + (checked: boolean) => updateArgs({ checked: !checked }), + [updateArgs], + ); + + return ( + +
+ +
+
+ +
+
+ +
+
+ ); +}; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + gap: 10%; + margin-left: 2rem; + margin-top: 2rem; + height: 300px; +`; + +Default.args = { + name: "Toggle Field", + description: "Sample description", + checked: true, + disabled: false, + onChange: () => console.log("clicked"), +}; diff --git a/web/src/beta/components/fields/ToggleField/index.tsx b/web/src/beta/components/fields/ToggleField/index.tsx new file mode 100644 index 0000000000..863bfbf9e8 --- /dev/null +++ b/web/src/beta/components/fields/ToggleField/index.tsx @@ -0,0 +1,17 @@ +import Property from "@reearth/beta/components/fields"; +import Toggle, { Props as ToggleProps } from "@reearth/beta/components/Toggle"; + +export type Props = { + name?: string; + description?: string; +} & ToggleProps; + +const ToggleField: React.FC = ({ name, description, ...args }: Props) => { + return ( + + + + ); +}; + +export default ToggleField; diff --git a/web/src/beta/features/Editor/tabs/publish/Nav/PublishModal/index.tsx b/web/src/beta/features/Editor/tabs/publish/Nav/PublishModal/index.tsx index b4616842d1..ddcb77fbc6 100644 --- a/web/src/beta/features/Editor/tabs/publish/Nav/PublishModal/index.tsx +++ b/web/src/beta/features/Editor/tabs/publish/Nav/PublishModal/index.tsx @@ -1,7 +1,7 @@ import { useMemo } from "react"; import Button from "@reearth/beta/components/Button"; -import ToggleButton from "@reearth/beta/components/fields/Toggle"; +import ToggleField from "@reearth/beta/components/fields/ToggleField"; import Icon from "@reearth/beta/components/Icon"; import Modal from "@reearth/beta/components/Modal"; import Text from "@reearth/beta/components/Text"; @@ -172,10 +172,12 @@ const PublishModal: React.FC = ({ - - {t("Search engine indexing")} - - + ) : ( @@ -244,9 +246,3 @@ const UrlText = styled(Text)` const HideableSection = styled(Section)<{ showOptions?: boolean }>` display: ${props => (props.showOptions ? null : "none")}; `; - -const Wrapper = styled.div` - display: flex; - justify-content: space-between; - align-content: center; -`; diff --git a/web/src/beta/features/Editor/tabs/widgets/Nav/index.tsx b/web/src/beta/features/Editor/tabs/widgets/Nav/index.tsx index 5b0a8161f6..05c695a10a 100644 --- a/web/src/beta/features/Editor/tabs/widgets/Nav/index.tsx +++ b/web/src/beta/features/Editor/tabs/widgets/Nav/index.tsx @@ -1,6 +1,7 @@ import { useEffect } from "react"; -import Toggle from "@reearth/beta/components/fields/Toggle"; +import Text from "@reearth/beta/components/Text"; +import Toggle from "@reearth/beta/components/Toggle"; import SecondaryNav from "@reearth/beta/features/Editor/SecondaryNav"; import { useT } from "@reearth/services/i18n"; import { selectedWidgetAreaVar } from "@reearth/services/state"; @@ -37,12 +38,8 @@ const Nav: React.FC = ({ - + {t("Align System")} + ); @@ -57,4 +54,7 @@ const StyledSecondaryNav = styled(SecondaryNav)` padding-left: 8px; `; -const AlignSystem = styled.div``; +const AlignSystem = styled.div` + display: flex; + gap: 10px; +`; diff --git a/web/src/services/i18n/translations/en.yml b/web/src/services/i18n/translations/en.yml index 7454a971a0..a714cd966b 100644 --- a/web/src/services/i18n/translations/en.yml +++ b/web/src/services/i18n/translations/en.yml @@ -32,6 +32,7 @@ Embed Code: Embed Code '* Please use this code if you want to embed your project into a webpage': '* Please use this code if you want to embed your project into a webpage' more options: more options Search engine indexing: Search engine indexing +Page will be available as result on search engines: '' Your project will be unpublished.: Your project will be unpublished. This means that anybody with the URL will become unable to view this project.: This means that anybody with the URL will become unable to view this project. '**Warning**: This includes websites where this project is embedded.': '**Warning**: This includes websites where this project is embedded.' diff --git a/web/src/services/i18n/translations/ja.yml b/web/src/services/i18n/translations/ja.yml index 53f1553dd5..dc2d335e1d 100644 --- a/web/src/services/i18n/translations/ja.yml +++ b/web/src/services/i18n/translations/ja.yml @@ -28,6 +28,7 @@ Embed Code: 埋め込み用コード '* Please use this code if you want to embed your project into a webpage': その他のWebサイトでこのプロジェクトを埋め込む場合は、こちらのコードを使用してください more options: その他の設定 Search engine indexing: 検索可能にする +Page will be available as result on search engines: '' Your project will be unpublished.: プロジェクトを非公開にする This means that anybody with the URL will become unable to view this project.: URLを知っている人もこのプロジェクトを見ることができなくなります。 '**Warning**: This includes websites where this project is embedded.': '**Warning**このプロジェクトを埋め込んだWebサイトへ影響を及ぼす可能性があります。'