nuxt / ui

A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.
https://ui.nuxt.com
MIT License
4.08k stars 528 forks source link

Use of Vue SFC in the UTabs #item slot #645

Closed cantkillemall closed 7 months ago

cantkillemall commented 1 year ago

I'm trying to insert an SFC inside of a UTabs panel, but nothing shows up.

<template>
   <div class="p-6 pr-auto md:px-6 md:py-12 mx-auto w-full md:w-11/12 xl:w-4/5 max-w-7xl">
      <h4 class="uppercase text-center text-2xl md:text-3xl font-bold mb-8">
         À L'AFFICHE :
      </h4>
      <UTabs :items="items" class="w-full">
         <template #default="{ item, index, selected }">
            <div class="flex items-center gap-2 relative truncate">
               {{ item.label }}
            </div>
         </template>
         <template #item="{ item }">
            <NewsArticle :info="item" :is-on-front-page="true" />
            <!-- <pre>{{ item.content }}</pre> -->
         </template>
      </UTabs>
   </div>
</template>

<script lang="ts" setup>
import { Info } from "@/types/Info";

const { data, error, refresh } = await useFetch<GenericResponse<Info[]>>(`/api/news/frontPageNews`);

const items = data.value?.data.map((item) => {
  return {
    key: item.id,
    label: `${item.attributes?.artistName} : ${item.attributes?.title}`,
    content: item,
  };
});
</script>

1st: I'm transforming my response data into something compliant with the UTabs props. Just guessing based on the docs. I can pass the items into the slot correctly because when using a "pre" tag, all my data is displayed. Switching between tabs works as well, the data is correctly matching the Tab title (#default slot) Capture d’écran 2023-09-09 à 20 38 32

I'm using this rather complex SFC in other parts of my app and I don't want to repeat a few 100s lines. Is there something that I'm missing? Thank you

PS: I have a TS error on UTabs that I don't know how to fix,

Argument of type '{ items: { key: number; label: string; content: Info; }[] | undefined; class: string; }' is not assignable to parameter of type '{ ui?: any; modelValue?: number | undefined; items?: TabItem[] | undefined; orientation?: "horizontal" | "vertical" | undefined; defaultIndex?: number | undefined; ref?: VNodeRef | undefined; ... 12 more ...; "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined; } & Record<...>'.
benjamincanac commented 1 year ago

Would you mind providing a reproduction link on StackBlitz, it would be easier to help. There is an example of what you're trying to achieve here: https://github.com/nuxt/ui/blob/dev/docs/components/content/examples/TabsExampleItemSlot.vue

cantkillemall commented 1 year ago

Hi @benjamincanac,

I have dug much deeper on this recently. 1st, I sorted my stuff out, and now it works.

I am able to pass an entire SFC inside the body of the tab through the template #item="{ item }". There is a hiccup though: The Tab SFC accepts items as TabItem[] and TabItem has only these properties:

label: string
slot?: string
disabled?: boolean
content?: string

Why do we only limit content to be a string, where it can handle Objects? This works perfectly:

<UTabs :items="items">
    <template #default="{ item, index, selected }">
        <div>
            {{ item.label }}
        </div>
    </template>
    <template #item="{ item }">
        <NewsArticle :info="item.content" :is-on-front-page="true" /> // This is a Nuxt SFC!!
    </template>
</UTabs>

I have a single TS error in this file: Type '{ label: string; content: Info; }[]' is not assignable to type 'TabItem[]' Which can be fixed by enhancing TabItem with Generics instead, for example.

Capture d’écran 2023-09-30 à 17 20 51

cantkillemall commented 1 year ago

Update: I wanted to propose a PR with this, but I saw that @Haythamfpco pushed a commit to allow other custom KV pairs of type 'any'. Here's from a git clone of nuxt/ui:

export interface TabItem {
  label: string
  slot?: string
  disabled?: boolean
  content?: string
  [key: string]: any  // => shouldn't this KV pair be optional?
}

My idea we to propose this:

export interface TabItem<T = string> {
  label: string
  slot?: string
  disabled?: boolean
  content?: T
  [key: string]: any // this would become irrelevant and 'content' could be the entry point for anything into the slot?
}

This way, we keep the default type of 'content' but it gives us the opportunity to pass any other type. Does that make sense?

cantkillemall commented 1 year ago

Would you mind providing a reproduction link on StackBlitz, it would be easier to help. There is an example of what you're trying to achieve here: https://github.com/nuxt/ui/blob/dev/docs/components/content/examples/TabsExampleItemSlot.vue

I have achieved it by naming my property 'article' and dropping 'content' but as I said above, I'm losing the type inference in 'item.article' and becomes 'any' instead of my custom type 'News'

benjamincanac commented 8 months ago

Can this be closed? If the remaining subject is about generic components, it will be done in #1289.