Skip to content

Commit

Permalink
chore(vue): Cleanup and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy committed Sep 6, 2024
1 parent 97472b3 commit 3bb3a0d
Show file tree
Hide file tree
Showing 17 changed files with 118 additions and 16 deletions.
10 changes: 10 additions & 0 deletions packages/create-react/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
# CARTO application template (React)

## Quickstart

```bash
# install dependencies
yarn

# start local development server
yarn dev
```
10 changes: 10 additions & 0 deletions packages/create-vue/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
# CARTO application template (Vue)

## Quickstart

```bash
# install dependencies
yarn

# start local development server
yarn dev
```
1 change: 1 addition & 0 deletions packages/create-vue/src/components/AppLayout.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
/** Primary app layout, including a header with navigation links. */
import { context } from '../context';
import { routes, RoutePath, NAV_ROUTES } from '../routes';
</script>
Expand Down
5 changes: 5 additions & 0 deletions packages/create-vue/src/components/Card.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<script setup lang="ts">
/**
* Card UI. Used as a simple, collapsible container for widgets, legends,
* and layer selection. If the title is omitted, no header is shown and
* the card is no longer collapsible.
*/
const props = withDefaults(
defineProps<{
title?: string;
Expand Down
7 changes: 7 additions & 0 deletions packages/create-vue/src/components/Layers.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
<script setup lang="ts">
/**
* Layer list and visibility controller.
*/
import Card from './Card.vue';
import { Layer } from '@deck.gl/core';
const props = withDefaults(
defineProps<{
/** Whether the layer list is open (default) or closed. */
open?: boolean;
/** List of deck.gl layers. */
layers: Layer[];
/** Layer visibility state. Object keys are layer names, values are boolean visibility. */
layerVisibility: Record<string, boolean>;
/** Callback to be invoked by the Layers component if a layer's visibility is toggled. */
onLayerVisibilityChange: (visibility: Record<string, boolean>) => void;
}>(),
{ open: true },
Expand Down
6 changes: 6 additions & 0 deletions packages/create-vue/src/components/ProtectedRoute.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<script setup lang="ts">
/**
* Wrapper component for routes that require authentication. If authentication
* is disabled at the application level, this component does nothing.
*/
import { effect } from 'vue';
import AppLayout from './AppLayout.vue';
import { useAuth0 } from '@auth0/auth0-vue';
Expand All @@ -9,6 +13,7 @@ import { useAuth } from '../hooks/useAuth';
useAuth();
const { isAuthenticated, isLoading } = useAuth0();
// If necessary, redirect to login page.
effect(() => {
if (
context.oauth.enabled &&
Expand All @@ -22,6 +27,7 @@ effect(() => {
</script>

<template>
<!-- If we're logged in but still waiting for an access token, wait to render children. -->
<template v-if="!context.oauth.enabled || context.accessToken">
<AppLayout>
<slot />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<script lang="ts">
/**
* Categorical legend entry, representing each category with a solid color.
*/
import { Color } from '@deck.gl/core';
import { toHexString } from '../../utils';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<script lang="ts">
/**
* Continuous legend entry, representing a continuous domain [min, max]
* as a color gradient, with labeled min and max values.
*/
import type { Color } from '@deck.gl/core';
export type LegendEntryContinuousProps = {
Expand Down
12 changes: 7 additions & 5 deletions packages/create-vue/src/components/views/CellTowersView.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<script setup lang="ts">
/**
* Example application page, showing world-wide cell towers and a few widgets.
*/
import {
computed,
onMounted,
Expand Down Expand Up @@ -42,10 +46,6 @@ const RADIO_COLORS: AccessorFunction<unknown, Color> = colorCategories({
const filters = ref<Record<string, Filter>>({});
const onFiltersChange = (_filters: Record<string, Filter>) => {
filters.value = _filters;
};
const data = computed(() =>
vectorQuerySource({
accessToken: context.accessToken,
Expand All @@ -61,6 +61,7 @@ const data = computed(() =>
* Layers (https://deck.gl/docs/api-reference/carto/overview#carto-layers)
*/
// Layer visibility represented as name/visibility pairs, managed by the Layers component.
const layerVisibility = ref<Record<string, boolean>>({
'Cell towers': true,
});
Expand All @@ -69,6 +70,7 @@ const onLayerVisibilityChange = (visibility: Record<string, boolean>) => {
layerVisibility.value = visibility;
};
// Update layers when data or visualization parameters change.
const layers = computed(() => [
new VectorTileLayer({
id: 'Cell towers',
Expand Down Expand Up @@ -159,7 +161,7 @@ onUnmounted(() => {
:operation="'count'"
:viewState="viewStateDebounced as MapViewState"
:filters
:onFiltersChange
:onFiltersChange="(nextFilters) => void (filters = nextFilters)"
/>
</Card>
</aside>
Expand Down
2 changes: 2 additions & 0 deletions packages/create-vue/src/components/views/LoginView.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script setup lang="ts">
/** Login page, showing a button that redirects to login form. */
import { useAuth0 } from '@auth0/auth0-vue';
import { context } from '../../context';
const { loginWithRedirect } = useAuth0();
Expand Down
2 changes: 2 additions & 0 deletions packages/create-vue/src/components/views/LogoutView.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script setup lang="ts">
/** Logout page, which immediately logs out and redirects to login. */
import { useAuth0 } from '@auth0/auth0-vue';
const { logout } = useAuth0();
logout();
Expand Down
3 changes: 3 additions & 0 deletions packages/create-vue/src/components/views/NotFoundView.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
<script setup lang="ts">
/** Page representing a 'page not found' state. */
</script>
<template>
<h1>Not Found</h1>
</template>
18 changes: 13 additions & 5 deletions packages/create-vue/src/components/views/PopulationView.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<script setup lang="ts">
/**
* Example application page, showing U.S. population.
*/
import {
computed,
onMounted,
Expand Down Expand Up @@ -53,14 +57,12 @@ const data = computed(() =>
* Layers (https://deck.gl/docs/api-reference/carto/overview#carto-layers)
*/
// Layer visibility represented as name/visibility pairs, managed by the Layers component.
const layerVisibility = ref<Record<string, boolean>>({
'U.S. population': true,
});
const onLayerVisibilityChange = (visibility: Record<string, boolean>) => {
layerVisibility.value = visibility;
};
// Update layers when data or visualization parameters change.
const layers = computed(() => [
new H3TileLayer({
id: 'U.S. population',
Expand Down Expand Up @@ -142,7 +144,13 @@ onUnmounted(() => {
id="deck-canvas"
style="position: absolute; width: 100%; height: 100%"
></canvas>
<Layers :layers :layerVisibility :onLayerVisibilityChange />
<Layers
:layers
:layerVisibility
:onLayerVisibilityChange="
(nextLayerVisibility) => (layerVisibility = nextLayerVisibility)
"
/>
<Card title="Legend" class="legend">
<LegendEntryContinuous
title="U.S. population"
Expand Down
29 changes: 23 additions & 6 deletions packages/create-vue/src/components/widgets/CategoryWidget.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<script setup lang="ts">
/**
* Category widget, displaying one or more categories by name, with a horizontal 'meter'
* representing the value (typically count) of each category.
*/
import { computed, ref } from 'vue';
import { computedAsync } from '@vueuse/core';
import { MapViewState } from '@deck.gl/core';
Expand All @@ -21,11 +26,17 @@ import { useToggleFilter } from '../../hooks/useToggleFilter';
const props = withDefaults(
defineProps<{
/** Widget-compatible data source, from vectorTableSource, vectorQuerySource, etc. */
data: Promise<{ widgetSource: WidgetSource }>;
/** Column containing category names. */
column?: string;
/** Operation used to aggregate features in each category. */
operation?: AggregationType;
/** Map view state. If specified, widget will be filtered to the view. */
viewState?: MapViewState;
/** Filter state. If specified, widget will be filtered. */
filters?: Record<string, Filter>;
/** Callback, to be invoked by the widget when its filters are set or cleared. */
onFiltersChange?: (filters: Record<string, Filter>) => void;
}>(),
{
Expand Down Expand Up @@ -69,6 +80,7 @@ const response = computedAsync<CategoryResponse>(async (onCancel) => {
});
}, []);
// Compute min/max over category values.
const minMax = computed<[number, number]>(() => {
let min = Infinity;
let max = -Infinity;
Expand All @@ -79,6 +91,7 @@ const minMax = computed<[number, number]>(() => {
return [min, max];
});
// Set of selected (filtered) categories, for quick lookups while rendering.
const selectedCategories = computed(() => {
const filter =
props.filters &&
Expand All @@ -99,12 +112,16 @@ const toggleFilter = useToggleFilter({
function onClearFilters() {
if (props.filters && props.onFiltersChange) {
props.onFiltersChange({
...removeFilter(props.filters, {
column: props.column,
owner: owner.value,
}),
});
// Replace, not mutate, the filters object.
props.onFiltersChange(
removeFilter(
{ ...props.filters },
{
column: props.column,
owner: owner.value,
},
),
);
}
}
</script>
Expand Down
7 changes: 7 additions & 0 deletions packages/create-vue/src/components/widgets/FormulaWidget.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<script setup lang="ts">
/**
* Formula widget, displaying a prominent 'scorecard' number.
*/
import { ref } from 'vue';
import { computedAsync } from '@vueuse/core';
import { MapViewState } from '@deck.gl/core';
Expand All @@ -11,9 +14,13 @@ import {
const props = withDefaults(
defineProps<{
/** Widget-compatible data source, from vectorTableSource, vectorQuerySource, etc. */
data: Promise<{ widgetSource: WidgetSource }>;
/** Column containing a value to be aggregated. */
column?: string;
/** Operation used to aggregate the specified column. */
operation?: AggregationType;
/** Map view state. If specified, widget will be filtered to the view. */
viewState?: MapViewState;
}>(),
{
Expand Down
10 changes: 10 additions & 0 deletions packages/create-vue/src/hooks/useToggleFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ import {
} from '@carto/api-client';

export type ToggleFilterProps = {
/** Column containing category-like strings for filtering. */
column: string;
/** Owner / Widget ID. */
owner: string;
/** Filter state. */
filters?: Record<string, Filter>;
/** Callback to be invoked when changing filter state. */
onChange?: (filters: Record<string, Filter>) => void;
};

/**
* Returns a function, `toggleFilter(category)`, which can be used to toggle
* a specified category on/off in filter state for a specified column. For
* use in category- and pie-style widgets.
*/
export function useToggleFilter({
column,
owner,
Expand Down Expand Up @@ -49,6 +58,7 @@ export function useToggleFilter({
filters = removeFilter(filters, { column, owner });
}

// Replace, not mutate, the filters object.
onChange({ ...filters });
};
}
3 changes: 3 additions & 0 deletions packages/create-vue/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import LoginView from './components/views/LoginView.vue';
import LogoutView from './components/views/LogoutView.vue';
import NotFoundView from './components/views/NotFoundView.vue';

/** Available paths (URLs) in the application. */
export const RoutePath = {
CELL_TOWERS: '/',
POPULATION: '/usa-population',
Expand All @@ -16,6 +17,7 @@ export const RoutePath = {
NOT_FOUND: '/404',
};

/** Routes to be shown in the header navigation list. */
export const NAV_ROUTES: { text: string; path: string }[] = [
{
text: 'Cell towers',
Expand All @@ -27,6 +29,7 @@ export const NAV_ROUTES: { text: string; path: string }[] = [
},
];

/** Routes (pages) mapped to their corresponding Vue components. */
export const routes = [
{
path: RoutePath.CELL_TOWERS,
Expand Down

0 comments on commit 3bb3a0d

Please sign in to comment.