Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
feat: main logic of the new layer system (#370)
Browse files Browse the repository at this point in the history
* init

* impl cesium

* enable vitest on vscode

* support photooverlay

* define appearance types

* add delegatedDataTypes

* fix storybook

* fix

* chore: update codeowners [ci skip]
  • Loading branch information
rot1024 committed Dec 12, 2022
1 parent 8d0a65e commit 0dd63e6
Show file tree
Hide file tree
Showing 82 changed files with 10,500 additions and 14 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
@@ -1,5 +1,6 @@
* @KaWaite
/e2e/ @rot1024
/src/core/ @rot1024
/.github/ @KaWaite @rot1024
/src/components/atoms/Plugin/ @KaWaite @rot1024
/src/components/molecules/Visualizer/ @KaWaite @rot1024
3 changes: 2 additions & 1 deletion .vscode/settings.json
Expand Up @@ -2,5 +2,6 @@
"typescript.tsdk": "node_modules/typescript/lib",
"yaml.schemas": {
"https://json.schemastore.org/github-workflow": "/.github/workflows/**/*.yml"
}
},
"vitest.commandLine": "yarn run vitest watch"
}
17 changes: 10 additions & 7 deletions package.json
Expand Up @@ -49,6 +49,11 @@
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "13.4.0",
"@testing-library/user-event": "14.4.3",
"@types/apollo-upload-client": "17.0.2",
"@types/gapi.auth2": "0.0.56",
"@types/gapi.client": "1.0.5",
"@types/gapi.client.sheets": "4.0.20201030",
"@types/google.picker": "0.0.39",
"@types/lodash-es": "4.17.6",
"@types/node": "18.11.9",
"@types/react": "18.0.24",
Expand All @@ -57,6 +62,8 @@
"@types/react-leaflet": "2.8.2",
"@types/storybook__addon-info": "5.2.5",
"@types/styled-components": "5.1.26",
"@types/tinycolor2": "1.4.3",
"@types/uuid": "^9.0.0",
"@vitejs/plugin-react": "2.2.0",
"@vitest/coverage-c8": "0.24.5",
"@welldone-software/why-did-you-render": "7.0.1",
Expand Down Expand Up @@ -95,12 +102,7 @@
"@rot1024/use-transition": "1.0.0",
"@sentry/browser": "6.19.7",
"@seznam/compose-react-refs": "1.0.6",
"@types/apollo-upload-client": "17.0.2",
"@types/gapi.auth2": "0.0.56",
"@types/gapi.client": "1.0.5",
"@types/gapi.client.sheets": "4.0.20201030",
"@types/google.picker": "0.0.39",
"@types/tinycolor2": "1.4.3",
"@turf/turf": "^6.5.0",
"@ungap/event-target": "0.2.3",
"apollo-link-sentry": "3.2.0",
"apollo-upload-client": "17.0.0",
Expand Down Expand Up @@ -155,6 +157,7 @@
"ts-easing": "0.2.0",
"use-callback-ref": "1.3.0",
"use-custom-compare": "1.2.0",
"use-file-input": "1.0.0"
"use-file-input": "1.0.0",
"uuid": "^9.0.0"
}
}
5 changes: 5 additions & 0 deletions src/core/Crust/Infobox/index.tsx
@@ -0,0 +1,5 @@
export type Props = {};

export default function Infobox(_props: Props): JSX.Element | null {
return null;
}
5 changes: 5 additions & 0 deletions src/core/Crust/Plugins/index.tsx
@@ -0,0 +1,5 @@
export type Props = {};

export default function Plugins(_props: Props): JSX.Element | null {
return null;
}
5 changes: 5 additions & 0 deletions src/core/Crust/Widgets/index.tsx
@@ -0,0 +1,5 @@
export type Props = {};

export default function Widgets(_props: Props): JSX.Element | null {
return null;
}
5 changes: 5 additions & 0 deletions src/core/Crust/index.tsx
@@ -0,0 +1,5 @@
export type Props = {};

export default function Crust(_props: Props): JSX.Element | null {
return null;
}
93 changes: 93 additions & 0 deletions src/core/Map/ClusteredLayers/index.tsx
@@ -0,0 +1,93 @@
import { ComponentType, useMemo, useCallback, ReactNode } from "react";

import type { Layer, Atom, Typography, DataType } from "../../mantle";
import LayerComponent, { type CommonProps, type Props as LayerProps } from "../Layer";

export type Props = {
layers?: Layer[];
atomMap?: Map<string, Atom>;
overrides?: Record<string, Record<string, any>>;
selectedLayerId?: string;
isHidden?: (id: string) => boolean;
clusters?: Cluster[];
delegatedDataTypes?: DataType[];
clusterComponent?: ClusterComponentType;
Feature?: LayerProps["Feature"];
} & Omit<CommonProps, "isSelected" | "isHidden">;

export type Cluster = {
id: string;
property?: ClusterProperty;
layers?: string[];
};

export type ClusterComponentProps = {
cluster: Cluster;
property?: ClusterProperty;
children?: ReactNode;
};

