refactor: unify asset sorting between AssetBrowser and FormDropdown

- Create shared sortAssets() utility in src/platform/assets/utils/assetSortUtils.ts
- Add 'default' option to AssetSortOption type for preserve-order sorting
- Extract shared filter types (OptionId, FilterOption, OwnershipOption, AssetSortOption) to src/platform/assets/types/filterTypes.ts
- Update useAssetBrowser.ts to use sortAssets() (removes inline switch statement)
- Update FormDropdown shared.ts to delegate to sortAssets()
- Make SortOption interface generic for type-safe sort IDs
- Add comprehensive tests for assetSortUtils

Amp-Thread-ID: https://ampcode.com/threads/T-019c10a1-1209-75bd-9c79-f312cee89f50
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Alexander Brown
2026-01-30 12:59:23 -08:00
parent b067bf733b
commit bbceecc94a
9 changed files with 276 additions and 65 deletions

View File

@@ -74,7 +74,7 @@ describe('getDefaultSortOptions', () => {
})
describe('A-Z sorter', () => {
const azSorter = sortOptions.find((o) => o.id === 'a-z')!.sorter
const azSorter = sortOptions.find((o) => o.id === 'name-asc')!.sorter
it('sorts items alphabetically by name', () => {
const items = [

View File

@@ -1,3 +1,6 @@
import type { AssetSortOption } from '@/platform/assets/types/filterTypes'
import { sortAssets } from '@/platform/assets/utils/assetSortUtils'
import type { DropdownItem, SortOption } from './types'
export async function defaultSearcher(query: string, items: DropdownItem[]) {
@@ -9,25 +12,23 @@ export async function defaultSearcher(query: string, items: DropdownItem[]) {
})
}
export function getDefaultSortOptions(): SortOption[] {
/**
* Create a SortOption that delegates to the shared sortAssets utility
*/
function createSortOption(
id: AssetSortOption,
name: string
): SortOption<AssetSortOption> {
return {
id,
name,
sorter: ({ items }) => sortAssets(items, id)
}
}
export function getDefaultSortOptions(): SortOption<AssetSortOption>[] {
return [
{
name: 'Default',
id: 'default',
sorter: ({ items }) => items.slice()
},
{
name: 'A-Z',
id: 'a-z',
sorter: ({ items }) =>
items.slice().sort((a, b) => {
const aLabel = a.label ?? a.name
const bLabel = b.label ?? b.name
return aLabel.localeCompare(bLabel, undefined, {
numeric: true,
sensitivity: 'base'
})
})
}
createSortOption('default', 'Default'),
createSortOption('name-asc', 'A-Z')
]
}

View File

@@ -1,8 +1,13 @@
import type { ComputedRef, InjectionKey } from 'vue'
import type {
FilterOption,
OptionId
} from '@/platform/assets/types/filterTypes'
import type { AssetKind } from '@/types/widgetTypes'
export type OptionId = string | number | symbol
export type { FilterOption, OptionId }
export type SelectedKey = OptionId
export interface DropdownItem {
@@ -12,17 +17,13 @@ export interface DropdownItem {
label?: string
metadata: string
}
export interface SortOption {
id: OptionId
export interface SortOption<TId extends OptionId = OptionId> {
id: TId
name: string
sorter: (ctx: { items: readonly DropdownItem[] }) => DropdownItem[]
}
export interface FilterOption {
id: OptionId
name: string
}
export type LayoutMode = 'list' | 'grid' | 'list-small'
export const AssetKindKey: InjectionKey<ComputedRef<AssetKind | undefined>> =