mirror of https://github.com/josh-berry/tab-stash
340 lines
9.0 KiB
Plaintext
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;
|
|
}
|
|
}
|
|
}
|
|
}
|