mqtik / macos-tabs

macOS-like Tabs for React (designed specifically for Electron)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

MacOS Tabs

npm version

MacOS Tabs

Installation

npm install --save macos-tabs

or

yarn add macos-tabs

Usage

API

MacOSTabs

type Props = {
	// onClick event when the user clicks on the AddTabButton on a tab header
	onAddTabButtonClick: (e: Event) => void,

	// onClick event when the user clicks on the CloseTabButton on a tab header
	onCloseTabButtonClick: (e: Event, index: number) => void,

	// Event when the user stops dragging a header
	// The updated tabs contain the new ordering, you can directly update your state with
	// these returned tabs
	// The updated index of the active tab is also provided
	onDragStop: (e: Event, index: number, tabs: Tabs) => void,

	// Event when the user clicks on a tab header
	// The index of the clicked header is passed back
	onTabClick: (e: Event, index: number) => void,

	// Event when the user's mouse enters a tab header
	onTabMouseEnter: (e: Event, index: number) => void,

	// Even when the user's mouse leaves a tab header
	onTabMouseLeave: (e: Event, index: number) => void,

	// Event when the activeTabIndex is updated internally
	// This allows you to keep track of the activeTabIndex even
	// if you are letting MacOSTabs handle the state
	onSetActiveTab: (index: number) => void,

	// Specify the position of addTabButton
	addTabPosition: 'none' | 'start' | 'end',

	// Declare whether or not you want to manage the activeTabIndex
	autoActiveTab: boolean,

	// Can be used to set initial active tab index
	// If autoActiveTab is false, then you should have methods
	// to manage the activeTabIndex state
	activeTabIndex: number,

	// Tab content should be wrapped in a TabBody component with the correct props
	// Tabs order can be modified programmatically by manually changing the order
	// of the array
	tabs: Tabs,

	// Declare a div or react component to render when there are no tabs open
	defaultContent: Object,

	// Flag to show or hide tab headers
	showHeader: boolean,

	// Declare the header height in px (note: styles have not been tested with
	// anything besides the default of 24px, this is a WIP)
	headerHeight: number | string,

	// Declare a custom element that the body should be rendered into
	// instead of directly below the tab headers
	// i.e. <div id="tabBody" />
	customBodyElementId: string,

	// Set tab scroll behavior
	scrollX: 'normal' | 'reversed' | 'disabled',

	// Set tab scroll behavior
	scrollY: 'normal' | 'reversed' | 'disabled',

	// Apply custom styles to specified component(s)
	styles: {
		addTabButton?: Object,
		closeTabButton?: Object,
		tab?: Object,
		tabActive?: Object,
		outerTabContainer?: Object,
		innerTabContainer?: Object
	},

	// Apply custom classnames to specified component(s)
	classNames: {
		addTabButton?: string,
		closeTabButton?: string,
		tab?: string,
		tabActive?: string,
		outerTabContainer?: string,
		innerTabContainer?: string
	},

	// Override icons on buttons
	icons: {
		addTabButton?: Object | string,
		closeTabButton?: Object | string
	},

	// Experimental/Not Completed
	onDragOut: (e: Event, outTabIndex: number) => void,
	dragOutDistance: number
}

static defaultProps = {
	tabs: [],
	activeTabIndex: 0,
	scrollX: 'normal',
	scrollY: 'disabled',
	addTabPosition: 'end',
	showHeader: true,
	headerHeight: 24,
	dragOutDistance: 40,
	autoActiveTab: true,
	styles: {},
	classNames: {},
	icons: {}
}

TabBody

type Props = {
	// Label that will be displayed on the tab header
	label: string | number,

	// Children components to render
	children?: HTMLElement | Component<*, *, *>,

	// Unique tabId
	tabId: string | number,

	// Optional styles/classnames

	// Set additional TabBody styles
	// Note: In most cases, setting styles on the child component is sufficient
	style?: Object,

	// Set TabBody classname
	className?: string
}

static defaultProps = {
	label: ''
}

Minimal Example

import React, { Component } from 'react'
import MacOSTabs, { TabBody } from 'macos-tabs'

const defaultStyles = {
	height: '100%'
}

