cities-generator-frontend
v1.2.8
Published
Portable hierarchical dropdown menu for cities-generator JSON data
Maintainers
Readme
layout: guide title: Cities Generator Frontend permalink: /frontend/
Cities Generator Frontend
A portable, hierarchical dropdown menu component for displaying cities data from cities-generator JSON files. The component is designed to work in any platform (WordPress, Drupal, Liferay, Joomla, etc.) with minimal integration effort.
Features
- 🌳 Hierarchical Navigation: Navigate through nested location data
- 📦 Highly Portable: Works as Web Component, React component, or vanilla JS
- 🎨 Customizable: Easy to style and integrate
- 📱 Responsive: Works on desktop and mobile
- ♿ Accessible: ARIA labels and keyboard navigation
Installation
Navigate to the frontend directory and install dependencies:
cd frontend
npm installDevelopment
Start the development server:
npm run devBuild
Build all variants (UMD, Standalone, Web Component):
npm run buildOr build specific variants:
npm run build:umd # UMD bundle (requires React external)
npm run build:standalone # Standalone bundle (includes React)
npm run build:webcomponent # Web Component ES moduleThe build outputs are in the dist/ directory:
cities-generator.umd.js- UMD bundle (~18KB, requires React external)cities-generator-standalone.iife.js- IIFE standalone bundle (~52KB, includes React, CSS, web component support, and process polyfill)style.css- Component styles (required for UMD bundle)
Usage
Method 1: Web Component (Most Portable - Recommended)
Works in any platform without framework dependencies. Simply include the scripts and use the custom HTML element:
<!-- Load IIFE standalone bundle (includes React, CSS, web component support, and process polyfill) -->
<script src="https://cdn.jsdelivr.net/gh/flashboss/cities-generator@master/frontend/dist/cities-generator-standalone.iife.js"></script>
<!-- Option 1: Use as Web Component -->
<!-- Using default parameters -->
<cities-dropdown />
<!-- Using default URL -->
<cities-dropdown
country="IT"
language="it"
placeholder="Select location...">
</cities-dropdown>
<!-- Using custom base URL (automatically appends /IT/it.json) -->
<cities-dropdown
country="IT"
language="it"
data-url="https://example.com/cities"
placeholder="Select location...">
</cities-dropdown>
<script>
document.querySelector('cities-dropdown').addEventListener('select', (e) => {
console.log('Selected:', e.detail);
// e.detail contains: { id, name, level, zones }
});
</script>
<!-- Option 2: Use via JavaScript API -->
<div id="my-dropdown"></div>
<script>
// Using default parameters
CitiesGenerator.render('#my-dropdown');
// Using default URL
CitiesGenerator.render('#my-dropdown', {
country: 'IT',
language: 'it',
placeholder: 'Select location...',
onSelect: (node) => {
console.log('Selected:', node);
}
});
// Using custom base URL (automatically appends /IT/it.json)
CitiesGenerator.render('#my-dropdown', {
country: 'IT',
language: 'it',
dataUrl: 'https://example.com/cities',
placeholder: 'Select location...',
onSelect: (node) => {
console.log('Selected:', node);
}
});
// Using search functionality
CitiesGenerator.render('#my-dropdown', {
country: 'IT',
language: 'it',
placeholder: 'Select location...',
enableSearch: true,
searchPlaceholder: 'Search location...',
onSelect: (node) => {
console.log('Selected:', node);
}
});
</script>Method 2: UMD Bundle (Requires React)
Use when you already have React loaded in your application. The UMD bundle is smaller and requires React and CSS to be loaded separately:
<!-- Polyfill for process (required for React) -->
<script>
if (typeof process === 'undefined') {
window.process = {
env: {
NODE_ENV: 'production'
}
};
}
</script>
<!-- Load React (if not already loaded) -->
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<!-- Load CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/flashboss/cities-generator@master/frontend/dist/style.css">
<!-- Load UMD bundle -->
<script src="https://cdn.jsdelivr.net/gh/flashboss/cities-generator@master/frontend/dist/cities-generator.umd.js"></script>
<!-- Option 1: Use as Web Component -->
<cities-dropdown
country="IT"
language="it"
placeholder="Select location...">
</cities-dropdown>
<script>
document.querySelector('cities-dropdown').addEventListener('select', (e) => {
console.log('Selected:', e.detail);
});
</script>
<!-- Option 2: Use via JavaScript API -->
<div id="my-dropdown"></div>
<script>
// Using default parameters
CitiesGenerator.render('#my-dropdown');
// Using default URL
CitiesGenerator.render('#my-dropdown', {
country: 'IT',
language: 'it',
placeholder: 'Select location...',
onSelect: (node) => {
console.log('Selected:', node);
}
});
// Using custom base URL (automatically appends /IT/it.json)
CitiesGenerator.render('#my-dropdown', {
country: 'IT',
language: 'it',
dataUrl: 'https://example.com/cities',
placeholder: 'Select location...',
onSelect: (node) => {
console.log('Selected:', node);
}
});
// Using search functionality
CitiesGenerator.render('#my-dropdown', {
country: 'IT',
language: 'it',
placeholder: 'Select location...',
enableSearch: true,
searchPlaceholder: 'Search location...',
onSelect: (node) => {
console.log('Selected:', node);
}
});
</script>Method 3: React Component
For React applications:
import { CitiesDropdown } from 'cities-generator-frontend';
function MyComponent() {
return (
<>
{/* Using default parameters */}
<CitiesDropdown />
{/* Using default URL */}
<CitiesDropdown
country="IT"
language="it"
placeholder="Select location..."
onSelect={(node) => console.log(node)}
/>
{/* Using custom base URL (automatically appends /IT/it.json) */}
<CitiesDropdown
country="IT"
language="it"
dataUrl="https://example.com/cities"
placeholder="Select location..."
onSelect={(node) => console.log(node)}
/>
{/* Using search functionality */}
<CitiesDropdown
country="IT"
language="it"
placeholder="Select location..."
enableSearch={true}
searchPlaceholder="Search location..."
onSelect={(node) => console.log(node)}
/>
</>
);
}Method 4: Angular Component
Angular supports Web Components natively. You can use the <cities-dropdown> custom element directly in your Angular templates:
Option 1: Direct usage (Recommended)
// location.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-location',
template: `
<cities-dropdown
[attr.country]="country"
[attr.language]="language"
[attr.placeholder]="placeholder"
(select)="onLocationSelect($event)">
</cities-dropdown>
`
})
export class LocationComponent {
country = 'IT';
language = 'it';
placeholder = 'Select location...';
onLocationSelect(event: CustomEvent) {
console.log('Selected:', event.detail);
// event.detail contains: { id, name, level, zones }
}
}Option 2: Wrapper component (for better TypeScript integration)
// cities-dropdown.component.ts
import { Component, Input, Output, EventEmitter, ElementRef, AfterViewInit, OnChanges, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-cities-dropdown',
template: '<cities-dropdown></cities-dropdown>'
})
export class CitiesDropdownComponent implements AfterViewInit, OnChanges {
@Input() country = 'IT';
@Input() language = 'it';
@Input() placeholder = 'Select location...';
@Input() dataUrl?: string;
@Input() enableSearch = false;
@Input() searchPlaceholder = 'Search location...';
@Input() model = 0;
@Output() select = new EventEmitter<any>();
private element: HTMLElement | null = null;
constructor(private el: ElementRef) {}
ngAfterViewInit() {
this.element = this.el.nativeElement.querySelector('cities-dropdown');
this.updateAttributes();
this.setupEventListener();
}
ngOnChanges(changes: SimpleChanges) {
if (this.element) {
this.updateAttributes();
}
}
private updateAttributes() {
if (!this.element) return;
this.element.setAttribute('country', this.country);
this.element.setAttribute('language', this.language);
this.element.setAttribute('placeholder', this.placeholder);
if (this.dataUrl) {
this.element.setAttribute('data-url', this.dataUrl);
}
if (this.enableSearch) {
this.element.setAttribute('enable-search', 'true');
this.element.setAttribute('search-placeholder', this.searchPlaceholder);
}
if (this.model !== 0) {
this.element.setAttribute('model', this.model.toString());
}
}
private setupEventListener() {
if (!this.element) return;
this.element.addEventListener('select', (e: Event) => {
const customEvent = e as CustomEvent;
this.select.emit(customEvent.detail);
});
}
}Usage:
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-cities-dropdown
[country]="'IT'"
[language]="'it'"
[placeholder]="'Select location...'"
[enableSearch]="true"
(select)="onLocationSelect($event)">
</app-cities-dropdown>
`
})
export class AppComponent {
onLocationSelect(location: any) {
console.log('Selected:', location);
}
}Note: Make sure to load the bundle in your index.html:
<!-- In index.html -->
<script src="https://cdn.jsdelivr.net/gh/flashboss/cities-generator@master/frontend/dist/cities-generator-standalone.iife.js"></script>Component Props
data(Nodes, optional): Direct data object (optional)onSelect(function, optional): Callback when a leaf node is selectedclassName(string, optional): Additional CSS classesdataUrl(string, optional): Base URL for remote data source. If not specified, uses default GitHub URL: https://raw.githubusercontent.com/flashboss/cities-generator/master/_db/EU/{country}/{language}.json. If specified, treated as base URL and automatically appends/{country}/{language}.json(any.jsonextension in the URL is automatically removed)country(string, optional): Country code, e.g., "IT", "GB" (default: "IT")language(string, optional): Language code, e.g., "it", "en", "fr", "de", "es", "pt". Supported languages: IT (Italian, default), EN (English), FR (French), DE (German), ES (Spanish), PT (Portuguese). In Java API, you can useLanguagesenum:Languages.IT,Languages.EN, etc.placeholder(string, optional): Placeholder text (default: "Select location...")username(string, optional): Username for HTTP Basic Authenticationpassword(string, optional): Password for HTTP Basic AuthenticationenableSearch(boolean, optional): Enable text search functionality (default: false)searchPlaceholder(string, optional): Placeholder text for the search input field (default: "Search location...")
Events
When using the Web Component, listen for the select event:
document.querySelector('cities-dropdown').addEventListener('select', (e) => {
const node = e.detail; // { id, name, level, zones }
console.log('Selected:', node.name, 'ID:', node.id);
});Platform Integration Examples
WordPress
1. Upload files to theme:
- Upload
cities-generator.umd.jstowp-content/themes/your-theme/js/ - Upload JSON files to
wp-content/themes/your-theme/data/
2. Enqueue scripts in functions.php:
function enqueue_cities_dropdown() {
wp_enqueue_script('react', 'https://unpkg.com/react@18/umd/react.production.min.js', [], '18.2.0', true);
wp_enqueue_script('react-dom', 'https://unpkg.com/react-dom@18/umd/react-dom.production.min.js', ['react'], '18.2.0', true);
wp_enqueue_script('cities-generator', get_template_directory_uri() . '/js/cities-generator.umd.js', ['react', 'react-dom'], '1.0.0', true);
wp_enqueue_style('cities-generator', get_template_directory_uri() . '/js/style.css', [], '1.0.0');
}
add_action('wp_enqueue_scripts', 'enqueue_cities_dropdown');3. Use in template:
<!-- Using default parameters -->
<cities-dropdown />
<!-- Using default URL -->
<cities-dropdown country="IT" language="it"></cities-dropdown>
<!-- Using custom base URL (automatically appends /IT/it.json) -->
<cities-dropdown
country="IT"
language="it"
data-url="<?php echo get_template_directory_uri(); ?>/data">
</cities-dropdown>Drupal
1. Create module or add to theme:
In your_theme.libraries.yml:
cities_dropdown:
js:
https://unpkg.com/react@18/umd/react.production.min.js: { type: external, minified: true }
https://unpkg.com/react-dom@18/umd/react-dom.production.min.js: { type: external, minified: true, dependencies: [react] }
js/cities-generator.umd.js: {}
css:
theme:
js/style.css: {}
dependencies:
- core/drupal2. Attach to template:
{{ attach_library('your_theme/cities_dropdown') }}
{# Using default parameters #}
<cities-dropdown />
{# Using default URL #}
<cities-dropdown country="IT" language="it"></cities-dropdown>
{# Using custom base URL (automatically appends /IT/it.json) #}
<cities-dropdown country="IT" language="it" data-url="/sites/default/files/cities"></cities-dropdown>Liferay
1. Add to module or theme:
In liferay-plugin-package.properties:
js.fast.load=true2. Include in JSP:
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="<%= themeDisplay.getCDNBaseURL() %>/o/cities-generator/js/cities-generator.umd.js"></script>
<link rel="stylesheet" href="<%= themeDisplay.getCDNBaseURL() %>/o/cities-generator/js/style.css">
<%-- Using default parameters --%>
<cities-dropdown />
<%-- Using default URL --%>
<cities-dropdown country="IT" language="it"></cities-dropdown>
<%-- Using custom base URL (automatically appends /IT/it.json) --%>
<cities-dropdown country="IT" language="it" data-url="<%= themeDisplay.getCDNBaseURL() %>/o/cities-generator/data"></cities-dropdown>Joomla
1. Add to template:
In index.php:
$document = JFactory::getDocument();
$document->addScript('https://unpkg.com/react@18/umd/react.production.min.js');
$document->addScript('https://unpkg.com/react-dom@18/umd/react-dom.production.min.js');
$document->addScript(JURI::root() . 'templates/your-template/js/cities-generator.umd.js');
$document->addStyleSheet(JURI::root() . 'templates/your-template/js/style.css');2. Use in template:
<!-- Using default parameters -->
<cities-dropdown />
<!-- Using default URL -->
<cities-dropdown country="IT" language="it"></cities-dropdown>
<!-- Using custom base URL (automatically appends /IT/it.json) -->
<cities-dropdown country="IT" language="it" data-url="<?php echo JURI::root(); ?>data"></cities-dropdown>Data Format
The component expects JSON in this format (matching the cities-generator output):
{
"zones": [
{
"id": "1",
"name": "I: ITALIA NORD-OCCIDENTALE",
"level": 0,
"zones": [
{
"id": "1-12345",
"name": "Piemonte",
"level": 1,
"zones": [
{
"id": "1-12345-67890",
"name": "Torino",
"level": 2,
"zones": []
}
]
}
]
}
]
}Styling
The component uses CSS classes prefixed with cities-dropdown-. You can override styles:
.cities-dropdown-trigger {
border-color: #your-color;
border-radius: 8px;
}
.cities-dropdown-item:hover {
background-color: #your-hover-color;
}NPM Package
This package is also available on npm:
npm install cities-generator-frontendSee the npm package page for more information.
