4dae1569cd
* list invites table * invites list works * update last reminded date on resend * fix FE * WIP invitedialog + updated debounced utility * invite create works * exclude users correctly * more adjustments * minor cleanup * using workspace invite server role * test fix * fixed multiple root eslint issues * minor adjustments
194 lines
5.4 KiB
Vue
194 lines
5.4 KiB
Vue
<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
|
|
<template>
|
|
<div class="text-foreground">
|
|
<div
|
|
class="w-full text-sm overflow-x-auto overflow-y-visible simple-scrollbar border border-outline-3 rounded-lg"
|
|
>
|
|
<div
|
|
class="grid z-10 grid-cols-12 items-center space-x-6 font-medium bg-foundation-page rounded-t-lg w-full border-b border-outline-3 pb-2 pt-4 px-4 min-w-[750px]"
|
|
:style="{ paddingRight: paddingRightStyle }"
|
|
>
|
|
<div
|
|
v-for="(column, colIndex) in columns"
|
|
:key="column.id"
|
|
:class="getHeaderClasses(column.id, colIndex)"
|
|
>
|
|
{{ column.header }}
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="divide-y divide-outline-3 h-full overflow-visible"
|
|
:class="{ 'pb-32': overflowCells }"
|
|
>
|
|
<div
|
|
v-if="loading || !items"
|
|
tabindex="0"
|
|
:style="{ paddingRight: paddingRightStyle }"
|
|
:class="rowsWrapperClasses"
|
|
>
|
|
<div :class="getClasses(undefined, 0, { noPadding: true })" tabindex="0">
|
|
<CommonLoadingBar loading />
|
|
</div>
|
|
</div>
|
|
<template v-else-if="items?.length">
|
|
<div
|
|
v-for="item in items"
|
|
:key="item.id"
|
|
:style="{ paddingRight: paddingRightStyle }"
|
|
:class="rowsWrapperClasses"
|
|
tabindex="0"
|
|
@click="handleRowClick(item)"
|
|
@keypress="handleRowClick(item)"
|
|
>
|
|
<template v-for="(column, colIndex) in columns" :key="column.id">
|
|
<div :class="getClasses(column.id, colIndex)" tabindex="0">
|
|
<slot :name="column.id" :item="item">
|
|
<div class="text-gray-900 font-medium order-1">Placeholder</div>
|
|
</slot>
|
|
</div>
|
|
</template>
|
|
<div class="absolute right-1.5 space-x-1 flex items-center p-0 h-full">
|
|
<template v-if="buttons">
|
|
<div v-for="button in buttons" :key="button.label">
|
|
<FormButton
|
|
:icon-left="button.icon"
|
|
size="sm"
|
|
color="outline"
|
|
hide-text
|
|
:class="button.class"
|
|
:to="isString(button.action) ? button.action : undefined"
|
|
@click.stop="!isString(button.action) ? button.action(item) : noop"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div
|
|
v-else
|
|
tabindex="0"
|
|
:style="{ paddingRight: paddingRightStyle }"
|
|
:class="rowsWrapperClasses"
|
|
>
|
|
<div :class="getClasses(undefined, 0)" tabindex="0">
|
|
<slot name="empty">
|
|
<div class="w-full text-center label-light text-foreground-2 italic">
|
|
{{ emptyMessage }}
|
|
</div>
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script setup lang="ts" generic="T extends {id: string}, C extends string">
|
|
import { noop, isString } from 'lodash'
|
|
import { computed } from 'vue'
|
|
import type { PropAnyComponent } from '~~/src/helpers/common/components'
|
|
import { CommonLoadingBar, FormButton } from '~~/src/lib'
|
|
|
|
export type TableColumn<I> = {
|
|
id: I
|
|
header: string
|
|
classes: string
|
|
}
|
|
|
|
export type RowButton<T = unknown> = {
|
|
icon: PropAnyComponent
|
|
label: string
|
|
action: (item: T) => unknown
|
|
class?: string
|
|
}
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
items: T[] | undefined | null
|
|
buttons?: RowButton<T>[]
|
|
columns: TableColumn<C>[]
|
|
overflowCells?: boolean
|
|
onRowClick?: (item: T) => void
|
|
rowItemsAlign?: 'center' | 'stretch'
|
|
emptyMessage?: string
|
|
loading?: boolean
|
|
}>(),
|
|
{ rowItemsAlign: 'center', emptyMessage: 'No data found' }
|
|
)
|
|
|
|
const buttonCount = computed(() => {
|
|
return (props.buttons || []).length
|
|
})
|
|
const paddingRightStyle = computed(() => {
|
|
let padding = 16
|
|
if (buttonCount.value > 0) {
|
|
padding = 48 + (buttonCount.value - 1) * 42
|
|
}
|
|
return `${padding}px`
|
|
})
|
|
|
|
const rowsWrapperClasses = computed(() => {
|
|
const classParts = [
|
|
'relative grid grid-cols-12 items-center space-x-6 px-4 py-0.5 min-w-[750px] bg-foundation text-body-xs'
|
|
]
|
|
|
|
if (props.onRowClick && props.items?.length) {
|
|
classParts.push('cursor-pointer hover:bg-primary-muted')
|
|
}
|
|
|
|
switch (props.rowItemsAlign) {
|
|
case 'center':
|
|
classParts.push('items-center')
|
|
break
|
|
case 'stretch':
|
|
classParts.push('items-stretch')
|
|
break
|
|
}
|
|
|
|
return classParts.join(' ')
|
|
})
|
|
|
|
const getHeaderClasses = (
|
|
column: C | undefined,
|
|
colIndex: number,
|
|
options?: Partial<{
|
|
noPadding: boolean
|
|
}>
|
|
): string => {
|
|
const classParts = [
|
|
column ? props.columns.find((c) => c.id === column)?.classes : '' || ''
|
|
]
|
|
|
|
if (!options?.noPadding) {
|
|
if (colIndex === 0) {
|
|
classParts.push('px-1')
|
|
} else {
|
|
classParts.push('lg:p-0 px-1')
|
|
}
|
|
}
|
|
|
|
return classParts.join(' ')
|
|
}
|
|
|
|
const getClasses = (
|
|
column: C | undefined,
|
|
colIndex: number,
|
|
options?: Partial<{
|
|
noPadding: boolean
|
|
}>
|
|
): string => {
|
|
const classParts = [getHeaderClasses(column, colIndex, options)]
|
|
|
|
if (colIndex === 0) {
|
|
classParts.push(`bg-transparent py-2 ${column ? 'pr-5' : 'col-span-full'}`)
|
|
} else {
|
|
classParts.push(`my-2`)
|
|
}
|
|
|
|
return classParts.join(' ')
|
|
}
|
|
|
|
const handleRowClick = (item: T) => {
|
|
props.onRowClick?.(item)
|
|
}
|
|
</script>
|