feat: add tabbar component

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/581/head
Ryan Wang 2022-04-28 21:55:56 +08:00
parent db32465a04
commit aa861c3783
10 changed files with 240 additions and 0 deletions

View File

@ -11,6 +11,12 @@ body {
overflow-y: overlay;
background: #eff4f9;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
*::-webkit-scrollbar-track-piece {
background-color: #f8f8f8;
-webkit-border-radius: 2em;

View File

@ -0,0 +1,5 @@
<script lang="ts" setup></script>
<template>
<div>tab item</div>
</template>
<style lang="scss"></style>

View File

@ -0,0 +1,37 @@
<script lang="ts" setup>
import { VTabbar } from "./index";
function initState() {
return {
active: "johnniang",
items: [
{ label: "Ryan Wang", value: "ryanwang" },
{ label: "JohnNiang", value: "johnniang" },
{ label: "guqing", value: "guqing" },
],
};
}
</script>
<template>
<Story title="Tabbar" :init-state="initState">
<template #default="{ state }">
<div class="p-3">
<VTabbar :items="state.items" v-model:active="state.active" />
</div>
<div class="p-3">
<VTabbar
type="pills"
:items="state.items"
v-model:active="state.active"
/>
</div>
<div class="p-3">
<VTabbar
type="outline"
:items="state.items"
v-model:active="state.active"
/>
</div>
</template>
</Story>
</template>

View File

@ -0,0 +1,159 @@
<script lang="ts" setup>
import type { PropType } from "vue";
import { computed } from "vue";
import type { Type } from "./interface";
const props = defineProps({
active: {
type: [Number, String],
},
items: {
type: Object as PropType<Array<Record<string, string>>>,
},
type: {
type: String as PropType<Type>,
default: "default",
},
valueKey: {
type: String,
default: "value",
},
labelKey: {
type: String,
default: "label",
},
});
const emit = defineEmits(["update:active", "change"]);
const classes = computed(() => {
return [`tabbar-${props.type}`];
});
const handleChange = (value: number | string) => {
emit("update:active", value);
emit("change", value);
};
</script>
<template>
<div class="tabbar-wrapper" :class="classes">
<div class="tabbar-items">
<div
v-for="(item, index) in items"
:key="index"
class="tabbar-item"
:class="{ 'tabbar-item-active': item[valueKey] === active }"
@click="handleChange(item[valueKey])"
>
<div v-if="item.icon" class="tabbar-item-icon">
<component :is="item.icon" />
</div>
<div class="tabbar-item-label">
{{ item[labelKey] }}
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.tabbar-wrapper {
.tabbar-items {
@apply flex;
@apply items-center;
}
.tabbar-item {
@apply flex;
@apply cursor-pointer;
@apply self-center;
@apply transition-all;
@apply text-base;
@apply w-full;
@apply justify-center;
.tabbar-item-label,
.tabbar-item-icon {
@apply self-center;
}
.tabbar-item-icon {
@apply mr-2;
}
}
&.tabbar-default {
border-bottom-width: 2px;
@apply border-b-gray-100;
.tabbar-items {
margin-bottom: -2px;
justify-content: flex-start;
}
.tabbar-item {
@apply h-10;
@apply px-5;
@apply py-1;
@apply border-b-gray-100;
border-bottom-width: 2px;
&.tabbar-item-active {
color: #0e1731;
border-bottom-color: #0e1731;
}
}
}
&.tabbar-pills {
.tabbar-items {
@apply gap-1;
justify-content: flex-start;
}
.tabbar-item {
@apply h-10;
@apply px-9;
@apply py-1;
@apply opacity-70;
border-radius: 4px;
&.tabbar-item-active {
@apply bg-gray-100;
@apply opacity-100;
}
&:hover {
@apply bg-gray-100;
}
}
}
&.tabbar-outline {
@apply p-1;
@apply bg-gray-100;
border-radius: 4px;
.tabbar-items {
@apply gap-1;
justify-content: flex-start;
}
.tabbar-item {
@apply h-10;
@apply px-9;
@apply py-1;
@apply opacity-70;
border-radius: 4px;
&.tabbar-item-active {
@apply bg-white;
@apply opacity-100;
@apply shadow-sm;
}
&:hover {
@apply bg-white;
}
}
}
}
</style>

View File

@ -0,0 +1,5 @@
<script lang="ts" setup></script>
<template>
<div>tabs</div>
</template>
<style lang="scss"></style>

View File

@ -0,0 +1,8 @@
import { describe, expect, it } from "vitest";
import { VTabItem } from "../index";
describe("TabItem", () => {
it("should render", () => {
expect(VTabItem).toBeDefined();
});
});

View File

@ -0,0 +1,8 @@
import { describe, expect, it } from "vitest";
import { VTabbar } from "../index";
describe("Tabbar", () => {
it("should render", () => {
expect(VTabbar).toBeDefined();
});
});

View File

@ -0,0 +1,8 @@
import { describe, expect, it } from "vitest";
import { VTabs } from "../index";
describe("Tabs", () => {
it("should render", () => {
expect(VTabs).toBeDefined();
});
});

View File

@ -0,0 +1,3 @@
export { default as VTabs } from "./Tabs.vue";
export { default as VTabItem } from "./TabItem.vue";
export { default as VTabbar } from "./Tabbar.vue";

View File

@ -0,0 +1 @@
export type Type = "default" | "pills" | "outline";