From 1b79cfb4113b30b5cd4da255e5493e07be468a88 Mon Sep 17 00:00:00 2001 From: nina992 <89770889+nina992@users.noreply.github.com> Date: Tue, 28 Nov 2023 10:33:26 +0400 Subject: [PATCH] chore(web): modify zoom level field (#808) Co-authored-by: nina992 Co-authored-by: KaWaite <34051327+KaWaite@users.noreply.github.com> --- .../components/RangeSlider/index.stories.tsx | 51 +++++++++++ web/src/beta/components/RangeSlider/index.tsx | 86 +++++++++++++++++++ .../fields/Property/PropertyField/index.tsx | 12 +++ .../fields/RangeField/index.stories.tsx | 62 +++++++++++++ .../components/fields/RangeField/index.tsx | 37 ++++++++ .../beta/features/Editor/Settings/index.tsx | 1 - web/src/beta/lib/core/Map/types/index.ts | 5 +- .../lib/core/engines/Cesium/core/Imagery.tsx | 9 +- 8 files changed, 254 insertions(+), 9 deletions(-) create mode 100644 web/src/beta/components/RangeSlider/index.stories.tsx create mode 100644 web/src/beta/components/RangeSlider/index.tsx create mode 100644 web/src/beta/components/fields/RangeField/index.stories.tsx create mode 100644 web/src/beta/components/fields/RangeField/index.tsx diff --git a/web/src/beta/components/RangeSlider/index.stories.tsx b/web/src/beta/components/RangeSlider/index.stories.tsx new file mode 100644 index 000000000..a131764b9 --- /dev/null +++ b/web/src/beta/components/RangeSlider/index.stories.tsx @@ -0,0 +1,51 @@ +import { useArgs } from "@storybook/preview-api"; +import { Meta, StoryObj } from "@storybook/react"; +import { useCallback } from "react"; + +import { styled } from "@reearth/services/theme"; + +import RangeSlider, { Props } from "."; + +const meta: Meta = { + component: RangeSlider, +}; + +type Story = StoryObj; + +export default meta; + +export const Default: Story = (args: Props) => { + const [_, updateArgs] = useArgs(); + + const handleChange = useCallback((value: number[]) => updateArgs({ value: value }), [updateArgs]); + + return ( + +
+ +
+
+ +
+
+ +
+
+ ); +}; + +Default.args = { + value: [2, 50], + min: 0, + max: 100, + step: 1, + disabled: false, +}; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + gap: 10%; + margin: 2rem; + height: 300px; +`; diff --git a/web/src/beta/components/RangeSlider/index.tsx b/web/src/beta/components/RangeSlider/index.tsx new file mode 100644 index 000000000..3a28be4a4 --- /dev/null +++ b/web/src/beta/components/RangeSlider/index.tsx @@ -0,0 +1,86 @@ +import RCSlider from "rc-slider"; +import React, { ComponentProps } from "react"; + +import { styled } from "@reearth/services/theme"; + +import "rc-slider/assets/index.css"; + +const RangeSliderWithTooltip = RCSlider.createSliderWithTooltip(RCSlider.Range); + +export type Props = { + min?: number; + max?: number; +} & ComponentProps; + +const calculateStep = (min?: number, max?: number, step?: number | null): number => { + if (step != undefined) { + return step; + } else if (min !== undefined && max !== undefined) { + const range = max - min; + let calculatedStep = range / 10; + if (range % calculatedStep !== 0) { + const steps = Math.ceil(range / calculatedStep); + calculatedStep = range / steps; + } + return calculatedStep; + } else { + return 1; + } +}; + +const RangeSlider: React.FC = ({ ...props }) => { + const calculatedStep = calculateStep(props?.min, props.max, props.step); + + return ( + + + + ); +}; + +const SliderStyled = styled.div<{ disabled: boolean }>` + width: 100%; + .rc-slider-disabled { + background-color: transparent; + opacity: ${({ disabled }) => (disabled ? 0.6 : 1)}; + cursor: ${({ disabled }) => (disabled ? "not-allowed" : "inherit")}; + } + + .rc-slider-rail { + height: 8px; + } + + .rc-slider-handle { + background-color: ${({ theme }) => theme.item.default}; + border: ${({ theme }) => theme.primary.weak}; + height: 12px; + width: 12px; + margin-top: -2px; + } + + .rc-slider-track { + background-color: ${({ theme }) => theme.primary.weak}; + height: 8px; + } + + .rc-slider-rail { + background-color: ${({ theme }) => theme.outline.weaker}; + box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.25) inset; + } + + .rc-slider-tooltip-arrow { + background-color: transparent; + border-top-color: ${({ theme }) => theme.bg[2]}; + bottom: 2px; + margin-left: -8px; + border-width: 9px 8px 0; + } + + .rc-slider-tooltip-inner { + background-color: ${({ theme }) => theme.bg[2]}; + color: ${({ theme }) => theme.content.main}; + box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.25); + } +`; + +export default RangeSlider; diff --git a/web/src/beta/components/fields/Property/PropertyField/index.tsx b/web/src/beta/components/fields/Property/PropertyField/index.tsx index 055d0fa55..9d9032376 100644 --- a/web/src/beta/components/fields/Property/PropertyField/index.tsx +++ b/web/src/beta/components/fields/Property/PropertyField/index.tsx @@ -10,6 +10,7 @@ import ColorField from "../../ColorField"; import DateTimeField from "../../DateTimeField"; import LocationField from "../../LocationField"; import NumberField from "../../NumberField"; +import RangeField from "../../RangeField"; import SelectField from "../../SelectField"; import SliderField from "../../SliderField"; import SpacingInput, { SpacingValues } from "../../SpacingInput"; @@ -154,6 +155,17 @@ const PropertyField: React.FC = ({ onSave={handleChange} onFlyTo={onFlyTo} /> + ) : schema.type === "array" && schema.ui === "range" ? ( + ) : (

{schema.name} field

)} diff --git a/web/src/beta/components/fields/RangeField/index.stories.tsx b/web/src/beta/components/fields/RangeField/index.stories.tsx new file mode 100644 index 000000000..0403b3706 --- /dev/null +++ b/web/src/beta/components/fields/RangeField/index.stories.tsx @@ -0,0 +1,62 @@ +import { useArgs } from "@storybook/preview-api"; +import { Meta, StoryObj } from "@storybook/react"; +import { useCallback } from "react"; + +import { styled } from "@reearth/services/theme"; + +import RangeField, { Props } from "."; + +const meta: Meta = { + component: RangeField, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = (args: Props) => { + const [_, updateArgs] = useArgs(); + + const handleChange = useCallback( + (value: number[]) => { + updateArgs({ value: value }); + }, + [updateArgs], + ); + + return ( + +
+ +
+
+ +
+
+ ); +}; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + gap: 10%; + margin: 2rem; + height: 300px; +`; + +Default.args = { + name: "Slider Field", + description: "Slider field Sample description", + value: [0, 50], + min: 0, + max: 100, + step: 1, + disabled: false, + onChange: () => console.log("clicked"), +}; diff --git a/web/src/beta/components/fields/RangeField/index.tsx b/web/src/beta/components/fields/RangeField/index.tsx new file mode 100644 index 000000000..6acd49099 --- /dev/null +++ b/web/src/beta/components/fields/RangeField/index.tsx @@ -0,0 +1,37 @@ +import { useCallback, useEffect, useState } from "react"; + +import Property from "@reearth/beta/components/fields"; +import RangeSlider, { Props as RangeProps } from "@reearth/beta/components/RangeSlider"; + +export type Props = { + name?: string; + description?: string; +} & RangeProps; + +const RangeField: React.FC = ({ name, description, value, onChange, ...args }: Props) => { + const [internalState, setInternalState] = useState(value); + + const handleChange = useCallback( + (value: number[]) => { + setInternalState(value); + }, + [setInternalState], + ); + + useEffect(() => { + setInternalState(value); + }, [value]); + + return ( + + + + ); +}; + +export default RangeField; diff --git a/web/src/beta/features/Editor/Settings/index.tsx b/web/src/beta/features/Editor/Settings/index.tsx index 540731389..0ab40c070 100644 --- a/web/src/beta/features/Editor/Settings/index.tsx +++ b/web/src/beta/features/Editor/Settings/index.tsx @@ -43,7 +43,6 @@ const Settings: React.FC = ({ selectedPage, onPageUpdate, }); - const visibleItems = useMemo(() => filterVisibleItems(propertyItems), [propertyItems]); return ( diff --git a/web/src/beta/lib/core/Map/types/index.ts b/web/src/beta/lib/core/Map/types/index.ts index 1f050c200..b16818301 100644 --- a/web/src/beta/lib/core/Map/types/index.ts +++ b/web/src/beta/lib/core/Map/types/index.ts @@ -251,7 +251,7 @@ export type SceneProperty = { id: string; tile_type?: string; tile_url?: string; - tile_zoomLevel?: number; + tile_zoomLevel?: number[]; tile_opacity?: number; }[]; terrain?: { @@ -320,8 +320,7 @@ type LegacySceneProperty = { id: string; tile_type?: string; tile_url?: string; - tile_maxLevel?: number; - tile_minLevel?: number; + tile_zoomLevel?: number[]; tile_opacity?: number; }[]; terrain?: TerrainProperty; diff --git a/web/src/beta/lib/core/engines/Cesium/core/Imagery.tsx b/web/src/beta/lib/core/engines/Cesium/core/Imagery.tsx index 3985773fc..14594a122 100644 --- a/web/src/beta/lib/core/engines/Cesium/core/Imagery.tsx +++ b/web/src/beta/lib/core/engines/Cesium/core/Imagery.tsx @@ -18,8 +18,7 @@ export type Tile = { tile_url?: string; tile_type?: string; tile_opacity?: number; - tile_minLevel?: number; - tile_maxLevel?: number; + tile_zoomLevel?: number[]; }; export type Props = { @@ -45,13 +44,13 @@ export default function ImageryLayers({ tiles, cesiumIonAccessToken }: Props) { <> {tiles ?.map(({ id, ...tile }) => ({ ...tile, id, provider: providers[id]?.[2] })) - .map(({ id, tile_opacity: opacity, tile_minLevel: min, tile_maxLevel: max, provider }, i) => + .map(({ id, tile_opacity: opacity, tile_zoomLevel, provider }, i) => provider ? (