tab-stash/styles/forest.less

340 lines
9.0 KiB
Plaintext

// A forest is a group of trees, visually something like this:
//
// <ul class="forest">
//
// <li> <!-- this is a group -->
// <??? class="forest-item">...</>
// <ul class="forest-children">
// <li><??? class="forest-item">...</></li>
// <li>
// <??? class="forest-item">...</>
// <ul class="forest-children"> ... </ul>
// </li>
// </ul>
// </li>
//
// ...
//
// </ul>
//
// Importantly, groups, trees and subtrees all share the same DOM, but are
// styled differently. So a group looks just like an item with child items.
//
// A forest item looks like this; each sub-element is optional except the title:
//
// <??? class="forest-item">
// <??? class="forest-collapse" />
// <??? class="forest-icon" />
// <??? class="forest-title" />
// <??? class="forest-badge" />
// <??? class="forest-toolbar" />
// </>
//
// There are a few special classes that can be applied to .forest-item and
// .forest-children to change their appearance/behavior:
//
// For .forest-item:
// .selectable - Means the item can be selected by clicking its icon
// .selected - The item is currently selected
//
// For .forest-children:
// .collapsed - The children should not be visible
.forest,
.forest-children {
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
& > li {
display: flex;
flex-direction: column;
list-style: none;
// Hide empty list items in the forest (as a workaround so that indexes in
// <dnd-list> stay accurate)
&:empty:not(.dnd-list-ghost) {
display: none;
}
// Hide the toolbar unless the item is being hovered over
&:not(:hover):not(:focus-within) > .forest-item > .forest-toolbar {
display: none;
}
// Hide the collapse button unless the item is collapsed or being hovered
// over
&:not(:hover):not(:focus-within)
> .forest-item:not(.collapsed)
> .forest-collapse {
display: none;
}
}
}
.forest-children {
.forest-children {
margin-left: var(--item-indent-w);
// Show an indent guide
border-left: var(--indent-guide-w) solid var(--indent-guide-border-clr);
// Give ourselves a little space at the bottom of each child list, with a
// nice curl to the indent guide--this makes drag-and-drop easier when
// appending inside a child vs. inserting below the child
&:last-child {
border-bottom-left-radius: calc(var(--item-h) / 4);
padding-bottom: calc(var(--item-h) / 4);
}
}
// Nested children should be hidden if the child is collapsed
&.collapsed {
display: none;
}
// Lower-level children which are folders should have bold titles. This is
// different from top-level folders, which use a bigger font size and
// therefore can use a lighter weight.
.forest-item.folder > .forest-title {
font-weight: bold;
}
}
.forest-item {
display: grid;
// The column layout. This is tweaked in the top-level .forest-item overrides
// above, so keep that in mind.
//
// 1:.forest-collapse, 2:.forest-icon, 3:.forest-title, 4:.forest-badge, 5:.forest-toolbar
grid-template-columns: var(--collapse-btn-size) var(--icon-btn-size) 1fr 0fr 0fr;
// No column-gap because we assign margins to individual items
align-items: center;
// We pad to --collapse-btn-size so that the left and right margins appear
// symmetric, and because --collapse-btn-size is wide enough that the
// auto-hiding scrollbars on macOS don't intrude on the right-side toolbar
// buttons. (Funny how macOS messed with their scrollbars and now we all just
// have to work around it...)
padding: 0 var(--page-pw) 0 0;
height: var(--item-h);
& > .forest-collapse {
grid-row: 1;
grid-column: 1;
width: var(--collapse-btn-size);
}
& > .forest-icon {
grid-row: 1;
grid-column: 2;
}
& > .forest-title {
grid-row: 1;
grid-column: 3;
}
& > .forest-badge {
grid-row: 1;
grid-column: 4;
}
& > .forest-toolbar {
grid-row: 1;
grid-column: 5;
}
&.selectable {
// If a selection is active and this is a candidate for selection, show
// a background on the select button indicating the item can be selected.
.selection-active & .action.select {
background-color: var(--button-bg);
}
&.selected .action.select {
background-color: var(--userlink-fg);
// When an item is selected, always show an icon indicating this instead
// of the actual item icon.
.icon-select-selected-inverse();
& > img,
& > span {
display: none;
}
&:hover {
background-color: var(--userlink-hover-fg);
}
&:active,
&:focus-within {
background-color: var(--userlink-active-fg);
}
}
&:hover,
&:focus-within {
background-color: var(--item-hover-bg);
&.selected {
background-color: var(--selected-hover-bg);
}
.action.select {
box-shadow: var(--ephemeral-hover-shadow-metrics) var(--ctrl-border-clr);
}
// Special highlighting behavior for using the icon to select the tab.
&:not(.selected) .action.select {
background-color: var(--button-bg);
// On hover/activation, show the "select me" icon instead of whatever
// actual icon is present.
.icon-select();
& > img,
& > span {
display: none;
}
&:focus-within,
&:hover {
background-color: var(--button-hover-bg);
}
&:active {
background-color: var(--button-active-bg);
}
}
}
}
& > .forest-title {
padding: 0 calc(var(--item-gap-w) / 2);
margin: 0 calc(var(--item-gap-w) / 2);
height: var(--item-h);
/* to vertically center text while text-overflow: ellipsis; */
line-height: var(--item-h);
.text-overflow-ellipsis();
}
// Don't double-highlight titles that are also links
& > a.forest-title {
&:hover,
&:focus-within,
&:active {
background-color: transparent;
}
}
}
.forest-badge {
height: var(--item-h);
line-height: var(--item-h);
&.icon {
.icon-wrapper();
}
}
// Like .forest-icon and .forest-badge but without the horizontal padding. It
// gets the item-height though, and is aligned for putting inline with text.
.forest-inline-icon {
// Similar to .action's sizing
box-sizing: border-box;
width: var(--icon-size);
height: var(--item-h);
padding: var(--icon-p) 0;
vertical-align: top;
}
.text-overflow-ellipsis() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
//
// Mods for top-level forest items (which should look like groups)
//
// The top level of the forest, which is shown as a set of different
// sections/groups.
.forest {
& > li {
border-radius: var(--group-border-radius);
// Make sure the sidebar/panel views have a visible gap between groups.
// This should be overridden in the tab view.
margin-top: var(--group-ph);
& > .forest-item {
border-top: var(--group-border);
// Must match the enclosing <li> or the background-color that is set on
// hover/selection will mess things up.
border-top-left-radius: var(--group-border-radius);
border-top-right-radius: var(--group-border-radius);
// Since there's no icon for the top-level group, don't leave space for it
// (unlike what we do inside the group). This should match the same
// layout as the regular .forest-item grid above, or the other columns
// won't line up properly.
grid-template-columns: var(--collapse-btn-size) 0fr 1fr 0fr 0fr;
margin: 0;
padding-top: var(--group-ph);
padding-bottom: var(--group-ph);
height: auto;
& > .forest-title {
font-weight: var(--group-header-font-weight);
font-size: var(--group-header-font-size);
margin-left: 0; // To align with the icons in child items
}
// Don't change the background color as if the group were a regular item
&:not(.selected) {
&:hover,
&:focus-within {
background-color: inherit;
}
}
}
& > .forest-children:last-child {
// This is here, instead of the enclosing <li>, so the bottom margin
// naturally disappears when the group is collapsed. It should also be
// overridden in the tab view.
//
// We use padding instead of margin here to give a bigger drop target for
// drag-and-drop operations.
padding-bottom: calc(var(--group-ph) + var(--page-ph));
flex: auto; // Fill all leftover space so we're a bigger drop target
}
}
// Mods to how drag-and-drop looks, since the top-level grouping has a ghost
// which displaces items in the list.
&.dnd-list {
& > .dragging {
display: none;
}
& > .dnd-list-ghost {
display: none;
&.dropping-here {
display: block;
box-sizing: border-box;
border: var(--ghost-border-width) dashed var(--disabled-fg);
background: transparent;
min-height: var(--item-h);
min-width: var(--icon-btn-size);
margin: 0;
}
}
}
}