Skip to content

Commit

Permalink
Multiple level dropdowns (#194)
Browse files Browse the repository at this point in the history
  • Loading branch information
aparlato authored Jul 23, 2024
1 parent 783749a commit 14bc271
Show file tree
Hide file tree
Showing 15 changed files with 463 additions and 82 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Unreleased

- Add multi-level dropdowns

## 0.15.1

- Update Maplibre GL to `v3.6.2`
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ Here, you can customize the following options:
- `stylePresets`: A list of styles with urls to show in the dropdowns. Styles must have the following keys:
- `id`: a unique id
- `name`: a display name
- `type`: the type of map (`mapbox-gl`, `maplibre-gl`, `google`, `leaflet`)
- `type`: the type of map or referencing a sublist of presets (`mapbox-gl`, `maplibre-gl`, `google`, `leaflet`, `sublist`)
- `url`: (currently applies to `mapbox-gl`, `maplibre-gl`, and `leaflet` maps only) the style's url
- `mapId`: (currently `google` only) the style's id
- [`presets`]: used only with `type: sublist`, this allows one level of nesting to add an additional nested list of style presets following this format
- `branchPatterns`: An array of objects that specify how to build a URL to fetch a style living on a branch with the following keys:
- `pattern`: a tokenized url pattern using `{branch}` and `{style}` tokens
- `styles`: an array specifying specific styles you can view on the specified branch
Expand Down
8 changes: 6 additions & 2 deletions dist/bundle.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions dist/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/bundle.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"sirv-cli": "^1.0.0",
"stamen-attribution": "github:stamen/stamen-attribution#v0.1.0",
"svelte-fa": "^2.4.0",
"sveltestrap": "^5.11.3",
"tangram": "^0.21.1"
},
"main": "dist/bundle.js"
Expand Down
28 changes: 18 additions & 10 deletions public/config/local.example.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,25 @@ const stylePresets = [
type: 'mapbox-gl',
url: 'mapbox://styles/mapbox/streets-v11',
},
// sublist is used to nest style options in the dropdown
// this is helpful if you need to organize a large amount of styles
{
id: 'mapbox-light',
name: 'Mapbox Light',
type: 'mapbox-gl',
url: 'mapbox://styles/mapbox/light-v10',
},
{
id: 'mapbox-dark',
name: 'Mapbox Dark',
type: 'mapbox-gl',
url: 'mapbox://styles/mapbox/dark-v10',
name: 'Data viz styles',
type: 'sublist',
presets: [
{
id: 'mapbox-light',
name: 'Mapbox Light',
type: 'mapbox-gl',
url: 'mapbox://styles/mapbox/light-v10',
},
{
id: 'mapbox-dark',
name: 'Mapbox Dark',
type: 'mapbox-gl',
url: 'mapbox://styles/mapbox/dark-v10',
},
],
},
{
id: 'openstreetmap',
Expand Down
1 change: 0 additions & 1 deletion src/components/MapLabel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
flex-direction: column;
align-items: left;
justify-content: center;
margin-right: 1em;
}
.location-control {
Expand Down
36 changes: 24 additions & 12 deletions src/components/MapLocationDropdown.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script>
import { createEventDispatcher } from 'svelte';
import { config as configStore } from '../stores';
import SimpleDropdown from './inputs/SimpleDropdown.svelte';
export let bearing;
export let center;
Expand Down Expand Up @@ -77,21 +78,32 @@
$: checkSelectedValue({ zoom, center, pitch, bearing });
$: handleChangeLocation(selected);
$: selectionOptions = Object.entries(gazetteer).reduce(
(acc, [locationHeader, locations]) => {
acc.push({ header: locationHeader });
for (const location of locations) {
let [label, _] = Object.entries(location)[0];
acc.push({ label, value: JSON.stringify(location) });
}
return acc;
},
[]
);
$: onSelect = v => {
selected = v;
};
</script>
{#if gazetteer}
<select id="locations" bind:value={selected}>
{#each Object.keys(gazetteer) as locationHeader}
<optgroup label={locationHeader}>
<option value="" disabled selected hidden>Go to...</option>
{#each gazetteer[locationHeader] as location}
<option value={JSON.stringify(location)}
>{Object.keys(location)[0]}</option
>
{/each}
</optgroup>
{/each}
</select>
<SimpleDropdown
placeholder={'Go to...'}
options={selectionOptions}
value={selected}
onClick={onSelect}
direction="down"
/>
{/if}
<style>
Expand Down
42 changes: 16 additions & 26 deletions src/components/MapStyleInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import { createBranchUrl } from '../branch-utils';
import { shortcut } from '../shortcut';
import { fetchUrl } from '../fetch-url';
import StylesDropdown from './inputs/StylesDropdown.svelte';
import SimpleDropdown from './inputs/SimpleDropdown.svelte';
const dispatch = createEventDispatcher();
export let dropdownDisplayOptions;
export let dropdownValue;
export let rendererOptions;
export let rendererValue;
export let activeUrl;
export let index;
let selected;
Expand Down Expand Up @@ -192,23 +195,12 @@
</script>
<div class="map-style-input">
<select
id="styles"
on:change={e => dispatch('selectOption', { dropdownId: e.target.value })}
>
{#each Object.keys(dropdownDisplayOptions) as group}
<optgroup value={group} label={group}>
{#each dropdownDisplayOptions[group] as value}
<option
value={value.dropdownId}
selected={dropdownValue.dropdownId === value.dropdownId}
>
{value.text}
</option>
{/each}
</optgroup>
{/each}
</select>
<StylesDropdown
{dropdownDisplayOptions}
{dropdownValue}
{index}
onSelect={v => dispatch('selectOption', { dropdownId: v })}
/>
{#if selected?.dropdownType === 'custom' || selected?.dropdownType === 'branch'}
<div class="custom-input">
Expand Down Expand Up @@ -236,15 +228,12 @@
<div class="renderer-control">
<span class="nowrap">Rendered with</span>
<select
on:change={e => dispatch('selectRenderer', { value: e.target.value })}
>
{#each rendererOptions as option}
<option value={option.value} selected={option.value === rendererValue}>
{option.name}
</option>
{/each}
</select>
<SimpleDropdown
options={rendererOptions.map(v => ({ label: v.name, value: v.value }))}
value={rendererValue}
onClick={v => dispatch('selectRenderer', { value: v })}
direction="up"
/>
</div>
</div>
Expand All @@ -254,6 +243,7 @@
display: flex;
flex-direction: column;
gap: 0.25rem;
max-width: 240px;
}
.custom-input {
Expand Down
103 changes: 84 additions & 19 deletions src/components/MapStyleInputWrapper.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,21 @@
// Selects the appropriate value from dropdownValues and adds any necessary key
const setSelectedValue = () => {
let nextValue = dropdownValues.find(item => !!item.selected);
let nextValue;
for (const dropdownValue of dropdownValues) {
if (!!dropdownValue?.selected) {
nextValue = dropdownValue;
}
if (dropdownValue?.type === 'sublist') {
for (const dv of dropdownValue?.presets) {
if (!!dv?.selected) {
nextValue = dv;
}
}
}
}
if (!nextValue) return;
// Don't mutate the dropdown values
Expand Down Expand Up @@ -105,19 +119,43 @@
dropdownType: 'preset',
selected: url === item?.url && type === item?.type,
dropdownId: hat(),
...(item.type === 'sublist' && {
presets: item.presets.map(v => ({
...v,
dropdownType: 'preset',
selected: url === v?.url && type === v?.type,
dropdownId: hat(),
})),
}),
}));
dropdownValues = dropdownValues.concat(stylePresetValues);
dropdownDisplayOptions['Presets'] = stylePresetValues.map(item => ({
text: item.name,
dropdownId: item.dropdownId,
}));
dropdownDisplayOptions['Presets'] = stylePresetValues.map(item => {
return {
text: item.name,
...(item.type !== 'sublist' && { dropdownId: item.dropdownId }),
...(item.type === 'sublist' && {
type: 'sublist',
presets: item.presets.map(v => ({
text: v.name,
dropdownId: v.dropdownId,
})),
}),
};
});
}
// Create values and displays for branch options
if (branchPatterns) {
let patterns = [];
for (const pattern of branchPatterns) {
let preset = {
name: pattern.name ?? pattern.id,
type: 'sublist',
dropdownId: hat(),
presets: [],
};
if (pattern?.styles?.length) {
const branchValues = pattern?.styles.map(s => {
return {
Expand All @@ -135,20 +173,33 @@
};
});
dropdownValues = dropdownValues.concat(branchValues);
preset.presets = preset.presets.concat(branchValues);
}
patterns.push(preset);
dropdownDisplayOptions[
`Styles on a branch${pattern.name ? `: ${pattern.name}` : ''}`
] = branchValues.map(item => ({
dropdownValues = dropdownValues.concat(patterns);
dropdownDisplayOptions[`Styles on a branch`] = patterns.map(item => {
return {
text: item.name,
dropdownId: item.dropdownId,
}));
}
...(item.type !== 'sublist' && { dropdownId: item.dropdownId }),
...(item.type === 'sublist' && {
type: 'sublist',
presets: item.presets.map(v => ({
text: v.name,
dropdownId: v.dropdownId,
})),
}),
};
});
}
}
// If no option has matched the URL, then it is custom
const hasSelectedOption = dropdownValues.some(item => !!item.selected);
const hasSelectedOption = dropdownValues.some(
item => !!item?.selected || item?.presets?.some(v => !!v.selected)
);
// Create a custom value and display option
const customValues = [
Expand Down Expand Up @@ -190,12 +241,25 @@
};
const onSelectOption = e => {
// TODO needs to handle nesting
const { dropdownId } = e.detail;
// Set selected property on value
dropdownValues = dropdownValues.map(v => ({
...v,
selected: v.dropdownId === dropdownId,
}));
dropdownValues = dropdownValues.reduce((acc, v) => {
let next = v;
if (next?.type === 'sublist') {
next.presets = next.presets.map(v => ({
...v,
selected: v.dropdownId === dropdownId,
}));
} else {
next = {
...next,
selected: v.dropdownId === dropdownId,
};
}
acc.push(next);
return acc;
}, []);
setSelectedValue();
setRendererOptions();
Expand Down Expand Up @@ -258,6 +322,7 @@
{dropdownDisplayOptions}
{rendererOptions}
{rendererValue}
{index}
activeUrl={url}
on:setUrl={onSetUrl}
on:selectOption={onSelectOption}
Expand Down
Loading

0 comments on commit 14bc271

Please sign in to comment.