export default class Home extends Component {
	constructor(props) {
		super(props)

		this.id = 0

		this.state = {
			tabs: [
				this.makeTab(this.id++),
				this.makeTab(this.id++),
				this.makeTab(this.id++)
			]
		}
	}

	makeTab(id) {
		return (
			<TabBody label={ `Test Tab ${ id }` } tabId={ id } key={ id }>
				<div
					style={{
						height: '100%',
						border: '1px solid red',
						textAlign: 'center',
						boxSizing: 'border-box',
						paddingTop: '20%'
					}}
				>
					Hello { id }!
				</div>
			</TabBody>
		)
	}

	onDragStop(e, activeTabIndex, tabs) {
		this.setState({
			tabs
		})
	}

	onAddTabButtonClick(e) {
		const newTabs = this.state.tabs.slice(0)

		newTabs.push(this.makeTab(this.id++))

		this.setState({
			tabs: newTabs
		})
	}

	onCloseTabButtonClick(e, closedTabIndex) {
		const newTabs = this.state.tabs.slice(0)

		newTabs.splice(closedTabIndex, 1)

		this.setState({
			tabs: newTabs
		})
	}

	render() {
		return (
			<div style={ defaultStyles }>
				<MacOSTabs
					tabs={ this.state.tabs }
					onDragStop={ this.onDragStop.bind(this) }
					onAddTabButtonClick={ this.onAddTabButtonClick.bind(this) }
					onCloseTabButtonClick={ this.onCloseTabButtonClick.bind(this) }
				/>
			</div>
		)
	}
}

Controlled Active Tab & CustomBodyElementID Example

import React, { Component } from 'react'
import MacOSTabs, { TabBody } from 'macos-tabs'

const defaultStyles = {
	height: '100%'
}

export default class Home extends Component {
	constructor(props) {
		super(props)

		this.id = 0

		this.state = {
			tabs: [
				this.makeTab(this.id++),
				this.makeTab(this.id++),
				this.makeTab(this.id++)
			],
			activeTabIndex: 0
		}
	}

	makeTab(id) {
		return (
			<TabBody label={ `Test Tab ${ id }` } tabId={ id } key={ id }>
				<div
					style={{
						height: '100%',
						border: '1px solid red',
						textAlign: 'center',
						boxSizing: 'border-box',
						paddingTop: '20%'
					}}
				>
					<form>
						<label>
							Name { id }:
							<input type="text" name="name" />
						</label>
						<input type="submit" value="Submit" />
					</form>
				</div>
			</TabBody>
		)
	}

	onDragStop(e, activeTabIndex, tabs) {
		this.setState({
			tabs,
			activeTabIndex
		})
	}

	onAddTabButtonClick(e) {
		const newTabs = this.state.tabs.slice(0)

		newTabs.push(this.makeTab(this.id++))

		this.setState({
			tabs: newTabs,
			activeTabIndex: newTabs.length - 1
		})
	}

	onCloseTabButtonClick(e, closedTabIndex) {
		const newTabs = this.state.tabs.slice(0)

		newTabs.splice(closedTabIndex, 1)

		if (closedTabIndex <= this.state.activeTabIndex) {
			this.setState({
				tabs: newTabs,
				activeTabIndex: (this.state.activeTabIndex === 0) ? 0 : this.state.activeTabIndex - 1
			})
		} else {
			this.setState({
				tabs: newTabs
			})
		}
	}

	onSetActiveTab(index) {
		this.setState({
			activeTabIndex: index
		})
	}

	render() {
		return (
			<div style={ defaultStyles }>
				<MacOSTabs
					tabs={ this.state.tabs }
					onDragStop={ this.onDragStop.bind(this) }
					onAddTabButtonClick={ this.onAddTabButtonClick.bind(this) }
					onCloseTabButtonClick={ this.onCloseTabButtonClick.bind(this) }
					onSetActiveTab={ this.onSetActiveTab.bind(this) }
					activeTabIndex={ this.state.activeTabIndex }
					defaultContent={ <div>Add a tab to get started!</div> }
					customBodyElementId='custom1'
				/>
				<div id='custom1' style={{ height: 'calc(100% - 24px)' }} />
			</div>
		)
	}
}

About

macOS-like Tabs for React (designed specifically for Electron)

License:MIT License


Languages

Language:JavaScript 93.6%Language:CSS 6.4%