GL Search
Version: 1.0.14
Package:@gift-card-market/gl-search
Last Updated: January 16, 2026
A reusable React search component with location input and Google Maps integration for Gift Card Market.
Features
- 🔍 Search with Location - Integrated Google Maps location autocomplete
- 🎨 Customizable UI - Configure banner colors, texts, and search button styling
- ⚡ Debounced Search - Configurable debounce time for better performance
- 🎯 Custom Events - Dispatch search and clear events for easy integration
- 📱 Responsive Design - Works seamlessly on all devices
- 🌐 Vanilla JS Support - Use as Web Component without React
- 💪 TypeScript - Full type definitions included
Table of Contents
- Installation
- Integration
- API Reference
- Custom Events
- Event Handling
- Complete Examples
- TypeScript Support
- Browser Support
- License
- Support
- Contributing
- Changelog
Installation
Using npm
npm install @gift-card-market/gl-search
Using yarn
yarn add @gift-card-market/gl-search
Integration
React Integration
1. Import the Component and Styles
import { GlSearch } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';
2. Basic Usage
import React from 'react';
import { GlSearch } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';
function App() {
return (
<div>
<GlSearch
environment="development"
jwt="your-jwt-token-here"
/>
</div>
);
}
export default App;
3. Advanced Usage with All Options
import React, { useRef } from 'react';
import { GlSearch } from '@gift-card-market/gl-search';
import type { GlSearchBarHandle } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';
function App() {
const searchRef = useRef<GlSearchBarHandle>(null);
// Configuration data
const searchData = {
banner_color: 'darkblue',
banner_text: [
`Search for your <strong>favorite</strong> restaurants! 🍔`,
`Find the perfect gift card 🎁`,
],
search_text: [
{
content: `(thinking <u>top burger joints</u>?)`,
key: 'burgers',
},
{
content: `(thinking <u>best pizza places</u>?)`,
key: 'pizza',
},
],
search_term: '',
search_button_color: {
text: 'yellow',
background: 'green',
},
};
return (
<div>
<GlSearch
ref={searchRef}
environment="development"
jwt="your-jwt-token-here"
google_api_key="your-google-api-key"
data={searchData}
debounce_ms={250}
query="Location=New York&SearchTerm=pizza"
/>
</div>
);
}
export default App;
Vanilla JS Integration
1. Include Scripts and Styles
After publishing, include the bundled JavaScript and CSS files from the dist/browser folder:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Vanilla JS Demo</title>
<!-- Include the bundled JS and CSS from published package -->
<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>
<link rel="stylesheet" href="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.css">
</head>
<body>
<div id="search-container"></div>
<script>
// Your initialization code here
</script>
</body>
</html>
2. Basic Usage
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Vanilla JS Demo</title>
<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>
<link rel="stylesheet" href="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.css">
</head>
<body>
<div id="search-container"></div>
<script>
// Initialize the web component
function initializeComponent() {
const container = document.getElementById('search-container');
// Create the custom element
const searchElement = document.createElement('gl-search');
// Set required attributes
searchElement.setAttribute('environment', 'development');
searchElement.setAttribute('jwt', 'your-jwt-token-here');
// Append to container
container.appendChild(searchElement);
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeComponent);
} else {
initializeComponent();
}
</script>
</body>
</html>
3. Advanced Usage with All Options
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Vanilla JS Advanced Demo</title>
<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>
<link rel="stylesheet" href="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.css">
</head>
<body>
<div id="search-container"></div>
<script>
// Configuration data
const searchData = {
banner_color: 'darkblue',
banner_text: [
`Search for your <strong>favorite</strong> restaurants! 🍔`,
`Find the perfect gift card 🎁`,
],
search_text: [
{
content: `(thinking <u>top burger joints</u>?)`,
key: 'burgers',
},
{
content: `(thinking <u>best pizza places</u>?)`,
key: 'pizza',
},
],
search_term: '',
search_button_color: {
text: 'yellow',
background: 'green',
},
};
// Store reference to the web component
let searchElement = null;
// Initialize the web component
function initializeComponent() {
const container = document.getElementById('search-container');
// Create the custom element
searchElement = document.createElement('gl-search');
// Set required attributes
searchElement.setAttribute('environment', 'development');
searchElement.setAttribute('jwt', 'your-jwt-token-here');
// Set optional attributes
searchElement.setAttribute('google_api_key', 'your-google-api-key');
searchElement.setAttribute('debounce_ms', '250');
searchElement.setAttribute('query', 'Location=New York&SearchTerm=pizza');
// Set data as JSON string
searchElement.setAttribute('data', JSON.stringify(searchData));
// Append to container
container.appendChild(searchElement);
console.log('Web Component initialized successfully');
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeComponent);
} else {
initializeComponent();
}
</script>
</body>
</html>
API Reference
Props / Attributes
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
environment | string | ✅ Yes | - | Environment mode: 'development', 'qa', 'staging' or 'production' |
jwt | string | ✅ Yes | - | JWT authentication token |
google_api_key | string | No | - | Google Maps API key for location autocomplete |
debounce_ms | number | No | 250 | Debounce time in milliseconds for search input |
query | string | No | - | Initial search query (format: 'Location=...&SearchTerm=...') |
data | object | No | {} | Configuration object (see below) |
Data Object Structure
{
banner_color?: string; // Banner background color
banner_text?: string[]; // Array of rotating banner texts (supports HTML)
search_text?: Array<{ // Array of search placeholder suggestions
content: string; // Placeholder text (supports HTML)
key: string; // Search term key
}>;
search_term?: string; // Initial search term
search_button_color?: { // Search button styling
text: string; // Text color
background: string; // Background color
};
}
Ref Methods (React Only)
When using React, you can access component methods via ref:
interface GlSearchBarHandle {
focus: () => void; // Focus on the location input field
setQuery: (query: string) => void; // Set search query programmatically
}
Example Usage
import React, { useRef } from 'react';
import { GlSearch } from '@gift-card-market/gl-search';
import type { GlSearchBarHandle } from '@gift-card-market/gl-search';
function App() {
const searchRef = useRef<GlSearchBarHandle>(null);
const handleFocus = () => {
searchRef.current?.focus();
};
const handleSetQuery = () => {
searchRef.current?.setQuery('Location=New York&SearchTerm=pizza');
};
return (
<div>
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleSetQuery}>Set Query</button>
<GlSearch
ref={searchRef}
environment="development"
jwt="your-jwt-token"
/>
</div>
);
}
Vanilla JS Methods
In Vanilla JS, you can call methods directly on the DOM element:
const searchElement = document.querySelector('gl-search');
// Focus on input
searchElement.focus();
// Set query
searchElement.setQuery('Location=New York&SearchTerm=pizza');
Custom Events
The component dispatches custom events that you can listen to for search interactions.
Available Events
| Event Name | Description | Event Detail |
|---|---|---|
gl:search | Dispatched when a search is performed | { query: string } |
gl:clear | Dispatched when the search is cleared | {} |
Event Handling
React Event Handling
import React, { useEffect } from 'react';
import { GlSearch, SearchBarEvents } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';
function App() {
useEffect(() => {
// Listen to search event
const handleSearch = (e: CustomEvent) => {
console.log('Search performed:', e.detail);
// e.detail.query contains: "Location=...&SearchTerm=...&SortBy=...&ProviderId=..."
// Parse query string
const params = new URLSearchParams(e.detail.query);
const location = params.get('Location');
const searchTerm = params.get('SearchTerm');
console.log('Location:', location);
console.log('Search Term:', searchTerm);
// Perform your search logic here
// e.g., fetch data from API, update state, etc.
};
// Listen to clear event
const handleClear = (e: CustomEvent) => {
console.log('Search cleared:', e.detail);
// Reset your search results, state, etc.
};
// Add event listeners
document.addEventListener(SearchBarEvents.SEARCH, handleSearch as EventListener);
document.addEventListener(SearchBarEvents.CLEAR, handleClear as EventListener);
// Cleanup
return () => {
document.removeEventListener(SearchBarEvents.SEARCH, handleSearch as EventListener);
document.removeEventListener(SearchBarEvents.CLEAR, handleClear as EventListener);
};
}, []);
return (
<div>
<GlSearch
environment="development"
jwt="your-jwt-token"
/>
</div>
);
}
export default App;
Vanilla JS Event Handling
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Event Handling</title>
<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>
<link rel="stylesheet" href="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.css">
</head>
<body>
<div id="search-container"></div>
<script>
// Initialize the component
function initializeComponent() {
const container = document.getElementById('search-container');
const searchElement = document.createElement('gl-search');
searchElement.setAttribute('environment', 'development');
searchElement.setAttribute('jwt', 'your-jwt-token');
container.appendChild(searchElement);
}
// Listen to search event
document.addEventListener('gl:search', function(e) {
console.log('Search performed:', e.detail);
// Parse query string
const params = new URLSearchParams(e.detail.query);
const location = params.get('Location');
const searchTerm = params.get('SearchTerm');
console.log('Location:', location);
console.log('Search Term:', searchTerm);
// Perform your search logic here
// e.g., fetch data from API, update UI, etc.
});
// Listen to clear event
document.addEventListener('gl:clear', function(e) {
console.log('Search cleared:', e.detail);
// Reset your search results, UI, etc.
});
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeComponent);
} else {
initializeComponent();
}
</script>
</body>
</html>
Complete Examples
React Complete Example
import React, { useRef, useEffect, useState } from 'react';
import { GlSearch, SearchBarEvents } from '@gift-card-market/gl-search';
import type { GlSearchBarHandle } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';
function App() {
const searchRef = useRef<GlSearchBarHandle>(null);
const [searchResults, setSearchResults] = useState([]);
const [loading, setLoading] = useState(false);
// Configuration
const searchData = {
banner_color: 'darkblue',
banner_text: [
`Do they love burgers? 🍔 Search for their <strong style="color: #ffe606">favorite</strong> below.`,
`You've got <strong style="color: #ffe606">great taste</strong>. We've got gift cards to match.`,
],
search_text: [
{
content: `(thinking <u>top smash burger joints</u>?)`,
key: 'smash burgers',
},
{
content: `(thinking <u>best lunch spots near me</u>?)`,
key: 'best lunch spots',
},
],
search_term: '',
search_button_color: {
text: 'yellow',
background: 'green',
},
};
useEffect(() => {
// Handle search event
const handleSearch = async (e: CustomEvent) => {
console.log('Search event:', e.detail);
setLoading(true);
try {
// Parse query parameters
const params = new URLSearchParams(e.detail.query);
const location = params.get('Location');
const searchTerm = params.get('SearchTerm');
// Call your API
const response = await fetch(`/api/search?location=${location}&term=${searchTerm}`);
const data = await response.json();
setSearchResults(data.results);
} catch (error) {
console.error('Search error:', error);
} finally {
setLoading(false);
}
};
// Handle clear event
const handleClear = (e: CustomEvent) => {
console.log('Clear event:', e.detail);
setSearchResults([]);
};
// Add listeners
document.addEventListener(SearchBarEvents.SEARCH, handleSearch as EventListener);
document.addEventListener(SearchBarEvents.CLEAR, handleClear as EventListener);
// Cleanup
return () => {
document.removeEventListener(SearchBarEvents.SEARCH, handleSearch as EventListener);
document.removeEventListener(SearchBarEvents.CLEAR, handleClear as EventListener);
};
}, []);
// Handler functions for buttons
const handleFocus = () => {
searchRef.current?.focus();
};
const handleSetQuery = () => {
searchRef.current?.setQuery('Location=New York&SearchTerm=pizza');
};
return (
<div className="app">
{/* Control buttons */}
<div style={{ padding: '20px', background: '#f5f5f5' }}>
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleSetQuery}>Set Query (NY/Pizza)</button>
</div>
{/* Search component */}
<GlSearch
ref={searchRef}
environment="development"
jwt="your-jwt-token"
google_api_key="your-google-api-key"
data={searchData}
debounce_ms={250}
/>
{/* Results */}
{loading && <div>Loading...</div>}
{searchResults.length > 0 && (
<div className="results">
{searchResults.map((result, index) => (
<div key={index}>{/* Render your results */}</div>
))}
</div>
)}
</div>
);
}
export default App;
Vanilla JS Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Complete Vanilla JS Example</title>
<!-- Include scripts and styles from published package -->
<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>
<link rel="stylesheet" href="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.css">
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
}
.control-panel {
padding: 20px;
background: #f5f5f5;
border-bottom: 2px solid #ddd;
}
.control-panel button {
margin-right: 10px;
padding: 10px 20px;
background: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.results {
padding: 20px;
}
.loading {
text-align: center;
padding: 20px;
font-size: 18px;
}
</style>
</head>
<body>
<!-- Control Panel -->
<div class="control-panel">
<button onclick="handleFocus()">🎯 Focus Input</button>
<button onclick="handleSetQuery()">📝 Set Query (NY/Pizza)</button>
</div>
<!-- Search Container -->
<div id="search-container"></div>
<!-- Results Container -->
<div id="results-container"></div>
<script>
// Configuration
const searchData = {
banner_color: 'darkblue',
banner_text: [
`Do they love burgers? 🍔 Search for their <strong style="color: #ffe606">favorite</strong> below.`,
`You've got <strong style="color: #ffe606">great taste</strong>. We've got gift cards to match.`,
],
search_text: [
{
content: `(thinking <u>top smash burger joints</u>?)`,
key: 'smash burgers',
},
{
content: `(thinking <u>best lunch spots near me</u>?)`,
key: 'best lunch spots',
},
],
search_term: '',
search_button_color: {
text: 'yellow',
background: 'green',
},
};
// Store reference to the web component
let searchElement = null;
// Initialize the web component
function initializeComponent() {
const container = document.getElementById('search-container');
// Create the custom element
searchElement = document.createElement('gl-search');
// Set required attributes
searchElement.setAttribute('environment', 'development');
searchElement.setAttribute('jwt', 'your-jwt-token');
// Set optional attributes
searchElement.setAttribute('google_api_key', 'your-google-api-key');
searchElement.setAttribute('debounce_ms', '250');
searchElement.setAttribute('data', JSON.stringify(searchData));
// Append to container
container.appendChild(searchElement);
console.log('Component initialized');
}
// Handle search event
document.addEventListener('gl:search', async function(e) {
console.log('Search performed:', e.detail);
const resultsContainer = document.getElementById('results-container');
resultsContainer.innerHTML = '<div class="loading">Loading...</div>';
try {
// Parse query parameters
const params = new URLSearchParams(e.detail.query);
const location = params.get('Location');
const searchTerm = params.get('SearchTerm');
console.log('Location:', location);
console.log('Search Term:', searchTerm);
// Call your API
const response = await fetch(`/api/search?location=${location}&term=${searchTerm}`);
const data = await response.json();
// Display results
resultsContainer.innerHTML = '<div class="results"><h2>Search Results</h2>' +
data.results.map(result => `<div>${result.name}</div>`).join('') +
'</div>';
} catch (error) {
console.error('Search error:', error);
resultsContainer.innerHTML = '<div class="results">Error loading results</div>';
}
});
// Handle clear event
document.addEventListener('gl:clear', function(e) {
console.log('Search cleared:', e.detail);
document.getElementById('results-container').innerHTML = '';
});
// Method to focus input
function handleFocus() {
if (searchElement) {
searchElement.focus();
console.log('Input focused');
}
}
// Method to set query
function handleSetQuery() {
if (searchElement) {
searchElement.setQuery('Location=New York&SearchTerm=pizza');
console.log('Query set');
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeComponent);
} else {
initializeComponent();
}
</script>
</body>
</html>
TypeScript Support
This package includes full TypeScript definitions. Import types as needed:
import type {
GlSearchProps,
GlSearchBarHandle,
SearchBarEventType
} from '@gift-card-market/gl-search';
import { SearchBarEvents } from '@gift-card-market/gl-search';
Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
License
ISC © Gift Card Market
Support
For issues and questions, please visit:
Contributing
Contributions are welcome! Please refer to the project's contributing guidelines.
Changelog
Version 1.0.14
- Current stable release
- Full React and Vanilla JS support
- Custom event system
- TypeScript definitions included