Skip to content

Commit

Permalink
feat: ✨ implement tabs package
Browse files Browse the repository at this point in the history
  • Loading branch information
huang-xiao-jian committed Jul 21, 2020
1 parent c70e2f2 commit 61fdb9d
Show file tree
Hide file tree
Showing 10 changed files with 794 additions and 0 deletions.
13 changes: 13 additions & 0 deletions packages/Tabs/Tabs.constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// packages
import { CSSProperties } from 'react';

export interface TabUnit {
// 私有变量,标记 unit 来源
id: string;
name: string;
title: string;
titleStyle?: CSSProperties;
disabled: boolean;
dot?: boolean;
info?: string;
}
20 changes: 20 additions & 0 deletions packages/Tabs/Tabs.context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// packages
import { createContext } from 'react';
import { TabAction } from './Tabs.redux';

export interface TabsContextPayload {
value: string;
animated: boolean;
dispatch: (action: TabAction) => void;
}

export const TabsContext = createContext<TabsContextPayload>({
// 默认值无所谓
value: '',
animated: false,
dispatch: () => {
throw new Error(
'TabsContext default dispatch should never have been called'
);
},
});
131 changes: 131 additions & 0 deletions packages/Tabs/Tabs.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
.van-tabs {
position: relative;
-webkit-tap-highlight-color: transparent;
}
.van-tabs__wrap {
display: flex;
overflow: hidden;
}
.van-tabs__wrap--scrollable .van-tab {
flex: 0 0 22%;
}
.van-tabs__scroll {
background-color: #fff;
background-color: var(--tabs-nav-background-color, #fff);
}
.van-tabs__scroll--line {
box-sizing: content-box;
height: calc(100% + 15px);
}
.van-tabs__scroll--card {
margin: 0 16px;
margin: 0 var(--padding-md, 16px);
}
.van-tabs__scroll::-webkit-scrollbar {
display: none;
}
.van-tabs__nav {
position: relative;
display: flex;
user-select: none;
}
.van-tabs__nav--card {
box-sizing: border-box;
height: 30px;
height: var(--tabs-card-height, 30px);
border: 1px solid #ee0a24;
border: var(--border-width-base, 1px) solid var(--tabs-default-color, #ee0a24);
border-radius: 2px;
border-radius: var(--border-radius-sm, 2px);
}
.van-tabs__nav--card .van-tab {
color: #ee0a24;
color: var(--tabs-default-color, #ee0a24);
line-height: calc(30px - 2 * 1px);
line-height: calc(
var(--tabs-card-height, 30px) - 2 * var(--border-width-base, 1px)
);
border-right: 1px solid #ee0a24;
border-right: var(--border-width-base, 1px) solid
var(--tabs-default-color, #ee0a24);
}
.van-tabs__nav--card .van-tab:last-child {
border-right: none;
}
.van-tabs__nav--card .van-tab.van-tab--active {
color: #fff;
color: var(--white, #fff);
background-color: #ee0a24;
background-color: var(--tabs-default-color, #ee0a24);
}
.van-tabs__nav--card .van-tab--disabled {
color: #c8c9cc;
color: var(--tab-disabled-text-color, #c8c9cc);
}
.van-tabs__line {
position: absolute;
bottom: 0;
left: 0;
z-index: 1;
height: 3px;
height: var(--tabs-bottom-bar-height, 3px);
border-radius: 3px;
border-radius: var(--tabs-bottom-bar-height, 3px);
background-color: #ee0a24;
background-color: var(--tabs-bottom-bar-color, #ee0a24);
}
.van-tabs__track {
position: relative;
width: 100%;
height: 100%;
}
.van-tabs__track--animated {
display: flex;
transition-property: transform;
}
.van-tabs__content {
overflow: hidden;
}
.van-tabs--line .van-tabs__wrap {
height: 44px;
height: var(--tabs-line-height, 44px);
}
.van-tabs--card .van-tabs__wrap {
height: 30px;
height: var(--tabs-card-height, 30px);
}
.van-tab {
position: relative;
flex: 1;
box-sizing: border-box;
min-width: 0;
/* hack for flex ellipsis */
padding: 0 5px;
text-align: center;
cursor: pointer;
color: #646566;
color: var(--tab-text-color, #646566);
font-size: 14px;
font-size: var(--tab-font-size, 14px);
line-height: 44px;
line-height: var(--tabs-line-height, 44px);
}
.van-tab--active {
font-weight: 500;
font-weight: var(--font-weight-bold, 500);
color: #323233;
color: var(--tab-active-text-color, #323233);
}
.van-tab--disabled {
color: #c8c9cc;
color: var(--tab-disabled-text-color, #c8c9cc);
}
.van-tab--complete {
flex: 1 0 auto !important;
}
.van-tab__title__info {
position: relative !important;
top: -1px !important;
display: inline-block;
transform: translateX(0) !important;
}
54 changes: 54 additions & 0 deletions packages/Tabs/Tabs.redux.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// packages
import { Reducer } from 'react';
// internal
import { TabUnit } from './Tabs.constant';

// 子组件上报信息
export interface TabState {
// 提取 name 快速获取 index
names: string[];
// 子组件上报信息管理
units: TabUnit[];
}

export enum TabActionTypes {
SettleTab = 'SettleTab', // 注册 tab 条目
SelectTab = 'SelectTab', // 选择 tab 条目
}

export interface SettleTabAction {
type: TabActionTypes.SettleTab;
payload: TabUnit;
}

export interface SelectTabAction {
type: TabActionTypes.SelectTab;
payload: {
value: string;
};
}

export type TabAction = SettleTabAction | SelectTabAction;

function handleSettleTab(state: TabState, action: SettleTabAction): TabState {
const match = state.units.find((unit) => unit.id === action.payload.id);
const units = match
? state.units.map((unit) =>
unit.id === match.id ? { ...match, ...action.payload } : unit
)
: [...state.units, action.payload];
const names = units.map((unit) => unit.name);

return { ...state, units, names };
}

export function tabsReducer(state: TabState, action: TabAction): TabState {
switch (action.type) {
case TabActionTypes.SettleTab:
return handleSettleTab(state, action);
default:
return state;
}
}

export type TabsReducerType = Reducer<TabState, TabAction>;
95 changes: 95 additions & 0 deletions packages/Tabs/Tabs.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* @description - concatate styles
* @author - huang.jian <[email protected]>
*/

// package
import { CSSProperties } from 'react';

// internal
import pickStyle from '../tools/pick-style';

export function ofScrollView(color: string): CSSProperties {
return pickStyle({
borderColor: color,
});
}

export function ofNaviView(
color: string,
type: 'line' | 'card'
): CSSProperties {
return pickStyle({
borderColor: type === 'card' ? color : '',
});
}

export function ofUnit(
active: boolean,
ellipsis: boolean,
color: string,
type: 'line' | 'card',
disabled: boolean,
activeColor: string | undefined,
inactiveColor: string | undefined,
swipeThreshold: number,
scrollable: boolean
): CSSProperties {
const style: CSSProperties = {};

// card theme color
if (color && type === 'card') {
style.borderColor = color;

if (!disabled) {
if (active) {
style.backgroundColor = color;
} else {
style.color = color;
}
}
}

if (scrollable && ellipsis) {
style.flexBasis = `${88 / swipeThreshold}%`;
}
style.color = active ? activeColor : inactiveColor;

return pickStyle(style);
}

export function ofTrackTranslateX(
animated: boolean,
index: number,
deltaX: number
): string {
if (animated) {
return deltaX >= 0
? `calc(${-100 * index}% + ${deltaX}px)`
: `calc(${-100 * index}% - ${Math.abs(deltaX)}px)`;
}

return `${deltaX}px`;
}

export function ofTrack(
animated: boolean,
index: number,
duration: number,
dragging: boolean,
deltaX: number
): CSSProperties {
const style: CSSProperties = {};

if (dragging) {
const translateX = ofTrackTranslateX(animated, index, deltaX);

style.transform = `translateX(${translateX})`;
style.transitionDuration = '0ms';
} else if (animated) {
style.transform = `translateX(${-100 * index}%)`;
style.transitionDuration = `${duration}ms`;
}

return style;
}
Loading

0 comments on commit 61fdb9d

Please sign in to comment.