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

[Chat] Add Resizable chat #22

Merged
merged 7 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "shadcn-ui-vue-admin",
"description": "Admin Dashboard UI crafted with Shadcn ui and Vite and Vue. Built with responsiveness and accessibility in mind.",
"private": true,
"version": "2024.0.3",
"version": "2024.0.4",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build && cp CNAME dist/",
Expand All @@ -12,6 +12,7 @@
"@radix-icons/vue": "^1.0.0",
"@vee-validate/zod": "^4.12.6",
"@vueuse/core": "^10.9.0",
"@vueuse/motion": "^2.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"lodash": "^4.17.21",
Expand Down
26 changes: 26 additions & 0 deletions src/components/ui/resizable/ResizableHandle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { SplitterResizeHandle, type SplitterResizeHandleEmits, type SplitterResizeHandleProps, useForwardPropsEmits } from 'radix-vue'
import { DragHandleDots2Icon } from '@radix-icons/vue'
import { cn } from '@/lib/utils'

const props = defineProps<SplitterResizeHandleProps & { class?: HTMLAttributes['class'], withHandle?: boolean }>()
const emits = defineEmits<SplitterResizeHandleEmits>()

const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})

const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
<SplitterResizeHandle v-bind="forwarded" :class="cn('relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 [&[data-orientation=vertical]]:h-px [&[data-orientation=vertical]]:w-full [&[data-orientation=vertical]]:after:left-0 [&[data-orientation=vertical]]:after:h-1 [&[data-orientation=vertical]]:after:w-full [&[data-orientation=vertical]]:after:-translate-y-1/2 [&[data-orientation=vertical]]:after:translate-x-0 [&[data-orientation=vertical]>div]:rotate-90', props.class)">
<template v-if="props.withHandle">
<div class="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
<DragHandleDots2Icon class="h-2.5 w-2.5" />
</div>
</template>
</SplitterResizeHandle>
</template>
21 changes: 21 additions & 0 deletions src/components/ui/resizable/ResizablePanelGroup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { SplitterGroup, type SplitterGroupEmits, type SplitterGroupProps, useForwardPropsEmits } from 'radix-vue'
import { cn } from '@/lib/utils'

const props = defineProps<SplitterGroupProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<SplitterGroupEmits>()

const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})

