UNPKG

react-panels

Version:

React.js panel component with tabs, toolbars, buttons, floating windows and customizable themes

1,821 lines (1,707 loc) 71.4 kB
/* * react-panels * https://github.com/Theadd/react-panels * * Copyright (c) 2015 R.Beltran https://github.com/Theadd * Licensed under the MIT license. */ (function() { var flexbox2Skin = function (skin) { switch (skin || "") { default: return { tabColor: "#b0b0b0", tabIconColor: "#b0b0b0", activeTabColor: "#daaf64", tabTextShadow: "#000000", activeTabTextShadow: "#000000", titleTextShadow: "#a6a6a6", iconTextShadow: "#000000", iconColor: "#daaf64", titleColor: "#daaf64", buttonBackgroundColor: "rgba(104, 226, 207, 0.15)", hoverButtonBackgroundColor: "rgba(104, 226, 207, 0.3)", activeButtonBackgroundColor: "rgba(131, 247, 220, 0.33)", buttonColor: "#eaeaea", hoverButtonColor: "#ffffff", activeButtonColor: "#daaf64", buttonTextShadow: "#7F7F7F", highlightedButtonBoxShadow: "rgba(255, 255, 255, 0.6)", tabBackgroundColor: "rgba(104, 226, 207, 0.15)", activeTabBackgroundColor: "rgba(131, 247, 220, 0.33)", hoverTabBackgroundColor: "rgba(104, 226, 207, 0.3)", toolbarBackgroundColor: "rgba(171, 255, 220, 0.2)", contentBackgroundColor: "rgba(171, 255, 220, 0.34)", footerBackgroundColor: "rgba(171, 255, 220, 0.2)", borderColor: "#000000" }; } } var flexbox2Style = function (_opts, skin) { var colors, opts = React.addons.update({ skin: "default", renderPanelBorder: true, activeTabHeaderBorder: true }, {$merge: _opts}), isSafari = /Safari/.test(window.navigator.userAgent) && /Apple Computer/.test(window.navigator.vendor); skin = skin || opts.skin; if (typeof skin === "object") { colors = React.addons.update(flexbox2Skin(), {$merge: skin}); } else { colors = flexbox2Skin(skin); } return { PanelWrapper: { config: { autocompact: false } }, Panel: { style: { borderTop: (opts.renderPanelBorder) ? "1px solid " + colors.borderColor : "0 none", borderRight: (opts.renderPanelBorder) ? "1px solid " + colors.borderColor : "0 none" }, header: { style: { backgroundColor: "transparent", display: isSafari ? "-webkit-flex" : "flex", minWidth: "100%", marginBottom: "-2px" } }, tabsStart: { style: { width: 0 } }, tabsEnd: { style: { width: 0 } }, tabs: { style: { float: "none", WebkitFlex: "1", flex: 1, display: isSafari ? "-webkit-flex" : "flex", overflow: "hidden" } }, icon: { style: { color: colors.iconColor, textShadow: "2px 2px 2px " + colors.iconTextShadow, float: "left" } }, box: { style: { float: "left" } }, title: { style: { color: colors.titleColor, textShadow: "1px 1px 1px " + colors.titleTextShadow } }, group: { style: { padding: 0, display: "inline-block", height: "100%", margin: 0 } }, body: { style: { borderLeft: (opts.renderPanelBorder) ? "1px solid " + colors.borderColor : "0 none", height: "calc(100% - " + Utils.pixelsOf(opts.headerHeight - 2) + ")" } } }, TabButton: { style: { borderBottom: "1px solid " + colors.borderColor, borderRight: "1px solid " + colors.borderColor, backgroundColor: colors.tabBackgroundColor, height: opts.headerHeight - 1, margin: "0", position: "inherit", float: "none", overflow: "hidden", WebkitFlex: "1", flex: "1 0 0px", opacity: 1 }, state: { hover: { style: { backgroundColor: colors.hoverTabBackgroundColor } } }, mods: { active: { style: { borderBottom: "1px solid " + (opts.activeTabHeaderBorder ? colors.borderColor : colors.activeTabBackgroundColor), backgroundColor: colors.activeTabBackgroundColor }, state: { hover: { style: { borderBottom: "1px solid " + (opts.activeTabHeaderBorder ? colors.borderColor : colors.activeTabBackgroundColor), backgroundColor: colors.activeTabBackgroundColor }, icon: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.tabTextShadow } }, title: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.activeTabTextShadow } } } }, icon: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.tabTextShadow } }, title: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.activeTabTextShadow } } }, last: { style: { borderRight: "0 none" } } }, icon: { style: { color: colors.tabIconColor, textShadow: "1px 1px 1px " + colors.tabTextShadow, opacity: 1 } }, title: { style: { color: colors.tabColor, textShadow: "1px 1px 1px " + colors.tabTextShadow } }, box: { style: { marginRight: 0, maxWidth: "calc(100% - " + Utils.pixelsOf(opts.headerHeight) + ")", opacity: 1 } } }, Tab: { toolbar: { style: { minHeight: 0, lineHeight: "inherit", padding: "0", display: "block", position: "relative", marginTop: "1px" }, children: { style: { padding: "10px", lineHeight: Utils.pixelsOf(opts.headerHeight), position: "relative", backgroundColor: colors.toolbarBackgroundColor } } }, content: { style: { backgroundColor: colors.contentBackgroundColor, boxShadow: "0px 0px 29px rgba(0, 0, 0, 0.7) inset", borderTop: "1px solid " + colors.borderColor, position: "relative" }, children: { style: { position: "relative" } } }, footer: { style: { backgroundColor: colors.footerBackgroundColor, borderTop: "1px solid " + colors.borderColor, position: "relative" }, children: { style: { position: "relative" } } } }, Button: { style: { height: Utils.pixelsOf(opts.headerHeight - 1), backgroundColor: colors.buttonBackgroundColor, borderBottom: "1px solid " + colors.borderColor, borderLeft: "1px solid " + colors.borderColor }, children: { style: { color: colors.buttonColor, textShadow: "1px 1px 1px " + colors.buttonTextShadow } }, state: { hover: { style: { backgroundColor: colors.hoverButtonBackgroundColor }, children: { style: { color: colors.hoverButtonColor } } } }, mods: { active: { style: { backgroundColor: colors.activeButtonBackgroundColor }, children: { style: { color: colors.activeButtonColor } } }, highlighted: { style: { boxShadow: "0 0 9px " + colors.highlightedButtonBoxShadow + " inset" } }, disabled: { style: { pointerEvents: "none", opacity: 0.5 } } } } }; }; var flexboxStyle = function (opts, skin) { var colors, isSafari = /Safari/.test(window.navigator.userAgent) && /Apple Computer/.test(window.navigator.vendor); skin = skin || opts.skin; switch (skin) { case "fiery": colors = { tabColor: "#b0b0b0", tabIconColor: "#616161", activeTabColor: "#f72121", tabTextShadow: "#000000", activeTabTextShadow: "#000000", titleTextShadow: "#a6a6a6", iconTextShadow: "#000000", iconColor: "#ffffff", titleColor: "#ffffff", buttonBackgroundColor: "#202020", hoverButtonBackgroundColor: "#342828", activeButtonBackgroundColor: "#4d2c2c", buttonColor: "#eaeaea", hoverButtonColor: "#ffffff", activeButtonColor: "#f72121", buttonTextShadow: "#7F7F7F", tabBackgroundColor: "#202020", activeTabBackgroundColor: "#2e2e2e", hoverTabBackgroundColor: "#342828", toolbarBackgroundColor: "#4d2c2c", contentBackgroundColor: "#3e3e3e", footerBackgroundColor: "#4e4e4e" }; break; default: colors = { tabColor: "#b0b0b0", tabIconColor: "#616161", activeTabColor: "#ffffff", tabTextShadow: "#000000", activeTabTextShadow: "#7F7F7F", titleTextShadow: "#a6a6a6", iconTextShadow: "#a6a6a6", iconColor: "#ffffff", titleColor: "#ffffff", buttonBackgroundColor: "#202020", hoverButtonBackgroundColor: "#2a2a2a", activeButtonBackgroundColor: "#4e4e4e", buttonColor: "#eaeaea", hoverButtonColor: "#ffffff", activeButtonColor: "#ffffff", buttonTextShadow: "#7F7F7F", tabBackgroundColor: "#202020", activeTabBackgroundColor: "#2e2e2e", hoverTabBackgroundColor: "#2a2a2a", toolbarBackgroundColor: "#4e4e4e", contentBackgroundColor: "#3e3e3e", footerBackgroundColor: "#4e4e4e" }; break; } return { PanelWrapper: { config: { autocompact: false } }, Panel: { style: { backgroundColor: "black", padding: "1px 1px 0 0" }, header: { style: { backgroundColor: "transparent", display: isSafari ? "-webkit-flex" : "flex", minWidth: "100%" } }, tabsStart: { style: { width: 0 } }, tabsEnd: { style: { width: 0 } }, tabs: { style: { float: "none", WebkitFlex: "1", flex: 1, display: isSafari ? "-webkit-flex" : "flex", overflow: "hidden" } }, icon: { style: { color: colors.iconColor, textShadow: "2px 2px 2px " + colors.iconTextShadow, float: "left" } }, box: { style: { float: "left" } }, title: { style: { color: colors.titleColor, textShadow: "1px 1px 1px " + colors.titleTextShadow } }, group: { style: { padding: 0, display: "inline-block", height: "100%", margin: 0 } }, body: { style: { marginLeft: "1px" } } }, TabButton: { style: { backgroundColor: colors.tabBackgroundColor, height: opts.headerHeight - 1, margin: "0 0 1px 1px", position: "inherit", float: "none", overflow: "hidden", WebkitFlex: "1", flex: "1 0 0px" }, state: { hover: { style: { backgroundColor: colors.hoverTabBackgroundColor } } }, mods: { active: { style: { backgroundColor: colors.activeTabBackgroundColor }, state: { hover: { style: { backgroundColor: colors.activeTabBackgroundColor }, icon: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.tabTextShadow } }, title: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.activeTabTextShadow } } } }, icon: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.tabTextShadow } }, title: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.activeTabTextShadow } } } }, icon: { style: { color: colors.tabIconColor, textShadow: "1px 1px 1px " + colors.tabTextShadow } }, title: { style: { color: colors.tabColor, textShadow: "1px 1px 1px " + colors.tabTextShadow } }, box: { style: { marginRight: 0, maxWidth: "calc(100% - " + Utils.pixelsOf(opts.headerHeight) + ")" } } }, Tab: { toolbar: { style: { minHeight: 0, lineHeight: "inherit", padding: "0", display: "block", position: "relative", top: "-1px" }, children: { style: { padding: "10px", lineHeight: Utils.pixelsOf(opts.headerHeight), position: "relative", marginTop: "1px", backgroundColor: colors.toolbarBackgroundColor } } }, content: { style: { backgroundColor: colors.contentBackgroundColor, marginBottom: "1px" } }, footer: { style: { backgroundColor: colors.footerBackgroundColor, marginBottom: "1px" } } }, Button: { style: { height: Utils.pixelsOf(opts.headerHeight - 1), backgroundColor: colors.buttonBackgroundColor, marginLeft: "1px" }, children: { style: { color: colors.buttonColor, textShadow: "1px 1px 1px " + colors.buttonTextShadow } }, state: { hover: { style: { backgroundColor: colors.hoverButtonBackgroundColor }, children: { style: { color: colors.hoverButtonColor } } } }, mods: { active: { style: { backgroundColor: colors.activeButtonBackgroundColor }, children: { style: { color: colors.activeButtonColor } } } } } }; }; var chemicalStyle = function (opts, skin) { var colors; skin = skin || opts.skin; switch (skin) { case "blueish": colors = { tabColor: "rgba(0, 0, 0, 0.8)", activeTabColor: "rgba(0, 0, 0, 0.9)", tabTextShadow: "#bbbbbb", activeTabTextShadow: "#999999", activeTabBackgroundColor: "rgba(102, 143, 182, 0.25)", activeTabBorderColor: "rgba(0, 0, 0, 0.5)", titleTextShadow: "#a6a6a6", iconTextShadow: "rgba(0, 0, 0, 0.9)", iconColor: "rgba(0, 0, 0, 0.8)", titleColor: "rgba(0, 0, 0, 0.8)", toolbarBoxShadow: "rgba(0, 0, 0, 0.1)", contentBackgroundColorWithToolbar: "rgba(102, 143, 182, 0.25)", footerBackgroundColor: "rgba(165, 165, 165, 0.32)", hoverTabBackgroundColor: "rgba(224, 230, 240, 0.65)", buttonBackgroundColor: "rgba(224, 230, 240, 0.65)", hoverButtonBackgroundColor: "rgba(102, 143, 182, 0.25)", activeButtonBackgroundColor: "rgba(102, 143, 182, 0.25)" }; break; default: colors = { tabColor: "#ffffff", activeTabColor: "rgba(0, 0, 0, 0.9)", tabTextShadow: "#bbbbbb", activeTabTextShadow: "#999999", activeTabBackgroundColor: "rgba(255, 255, 255, 0.9)", activeTabBorderColor: "rgba(0, 0, 0, 0.5)", titleTextShadow: "#a6a6a6", iconTextShadow: "rgba(0, 0, 0, 0.9)", iconColor: "#ffffff", titleColor: "#ffffff", toolbarBoxShadow: "rgba(0, 0, 0, 0.1)", contentBackgroundColorWithToolbar: "rgba(255, 255, 255, 0.85)", footerBackgroundColor: "rgba(224, 230, 240, 0.8)", hoverTabBackgroundColor: "rgba(224, 230, 240, 0.65)", buttonBackgroundColor: "rgba(255, 255, 255, 0.2)", hoverButtonBackgroundColor: "rgba(255, 255, 255, 0.9)", activeButtonBackgroundColor: "rgba(255, 255, 255, 0.9)" }; break; } return { Panel: { header: { style: { backgroundColor: "transparent", paddingRight: Utils.pixelsOf(opts.headerHeight) } }, tabsStart: { style: { width: 50, float: "left" } }, tabsEnd: { style: { width: 10, float: "right" } }, tabs: { style: { float: "left" } }, icon: { style: { color: colors.iconColor, textShadow: "2px 2px 2px " + colors.iconTextShadow, float: "left" } }, box: { style: { float: "left" } }, title: { style: { color: colors.titleColor, textShadow: "1px 1px 1px " + colors.titleTextShadow } }, body: { style: { backgroundColor: "transparent", borderColor: "rgba(0, 0, 0, 0.5)" } } }, TabButton: { style: { borderRadius: "2px 2px 0 0", marginLeft: 1 }, state: { hover: { style: { backgroundColor: colors.hoverTabBackgroundColor }, icon: { style: { color: "rgba(0, 0, 0, 0.9)", textShadow: "1px 1px 1px #999999" } }, title: { style: { color: "rgba(0, 0, 0, 0.9)", textShadow: "1px 1px 1px #999999" } } } }, mods: { active: { style: { borderColor: colors.activeTabBorderColor, backgroundColor: colors.activeTabBackgroundColor }, state: { hover: { style: { backgroundColor: colors.activeTabBackgroundColor }, icon: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.activeTabTextShadow } }, title: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.activeTabTextShadow } } } }, icon: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.activeTabTextShadow } }, title: { style: { color: colors.activeTabColor, textShadow: "1px 1px 1px " + colors.activeTabTextShadow } } } }, icon: { style: { color: colors.tabColor, textShadow: "1px 1px 1px " + colors.tabTextShadow } }, title: { style: { color: colors.tabColor, textShadow: "1px 1px 1px " + colors.tabTextShadow } } }, Tab: { mods: { withToolbar: { content: { style: { backgroundColor: colors.contentBackgroundColorWithToolbar } } } }, toolbar: { style: { backgroundColor: colors.activeTabBackgroundColor, borderBottom: "0 none", marginBottom: "1px", borderRadius: "2px", boxShadow: "0 -2px 0 " + colors.toolbarBoxShadow + " inset" } }, content: { style: { backgroundColor: colors.activeTabBackgroundColor, borderBottom: "0 none", marginBottom: "1px", borderRadius: "2px" } }, footer: { style: { backgroundColor: colors.footerBackgroundColor, borderRadius: "2px" } } }, Button: { style: { borderRadius: "2px 2px 0 0", backgroundColor: colors.buttonBackgroundColor, marginLeft: "1px" }, state: { hover: { style: { backgroundColor: colors.hoverButtonBackgroundColor }, children: { style: { color: "rgba(0, 0, 0, 0.9)", textShadow: "1px 1px 1px #ffffff" } } } }, mods: { active: { style: { backgroundColor: colors.activeButtonBackgroundColor } } }, children: { style: { color: "#ffffff", textShadow: "1px 1px 1px rgba(0, 0, 0, 0.9)" } } } }; }; var buildStyle = function (opts) { opts = opts || {}; opts = { theme: opts.theme || "base", skin: opts.skin || "default", headerHeight: opts.headerHeight || 32, headerFontSize: opts.headerFontSize || 14, borderRadius: opts.borderRadius || 3, maxTitleWidth: opts.maxTitleWidth || 130, useAvailableHeight: opts.useAvailableHeight || false, renderPanelBorder: (typeof opts.renderPanelBorder === "boolean") ? opts.renderPanelBorder : true, activeTabHeaderBorder: (typeof opts.activeTabHeaderBorder === "boolean") ? opts.activeTabHeaderBorder : true }; var isSafari = /Safari/.test(window.navigator.userAgent) && /Apple Computer/.test(window.navigator.vendor); var styles = { base: { PanelWrapper: { style: {}, config: { autocompact: true } }, Panel: { style: { height: (opts.useAvailableHeight) ? "100%" : "inherit" }, header: { style: { display: "block", fontSize: Utils.pixelsOf(opts.headerFontSize), height: opts.headerHeight } }, tabsStart: { style: { width: 20, height: "100%" } }, tabsEnd: { style: { width: 20, height: "100%" } }, tabs: { style: { height: opts.headerHeight, float: "right", display: "inline-block", margin: 0, minWidth: Utils.pixelsOf(opts.headerHeight), padding: 0 } }, icon: { style: { display: "block", float: "left", fontSize: "125%", height: opts.headerHeight, lineHeight: Utils.pixelsOf(opts.headerHeight - 4), marginRight: -6, textAlign: "center", width: opts.headerHeight - 2 } }, box: { style: { marginLeft: 10, height: "100%", display: "inline-block", position: "relative", maxWidth: Utils.pixelsOf(opts.maxTitleWidth) } }, title: { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", letterSpacing: 0, lineHeight: Utils.pixelsOf(opts.headerHeight), width: "auto" } }, group: { style: { padding: "0 5px", backgroundColor: "transparent" } }, body: { style: { height: (opts.useAvailableHeight) ? "calc(100% - " + opts.headerHeight + "px)" : "inherit" } } }, TabButton: { style: { position: "relative", float: "left", display: "block", listStyle: "none", padding: "0 5px", height: opts.headerHeight, fontSize: "0.95em", cursor: "pointer" }, mods: { untitled: { box: { style: { marginLeft: 0 } } }, active: { style: { cursor: "default" } } }, icon: { style: { display: "block", float: "left", fontSize: "125%", height: opts.headerHeight, textAlign: "center", width: opts.headerHeight - 2, lineHeight: Utils.pixelsOf(opts.headerHeight - 2), marginRight: -9, marginLeft: -3, opacity: 0.85 } }, box: { style: { lineHeight: Utils.pixelsOf(opts.headerHeight), marginRight: 6, opacity: 0.85, marginLeft: 10, height: "100%", display: "inline-block", position: "relative", maxWidth: Utils.pixelsOf(opts.maxTitleWidth) } }, title: { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", letterSpacing: 0, lineHeight: Utils.pixelsOf(opts.headerHeight), width: "auto" } } }, Tab: { style: { display: "none" }, mods: { active: { style: { display: (opts.useAvailableHeight) ? (isSafari ? "-webkit-flex" : "flex") : "block", minHeight: (opts.useAvailableHeight) ? "100%" : "inherit", WebkitFlexDirection: (opts.useAvailableHeight) ? "column" : "inherit", flexDirection: (opts.useAvailableHeight) ? "column" : "inherit", height: "100%" }, content: { style: (opts.useAvailableHeight) ? { WebkitFlex: 1, flex: 1, position: "relative" } : {}, children: { style: (opts.useAvailableHeight) ? { padding: "10px", position: "absolute", height: "100%", width: "100%" } : { } } } }, withToolbar: { toolbar: { style: { } } } }, toolbar: { style: { minHeight: Utils.pixelsOf(opts.headerHeight), lineHeight: Utils.pixelsOf(opts.headerHeight) }, children: { style: { padding: "10px" } } }, content: { style: { }, children: { style: { padding: "10px" } } }, footer: { style: { minHeight: Utils.pixelsOf(opts.headerHeight), lineHeight: Utils.pixelsOf(opts.headerHeight), padding: "10px" }, children: { style: {} } } }, Button: { style: { float: "right", height: Utils.pixelsOf(opts.headerHeight), minWidth: Utils.pixelsOf(opts.headerHeight), display: "inline-block", lineHeight: Utils.pixelsOf(opts.headerHeight), margin: 0, padding: 0, textAlign: "center", cursor: "pointer", WebkitUserSelect: "none", MozUserSelect: "none", msUserSelect: "none", userSelect: "none" }, mods: { disabled: { style: { cursor: "default", pointerEvents: "none", opacity: 0.5 } }, hidden: { style: { display: "none" } }, highlighted: { style: {} } }, children: { style: {} } } }, /* THEME: Chemical */ chemical: chemicalStyle, flexbox: flexboxStyle, flexbox2: flexbox2Style }; var theme = (opts.theme != "base") ? styles[opts.theme](opts) : {}; return Utils.merge(styles.base, theme); }; var createSheet = (function (opts) { var _sheet = buildStyle(opts), _skin = {}; return function (target, mods, alter) { var using = _sheet; mods = mods || []; alter = alter || {} if (alter.skin || false) { if (!(_skin[alter.skin] || false)) { _skin[alter.skin] = buildStyle(React.addons.update(opts, {$merge: {skin: alter.skin}})); } using = _skin[alter.skin]; } if (!mods.length) return using[target]; var sheet = React.addons.update(using[target], {$merge: {}}), i; for (i = 0; i < mods.length; ++i) { if ((sheet.mods || false) && (sheet.mods[mods[i]] || false)) { sheet = Utils.merge(sheet, sheet.mods[mods[i]]); } } return sheet; } }); var Utils = { pixelsOf: function (value) { var val = parseInt(value) || 0 return (val) ? String(val) + "px" : "0"; }, /* Copyright (c) 2012 Nicholas Fisher (MIT License) https://github.com/KyleAMathews/deepmerge */ merge: function (target, src) { var array = Array.isArray(src); var dst = array && [] || {}; if (array) { target = target || []; dst = dst.concat(target); src.forEach(function(e, i) { if (typeof dst[i] === 'undefined') { dst[i] = e; } else if (typeof e === 'object') { dst[i] = Utils.merge(target[i], e); } else { if (target.indexOf(e) === -1) { dst.push(e); } } }); } else { if (target && typeof target === 'object') { Object.keys(target).forEach(function (key) { dst[key] = target[key]; }) } Object.keys(src).forEach(function (key) { if (typeof src[key] !== 'object' || !src[key]) { dst[key] = src[key]; } else { if (!target[key]) { dst[key] = src[key]; } else { dst[key] = Utils.merge(target[key], src[key]); } } }); } return dst; } }; var DragAndDropHandler = function (opts, callback) { var self = this; if (!(self instanceof DragAndDropHandler)) return new DragAndDropHandler(opts, callback); /** Not yet implemented. */ this.opt = Utils.merge({ detachOnLeave: true, /** If true, the tab button being dragged will be rendered by * cloning an existing tab of the target panel. */ cloakInGroup: false, onDragStart: false, onDragEnd: false }, opts || {}); this.ctx = { sortable: true, dragging: false, parentId: false }; this._member = []; this._callback = callback || function () {}; }; DragAndDropHandler.prototype.trigger = function (event, data) { switch (event) { case 'onDragEnd': return this._callback(data); default: throw new Error("Not implemented"); } }; DragAndDropHandler.prototype.addMember = function (component) { return this._member.push(component) - 1; }; DragAndDropHandler.prototype.setParentOfToken = function (memberId) { if (this.ctx.parentId !== false) { this._member[this.ctx.parentId].releaseToken(); } this.ctx.parentId = memberId; }; var Mixins = { Styleable: { getInitialState: function () { this.__ssv = {}; this.__ssvh = false; this.__ssa = {target: '', mods: [], alter: {}}; return {}; }, contextTypes: { sheet: React.PropTypes.func }, getSheet: function (target, mods, alter) { var rebuild = false, i; mods = (typeof this['getSheetMods'] === "function") ? this['getSheetMods'](mods || []) : mods || []; alter = alter || {} if (target != this.__ssa.target) rebuild = true; else { if (mods.length != this.__ssa.mods.length) rebuild = true; else if (mods.length != 0) { for (i = mods.length; --i >= 0;) { if (this.__ssa.mods.indexOf(mods[i]) == -1) { rebuild = true; break; } } } // TODO: check if alter has changed } if (rebuild) { this.__ssv = this.context.sheet(target, mods, alter); this.__ssvh = false; this.__ssa = { target: target, mods: Utils.merge(mods, []), alter: Utils.merge(alter, {}) }; } if ((typeof this.state._hover === "boolean")) { if (this.state._hover) { if (this.__ssvh || false) { return this.__ssvh; } if ((this.__ssv.state || false) && (this.__ssv.state.hover || false)) { this.__ssvh = Utils.merge(this.__ssv, this.__ssv.state.hover); return this.__ssvh; } } } return this.__ssv; } }, Transitions: { propTypes: { transitionName: React.PropTypes.string, transitionEnter: React.PropTypes.bool, transitionLeave: React.PropTypes.bool, transitionAppear: React.PropTypes.bool, /** React.addons.CSSTransitionGroup might not work well in some scenarios, * use this to specify another component. * * @see https://github.com/Khan/react-components/blob/master/js/timeout-transition-group.jsx * */ transitionComponent: React.PropTypes.any, /** Additional props specific to transitionComponent. */ transitionCustomProps: React.PropTypes.object }, getTransitionProps: function (pcType) { pcType = pcType || this.props.panelComponentType; var props = {}, globals = (this.context && this.context.globals && this.context.globals[pcType]) ? this.context.globals[pcType] : {}, transitionName = (typeof this.props.transitionName === "string") ? this.props.transitionName : globals.transitionName || ""; if (transitionName.length) { props = { transitionName: transitionName, transitionEnter: (typeof this.props.transitionEnter === "boolean") ? this.props.transitionEnter : globals.transitionEnter || false, transitionLeave: (typeof this.props.transitionLeave === "boolean") ? this.props.transitionLeave : globals.transitionLeave || false, transitionAppear: (typeof this.props.transitionAppear === "boolean") ? this.props.transitionAppear : globals.transitionAppear || false, transitionComponent: (typeof this.props.transitionComponent !== "undefined") ? this.props.transitionComponent : globals.transitionComponent || React.addons.CSSTransitionGroup, transitionCustomProps: this.props.transitionCustomProps || globals.transitionCustomProps || {} }; } else { props = { transitionName: "none", transitionEnter: false, transitionLeave: false, transitionAppear: false, transitionComponent: React.addons.CSSTransitionGroup, transitionCustomProps: {} }; } return props; } }, Toolbar: { getDefaultProps: function () { return { panelComponentType: "Toolbar" }; } }, Content: { getDefaultProps: function () { return { panelComponentType: "Content" }; } }, Footer: { getDefaultProps: function () { return { panelComponentType: "Footer" }; } } }; Mixins.StyleableWithEvents = { mixins: [Mixins.Styleable], getDefaultProps: function () { return { onMouseEnter: false, onMouseLeave: false }; }, getInitialState: function () { this.listeners = { onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave }; return { _hover: false, _focus: false }; }, handleMouseEnter: function (ev) { if (typeof this.props['onMouseEnter'] === "function") this.props['onMouseEnter'](ev); this.setState({ _hover: true }); }, handleMouseLeave: function (ev) { if (typeof this.props['onMouseLeave'] === "function") this.props['onMouseLeave'](ev); this.setState({ _hover: false }); } }; Mixins.PanelWrapper = { propTypes: { transitionName: React.PropTypes.string, transitionEnter: React.PropTypes.bool, transitionLeave: React.PropTypes.bool, transitionAppear: React.PropTypes.bool, globals: React.PropTypes.object, /** React.addons.CSSTransitionGroup might not work well in some scenarios, * use this to specify another component. * * @see https://github.com/Khan/react-components/blob/master/js/timeout-transition-group.jsx * */ transitionComponent: React.PropTypes.any, /** Additional props specific to transitionComponent. */ transitionCustomProps: React.PropTypes.object, dragAndDropHandler: React.PropTypes.oneOfType([ React.PropTypes.object, React.PropTypes.bool ]) }, getDefaultProps: function () { return { "icon": false, "title": "", "selectedIndex": 0, /** Triggered before a change tab event propagated from within the Panel (e.g., user's click). * Optionally, return false to stop it. */ "onTabChange": null, "buttons": [], "globals": {} }; }, getInitialState: function () { var opts = { theme: this.props.theme, skin: this.props.skin, headerHeight: this.props.headerHeight, headerFontSize: this.props.headerFontSize, borderRadius: this.props.borderRadius, maxTitleWidth: this.props.maxTitleWidth, useAvailableHeight: this.props.useAvailableHeight, renderPanelBorder: this.props.renderPanelBorder, activeTabHeaderBorder: this.props.activeTabHeaderBorder }; this._sheet = createSheet(opts); this.config = this._sheet("PanelWrapper").config; return { selectedIndex: parseInt(this.props.selectedIndex) }; }, childContextTypes: { selectedIndex: React.PropTypes.number, sheet: React.PropTypes.func, onTabChange: React.PropTypes.func, globals: React.PropTypes.object, numTabs: React.PropTypes.number }, getChildContext: function () { return { selectedIndex: this.state.selectedIndex, sheet: this._sheet, onTabChange: this.handleTabChange, globals: this.props.globals, numTabs: React.Children.count(this.props.children) }; }, handleTabChange: function (index) { if (typeof this.props.onTabChange === "function") { if (this.props.onTabChange(index, this) !== false) { this.setSelectedIndex(index); } } else { this.setSelectedIndex(index); } }, getSelectedIndex: function () { return this.state.selectedIndex; }, setSelectedIndex: function (index, callback) { this.setState({selectedIndex: parseInt(index)}); this.forceUpdate(function () { if (typeof callback === "function") { callback(); } }); }, componentWillReceiveProps: function (nextProps) { var sIndex = this.state.selectedIndex, resetIndex = false, numTabs = React.Children.count(nextProps.children); if (nextProps.selectedIndex != this.props.selectedIndex) { sIndex = nextProps.selectedIndex; resetIndex = true; } if (sIndex >= numTabs) { sIndex = Math.max(numTabs - 1, 0); resetIndex = true; } if (resetIndex) { this.setState({selectedIndex: parseInt(sIndex)}); } } }; Mixins.TabWrapper = { observedProps: ['selectedIndex', 'index'], propTypes: { tabKey: React.PropTypes.any }, getDefaultProps: function () { return { panelComponentType: "TabWrapper", icon: "", title: "", pinned: false, showToolbar: true, showFooter: true }; }, childContextTypes: { index: React.PropTypes.number, tabKey: React.PropTypes.any }, getChildContext: function () { return { index: this.props.index, tabKey: this.props.tabKey }; }, contextTypes: { selectedIndex: React.PropTypes.number } }; Mixins.Button = { mixins: [Mixins.StyleableWithEvents], getDefaultProps: function () { return { name: "default", title: "", visible: true, enabled: true, active: false, highlighted: false, onClick: false, onDoubleClick: false, onContextMenu: false, onChange: false }; }, getInitialState: function () { this.listeners.onClick = this._handleClick; this.listeners.onDoubleClick = this._handleDoubleClick; this.listeners.onContextMenu = this._handleContextMenu; return { visible: this.props.visible, enabled: this.props.enabled, active: this.props.active, highlighted: this.props.highlighted }; }, childContextTypes: { btnTitle: React.PropTypes.string, btnVisible: React.PropTypes.bool, btnEnabled: React.PropTypes.bool, btnActive: React.PropTypes.bool }, getChildContext: function () { return { btnTitle: this.props.title, btnVisible: this.state.visible, btnEnabled: this.state.enabled, btnActive: this.state.active }; }, contextTypes: { selectedIndex: React.PropTypes.number }, getSheetMods: function (otherMods) { var mods = otherMods || []; //np if (this.state.active && mods.indexOf('active') == -1) mods.push('active'); if (!this.state.visible && mods.indexOf('hidden') == -1) mods.push('hidden'); if (!this.state.enabled && mods.indexOf('disabled') == -1) mods.push('disabled'); if (this.state.highlighted && mods.indexOf('highlighted') == -1) mods.push('highlighted'); return mods; }, _handleDoubleClick: function (ev) { if (typeof this.props.onDoubleClick === "function" && this.props.onDoubleClick(ev, this) === false) return; if (typeof this['handleDoubleClick'] === "function") { return this['handleDoubleClick'](ev); } }, _handleClick: function (ev) { if (typeof this.props.onClick === "function" && this.props.onClick(ev, this) === false) return; if (typeof this['handleClick'] === "function") { return this['handleClick'](ev); } }, _handleContextMenu: function (ev) { if (typeof this.props.onContextMenu === "function" && this.props.onContextMenu(ev, this) === false) return; if (typeof this['handleContextMenu'] === "function") { return this['handleContextMenu'](ev); } } }; var FloatingPanel = React.createClass({ displayName: 'FloatingPanel', mixins: [Mixins.PanelWrapper], propTypes: { left:React.PropTypes.number, top:React.PropTypes.number, width:React.PropTypes.number, style:React.PropTypes.object, onClick:React.PropTypes.func, }, getDefaultProps: function () { return { "left": 0, "top": 0, "width": 420, "style": {} }; }, getInitialState: function () { this.skipUpdate = false; return { left: parseInt(this.props.left), top: parseInt(this.props.top), width: parseInt(this.props.width) }; }, componentWillReceiveProps:function(nextProps) { this.setState({width:nextProps.width}); }, dragStart: function (e) { this.panelBounds = { startLeft: this.state.left, startTop: this.state.top, startPageX: e.pageX, startPageY: e.pageY }; try { var img = document.createElement("img"); img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABmJLR0QA/wD/AP+gvaeTAAAADUlEQVQI12NgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg=="; img.width = 1; e.dataTransfer.setData('text/plain', "Panel"); e.dataTransfer.setDragImage(img, -1000, -1000); } catch (err) { /* Fix for IE */ } window.addEventListener('dragover', this.dragOver); }, dragEnd: function() { delete this.panelBounds; window.removeEventListener('dragover', this.dragOver); if (this.props.onBoundsChange) { var height=this.getDOMNode().offsetHeight; this.props.onBoundsChange({left:this.state.left, top:this.state.top, width:this.state.width, height:height}); } }, dragOver: function(e) { if (this.panelBounds || false) { var left = this.panelBounds.startLeft + (e.pageX - this.panelBounds.startPageX), top = this.panelBounds.startTop + (e.pageY - this.panelBounds.startPageY); this.skipUpdate = true; this.setState({ left: left, top: top }); } }, handleMouseClick: function (e) { if (typeof this.props.onClick === "function") { this.props.onClick(e); } }, render: function() { var transform = "translate3d(" + Utils.pixelsOf(this.state.left) + ", " + Utils.pixelsOf(this.state.top) + ", 0)", wrapperStyle = React.addons.update({ WebkitTransform: transform, MozTransform: transform, msTransform: transform, transform: transform, width: Utils.pixelsOf(this.state.width), position: "absolute" }, {$merge: this.props.style}); if (!this.skipUpdate) { var props = React.addons.update({ onDragStart: this.dragStart, onDragEnd: this.dragEnd, floating: true }, {$merge: this.config}), keys = Object.keys(this.props); for (var i = keys.length; --i >= 0;) { if (["children", "left", "top", "width", "style"].indexOf(keys[i]) != -1) continue; props[keys[i]] = this.props[keys[i]]; } this.inner = ( React.createElement(ReactPanel, props, this.props.children ) ); } else { this.skipUpdate = false; } return React.createElement("div", {style:wrapperStyle, onClick:this.handleMouseClick}, this.inner); } }); var Panel = React.createClass({ displayName: 'Panel', mixins: [Mixins.PanelWrapper], render: function() { var props = React.addons.update({}, {$merge: this.config}), keys = Object.keys(this.props); for (var i = keys.length; --i >= 0;) { if (["children"].indexOf(keys[i]) != -1) continue; props[keys[i]] = this.props[keys[i]]; } return React.createElement(ReactPanel, props, this.props.children ); } }); var ReactPanel = React.createClass({ displayName: 'ReactPanel', mixins: [Mixins.Styleable, Mixins.Transitions], getDefaultProps: function () { return { "icon": false, "title": "", "autocompact": true, "floating": false, "onDragStart": null, "onDragEnd": null, "maxTitleWidth": 130, "buttons": [] }; }, propTypes: { dragAndDropHandler: React.PropTypes.oneOfType([ React.PropTypes.object, React.PropTypes.bool ]) }, getInitialState: function () { return { compacted: (this.props.autocompact) }; }, contextTypes: { selectedIndex: React.PropTypes.number, sheet: React.PropTypes.func, onTabChange: React.PropTypes.func, globals: React.PropTypes.object }, getSelectedIndex: function () { return this.context.selectedIndex; }, handleClick: function (event, index) { this.context.onTabChange(parseInt(index)); }