Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ascott18 committed Nov 27, 2024
1 parent c2acd3a commit 090526a
Show file tree
Hide file tree
Showing 13 changed files with 77 additions and 45 deletions.
10 changes: 5 additions & 5 deletions docs/stacks/vue/coalesce-vue-vuetify/components/c-select.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ A metadata specifier for the value being bound. One of:
- The name of a model type.
- A direct reference to a metadata object.

::: tip
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" />

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.
Expand All @@ -89,10 +93,6 @@ Enables multi-select functionality. Bindings for `modelValue`, `keyValue`, and `

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.

::: 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.
:::

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

Expand Down Expand Up @@ -171,4 +171,4 @@ createMethods = {

`#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: TModel, search: string, 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.
`#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.
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.

7 changes: 2 additions & 5 deletions playground/Coalesce.Web.Vue3/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +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="/examples">Examples</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
9 changes: 6 additions & 3 deletions playground/Coalesce.Web.Vue3/src/components/Examples.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
>
<v-list density="compact" style="width: 220px">
<template v-for="dir in pages">
<v-list-subheader
class="font-weight-bold text-uppercase ml-n2 mt-3"
>{{ dir.name }}</v-list-subheader
<div
class="font-weight-medium text-uppercase mt-3 mx-2 pt-3 pb-1"
style="border-top: 1px solid rgba(var(--v-theme-on-surface), 0.3)"
>
{{ dir.name }}
</div>
<v-list-item
v-for="page in dir.children"
:to="'/examples/' + page.path"
:title="page.name"
class="pl-8"
>
</v-list-item>
</template>
Expand Down
14 changes: 7 additions & 7 deletions playground/Coalesce.Web.Vue3/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,11 @@ new Worker(new URL("./worker.ts", import.meta.url));
AxiosClient.defaults.baseURL = "/api";
AxiosClient.defaults.withCredentials = true;

const examples = import.meta.glob('@/examples/**/*.vue')
const examples = import.meta.glob("@/examples/**/*.vue");

const router = createRouter({
history: createWebHistory(),
routes: [
{ path: "/", component: () => import("@/components/HelloWorld.vue") },
{ path: "/test", component: () => import("./components/test.vue") },
{
path: "/test-setup",
Expand All @@ -51,12 +50,13 @@ const router = createRouter({
props: { type: "AuditLog" },
},
{
path: "/examples",
path: "/examples",
alias: "/",
component: Examples,
children: Object.entries(examples).map(x => ({
path: x[0].replace('/src/examples/', '').replace('.vue', ''),
component: x[1]
}))
children: Object.entries(examples).map((x) => ({
path: x[0].replace("/src/examples/", "").replace(".vue", ""),
component: x[1],
})),
},
{
path: "/admin/:type",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function render() {
}
}
return h(CDisplay, { ...props });
return h(CDisplay<TModel>, { ...props });
}
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type MethodsOf<TModel> = TModel extends {
// prettier-ignore
export type ForSpec<
TModel extends ModelAllowedType | unknown = unknown,
ValueKind extends Value = Value
ValueKind extends Value | ClassType = Value | ClassType
> =
// Handle binding of `:model` to a Model or ViewModel:
TModel extends Model ?
Expand Down Expand Up @@ -112,7 +112,7 @@ TModel extends Model ?
)

// Fallback to allowing anything:
: undefined | string | ValueKind | (ValueKind extends ModelValue ? ModelType : never);
: undefined | string | ValueKind ;

export type MethodForSpec<
TModel extends
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<template>
<!-- TODO: Allow c-select to bind directly to ModelType metadata -->
<c-select
class="c-select-many-to-many"
v-bind="inputBindAttrs"
Expand Down
26 changes: 13 additions & 13 deletions src/coalesce-vue-vuetify3/src/components/input/c-select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@
<div class="v-field__input">
<span
class="v-autocomplete__selection"
v-for="item in internalModelValue"
v-for="(item, index) in internalModelValue"
:key="item[modelObjectMeta.keyProp.name]"
>
<slot
name="selected-item"
:item="item"
:search="search"
:index
:remove="() => onInput(item)"
>
<slot name="item" :item="item" :search="search">
Expand Down Expand Up @@ -273,7 +274,7 @@
TModel extends Model | AnyArgCaller | undefined,
TFor extends ForSpec<
TModel,
ForeignKeyProperty | ModelReferenceNavigationProperty | ModelValue | (ModelCollectionValue & {manyToMany: never})
ForeignKeyProperty | ModelReferenceNavigationProperty | ModelValue | (ModelCollectionValue & {manyToMany: never}) | ModelType
> = any,
TMultiple extends boolean = false"
>
Expand Down Expand Up @@ -405,6 +406,7 @@ defineSlots<{
["selected-item"]?(props: {
item: SelectedModelTypeSingle;
search: string | null;
index: number;
/** Remove/unselect the item. Only applicable for multiselect/multiple mode. */
remove: () => void;
}): any;
Expand Down Expand Up @@ -520,7 +522,7 @@ const pendingSelection = ref(0);
* in the case that only the PK was provided to the component.
*/
const internallyFetchedModels = new Map<
any,
SelectedPkTypeSingle,
WeakRef<SelectedModelTypeSingle>
>();
Expand Down Expand Up @@ -664,7 +666,7 @@ const internalModelValue = computed((): SelectedModelTypeSingle[] => {
// All we have is the PK. First, check if it is already in our item array.
// If so, capture it. If not, request the object from the server.
const item = items.value.filter(
(i) => key === (i as any)[modelObjectMeta.value.keyProp.name]
(i) => key === i[modelObjectMeta.value.keyProp.name]
)[0];
if (item) {
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
Expand Down Expand Up @@ -705,10 +707,10 @@ const internalModelValue = computed((): SelectedModelTypeSingle[] => {
});
/** The effective key (whose type is described by `modelObjectMeta`) that has been provided to the component. */
const internalKeyValue = computed((): SelectedPkType[] => {
let value: any;
const internalKeyValue = computed((): SelectedPkTypeSingle[] => {
let value: SelectedPkTypeSingle[];
if (props.keyValue) {
value = toArray(props.keyValue);
value = toArray(props.keyValue) as SelectedPkTypeSingle[];
} else if (valueOwner.value && modelKeyProp.value) {
value = toArray(valueOwner.value[modelKeyProp.value.name]);
} else if (props.modelValue && primaryBindKind.value == "key") {
Expand All @@ -719,9 +721,7 @@ const internalKeyValue = computed((): SelectedPkType[] => {
// Parse the values in case we were given a string instead of a number, or something like that, via the `keyValue` prop.
// This prevents `internalModelValue` from getting confused and infinitely calling the `getCaller`.
return value.map((v: any) =>
mapValueToModel(v, modelObjectMeta.value.keyProp)
);
return value.map((v) => mapValueToModel(v, modelObjectMeta.value.keyProp));
});
const selectedKeysSet = computed(
Expand Down Expand Up @@ -819,7 +819,7 @@ function onInput(value: SelectedModelTypeSingle | null, dontFocus = false) {
return;
}
const key = value ? (value as any)[modelObjectMeta.value.keyProp.name] : null;
const key = value ? value[modelObjectMeta.value.keyProp.name] : null;
let newKey, newObjectValue: any;
if (effectiveMultiple.value) {
Expand Down Expand Up @@ -1045,7 +1045,7 @@ const getCaller = new ModelApiClient<SelectedModelTypeSingle>(
function () {
throw "expected calls to be made with invokeWithArgs";
},
() => ({ ids: [] as any[] }),
() => ({ ids: [] as SelectedPkTypeSingle[] }),
(c, args) => {
if (!args.ids.length) return;
Expand Down Expand Up @@ -1135,7 +1135,7 @@ watch(pendingSelection, async () => {
});
});
watch(search, (newVal: any, oldVal: any) => {
watch(search, (newVal, oldVal) => {
searchChanged.value = new Date();
if (newVal != oldVal) {
listCaller();
Expand Down

0 comments on commit 090526a

Please sign in to comment.