Skip to content

Commit 629b8d2

Browse files
committed
2025-05-04: v1.1.0 - Major UI/UX Enhancement
1 parent 9a2c190 commit 629b8d2

File tree

2 files changed

+286
-47
lines changed

2 files changed

+286
-47
lines changed

Update.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Update Log
2+
3+
## 2025-05-04: v1.1.0 - Major UI/UX Enhancement
4+
5+
### Added
6+
7+
- Responsive design for better mobile experience
8+
- Modern UI with Google Material Design inspired styling
9+
- Loading spinner for better feedback during searches
10+
- Error handling with user-friendly error messages
11+
- Accessibility improvements (ARIA labels, semantic HTML)
12+
- Meta tags for better SEO and device compatibility
13+
14+
### Changed
15+
16+
- Updated PapaParse library to version 5.3.2
17+
- Improved form layout with flexbox
18+
- Enhanced search input field styling
19+
- Better checkbox group organization
20+
- Modernized button styling with hover effects
21+
- Async/await implementation for better error handling
22+
- Added proper HTML document language attribute
23+
24+
### Technical Improvements
25+
26+
- CSS custom properties (variables) for consistent theming
27+
- Mobile-first responsive design
28+
- Improved error handling for API calls
29+
- Better loading state management
30+
- Enhanced accessibility for screen readers
31+
- Proper semantic HTML structure
32+
- Optimized CSS with modern best practices
33+
34+
### Previous Updates
35+
36+
- 2022-02-01: v1.0.2 - Updated sheet name reference
37+
- 2020-05-26: v1.0.1 - Added duplicate checker
38+
- 2020-03-12: v1.0.0 - Initial release

searchApp.html

Lines changed: 248 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
<!DOCTYPE html>
3-
<html>
3+
<html lang="en">
44
<head>
55
<!--
66
This is a web application for searching Google Apps Script Libraries. This can be run with the standalone.
@@ -11,28 +11,192 @@
1111
-->
1212

