Skip to content

Commit

Permalink
feat(Table): implement component (#2364)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamincanac authored Oct 23, 2024
1 parent 34bddd4 commit b54950e
Show file tree
Hide file tree
Showing 40 changed files with 4,000 additions and 62 deletions.
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

0 comments on commit b54950e

Please sign in to comment.