|
29 | 29 | </PopoverButton> |
30 | 30 | </LayoutRow> |
31 | 31 | <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()"> |
33 | 33 | <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"> |
35 | 35 | <IconButton |
36 | 36 | :action="(e) => (toggleLayerVisibility(layer.path), e && e.stopPropagation())" |
37 | 37 | :icon="layer.visible ? 'EyeVisible' : 'EyeHidden'" |
38 | 38 | :size="24" |
39 | 39 | :title="layer.visible ? 'Visible' : 'Hidden'" |
40 | 40 | /> |
41 | 41 | </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> |
49 | 44 | <div |
50 | 45 | class="layer" |
51 | 46 | :class="{ selected: layer.layer_metadata.selected }" |
52 | | - :style="{ marginLeft: layerIndent(layer) }" |
53 | 47 | @click.shift.exact.stop="selectLayer(layer, false, true)" |
54 | 48 | @click.shift.ctrl.exact.stop="selectLayer(layer, true, true)" |
55 | 49 | @click.ctrl.exact.stop="selectLayer(layer, true, false)" |
|
59 | 53 | @dragstart="dragStart($event, layer)" |
60 | 54 | :title="layer.path" |
61 | 55 | > |
62 | | - <div class="layer-thumbnail" v-html="layer.thumbnail"></div> |
63 | 56 | <div class="layer-type-icon"> |
64 | 57 | <IconLabel v-if="layer.layer_type === 'Folder'" :icon="'NodeTypeFolder'" title="Folder" /> |
65 | 58 | <IconLabel v-else :icon="'NodeTypePath'" title="Path" /> |
66 | 59 | </div> |
67 | 60 | <div class="layer-name"> |
68 | 61 | <span>{{ layer.name }}</span> |
69 | 62 | </div> |
| 63 | + <div class="thumbnail" v-html="layer.thumbnail"></div> |
70 | 64 | </div> |
71 | | - <!-- <div class="glue" :style="{ marginLeft: layerIndent(layer) }"></div> --> |
72 | 65 | </div> |
73 | 66 | </LayoutCol> |
74 | 67 | </LayoutRow> |
|
96 | 89 | } |
97 | 90 |
|
98 | 91 | .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 | +
|
99 | 95 | .layer-row { |
| 96 | + flex: 0 0 auto; |
100 | 97 | display: flex; |
101 | | - height: 36px; |
102 | 98 | align-items: center; |
103 | | - flex: 0 0 auto; |
104 | 99 | position: relative; |
| 100 | + height: 36px; |
| 101 | + margin: 0 4px; |
| 102 | + border-bottom: 1px solid var(--color-4-dimgray); |
105 | 103 |
|
106 | | - & + .layer-row, |
107 | | - & + .insert-mark + .layer-row { |
108 | | - margin-top: 2px; |
109 | | - } |
110 | | -
|
111 | | - .layer-visibility { |
| 104 | + .visibility { |
| 105 | + height: 100%; |
112 | 106 | 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 | + } |
114 | 114 | } |
115 | 115 |
|
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; |
123 | 121 | outline: none; |
124 | 122 | border: none; |
125 | 123 | 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 | + } |
126 | 134 |
|
127 | 135 | &::after { |
128 | 136 | content: ""; |
129 | 137 | position: absolute; |
130 | 138 | width: 0; |
131 | 139 | height: 0; |
132 | | - top: 3px; |
133 | | - left: 4px; |
134 | 140 | border-style: solid; |
135 | 141 | 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 | + } |
137 | 147 | } |
138 | 148 |
|
139 | 149 | &.expanded::after { |
140 | | - top: 4px; |
141 | | - left: 3px; |
142 | 150 | 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; |
146 | 152 |
|
147 | | - .node-connector-missing { |
148 | | - width: 16px; |
149 | | - flex: 0 0 auto; |
| 153 | + &:hover { |
| 154 | + color: var(--color-f-white); |
| 155 | + } |
| 156 | + } |
150 | 157 | } |
151 | 158 |
|
152 | 159 | .layer { |
153 | 160 | display: flex; |
154 | | - min-width: 0; |
155 | 161 | 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; |
159 | 164 | width: 100%; |
160 | 165 | height: 100%; |
161 | | - z-index: 1; |
| 166 | + border-radius: 2px; |
| 167 | + padding: 0 4px; |
| 168 | + margin-right: 8px; |
162 | 169 |
|
163 | 170 | &.selected { |
164 | | - background: var(--color-7-middlegray); |
| 171 | + background: var(--color-5-dullgray); |
165 | 172 | color: var(--color-f-white); |
166 | 173 | } |
167 | 174 |
|
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 | | -
|
182 | 175 | .layer-type-icon { |
183 | | - margin-left: 8px; |
184 | | - margin-right: 4px; |
185 | 176 | flex: 0 0 auto; |
| 177 | + margin: 0 4px; |
186 | 178 | } |
187 | 179 |
|
188 | 180 | .layer-name { |
| 181 | + flex: 1 1 100%; |
189 | 182 | display: flex; |
190 | 183 | min-width: 0; |
191 | | - flex: 1 1 100%; |
192 | | - margin-right: 8px; |
| 184 | + margin: 0 4px; |
193 | 185 |
|
194 | 186 | span { |
195 | 187 | text-overflow: ellipsis; |
196 | 188 | white-space: nowrap; |
197 | 189 | overflow: hidden; |
198 | 190 | } |
199 | 191 | } |
200 | | - } |
201 | 192 |
|
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 | + } |
210 | 208 | } |
211 | 209 | } |
212 | 210 |
|
|
221 | 219 | position: absolute; |
222 | 220 | background: var(--color-accent-hover); |
223 | 221 | width: 100%; |
224 | | - height: 6px; |
| 222 | + height: 5px; |
225 | 223 | } |
226 | 224 |
|
227 | 225 | &:not(:first-child, :last-child) { |
228 | | - top: -2px; |
| 226 | + top: -3px; |
229 | 227 | } |
230 | 228 |
|
231 | 229 | &:first-child::after { |
232 | 230 | top: 0; |
233 | 231 | } |
234 | 232 |
|
235 | 233 | &: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; |
237 | 236 | } |
238 | 237 | } |
239 | 238 | } |
@@ -318,12 +317,12 @@ export default defineComponent({ |
318 | 317 | }, |
319 | 318 | methods: { |
320 | 319 | layerIndent(layer: LayerPanelEntry) { |
321 | | - return `${(layer.path.length - 1) * 16}px`; |
| 320 | + return `${layer.path.length * 16}px`; |
322 | 321 | }, |
323 | 322 | async toggleLayerVisibility(path: BigUint64Array) { |
324 | 323 | this.editor.instance.toggle_layer_visibility(path); |
325 | 324 | }, |
326 | | - async handleNodeConnectorClick(path: BigUint64Array) { |
| 325 | + async handleExpandArrowClick(path: BigUint64Array) { |
327 | 326 | this.editor.instance.toggle_layer_expansion(path); |
328 | 327 | }, |
329 | 328 | async setLayerBlendMode(newSelectedIndex: number) { |
@@ -416,15 +415,15 @@ export default defineComponent({ |
416 | 415 |
|
417 | 416 | const [nearestPath, above, nearestElement] = this.closest(tree, event.clientY); |
418 | 417 |
|
419 | | - // Set the initial state of the line |
| 418 | + // Set the initial state of the insert line |
420 | 419 | 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 |
422 | 421 | tree.insertBefore(insertLine, nearestElement); |
423 | 422 | } |
424 | 423 |
|
425 | 424 | this.draggingData = { path: layer.path, above, nearestPath, insertLine }; |
426 | 425 | }, |
427 | | - updateLine(event: DragEvent) { |
| 426 | + updateInsertLine(event: DragEvent) { |
428 | 427 | // Stop the drag from being shown as cancelled |
429 | 428 | event.preventDefault(); |
430 | 429 |
|
|
0 commit comments