-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #31 from qianmoQ/feature-components
[Tabs] Support tabs
- Loading branch information
Showing
12 changed files
with
274 additions
and
14 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<template> | ||
<div v-if="isActive" class="flex-1"> | ||
<TabsContent :value="value"> | ||
<slot/> | ||
</TabsContent> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { computed, defineComponent, inject } from 'vue' | ||
import { TabsContent } from '@/components/ui/tabs' | ||
interface TabsContext | ||
{ | ||
selectedValue: { value: string } | ||
orientation: 'horizontal' | 'vertical' | ||
setSelectedValue: (val: string) => void | ||
} | ||
export default defineComponent({ | ||
name: 'ITabContent', | ||
components: { TabsContent }, | ||
props: { | ||
value: { | ||
type: String, | ||
required: true | ||
} | ||
}, | ||
setup(props) | ||
{ | ||
// Add types to inject and provide default values to avoid type errors | ||
const tabsContext = inject<TabsContext>('tabsContext', { | ||
selectedValue: { value: '' }, | ||
orientation: 'horizontal', | ||
setSelectedValue: () => { | ||
} | ||
}) | ||
// Make sure tabsContext exists and compare selectedValue | ||
const isActive = computed(() => tabsContext.selectedValue.value === props.value) | ||
return { isActive } | ||
} | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<template> | ||
<Tabs :default-value="value"> | ||
<div :class="cn( | ||
isVertical(orientation) | ||
? 'flex' | ||
: 'flex flex-col space-y-2' | ||
)"> | ||
<TabsList :class="cn( | ||
isVertical(orientation) | ||
? 'flex flex-col h-full py-2 space-y-2' // Vertical: column layout | ||
: 'flex flex-row space-x-2 w-fit' // Horizontal: row layout | ||
)"> | ||
<slot name="default"/> | ||
</TabsList> | ||
|
||
<div :class="cn( | ||
isVertical(orientation) | ||
? 'flex-1 ml-4' // Vertical mode is on the right | ||
: 'flex-1 mt-2' // Horizontal mode is below | ||
)"> | ||
<slot name="content"/> | ||
</div> | ||
</div> | ||
</Tabs> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { defineComponent, onMounted, PropType, provide, ref } from 'vue' | ||
import { Tabs, TabsList } from '@/components/ui/tabs' | ||
import { cn } from '@/lib/utils.ts' | ||
import { isVertical } from '@/ui/tab/utils.ts' | ||
export default defineComponent({ | ||
name: 'ITabs', | ||
components: { Tabs, TabsList }, | ||
props: { | ||
value: { | ||
type: String, | ||
required: true | ||
}, | ||
orientation: { | ||
type: String as PropType<'horizontal' | 'vertical'>, | ||
default: 'vertical', | ||
validator: (value: string) => ['horizontal', 'vertical'].includes(value) | ||
} | ||
}, | ||
setup(props, { emit, slots }) | ||
{ | ||
const selectedValue = ref(props.value) | ||
// Provide orientation and selected value by providing | ||
provide('tabsContext', { | ||
selectedValue, | ||
orientation: props.orientation, | ||
setSelectedValue: (val: string) => { | ||
selectedValue.value = val | ||
emit('update:value', val) | ||
} | ||
}) | ||
// Slot type checking function | ||
const validateSlots = () => { | ||
if (slots.default) { | ||
const defaultSlotChildren = slots.default() | ||
defaultSlotChildren.forEach((child) => { | ||
// Check if the child is a component and has a 'name' property | ||
if (typeof child.type === 'object' && 'name' in child.type) { | ||
if (child.type.name !== 'ITab') { | ||
console.warn('The default slot can only contain ITab components') | ||
} | ||
} | ||
}) | ||
} | ||
if (slots.content) { | ||
const contentSlotChildren = slots.content() | ||
contentSlotChildren.forEach((child) => { | ||
if (typeof child.type === 'object' && 'name' in child.type) { | ||
if (child.type.name !== 'ITabContent') { | ||
console.warn('The content slot can only contain ITabContent components') | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
// Check slot contents when component is mounted | ||
onMounted(validateSlots) | ||
return { cn, isVertical } | ||
} | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<template> | ||
<TabsTrigger :value="value" | ||
:class="cn(isVertical(orientation) && 'vertical-text p-1.5')" | ||
@click="handleClick"> | ||
<slot/> | ||
</TabsTrigger> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { defineComponent, inject } from 'vue' | ||
import { cn } from '@/lib/utils.ts' | ||
import { isVertical } from '@/ui/tab/utils.ts' | ||
import { TabsTrigger } from '@/components/ui/tabs' | ||
interface TabsContext | ||
{ | ||
selectedValue: { value: string } | ||
orientation: 'horizontal' | 'vertical' | ||
setSelectedValue: (val: string) => void | ||
} | ||
export default defineComponent({ | ||
name: 'ITab', | ||
components: { TabsTrigger }, | ||
props: { | ||
value: { | ||
type: String, | ||
required: true | ||
} | ||
}, | ||
setup(props) | ||
{ | ||
// Specify the type when injecting the context and provide a default value to prevent injection failure | ||
const tabsContext = inject<TabsContext>('tabsContext', { | ||
selectedValue: { value: '' }, | ||
orientation: 'horizontal', | ||
setSelectedValue: () => { | ||
} | ||
}) | ||
// Get direction value | ||
const orientation = tabsContext.orientation | ||
const handleClick = () => { | ||
if (tabsContext) { | ||
tabsContext.setSelectedValue(props.value) | ||
} | ||
} | ||
return { cn, isVertical, handleClick, orientation } | ||
} | ||
}) | ||
</script> | ||
|
||
<style scoped> | ||
.vertical-text { | ||
writing-mode: vertical-rl; | ||
text-orientation: upright; | ||
white-space: nowrap; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export function isVertical(position: string): boolean | ||
{ | ||
return position === 'vertical' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<template> | ||
<ICard> | ||
<template #title>Horizontal</template> | ||
<div class="mt-2"> | ||
<ITabs value="tab1" orientation="horizontal"> | ||
<template #default> | ||
<ITab value="tab1">Tab 1</ITab> | ||
<ITab value="tab2">Tab 2</ITab> | ||
</template> | ||
<template #content> | ||
<ITabContent value="tab1">Content 1</ITabContent> | ||
<ITabContent value="tab2">Content 2</ITabContent> | ||
</template> | ||
</ITabs> | ||
</div> | ||
</ICard> | ||
<ICard> | ||
<template #title>Vertical</template> | ||
<div class="mt-2"> | ||
<ITabs value="tab1" orientation="vertical"> | ||
<template #default> | ||
<ITab value="tab1">Tab 1</ITab> | ||
<ITab value="tab2">Tab 2</ITab> | ||
</template> | ||
<template #content> | ||
<ITabContent value="tab1">Content 1</ITabContent> | ||
<ITabContent value="tab2">Content 2</ITabContent> | ||
</template> | ||
</ITabs> | ||
</div> | ||
</ICard> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { defineComponent } from 'vue' | ||
import ICard from '@/ui/card/card.vue' | ||
import ITab from '@/ui/tab/tab.vue' | ||
import ITabs from '@/ui/tab/index.vue' | ||
import ITabContent from '@/ui/tab/content.vue' | ||
export default defineComponent({ | ||
name: 'TabHome', | ||
components: { | ||
ITabContent, ITabs, ITab, | ||
ICard | ||
} | ||
}) | ||
</script> |