提交 7cdb85d0 编写于 作者: T tfennelly

Modernized projectview and plugin manager (bigtables with tabs) [JENKINS-24030]

上级 2de93f86
......@@ -73,11 +73,13 @@ public enum BallColor implements StatusIcon {
;
private final Localizable description;
private final String iconName;
private final String iconClassName;
private final String image;
private final Color baseColor;
BallColor(String image, Localizable description, Color baseColor) {
this.iconName = Icon.toNormalizedIconName(image);
this.iconClassName = Icon.toNormalizedIconNameClass(image);
this.baseColor = baseColor;
// name() is not usable in the constructor, so I have to repeat the name twice
......@@ -86,6 +88,14 @@ public enum BallColor implements StatusIcon {
this.description = description;
}
/**
* Get the status ball icon name.
* @return The status ball icon name.
*/
public String getIconName() {
return iconName;
}
/**
* Get the status ball icon class spec name.
* @return The status ball icon class spec name.
......@@ -123,7 +133,7 @@ public enum BallColor implements StatusIcon {
* Returns the {@link #getBaseColor()} in the "#RRGGBB" format.
*/
public String getHtmlBaseColor() {
return String.format("#%06X",baseColor.getRGB()&0xFFFFFF);
return String.format("#%06X", baseColor.getRGB() & 0xFFFFFF);
}
/**
......
......@@ -31,70 +31,72 @@ THE SOFTWARE.
<st:include page="sidepanel.jelly"/>
<l:main-panel>
<local:tabBar page="advanced" xmlns:local="/hudson/PluginManager" />
<table id="pluginsAdv" class="pane" style="margin-top:0; border-top:none">
<tr style="border-top:none; white-space: normal">
<td>
<l:hasPermission permission="${app.pluginManager.CONFIGURE_UPDATECENTER}">
<h1>${%HTTP Proxy Configuration}</h1>
<f:form method="post" action="proxyConfigure" name="proxyConfigure">
<j:scope>
<j:set var="instance" value="${app.proxy}"/>
<j:set var="descriptor" value="${it.proxyDescriptor}"/>
<st:include from="${descriptor}" page="${descriptor.configPage}" />
</j:scope>
<f:block>
<f:submit value="${%Submit}" />
</f:block>
</f:form>
</l:hasPermission>
<div class="pane-frame">
<table id="pluginsAdv" class="pane" style="margin-top:0; border-top:none">
<tr style="border-top:none; white-space: normal">
<td>
<l:hasPermission permission="${app.pluginManager.CONFIGURE_UPDATECENTER}">
<h1>${%HTTP Proxy Configuration}</h1>
<f:form method="post" action="proxyConfigure" name="proxyConfigure">
<j:scope>
<j:set var="instance" value="${app.proxy}"/>
<j:set var="descriptor" value="${it.proxyDescriptor}"/>
<st:include from="${descriptor}" page="${descriptor.configPage}" />
</j:scope>
<f:block>
<f:submit value="${%Submit}" />
</f:block>
</f:form>
</l:hasPermission>
<l:hasPermission permission="${app.pluginManager.UPLOAD_PLUGINS}">
<h1>${%Upload Plugin}</h1>
<f:form method="post" action="uploadPlugin" name="uploadPlugin" enctype="multipart/form-data">
<f:block>
<div style="margin-bottom: 1em;">
${%uploadtext}
</div>
</f:block>
<f:block>
<!-- @size is for other browsers, @style is for IE -->
${%File}: <input type="file" name="name" class="setting-input" style="width:80%" size='40' />
</f:block>
<f:block>
<f:submit value="${%Upload}" />
</f:block>
</f:form>
</l:hasPermission>
<l:hasPermission permission="${app.pluginManager.CONFIGURE_UPDATECENTER}">
<h1>${%Update Site}</h1>
<f:form method="post" action="siteConfigure" name="siteConfigure">
<f:entry title="${%URL}" >
<f:textbox name="site" value="${app.updateCenter.getSite(app.updateCenter.ID_DEFAULT).url}" />
</f:entry>
<f:block>
<f:submit value="${%Submit}" />
</f:block>
</f:form>
<j:set var="hasNonDefault" value="${false}"/>
<j:forEach var="site" items="${app.updateCenter.sites}">
<j:if test="${site.id != app.updateCenter.ID_DEFAULT}">
<j:set var="hasNonDefault" value="${true}"/>
</j:if>
</j:forEach>
<j:if test="${hasNonDefault}">
<h2>${%Other Sites}</h2>
<ul>
<l:hasPermission permission="${app.pluginManager.UPLOAD_PLUGINS}">
<h1>${%Upload Plugin}</h1>
<f:form method="post" action="uploadPlugin" name="uploadPlugin" enctype="multipart/form-data">
<f:block>
<div style="margin-bottom: 1em;">
${%uploadtext}
</div>
</f:block>
<f:block>
<!-- @size is for other browsers, @style is for IE -->
${%File}: <input type="file" name="name" class="setting-input" style="width:80%" size='40' />
</f:block>
<f:block>
<f:submit value="${%Upload}" />
</f:block>
</f:form>
</l:hasPermission>
<l:hasPermission permission="${app.pluginManager.CONFIGURE_UPDATECENTER}">
<h1>${%Update Site}</h1>
<f:form method="post" action="siteConfigure" name="siteConfigure">
<f:entry title="${%URL}" >
<f:textbox name="site" value="${app.updateCenter.getSite(app.updateCenter.ID_DEFAULT).url}" />
</f:entry>
<f:block>
<f:submit value="${%Submit}" />
</f:block>
</f:form>
<j:set var="hasNonDefault" value="${false}"/>
<j:forEach var="site" items="${app.updateCenter.sites}">
<j:if test="${site.id != app.updateCenter.ID_DEFAULT}">
<li>${site.url}</li>
<j:set var="hasNonDefault" value="${true}"/>
</j:if>
</j:forEach>
</ul>
</j:if>
</l:hasPermission>
</td>
</tr>
</table>
<j:if test="${hasNonDefault}">
<h2>${%Other Sites}</h2>
<ul>
<j:forEach var="site" items="${app.updateCenter.sites}">
<j:if test="${site.id != app.updateCenter.ID_DEFAULT}">
<li>${site.url}</li>
</j:if>
</j:forEach>
</ul>
</j:if>
</l:hasPermission>
</td>
</tr>
</table>
</div>
<div align="right" style="margin-top:1em">
<j:invokeStatic var="ds" className="jenkins.model.DownloadSettings" method="get"/>
<form method="post" action="${ds.useBrowser ? 'checkUpdates' : 'checkUpdatesServer'}">
......
......@@ -36,104 +36,107 @@ THE SOFTWARE.
<input type="text" id="filter-box"/>
</div>
<local:tabBar page="installed" xmlns:local="/hudson/PluginManager" />
<table id="plugins" class="sortable pane bigtable">
<j:choose>
<j:when test="${empty(app.pluginManager.plugins) and empty(app.pluginManager.failedPlugins)}">
<tr><td>
${%No plugins installed.}
</td></tr>
</j:when>
<j:otherwise>
<tr style="border-top: 0px;">
<th width="32" tooltip="${%Uncheck to disable the plugin}">${%Enabled}</th>
<th initialSortDir="down">${%Name}</th>
<th width="1">${%Version}</th>
<th width="1">${%Previously installed version}</th>
<th width="1">${%Pinned}</th>
<th width="1">${%Uninstall}</th>
</tr>
<j:forEach var="p" items="${app.pluginManager.plugins}">
<tr class="plugin">
<j:set var="state" value="${p.enabled?'true':null}"/>
<td class="center pane" data="${state}">
<input type="checkbox" checked="${state}" onclick="flip(event)"
url="plugin/${p.shortName}"
original="${p.active?'true':'false'}"/>
</td>
<td class="pane">
<div>
<a href="${p.url}">
${p.updateInfo.displayName?:p.displayName}
<div class="pane-frame">
<table id="plugins" class="sortable pane bigtable stripped-odd">
<j:choose>
<j:when test="${empty(app.pluginManager.plugins) and empty(app.pluginManager.failedPlugins)}">
<tr><td>
${%No plugins installed.}
</td></tr>
</j:when>
<j:otherwise>
<tr>
<th width="32" tooltip="${%Uncheck to disable the plugin}">${%Enabled}</th>
<th initialSortDir="down">${%Name}</th>
<th width="1">${%Version}</th>
<th width="1">${%Previously installed version}</th>
<th width="1">${%Pinned}</th>
<th width="1">${%Uninstall}</th>
</tr>
<j:forEach var="p" items="${app.pluginManager.plugins}">
<tr class="plugin">
<j:set var="state" value="${p.enabled?'true':null}"/>
<td class="center pane" data="${state}">
<input type="checkbox" checked="${state}" onclick="flip(event)"
url="plugin/${p.shortName}"
original="${p.active?'true':'false'}"/>
</td>
<td class="pane">
<div>
<a href="${p.url}">
${p.updateInfo.displayName?:p.displayName}
</a>
</div>
<div class="excerpt">
<j:set var="indexPage" value="${p.indexPage.toString()}" />
<j:set var="excerpt" value="${app.updateCenter.getPlugin(p.shortName).excerpt}"/>
<j:set var="description" value="${p.manifest.mainAttributes.getValue('Specification-Title')}"/>
<j:choose>
<j:when test="${!empty(indexPage)}"> <!-- src/main/resources/index.jelly, preferred -->
<j:include uri="${indexPage}"/>
</j:when>
<j:when test="${excerpt != null}"> <!-- HTML from {excerpt} in wiki page -->
<j:out value="${excerpt}"/>
</j:when>
<j:when test="${description != null}"> <!-- last resort, from POM -->
${description}
</j:when>
<j:otherwise>
${%No description available.}
</j:otherwise>
</j:choose>
</div>
</td>
<td class="center pane" style="white-space:normal">
<a href="plugin/${p.shortName}/thirdPartyLicenses">
${p.version}
</a>
</div>
<div class="excerpt">
<j:set var="indexPage" value="${p.indexPage.toString()}" />
<j:set var="excerpt" value="${app.updateCenter.getPlugin(p.shortName).excerpt}"/>
<j:set var="description" value="${p.manifest.mainAttributes.getValue('Specification-Title')}"/>
</td>
<td class="pane">
<j:if test="${p.downgradable}">
<form method="post" action="${rootURL}/updateCenter/plugin/${p.shortName}/downgrade">
<s:submit value="${%downgradeTo(p.backupVersion)}"/>
</form>
</j:if>
</td>
<td class="center pane" id='unpin-${p.shortName}'>
<j:if test="${p.isPinned()}">
<input type="button" onclick="unpin(this,'${p.shortName}')" value="${%Unpin}" class="yui-button" />
<a href="${%wiki.url}" target="_blank"><l:icon class="icon-help icon-sm" style="vertical-align:top"/></a>
</j:if>
</td>
<td class="center pane">
<j:choose>
<j:when test="${!empty(indexPage)}"> <!-- src/main/resources/index.jelly, preferred -->
<j:include uri="${indexPage}"/>
</j:when>
<j:when test="${excerpt != null}"> <!-- HTML from {excerpt} in wiki page -->
<j:out value="${excerpt}"/>
<j:when test="${p.isDeleted()}">
<p>${%Uninstallation pending}</p>
</j:when>
<j:when test="${description != null}"> <!-- last resort, from POM -->
${description}
<j:when test="${!p.isBundled()}">
<form method="post" action="plugin/${p.shortName}/uninstall">
<input type="submit" value="${%Uninstall}"/>
</form>
</j:when>
<j:otherwise>
${%No description available.}
</j:otherwise>
</j:choose>
</div>
</td>
<td class="center pane" style="white-space:normal">
<a href="plugin/${p.shortName}/thirdPartyLicenses">
${p.version}
</a>
</td>
<td class="pane">
<j:if test="${p.downgradable}">
<form method="post" action="${rootURL}/updateCenter/plugin/${p.shortName}/downgrade">
<s:submit value="${%downgradeTo(p.backupVersion)}"/>
</form>
</j:if>
</td>
<td class="center pane" id='unpin-${p.shortName}'>
<j:if test="${p.isPinned()}">
<input type="button" onclick="unpin(this,'${p.shortName}')" value="${%Unpin}" class="yui-button" />
<a href="${%wiki.url}" target="_blank"><l:icon class="icon-help icon-sm" style="vertical-align:top"/></a>
</j:if>
</td>
<td class="center pane">
<j:choose>
<j:when test="${p.isDeleted()}">
<p>${%Uninstallation pending}</p>
</j:when>
<j:when test="${!p.isBundled()}">
<form method="post" action="plugin/${p.shortName}/uninstall">
<input type="submit" value="${%Uninstall}"/>
</form>
</j:when>
</j:choose>
</td>
</tr>
</j:forEach>
<!-- failed ones -->
<j:forEach var="p" items="${it.pluginManager.failedPlugins}">
<tr class="hoverback">
<td class="pane" />
<td class="pane">
<h4 class="error">Failed : ${p.name}</h4>
<div stlyle="padding-left: 1em">
<pre>${p.exceptionString}</pre>
</div>
</td>
<td class="pane" />
</tr>
</j:forEach>
</j:otherwise>
</j:choose>
</table>
</td>
</tr>
</j:forEach>
<!-- failed ones -->
<j:forEach var="p" items="${it.pluginManager.failedPlugins}">
<tr class="hoverback">
<td class="pane" />
<td class="pane">
<h4 class="error">Failed : ${p.name}</h4>
<div stlyle="padding-left: 1em">
<pre>${p.exceptionString}</pre>
</div>
</td>
<td class="pane" />
</tr>
</j:forEach>
</j:otherwise>
</j:choose>
</table>
</div>
<div class="warning" id="needRestart" style="display:none; margin: 1em; height: 1em">
<form method="post" action="${rootURL}/safeRestart">
......
......@@ -57,8 +57,9 @@ THE SOFTWARE.
<j:set var="isUpdates" value="${attrs.page=='updates'}" />
<local:tabBar page="${attrs.page}" xmlns:local="/hudson/PluginManager" />
<table id="plugins" class="sortable pane bigtable">
<tr style="border-top: 0px;">
<div class="pane-frame">
<table id="plugins" class="sortable pane bigtable stripped-odd">
<tr>
<th initialSortDir="${isUpdates?null:'down'}"
tooltip="${%Check to install the plugin}${isUpdates?'':'&lt;br/&gt;'+'%Click this heading to sort by category'}"
width="32" onclick="showhideCategories(this,1)">${%Install}</th>
......@@ -83,7 +84,7 @@ THE SOFTWARE.
</j:if>
<j:set var="installJob" value="${isUpdates ? app.updateCenter.getJob(p) : null}" />
<j:set var="installedOk"
value="${installJob.status.success and installJob.plugin.version==p.version}" />
value="${installJob.status.success and installJob.plugin.version==p.version}" />
<tr class="${installJob!=null ? 'already-upgraded' : ''} plugin" name="${p.displayName}">
<td class="pane" align="center" data="${thisCat}_">
<input type="checkbox" name="plugin.${p.name}.${p.sourceId}"
......@@ -135,7 +136,8 @@ THE SOFTWARE.
</tr>
</j:otherwise>
</j:choose>
</table>
</table>
</div>
<j:if test="${!empty(list)}">
<div id="bottom-sticker" >
......
......@@ -26,17 +26,17 @@ THE SOFTWARE.
<j:jelly xmlns:j="jelly:core">
<style type="text/css">
#noJobDiv {
margin: 20px 0px;
padding: 10px;
text-align: center;
border-width: 0px 1px 1px;
border-style: none solid solid;
border-color: #BBBBBB;
}
</style>
<div id="noJobDiv">
${%description_1}
<j:if test="${it.hasPermission(it.CONFIGURE)}">
<div class="pane-frame">
<div id="noJobDiv">
${%description_1}
<j:if test="${it.hasPermission(it.CONFIGURE)}">
${%description_2}
</j:if>
</j:if>
</div>
</div>
</j:jelly>
......@@ -63,27 +63,30 @@ THE SOFTWARE.
<j:if test="${!empty(jobs) or !empty(attrs.views)}">
<!-- the caller can inject a tab bar here -->
<d:invokeBody/>
<div id="projectstatus-tabBar">
<d:invokeBody/>
</div>
<!-- project list -->
<table id="projectstatus" class="sortable pane bigtable"
style="${showViewTabs?'margin-top:0px; border-top: none;':null}">
<tr style="border-top: 0px;">
<j:forEach var="col" items="${columnExtensions}">
<st:include page="columnHeader.jelly" it="${col}" />
</j:forEach>
<th>
<st:nbsp/>
</th>
</tr>
<div class="pane-frame">
<table id="projectstatus" class="sortable pane bigtable stripped-odd">
<tr class="header">
<j:forEach var="col" items="${columnExtensions}">
<st:include page="columnHeader.jelly" it="${col}" />
</j:forEach>
<th>
<st:nbsp/>
</th>
</tr>
<j:forEach var="v" items="${attrs.views}">
<t:projectViewNested />
</j:forEach>
<j:forEach var="job" items="${jobs}">
<j:set var="relativeLinkToJob" value="${h.getRelativeLinkTo(job)}"/>
<t:projectViewRow jobBaseUrl="${relativeLinkToJob.substring(0, relativeLinkToJob.length() - job.shortUrl.length())}"/>
</j:forEach>
</table>
<j:forEach var="v" items="${attrs.views}">
<t:projectViewNested />
</j:forEach>
<j:forEach var="job" items="${jobs}">
<j:set var="relativeLinkToJob" value="${h.getRelativeLinkTo(job)}"/>
<t:projectViewRow jobBaseUrl="${relativeLinkToJob.substring(0, relativeLinkToJob.length() - job.shortUrl.length())}"/>
</j:forEach>
</table>
</div>
<div>
<t:iconSize><t:rssBar/></t:iconSize>
</div>
......
......@@ -30,7 +30,7 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:x="jelly:xml" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<tr id="job_${job.name}" class="${job.disabled?'disabledJob':null}">
<tr id="job_${job.name}" class="${job.disabled?'disabledJob':null} job-status-${job.iconColor.iconName}">
<j:forEach var="col" items="${columnExtensions}">
<st:include page="column.jelly" it="${col}"/>
</j:forEach>
......
......@@ -30,7 +30,7 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core">
<div class="tab">
<div class="tab${active?' active':''}">
<j:choose>
<j:when test="${active}">
<input type="radio" id="tab-${tabBarId}-${tabIndex}" name="tab-group-${tabBarId}" checked="checked" />
......
......@@ -25,11 +25,19 @@ THE SOFTWARE.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:d="jelly:define" xmlns:st="jelly:stapler">
<st:documentation>
<st:attribute name="class">
'tabBarFrame' class specification. This will allow styling.
</st:attribute>
</st:documentation>
<!-- Use the current millis time as an id for the tab group -->
<j:set scope="parent" var="tabBarId" value="${h.getCurrentTime().getTime()}" />
<div class="tabBar">
<d:invokeBody/>
<div class="tabBarFrame ${attrs.class}">
<div class="tabBar">
<d:invokeBody/>
</div>
<div class="tabBarBaseline"></div>
</div>
<div class="tabBarBaseline"></div>
</j:jelly>
\ No newline at end of file
......@@ -105,7 +105,7 @@ public class ViewTest {
HtmlPage privateViewsPage = (HtmlPage) privateViewsLink.click();
Text viewLabel = (Text) privateViewsPage.getFirstByXPath("//table[@id='viewList']//td[@class='active']/text()");
Text viewLabel = (Text) privateViewsPage.getFirstByXPath("//div[@class='tabBar']//div[@class='tab active']/a/text()");
assertTrue("'All' view should be selected", viewLabel.getTextContent().contains(Hudson_ViewName()));
View listView = listView("listView");
......
......@@ -149,13 +149,26 @@ public class Icon {
* @return The normalized icon name class.
*/
public static String toNormalizedIconNameClass(String string) {
if (string == null) {
return null;
}
return "icon-" + toNormalizedIconName(string);
}
/**
* Normalize the supplied string to an Icon name e.g. "blue_anime" to "blue-anime".
*
* @param string The string to be normalized.
* @return The normalized icon name.
*/
public static String toNormalizedIconName(String string) {
if (string == null) {
return null;
}
if (string.endsWith(".png") || string.endsWith(".gif")) {
string = string.substring(0, string.length() - 4);
}
return "icon-" + string.replace("_", "-");
return string.replace("_", "-");
}
/**
......
......@@ -353,6 +353,67 @@ pre.console {
padding-top: 0;
}
/* tabBar */
.tabBar {
clear: both;
overflow: auto;
position: relative;
top: 1px;
}
.tabBar .tab {
float: left;
margin-left: 3px;
}
.tabBar .tab:first-child {
margin-left: 15px;
}
.tabBar .tab a {
position: relative;
display: block;
border: solid 1px #f0f0f0;
border-bottom: none;
text-decoration: none;
color: #999;
padding: 7px 10px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.tabBar .tab a:hover {
background: #eee;
}
.tabBar .tab a.addTab {
font-weight: bold;
color: #0460d1;
border: 1px solid #d8efff;
border-bottom: none;
}
.tabBar .tab a.addTab:hover {
background: #d8efff;
}
.tabBar .tab [type=radio] {
display: none;
}
.tabBar [type=radio]:checked ~ a {
border: solid 1px #f0f0f0;
background: #eee;
color: #000;
font-weight: bold;
border-bottom: 1px solid #eee;
z-index: 2;
}
.tabBarBaseline {
border-top: 1px solid #f0f0f0;
z-index: 1;
}
.tabBarBaseline {
display: none; /* Baseline is hidden by default. See next rule fo adding visibility. */
}
.tabBarFrame.showBaseline .tabBarBaseline {
display: block;
}
/* div that looks like a hyperlink */
.pseudoLink {
cursor: pointer;
......@@ -411,17 +472,30 @@ div.dashboard {
/* pane */
.pane-frame {
border: solid 1px #f0f0f0;
border-radius: 4px;
}
#side-panel .pane-frame:hover {
border: solid 1px #cecece;
}
.pane-header, .pane-footer {
padding: 8px 0px;
background-color: #eee;
border: solid 1px #e0e0e0;
border: solid 1px #f3f3f3;
border-left: none;
border-right: none;
color: #3b3b3b;
}
.pane-header {
border-top: none;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.pane-footer {
border-bottom: none;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
......@@ -434,6 +508,7 @@ div.dashboard {
table.pane {
width: 100%;
border-collapse: collapse;
border: 1px #bbb solid;
}
td.pane {
......@@ -441,8 +516,14 @@ td.pane {
vertical-align: middle;
}
table.pane.stripped tr:nth-child(even) {
background: #f7f7f7;
table.stripped tr:nth-child(even) {
background: #fbfbfb;
}
table.stripped-even tr:nth-child(even) {
background: #fbfbfb;
}
table.stripped-odd tr:nth-child(odd) {
background: #fbfbfb;
}
div.pane-header {
......@@ -476,16 +557,19 @@ th.pane {
border: 1px solid #bbb;
padding: 3px 4px 3px 4px;
}
table.bigtable.pane > tbody > tr > td:last-child {
border-right: none;
}
.bigtable tr:hover {
background-color: #f0f0f0;
.pane-frame table, .pane-frame .bigtable tr {
border: none; /* Border will be provided by the pane-frame */
}
.bigtable th {
font-weight: bold;
border: none;
background-color: #f0f0f0;
padding: 3px 4px 3px 4px;
padding: 6px 4px;
}
.bigtable td {
......@@ -757,8 +841,24 @@ LABEL.attach-previous {
white-space: nowrap;
}
#projectstatus .header {
border-bottom: 1px solid #ddd;
}
#projectstatus th {
text-align: left;
padding: 7px 0px;
}
#projectstatus tbody tr:first-child th:first-child {
border-top-left-radius: 4px;
}
#projectstatus tbody tr:first-child th:last-child {
border-top-right-radius: 4px;
}
#projectstatus tbody tr:last-child td:first-child {
border-bottom-left-radius: 4px;
}
#projectstatus tr:last-child td:last-child {
border-bottom-right-radius: 4px;
}
/* ============================ list view entries ======================== */
......@@ -1428,52 +1528,4 @@ table#legend-table td {
background-color: #e5958c;
color: #f8f8f8;
border: 1px solid #e39280;
}
/* tabBar */
.tabBar {
clear: both;
overflow: auto;
}
.tabBar .tab {
float: left;
margin-left: 3px;
}
.tabBar .tab:first-child {
margin-left: 15px;
}
.tabBar .tab a {
position: relative;
display: block;
background: #eee;
text-decoration: none;
color: #000;
padding: 10px;
border: 1px solid #ccc;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.tabBar .tab a:hover {
background: #d1d1d1;
}
.tabBar .tab a.addTab {
background: #d8efff;
}
.tabBar .tab a.addTab:hover {
background: #bed5e5;
}
.tabBar .tab [type=radio] {
display: none;
}
.tabBar [type=radio]:checked ~ a {
background: white;
border-bottom: 1px solid white;
z-index: 2;
}
.tabBarBaseline {
position: relative;
top: -1px;
border-top: 1px solid #ccc;
z-index: 1;
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册