diff --git a/packages/varlet-vue2-ui/src/bottom-navigation-item/BottomNavigationItem.vue b/packages/varlet-vue2-ui/src/bottom-navigation-item/BottomNavigationItem.vue new file mode 100644 index 00000000..2de5eacf --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation-item/BottomNavigationItem.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/packages/varlet-vue2-ui/src/bottom-navigation-item/bottomNavigationItem.less b/packages/varlet-vue2-ui/src/bottom-navigation-item/bottomNavigationItem.less new file mode 100644 index 00000000..90db60d9 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation-item/bottomNavigationItem.less @@ -0,0 +1,78 @@ +@bottom-navigation-item-font-size: var(--font-size-sm); +@bottom-navigation-item-inactive-color: #646566; +@bottom-navigation-item-active-color: var(--color-primary); +@bottom-navigation-item-active-background-color: #fff; +@bottom-navigation-item-line-height: 1; +@bottom-navigation-item-icon-size: 22px; +@bottom-navigation-item-icon-margin-bottom: 5px; + +:root { + --bottom-navigation-item-font-size: @bottom-navigation-item-font-size; + --bottom-navigation-item-inactive-color: @bottom-navigation-item-inactive-color; + --bottom-navigation-item-active-color: @bottom-navigation-item-active-color; + --bottom-navigation-item-active-background-color: @bottom-navigation-item-active-background-color; + --bottom-navigation-item-line-height: @bottom-navigation-item-line-height; + --bottom-navigation-item-icon-size: @bottom-navigation-item-icon-size; + --bottom-navigation-item-icon-margin-bottom: @bottom-navigation-item-icon-margin-bottom; +} + +.var-bottom-navigation-item { + height: 100%; + padding: 6px 12px 8px; + position: relative; + display: inline-flex; + flex: 1 1 0%; + flex-direction: column; + align-items: center; + justify-content: center; + line-height: var(--bottom-navigation-item-line-height); + color: var(--bottom-navigation-item-inactive-color); + cursor: pointer; + user-select: none; + vertical-align: middle; + appearance: none; + text-decoration: none; + background-color: transparent; + outline: 0; + border: 0; + transition: color 250ms, margin 250ms; + + &--active { + color: var(--bottom-navigation-item-active-color); + background-color: var(--bottom-navigation-item-active-background-color); + transition: background-color 250ms; + + .var-bottom-navigation-item__label { + font-size: calc(var(--bottom-navigation-item-font-size) * 1.16); + } + } + + &--right-half-space { + margin-right: calc(var(--bottom-navigation-height) / 2); + } + + &--left-half-space { + margin-left: calc(var(--bottom-navigation-height) / 2); + } + + &--right-space { + margin-right: calc(var(--bottom-navigation-height) + var(--bottom-navigation-fab-offset)); + } + + &__icon { + font-size: var(--bottom-navigation-item-icon-size); + } + + &__badge { + position: absolute; + left: 40px; + transform: translateY(-16px); + } + + &__label { + margin-top: var(--bottom-navigation-item-icon-margin-bottom); + font-size: var(--bottom-navigation-item-font-size); + transition: font-size 0.2s ease 0.1s; + white-space: nowrap; + } +} diff --git a/packages/varlet-vue2-ui/src/bottom-navigation-item/docs/zh-CN.md b/packages/varlet-vue2-ui/src/bottom-navigation-item/docs/zh-CN.md new file mode 100644 index 00000000..eeb40c37 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation-item/docs/zh-CN.md @@ -0,0 +1,24 @@ +## API + +### 属性 + +|参数 | 说明 | 类型 | 默认值 | +| ---- | ---- | ---- | ---- | +| `name` | 标签名称,作为匹配的标识符 | _string_ | `-` | +| `icon` | 图标名称,等同于 Icon 组件的 [name 属性](/#/zh-CN/icon) | _string_ | `-` | +| `label` | 标签文字内容 | _string_ | - | +| `namespace` | 图标的命名空间, 可扩展自定义图标库,等同于 Icon 组件的 [namespace 属性](/#/zh-CN/icon) | _string_ | `var-icon` | +| `badge` | 图标右上角徽标 | _boolean \| BadgeProps_ | `false` | + +### 事件 + +|事件名 | 说明 | 回调参数 | +| ---- | ---- | ---- | +| `click` | 点击时触发 | `active: number \| string` | + +### 插槽 + +| 名称 | 说明 | 参数 | +| ---- | ---- | ----| +| `default` | 自定义标签文字内容,会覆盖 `label` 的内容 | `-` | +| `icon` | 自定义图标 | `active: boolean` | \ No newline at end of file diff --git a/packages/varlet-vue2-ui/src/bottom-navigation-item/index.ts b/packages/varlet-vue2-ui/src/bottom-navigation-item/index.ts new file mode 100644 index 00000000..18e83057 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation-item/index.ts @@ -0,0 +1,10 @@ +import type { VueConstructor } from 'vue' +import BottomNavigationItem from './BottomNavigationItem.vue' + +BottomNavigationItem.install = function (app: VueConstructor) { + app.component(BottomNavigationItem.name, BottomNavigationItem) +} + +export const _BottomNavigationItemComponent = BottomNavigationItem + +export default BottomNavigationItem diff --git a/packages/varlet-vue2-ui/src/bottom-navigation-item/props.ts b/packages/varlet-vue2-ui/src/bottom-navigation-item/props.ts new file mode 100644 index 00000000..11a90601 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation-item/props.ts @@ -0,0 +1,25 @@ +import type { PropType } from 'vue' +import type { BadgeProps } from '../../types' + +export const props = { + name: { + type: String, + }, + icon: { + type: String, + }, + label: { + type: String, + }, + namespace: { + type: String, + default: 'var-icon', + }, + badge: { + type: [Boolean, Object] as PropType>, + default: false, + }, + onClick: { + type: Function as PropType<(active: number | string) => void>, + }, +} diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/BottomNavigation.vue b/packages/varlet-vue2-ui/src/bottom-navigation/BottomNavigation.vue new file mode 100644 index 00000000..03fc1bcf --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/BottomNavigation.vue @@ -0,0 +1,178 @@ + + + + + diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-vue2-ui/src/bottom-navigation/__tests__/__snapshots__/index.spec.js.snap new file mode 100644 index 00000000..1a17bd97 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/__tests__/__snapshots__/index.spec.js.snap @@ -0,0 +1,180 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`fab button 1`] = ` +"
" +`; + +exports[`test BottomNavigation relation BottomNavigationItems 1`] = ` +"
+ +
" +`; + +exports[`test BottomNavigation relation BottomNavigationItems 2`] = ` +"
+ +
" +`; + +exports[`test bottom-navigation example 1`] = ` +"
+
基本使用
+
+ +
+
通过名称匹配
+
+ +
+
徽标提示
+
+ +
+
自定义颜色
+
+ +
+
监听切换事件
+
+ +
+
监听点击事件
+
+ +
+
悬浮按钮
+
+
+
" +`; diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/__tests__/index.spec.js b/packages/varlet-vue2-ui/src/bottom-navigation/__tests__/index.spec.js new file mode 100644 index 00000000..74dd7914 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/__tests__/index.spec.js @@ -0,0 +1,234 @@ +import example from '../example' +import BottomNavigation from '..' +import BottomNavigationItem from '../../bottom-navigation-item' +import VarBottomNavigation from '../BottomNavigation' +import VarBottomNavigationItem from '../../bottom-navigation-item/BottomNavigationItem' +import { mount } from '@vue/test-utils' +import Vue from 'vue' +import { delay } from '../../utils/jest' + +test('test bottom-navigation example', () => { + const wrapper = mount(example) + + expect(wrapper.html()).toMatchSnapshot() + + wrapper.destroy() +}) + +test('test bottom-navigation plugin', () => { + Vue.use(BottomNavigation).use(BottomNavigationItem) + expect(Vue.component(BottomNavigation.name)).toBeTruthy() +}) + +test('test bottom-navigation before-change prevent switch', async () => { + const onBeforeChange = jest.fn(() => false) + const Wrapper = { + components: { + [VarBottomNavigation.name]: VarBottomNavigation, + [VarBottomNavigationItem.name]: VarBottomNavigationItem, + }, + props: ['onBeforeChange'], + data: () => ({ + active: 0, + }), + template: ` + + + + + + + `, + } + const wrapper = mount(Wrapper, { + propsData: { + onBeforeChange, + }, + }) + await delay(16) + + const bottomNavigationItem = wrapper.findAll('.var-bottom-navigation-item').at(1) + await bottomNavigationItem.trigger('click') + + expect(onBeforeChange).toHaveBeenCalledTimes(1) + expect(wrapper.vm.active).toBe(0) + + wrapper.destroy() +}) + +test('test bottom-navigation before-change return promise', async () => { + const onBeforeChange = jest.fn( + () => + new Promise((resolve) => { + setTimeout(() => { + resolve(true) + }, 300) + }) + ) + const Wrapper = { + components: { + [VarBottomNavigation.name]: VarBottomNavigation, + [VarBottomNavigationItem.name]: VarBottomNavigationItem, + }, + props: ['onBeforeChange'], + data: () => ({ + active: 0, + }), + template: ` + + + + + + + `, + } + const wrapper = mount(Wrapper, { + propsData: { + onBeforeChange, + }, + }) + await delay(16) + + const bottomNavigationItem = wrapper.findAll('.var-bottom-navigation-item').at(1) + await bottomNavigationItem.trigger('click') + expect(wrapper.vm.active).toBe(0) + + await delay(600) + expect(onBeforeChange).toHaveBeenCalledTimes(1) + expect(wrapper.vm.active).toBe(1) + + wrapper.destroy() +}) + +test('test bottom-navigation change event', async () => { + let dummy + const handleChange = jest.fn((active) => { + dummy = active + }) + const Wrapper = { + components: { + [VarBottomNavigation.name]: VarBottomNavigation, + [VarBottomNavigationItem.name]: VarBottomNavigationItem, + }, + data: () => ({ + active: 0, + }), + methods: { + handleChange, + }, + template: ` + + + + + + + `, + } + const wrapper = mount(Wrapper) + await delay(16) + + const bottomNavigationItem = wrapper.findAll('.var-bottom-navigation-item').at(1) + await bottomNavigationItem.trigger('click') + + expect(handleChange).toHaveBeenCalledTimes(1) + expect(dummy).toBe(1) + + wrapper.destroy() +}) + +test('test bottom-navigation-item click event', async () => { + let dummy + const handleClick = jest.fn((active) => { + dummy = active + }) + const Wrapper = { + components: { + [VarBottomNavigation.name]: VarBottomNavigation, + [VarBottomNavigationItem.name]: VarBottomNavigationItem, + }, + data: () => ({ + active: 0, + }), + methods: { + handleClick, + }, + template: ` + + + + + + + `, + } + const wrapper = mount(Wrapper) + await delay(16) + + const bottomNavigationItem = wrapper.findAll('.var-bottom-navigation-item').at(1) + await bottomNavigationItem.trigger('click') + + expect(handleClick).toHaveBeenCalledTimes(1) + expect(dummy).toBe(1) + + wrapper.destroy() +}) + +test('test BottomNavigation relation BottomNavigationItems', async () => { + const Wrapper = { + components: { + [VarBottomNavigation.name]: VarBottomNavigation, + [VarBottomNavigationItem.name]: VarBottomNavigationItem, + }, + data: () => ({ + active: 0, + }), + template: ` + + + + + + + `, + } + const wrapper = mount(Wrapper) + await delay(100) + + expect(wrapper.html()).toMatchSnapshot() + await wrapper.setData({ active: 1 }) + + await delay(50) + expect(wrapper.html()).toMatchSnapshot() + + wrapper.destroy() +}) + +test('fab button', async () => { + const Wrapper = { + components: { + [VarBottomNavigation.name]: VarBottomNavigation, + [VarBottomNavigationItem.name]: VarBottomNavigationItem, + }, + data: () => ({ + active: 0, + }), + template: ` + + + + + + + `, + } + const wrapper = mount(Wrapper) + await delay(100) + + expect(wrapper.html()).toMatchSnapshot() + + wrapper.destroy() +}) diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/bottomNavigation.less b/packages/varlet-vue2-ui/src/bottom-navigation/bottomNavigation.less new file mode 100644 index 00000000..e0de9639 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/bottomNavigation.less @@ -0,0 +1,51 @@ +@bottom-navigation-height: 50px; +@bottom-navigation-z-index: 1; +@bottom-navigation-background-color: #fff; +@bottom-navigation-border-color: #e3e3e3; +@bottom-navigation-fab-offset: 4px; + +:root { + --bottom-navigation-height: @bottom-navigation-height; + --bottom-navigation-z-index: @bottom-navigation-z-index; + --bottom-navigation-background-color: @bottom-navigation-background-color; + --bottom-navigation-border-color: @bottom-navigation-border-color; + --bottom-navigation-fab-offset: @bottom-navigation-fab-offset; +} + +.var-bottom-navigation { + width: 100%; + height: var(--bottom-navigation-height); + display: flex; + position: relative; + background-color: var(--bottom-navigation-background-color); + transition: background-color 250ms, border-color 250ms; + -webkit-tap-highlight-color: transparent; + + &--fixed { + position: fixed; + left: 0; + bottom: 0; + } + + &--border { + border-top: 1px solid var(--bottom-navigation-border-color); + } + + &__fab { + width: var(--bottom-navigation-height); + height: var(--bottom-navigation-height) !important; + position: absolute !important; + z-index: 2; + transform: translateY(-50%); + overflow: hidden; + transition: right 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + } + + &--fab-center { + right: calc(50% - var(--bottom-navigation-height) / 2); + } + + &--fab-right { + right: var(--bottom-navigation-fab-offset); + } +} diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/docs/en-US.md b/packages/varlet-vue2-ui/src/bottom-navigation/docs/en-US.md new file mode 100644 index 00000000..6283cf0a --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/docs/en-US.md @@ -0,0 +1,258 @@ +# BottomNavigation + +### Install + +```js +import Vue from 'vue' +import { BottomNavigation, BottomNavigationItem } from '@varlet-vue2/ui' + +Vue.use(BottomNavigation).use(BottomNavigationItem) +``` + +### Basic Usage + +```html + + + + + + +``` + +```js +export default { + data: () => ({ + active: 0 + }) +} +``` + +### Match by name + +```html + + + + + + +``` + +```js +export default { + data: () => ({ + active: 0 + }) +} +``` + +### Show Badge + +```html + + + + + + +``` + +```js +export default { + data: () => ({ + active: 0, + badgeProps: { + type: 'primary', + value: '66' + } + }) +} +``` + +### Custom Color + +```html + + + + + + +``` + +```js +export default { + data: () => ({ + active: 0 + }) +} +``` + +### Change Event + +```html + + + + + + +``` + +```js +import { Snackbar } from '@varlet-vue2/ui' + +export default { + data: () => ({ + active: 0 + }), + methods: { + handleChange(active) => { + Snackbar.info(`changed to ${active}`) + } + } +} +``` + +### Click Event + +```html + + + + + + +``` + +```js +import { Snackbar } from '@varlet-vue2/ui' + +export default { + data: () => ({ + active: 0 + }), + methods: { + handleClick(active) => { + Snackbar.info(`clicked ${active}`) + } + } +} +``` + +### Fab + +```html + + + + + + + + +``` + +```js +export default { + data: () => ({ + active: 0, + isEven: true + }) +} +``` + +```css +.bottom-navigation-example { + margin-top: 40px; +} +``` + + +## API + +### Props + +#### BottomNavigation Props + +| Prop | Description | Type | Default | +|-----------------|-----------------------------------------------------| ---- | ---- | +| `active.sync` | Identifier of current tab | _number \| string_ | `0` | +| `fixed` | Whether to fixed bottom | _boolean_ | `false` | +| `border` | Whether to show border | _boolean_ | `false` | +| `z-index` | Z-index | _number \| string_ | `1` | +| `active-color` | Color of active tab item | _string_ | `-` | +| `inactive-color` | Color of inactive tab item | _string_ | `-` | +| `fab-props` | Fab button props | _ButtonProps_ | `{type: "primary"}` | + +#### BottomNavigationItem Props + +|Prop | Description | Type | Default | +| ---- | ---- | ---- | ---- | +| `name` | Identifier | _string_ | `-` | +| `icon` | Icon name, equivalent to the [name](/#/en-US/icon) of Icon component | _string_ | `-` | +| `label` | Label text content | _string_ | - | +| `namespace` | Icon namespace, extensible custom icon library, equivalent to the [namespace](/#/en-US/icon) of Icon component | _string_ | `var-icon` | +| `badge` | Logo in the upper right corner of the icon | _boolean \| BadgeProps_ | `false` | + +### Events + +#### BottomNavigation Events + +|Event | Description | Arguments | +| ---- | ---- | ---- | +| `before-change` | The callback function before switching labels, which returns false to prevent switching, supports the return of promise | `active: number \| string` | +| `change` | Triggered when switching labels | `active: number \| string` | +| `fab-click` | Triggered when fab button click | `-` | + +#### BottomNavigationItem Events + +|Event | Description | Arguments | +| ---- | ---- | ---- | +| `click` | Trigger on click | `active: number \| string` | + +### Slots + +#### BottomNavigation Slots + +| Slot | Description | Arguments | +| ---- | ---- | ----| +| `fab` | Support for inserting a custom Fab button into a component | `-` | +#### BottomNavigationItem Slots + +| Slot | Description | Arguments | +| ---- | ---- | ----| +| `default` | Custom label text content that overwrites the content of `label` | `-` | +| `icon` | Custom Icon | `active: boolean` | + +### Style Variables +Here are the CSS variables used by the component, Styles can be customized using [StyleProvider](#/en-US/style-provider) + +#### BottomNavigation Variables + +| Variable | Default | +| --- | --- | +| `--bottom-navigation-height` | `50px` | +| `--bottom-navigation-z-index` | `1` | +| `--bottom-navigation-background-color` | `#fff` | +| `--bottom-navigation-border-color` | `#bcc2cb` | +| `--bottom-navigation-fab-offset` | `4px` | + +#### BottomNavigationItem Variables + +| Variable | Default | +| --- | --- | +| `--bottom-navigation-item-font-size` | `var(--font-size-sm)` | +| `--bottom-navigation-item-inactive-color` | `#646566` | +| `--bottom-navigation-item-active-color` | `var(--color-primary)` | +| `--bottom-navigation-item-active-background-color` | `#fff` | +| `--bottom-navigation-item-line-height` | `1` | +| `--bottom-navigation-item-icon-size` | `22px` | +| `--bottom-navigation-item-icon-margin-bottom` | `5px` | diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/docs/zh-CN.md b/packages/varlet-vue2-ui/src/bottom-navigation/docs/zh-CN.md new file mode 100644 index 00000000..56a4837a --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/docs/zh-CN.md @@ -0,0 +1,247 @@ +# 底部导航栏 + +### 基础用法 + +```html + + + +``` + +### 名称匹配 + +```html + + + +``` + +### 徽标提示 + +```html + + + +``` + +### 自定义颜色 + +```html + + + +``` + +### 监听切换事件 + +```html + + + +``` + +### 监听点击事件 + +```html + + + +``` + +### 悬浮按钮 + +Item 数量为偶数时,悬浮按钮在中间位置,为奇数时在最右侧。 + +```html + + + + + +``` + +## API + +### 属性 + +#### BottomNavigation Props + +| 参数 | 说明 | 类型 | 默认值 | +|------------------|-----------------| ---- | ---- | +| `active.sync` | 选中标签的名称或者索引值 | _number \| string_ | `0` | +| `fixed` | 是否固定在底部 | _boolean_ | `false` | +| `border` | 是否显示外边框 | _boolean_ | `false` | +| `z-index` | 元素 z-index | _number \| string_ | `1` | +| `active-color` | 选中标签的颜色 | _string_ | `-` | +| `inactive-color` | 未选中标签的颜色 | _string_ | `-` | +| `fab-props` | 悬浮按钮属性 | _ButtonProps_ | `{type: "primary"}` | + + +#### BottomNavigationItem Props + +|参数 | 说明 | 类型 | 默认值 | +| ---- | ---- | ---- | ---- | +| `name` | 标签名称,作为匹配的标识符 | _string_ | `-` | +| `icon` | 图标名称,等同于 Icon 组件的 [name](/#/zh-CN/icon) | _string_ | `-` | +| `label` | 标签文字内容 | _string_ | - | +| `namespace` | 图标的命名空间, 可扩展自定义图标库,等同于 Icon 组件的 [namespace](/#/zh-CN/icon) | _string_ | `var-icon` | +| `badge` | 图标右上角徽标 | _boolean \| BadgeProps_ | `false` | + + +### 事件 + +#### BottomNavigation Events + +|事件名 | 说明 | 回调参数 | +| ---- | ---- | ---- | +| `before-change` | 切换标签前的回调函数,返回 false 可阻止切换,支持返回 Promise | `active: number \| string` | +| `change` | 切换标签时触发 | `active: number \| string` | +| `fab-click` | 悬浮按钮点击时触发 | `-` | + +#### BottomNavigationItem Events + +|事件名 | 说明 | 回调参数 | +| ---- | ---- | ---- | +| `click` | 点击时触发 | `active: number \| string` | + +### 插槽 + +#### BottomNavigation Slots + +| 名称 | 说明 | 参数 | +| ---- | ---- | ----| +| `fab` | 支持在组件中插入一个自定义的 fab 按钮 | `-` | + +#### BottomNavigationItem Slots + +| 名称 | 说明 | 参数 | +| ---- | ---- | ----| +| `default` | 自定义标签文字内容,会覆盖 `label` 的内容 | `-` | +| `icon` | 自定义图标 | `active: boolean` | + +### 样式变量 +以下为组件使用的 css 变量,可以使用 [StyleProvider 组件](#/zh-CN/style-provider) 进行样式定制 + +#### BottomNavigation Variables + +| 变量名 | 默认值 | +| --- | --- | +| `--bottom-navigation-height` | `50px` | +| `--bottom-navigation-z-index` | `1` | +| `--bottom-navigation-background-color` | `#fff` | +| `--bottom-navigation-border-color` | `#bcc2cb` | +| `--bottom-navigation-fab-offset` | `4px` | + +#### BottomNavigationItem Variables + +| 变量名 | 默认值 | +| --- | --- | +| `--bottom-navigation-item-font-size` | `var(--font-size-sm)` | +| `--bottom-navigation-item-inactive-color` | `#646566` | +| `--bottom-navigation-item-active-color` | `var(--color-primary)` | +| `--bottom-navigation-item-active-background-color` | `#fff` | +| `--bottom-navigation-item-line-height` | `1` | +| `--bottom-navigation-item-icon-size` | `22px` | +| `--bottom-navigation-item-icon-margin-bottom` | `5px` | diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/example/index.vue b/packages/varlet-vue2-ui/src/bottom-navigation/example/index.vue new file mode 100644 index 00000000..cedc2416 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/example/index.vue @@ -0,0 +1,120 @@ + + + diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/example/locale/en-US.ts b/packages/varlet-vue2-ui/src/bottom-navigation/example/locale/en-US.ts new file mode 100644 index 00000000..4b0f1456 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/example/locale/en-US.ts @@ -0,0 +1,10 @@ +export default { + basicUsage: 'Basic Usage', + matchByName: 'Match by name', + showBadge: 'Show Badge', + customColor: 'Custom Color', + changeEvent: 'Change Event', + clickEvent: 'Click Event', + fab: 'Fab', + label: 'label', +} diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/example/locale/index.ts b/packages/varlet-vue2-ui/src/bottom-navigation/example/locale/index.ts new file mode 100644 index 00000000..d2e375e6 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/example/locale/index.ts @@ -0,0 +1,23 @@ +// lib +import _zhCN from '../../../locale/zh-CN' +import _enCN from '../../../locale/en-US' +// mobile example doc +import zhCN from './zh-CN' +import enUS from './en-US' +import { useLocale, add as _add, use as _use } from '../../../locale' + +const { add, use: exampleUse, pack, packs, merge } = useLocale() + +const use = (lang: string) => { + _use(lang) + exampleUse(lang) +} + +export { add, pack, packs, merge, use } + +// lib +_add('zh-CN', _zhCN) +_add('en-US', _enCN) +// mobile example doc +add('zh-CN', zhCN as any) +add('en-US', enUS as any) diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/example/locale/zh-CN.ts b/packages/varlet-vue2-ui/src/bottom-navigation/example/locale/zh-CN.ts new file mode 100644 index 00000000..14a87146 --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/example/locale/zh-CN.ts @@ -0,0 +1,10 @@ +export default { + basicUsage: '基本使用', + matchByName: '通过名称匹配', + showBadge: '徽标提示', + customColor: '自定义颜色', + changeEvent: '监听切换事件', + clickEvent: '监听点击事件', + fab: '悬浮按钮', + label: '标签', +} diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/index.ts b/packages/varlet-vue2-ui/src/bottom-navigation/index.ts new file mode 100644 index 00000000..f4a1735e --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/index.ts @@ -0,0 +1,10 @@ +import type { VueConstructor } from 'vue' +import BottomNavigation from './BottomNavigation.vue' + +BottomNavigation.install = function (app: VueConstructor) { + app.component(BottomNavigation.name, BottomNavigation) +} + +export const _BottomNavigationComponent = BottomNavigation + +export default BottomNavigation diff --git a/packages/varlet-vue2-ui/src/bottom-navigation/props.ts b/packages/varlet-vue2-ui/src/bottom-navigation/props.ts new file mode 100644 index 00000000..80c1e52c --- /dev/null +++ b/packages/varlet-vue2-ui/src/bottom-navigation/props.ts @@ -0,0 +1,39 @@ +import type { PropType } from 'vue' +import type { ButtonProps } from '../../types' + +export const props = { + active: { + type: [Number, String] as PropType, + default: 0, + }, + fixed: { + type: Boolean, + default: false, + }, + border: { + type: Boolean, + default: false, + }, + zIndex: { + type: [Number, String] as PropType, + default: 1, + }, + activeColor: { + type: String, + }, + inactiveColor: { + type: String, + }, + onChange: { + type: Function as PropType<(active: number | string) => void>, + }, + onBeforeChange: { + type: Function as PropType<(active: number | string) => boolean | Promise>, + }, + onFabClick: { + type: Function as PropType<() => void>, + }, + fabProps: { + type: Object as PropType>, + }, +} diff --git a/packages/varlet-vue2-ui/src/themes/dark/bottomNavigation.ts b/packages/varlet-vue2-ui/src/themes/dark/bottomNavigation.ts new file mode 100644 index 00000000..891659df --- /dev/null +++ b/packages/varlet-vue2-ui/src/themes/dark/bottomNavigation.ts @@ -0,0 +1,4 @@ +export default { + '--bottom-navigation-background-color': '#272727', + '--bottom-navigation-border-color': '#444', +} diff --git a/packages/varlet-vue2-ui/src/themes/dark/bottomNavigationItem.ts b/packages/varlet-vue2-ui/src/themes/dark/bottomNavigationItem.ts new file mode 100644 index 00000000..c53649b1 --- /dev/null +++ b/packages/varlet-vue2-ui/src/themes/dark/bottomNavigationItem.ts @@ -0,0 +1,3 @@ +export default { + '--bottom-navigation-item-active-background-color': '#272727', +} diff --git a/packages/varlet-vue2-ui/src/themes/dark/index.ts b/packages/varlet-vue2-ui/src/themes/dark/index.ts index 288c1d8d..0306eeea 100644 --- a/packages/varlet-vue2-ui/src/themes/dark/index.ts +++ b/packages/varlet-vue2-ui/src/themes/dark/index.ts @@ -26,6 +26,8 @@ import timePicker from './timePicker' import uploader from './uploader' import tabs from './tabs' import appBar from './appBar' +import bottomNavigation from './bottomNavigation' +import bottomNavigationItem from './bottomNavigationItem' export default { // common @@ -66,4 +68,6 @@ export default { ...divider, ...picker, ...appBar, + ...bottomNavigation, + ...bottomNavigationItem, } as StyleVars diff --git a/packages/varlet-vue2-ui/types/bottomNavigation.d.ts b/packages/varlet-vue2-ui/types/bottomNavigation.d.ts new file mode 100644 index 00000000..aa2000a4 --- /dev/null +++ b/packages/varlet-vue2-ui/types/bottomNavigation.d.ts @@ -0,0 +1,22 @@ +import { VarComponent } from './varComponent' +import { ButtonProps } from './button' + +export interface BottomNavigationProps { + active?: number | string + fixed?: boolean + border?: boolean + zIndex?: number | string + activeColor?: string + inactiveColor?: string + fabProps?: Partial + onChange?: (active: string | number) => void + 'onUpdate:active'?: (active: string | number) => void + onBeforeChange?: (active: string | number) => boolean | Promise + onFabClick?: () => void +} + +export class BottomNavigation extends VarComponent { + $props: BottomNavigationProps +} + +export class _BottomNavigationComponent extends BottomNavigation {} diff --git a/packages/varlet-vue2-ui/types/bottomNavigationItem.d.ts b/packages/varlet-vue2-ui/types/bottomNavigationItem.d.ts new file mode 100644 index 00000000..b7e7d911 --- /dev/null +++ b/packages/varlet-vue2-ui/types/bottomNavigationItem.d.ts @@ -0,0 +1,17 @@ +import { VarComponent } from './varComponent' +import { BadgeProps } from './badge' + +export interface BottomNavigationItemProps { + name?: string + icon?: string + label?: string + namespace?: string + badge?: boolean | BadgeProps + onClick?: (active: string | number) => void +} + +export class BottomNavigationItem extends VarComponent { + $props: BottomNavigationItemProps +} + +export class _BottomNavigationItemComponent extends BottomNavigationItem {} diff --git a/packages/varlet-vue2-ui/types/global.d.ts b/packages/varlet-vue2-ui/types/global.d.ts index 3b23792e..9821bbc8 100644 --- a/packages/varlet-vue2-ui/types/global.d.ts +++ b/packages/varlet-vue2-ui/types/global.d.ts @@ -4,6 +4,8 @@ declare module 'vue' { VarAppBar: typeof import('@varlet-vue2/ui')['_AppBarComponent'] VarBackTop: typeof import('@varlet-vue2/ui')['_BackTopComponent'] VarBadge: typeof import('@varlet-vue2/ui')['_BadgeComponent'] + VarBottomNavigation: typeof import('@varlet-vue2/ui')['_BottomNavigationComponent'] + VarBottomNavigationItem: typeof import('@varlet-vue2/ui')['_BottomNavigationItemComponent'] VarButton: typeof import('@varlet-vue2/ui')['_ButtonComponent'] VarCard: typeof import('@varlet-vue2/ui')['_CardComponent'] VarCell: typeof import('@varlet-vue2/ui')['_CellComponent'] diff --git a/packages/varlet-vue2-ui/types/index.d.ts b/packages/varlet-vue2-ui/types/index.d.ts index 8528ee58..5312d7e7 100644 --- a/packages/varlet-vue2-ui/types/index.d.ts +++ b/packages/varlet-vue2-ui/types/index.d.ts @@ -6,6 +6,8 @@ export * from './actionSheet' export * from './appBar' export * from './backTop' export * from './badge' +export * from './bottomNavigation' +export * from './bottomNavigationItem' export * from './button' export * from './card' export * from './cell' diff --git a/packages/varlet-vue2-ui/varlet.config.js b/packages/varlet-vue2-ui/varlet.config.js index a6b66bce..b0b69d4c 100644 --- a/packages/varlet-vue2-ui/varlet.config.js +++ b/packages/varlet-vue2-ui/varlet.config.js @@ -289,6 +289,14 @@ module.exports = { doc: 'app-bar', type: 2, }, + { + text: { + 'zh-CN': 'BottomNavigation 底部导航栏', + 'en-US': 'BottomNavigation', + }, + doc: 'bottom-navigation', + type: 2, + }, { text: { 'zh-CN': '功能指令',