Skip to content

Commit

Permalink
Location input (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
DanWebb authored Feb 7, 2024
1 parent 1c1c0d6 commit 3922775
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
# The public path for asset loading, this needs to be a publically accessible URL on the
# production version
VITE_PUBLIC_PATH=/

# The public Rest API key for the Here Maps API
VITE_HERE_MAPS_PLACES_KEY=
25 changes: 25 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
},
"dependencies": {
"@etchteam/diamond-ui": "^1.2.0",
"@preact/signals": "^1.2.2",
"i18next": "^23.7.18",
"i18next-http-backend": "^2.4.2",
"preact": "^10.19.3",
Expand Down
18 changes: 18 additions & 0 deletions src/components/LocationInput/LocationInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { StoryObj } from '@storybook/preact';

import LocationInputComponent from './LocationInput';

const meta = {
component: LocationInputComponent,
};

export default meta;

export const LocationInput: StoryObj = {
render: () => (
<>
<label htmlFor="custom-location-input">Where are you?</label>
<locator-location-input inputId="custom-location-input"></locator-location-input>
</>
),
};
89 changes: 89 additions & 0 deletions src/components/LocationInput/LocationInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Signal, signal } from '@preact/signals';
import { Component } from 'preact';
import register from 'preact-custom-element';

import config from '../../config';

interface HereMapsAutosuggestResult {
items: {
title: string;
}[];
}

interface LocationInputProps {
readonly inputId?: string;
}

/**
* An autosuggest input for locations.
* The autosuggest list will appear after > 3 characters are entered.
*/
export default class LocationInput extends Component<LocationInputProps> {
apiKey = config.mapsPlacesKey;
autosuggestEndpoint = 'https://autosuggest.search.hereapi.com/v1/autosuggest';
boundingBox = '-7.57216793459,49.959999905,1.68153079591,58.6350001085';
resultTypes = 'address,place';
locationSuggestions: Signal<string[]>;

constructor(props: LocationInputProps) {
super(props);
this.locationSuggestions = signal<string[]>([]);
}

autosuggest = async (query: string): Promise<HereMapsAutosuggestResult> => {
const apiKey = `apiKey=${this.apiKey}`;
const bbox = `in=bbox:${this.boundingBox}`;
const resultTypes = `result_types=${this.resultTypes}`;

const response = await fetch(
`${this.autosuggestEndpoint}?q=${query}&${apiKey}&${bbox}&${resultTypes}`,
);
return response.json();
};

handleInput = async (event: preact.JSX.TargetedEvent<HTMLInputElement>) => {
const query = event.currentTarget.value;

if (query.length <= 3) {
return;
}

const result = await this.autosuggest(query);
const locations = result.items.map((item) => item.title);
this.locationSuggestions.value = locations;
};

render() {
const locations = this.locationSuggestions.value;
const inputId = this.props.inputId ?? 'location-input';
const listId = `${inputId}-locations`;

return (
<>
<input
type="text"
id={inputId}
list={listId}
onInput={this.handleInput}
/>
<datalist id={listId}>
{locations.map((location) => (
<option value={location} key={location}>
{location}
</option>
))}
</datalist>
</>
);
}
}

register(LocationInput, 'locator-location-input', ['inputId']);

declare module 'preact' {
namespace JSX {
interface IntrinsicElements {
'locator-location-input': LocationInputProps & preact.JSX.HTMLAttributes;
}
}
}
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const PUBLIC_PATH = import.meta.env.VITE_PUBLIC_PATH ?? '/';
const config = {
publicPath: PUBLIC_PATH,
imagePath: `${PUBLIC_PATH}images/`,
mapsPlacesKey: import.meta.env.VITE_HERE_MAPS_PLACES_KEY,
};

export default config;
5 changes: 3 additions & 2 deletions src/pages/Start.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
import '../components/Layout/Layout';
import '../components/Logo/Logo';
import '../components/Tip/Tip';
import '../components/LocationInput/LocationInput';

export default function StartPage() {
const { t } = useTranslation();
Expand All @@ -13,8 +14,8 @@ export default function StartPage() {
<div slot="main">
<h2>{t('start.title')}</h2>
<form>
<label htmlFor="location">Where are you?</label>
<input type="text" name="location" id="location" />
<label htmlFor="location-input">Where are you?</label>
<locator-location-input></locator-location-input>
<button type="submit">Get started</button>
</form>
</div>
Expand Down

0 comments on commit 3922775

Please sign in to comment.