1313
<base target="_top">
14+
<meta charset="UTF-8">
15+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
16+
<title>Search Google Apps Script Libraries</title>
1417
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
1518
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
16-
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.1.0/papaparse.min.js"></script>
19+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.2/papaparse.min.js"></script>
1720
<style>
21+
:root {
22+
--primary-color: #4285f4;
23+
--hover-color: #3367d6;
24+
--text-color: #333;
25+
--border-color: #cccccc;
26+
}
27+
28+
body {
29+
font-family: 'Google Sans', Arial, sans-serif;
30+
line-height: 1.6;
31+
color: var(--text-color);
32+
margin: 0;
33+
padding: 20px;
34+
}
35+
1836
form {
19-
border-radius:10px;
20-
border:1px solid #cccccc;
21-
margin: 0px;
22-
padding: 10px;
37+
border-radius: 8px;
38+
border: 1px solid var(--border-color);
39+
margin: 0 auto;
40+
padding: 20px;
41+
max-width: 1200px;
42+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
2343
}
24-
44+
2545
.title {
26-
font-size: 3vmin;
46+
font-size: clamp(1.5rem, 3vmin, 2rem);
47+
color: var(--primary-color);
48+
margin-bottom: 1rem;
49+
}
50+
51+
.search-container {
52+
display: flex;
53+
gap: 10px;
54+
margin-bottom: 1rem;
55+
flex-wrap: wrap;
56+
}
57+
58+
.search-input {
59+
flex: 1;
60+
min-width: 200px;
61+
padding: 8px 12px;
62+
border: 1px solid var(--border-color);
63+
border-radius: 4px;
64+
font-size: 1rem;
65+
}
66+
67+
.checkbox-group {
68+
display: flex;
69+
gap: 1rem;
70+
flex-wrap: wrap;
71+
align-items: center;
72+
margin: 1rem 0;
73+
}
74+
75+
.checkbox-label {
76+
display: flex;
77+
align-items: center;
78+
gap: 4px;
79+
cursor: pointer;
80+
}
81+
82+
.action {
83+
background: var(--primary-color);
84+
color: white;
85+
border: none;
86+
padding: 8px 16px;
87+
border-radius: 4px;
88+
cursor: pointer;
89+
transition: background-color 0.3s;
90+
}
91+
92+
.action:hover {
93+
background: var(--hover-color);
94+
}
95+
96+
.action:disabled {
97+
background: #ccc;
98+
cursor: not-allowed;
99+
}
100+
101+
.loading {
102+
display: inline-block;
103+
position: relative;
104+
width: 20px;
105+
height: 20px;
106+
margin-right: 8px;
107+
}
108+
109+
.loading:after {
110+
content: '';
111+
display: block;
112+
width: 16px;
113+
height: 16px;
114+
border-radius: 50%;
115+
border: 2px solid #fff;
116+
border-color: #fff transparent #fff transparent;
117+
animation: loading 1.2s linear infinite;
118+
}
119+
120+
@keyframes loading {
121+
0% { transform: rotate(0deg); }
122+
100% { transform: rotate(360deg); }
123+
}
124+
125+
#result {
126+
margin-top: 2rem;
127+
overflow-x: auto;
128+
}
129+
130+
.error-message {
131+
color: #d32f2f;
132+
background: #ffebee;
133+
padding: 1rem;
134+
border-radius: 4px;
135+
margin: 1rem 0;
136+
}
137+
138+
@media (max-width: 768px) {
139+
body {
140+
padding: 10px;
141+
}
142+
143+
form {
144+
padding: 15px;
145+
}
146+
147+
.search-container {
148+
flex-direction: column;
149+
}
150+
151+
.checkbox-group {
152+
flex-direction: column;
153+
align-items: flex-start;
154+
}
27155
}
28156
</style>
29157
</head>
30158
<body>
31-
<form class="form-group" id="form">
32-
<p class="title"><b>Search Google Apps Script Libraries v1.0.0</b> <span id="items"></span></p>
33-
<p>Author: <a href="https://tanaikech.github.io/" target="_blank">Tanaike</a></p>
34-
<p>Input text for searching : <input type="text" id="searchtext" name="searchtext" size="50" placeholder="Please input search text for searching GAS libraries."> </p>
35-
<p>Searching the specific properties with the inputted text : <input type="checkbox" name="props" value="libraryName" id="check1">Library name <input type="checkbox" name="props" value="description" id="check2">Description <input type="checkbox" name="props" value="authors" id="check3">Authors <input type="checkbox" name="props" value="tags" id="check4">Tags <input type="checkbox" name="propall" value="all" id="allcheck">All </p> <input type="button" id="gosearch" class="action" value="loading..." onclick="search(this.parentNode)" disabled>
159+
<form class="form-group" id="form" role="search">
160+
<h1 class="title">Search Google Apps Script Libraries <span id="items"></span></h1>
161+
<p>Author: <a href="https://tanaikech.github.io/" target="_blank" rel="noopener">Tanaike</a></p>
162+
163+
<div class="search-container">
164+
<input
165+
type="search"
166+
id="searchtext"
167+
name="searchtext"
168+
class="search-input"
169+
placeholder="Search for GAS libraries..."
170+
aria-label="Search for Google Apps Script libraries"
171+
>
172+
<button type="button" id="gosearch" class="action" onclick="search(this.form)" disabled>
173+
<span class="loading" id="loading-spinner" style="display: none;"></span>
174+
<span id="search-button-text">Loading...</span>
175+
</button>
176+
</div>
177+
178+
<div class="checkbox-group" role="group" aria-label="Search filters">
179+
<label class="checkbox-label" for="check1">
180+
<input type="checkbox" name="props" value="libraryName" id="check1" aria-label="Search in library names">
181+
Library name
182+
</label>
183+
<label class="checkbox-label" for="check2">
184+
<input type="checkbox" name="props" value="description" id="check2" aria-label="Search in descriptions">
185+
Description
186+
</label>
187+
<label class="checkbox-label" for="check3">
188+
<input type="checkbox" name="props" value="authors" id="check3" aria-label="Search by authors">
189+
Authors
190+
</label>
191+
<label class="checkbox-label" for="check4">
192+
<input type="checkbox" name="props" value="tags" id="check4" aria-label="Search by tags">
193+
Tags
194+
</label>
195+
<label class="checkbox-label" for="allcheck">
196+
<input type="checkbox" name="propall" value="all" id="allcheck" aria-label="Search in all fields">
197+
All
198+
</label>
199+
</div>
36200
<p>- In the current stage, the library data is retrieved from 2 databases by <a href="https://github.com/tanaikech/Google-Apps-Script-Library-Database" target="_blank">Tanaike</a> and <a href="https://docs.google.com/spreadsheets/d/1Lk6OClOPA8p94fspQrs8-M-W080tb244U-fWGqvnApk/edit#gid=0" target="_blank">Andrew Roberts</a>.</p>
37201
<p>- When you push "search" button without the search text, all libraries are shown.</p>
38202
<p>- When you can add new GAS libraries to this database, please add them using <a href="https://docs.google.com/forms/d/e/1FAIpQLSckRzFtF-i1CUwdhA21GteWok9p5-_G4Py3PH5bC9KaqXoOxA/viewform">this Google Form</a> by Andrew Roberts.</p>
@@ -43,15 +207,38 @@
43207
const databaseUrls1 = "https://raw.githubusercontent.com/tanaikech/Google-Apps-Script-Library-Database/master/Google-Apps-Script-Library-Database.json";
44208
const databaseUrls2 = "https://docs.google.com/spreadsheets/d/1Lk6OClOPA8p94fspQrs8-M-W080tb244U-fWGqvnApk/gviz/tq?tqx=out:csv&sheet=Libraries";
45209

