Skip to content

Commit

Permalink
feat: add multiple support to c-select. (#499)
Browse files Browse the repository at this point in the history
  • Loading branch information
ascott18 authored Dec 2, 2024
1 parent 2099985 commit 0361c43
Show file tree
Hide file tree
Showing 34 changed files with 1,621 additions and 808 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 5.3.0

- Added `multiple` prop to `c-select`, allowing for the selection of multiple models.
- `c-select-many-to-many` is now based on `c-select` rather than `v-autocomplete`. As a result, it has gained support for all of the props and slots of `c-select`.

# 5.2.1

## Fixes
Expand Down
1 change: 1 addition & 0 deletions docs/stacks/vue/coalesce-vue-vuetify/components/c-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ A summary of the components delegated to, by type:
- date: [c-datetime-picker](/stacks/vue/coalesce-vue-vuetify/components/c-datetime-picker.md)
- model: [c-select](/stacks/vue/coalesce-vue-vuetify/components/c-select.md)
- [[ManyToMany]](/modeling/model-components/attributes/many-to-many.md) collection: [c-select-many-to-many](/stacks/vue/coalesce-vue-vuetify/components/c-select-many-to-many.md)
- Model collection (not many-to-many): [c-select](/stacks/vue/coalesce-vue-vuetify/components/c-select.md)
- Non-object collection: [c-select-values](/stacks/vue/coalesce-vue-vuetify/components/c-select-values.md)

Any other unsupported type will simply be displayed with [c-display](/stacks/vue/coalesce-vue-vuetify/components/c-display.md), unless a [default slot](https://vuejs.org/guide/components/slots.html) is provided - in that case, the default slot will be rendered instead.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,50 +19,17 @@ It is unlikely that you'll ever need to use this component directly - it is high
``` vue-html
<c-select-many-to-many
:model="case"
for="caseProducts"
dense
outlined
/>
```

``` vue-html
<c-select-many-to-many
v-model="case.caseProducts"
for="Case.caseProducts"
for="caseProducts"
variant="outlined"
density="compact"
/>
```

## Props

<Prop def="for: string | Property | Value" lang="ts" />

A metadata specifier for the value being bound. One of:

- A string with the name of the value belonging to `model`.
- A direct reference to a metadata object.
- A string in dot-notation that starts with a type name.

::: tip Note
c-select-many-to-many expects metadata for the "real" collection navigation property on a model. If you provide it the string you passed to [[ManyToMany]](/modeling/model-components/attributes/many-to-many.md), an error wil be thrown.
:::
See [c-select / Props](./c-select.md#props).

<Prop def="model?: Model" lang="ts" />

An object owning the value that was specified by the `for` prop. If provided, the input will be bound to the corresponding property on the `model` object.

<Prop def="value?: any // Vue 2
modelValue?: any // Vue 3" lang="ts" />

If binding the component with ``v-model``, accepts the ``value`` part of ``v-model``.

<Prop def="params?: ListParameters" lang="ts" />

An optional set of [Data Source Standard Parameters](/modeling/model-components/data-sources.md#standard-parameters) to pass to API calls made to the server.

<Prop def="cache?: ResponseCachingConfiguration | boolean" lang="ts" />

If provided and non-false, enables [response caching](/stacks/vue/layers/api-clients.md#response-caching) on the component's internal API caller.

Since `c-select-many-to-many` internally uses `c-select` as its implementation, all props of `c-select` are also supported by `c-select-many-to-many`.

## Events

Expand Down
36 changes: 27 additions & 9 deletions docs/stacks/vue/coalesce-vue-vuetify/components/c-select.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ Binding an arbitrary primary key value or an arbitrary object:
<c-select for="Person" v-model="selectedPerson" />
```

Multi-select:

``` vue-html
<!-- Binding selected primary keys: -->
<c-select for="Person" multiple v-model:key-value="selectedPeopleIds" />
<!-- Binding selected objects: -->
<c-select for="Person" multiple v-model="selectedPeople" />
<c-select for="Person" multiple v-model:object-value="selectedPeople" />
```

Examples of other props:

``` vue-html
Expand All @@ -48,17 +59,18 @@ Examples of other props:

## Props

Note: In addition to the below props, `c-select` also supports most props that are supported by Vuetify's [v-text-field](https://vuetifyjs.com/en/components/text-fields/).

<Prop def="for: string | ForeignKeyProperty | ModelReferenceNavigationProperty | ModelType" lang="ts" />

A metadata specifier for the value being bound. One of:

- The name of a foreign key or reference navigation property belonging to `model`.
- The name of a model type.
- A direct reference to a metadata object.
- A string in dot-notation that starts with a type name that resolves to a foreign key or reference navigation property.

::: tip
When binding by a key value, if the corresponding object cannot be found (e.g. there is no navigation property, or the navigation property is null), c-select will automatically attempt to load the object from the server so it can be displayed in the UI.
When the binding can only locate a PK value and the corresponding object cannot be found (e.g. there is no navigation property, or the navigation property is null), c-select will automatically attempt to load the object from the server so it can be displayed in the UI.
:::

<Prop def="model?: Model" lang="ts" />
Expand All @@ -72,13 +84,19 @@ modelValue?: any // Vue 3" lang="ts" />

When binding the component with ``v-model``, accepts the ``value`` part of ``v-model``. If `for` was specified as a foreign key, this will expect a key; likewise, if `for` was specified as a type or as a navigation property, this will expect an object.

<Prop def="keyValue?: any" lang="ts" />
<Prop def="multiple?: boolean" lang="ts" />

Enables multi-select functionality. Bindings for `modelValue`, `keyValue`, and `objectValue` will accept and emit arrays instead of single values.

<Prop def="keyValue?: TKey
'onUpdate:keyValue': (value: TKey) => void" lang="ts" />

When bound with `v-model:key-value="keyValue"`, allows binding the primary key of the selected object explicitly.
When bound with `v-model:key-value="keyValue"`, allows binding the primary key of the selected object explicitly. Binds an array when in multi-select mode.

<Prop def="objectValue?: any" lang="ts" />
<Prop def="objectValue?: TModel
'onUpdate:objectValue': (value: TModel) => void" lang="ts" />

When bound with `v-model:object-value="objectValue"`, allows binding the selected object explicitly.
When bound with `v-model:object-value="objectValue"`, allows binding the selected object explicitly. Binds an array when in multi-select mode.

<Prop def="clearable?: boolean" lang="ts" />

Expand Down Expand Up @@ -149,8 +167,8 @@ createMethods = {

## Slots

`#item="{ item, search }"` - Slot used to customize the text of both items inside the list, as well as the text of selected items. By default, items are rendered with [c-display](/stacks/vue/coalesce-vue-vuetify/components/c-display.md). Slot is passed a parameter `item` containing a [model instance](/stacks/vue/layers/models.md), and `search` containing the current search query.
`#item="{ item: TModel, search: string }"` - Slot used to customize the text of both items inside the list, as well as the text of selected items. By default, items are rendered with [c-display](/stacks/vue/coalesce-vue-vuetify/components/c-display.md). Slot is passed a parameter `item` containing a [model instance](/stacks/vue/layers/models.md), and `search` containing the current search query.

`#list-item="{ item, search }"` - Slot used to customize the text of items inside the list. If not provided, falls back to the `item` slot.
`#list-item="{ item: TModel, search: string, selected: boolean }"` - Slot used to customize the text of items inside the list. If not provided, falls back to the `item` slot. Contents are wrapped in a `v-list-item-title`.

`#selected-item="{ item, search }"` - Slot used to customize the text of selected items. If not provided, falls back to the `item` slot.
`#selected-item="{ item: TModel, search: string, index: number, remove: () => void }"` - Slot used to customize the text of selected items. If not provided, falls back to the `item` slot. The `remove` function will deselect the item and is only valid when using multi-select.
9 changes: 9 additions & 0 deletions playground/Coalesce.Domain/Case.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ public override IQueryable<Case> GetQuery(IDataSourceParameters parameters) => D
.IncludeChildren();
}

public class MissingManyToManyFarSide(CrudContext<AppDbContext> context) : StandardDataSource<Case, AppDbContext>(context)
{
public override IQueryable<Case> GetQuery(IDataSourceParameters parameters) => Db.Cases
.Include(c => c.CaseProducts)
.Include(c => c.AssignedTo)
.Include(c => c.ReportedBy);
}


/// <summary>
/// Returns a list of summary information about Cases
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion playground/Coalesce.Domain/Person.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ public static string[] MethodWithStringArrayParameter(AppDbContext db, string[]
}

[Coalesce, Execute]
public static Person MethodWithEntityParameter(AppDbContext db, Person person)
public static Person MethodWithEntityParameter(AppDbContext db, Person person, Person[] people)
{
return person;
}
Expand Down
13 changes: 9 additions & 4 deletions playground/Coalesce.Web.Vue2/Api/Generated/PersonController.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion playground/Coalesce.Web.Vue2/src/api-clients.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions playground/Coalesce.Web.Vue2/src/metadata.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions playground/Coalesce.Web.Vue2/src/models.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions playground/Coalesce.Web.Vue2/src/viewmodels.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions playground/Coalesce.Web.Vue3/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.tsdk": "node_modules\\typescript\\lib",
"dotnet.defaultSolution": "Coalesce.Web.Vue3.sln"
}
13 changes: 9 additions & 4 deletions playground/Coalesce.Web.Vue3/Api/Generated/PersonController.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 4 additions & 8 deletions playground/Coalesce.Web.Vue3/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
label="Dark Mode"
v-model="darkMode"
hide-details
class="ml-2"
class="mx-3"
density="compact"
/>

<v-btn variant="text" to="/">Home</v-btn>
<v-btn variant="text" to="/test">Test</v-btn>
<v-btn variant="text" to="/test-setup">Test2</v-btn>
<v-btn variant="text" to="/examples">Examples/Tests</v-btn>
<v-btn variant="text" to="/audit-logs">Audit</v-btn>
<v-btn variant="text" href="/swagger">Swagger</v-btn>
<v-btn variant="text" href="/scalar/v1">OpenAPI</v-btn>
Expand Down Expand Up @@ -46,10 +44,8 @@

<v-main>
<router-view v-slot="{ Component }">
<transition name="router-transition" mode="out-in" appear>
<!-- https://stackoverflow.com/questions/52847979/what-is-router-view-key-route-fullpath -->
<component ref="routerView" :is="Component" :key="$route.path" />
</transition>
<!-- https://stackoverflow.com/questions/52847979/what-is-router-view-key-route-fullpath -->
<component ref="routerView" :is="Component" :key="$route.path" />
</router-view>
</v-main>
</v-app>
Expand Down
3 changes: 2 additions & 1 deletion playground/Coalesce.Web.Vue3/src/api-clients.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0361c43

Please sign in to comment.