|
| 1 | +# `useCollection` composable |
| 2 | + |
| 3 | +The `useCollection` composable helps you manage a collection of items with flags (boolean states) and computed properties. It provides a type-safe way to filter, flag, and manipulate items in a collection. |
| 4 | + |
| 5 | +## Usage |
| 6 | + |
| 7 | +```typescript |
| 8 | +const { items, useSubset, useFlag } = useCollection(sources, { |
| 9 | + identifier: source => source.id, |
| 10 | + flags: 'selected', |
| 11 | + properties: source => ({ |
| 12 | + isAvailable: computed(() => source.status === 'available'), |
| 13 | + fullName: computed(() => `${source.firstName} ${source.lastName}`), |
| 14 | + }), |
| 15 | +}) |
| 16 | +``` |
| 17 | + |
| 18 | +## Core Concepts |
| 19 | + |
| 20 | +- **Collection Item**: An object with a unique identifier, a reference to its source object, flags, computed properties, and methods to manipulate flags |
| 21 | +- **Flags**: Boolean states attached to items (like 'selected', 'active', 'highlighted') |
| 22 | +- **Properties**: Computed values derived from the source object |
| 23 | + |
| 24 | +## `useCollection` parameters |
| 25 | + |
| 26 | +| Name | Type | Required | Description | |
| 27 | +| --------- | --------------------------------- | :------: | ---------------------------------------------------- | |
| 28 | +| `sources` | `MaybeRefOrGetter<TSource[]>` | ✓ | Array of source objects for the collection | |
| 29 | +| `options` | `CollectionOptions<TSource, TId>` | ✓ | Configuration options for the collection (see below) | |
| 30 | + |
| 31 | +### `options` object |
| 32 | + |
| 33 | +| Name | Type | Required | Description | |
| 34 | +| ------------ | -------------------------------------------------- | :------: | ----------------------------------------------------------------------- | |
| 35 | +| `identifier` | `(source: TSource) => TId` | ✓ | Function to extract a unique identifier from each source | |
| 36 | +| `flags` | `MaybeArray<string \| Record<string, FlagConfig>>` | | Flags that can be applied to items in the collection | |
| 37 | +| `properties` | `(source: TSource) => Record<string, ComputedRef>` | | Function that returns computed properties for each item | |
| 38 | +| `context` | `Reactive<{ flags, registeredFlags }>` | | Shared context for multiple collections (usually handled automatically) | |
| 39 | + |
| 40 | +### `FlagConfig` object |
| 41 | + |
| 42 | +| Name | Type | Default | Description | |
| 43 | +| ---------- | --------- | ------- | ------------------------------------------------------------------------ | |
| 44 | +| `multiple` | `boolean` | `true` | Whether multiple items can have this flag set (false = single selection) | |
| 45 | +| `default` | `boolean` | `false` | Default value for the flag when not explicitly set | |
| 46 | + |
| 47 | +## Return Value |
| 48 | + |
| 49 | +| Name | Type | Description | |
| 50 | +| ----------- | ------------------------------------------- | ----------------------------------------------------------------------------- | |
| 51 | +| `items` | `ComputedRef<CollectionItem[]>` | Array of collection items with flags and properties | |
| 52 | +| `useSubset` | `(filter: (item) => boolean) => Collection` | Creates a new collection that's a subset of the original, with shared context | |
| 53 | +| `useFlag` | `(flag: TFlag) => object` | Utilities for working with a specific flag (see below) | |
| 54 | + |
| 55 | +### `CollectionItem` object |
| 56 | + |
| 57 | +| Name | Type | Description | |
| 58 | +| ------------ | ------------------------------ | ------------------------------------------------------- | |
| 59 | +| `id` | `TId` | Unique identifier for the item | |
| 60 | +| `source` | `TSource` | The original source object | |
| 61 | +| `flags` | `Record<TFlag, boolean>` | Object containing the state of all flags for this item | |
| 62 | +| `properties` | `TProperties` | Object containing all computed properties for this item | |
| 63 | +| `toggleFlag` | `(flag, forcedValue?) => void` | Method to toggle a flag on this item | |
| 64 | + |
| 65 | +### Return value of `useFlag` |
| 66 | + |
| 67 | +| Name | Type | Description | |
| 68 | +| ----------- | ------------------------------- | ------------------------------------------------------ | |
| 69 | +| `items` | `ComputedRef<CollectionItem[]>` | Array of items that have this flag set | |
| 70 | +| `ids` | `ComputedRef<TId[]>` | Array of IDs of items that have this flag set | |
| 71 | +| `count` | `ComputedRef<number>` | Number of items that have this flag set | |
| 72 | +| `areAllOn` | `ComputedRef<boolean>` | Whether all items in the collection have this flag set | |
| 73 | +| `areSomeOn` | `ComputedRef<boolean>` | Whether at least one item has this flag set | |
| 74 | +| `areNoneOn` | `ComputedRef<boolean>` | Whether no items have this flag set | |
| 75 | +| `toggle` | `(forcedValue?) => void` | Toggle this flag on all items in the collection | |
| 76 | + |
| 77 | +## Examples |
| 78 | + |
| 79 | +### Basic Usage |
| 80 | + |
| 81 | +```typescript |
| 82 | +// Source type |
| 83 | +interface User { |
| 84 | + id: string |
| 85 | + firstName: string |
| 86 | + lastName: string |
| 87 | + status: 'active' | 'inactive' |
| 88 | + role: 'admin' | 'user' |
| 89 | +} |
| 90 | + |
| 91 | +// Source data |
| 92 | +const users = ref<User[]>([ |
| 93 | + { |
| 94 | + id: '1', |
| 95 | + firstName: 'John', |
| 96 | + lastName: 'Doe', |
| 97 | + status: 'active', |
| 98 | + role: 'admin', |
| 99 | + }, |
| 100 | + { |
| 101 | + id: '2', |
| 102 | + firstName: 'Jane', |
| 103 | + lastName: 'Smith', |
| 104 | + status: 'active', |
| 105 | + role: 'user', |
| 106 | + }, |
| 107 | + { |
| 108 | + id: '3', |
| 109 | + firstName: 'Bob', |
| 110 | + lastName: 'Johnson', |
| 111 | + status: 'inactive', |
| 112 | + role: 'user', |
| 113 | + }, |
| 114 | +]) |
| 115 | + |
| 116 | +// Create a collection |
| 117 | +const { items: userItems, useFlag } = useCollection(users, { |
| 118 | + identifier: user => user.id, |
| 119 | + flags: ['selected', 'active'], |
| 120 | + properties: user => ({ |
| 121 | + fullName: computed(() => `${user.firstName} ${user.lastName}`), |
| 122 | + isAdmin: computed(() => user.role === 'admin'), |
| 123 | + }), |
| 124 | +}) |
| 125 | + |
| 126 | +// Work with a specific flag |
| 127 | +const { areAllOn: areAllUsersSelected, count: selectedUsersCount, toggle: toggleAllSelectedUsers } = useFlag('selected') |
| 128 | +``` |
| 129 | + |
| 130 | +```vue |
| 131 | +<!-- Basic usage in a template --> |
| 132 | +<template> |
| 133 | + <div> |
| 134 | + <div>Selected: {{ selectedUsersCount }}</div> |
| 135 | + <div>All selected: {{ areAllUsersSelected }}</div> |
| 136 | + <button @click="toggleAllSelectedUsers()">Toggle All</button> |
| 137 | +
|
| 138 | + <ul> |
| 139 | + <li v-for="user in userItems" :key="user.id"> |
| 140 | + <input type="checkbox" v-model="user.flags.selected" /> |
| 141 | + {{ user.properties.fullName }} |
| 142 | + <span v-if="user.properties.isAdmin">(Admin)</span> |
| 143 | + </li> |
| 144 | + </ul> |
| 145 | + </div> |
| 146 | +</template> |
| 147 | +``` |
| 148 | + |
| 149 | +### Using Subsets |
| 150 | + |
| 151 | +```typescript |
| 152 | +const { items: userItems, useSubset } = useCollection(users, { |
| 153 | + identifier: user => user.id, |
| 154 | + flags: ['selected'], |
| 155 | + properties: user => ({ |
| 156 | + isActive: computed(() => user.status === 'active'), |
| 157 | + }), |
| 158 | +}) |
| 159 | + |
| 160 | +// Create a subset of only active users |
| 161 | +const { items: activeUserItems, useFlag: useActiveUsersFlag } = useSubset(item => item.properties.isActive) |
| 162 | + |
| 163 | +// Work with a specific flag on the subset |
| 164 | +const { count: selectedActiveUsersCount } = useActiveUsersFlag('selected') |
| 165 | + |
| 166 | +// Now you can work with just the active users |
| 167 | +console.log(`${activeUserItems.value.length} active users`) |
| 168 | +console.log(`${selectedActiveUsersCount.value} selected active users`) |
| 169 | +``` |
| 170 | + |
| 171 | +### Exclusive Selection (Radio Button Behavior) |
| 172 | + |
| 173 | +```typescript |
| 174 | +const { items } = useCollection(users, { |
| 175 | + identifier: user => user.id, |
| 176 | + flags: { |
| 177 | + current: { multiple: false }, |
| 178 | + }, |
| 179 | +}) |
| 180 | +``` |
| 181 | + |
| 182 | +When an item activate its `current` flag, it is deactivated on every other items |
| 183 | + |
| 184 | +```vue |
| 185 | +<template> |
| 186 | + <MyComponent v-for="item in items" :key="item.id" :current="item.flags.current" @click="items.flags.current = true"> |
| 187 | + {{ item.source.firstName }} |
| 188 | + </MyComponent> |
| 189 | +</template> |
| 190 | +``` |
0 commit comments