Skip to content

Commit 05e8a98

Browse files
authored
Redesign the Layer Tree UI (#468)
1 parent 8e3d237 commit 05e8a98

File tree

1 file changed

+79
-80
lines changed

1 file changed

+79
-80
lines changed

frontend/src/components/panels/LayerTree.vue

Lines changed: 79 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,21 @@
2929
</PopoverButton>
3030
</LayoutRow>
3131
<LayoutRow :class="'layer-tree scrollable-y'">
32-
<LayoutCol :class="'list'" ref="layerTreeList" @click="() => deselectAllLayers()" @dragover="updateLine($event)" @dragend="drop()">
32+
<LayoutCol :class="'list'" ref="layerTreeList" @click="() => deselectAllLayers()" @dragover="updateInsertLine($event)" @dragend="drop()">
3333
<div class="layer-row" v-for="(layer, index) in layers" :key="String(layer.path.slice(-1))">
34-
<div class="layer-visibility">
34+
<div class="visibility">
3535
<IconButton
3636
:action="(e) => (toggleLayerVisibility(layer.path), e && e.stopPropagation())"
3737
:icon="layer.visible ? 'EyeVisible' : 'EyeHidden'"
3838
:size="24"
3939
:title="layer.visible ? 'Visible' : 'Hidden'"
4040
/>
4141
</div>
42-
<button
43-
v-if="layer.layer_type === 'Folder'"
44-
class="node-connector"
45-
:class="{ expanded: layer.layer_metadata.expanded }"
46-
@click.stop="handleNodeConnectorClick(layer.path)"
47-
></button>
48-
<div v-else class="node-connector-missing"></div>
42+
<div class="indent" :style="{ marginLeft: layerIndent(layer) }"></div>
43+
<button v-if="layer.layer_type === 'Folder'" class="expand-arrow" :class="{ expanded: layer.layer_metadata.expanded }" @click.stop="handleExpandArrowClick(layer.path)"></button>
4944
<div
5045
class="layer"
5146
:class="{ selected: layer.layer_metadata.selected }"
52-
:style="{ marginLeft: layerIndent(layer) }"
5347
@click.shift.exact.stop="selectLayer(layer, false, true)"
5448
@click.shift.ctrl.exact.stop="selectLayer(layer, true, true)"
5549
@click.ctrl.exact.stop="selectLayer(layer, true, false)"
@@ -59,16 +53,15 @@
5953
@dragstart="dragStart($event, layer)"
6054
:title="layer.path"
6155
>
62-
<div class="layer-thumbnail" v-html="layer.thumbnail"></div>
6356
<div class="layer-type-icon">
6457
<IconLabel v-if="layer.layer_type === 'Folder'" :icon="'NodeTypeFolder'" title="Folder" />
6558
<IconLabel v-else :icon="'NodeTypePath'" title="Path" />
6659
</div>
6760
<div class="layer-name">
6861
<span>{{ layer.name }}</span>
6962
</div>
63+
<div class="thumbnail" v-html="layer.thumbnail"></div>
7064
</div>
71-
<!-- <div class="glue" :style="{ marginLeft: layerIndent(layer) }"></div> -->
7265
</div>
7366
</LayoutCol>
7467
</LayoutRow>
@@ -96,117 +89,122 @@
9689
}
9790
9891
.layer-tree {
92+
// Crop away the 1px border below the bottom layer entry when it uses the full space of this panel
93+
margin-bottom: -1px;
94+
9995
.layer-row {
96+
flex: 0 0 auto;
10097
display: flex;
101-
height: 36px;
10298
align-items: center;
103-
flex: 0 0 auto;
10499
position: relative;
100+
height: 36px;
101+
margin: 0 4px;
102+
border-bottom: 1px solid var(--color-4-dimgray);
105103
106-
& + .layer-row,
107-
& + .insert-mark + .layer-row {
108-
margin-top: 2px;
109-
}
110-
111-
.layer-visibility {
104+
.visibility {
105+
height: 100%;
112106
flex: 0 0 auto;
113-
margin-left: 4px;
107+
display: flex;
108+
align-items: center;
109+
110+
.icon-button {
111+
height: 100%;
112+
width: calc(24px + 2 * 4px);
113+
}
114114
}
115115
116-
.node-connector {
117-
flex: 0 0 auto;
118-
width: 12px;
119-
height: 12px;
120-
margin: 0 2px;
121-
border-radius: 50%;
122-
background: var(--color-data-raster);
116+
.expand-arrow {
117+
margin-left: -16px;
118+
width: 16px;
119+
height: 100%;
120+
padding: 0;
123121
outline: none;
124122
border: none;
125123
position: relative;
124+
background: none;
125+
flex: 0 0 auto;
126+
display: flex;
127+
align-items: center;
128+
justify-content: center;
129+
border-radius: 2px;
130+
131+
&:hover {
132+
background: var(--color-6-lowergray);
133+
}
126134
127135
&::after {
128136
content: "";
129137
position: absolute;
130138
width: 0;
131139
height: 0;
132-
top: 3px;
133-
left: 4px;
134140
border-style: solid;
135141
border-width: 3px 0 3px 6px;
136-
border-color: transparent transparent transparent var(--color-2-mildblack);
142+
border-color: transparent transparent transparent var(--color-e-nearwhite);
143+
144+
&:hover {
145+
color: var(--color-f-white);
146+
}
137147
}
138148
139149
&.expanded::after {
140-
top: 4px;
141-
left: 3px;
142150
border-width: 6px 3px 0 3px;
143-
border-color: var(--color-2-mildblack) transparent transparent transparent;
144-
}
145-
}
151+
border-color: var(--color-e-nearwhite) transparent transparent transparent;
146152
147-
.node-connector-missing {
148-
width: 16px;
149-
flex: 0 0 auto;
153+
&:hover {
154+
color: var(--color-f-white);
155+
}
156+
}
150157
}
151158
152159
.layer {
153160
display: flex;
154-
min-width: 0;
155161
align-items: center;
156-
border-radius: 2px;
157-
background: var(--color-5-dullgray);
158-
margin-right: 16px;
162+
z-index: 1;
163+
min-width: 0;
159164
width: 100%;
160165
height: 100%;
161-
z-index: 1;
166+
border-radius: 2px;
167+
padding: 0 4px;
168+
margin-right: 8px;
162169
163170
&.selected {
164-
background: var(--color-7-middlegray);
171+
background: var(--color-5-dullgray);
165172
color: var(--color-f-white);
166173
}
167174
168-
.layer-thumbnail {
169-
width: 64px;
170-
height: 100%;
171-
background: white;
172-
border-radius: 2px;
173-
flex: 0 0 auto;
174-
175-
svg {
176-
width: calc(100% - 4px);
177-
height: calc(100% - 4px);
178-
margin: 2px;
179-
}
180-
}
181-
182175
.layer-type-icon {
183-
margin-left: 8px;
184-
margin-right: 4px;
185176
flex: 0 0 auto;
177+
margin: 0 4px;
186178
}
187179
188180
.layer-name {
181+
flex: 1 1 100%;
189182
display: flex;
190183
min-width: 0;
191-
flex: 1 1 100%;
192-
margin-right: 8px;
184+
margin: 0 4px;
193185
194186
span {
195187
text-overflow: ellipsis;
196188
white-space: nowrap;
197189
overflow: hidden;
198190
}
199191
}
200-
}
201192
202-
.glue {
203-
position: absolute;
204-
background: var(--color-data-raster);
205-
height: 6px;
206-
bottom: -4px;
207-
left: 44px;
208-
right: 16px;
209-
z-index: 0;
193+
.thumbnail {
194+
height: calc(100% - 4px);
195+
margin: 2px 0;
196+
margin-left: 4px;
197+
width: 64px;
198+
background: white;
199+
border-radius: 2px;
200+
flex: 0 0 auto;
201+
202+
svg {
203+
width: calc(100% - 4px);
204+
height: calc(100% - 4px);
205+
margin: 2px;
206+
}
207+
}
210208
}
211209
}
212210
@@ -221,19 +219,20 @@
221219
position: absolute;
222220
background: var(--color-accent-hover);
223221
width: 100%;
224-
height: 6px;
222+
height: 5px;
225223
}
226224
227225
&:not(:first-child, :last-child) {
228-
top: -2px;
226+
top: -3px;
229227
}
230228
231229
&:first-child::after {
232230
top: 0;
233231
}
234232
235233
&:last-child::after {
236-
bottom: 0;
234+
// Shifted up 1px to account for the shifting down of the entire `.layer-tree` panel
235+
bottom: 1px;
237236
}
238237
}
239238
}
@@ -318,12 +317,12 @@ export default defineComponent({
318317
},
319318
methods: {
320319
layerIndent(layer: LayerPanelEntry) {
321-
return `${(layer.path.length - 1) * 16}px`;
320+
return `${layer.path.length * 16}px`;
322321
},
323322
async toggleLayerVisibility(path: BigUint64Array) {
324323
this.editor.instance.toggle_layer_visibility(path);
325324
},
326-
async handleNodeConnectorClick(path: BigUint64Array) {
325+
async handleExpandArrowClick(path: BigUint64Array) {
327326
this.editor.instance.toggle_layer_expansion(path);
328327
},
329328
async setLayerBlendMode(newSelectedIndex: number) {
@@ -416,15 +415,15 @@ export default defineComponent({
416415
417416
const [nearestPath, above, nearestElement] = this.closest(tree, event.clientY);
418417
419-
// Set the initial state of the line
418+
// Set the initial state of the insert line
420419
if (nearestElement.parentNode) {
421-
insertLine.style.marginLeft = `${LAYER_LEFT_MARGIN_OFFSET + LAYER_LEFT_INDENT_OFFSET * nearestPath.length}px`;
420+
insertLine.style.marginLeft = `${LAYER_LEFT_MARGIN_OFFSET + LAYER_LEFT_INDENT_OFFSET * nearestPath.length}px`; // TODO: use layerIndent function to calculate this
422421
tree.insertBefore(insertLine, nearestElement);
423422
}
424423
425424
this.draggingData = { path: layer.path, above, nearestPath, insertLine };
426425
},
427-
updateLine(event: DragEvent) {
426+
updateInsertLine(event: DragEvent) {
428427
// Stop the drag from being shown as cancelled
429428
event.preventDefault();
430429

0 commit comments

Comments
 (0)