Skip to content

Commit

Permalink
Merge ca65e59 into efd1d7d
Browse files Browse the repository at this point in the history
  • Loading branch information
steveoh authored Nov 26, 2024
2 parents efd1d7d + ca65e59 commit 7169caa
Show file tree
Hide file tree
Showing 22 changed files with 3,529 additions and 2,186 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"Geospatial",
"gnis",
"hostingchannels",
"Immer",
"lods",
"lucide",
"noopener",
Expand Down
4,391 changes: 2,444 additions & 1,947 deletions package-lock.json

Large diffs are not rendered by default.

58 changes: 31 additions & 27 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,59 +30,63 @@
]
},
"dependencies": {
"@arcgis/core": "^4.31.0",
"@ugrc/layer-selector": "^6.2.9",
"@ugrc/utah-design-system": "^1.14.1",
"firebase": "^11.0.1",
"@arcgis/core": "^4.31.6",
"@ugrc/layer-selector": "^6.2.10",
"@ugrc/utah-design-system": "^1.16.1",
"firebase": "^11.0.2",
"immer": "^10.1.1",
"ky": "^1.7.2",
"lodash.startcase": "^4.4.0",
"react": "^18.3.1",
"react-aria": "^3.35.1",
"react-aria-components": "^1.4.1",
"react-aria": "^3.36.0",
"react-aria-components": "^1.5.0",
"react-content-loader": "^7.0.2",
"react-dom": "^18.3.1",
"react-error-boundary": "^4.1.2",
"react-fast-compare": "^3.2.2",
"react-stately": "^3.33.0"
"react-stately": "^3.34.0",
"tailwind-variants": "^0.3.0",
"use-immer": "^0.10.0"
},
"devDependencies": {
"@chromatic-com/storybook": "^3.2.2",
"@eslint/js": "^9.14.0",
"@storybook/addon-essentials": "^8.4.2",
"@storybook/addon-interactions": "^8.4.2",
"@storybook/addon-links": "^8.4.2",
"@storybook/addon-onboarding": "^8.4.2",
"@storybook/blocks": "^8.4.2",
"@storybook/react": "^8.4.2",
"@storybook/react-vite": "^8.4.2",
"@storybook/test": "^8.4.2",
"@eslint/js": "^9.15.0",
"@storybook/addon-essentials": "^8.4.5",
"@storybook/addon-interactions": "^8.4.5",
"@storybook/addon-links": "^8.4.5",
"@storybook/addon-onboarding": "^8.4.5",
"@storybook/blocks": "^8.4.5",
"@storybook/react": "^8.4.5",
"@storybook/react-vite": "^8.4.5",
"@storybook/test": "^8.4.5",
"@types/eslint__js": "^8.42.3",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@typescript-eslint/eslint-plugin": "^8.14.0",
"@typescript-eslint/eslint-plugin": "^8.16.0",
"@vitejs/plugin-react": "^4.3.3",
"@vitest/coverage-v8": "^2.1.5",
"autoprefixer": "^10.4.20",
"cpy-cli": "^5.0.0",
"eslint": "^9.14.0",
"eslint": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.1.0-rc-5c56b873-20241107",
"eslint-plugin-storybook": "^0.11.0",
"eslint-plugin-storybook": "^0.11.1",
"globals": "^15.12.0",
"lucide-react": "^0.456.0",
"lucide-react": "^0.460.0",
"postcss": "^8.4.49",
"prettier": "^3.3.3",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-packagejson": "^2.5.3",
"prettier-plugin-tailwindcss": "^0.6.8",
"storybook": "^8.4.2",
"tailwindcss": "^3.4.14",
"tailwindcss-react-aria-components": "^1.1.6",
"prettier-plugin-packagejson": "^2.5.6",
"prettier-plugin-tailwindcss": "^0.6.9",
"storybook": "^8.4.5",
"tailwindcss": "^3.4.15",
"tailwindcss-react-aria-components": "^1.2.0",
"typescript": "^5.6.3",
"typescript-eslint": "^8.14.0",
"typescript-eslint": "^8.16.0",
"vite": "^5.4.11",
"vite-plugin-package-version": "^1.1.0",
"vitest": "^2.1.4"
"vitest": "^2.1.5"
}
}
42 changes: 19 additions & 23 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ import esriConfig from '@arcgis/core/config.js';
import Collection from '@arcgis/core/core/Collection.js';

import { Drawer } from '@ugrc/utah-design-system';
import PropTypes from 'prop-types';
import { useOverlayTrigger } from 'react-aria';
import { ErrorBoundary } from 'react-error-boundary';
import { useOverlayTriggerState } from 'react-stately';
import {
CentroidToggle,
FeatureData,
MapContainer,
ProjectStatus,
ReferenceData,
ReferenceLabelSwitch,
ReferenceLayer,
TagGroupLoader,
} from './components';
import { projectStatus } from './components/data/filters.js';
import { FilterProvider } from './components/contexts';
import { featureTypes, projectStatus } from './components/data/filters.js';
import { useMap } from './components/hooks';
import config from './config.js';

