Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Table): implement component #2364

Merged
merged 38 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c34ad10
feat(Table): implement component
benjamincanac Oct 11, 2024
25713a3
feat(InputMenu/RadioGroup/Select/SelectMenu): handle `labelKey` and u…
benjamincanac Oct 11, 2024
3e4d2d5
feat(Accordion/Breadcrumb/ContextMenu/DropdownMenu/NavigationMenu/Tab…
benjamincanac Oct 11, 2024
31b4c42
Merge branch 'v3' into feat/table
benjamincanac Oct 11, 2024
8941591
up
benjamincanac Oct 11, 2024
fbc3344
up
benjamincanac Oct 11, 2024
15b4949
feat(Accordion/Breadcrumb/ContextMenu/DropdownMenu/NavigationMenu/Tab…
benjamincanac Oct 11, 2024
416b515
Merge branch 'v3' into feat/table
benjamincanac Oct 11, 2024
ee1d464
fix conflicts
benjamincanac Oct 11, 2024
85429e5
up
benjamincanac Oct 11, 2024
c76bb19
up
benjamincanac Oct 11, 2024
9eb7427
Merge branch 'v3' into feat/table
benjamincanac Oct 11, 2024
ff50e6b
up
benjamincanac Oct 13, 2024
78ba7eb
Merge branch 'v3' into feat/table
benjamincanac Oct 14, 2024
3b7a4d9
up
benjamincanac Oct 14, 2024
0b0a4d9
up
benjamincanac Oct 14, 2024
d26a44c
up
benjamincanac Oct 14, 2024
b551830
Merge branch 'v3' into feat/table
benjamincanac Oct 14, 2024
354c5cb
Merge branch 'v3' into feat/table
benjamincanac Oct 15, 2024
3b98129
Merge branch 'v3' into feat/table
benjamincanac Oct 15, 2024
584dd8d
up
benjamincanac Oct 15, 2024
bf99ba0
up
benjamincanac Oct 15, 2024
07299d4
Merge branch 'v3' into feat/table
benjamincanac Oct 15, 2024
daa8405
up
benjamincanac Oct 16, 2024
900480d
up
benjamincanac Oct 16, 2024
470d9d6
up
benjamincanac Oct 17, 2024
69b3a65
Merge branch 'v3' into feat/table
benjamincanac Oct 17, 2024
3dda0a9
Merge branch 'v3' into feat/table
benjamincanac Oct 21, 2024
4d41100
Merge branch 'v3' into feat/table
benjamincanac Oct 22, 2024
cfebdf6
up
benjamincanac Oct 22, 2024
a9b373f
up
benjamincanac Oct 22, 2024
7c366c8
up
benjamincanac Oct 22, 2024
b06a7aa
Merge branch 'v3' into feat/table
benjamincanac Oct 22, 2024
c208c54
up
benjamincanac Oct 23, 2024
be64735
docs(ComponentCode/ComponentExample): handle `highlights` prop
benjamincanac Oct 23, 2024
1c0c4f5
up
benjamincanac Oct 23, 2024
e27d36f
up
benjamincanac Oct 23, 2024
c0b2241
up
benjamincanac Oct 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions docs/app/components/content/ComponentCode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ const props = defineProps<{
* @defaultValue false
*/
collapse?: boolean
/**
* A list of line numbers to highlight in the code block
*/
highlights?: number[]
}>()

const route = useRoute()
Expand Down Expand Up @@ -105,7 +109,7 @@ const code = computed(() => {
`
}

code += `\`\`\`vue`
code += `\`\`\`vue${props.highlights?.length ? ` {${props.highlights.join('-')}}` : ''}`

if (props.external?.length) {
code += `
Expand Down Expand Up @@ -201,7 +205,8 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
formatted = await $prettier.format(code.value, {
trailingComma: 'none',
semi: false,
singleQuote: true
singleQuote: true,
printWidth: 100
})
} catch {
formatted = code.value
Expand Down
15 changes: 9 additions & 6 deletions docs/app/components/content/ComponentExample.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,11 @@ const props = withDefaults(defineProps<{
* @defaultValue true
*/
preview?: boolean

/**
* Whether to show the source code
* @defaultValue true
*/
source?: boolean

/**
* A list of variable props to link to the component.
*/
Expand All @@ -40,6 +38,10 @@ const props = withDefaults(defineProps<{
default: any
multiple?: boolean
}>
/**
* A list of line numbers to highlight in the code block
*/
highlights?: number[]
}>(), {
preview: true,
source: true
Expand All @@ -65,7 +67,7 @@ const code = computed(() => {
`
}

code += `\`\`\`vue${props.preview ? '' : ` [${data.pascalName}.vue]`}
code += `\`\`\`vue ${props.preview ? '' : ` [${data.pascalName}.vue]`}${props.highlights?.length ? `{${props.highlights.join('-')}}` : ''}
${data?.code ?? ''}
\`\`\``

Expand All @@ -87,7 +89,8 @@ const { data: ast } = await useAsyncData(`component-example-${camelName}`, async
formatted = await $prettier.format(code.value, {
trailingComma: 'none',
semi: false,
singleQuote: true
singleQuote: true,
printWidth: 100
})
} catch {
formatted = code.value
Expand All @@ -113,7 +116,7 @@ const optionsValues = ref(props.options?.reduce((acc, option) => {

<template>
<div class="my-5">
<div v-if="preview">
<template v-if="preview">
<div class="border border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] relative z-[1]" :class="[{ 'border-b-0 rounded-t-[calc(var(--ui-radius)*1.5)]': props.source, 'rounded-[calc(var(--ui-radius)*1.5)]': !props.source }]">
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)]">
<slot name="options" />
Expand Down Expand Up @@ -170,7 +173,7 @@ const optionsValues = ref(props.options?.reduce((acc, option) => {
<component :is="camelName" v-bind="{ ...componentProps, ...optionsValues }" />
</div>
</div>
</div>
</template>

<MDCRenderer v-if="ast && props.source" :body="ast.body" :data="ast.data" class="[&_pre]:!rounded-t-none [&_div.my-5]:!mt-0" />
</div>
Expand Down
1 change: 1 addition & 0 deletions docs/app/components/content/ComponentProps.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const metaProps: ComputedRef<ComponentMeta['props']> = computed(() => {

<MDC v-if="prop.description" :value="prop.description" class="text-[var(--ui-text-toned)] mt-1" />

<ComponentPropsLinks v-if="prop.tags?.length" :prop="prop" />
<ComponentPropsSchema v-if="prop.schema" :prop="prop" :ignore="ignore" />
</ProseTd>
</ProseTr>
Expand Down
17 changes: 17 additions & 0 deletions docs/app/components/content/ComponentPropsLinks.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { PropertyMeta } from 'vue-component-meta'

const props = defineProps<{
prop: PropertyMeta
}>()

const links = computed(() => props.prop.tags?.filter((tag: any) => tag.name === 'link'))
</script>

<template>
<ProseUl v-if="links?.length">
<ProseLi v-for="link in links" :key="link.name">
<MDC :value="link.text ?? ''" class="my-1" />
</ProseLi>
</ProseUl>
</template>
8 changes: 8 additions & 0 deletions docs/app/components/content/ComponentTheme.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ function stripCompoundVariants(component?: any) {
}
}

if (compoundVariant.loadingColor) {
if (!['primary', 'neutral'].includes(compoundVariant.loadingColor)) {
strippedCompoundVariants.value = true

return false
}
}

return true
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<script setup lang="ts">
import { h, resolveComponent } from 'vue'
import type { TableColumn } from '@nuxt/ui'

const UBadge = resolveComponent('UBadge')

type Payment = {
id: string
date: string
status: 'paid' | 'failed' | 'refunded'
email: string
amount: number
}

const data = ref<Payment[]>([{
id: '4600',
date: '2024-03-11T15:30:00',
status: 'paid',
email: '[email protected]',
amount: 594
}, {
id: '4599',
date: '2024-03-11T10:10:00',
status: 'failed',
email: '[email protected]',
amount: 276
}, {
id: '4598',
date: '2024-03-11T08:50:00',
status: 'refunded',
email: '[email protected]',
amount: 315
}, {
id: '4597',
date: '2024-03-10T19:45:00',
status: 'paid',
email: '[email protected]',
amount: 529
}, {
id: '4596',
date: '2024-03-10T15:55:00',
status: 'paid',
email: '[email protected]',
amount: 639
}])

const columns: TableColumn<Payment>[] = [{
accessorKey: 'id',
header: '#',
cell: ({ row }) => `#${row.getValue('id')}`
}, {
accessorKey: 'date',
header: 'Date',
cell: ({ row }) => {
return new Date(row.getValue('date')).toLocaleString('en-US', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
}, {
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const color = ({
paid: 'success' as const,
failed: 'error' as const,
refunded: 'neutral' as const
})[row.getValue('status') as string]

return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
}
}, {
accessorKey: 'email',
header: 'Email'
}, {
accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'),
cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))

const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(amount)

return h('div', { class: 'text-right font-medium' }, formatted)
}
}]

const table = useTemplateRef('table')

const columnFilters = ref([{
id: 'email',
value: 'james'
}])
</script>

<template>
<div class="flex flex-col flex-1">
<div class="flex px-4 py-3.5 border-b border-[var(--ui-border-accented)]">
<UInput
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
class="max-w-sm"
placeholder="Filter emails..."
@update:model-value="table?.tableApi?.getColumn('email')?.setFilterValue($event)"
/>
</div>

<UTable
ref="table"
v-model:column-filters="columnFilters"
:data="data"
:columns="columns"
/>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<script setup lang="ts">
import { h, resolveComponent } from 'vue'
import type { TableColumn } from '@nuxt/ui'
import type { Column } from '@tanstack/vue-table'

const UBadge = resolveComponent('UBadge')
const UButton = resolveComponent('UButton')

type Payment = {
id: string
date: string
status: 'paid' | 'failed' | 'refunded'
email: string
amount: number
}

const data = ref<Payment[]>([{
id: '46000000000000000000000000000000000000000',
date: '2024-03-11T15:30:00',
status: 'paid',
email: '[email protected]',
amount: 594000
}, {
id: '45990000000000000000000000000000000000000',
date: '2024-03-11T10:10:00',
status: 'failed',
email: '[email protected]',
amount: 276000
}, {
id: '45980000000000000000000000000000000000000',
date: '2024-03-11T08:50:00',
status: 'refunded',
email: '[email protected]',
amount: 315000
}, {
id: '45970000000000000000000000000000000000000',
date: '2024-03-10T19:45:00',
status: 'paid',
email: '[email protected]',
amount: 5290000
}, {
id: '45960000000000000000000000000000000000000',
date: '2024-03-10T15:55:00',
status: 'paid',
email: '[email protected]',
amount: 639000
}])

const columns: TableColumn<Payment>[] = [{
accessorKey: 'id',
header: ({ column }) => getHeader(column, 'ID', 'left'),
cell: ({ row }) => `#${row.getValue('id')}`
}, {
accessorKey: 'date',
header: ({ column }) => getHeader(column, 'Date', 'left')
}, {
accessorKey: 'status',
header: ({ column }) => getHeader(column, 'Status', 'left'),
cell: ({ row }) => {
const color = ({
paid: 'success' as const,
failed: 'error' as const,
refunded: 'neutral' as const
})[row.getValue('status') as string]

return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
}
}, {
accessorKey: 'email',
header: ({ column }) => getHeader(column, 'Email', 'left')
}, {
accessorKey: 'amount',
header: ({ column }) => h('div', { class: 'text-right' }, getHeader(column, 'Amount', 'right')),
cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))

const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(amount)

return h('div', { class: 'text-right font-medium' }, formatted)
}
}]

function getHeader(column: Column<Payment>, label: string, position: 'left' | 'right') {
const isPinned = column.getIsPinned()

return h(UButton, {
color: 'neutral',
variant: 'ghost',
label,
icon: isPinned ? 'i-heroicons-star-20-solid' : 'i-heroicons-star',
class: '-mx-2.5',
onClick() {
column.pin(isPinned === position ? false : position)
}
})
}

const columnPinning = ref({
left: [],
right: ['amount']
})
</script>

<template>
<UTable
v-model:column-pinning="columnPinning"
:data="data"
:columns="columns"
class="flex-1"
/>
</template>
Loading