const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
<SplitterGroup v-bind="forwarded" :class="cn('flex h-full w-full data-[panel-group-direction=vertical]:flex-col', props.class)">
<slot />
</SplitterGroup>
</template>
3 changes: 3 additions & 0 deletions src/components/ui/resizable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as ResizablePanelGroup } from './ResizablePanelGroup.vue'
export { default as ResizableHandle } from './ResizableHandle.vue'
export { SplitterPanel as ResizablePanel } from 'radix-vue'
17 changes: 10 additions & 7 deletions src/data/Navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const createNavigation = (): void => {

const datacap = createNavigationItem('common.common.datacap',
undefined,
'https://datacap.edurt.io',
'https://datacap.devlive.org',
undefined,
NavigationPosition.TOP,
undefined
Expand All @@ -46,16 +46,13 @@ const createNavigation = (): void => {
formArray, undefined, 'common.common.form')
NavigationService.addNavigation(froms)

const cardHome = createNavigationItem('card.common.default', undefined, '/card/index', undefined, NavigationPosition.LEFT_TOP)
const cardWithGit = createNavigationItem('card.common.cardWithGit', undefined, '/card/git', undefined, NavigationPosition.LEFT_TOP)
const cardWithTeam = createNavigationItem('card.common.cardWithTeam', undefined, '/card/team', undefined, NavigationPosition.LEFT_TOP)
const cardHome = createNavigationItem('card.common.default', undefined, '/cards/index', undefined, NavigationPosition.LEFT_TOP)
const cardWithGit = createNavigationItem('card.common.cardWithGit', undefined, '/cards/git', undefined, NavigationPosition.LEFT_TOP)
const cardWithTeam = createNavigationItem('card.common.cardWithTeam', undefined, '/cards/team', undefined, NavigationPosition.LEFT_TOP)
const cardArray = [cardHome, cardWithGit, cardWithTeam]
const cards = createNavigationItem('card.common.default', cardArray.length.toString(), '/card/index', CreditCard, NavigationPosition.LEFT_TOP, cardArray)
NavigationService.addNavigation(cards)

const chat = createNavigationItem('common.common.chat', undefined, '/chat', MessageCircle, NavigationPosition.LEFT_TOP)
NavigationService.addNavigation(chat)

const page404 = createNavigationItem('common.common.page404', undefined, '/common/404', Ban, NavigationPosition.LEFT_TOP)
const page403 = createNavigationItem('common.common.page403', undefined, '/common/403', CircleOff, NavigationPosition.LEFT_TOP)
const pageArray = [page404, page403]
Expand All @@ -69,6 +66,12 @@ const createNavigation = (): void => {
const components = createNavigationItem('common.common.component', componentArray.length.toString(), '/components', Command, NavigationPosition.LEFT_TOP, componentArray)
NavigationService.addNavigation(components)

const chat = createNavigationItem('common.common.chat', undefined, '/chats/basic', MessageCircle, NavigationPosition.LEFT_TOP)
const resizable = createNavigationItem('common.common.resizable', undefined, '/chats/resizable', MessageCircle, NavigationPosition.LEFT_TOP)
const chatArray = [chat, resizable]
const chats = createNavigationItem('common.common.chat', chatArray.length.toString(), '/chats', MessageCircle, NavigationPosition.LEFT_TOP, chatArray)
NavigationService.addNavigation(chats)

const external = createNavigationItem('common.common.externalLink', undefined, 'https://datacap.devlive.org', Link, NavigationPosition.LEFT_TOP)
external.external = true
NavigationService.addNavigation(external)
Expand Down
1 change: 1 addition & 0 deletions src/i18n/langs/en/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default {
type: 'Type',
icon: 'Icon',
card: 'Card',
resizable: 'Resizable',
},
tip: {
signInInfo: 'Enter your information to sign in to your account.',
Expand Down
1 change: 1 addition & 0 deletions src/i18n/langs/zhCn/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default {
type: '类型',
icon: '图标',
card: '卡片',
resizable: '可调整大小',
},
tip: {
signInInfo: '输入您的信息以登录您的帐户。',
Expand Down
3 changes: 3 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import './assets/index.css'
// Import mock data
import '@/data/index'

import { MotionPlugin } from '@vueuse/motion'

const app = createApp(App)
app.use(router)
app.use(i18n)
app.use(MotionPlugin)
app.provide('$t', i18n.global.t)
// Setting moment
app.config.globalProperties.$dt = moment
Expand Down
73 changes: 47 additions & 26 deletions src/router/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,14 @@ const createDefaultRouter = (router: Router): void => {
name: 'dashboard',
path: 'dashboard',
component: () => import('@/views/pages/dashboard/DashboardHome.vue')
},
{
name: 'card',
path: 'card',
children: [
{
name: 'index',
path: 'index',
component: () => import('@/views/pages/card/CardHome.vue')
},
{
name: 'git',
path: 'git',
component: () => import('@/views/pages/card/CardWithGit.vue')
},
{
name: 'team',
path: 'team',
component: () => import('@/views/pages/card/CardWithTeam.vue')
}
]
},
{
name: 'chat',
path: 'chat',
component: () => import('@/views/pages/chat/ChatHome.vue')
}
]
})
createHttpRouter(router)
createUserRouter(router)
createFormRouter(router)
createCardRouter(router)
createChatRouter(router)
createComponentRouter(router)
}

Expand Down Expand Up @@ -138,6 +114,51 @@ const createFormRouter = (router: Router): void => {
})
}

const createCardRouter = (router: Router): void => {
router.addRoute({
path: '/cards',
name: 'cardExample',
component: LayoutContainer,
children: [
{
name: 'index',
path: 'index',
component: () => import('@/views/pages/card/CardHome.vue')
},
{
name: 'git',
path: 'git',
component: () => import('@/views/pages/card/CardWithGit.vue')
},
{
name: 'team',
path: 'team',
component: () => import('@/views/pages/card/CardWithTeam.vue')
}
]
})
}

const createChatRouter = (router: Router): void => {
router.addRoute({
path: '/chats',
name: 'chat',
component: LayoutContainer,
children: [
{
name: 'chatBasic',
path: 'basic',
component: () => import('@/views/pages/chat/basic/ChatHome.vue')
},
{
name: 'chatResizable',
path: 'resizable',
component: () => import('@/views/pages/chat/resizable/ResizableChatHome.vue')
}
]
})
}

