diff --git a/src/components/atoms/Icon/Icons/update.svg b/src/components/atoms/Icon/Icons/update.svg new file mode 100644 index 000000000..d0c283d23 --- /dev/null +++ b/src/components/atoms/Icon/Icons/update.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/atoms/Icon/icons.ts b/src/components/atoms/Icon/icons.ts index 312ce299f..af0e6d22a 100644 --- a/src/components/atoms/Icon/icons.ts +++ b/src/components/atoms/Icon/icons.ts @@ -31,6 +31,7 @@ import File from "./Icons/fileIcon.svg"; import PcIcon from "./Icons/pcIcon.svg"; import GoogleDriveIcon from "./Icons/googleDriveIcon.svg"; import SheetFileIcon from "./Icons/sheet-file.svg"; +import Update from "./Icons/update.svg"; // Asset import AssetGrid from "./Icons/assetGrid.svg"; @@ -148,6 +149,7 @@ export default { dataset: Dataset, datasetAdd: DatasetAdd, file: File, + update: Update, googleDrive: GoogleDriveIcon, sheetFile: SheetFileIcon, computer: PcIcon, diff --git a/src/components/atoms/TabCard/index.stories.tsx b/src/components/atoms/TabCard/index.stories.tsx new file mode 100644 index 000000000..06a9e9c42 --- /dev/null +++ b/src/components/atoms/TabCard/index.stories.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +import { Meta, Story } from "@storybook/react"; +import TabCard, { Props } from "."; + +export default { + title: "atoms/TabCard", + component: TabCard, +} as Meta; + +const SampleBody = () =>
Hello
; + +export const Default: Story = args => ( + + + +); +Default.args = { + name: "Property", +}; diff --git a/src/components/atoms/TabCard/index.tsx b/src/components/atoms/TabCard/index.tsx new file mode 100644 index 000000000..1f39b970a --- /dev/null +++ b/src/components/atoms/TabCard/index.tsx @@ -0,0 +1,40 @@ +import React from "react"; + +import { styled, useTheme } from "@reearth/theme"; +import Text from "@reearth/components/atoms/Text"; +import Flex from "../Flex"; +import Box from "../Box"; + +export type Props = { + className?: string; + name?: string; + children?: React.ReactNode; +}; + +const TabCard: React.FC = ({ className, name, children }) => { + const theme = useTheme(); + return ( + + + + + {name} + + + {children} + + + ); +}; + +const Body = styled.div` + width: calc(100% - 32px); + background-color: ${props => props.theme.properties.bg}; + padding: 16px; +`; + +export default TabCard; diff --git a/src/components/atoms/Table/index.stories.tsx b/src/components/atoms/Table/index.stories.tsx new file mode 100644 index 000000000..7ff87c682 --- /dev/null +++ b/src/components/atoms/Table/index.stories.tsx @@ -0,0 +1,57 @@ +import React from "react"; + +import { Meta, Story } from "@storybook/react"; +import Table, { Props } from "."; + +export default { + title: "atoms/Table", + component: Table, +} as Meta; + +const headers = ["title", "lat", "lng", "size", "color", "text"]; +type Rows = typeof headers[number]; +type Item = { [k in Rows]: string | number }; +const data: Item[] = [ + { + title: "Japan", + lat: 35.03, + lng: 135.71, + size: 10, + color: "#3d86fa", + text: "short text", + }, + { + title: "America", + lat: 50.1, + lng: 170.71, + size: 40, + color: "#ffffff", + text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + }, +]; + +export const Default: Story> = args => ; + +export const Scroll: Story> = args =>
; +export const Auto: Story> = args =>
; + +Default.args = { + headers, + items: data, + scroll: false, +}; + +Scroll.args = { + headers, + items: data, + layout: "fixed", + columnWidth: "100px", + width: "400px", +}; + +Auto.args = { + headers, + items: data, + layout: "auto", + scroll: false, +}; diff --git a/src/components/atoms/Table/index.tsx b/src/components/atoms/Table/index.tsx new file mode 100644 index 000000000..b0345ed87 --- /dev/null +++ b/src/components/atoms/Table/index.tsx @@ -0,0 +1,115 @@ +import React from "react"; + +import theme, { fonts, styled } from "@reearth/theme"; +import { TypographySize } from "@reearth/theme/fonts"; + +export type Props = { + className?: string; + headers?: (keyof T)[]; + items?: T[]; + bg?: string; + borderColor?: string; + textColor?: string; + textSize?: TypographySize; + layout?: "auto" | "fixed"; + textAlign?: "left" | "center" | "right"; + width?: string; + columnWidth?: string; + columnHeight?: string; + scroll?: boolean; + multiLine?: boolean; +}; + +export default function Table({ + className, + width, + headers, + items, + bg, + borderColor, + textColor, + textSize = "s", + layout = "auto", + textAlign = "left", + columnWidth, + columnHeight, + scroll = true, + multiLine = false, +}: Props): JSX.Element | null { + return ( + + + + {headers?.map((h, i) => ( + + {h} + + ))} + + + + {items?.map((item, i) => { + return ( + + {headers?.map((h, i) => { + return {item[h]}; + })} + + ); + })} + + + ); +} + +const StyledTable = styled.table<{ + bg?: string; + borderColor?: string; + textColor?: string; + textSize?: number; + layout?: "auto" | "fixed"; + textAlign?: "left" | "center" | "right"; + multiLine?: boolean; + columnWidth?: string; + columnHeight?: string; + scroll?: boolean; + width?: string; +}>` + table-layout: ${({ layout }) => layout}; + text-align: ${({ textAlign }) => textAlign}; + white-space: ${({ multiLine }) => (multiLine ? "normal" : "nowrap")}; + background: ${({ bg, theme }) => (bg ? bg : theme.main.bg)}; + border-color: ${({ borderColor, theme }) => (borderColor ? borderColor : theme.main.lighterBg)}; + color: ${({ textColor }) => (textColor ? textColor : theme.main.text)}; + font-size: ${({ textSize }) => `${textSize}px`}; + width: ${({ width }) => (width ? width : "100%")}; + height: ${({ columnHeight }) => columnHeight}; + overflow: ${({ scroll }) => (scroll ? "scroll" : "hidden")}; + display: block; +`; + +const StyledTh = styled.th<{ width?: string }>` + padding: ${({ theme }) => theme.metrics.s}px; + font-weight: ${fonts.weight.normal}; + width: ${({ width }) => width}; + overflow: hidden; + text-overflow: ellipsis; +`; + +const StyledTd = styled.td` + padding: ${({ theme }) => theme.metrics.s}px; + overflow: hidden; + text-overflow: ellipsis; +`; diff --git a/src/components/molecules/EarthEditor/DatasetInfoPane/DatasetProperty/PropertyItem/index.tsx b/src/components/molecules/EarthEditor/DatasetInfoPane/DatasetProperty/PropertyItem/index.tsx new file mode 100644 index 000000000..9d9ce8957 --- /dev/null +++ b/src/components/molecules/EarthEditor/DatasetInfoPane/DatasetProperty/PropertyItem/index.tsx @@ -0,0 +1,68 @@ +import React, { useCallback, useState } from "react"; +import { useIntl } from "react-intl"; + +import Button from "@reearth/components/atoms/Button"; +import Flex from "@reearth/components/atoms/Flex"; +import SelectField from "@reearth/components/atoms/SelectBox"; +import Text from "@reearth/components/atoms/Text"; +import { styled } from "@reearth/theme"; + +export type PrimitiveItem = { name: string; extensionId: string; icon: string; pluginId: string }; + +export type Props = { + primitiveItems?: PrimitiveItem[]; + onCreateLayerGroup?: (pluginId: string, extensionId: string) => void; +}; + +const DatasetPropertyItem: React.FC = ({ primitiveItems, onCreateLayerGroup }) => { + const intl = useIntl(); + const [selectedPrimitiveType, selectPrimitiveType] = useState(""); + + const handlePrimitiveTypeChange = (type: string) => { + if (primitiveItems?.map(p => p.extensionId).includes(type)) { + selectPrimitiveType(type); + } + }; + + const handleSubmit = useCallback(() => { + const item = primitiveItems?.find(p => p.extensionId === selectedPrimitiveType); + if (!item) return; + onCreateLayerGroup?.(item?.pluginId, item?.extensionId); + }, [onCreateLayerGroup, primitiveItems, selectedPrimitiveType]); + + const convertPrimitiveItemToDatasetPropertyItem = ( + items?: PrimitiveItem[], + ): { key: string; label: string; icon: string }[] => { + return items?.map(i => ({ key: i.extensionId, label: i.name, icon: i.icon })) || []; + }; + return ( + + + + {intl.formatMessage({ defaultMessage: "Layer style" })} + + + + + + + + ); +}; + +const StyledButton = styled(Button)` + width: 80px; + margin-left: auto; +`; + +export default DatasetPropertyItem; diff --git a/src/components/molecules/EarthEditor/DatasetInfoPane/index.stories.tsx b/src/components/molecules/EarthEditor/DatasetInfoPane/index.stories.tsx new file mode 100644 index 000000000..a8c47c5ac --- /dev/null +++ b/src/components/molecules/EarthEditor/DatasetInfoPane/index.stories.tsx @@ -0,0 +1,15 @@ +import React from "react"; + +import { Meta, Story } from "@storybook/react"; +import DatasetInfoPane, { Props } from "."; + +export default { + title: "molecules/EarthEditor/DatasetInfoPane", + component: DatasetInfoPane, +} as Meta; + +export const Default: Story = args => { + return ; +}; + +Default.args = {}; diff --git a/src/components/molecules/EarthEditor/DatasetInfoPane/index.tsx b/src/components/molecules/EarthEditor/DatasetInfoPane/index.tsx new file mode 100644 index 000000000..c2c262a33 --- /dev/null +++ b/src/components/molecules/EarthEditor/DatasetInfoPane/index.tsx @@ -0,0 +1,64 @@ +import React from "react"; +import { useIntl } from "react-intl"; + +import Flex from "@reearth/components/atoms/Flex"; +import TabCard from "@reearth/components/atoms/TabCard"; +import Table from "@reearth/components/atoms/Table"; +import { useTheme } from "@reearth/theme"; + +import DatasetPropertyItem, { + PrimitiveItem as PrimitiveItemType, +} from "./DatasetProperty/PropertyItem"; + +export type PrimitiveItem = PrimitiveItemType; + +export type Props = { + className?: string; + datasets?: { [key: string]: string }[]; + datasetHeaders?: string[]; + primitiveItems?: PrimitiveItem[]; + onCreateLayerGroup?: (pluginId: string, extensionId: string) => void; +}; + +const DatasetInfoPane: React.FC = ({ + datasetHeaders, + datasets, + primitiveItems, + onCreateLayerGroup, +}) => { + const intl = useIntl(); + const theme = useTheme(); + return ( + + + + {/* + from PC file + */} +
+ {/*