Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4ab7c17
[feature] Add model selector dialog integration and custom component …
viva-jinyi Aug 7, 2025
65022ad
[refactor] apply code reviews
viva-jinyi Aug 10, 2025
096693a
[refactor] Improve modal dialog components based on PR feedback
viva-jinyi Aug 10, 2025
127c4f9
[feature] Add right panel support and improve modal transitions
viva-jinyi Aug 10, 2025
a570ad1
fix: code review
viva-jinyi Aug 12, 2025
c066894
feature: searchbox added
viva-jinyi Aug 10, 2025
f303611
feature: MultiSelect added
viva-jinyi Aug 10, 2025
664716e
feature: multiSelect added
viva-jinyi Aug 10, 2025
1cc6a96
feature: singleSelect border style added
viva-jinyi Aug 10, 2025
22eeba0
feature: implement Textbutton
viva-jinyi Aug 11, 2025
28491a9
feature: Morebutton added
viva-jinyi Aug 11, 2025
f8f1b52
feature: icon Text button
viva-jinyi Aug 11, 2025
2000421
feature: icon button group added
viva-jinyi Aug 11, 2025
a29ca2b
feature: Add size and class props to IconButton component
viva-jinyi Aug 11, 2025
26a2f0c
feature: Implement unified button system and card components
viva-jinyi Aug 11, 2025
0f83615
style: button style added
viva-jinyi Aug 11, 2025
15d5367
feature: Add CardDescription and CardTitle components
viva-jinyi Aug 11, 2025
e0e01f3
fix: checkbox style
viva-jinyi Aug 11, 2025
6017fb4
fix: baseWidgetLayout style bug
viva-jinyi Aug 12, 2025
d6a6577
chore: add design system components to knip ignore list
viva-jinyi Aug 14, 2025
55c9ade
fix: add missing @types/lodash dev dependency
viva-jinyi Aug 14, 2025
3394aea
style: navigation style fixed
viva-jinyi Aug 14, 2025
3e16b82
fix: update CardContainer to use inline styles for dynamic width values
viva-jinyi Aug 14, 2025
5597cf8
chore: add more design system components to knip ignore list
viva-jinyi Aug 15, 2025
224021f
fix: error
viva-jinyi Aug 15, 2025
8cd1bb0
[fix] Address PR review comments
viva-jinyi Aug 16, 2025
f56bc42
feature: multiSelect, SingleSelect style deleted according to Claude …
viva-jinyi Aug 16, 2025
98cb1ce
feature: duplicated style deleted
viva-jinyi Aug 16, 2025
0db8adf
feature: fallback title added
viva-jinyi Aug 16, 2025
141e78b
fix: popover border added
viva-jinyi Aug 16, 2025
a8f7401
feature: hasIcon props
viva-jinyi Aug 16, 2025
748357a
feature: hasfoldericon
viva-jinyi Aug 16, 2025
4217579
fix: vertical center align
viva-jinyi Aug 16, 2025
f6e8324
feature: directory modified
viva-jinyi Aug 19, 2025
6649227
fix: deleted lodash in package
viva-jinyi Aug 19, 2025
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
Prev Previous commit
Next Next commit
feature: Implement unified button system and card components
- Create BaseButtonProps interface for consistent button types
- Update all button components to use unified type system with PrimeVue Button
- Add support for primary, secondary, transparent button types with dark theme
- Create Card components (CardContainer, CardTop, CardBottom) with flexible layouts
- Add overlay slots to CardTop for corner positioning
- Add SquareTag component for labels
- Update ModelSelector with card and button examples

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
  • Loading branch information
viva-jinyi and claude committed Aug 19, 2025
commit 26a2f0c2f56b4242fe684b9add78e572437d836c
13 changes: 13 additions & 0 deletions src/components/custom/SquareTag.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<div
class="inline-flex justify-center items-center gap-1 flex-shrink-0 py-1 px-2 text-xs bg-[#D9D9D966]/40 rounded font-bold text-white/90"
>
<slot name="icon" class="text-xs text-white/90"></slot>
<span>{{ label }}</span>
</div>
</template>
<script setup lang="ts">
const { label } = defineProps<{
label: string
}>()
</script>
36 changes: 22 additions & 14 deletions src/components/custom/button/IconButton.vue
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
<template>
<button :class="buttonStyle" role="button" @click="onClick">
<Button unstyled :class="buttonStyle" @click="onClick">
<slot></slot>
</button>
</Button>
</template>

<script setup lang="ts">
import Button from 'primevue/button'
import { computed } from 'vue'
import type { HTMLAttributes } from 'vue'

import type { BaseButtonProps } from '@/types/custom_components/buttonTypes'
import {
getBaseButtonClasses,
getButtonTypeClasses,
getIconButtonSizeClasses
} from '@/types/custom_components/buttonTypes'

interface IconButtonProps extends BaseButtonProps {
onClick: (event: Event) => void
}

const {
size = 'md',
type = 'secondary',
class: className,
onClick
} = defineProps<{
size?: 'sm' | 'md'
class?: HTMLAttributes['class']
onClick: (event: Event) => void
}>()
} = defineProps<IconButtonProps>()

