diff --git a/i18next-parser.config.js b/i18next-parser.config.js index 221e4d7e5..013887b68 100644 --- a/i18next-parser.config.js +++ b/i18next-parser.config.js @@ -2,4 +2,6 @@ module.exports = { locales: ["en", "ja"], output: "src/i18n/translations/$LOCALE.yml", input: ["src/**/*.{ts,tsx}"], + keySeparator: false, + createOldCatalogs: false, }; diff --git a/package.json b/package.json index 10b39488d..df36e709b 100644 --- a/package.json +++ b/package.json @@ -217,4 +217,4 @@ "use-debounce": "^7.0.1", "use-file-input": "^1.0.0" } -} +} \ No newline at end of file diff --git a/src/components/atoms/TabSection/index.tsx b/src/components/atoms/TabSection/index.tsx new file mode 100644 index 000000000..2652a5794 --- /dev/null +++ b/src/components/atoms/TabSection/index.tsx @@ -0,0 +1,106 @@ +import React, { useEffect, useState } from "react"; + +import Text from "@reearth/components/atoms/Text"; +import { styled, useTheme } from "@reearth/theme"; + +import Divider from "../Divider"; + +export type MenuAlignment = "left" | "top"; + +export interface Props { + className?: string; + children?: { [m in T]?: React.ReactNode }; + headerAction?: React.ReactNode; + menuAlignment?: MenuAlignment; + scrollable?: boolean; + expandedMenuIcon?: boolean; + selected?: T; + initialSelected?: T; + onChange?: (m: T) => void; + headers?: { [m in T]?: string }; +} +const TabSection = ({ + className, + children, + headerAction, + menuAlignment, + scrollable, + selected, + onChange, + initialSelected, + headers, +}: Props) => { + const tabs: T[] = (Object.keys(children || {}) as T[]).filter(k => !!children?.[k]); + const [selectedTab, select] = useState(initialSelected ?? tabs?.[0]); + useEffect(() => { + select(selected); + }, [selected]); + + const theme = useTheme(); + + return ( + + + {tabs?.map((m: T) => ( + { + select(m); + onChange?.(m); + }}> + + {headers?.[m] ?? m} + + + ))} + {{headerAction}} + + + + {selectedTab ? children?.[selectedTab] ?? null : null} + + + ); +}; +const Wrapper = styled.div<{ menuAlignment?: MenuAlignment }>` + display: flex; + flex-flow: ${({ menuAlignment }) => (menuAlignment === "top" ? "column" : "row")} nowrap; + justify-content: stretch; + position: relative; +`; +const TabHeader = styled.div<{ menuAlignment?: MenuAlignment }>` + display: flex; + flex-flow: ${({ menuAlignment }) => (menuAlignment === "top" ? "column" : "row")} nowrap; + align-items: space-between; + gap: 24px; + padding: 0px; + width: 100%; + height: 100%; +`; + +const TabTitle = styled.div<{ selected?: boolean }>` + cursor: pointer; + user-select: none; + border-bottom: 2px solid ${({ selected, theme }) => (selected ? theme.main.select : "none")}; + opacity: ${({ selected }) => (selected ? "1" : "0.7")}; +`; + +const ActionWrapper = styled.div<{ expanded?: boolean }>` + flex: ${props => (props.expanded ? "1" : null)}; + display: flex; + flex-direction: row-reverse; + align-items: flex-start; + margin-block-start: -13px; +`; +const TabContent = styled.div<{ scrollable?: boolean }>` + flex: auto; + display: flex; + flex-flow: column nowrap; + justify-content: stretch; + overflow-x: hidden; + overflow-y: ${({ scrollable }) => (scrollable ? "auto" : "hidden")}; + -webkit-overflow-scrolling: touch; +`; + +export default TabSection; diff --git a/src/components/molecules/Common/AssetModal/AssetContainer/hooks.ts b/src/components/molecules/Common/AssetModal/AssetContainer/hooks.ts index 57cf37a3d..ee0e1e8df 100644 --- a/src/components/molecules/Common/AssetModal/AssetContainer/hooks.ts +++ b/src/components/molecules/Common/AssetModal/AssetContainer/hooks.ts @@ -19,15 +19,6 @@ export type Asset = { contentType: string; }; -function handleScroll( - { currentTarget }: React.UIEvent, - onLoadMore?: () => void, -) { - if (currentTarget.scrollTop + currentTarget.clientHeight >= currentTarget.scrollHeight) { - onLoadMore?.(); - } -} - export default ({ isMultipleSelectable, selectedAssets, @@ -112,7 +103,6 @@ export default ({ iconChoice, deleteModalVisible, sortOptions, - handleScroll, setLayoutType, handleUploadToAsset, handleReverse, diff --git a/src/components/molecules/Common/AssetModal/AssetContainer/index.tsx b/src/components/molecules/Common/AssetModal/AssetContainer/index.tsx index 5515b98d5..f16646218 100644 --- a/src/components/molecules/Common/AssetModal/AssetContainer/index.tsx +++ b/src/components/molecules/Common/AssetModal/AssetContainer/index.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { useIntl } from "react-intl"; import Button from "@reearth/components/atoms/Button"; import Divider from "@reearth/components/atoms/Divider"; @@ -9,8 +8,10 @@ import Loading from "@reearth/components/atoms/Loading"; import SearchBar from "@reearth/components/atoms/SearchBar"; import Text from "@reearth/components/atoms/Text"; import AssetDeleteModal from "@reearth/components/molecules/Common/AssetModal/AssetDeleteModal"; +import { useT } from "@reearth/i18n"; import { styled } from "@reearth/theme"; import { metricsSizes } from "@reearth/theme/metrics"; +import { handleScroll } from "@reearth/util/handleScroll"; import AssetCard from "../AssetCard"; import AssetListItem from "../AssetListItem"; @@ -73,13 +74,12 @@ const AssetContainer: React.FC = ({ onSortChange, onSearch, }) => { - const intl = useIntl(); + const t = useT(); const { layoutType, iconChoice, deleteModalVisible, sortOptions, - handleScroll, setLayoutType, handleUploadToAsset, handleReverse, @@ -103,7 +103,7 @@ const AssetContainer: React.FC = ({