export type ClusterProperty = {
default?: {
clusterPixelRange: number;
clusterMinSize: number;
clusterLabelTypography?: Typography;
clusterImage?: string;
clusterImageHeight?: number;
clusterImageWidth?: number;
};
layers?: { layer?: string }[];
};

export type ClusterComponentType = ComponentType<ClusterComponentProps>;

export default function ClusteredLayers({
clusters,
clusterComponent,
layers,
atomMap,
selectedLayerId,
overrides,
delegatedDataTypes,
isHidden,
...props
}: Props): JSX.Element | null {
const Cluster = clusterComponent;
const clusteredLayers = useMemo<Set<string>>(
() => new Set(clusters?.flatMap(c => (c.layers ?? []).filter(Boolean))),
[clusters],
);

const renderLayer = useCallback(
(layer: Layer) => {
const a = atomMap?.get(layer.id);
return !layer.id || !a ? null : (
<LayerComponent
key={layer.id}
{...props}
layer={layer}
atom={a}
overrides={overrides?.[layer.id]}
isSelected={!!selectedLayerId && selectedLayerId == layer.id}
isHidden={isHidden?.(layer.id)}
delegatedDataTypes={delegatedDataTypes}
/>
);
},
[atomMap, isHidden, overrides, props, selectedLayerId, delegatedDataTypes],
);

return (
<>
{Cluster &&
clusters
?.filter(cluster => !!cluster.id)
.map(cluster => (
<Cluster key={cluster.id} cluster={cluster}>
{layers?.filter(layer => cluster?.layers?.some(l => l === layer.id)).map(renderLayer)}
</Cluster>
))}
{layers?.filter(layer => !clusteredLayers.has(layer.id)).map(renderLayer)}
</>
);
}
58 changes: 58 additions & 0 deletions src/core/Map/Layer/hooks.ts
@@ -0,0 +1,58 @@
import { useAtom } from "jotai";
import { useCallback, useLayoutEffect, useMemo } from "react";

import { computeAtom, DataType, type Atom } from "../../mantle";
import type { DataRange, Feature, Layer } from "../../mantle";

export type { Atom as Atoms } from "../../mantle";

export const createAtom = computeAtom;

export default function useHooks(
layer: Layer | undefined,
atom: Atom | undefined,
overrides?: Record<string, any>,
delegatedDataTypes?: DataType[],
) {
const [computedLayer, set] = useAtom(useMemo(() => atom ?? createAtom(), [atom]));
const writeFeatures = useCallback(
(features: Feature[]) => set({ type: "writeFeatures", features }),
[set],
);
const requestFetch = useCallback(
(range: DataRange) => set({ type: "requestFetch", range }),
[set],
);
const deleteFeatures = useCallback(
(features: string[]) => set({ type: "deleteFeatures", features }),
[set],
);

useLayoutEffect(() => {
set({ type: "updateDelegatedDataTypes", delegatedDataTypes: delegatedDataTypes ?? [] });
}, [delegatedDataTypes, set]);

useLayoutEffect(() => {
set({
type: "override",
overrides,
});
}, [set, overrides]);

useLayoutEffect(() => {
set({
type: "setLayer",
layer:
typeof layer?.visible === "undefined" || layer?.type === null || layer?.type
? layer
: undefined,
});
}, [layer, set]);

return {
computedLayer,
handleFeatureRequest: requestFetch,
handleFeatureFetch: writeFeatures,
handleFeatureDelete: deleteFeatures,
};
}
59 changes: 59 additions & 0 deletions src/core/Map/Layer/index.tsx
@@ -0,0 +1,59 @@
import { ComponentType } from "react";

import type { DataRange, Feature, ComputedLayer, Layer, DataType } from "../../mantle";

import useHooks, { type Atoms } from "./hooks";

export type { Layer, LayerSimple } from "../../mantle";

export type FeatureComponentType = ComponentType<FeatureComponentProps>;

export type CommonProps = {
isBuilt?: boolean;
isEditable?: boolean;
isHidden?: boolean;
isSelected?: boolean;
sceneProperty?: any;
};

export type FeatureComponentProps = {
layer: ComputedLayer;
onFeatureRequest?: (range: DataRange) => void;
onFeatureFetch?: (features: Feature[]) => void;
onFeatureDelete?: (features: string[]) => void;
} & CommonProps;

export type Props = {
layer?: Layer;
atom?: Atoms;
overrides?: Record<string, any>;
delegatedDataTypes?: DataType[];
/** Feature component should be injected by a map engine. */
Feature?: ComponentType<FeatureComponentProps>;
} & CommonProps;

export default function LayerComponent({
Feature,
layer,
atom: atoms,
overrides,
delegatedDataTypes,
...props
}: Props): JSX.Element | null {
const { computedLayer, handleFeatureDelete, handleFeatureFetch, handleFeatureRequest } = useHooks(
Feature ? layer : undefined,
atoms,
overrides,
delegatedDataTypes,
);

return layer && computedLayer && Feature ? (
<Feature
layer={computedLayer}
onFeatureDelete={handleFeatureDelete}
onFeatureFetch={handleFeatureFetch}
onFeatureRequest={handleFeatureRequest}
{...props}
/>
) : null;
}

0 comments on commit 0dd63e6

Please sign in to comment.