v0.1.35 - Ingredient categories: created categories now available across all rows
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,14 @@ export type ChangelogEntry = {
|
||||
export const APP_VERSION: string = packageInfo.version;
|
||||
|
||||
export const changelog: ChangelogEntry[] = [
|
||||
{
|
||||
version: '0.1.35',
|
||||
date: '2026-06-21',
|
||||
highlights: [
|
||||
'App: Bug fixes & improvements.',
|
||||
'App: General improvements.'
|
||||
]
|
||||
},
|
||||
{
|
||||
version: '0.1.34',
|
||||
date: '2026-06-21',
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
placeholder = 'Category',
|
||||
inputId,
|
||||
disabled = false,
|
||||
ariaLabel = 'Category'
|
||||
ariaLabel = 'Category',
|
||||
oncreate
|
||||
}: {
|
||||
value?: string;
|
||||
options?: string[];
|
||||
@@ -22,6 +23,9 @@
|
||||
inputId?: string;
|
||||
disabled?: boolean;
|
||||
ariaLabel?: string;
|
||||
/** Fired when the user deliberately creates a brand-new category, so the
|
||||
* parent can keep it available to every other row. */
|
||||
oncreate?: (category: string) => void;
|
||||
} = $props();
|
||||
|
||||
// `query` is the ephemeral search text; `value` is the committed category.
|
||||
@@ -80,7 +84,9 @@
|
||||
}
|
||||
|
||||
function createNew() {
|
||||
value = trimmed;
|
||||
const created = trimmed;
|
||||
value = created;
|
||||
oncreate?.(created);
|
||||
closeMenu();
|
||||
}
|
||||
|
||||
|
||||
@@ -174,14 +174,28 @@
|
||||
})
|
||||
);
|
||||
|
||||
// Categories the user has explicitly created via the combobox this session.
|
||||
// Tracked separately from row values so a freshly-created category stays
|
||||
// available to every row even before it's been saved to (or assigned on) any
|
||||
// ingredient — otherwise it would vanish as soon as you moved off the row.
|
||||
let createdCategories = $state<string[]>([]);
|
||||
|
||||
function registerCategory(category: string) {
|
||||
const name = category.trim();
|
||||
if (!name) return;
|
||||
if (createdCategories.some((existing) => existing.toLowerCase() === name.toLowerCase())) return;
|
||||
createdCategories = [...createdCategories, name];
|
||||
}
|
||||
|
||||
// Existing categories, offered as autocomplete suggestions so spelling stays
|
||||
// consistent across ingredients.
|
||||
const knownCategories = $derived(
|
||||
Array.from(
|
||||
new Set(
|
||||
rows
|
||||
.map((row) => (row.draft_category || row.category || '').trim())
|
||||
.filter((value) => value.length > 0)
|
||||
[
|
||||
...rows.map((row) => (row.draft_category || row.category || '').trim()),
|
||||
...createdCategories.map((value) => value.trim())
|
||||
].filter((value) => value.length > 0)
|
||||
)
|
||||
).sort((a, b) => a.localeCompare(b))
|
||||
);
|
||||
@@ -298,7 +312,7 @@
|
||||
</label>
|
||||
<label>
|
||||
<span>Category</span>
|
||||
<CategoryCombobox bind:value={newIngredient.category} options={knownCategories} placeholder="e.g. Grains" inputId="new-ingredient-category" />
|
||||
<CategoryCombobox bind:value={newIngredient.category} options={knownCategories} placeholder="e.g. Grains" inputId="new-ingredient-category" oncreate={registerCategory} />
|
||||
</label>
|
||||
<label>
|
||||
<span>Rounding</span>
|
||||
@@ -368,7 +382,7 @@
|
||||
|
||||
<div class="cell">
|
||||
<span class="cell-label">Category</span>
|
||||
<CategoryCombobox bind:value={row.draft_category} options={knownCategories} placeholder="—" ariaLabel={`Category for ${row.name}`} />
|
||||
<CategoryCombobox bind:value={row.draft_category} options={knownCategories} placeholder="—" ariaLabel={`Category for ${row.name}`} oncreate={registerCategory} />
|
||||
</div>
|
||||
|
||||
<div class="cell">
|
||||
|
||||
Reference in New Issue
Block a user