Skip to content

Commit

Permalink
Add expense description autocomplete suggestions
Browse files Browse the repository at this point in the history
For now, no keyboard UX, will be added if there's enough demand.
  • Loading branch information
BrunoBernardino committed Jul 16, 2023
1 parent c1dff0c commit b83d9be
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 8 deletions.
7 changes: 2 additions & 5 deletions pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ export function pageContent() {
autocomplete="off"
type="text"
inputmode="decimal"
data-automation="add-expense-cost"
/>
</fieldset>
<fieldset class="input-wrapper">
Expand All @@ -121,16 +120,15 @@ export function pageContent() {
id="expense-description"
placeholder="Lunch"
type="text"
data-automation="add-expense-description"
/>
<aside id="expense-description-autocomplete-suggestions" class="hidden"></aside>
</fieldset>
<fieldset class="input-wrapper">
<label for="expense-budget">Budget</label>
<select
id="expense-budget"
placeholder="Misc"
autocomplete="off"
data-automation="add-expense-budget"
></select>
</fieldset>
<fieldset class="input-wrapper">
Expand All @@ -139,11 +137,10 @@ export function pageContent() {
id="expense-date"
placeholder="Today"
type="date"
data-automation="add-expense-date"
/>
</fieldset>
<button type="submit" id="add-expense-button" data-automation="add-expense-button">Add Expense</button>
<button type="submit" id="add-expense-button">Add Expense</button>
</form>
</section>
</div>
Expand Down
1 change: 1 addition & 0 deletions public/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ a.button.secondary:focus {
background: #fff;
border-radius: 3px;
transition: all 80ms ease-in-out;
color: var(--color-background);
}
.input-wrapper input[type="text"]:focus,
.input-wrapper input[type="date"]:focus,
Expand Down
39 changes: 39 additions & 0 deletions public/scss/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,42 @@
text-align: right;
text-transform: uppercase;
}

#expense-description-autocomplete-suggestions {
display: flex;
position: absolute;
z-index: 10;
padding: 0;
border-radius: 3px;
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.1);
background-color: #efefef;
margin: 0.5rem;
min-width: 250px;
max-width: 275px;
flex-direction: column;
overflow: auto;
max-height: 130px;

span {
padding: 0.5rem;
display: block;
color: var(--color-background);
cursor: pointer;

&:hover {
background-color: #ddd !important;
}

&:nth-child(2n+1) {
background-color: #fff;
}

&:first-child {
border-radius: 3px 3px 0 0;
}

&:last-child {
border-radius: 0 0 3px 3px;
}
}
}
57 changes: 54 additions & 3 deletions public/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@ document.addEventListener('app-loaded', async () => {
const budgetsFilterButton = document.getElementById('budgets-filter-button') as HTMLButtonElement;
const addBudgetButton = document.getElementById('add-budget-button') as HTMLButtonElement;

const expensesList = document.getElementById('expenses')!;
const budgetsList = document.getElementById('budgets')!;
const expensesList = document.getElementById('expenses') as HTMLDivElement;
const budgetsList = document.getElementById('budgets') as HTMLDivElement;
const addExpenseForm = document.getElementById('add-expense-form') as HTMLFormElement;
const addExpenseButton = document.getElementById('add-expense-button') as HTMLButtonElement;
const expenseCostInput = document.getElementById('expense-cost') as HTMLInputElement;
const expenseDescriptionInput = document.getElementById('expense-description') as HTMLInputElement;
const expenseDescriptionAutocompleteSuggestionsList = document.getElementById(
'expense-description-autocomplete-suggestions',
) as HTMLDivElement;
const expenseBudgetSelect = document.getElementById('expense-budget') as HTMLSelectElement;
const expenseDateInput = document.getElementById('expense-date') as HTMLInputElement;
const monthNavigationPreviousButton = document.getElementById('month-navigation-previous') as HTMLButtonElement;
Expand All @@ -48,6 +51,7 @@ document.addEventListener('app-loaded', async () => {
let currentMonth = (new Date().toISOString()).substring(0, 7);
let currency: SupportedCurrencySymbol = '$';
const budgetFilters = new Set<string>();
const uniqueExpenseNames = new Set<string>();

async function login(event: MouseEvent | SubmitEvent) {
loginButton.textContent = 'Logging in...';
Expand Down Expand Up @@ -200,7 +204,15 @@ document.addEventListener('app-loaded', async () => {
const allBudgets = await fetchBudgets('all');
const budgets = await fetchBudgets(currentMonth);
const expenses = await fetchExpenses(currentMonth);
fetchExpenses('all'); // Helpful for faster addition

// IIFE because we don't want to wait for all expenses in the main event-loop, but need it to get unique names
(async () => {
const allExpenses = await fetchExpenses('all');
const allExpenseNames: string[] = []; // This is necessary because you can't sort a Set, but it follows insertion order
allExpenses.forEach((expense) => allExpenseNames.push(expense.description));
allExpenseNames.sort();
allExpenseNames.forEach((expenseName) => uniqueExpenseNames.add(expenseName));
})();

const budgetOptions = new Set([{ name: 'Misc' }, ...allBudgets].map((budget) => budget.name));

Expand Down Expand Up @@ -718,6 +730,42 @@ document.addEventListener('app-loaded', async () => {
}
}

function showAutocompleteSuggestions() {
const { value } = expenseDescriptionInput;

if (value.length <= 2) {
expenseDescriptionAutocompleteSuggestionsList.classList.add('hidden');
return;
}

const suggestedExpenseNames = new Set<string>();

uniqueExpenseNames.forEach((expenseName) => {
if (expenseName.toLocaleLowerCase().includes(value.toLocaleLowerCase())) {
suggestedExpenseNames.add(expenseName);
}
});

expenseDescriptionAutocompleteSuggestionsList.replaceChildren();
suggestedExpenseNames.forEach((expenseName) => {
const expenseNameElement = document.createElement('span');
expenseNameElement.innerText = expenseName;

expenseNameElement.addEventListener('click', () => {
expenseDescriptionInput.value = expenseName;
expenseDescriptionInput.focus();
});

expenseDescriptionAutocompleteSuggestionsList.appendChild(expenseNameElement);
});

expenseDescriptionAutocompleteSuggestionsList.classList.remove('hidden');
}

function hideAutocompleteSuggestions() {
expenseDescriptionAutocompleteSuggestionsList.classList.add('hidden');
}

if (window.app.isLoggedIn) {
initializePage();
}
Expand All @@ -731,4 +779,7 @@ document.addEventListener('app-loaded', async () => {
monthNavigationPreviousButton.addEventListener('click', navigateToPreviousMonth);
monthNavigationNextButton.addEventListener('click', navigateToNextMonth);
monthNavigationLabel.addEventListener('click', chooseMonthModal);
expenseDescriptionInput.addEventListener('focus', showAutocompleteSuggestions);
expenseDescriptionInput.addEventListener('keyup', debounce(showAutocompleteSuggestions, 150));
expenseDescriptionInput.addEventListener('blur', debounce(hideAutocompleteSuggestions, 150));
});

0 comments on commit b83d9be

Please sign in to comment.