@servicetitan/navigation 14.3.0 → 14.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.d.ts.map +1 -1
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.js +68 -107
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.js.map +1 -1
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.module.less +70 -127
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.module.less.d.ts +2 -0
- package/dist/utils/__tests__/catalog-groups.test.d.ts +2 -0
- package/dist/utils/__tests__/catalog-groups.test.d.ts.map +1 -0
- package/dist/utils/catalog-groups.d.ts +11 -0
- package/dist/utils/catalog-groups.d.ts.map +1 -0
- package/dist/utils/catalog-groups.js +66 -0
- package/dist/utils/catalog-groups.js.map +1 -0
- package/package.json +2 -2
- package/src/components/titan-layout/__tests__/navigation-catalog-reset.test.tsx +7 -3
- package/src/components/titan-layout/navigation-catalog/navigation-catalog.module.less +70 -127
- package/src/components/titan-layout/navigation-catalog/navigation-catalog.module.less.d.ts +2 -0
- package/src/components/titan-layout/navigation-catalog/navigation-catalog.tsx +106 -134
- package/src/utils/__tests__/catalog-groups.test.ts +97 -0
- package/src/utils/catalog-groups.ts +57 -0
|
@@ -8,23 +8,9 @@
|
|
|
8
8
|
* inside it plain, rather than each one having to defend itself with `!important`.
|
|
9
9
|
*/
|
|
10
10
|
:global(.anvil2.anvil2) {
|
|
11
|
-
|
|
12
|
-
* The catalog's column geometry.
|
|
13
|
-
*
|
|
14
|
-
* `@catalog-column` is the *minimum* a column may be, not its width — the columns share out the
|
|
15
|
-
* dialog evenly and end up wider than this. Its job is to set how many there are: at `xlarge`
|
|
16
|
-
* (70rem) four of these plus their gaps fit and a fifth does not. The columns land at 248px, which
|
|
17
|
-
* still clears the widest row the catalog renders (229px including its star), so nothing clips.
|
|
18
|
-
*
|
|
19
|
-
* Width rather than `column-count: 4`, so a dialog narrowed by a small viewport drops to three
|
|
20
|
-
* columns and then two instead of squeezing four ever thinner. A fixed count gave 208px per column
|
|
21
|
-
* at a 768px viewport, under that 229px, which turned the longest labels into ellipses —
|
|
22
|
-
* "Batch/Export Transactions" and "Recurring Service Events" among them.
|
|
23
|
-
*/
|
|
11
|
+
// Four readable columns fit the desktop dialog; narrower dialogs wrap whole groups.
|
|
24
12
|
@catalog-column: 15rem;
|
|
25
13
|
@catalog-gap: 1.5rem;
|
|
26
|
-
|
|
27
|
-
// The dialog's own horizontal inset, shared by the search field's row and the footer.
|
|
28
14
|
@catalog-padding: @spacing-2;
|
|
29
15
|
|
|
30
16
|
/*
|
|
@@ -93,6 +79,7 @@
|
|
|
93
79
|
|
|
94
80
|
> * {
|
|
95
81
|
height: 100%;
|
|
82
|
+
padding-bottom: @spacing-0;
|
|
96
83
|
}
|
|
97
84
|
}
|
|
98
85
|
|
|
@@ -104,6 +91,7 @@
|
|
|
104
91
|
* in the left third of the dialog, reading as a fourth column.
|
|
105
92
|
*/
|
|
106
93
|
.body {
|
|
94
|
+
container-type: inline-size;
|
|
107
95
|
display: flex;
|
|
108
96
|
flex: 1;
|
|
109
97
|
min-width: 0;
|
|
@@ -119,6 +107,8 @@
|
|
|
119
107
|
*/
|
|
120
108
|
.content {
|
|
121
109
|
min-height: 0;
|
|
110
|
+
padding-inline: @catalog-padding;
|
|
111
|
+
padding-bottom: @spacing-0;
|
|
122
112
|
}
|
|
123
113
|
|
|
124
114
|
// Results scroll above the full-width footer; allow its content to wrap on narrow screens.
|
|
@@ -160,14 +150,12 @@
|
|
|
160
150
|
/*
|
|
161
151
|
* Fills whatever the now fixed-height dialog leaves under the search field, instead of sizing to
|
|
162
152
|
* its own content, so browse, a narrowed search, and the empty state all hold the same height.
|
|
163
|
-
*
|
|
164
|
-
* `.columns` stays at `height: auto` regardless — a multi-column box given a fixed height becomes a
|
|
165
|
-
* fragmentainer and spills overflow sideways into further columns instead of growing downwards, which
|
|
166
|
-
* is a horizontal scrollbar rather than the vertical one wanted here.
|
|
167
153
|
*/
|
|
168
154
|
.results {
|
|
169
155
|
flex: 1;
|
|
170
156
|
min-height: 0;
|
|
157
|
+
// Overlay scrollbars ignore scrollbar-gutter; keep the last column's stars clear of them.
|
|
158
|
+
padding-right: @spacing-2;
|
|
171
159
|
overflow-y: auto;
|
|
172
160
|
|
|
173
161
|
/*
|
|
@@ -177,107 +165,81 @@
|
|
|
177
165
|
scrollbar-gutter: stable;
|
|
178
166
|
}
|
|
179
167
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
* given by three. At a 768px viewport that was 208px per column, narrower than the 229px the widest
|
|
186
|
-
* category wants, and `max-width: 100%` on the category turned the overflow into ellipses — "Batch/
|
|
187
|
-
* Export Transactions" and "Recurring Service Events" among them.
|
|
188
|
-
*
|
|
189
|
-
* Giving the width instead lets the count fall out of it: three columns at the cap, then two, then
|
|
190
|
-
* one, and never a clipped label at any width. It cannot reach four, because a fourth column and its
|
|
191
|
-
* gap need more room than the cap ever offers, which is what keeps the reference's three-column
|
|
192
|
-
* layout at every desktop size.
|
|
193
|
-
*
|
|
194
|
-
* Multi-column rather than grid so categories pack against one another and flow into the next column,
|
|
195
|
-
* instead of leaving a ragged row of half-empty grid cells whenever one category is much taller than
|
|
196
|
-
* its neighbours.
|
|
197
|
-
*/
|
|
168
|
+
// Match the input's width; let either scrollbar style occupy the dialog's right padding.
|
|
169
|
+
.searchResults {
|
|
170
|
+
padding-right: @spacing-0;
|
|
171
|
+
margin-right: -@catalog-padding;
|
|
172
|
+
scrollbar-gutter: auto;
|
|
198
173
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
* exists, and at the full height of the column block — so it disappears on its own when the dialog
|
|
203
|
-
* narrows to a single column, and a short last column still gets the full-height line to its left
|
|
204
|
-
* rather than one that stops where its content does.
|
|
205
|
-
*
|
|
206
|
-
* A translucent white rather than the `subdued` border token, for the same reason as the row hover:
|
|
207
|
-
* the token is a flat neutral grey, and a grey line on a navy surface reads as a slightly dirty
|
|
208
|
-
* seam. At 12% it lands about where the token's grey would in lightness, but tinted by the navy
|
|
209
|
-
* underneath it, so it belongs to the surface it divides.
|
|
210
|
-
*/
|
|
211
|
-
.columns {
|
|
212
|
-
columns: @catalog-column;
|
|
213
|
-
column-gap: @catalog-gap;
|
|
214
|
-
column-rule: 1px solid rgba(255, 255, 255, 0.12);
|
|
174
|
+
> div {
|
|
175
|
+
width: 100cqw;
|
|
176
|
+
}
|
|
215
177
|
}
|
|
216
178
|
|
|
217
179
|
/*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* of the next column with no heading over it, reading as though those destinations belonged to
|
|
222
|
-
* whatever was above them. `break-after` on the heading only keeps the heading with its first row;
|
|
223
|
-
* it says nothing about the rest.
|
|
224
|
-
*
|
|
225
|
-
* This is a preference, not a guarantee: a category taller than the column still has to break
|
|
226
|
-
* somewhere, and the browser will split it rather than overflow. That degrades to today's behaviour
|
|
227
|
-
* for the one or two largest categories and fixes it for the rest.
|
|
180
|
+
* Each group owns a column, regardless of its length. In particular, Projects & Field starts
|
|
181
|
+
* beside Demand rather than flowing beneath it. The minimum shrinks to the available width
|
|
182
|
+
* on small screens. Search uses a plain single-column list instead of this grid.
|
|
228
183
|
*/
|
|
184
|
+
.columns {
|
|
185
|
+
display: grid;
|
|
186
|
+
grid-template-columns: repeat(auto-fill, minmax(min(100%, @catalog-column), 1fr));
|
|
187
|
+
gap: @catalog-gap;
|
|
188
|
+
overflow: hidden;
|
|
189
|
+
}
|
|
229
190
|
|
|
230
191
|
/*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* whatever the category's right edge is; letting that edge be the column's puts all of them on one
|
|
234
|
-
* line down the whole column rather than one line per category.
|
|
192
|
+
* Grid items stretch to the tallest group so dividers span the row. Place each divider in the
|
|
193
|
+
* preceding gap; the grid clips the one before its first column, including after wrapping.
|
|
235
194
|
*/
|
|
236
195
|
.group {
|
|
237
|
-
|
|
238
|
-
|
|
196
|
+
position: relative;
|
|
197
|
+
min-width: 0;
|
|
198
|
+
|
|
199
|
+
&::before {
|
|
200
|
+
position: absolute;
|
|
201
|
+
top: 0;
|
|
202
|
+
bottom: 0;
|
|
203
|
+
left: -(@catalog-gap / 2);
|
|
204
|
+
width: 1px;
|
|
205
|
+
background: rgba(255, 255, 255, 0.12);
|
|
206
|
+
content: '';
|
|
207
|
+
}
|
|
239
208
|
}
|
|
240
209
|
|
|
241
|
-
|
|
242
|
-
* Level 1 of three. It shares its size with the module rows below it and is told apart by case,
|
|
243
|
-
* tracking and colour rather than by being bigger or smaller — the standard eyebrow treatment, and
|
|
244
|
-
* the one place a plain grey belongs.
|
|
245
|
-
*
|
|
246
|
-
* `break-after` keeps a heading from being stranded at the foot of a column.
|
|
247
|
-
*
|
|
248
|
-
* The colour is spelled out rather than taken from `--a2-foreground-color-subdued`, which resolves
|
|
249
|
-
* to `#c4c4c4` in dark mode: close enough to white that the heading competed with the destinations
|
|
250
|
-
* under it. This is `--a2-color-neutral-400`, a genuine mid grey, and it is the same value the rail
|
|
251
|
-
* headings use.
|
|
252
|
-
*
|
|
253
|
-
* Note that anvil2's colour tokens are spelled `foreground`, not `text`; a `--a2-text-color-*` name
|
|
254
|
-
* resolves to nothing and drops silently to the fallback. That matters here because this
|
|
255
|
-
* repository's LESS `@color-neutral` ramp runs dark-to-light, opposite to anvil2's, so a wrong
|
|
256
|
-
* guess lands on near-black text against a near-black dialog.
|
|
257
|
-
*/
|
|
210
|
+
// Keep eyebrow medium typography and use the catalog's specified subdued colour.
|
|
258
211
|
.groupTitle {
|
|
212
|
+
--a2-text-foreground-color-subdued: @color-neutral-70;
|
|
213
|
+
|
|
259
214
|
display: block;
|
|
260
|
-
break-after: avoid;
|
|
261
215
|
padding: @spacing-0 0.5rem;
|
|
262
216
|
margin-bottom: 0.5rem;
|
|
263
|
-
color: light-dark(
|
|
264
|
-
var(--a2-color-neutral-500, #707070),
|
|
265
|
-
var(--a2-color-neutral-400, #8b8b8b)
|
|
266
|
-
);
|
|
267
|
-
text-transform: uppercase;
|
|
268
|
-
letter-spacing: 0.06em;
|
|
269
217
|
}
|
|
270
218
|
|
|
271
|
-
/* A module and its pages are one unit and stay in the same column. */
|
|
272
219
|
.module-group {
|
|
273
|
-
break-inside: avoid;
|
|
274
220
|
margin-bottom: 0.75rem;
|
|
275
221
|
}
|
|
276
222
|
|
|
223
|
+
// Match the sidebar's small, subdued headings and spacing above each page group.
|
|
224
|
+
.submenuTitle {
|
|
225
|
+
--a2-text-foreground-color-subdued: @color-neutral-70;
|
|
226
|
+
|
|
227
|
+
display: block;
|
|
228
|
+
padding: 12px @spacing-1 @spacing-half 2rem;
|
|
229
|
+
margin: @spacing-0;
|
|
230
|
+
}
|
|
231
|
+
|
|
277
232
|
.row {
|
|
278
233
|
display: flex;
|
|
279
234
|
align-items: center;
|
|
280
235
|
gap: 0.25rem;
|
|
236
|
+
border-radius: var(--a2-border-radius-medium, 4px);
|
|
237
|
+
|
|
238
|
+
// Share the highlight while keeping the link and favorite button independently focusable.
|
|
239
|
+
&:hover,
|
|
240
|
+
&:has(:focus-visible) {
|
|
241
|
+
background-color: @rail-hover;
|
|
242
|
+
}
|
|
281
243
|
}
|
|
282
244
|
|
|
283
245
|
.link {
|
|
@@ -290,15 +252,8 @@
|
|
|
290
252
|
border-radius: var(--a2-border-radius-medium, 4px);
|
|
291
253
|
text-decoration: none;
|
|
292
254
|
|
|
293
|
-
/*
|
|
294
|
-
* The rail's hover, not anvil2's. The token resolves to a flat neutral grey in dark mode, which
|
|
295
|
-
* on this navy replaces the surface's hue instead of lifting it. A translucent white tints
|
|
296
|
-
* whatever it covers, so the hovered row stays the same colour, just brighter — and it is the
|
|
297
|
-
* same value the rail uses on its own rows, which is what the user is comparing it against.
|
|
298
|
-
*/
|
|
299
255
|
&:hover,
|
|
300
256
|
&:focus-visible {
|
|
301
|
-
background-color: @rail-hover;
|
|
302
257
|
text-decoration: none;
|
|
303
258
|
}
|
|
304
259
|
}
|
|
@@ -320,24 +275,15 @@
|
|
|
320
275
|
white-space: nowrap;
|
|
321
276
|
color: var(--a2-foreground-color-default, light-dark(#040404, #ffffff));
|
|
322
277
|
font-weight: var(--a2-font-weight-semibold, 600);
|
|
278
|
+
line-height: 1rem;
|
|
323
279
|
}
|
|
324
280
|
|
|
325
|
-
|
|
281
|
+
// As in the sidebar, page text sits 8px farther in than its group heading.
|
|
326
282
|
.childLink {
|
|
327
|
-
padding
|
|
283
|
+
padding: @spacing-1 @spacing-1 @spacing-1 2.5rem;
|
|
328
284
|
}
|
|
329
285
|
|
|
330
|
-
|
|
331
|
-
* Level 3 of three: below the module row, above the category heading, at regular weight and at the
|
|
332
|
-
* same full-strength foreground. It is still a destination, so it is not dimmed — size and weight
|
|
333
|
-
* are what place it under its module. Only the category heading is greyed.
|
|
334
|
-
*
|
|
335
|
-
* 13px is deliberately off the token scale, which jumps 0.75rem → 0.875rem with nothing in between.
|
|
336
|
-
* At 0.75rem this tied with the category heading and read as another label rather than as somewhere
|
|
337
|
-
* to go. It has to clear the heading and stay under the module row, and the gap between those two is
|
|
338
|
-
* one token wide, so the size is spelled out. It matches the rail's pinned pages, which are sized
|
|
339
|
-
* the same way and for the same reason.
|
|
340
|
-
*/
|
|
286
|
+
// Match the expanded sidebar's child-page typography.
|
|
341
287
|
.childTitle {
|
|
342
288
|
flex: 1;
|
|
343
289
|
min-width: 0;
|
|
@@ -345,7 +291,9 @@
|
|
|
345
291
|
text-overflow: ellipsis;
|
|
346
292
|
white-space: nowrap;
|
|
347
293
|
color: var(--a2-foreground-color-default, light-dark(#040404, #ffffff));
|
|
348
|
-
font-size:
|
|
294
|
+
font-size: @typescale-2;
|
|
295
|
+
font-weight: @font-weight-semibold;
|
|
296
|
+
line-height: 20px;
|
|
349
297
|
}
|
|
350
298
|
|
|
351
299
|
/*
|
|
@@ -382,24 +330,19 @@
|
|
|
382
330
|
.star {
|
|
383
331
|
flex: none;
|
|
384
332
|
opacity: 0;
|
|
385
|
-
|
|
386
|
-
&:focus-visible {
|
|
387
|
-
opacity: 1;
|
|
388
|
-
}
|
|
389
333
|
}
|
|
390
334
|
|
|
391
|
-
|
|
335
|
+
// Mouse clicks retain focus after leaving the row; only keyboard focus should reveal a star.
|
|
336
|
+
.row:hover .star,
|
|
337
|
+
.row:has(:focus-visible) .star {
|
|
392
338
|
opacity: 0.5;
|
|
393
339
|
|
|
394
|
-
&:hover
|
|
340
|
+
&:hover,
|
|
341
|
+
&:focus-visible {
|
|
395
342
|
opacity: 1;
|
|
396
343
|
}
|
|
397
344
|
}
|
|
398
345
|
|
|
399
|
-
.row:focus-within .star {
|
|
400
|
-
opacity: 0.5;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
346
|
/*
|
|
404
347
|
* After the hover rules, which match at the same specificity, so a pinned star stays lit.
|
|
405
348
|
*
|
|
@@ -411,7 +354,7 @@
|
|
|
411
354
|
*/
|
|
412
355
|
.starActive,
|
|
413
356
|
.row:hover .starActive,
|
|
414
|
-
.row:focus-
|
|
357
|
+
.row:has(:focus-visible) .starActive {
|
|
415
358
|
color: var(--a2-status-color-warning, light-dark(#ffbe00, #ffe278));
|
|
416
359
|
opacity: 1;
|
|
417
360
|
}
|
|
@@ -19,6 +19,8 @@ export const moduleTitle: string;
|
|
|
19
19
|
export const results: string;
|
|
20
20
|
export const row: string;
|
|
21
21
|
export const search: string;
|
|
22
|
+
export const searchResults: string;
|
|
22
23
|
export const star: string;
|
|
23
24
|
export const starActive: string;
|
|
25
|
+
export const submenuTitle: string;
|
|
24
26
|
|