Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sorting: its not working lol #45

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b380cf3
its not working lol
ashlsun Jul 11, 2024
565ea01
refactor: sort type string to string literal
Rettend Jul 11, 2024
0fce6dd
refactor!: global item store
Rettend Jul 11, 2024
c5726d8
refactor: group storage related ops
Rettend Jul 11, 2024
fec5b35
chore: cleanup
Rettend Jul 11, 2024
72dead3
move parsing logic to handler in component, remove importItem
ashlsun Jul 12, 2024
15aff94
deps: bump svelte and fix sorting bruhhhh
Rettend Jul 12, 2024
a3b8350
chore: resolve conflicts
Rettend Jul 12, 2024
415a86e
fix: messed up format in tips
Rettend Jul 12, 2024
c0f6ba3
revert: key with id
Rettend Jul 12, 2024
1ce18b6
feat: add none sort, remove hardcodes
Rettend Jul 12, 2024
c5632db
feat: store sorts in localstorage
Rettend Jul 12, 2024
8c42358
style: change delete text to x icon
Rettend Jul 12, 2024
5622ad0
fix: item count, tests broken by delete icon
Rettend Jul 12, 2024
e43ed5f
fix: preserve selected item when sorting
Rettend Jul 12, 2024
e109cbd
feat: add and rename storages
Rettend Jul 12, 2024
9ca5c04
make x icon a thinner stroke
ashlsun Jul 13, 2024
006edcd
use option/alt + number to select the first item of corresponding sto…
ashlsun Jul 17, 2024
df110db
don't select when storage is an empty list
ashlsun Jul 17, 2024
b53c7cb
fix: remove random idb query
Rettend Jul 17, 2024
133a0f8
feat: add flex wrapping to storages
Rettend Jul 17, 2024
8e6ac1b
fix: prevent scrolling with arrows
Rettend Jul 17, 2024
c02e645
refactor: change sort dropdown handling for tests
Rettend Jul 17, 2024
07b8d8e
test: add basic tests for storageplace
Rettend Jul 17, 2024
f5d663b
fix: ♾️ recursion
Rettend Jul 18, 2024
059d52d
fix ai workflow
ashlsun Jul 21, 2024
539923d
clear selection after input
ashlsun Jul 21, 2024
42cc922
fix: select previous item if deleting last item
ashlsun Jul 21, 2024
9d6c392
handle keyboard navigation at page level instead of item level
ashlsun Jul 26, 2024
a1db555
fix tests & remove obsolete ones
ashlsun Sep 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"globals": "^15.0.0",
"jsdom": "^24.1.0",
"postcss": "^8.4.38",
"svelte": "^5.0.0-next.166",
"svelte": "^5.0.0-next.182",
"svelte-check": "^3.6.0",
"tailwindcss": "^3.4.4",
"tslib": "^2.4.1",
Expand Down
141 changes: 100 additions & 41 deletions src/lib/components/StoragePlace.svelte
Original file line number Diff line number Diff line change
@@ -1,78 +1,137 @@
<script lang="ts">
import { untrack } from 'svelte'
import Item from '$lib/components/item/Item.svelte'
import { type StoredItem } from '$lib/db'
import { type ItemStore } from '$lib/stores/item.svelte'
import type { SortBy, StoredItem } from '$lib/types'
import { sortBy } from '$lib/types'
import { itemStore } from '$lib/stores/item.svelte'
import { localDb } from '$lib/db'

// Props
type Props = {
storagePlaceName: string
items: ItemStore | null
storageName: string
}

const {
storagePlaceName,
items,
storageName,
}: Props = $props()

// State
let newItemName = $state('')
let newItemInput = $state('')
let sortOption = $state<SortBy>(localDb.storage.getSort(storageName))
let previousStorageName = storageName
const storageOps = $derived(itemStore.storage(storageName))

// Reactive declarations
$effect(() => {
if (localDb.storage.getSort(storageName) !== sortOption)
localDb.storage.setSort(storageName, sortOption)

untrack(() => storageOps.sortItems(sortOption))
})