210+
async function fetchData() {
211+
try {
212+
showLoading(true);
213+
const response = await fetch(databaseUrls1);
214+
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
215+
const data = await response.json();
216+
return data;
217+
} catch (error) {
218+
showError('Failed to fetch library database. Please try again later.');
219+
console.error('Error fetching database:', error);
220+
throw error;
221+
}
222+
}
223+
224+
function showLoading(show) {
225+
const spinner = document.getElementById('loading-spinner');
226+
const buttonText = document.getElementById('search-button-text');
227+
spinner.style.display = show ? 'inline-block' : 'none';
228+
buttonText.textContent = show ? 'Loading...' : 'Search';
229+
}
230+
231+
function showError(message) {
232+
const errorDiv = document.createElement('div');
233+
errorDiv.className = 'error-message';
234+
errorDiv.setAttribute('role', 'alert');
235+
errorDiv.textContent = message;
236+
document.getElementById('result').prepend(errorDiv);
237+
setTimeout(() => errorDiv.remove(), 5000);
238+
}
239+
46240
Promise.resolve()
47-
.then(() => {
48-
return new Promise((resolve, reject) => {
49-
fetch(databaseUrls1)
50-
.then(res => res.json())
51-
.then(obj => resolve(obj))
52-
.catch(err => reject(err));
53-
});
54-
})
241+
.then(() => fetchData())
55242
.then(object => {
56243
return new Promise((resolve, reject) => {
57244
fetch(databaseUrls2)
@@ -141,32 +328,46 @@
141328
}
142329
})
143330

144-
function search(e) {
145-
const searchProps = [...e.props].reduce((ar, e) => {
146-
if (e.checked) ar.push(e.value);
147-
return ar;
148-
}, []);
149-
const searchTexts = e.searchtext.value.split(/,| /g).reduce((ar, e) => {
150-
if (e) ar.push(e.trim());
151-
return ar;
152-
}, []);
153-
const res = searchLibraries({searchValues: searchTexts, searchProperties: searchProps});
154-
res.sort((a, b) => a.libraryName.toLowerCase() < b.libraryName.toLowerCase() ? -1 : 1);
155-
const values = res.map(({libraryName, authors, publishedDate, siteUrl, projectKey, description}) =>
156-
[libraryName, authors.join(","), (publishedDate ? new Date(publishedDate * 1000) : null), (siteUrl ? '<a href="' + siteUrl + '" target="_blank">link</a>' : ""), projectKey, description]
157-
);
158-
google.charts.setOnLoadCallback(() => {
159-
var data = new google.visualization.DataTable();
160-
data.addColumn('string', 'Library name');
161-
data.addColumn('string', 'Author');
162-
data.addColumn('date', 'Published date');
163-
data.addColumn('string', 'Url');
164-
data.addColumn('string', 'Project key');
165-
data.addColumn('string', 'Description');
166-
data.addRows(values);
167-
var table = new google.visualization.Table(document.getElementById('result'));
168-
table.draw(data, {allowHtml: true, showRowNumber: true, width: '100%'});
169-
});
331+
async function search(e) {
332+
try {
333+
showLoading(true);
334+
document.getElementById('result').innerHTML = '';
335+
336+
const searchProps = [...e.props].reduce((ar, e) => {
337+
if (e.checked) ar.push(e.value);
338+
return ar;
339+
}, []);
340+
341+
const searchTexts = e.searchtext.value.split(/,| /g).reduce((ar, e) => {
342+
if (e) ar.push(e.trim());
343+
return ar;
344+
}, []);
345+
346+
const res = searchLibraries({searchValues: searchTexts, searchProperties: searchProps});
347+
res.sort((a, b) => a.libraryName.toLowerCase() < b.libraryName.toLowerCase() ? -1 : 1);
348+
349+
const values = res.map(({libraryName, authors, publishedDate, siteUrl, projectKey, description}) =>
350+
[libraryName, authors.join(","), (publishedDate ? new Date(publishedDate * 1000) : null), (siteUrl ? '<a href="' + siteUrl + '" target="_blank">link</a>' : ""), projectKey, description]
351+
);
352+
353+
google.charts.setOnLoadCallback(() => {
354+
var data = new google.visualization.DataTable();
355+
data.addColumn('string', 'Library name');
356+
data.addColumn('string', 'Author');
357+
data.addColumn('date', 'Published date');
358+
data.addColumn('string', 'Url');
359+
data.addColumn('string', 'Project key');
360+
data.addColumn('string', 'Description');
361+
data.addRows(values);
362+
var table = new google.visualization.Table(document.getElementById('result'));
363+
table.draw(data, {allowHtml: true, showRowNumber: true, width: '100%'});
364+
});
365+
} catch (error) {
366+
showError('An error occurred while searching. Please try again.');
367+
console.error('Search error:', error);
368+
} finally {
369+
showLoading(false);
370+
}
170371
}
171372

172373
function searchLibraries(searchObj) {

0 commit comments

Comments
 (0)