-
-
Notifications
You must be signed in to change notification settings - Fork 632
Expand file tree
/
Copy pathlogic-tree.css
More file actions
344 lines (302 loc) · 13.3 KB
/
Copy pathlogic-tree.css
File metadata and controls
344 lines (302 loc) · 13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
/* GROUP LOGIC TREE CONNECTORS
=================================================== */
/*
URL: /cp/forms/{handle}/logic (Tree view)
Connects source fields to destination page headers using CSS anchor
positioning. Inspired by: https://codepen.io/cbolson/pen/emzegWP
Markup (LogicTree.vue):
- Each page column is a .linked-list__column (.linked-list__page-name + .linked-list__sections).
- Each section is a .linked-list__section (.linked-list__section-marker + <ul>).
- Page headers: .linked-list__page-name with anchor-name: --page-N.
- Source fields: .linked-list__connector with --end-connection: --page-N, pointing at the target page’s anchor name.
- Multi-column hops: add .linked-list__page-leap on the source <li> and a .linked-list__extra-leap-connector child for the bridge segment. This helps separate multi-column connectors from adjacent pages.
Direct leap anatomy (source below destination page header):
[destination page header]
┌────── ::after — gap midpoint → destination corner
│
[source]───────────┘ ::before — source centre → gap midpoint
Each direct connector is two pseudo-elements on .linked-list__connector:
::before — vertical leg from source centre up toward the destination,
plus the left half of the inter-column gap
::after — right half of the gap + rounded corner at the destination
Page leaps (.linked-list__page-leap + .linked-list__extra-leap-connector):
::before on .linked-list__extra-leap-connector — stub out from the
source and along the first gap
::after on .linked-list__extra-leap-connector — corner into the
second column
::before on .linked-list__connector — vertical leg from the buffer
rail down to the destination page header
Anchors:
--start-connection — set on each .linked-list__connector <li> (the source)
var(--end-connection) — anchor name of the target page header, declared in
markup as --end-connection: --page-N on the source <li>
anchor-scope: --start-connection — keeps each source’s connector isolated
*/
@layer components {
.linked-list-container {
/* Don't let max-content grid width expand ancestors — scroll inside here only */
width: 100%;
max-width: 100%;
overflow-x: auto;
overflow-y: visible;
contain: inline-size;
overscroll-behavior-x: contain;
}
.linked-list {
--join-stroke: 1px;
--join-line: var(--join-stroke) solid var(--color-blue-400);
--join-radius: 20px;
:where(.dark) & {
--join-line: var(--join-stroke) solid var(--color-blue-500);
}
--gap: 6rem;
--column-width: 12.5rem;
--item-padding: 1rem;
--item-height: 3.2rem;
/* Bit of a magic number here to pull the line into the gap between the items */
--buffer-top-pull: calc(0.8rem + var(--item-padding));
--buffer-left-pull: 1.5rem;
/* GROUP LOGIC TREE CONNECTORS / LAYOUT
=================================================== */
position: relative;
display: grid;
align-items: start;
grid-auto-flow: column;
grid-auto-columns: var(--column-width);
width: max-content;
margin-inline: auto;
gap: var(--gap);
.linked-list__column {
display: flex;
flex-direction: column;
gap: 1rem;
}
.linked-list__page-name {
@apply px-2 pt-2;
}
.linked-list__sections {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.linked-list__section {
display: flex;
flex-direction: column;
}
.linked-list__section-marker {
display: flex;
align-items: end;
justify-content: center;
.linked-list__section:not(:first-child) & {
height: var(--item-height);
}
@apply text-2xs font-medium text-gray-800 dark:text-gray-200;
+ ul {
@apply rounded-t-none;
}
}
.linked-list__section-marker-label {
@apply max-w-[8rem] shrink-0 select-none pt-1.5 rounded-t-xl bg-gray-100 dark:bg-transparent;
max-width: unset;
width: 100%;
}
ul {
margin: 0;
list-style: none;
display: grid;
gap: 0.5rem;
@apply p-1.5 px-1.5 bg-gray-100 rounded-lg dark:bg-gray-900/60;
li {
/* Use a grid instead of flexbox so we can force things into column 1 and 2.
If we use flexbox then invisible items such as .linked-list__extra-leap-connector will take up an flex child space, changing the layout of items with a leap connector. */
display: grid;
grid-template-columns: 1rem 1fr;
> * {
grid-row: 1;
}
& > svg {
grid-column: 1;
}
.linked-list__field-name {
grid-column: 2;
}
gap: 0.5rem;
height: var(--item-height);
padding-block: var(--item-padding);
@apply px-3 border-1 border-gray-300 rounded-md bg-white shadow-ui-xs text-xs text-gray-850 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 dark:shadow-none;
}
/* A fieldset import <li> is a plain column; its rows carry the chrome. */
li.linked-list__fieldset {
display: flex;
flex-direction: column;
height: auto;
padding: 0;
@apply border-0! bg-transparent! shadow-none!;
}
/* GROUP LOGIC TREE CONNECTORS / SOURCE
=================================================== */
.linked-list__connector {
anchor-name: --start-connection;
anchor-scope: --start-connection; /* one anchor per source row */
/* GROUP LOGIC TREE CONNECTORS / DIRECT LEAP
=================================================== */
/* ::before spans source right edge → gap midpoint.
::after spans gap midpoint → destination left edge. */
&::before,
&::after {
content: '';
position: absolute;
pointer-events: none;
border: var(--join-line);
right: calc(anchor(left var(--end-connection)) + var(--gap) / 2);
left: anchor(right --start-connection);
}
&::before {
top: anchor(bottom var(--end-connection));
bottom: anchor(center --start-connection);
border-left-color: transparent;
border-top-color: transparent;
border-radius: 0 0 var(--join-radius) 0;
}
&::after {
top: auto;
right: anchor(left var(--end-connection));
bottom: calc(anchor(bottom var(--end-connection)) - var(--join-stroke));
left: calc(anchor(left var(--end-connection)) - var(--gap) / 2 - var(--join-stroke));
height: var(--join-radius);
border-right-color: transparent;
border-bottom-color: transparent;
border-radius: var(--join-radius) 0 0 0;
}
}
/* GROUP LOGIC TREE CONNECTORS / PAGE LEAP (SOURCE → NON-ADJACENT PAGE)
=================================================== */
/* Overrides the direct-hop ::before to terminate at the buffer rail instead of the source centre, leaving room for the bridge element. */
.linked-list__page-leap {
&::before,
&::after {
border-style: dashed;
}
&::before {
bottom: calc(anchor(center --start-connection) + var(--buffer-top-pull));
left: calc(anchor(right --start-connection) + var(--gap));
}
}
}
}
/* GROUP LOGIC TREE CONNECTORS / VIEW MODES
=================================================== */
.linked-list__field-name {
@apply line-clamp-1 select-none;
[data-field-number] {
/* Optically align */
position: relative;
top: -0.2px;
}
}
.linked-list--expanded {
/* Bit of a magic number here to pull the line into the gap between the items */
--buffer-top-pull: calc(1.45rem + var(--item-padding));
--item-height: 4.5rem;
.linked-list__field-name {
@apply line-clamp-2;
}
}
/* GROUP LOGIC TREE / EMPTY SECTION
=================================================== */
/* Shown when a section has no fields (e.g. its last field was dragged out) so
the section sticks around as a drop target. */
.linked-list__empty-section {
@apply flex items-center justify-center rounded-md border border-dashed border-gray-300 bg-white dark:border-gray-700 dark:bg-gray-800 text-2xs text-gray-500 dark:text-gray-400 select-none;
height: var(--item-height, 3.2rem);
}
/* While an item is dragged over the section, Sortable inserts it here - hide the placeholder. */
.field-sort-container:has([data-field-item]) .linked-list__empty-section {
display: none;
}
/* GROUP LOGIC TREE CONNECTORS / FIELDSETS
=================================================== */
/* A fieldset import is one entry (one <li>) holding a row per field. Each row
mirrors a normal field chip. The var fallbacks let the drag mirror - which
is cloned outside the tree - keep the right height. */
.linked-list__fieldset-row {
display: grid;
grid-template-columns: 1rem 1fr;
gap: 0.5rem;
height: var(--item-height, 3.2rem);
padding-block: var(--item-padding, 1rem);
@apply px-3 border-1 border-dashed border-gray-400/75! rounded-md bg-white shadow-ui-xs text-xs text-gray-850 dark:border-gray-600! dark:bg-gray-800 dark:text-gray-200 dark:shadow-none opacity-75;
> * {
grid-row: 1;
}
& > svg {
grid-column: 1;
}
.linked-list__field-name {
grid-column: 2;
}
}
.linked-list__hidden-field {
@apply opacity-75;
}
/* GROUP LOGIC TREE CONNECTORS / LEAP BRIDGE (FIRST GAP + CORNER)
=================================================== */
.linked-list__extra-leap-connector {
&::before {
top: calc(anchor(top --start-connection) + var(--buffer-top-pull) / 2 - var(--join-stroke) * 4);
bottom: anchor(center --start-connection);
left: anchor(right --start-connection);
width: calc(var(--gap) - var(--buffer-left-pull));
border-block-end: var(--join-line);
border-inline-end: var(--join-line);
border-bottom-right-radius: var(--join-radius);
}
&::after {
/* display: none; */
top: calc(anchor(center --start-connection) - var(--buffer-top-pull) - var(--join-stroke));
bottom: calc(anchor(center --start-connection) + var(--buffer-top-pull) / 2 + var(--join-stroke));
width: calc(var(--buffer-left-pull) + var(--join-stroke) * 2);
left: calc(anchor(right --start-connection) + var(--gap) - var(--buffer-left-pull) - var(--join-stroke));
border-inline-start: var(--join-line);
border-block-start: var(--join-line);
border-top-left-radius: var(--join-radius);
}
&::before,
&::after {
border-style: dashed;
content: '';
position: absolute;
pointer-events: none;
}
}
/* GROUP LOGIC TREE CONNECTORS / DRAG MIRROR
=================================================== */
/* The drag mirror is cloned into <body>, outside the scoped tree styles, so it
needs its own look. A single field mirrors a normal chip. */
.linked-list__mirror {
display: grid;
grid-template-columns: 1rem 1fr;
gap: 0.5rem;
height: var(--item-height, 3.2rem);
padding-block: var(--item-padding, 1rem);
@apply px-3 border border-gray-300 rounded-md bg-white shadow-lg text-xs text-gray-850 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 cursor-grabbing;
> * {
grid-row: 1;
}
& > svg {
grid-column: 1;
}
.linked-list__field-name {
grid-column: 2;
}
}
/* A fieldset mirror is a plain column; its .linked-list__fieldset-row children keep their own chrome. */
.linked-list__mirror.linked-list__fieldset {
display: flex;
flex-direction: column;
gap: 0.5rem;
height: auto;
padding: 0;
@apply border-0! bg-transparent! shadow-none!;
}
}