Expand All @@ -28,10 +29,6 @@ const ErrorFallback = ({ error }: { error: Error }) => {
);
};

ErrorFallback.propTypes = {
error: PropTypes.object,
};

esriConfig.assetsPath = import.meta.env.MODE === 'production' ? '/wri/js/ugrc/assets' : '/js/ugrc/assets';

export default function App() {
Expand Down Expand Up @@ -59,7 +56,7 @@ export default function App() {

return (
<main className="flex h-full flex-1 flex-col md:gap-2">
<section className="relative flex min-h-0 flex-1 gap-2">
<section className="relative flex min-h-0 flex-1 overflow-x-hidden md:mr-2">
<Drawer main state={sideBarState} {...sideBarTriggerProps}>
<div className="mx-2 mb-2 grid grid-cols-1 gap-2">
<h2 className="text-xl font-bold dark:text-zinc-200">Map controls</h2>
Expand All @@ -68,22 +65,21 @@ export default function App() {
<h5 className="dark:text-zinc-200">Search tool</h5>
</ErrorBoundary>
</div>
<div className="flex flex-col gap-4 rounded border border-zinc-200 p-3 dark:border-zinc-700">
<ErrorBoundary FallbackComponent={ErrorFallback}>
<h5 className="dark:text-zinc-200">Project Status</h5>
{featureLayers.length > 0 ? (
<FeatureData layers={featureLayers} status={projectStatus} />
) : (
<TagGroupLoader />
)}
{featureLayers.length > 0 && <CentroidToggle />}
</ErrorBoundary>
</div>
<div className="flex flex-col gap-4 rounded border border-zinc-200 p-3 dark:border-zinc-700">
<ErrorBoundary FallbackComponent={ErrorFallback}>
<h5 className="dark:text-zinc-200">Feature Type</h5>
</ErrorBoundary>
</div>
<FilterProvider featureLayers={featureLayers}>
<div className="flex flex-col gap-4 rounded border border-zinc-200 p-3 dark:border-zinc-700">
<ErrorBoundary FallbackComponent={ErrorFallback}>
<h5 className="dark:text-zinc-200">Project Status</h5>
{featureLayers.length > 0 ? <ProjectStatus status={projectStatus} /> : <TagGroupLoader />}
{featureLayers.length > 0 && <CentroidToggle />}
</ErrorBoundary>
</div>
<div className="flex flex-col gap-4 rounded border border-zinc-200 p-3 dark:border-zinc-700">
<ErrorBoundary FallbackComponent={ErrorFallback}>
<h5 className="dark:text-zinc-200">Feature Type</h5>
{featureLayers.length > 0 ? <FeatureData featureTypes={featureTypes} /> : <TagGroupLoader />}
</ErrorBoundary>
</div>
</FilterProvider>
<div className="flex flex-col gap-4 rounded border border-zinc-200 p-3 dark:border-zinc-700">
<ErrorBoundary FallbackComponent={ErrorFallback}>
<h5 className="dark:text-zinc-200">Map Reference data</h5>
Expand Down
8 changes: 4 additions & 4 deletions src/components/CentroidToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ export const CentroidToggle = () => {
console.error('Error setting feature-centroid visibility');
}
try {
mapView.map.findLayerById('feature-polygons').visible = !selected;
mapView.map.findLayerById('feature-poly').visible = !selected;
} catch {
console.error('Error setting feature-polygon visibility');
console.error('Error setting feature-poly visibility');
}
try {
mapView.map.findLayerById('feature-lines').visible = !selected;
mapView.map.findLayerById('feature-line').visible = !selected;
} catch {
console.error('Error setting feature-line visibility');
}
try {
mapView.map.findLayerById('feature-points').visible = !selected;
mapView.map.findLayerById('feature-point').visible = !selected;
} catch {
console.error('Error setting feature-point visibility');
}
Expand Down
157 changes: 84 additions & 73 deletions src/components/FeatureData.tsx
Original file line number Diff line number Diff line change
@@ -1,84 +1,95 @@
import Collection from '@arcgis/core/core/Collection.js';
import { Button, Tag, TagGroup } from '@ugrc/utah-design-system';
import { useEffect, useState } from 'react';
import { type Selection } from 'react-aria-components';
import { tv } from 'tailwind-variants';
import { ProjectStatus } from './data/filters';
import { Button, Radio, RadioGroup, Tag, TagGroup } from '@ugrc/utah-design-system';
import { Dispatch, useContext } from 'react';
import { type Key } from 'react-stately';
import { type FilterAction, FilterContext } from './contexts';
import { FeatureType } from './data/filters';
import { areSetsEqual } from './utils';
const emptySet = new Set<Key>();

const defaultState = new Set(['Proposed', 'Current', 'Pending Completed', 'Completed']);
const all = '';
const none = '1=0';

const tagStyles = tv({
variants: {
status: {
draft: 'data-[selected]:bg-zinc-500 data-[selected]:hover:border-zinc-700 data-[selected]:border-gray-200',
proposed: 'data-[selected]:bg-zinc-800 data-[selected]:hover:border-zinc-900 data-[selected]:border-gray-200',
current: 'data-[selected]:bg-sky-600 data-[selected]:hover:border-sky-800 data-[selected]:border-gray-200',
'pending completed':
'data-[selected]:bg-yellow-500 data-[selected]:hover:border-yellow-600 data-[selected]:border-gray-200',
completed: 'data-[selected]:bg-green-700 data-[selected]:hover:border-green-900 data-[selected]:border-gray-200',
cancelled: 'data-[selected]:bg-red-700 data-[selected]:hover:border-red-900 data-[selected]:border-gray-200',
},
},
});

type Status = keyof typeof tagStyles.variants.status;

const setDefinitionExpression = (layers: Collection<__esri.FeatureLayer>, keys: Selection) =>
layers
.filter((x) => x.id.startsWith('feature'))
.forEach((layer) => {
if (keys === 'all') {
layer.definitionExpression = all;

return;
}

if (keys.size === 0) {
layer.definitionExpression = none;

return;
}

const statusField = layer.id === 'feature-centroids' ? 'status' : 'statusDescription';

layer.definitionExpression = `${statusField} in (${Array.from(keys)
.map((status) => `'${status}'`)
.join(',')})`;
});

export const FeatureData = ({
layers,
status,
}: {
layers: __esri.Collection<__esri.FeatureLayer>;
status: ProjectStatus[];
}) => {
const [selected, setSelected] = useState<Selection>(defaultState);

// synchronizes the definition expressions with the initial ui state
useEffect(() => {
setDefinitionExpression(layers, selected);
}, [layers, selected]);
export const FeatureData = ({ featureTypes }: { featureTypes: FeatureType[] }) => {
const { dispatch, defaultFeatureState, selectedFeatures } = useContext(FilterContext);

return (
<>
<TagGroup selectionMode="multiple" selectedKeys={selected} onSelectionChange={setSelected}>
{status.map(({ code, value }) => (
<Tag id={value} key={code} textValue={value} className={tagStyles({ status: value.toLowerCase() as Status })}>
{value}
<TagGroup
selectionMode="multiple"
defaultSelectedKeys={defaultFeatureState}
selectedKeys={selectedFeatures}
onSelectionChange={(value) =>
dispatch({
type: 'set',
payload: {
features: value as Set<Key>,
},
metadata: 'feature',
})
}
>
{featureTypes.map(({ code, featureType }) => (
<Tag id={featureType} key={code} textValue={featureType}>
{featureType}
</Tag>
))}
</TagGroup>
{!areSetsEqual(defaultState, selected === 'all' ? new Set([]) : selected) && (
<span>
<Button variant="destructive" size="extraSmall" onPress={() => setSelected(defaultState)}>
Reset
</Button>
</span>
)}
<span className="flex gap-2">
<Button
variant="destructive"
size="extraSmall"
isDisabled={areSetsEqual(
defaultFeatureState as Set<Key>,
selectedFeatures === 'all' ? emptySet : selectedFeatures,
)}
onPress={() =>
dispatch({
type: 'set',
payload: {
features: defaultFeatureState as Set<Key>,
},
metadata: 'feature',
})
}
>
Reset
</Button>
<Button
variant="destructive"
size="extraSmall"
isDisabled={selectedFeatures === 'all' || selectedFeatures.size === 0}
onPress={() =>
dispatch({
type: 'set',
payload: {
features: emptySet,
},
metadata: 'feature',
})
}
>
Clear
</Button>
</span>
<JoinWith defaultValue={'or'} dispatch={dispatch} />
</>
);
};

export const JoinWith = ({ defaultValue, dispatch }: { defaultValue: string; dispatch: Dispatch<FilterAction> }) => {
return (
<RadioGroup
defaultValue={defaultValue}
label="Feature match style"
onChange={(value) => {
dispatch({
type: 'set',
payload: {
join: value as 'and' | 'or',
},
metadata: 'feature-join',
});
}}
>
<Radio value="or">any</Radio>
<Radio value="and">all</Radio>
</RadioGroup>
);
};
Loading

0 comments on commit 7169caa

Please sign in to comment.