// Methods
function addItem() {
if (items)
items.add(newItemName)
newItemName = ''
function inputItem() {
if (newItemInput === '')
return

let name = newItemInput
let quantity = 1

const itemList = newItemInput.split(' ')
if (itemList.length > 1 && itemList[0].match(/^\d+$/)) {
name = newItemInput.slice(itemList[0].length).trim()
quantity = Number(itemList[0])
}
storageOps.addItem({ name, quantity })
sortOption = 'none'
newItemInput = ''
}

// Handlers
function handleInputKeypress(event: KeyboardEvent) {
if (event.key === 'Enter')
addItem()
inputItem()
}

function handleSort() {
storageOps.sortItems(sortOption)
}

function handleEnter(event: KeyboardEvent) {
if (event.key !== 'Enter')
return

const target = event.target as HTMLElement
event.preventDefault()
itemStore.updateStorage(previousStorageName, target.textContent || '')
localDb.storage.rename(previousStorageName, target.textContent || '')
previousStorageName = target.textContent || ''
target.blur()
}
</script>

<div
class="rounded-sm border m-3 inline-block h-fit min-w-80 max-w-[420px] border-black p-1"
role="tree"
>
<h1 class="font-bold">{storagePlaceName}
{#if items} <span class="text-stone-400">({items.list.length})</span> {/if}
</h1>
{#if items}
{@const store = items}
<div role="group">
{#each items.list as item, i (item.id)}
<Item
bind:item={items.list[i]}
isSelected={items.selected === i}
onSelected={(amount = 0) => {
store.select(i + amount)
}}
onDelete={() => {
store.delete(item.id)
}}
onUpdate={(updatedItem: StoredItem) => {
store.update(item.id, updatedItem)
}}
/>
{/each}
<div class="flex w-full justify-between items-center ">
<h1>
<span
contenteditable
role="textbox"
aria-multiline="false"
tabindex="0"
onkeydown={handleEnter}
>
<b>{storageName}</b>
</span>
<span class="text-stone-400">({itemStore.itemCounts[storageName]})</span>
</h1>

<div class="flex items-center">
<span class="text-sm italic text-stone-500 mr-1">sort:</span>
<select
class="text-sm py-1 my-1 italic text-stone-500"
bind:value={sortOption}
onchange={handleSort}
>
{#each sortBy as sort}
<option value={sort} hidden={sort === 'none'}>{sort}</option>
{/each}
</select>
</div>
{#if items.list.length === 0}
<div class="text-stone-400">Nothing in the {storagePlaceName}.</div>
{/if}
{:else}
<p class="text-stone-400">Loading...</p>

</div>
<div role="group">
{#each itemStore.items[storageName] as item, i (item.id)}
<Item
bind:item={itemStore.items[storageName][i]}
isSelected={itemStore.selected.storage === storageName && itemStore.selected.index === i}
onSelected={(amount = 0) => {
itemStore.selectItem(storageName, i + amount)
}}
onDelete={() => {
storageOps.deleteItem(item.id)
}}
onUpdate={(updatedItem: StoredItem) => {
storageOps.updateItem(updatedItem)
}}
/>
{/each}
</div>
{#if itemStore.itemCounts[storageName] === 0}
<div class="text-stone-400">Nothing in the {storageName}.</div>
{/if}
<input
class="rounded-sm border border-black px-1 transition mt-5 outline-emerald-600 placeholder:text-stone-400 placeholder:italic placeholder:text-sm"
bind:value={newItemName}
bind:value={newItemInput}
onkeypress={handleInputKeypress}
maxlength="20"
placeholder="Add a new item..."
/>
<button
class="transition hover:font-bold hover:text-emerald-600"
onclick={addItem}
class="transition hover:font-bold hover:text-emerald-700"
onclick={inputItem}
>
+
</button>
Expand Down
9 changes: 3 additions & 6 deletions src/lib/components/item/Item.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@
// DOM nodes
let itemDiv: HTMLDivElement
let itemNameInput: HTMLSpanElement
let itemQuantityInput: HTMLInputElement
let childrenDiv: HTMLDivElement
let dateAddedInput: HTMLInputElement
let shelfLifeInput: HTMLInputElement

// Reactive declarations
const daysTilSpoil = $derived(
Expand Down Expand Up @@ -229,7 +227,6 @@
<div class="flex justify-between">
<span>
<input
bind:this={itemQuantityInput}
bind:value={item.quantity}
type="number"
min="0"
Expand Down Expand Up @@ -271,11 +268,12 @@
: ' text-stone-400'}">{dayjs(item.dateAdded).fromNow()}
</span>
<button
class="transition items-end hover:text-red-600"
class="transition items-end text-stone-600 hover:text-red-600"
onclick={() => {
onDelete()
}}
>delete
>
<span title="delete" class="icon tabler--x" aria-hidden="true"></span>
</button>
</span>
</div>
Expand Down Expand Up @@ -303,7 +301,6 @@
<div role="treeitem" aria-selected="false">
Edit shelf life:
<input
bind:this={shelfLifeInput}
type="number"
class="border-1 max-w-12 border border-dashed border-stone-400 text-center always-display-spinner sm mb-1 ml-3 w-fit rounded"
bind:value={draftShelfLife}
Expand Down
8 changes: 4 additions & 4 deletions src/lib/components/item/Item.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ describe('the Item component', () => {
})

it('renders correctly', () => {
const { getByText, getByDisplayValue } = component
const { getByText, getByDisplayValue, getByTitle } = component

expect(getByText(/Test Item/)).toBeTruthy()
expect(getByDisplayValue('2')).toBeTruthy()
expect(getByText('delete')).toBeTruthy()
expect(getByTitle('delete')).toBeTruthy()
})

it('increases quantity on right arrow key press', async () => {
Expand Down Expand Up @@ -76,9 +76,9 @@ describe('the Item component', () => {
})

it('calls deleteItem when delete button is clicked', async () => {
const { getByText } = component
const { getByTitle } = component

await fireEvent.click(getByText('delete'))
await fireEvent.click(getByTitle('delete'))
expect(mockDeleteItem).toHaveBeenCalledOnce()
})
})
63 changes: 54 additions & 9 deletions src/lib/db.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import Dexie, { type Table } from 'dexie'

export interface StoredItem {
id: string
name: string
quantity: number
dateAdded: string
storage: string
shelfLife: number
}
import type { SortBy, StoredItem } from './types'

export interface CommonItem {
id: string
Expand Down Expand Up @@ -37,4 +29,57 @@ class FoodInventoryDatabase extends Dexie {
}
}

type SortStorage = {
storage: string
sortBy: SortBy
}[]

class LocalStorageDatabase {
private getTable<T>(tableName: string): T {
const tableJson = localStorage.getItem(tableName)
return tableJson ? JSON.parse(tableJson) : []
}

private setTable<T>(tableName: string, data: T) {
localStorage.setItem(tableName, JSON.stringify(data))
}

constructor() {
if (!localStorage.getItem('sortOptions'))
this.setTable<SortStorage>('sortOptions', [])
}

public storage = {
add: (storage: string) => {
const sortOptions = this.getTable<SortStorage>('sortOptions')
sortOptions.push({ storage, sortBy: 'none' })
this.setTable('sortOptions', sortOptions)
},
getSort: (storage: string) => {
const sortOptions = this.getTable<SortStorage>('sortOptions')
const storageSort = sortOptions.find(sort => sort.storage === storage)
return storageSort ? storageSort.sortBy : 'none'
},
setSort: (storage: string, sortBy: SortBy) => {
const sortOptions = this.getTable<SortStorage>('sortOptions')
const storageSort = sortOptions.find(sort => sort.storage === storage)
if (storageSort)
storageSort.sortBy = sortBy
else
sortOptions.push({ storage, sortBy })

this.setTable('sortOptions', sortOptions)
},
rename: (oldName: string, newName: string) => {
const sortOptions = this.getTable<SortStorage>('sortOptions')
const storageSort = sortOptions.find(sort => sort.storage === oldName)
if (storageSort)
storageSort.storage = newName

this.setTable('sortOptions', sortOptions)
},
}
}

export const db = new FoodInventoryDatabase()
export const localDb = new LocalStorageDatabase()
14 changes: 0 additions & 14 deletions src/lib/imageUtils.ts

This file was deleted.

Loading