const buttonStyle = computed(() => {
const baseClasses =
'flex justify-center items-center flex-shrink-0 outline-none border-none p-0 bg-white text-neutral-950 dark-theme:bg-zinc-700 dark-theme:text-white cursor-pointer'

const sizeClasses =
size === 'sm' ? 'w-6 h-6 text-xs rounded-md' : 'w-8 h-8 text-sm rounded-lg'
const baseClasses = `${getBaseButtonClasses()} p-0`
const sizeClasses = getIconButtonSizeClasses(size)
const typeClasses = getButtonTypeClasses(type)

return [baseClasses, sizeClasses, className].filter(Boolean).join(' ')
return [baseClasses, sizeClasses, typeClasses, className]
.filter(Boolean)
.join(' ')
})
</script>
47 changes: 26 additions & 21 deletions src/components/custom/button/IconTextButton.vue
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
<template>
<button :class="buttonStyle" role="button" @click="onClick">
<Button unstyled :class="buttonStyle" @click="onClick">
<slot v-if="iconPosition !== 'right'" name="icon"></slot>
<span class="text-sm">{{ label }}</span>
<span>{{ label }}</span>
<slot v-if="iconPosition === 'right'" name="icon"></slot>
</button>
</Button>
</template>

<script setup lang="ts">
import Button from 'primevue/button'
import { computed } from 'vue'

import type { BaseButtonProps } from '@/types/custom_components/buttonTypes'
import {
getBaseButtonClasses,
getButtonSizeClasses,
getButtonTypeClasses
} from '@/types/custom_components/buttonTypes'

interface IconTextButtonProps extends BaseButtonProps {
iconPosition?: 'left' | 'right'
label: string
onClick: () => void
}

const {
size = 'md',
type = 'primary',
class: className,
iconPosition = 'left',
label,
onClick
} = defineProps<{
type?: 'primary' | 'secondary' | 'transparent'
iconPosition?: 'left' | 'right'
label: string
onClick: () => void
}>()
} = defineProps<IconTextButtonProps>()

const buttonStyle = computed(() => {
const baseClasses =
'flex items-center gap-2 flex-shrink-0 outline-none border-none px-2.5 py-2 rounded-lg cursor-pointer transition-all duration-200'
const baseClasses = `${getBaseButtonClasses()} !justify-start gap-2`
const sizeClasses = getButtonSizeClasses(size)
const typeClasses = getButtonTypeClasses(type)

switch (type) {
case 'primary':
return `${baseClasses} bg-neutral-900 text-white dark-theme:bg-white dark-theme:text-neutral-900`
case 'secondary':
return `${baseClasses} bg-white text-neutral dark-theme:bg-zinc-700 dark-theme:text-white`
case 'transparent':
return `${baseClasses} bg-transparent text-neutral-400 dark-theme:text-neutral-400`
default:
return `${baseClasses} bg-white text-neutral dark-theme:bg-zinc-700 dark-theme:text-white`
}
return [baseClasses, sizeClasses, typeClasses, className]
.filter(Boolean)
.join(' ')
})
</script>
43 changes: 25 additions & 18 deletions src/components/custom/button/TextButton.vue
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
<template>
<button :class="buttonStyle" role="button" @click="onClick">
<span class="text-sm">{{ label }}</span>
</button>
<Button unstyled :class="buttonStyle" role="button" @click="onClick">
<span>{{ label }}</span>
</Button>
</template>

<script setup lang="ts">
import Button from 'primevue/button'
import { computed } from 'vue'

import type { BaseButtonProps } from '@/types/custom_components/buttonTypes'
import {
getBaseButtonClasses,
getButtonSizeClasses,
getButtonTypeClasses
} from '@/types/custom_components/buttonTypes'

interface TextButtonProps extends BaseButtonProps {
label: string
onClick: () => void
}

const {
size = 'md',
type = 'primary',
class: className,
label,
onClick
} = defineProps<{
type?: 'primary' | 'secondary'
label: string
onClick: () => void
}>()
} = defineProps<TextButtonProps>()

const buttonStyle = computed(() => {
const baseClasses =
'flex justify-center items-center flex-shrink-0 outline-none border-none px-2.5 py-2 rounded-lg cursor-pointer transition-all duration-200'
const baseClasses = getBaseButtonClasses()
const sizeClasses = getButtonSizeClasses(size)
const typeClasses = getButtonTypeClasses(type)

switch (type) {
case 'primary':
return `${baseClasses} bg-neutral-900 text-white dark-theme:bg-white dark-theme:text-neutral-900`
case 'secondary':
return `${baseClasses} bg-white text-neutral dark-theme:bg-zinc-700`
default:
return `${baseClasses} bg-white text-neutral dark-theme:bg-zinc-700`
}
return [baseClasses, sizeClasses, typeClasses, className]
.filter(Boolean)
.join(' ')
})
</script>
7 changes: 7 additions & 0 deletions src/components/custom/card/CardBottom.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<template>
<div class="flex-1 w-full h-full">
<slot></slot>
</div>
</template>