const createComponentRouter = (router: Router): void => {
router.addRoute({
path: '/components',
Expand Down
38 changes: 38 additions & 0 deletions src/ui/avatar/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<Avatar>
<AvatarImage :src="src as string" :alt="alt as string" :width="width" :height="height" :class="imageClass"/>
<AvatarFallback>{{ alt }}</AvatarFallback>
</Avatar>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'

export default defineComponent({
name: 'IAvatar',
components: {
Avatar, AvatarImage, AvatarFallback
},
props: {
src: {
type: String
},
alt: {
type: String
},
width: {
type: Number,
default: 32
},
height: {
type: Number,
default: 32
},
imageClass: {
type: String,
default: ''
}
}
})
</script>
38 changes: 38 additions & 0 deletions src/ui/tooltip/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<TooltipProvider :delay-duration="duration">
<Tooltip>
<TooltipTrigger as-child>
<slot/>
</TooltipTrigger>
<TooltipContent :side="side as any">
<p v-if="content">{{ content }}</p>
<slot v-else name="content"/>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'

export default defineComponent({
name: 'ITooltip',
components: {
Tooltip, TooltipContent, TooltipProvider, TooltipTrigger
},
props: {
content: {
type: String
},
duration: {
type: Number,
default: 0
},
side: {
type: String,
default: 'top'
}
}
})
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="flex flex-1 flex-col">
<div class="flex flex-1 flex-col gap-4 p-4 md:gap-8 md:p-8">
<Card>
<CardHeader class="flex flex-row items-center justify-between">
<template #title>
<div class="flex items-center space-x-4">
<Avatar>
<AvatarImage alt="SUA" src="https://www.shadcn-vue.com/avatars/01.png"/>
Expand All @@ -13,6 +13,8 @@
<p class="text-sm text-muted-foreground">[email protected]</p>
</div>
</div>
</template>
<template #extra>
<TooltipProvider>
<Tooltip :delay-duration="200">
<TooltipTrigger as-child>
Expand All @@ -23,25 +25,24 @@
<TooltipContent :side-offset="10">{{ $t('common.common.newMessage') }}</TooltipContent>
</Tooltip>
</TooltipProvider>
</CardHeader>
<Separator/>
<CardContent class="mt-3">
</template>
<div class="mt-3 mb-3">
<div class="space-y-4">
<div v-for="(message, index) in messages" :key="index"
:class="cn( 'flex w-max max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm', message.role === 'user' ? 'ml-auto bg-primary text-primary-foreground' : 'bg-muted')">
{{ `${message.content} ${index + 1}` }}
{{ `${ message.content } ${ index + 1 }` }}
</div>
</div>
</CardContent>
<CardFooter>
</div>
<template #footer>
<form class="flex w-full items-center space-x-2" @submit.prevent="handlerSubmit">
<Input v-model="inputValue as string" :placeholder="$t('common.tip.inputMessage')" class="flex-1"/>
<Button class="p-2.5 flex items-center justify-center" :disabled="!inputValue">
<Send class="w-4 h-4"/>
<span class="sr-only">{{ $t('common.common.send') }}</span>
</Button>
</form>
</CardFooter>
</template>
</Card>
</div>
<ChatUser :visible="visible" @close="visible = $event"/>
Expand All @@ -50,24 +51,24 @@

<script lang="ts">
import { defineComponent } from 'vue'
import { Card, CardContent, CardFooter, CardHeader } from '@/components/ui/card'
import Card from '@/ui/card/card.vue'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { cn } from '@/lib/utils'
import { cn } from '@/lib/utils.ts'
import { PlusIcon, Send } from 'lucide-vue-next'
import { createMessages } from '@/views/pages/chat/ChatUtils'
import { createMessages } from '@/views/pages/chat/basic/ChatUtils.ts'
import { useI18n } from 'vue-i18n'
import { Separator } from '@/components/ui/separator'
import ChatUser from '@/views/pages/chat/components/ChatUser.vue'
import ChatUser from '@/views/pages/chat/basic/components/ChatUser.vue'

export default defineComponent({
name: 'ChatHome',
components: {
ChatUser,
Separator,
Card, CardContent, CardFooter, CardHeader,
Card,
Avatar, AvatarFallback, AvatarImage,
Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,
Button,
Expand Down
Loading
Loading