<script setup lang="ts"></script>
27 changes: 27 additions & 0 deletions src/components/custom/card/CardContainer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<div :class="containerStyle">
<slot name="top"></slot>
<slot name="bottom"></slot>
</div>
</template>

<script setup lang="ts">
import { computed } from 'vue'

const { ratio = 'square' } = defineProps<{
ratio?: 'square' | 'portrait' | 'tallPortrait'
}>()

const containerStyle = computed(() => {
const baseClasses =
'flex flex-col bg-white dark-theme:bg-zinc-800 rounded-lg shadow-sm border border-zinc-200 dark-theme:border-zinc-700 overflow-hidden'

const ratioClasses = {
square: 'aspect-[256/308]',
portrait: 'aspect-[256/325]',
tallPortrait: 'aspect-[256/353]'
}

return `${baseClasses} ${ratioClasses[ratio]}`
})
</script>
40 changes: 40 additions & 0 deletions src/components/custom/card/CardTop.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<div :class="topStyle">
<slot class="absolute top-0 left-0 w-full h-full"></slot>

<div class="absolute top-2 left-2 flex gap-2">
<slot name="top-left"></slot>
</div>

<div class="absolute top-2 right-2 flex gap-2">
<slot name="top-right"></slot>
</div>

<div class="absolute bottom-2 left-2 flex gap-2">
<slot name="bottom-left"></slot>
</div>

<div class="absolute bottom-2 right-2 flex gap-2">
<slot name="bottom-right"></slot>
</div>
</div>
</template>

<script setup lang="ts">
import { computed } from 'vue'

const { ratio = 'square' } = defineProps<{
ratio?: 'square' | 'landscape'
}>()

const topStyle = computed(() => {
const baseClasses = 'relative p-0'

const ratioClasses = {
square: 'aspect-[1/1]',
landscape: 'aspect-[48/27]'
}

return `${baseClasses} ${ratioClasses[ratio]}`
})
</script>
75 changes: 71 additions & 4 deletions src/components/custom/widget/ModelSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,21 @@

<template #header-right-area>
<div class="flex gap-2">
<IconButton size="sm" class="bg-orange-500 text-white">
<IconButton
size="sm"
class="!bg-orange-500 text-white"
@click="console.log('Hello World!!')"
>
<i-lucide:triangle-alert />
</IconButton>
<IconGroup>
<IconButton>
<IconButton @click="console.log('Hello World!!')">
<i-lucide:heart />
</IconButton>
<IconButton>
<IconButton @click="console.log('Hello World!!')">
<i-lucide:download />
</IconButton>
<IconButton>
<IconButton @click="console.log('Hello World!!')">
<i-lucide:external-link />
</IconButton>
</IconGroup>
Expand Down Expand Up @@ -89,6 +93,64 @@
:options="projectOptions"
/>
</div>

<!-- Card Examples -->
<div class="grid grid-cols-6 gap-4 px-6 py-4">
<!-- Square Card -->
<CardContainer ratio="tallPortrait">
<template #top>
<CardTop ratio="square">
<template #default>
<div class="w-full h-full bg-blue-500"></div>
</template>
<template #top-right>
<IconButton
class="!bg-white !text-neutral-900"
@click="console.log('Hello World!!')"
>
<i-lucide:info />
</IconButton>
</template>
<template #bottom-right>
<SquareTag label="png" />
<SquareTag label="1.2 MB" />
<SquareTag label="LoRA">
<template #icon>
<i-lucide:folder />
</template>
</SquareTag>
</template>
</CardTop>
</template>
<template #bottom>
<CardBottom></CardBottom>
</template>
</CardContainer>

<!-- Tall Card -->
<CardContainer ratio="portrait">
<template #top>
<CardTop ratio="square">
<div class="w-full h-full bg-red-500"></div>
</CardTop>
</template>
<template #bottom>
<CardBottom></CardBottom>
</template>
</CardContainer>

<!-- Taller Card -->
<CardContainer ratio="square">
<template #top>
<CardTop ratio="landscape">
<div class="w-full h-full bg-red-500"></div>
</CardTop>
</template>
<template #bottom>
<CardBottom></CardBottom>
</template>
</CardContainer>
</div>
</template>

<template #rightPanel>
Expand All @@ -104,9 +166,14 @@ import { useI18n } from 'vue-i18n'
import { NavGroupData, NavItemData } from '@/types/custom_components/navTypes'
import { OnCloseKey } from '@/types/custom_components/widgetTypes'

import SquareTag from '../SquareTag.vue'
import IconButton from '../button/IconButton.vue'
import IconTextButton from '../button/IconTextButton.vue'
import MoreButton from '../button/MoreButton.vue'
import TextButton from '../button/TextButton.vue'
import CardBottom from '../card/CardBottom.vue'
import CardContainer from '../card/CardContainer.vue'
import CardTop from '../card/CardTop.vue'
import MultiSelect from '../input/MultiSelect.vue'
import SearchBox from '../input/SearchBox.vue'
import BaseWidgetLayout from './layout/BaseWidgetLayout.vue'
Expand Down
Loading